From 820da6ecabbfbb449d8f4aa92c17a2bb38ae635a Mon Sep 17 00:00:00 2001 From: Britt Park Date: Sat, 2 Sep 2006 18:19:00 +0000 Subject: [PATCH] Merge from HEAD to WCM-DEV2. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@3659 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/action-services-context.xml | 12 +- config/alfresco/application-context.xml | 2 + config/alfresco/audit-services-context.xml | 87 ++ config/alfresco/auditConfig.xml | 4 +- config/alfresco/auditSchema.xsd | 15 +- config/alfresco/bootstrap-context.xml | 25 + config/alfresco/cache-context.xml | 2 +- config/alfresco/core-services-context.xml | 4 +- .../org.hibernate.dialect.Dialect/sample.sql | 4 + .../sample.sql | 4 + .../AlfrescoSchemaMigrate-1.3.sql | 653 +++++++++ .../AlfrescoSchemaMigrate-1.3.sql | 636 +++++++++ .../AlfrescoSchemaUpdate-1.4-1.sql | 75 + .../AlfrescoSchemaUpdate-1.4-2.sql | 59 + .../alfresco/domain/hibernate-cfg.properties | 5 +- config/alfresco/ehcache-default.xml | 127 +- .../custom-db-connection.properties.sample | 1 + config/alfresco/hibernate-context.xml | 29 +- config/alfresco/index-recovery-context.xml | 47 +- .../messages/action-config.properties | 7 +- .../messages/patch-service.properties | 3 + .../messages/schema-update.properties | 8 + config/alfresco/model/contentModel.xml | 3 + .../alfresco/patch/patch-services-context.xml | 22 +- config/alfresco/public-services-context.xml | 1272 +++++++++-------- .../public-services-security-context.xml | 16 + config/alfresco/repository.properties | 3 + .../content/examples/RSS_2.0_recent_docs.ftl | 42 - .../content/examples/company_logos.ftl | 20 - .../templates/content/examples/doc_info.ftl | 31 +- .../templates/content/examples/example.ftl | 47 - .../content/examples/my_pressreleases.ftl | 22 - .../content/examples/userhome_docs.ftl | 15 - .../content/examples/xpath_search.ftl | 26 - .../templates/content_template_examples.xml | 32 +- config/alfresco/version.properties | 4 +- source/java/log4j.properties | 2 +- .../java/org/alfresco/model/ContentModel.java | 7 + .../CounterIncrementActionExecuter.java | 84 ++ ...xecutor.java => ScriptActionExecuter.java} | 6 +- .../repo/admin/patch/PatchExecuter.java | 2 +- .../patch/impl/SchemaUpgradeScriptPatch.java | 27 +- .../patch/impl/UniqueChildNamePatch.java | 3 + .../repo/audit/ApplicationAuditModel.java | 60 + .../alfresco/repo/audit/AuditComponent.java | 52 + .../repo/audit/AuditComponentImpl.java | 425 ++++++ .../repo/audit/AuditConfiguration.java | 36 + .../repo/audit/AuditConfigurationImpl.java | 61 + .../org/alfresco/repo/audit/AuditDAO.java | 32 + .../alfresco/repo/audit/AuditException.java | 54 + .../org/alfresco/repo/audit/AuditInfo.java | 385 +++++ .../repo/audit/AuditMethodInterceptor.java | 65 + .../org/alfresco/repo/audit/AuditMode.java | 67 + .../org/alfresco/repo/audit/AuditModel.java | 74 + .../alfresco/repo/audit/AuditServiceImpl.java | 88 ++ .../alfresco/repo/audit/MethodAuditModel.java | 57 + .../repo/audit/PublicServiceIdentifier.java | 35 + .../audit/PublicServiceIdentifierImpl.java | 165 +++ .../alfresco/repo/audit/RecordOptions.java | 38 + .../repo/audit/hibernate/Audit.hbm.xml | 158 ++ .../repo/audit/hibernate/AuditConfig.java | 41 + .../repo/audit/hibernate/AuditConfigImpl.java | 118 ++ .../repo/audit/hibernate/AuditDate.java | 106 ++ .../repo/audit/hibernate/AuditDateImpl.java | 289 ++++ .../repo/audit/hibernate/AuditFact.java | 74 + .../repo/audit/hibernate/AuditFactImpl.java | 578 ++++++++ .../repo/audit/hibernate/AuditSource.java | 29 + .../repo/audit/hibernate/AuditSourceImpl.java | 139 ++ .../audit/hibernate/HibernateAuditDAO.java | 439 ++++++ .../repo/audit/model/AbstractAuditEntry.java | 203 +++ .../repo/audit/model/AbstractFilter.java | 92 ++ .../audit/model/AbstractNamedAuditEntry.java | 63 + .../audit/model/ApplicationAuditEntry.java | 60 + .../alfresco/repo/audit/model/AuditEntry.java | 220 +++ .../repo/audit/model/AuditModelException.java | 54 + .../alfresco/repo/audit/model/FilterSet.java | 72 + .../repo/audit/model/FilterSetMode.java | 43 + .../alfresco/repo/audit/model/KeyFilter.java | 70 + .../repo/audit/model/KeyFilterMode.java | 86 ++ .../repo/audit/model/MethodAuditEntry.java | 59 + .../repo/audit/model/ParameterFilter.java | 62 + .../repo/audit/model/RecordOptionsImpl.java | 189 +++ .../repo/audit/model/ServiceAuditEntry.java | 100 ++ .../repo/audit/model/TrueFalseUnset.java | 52 + .../repo/audit/model/XMLModelElement.java | 25 + .../alfresco/repo/cache/EhCacheAdapter.java | 9 +- .../repo/cache/EhCacheManagerFactoryBean.java | 84 ++ .../repo/cache/TransactionalCache.java | 12 +- .../metadata/MailMetadataExtracter.java | 46 +- .../alfresco/repo/copy/CopyServiceImpl.java | 19 + .../org/alfresco/repo/domain/NodeStatus.java | 4 +- .../java/org/alfresco/repo/domain/Server.java | 33 + .../org/alfresco/repo/domain/Transaction.java | 35 + .../domain/hibernate/HibernateNodeTest.java | 27 +- .../repo/domain/hibernate/Node.hbm.xml | 78 +- .../repo/domain/hibernate/NodeStatusImpl.java | 14 +- .../repo/domain/hibernate/ServerImpl.java | 76 + .../repo/domain/hibernate/Transaction.hbm.xml | 62 + .../domain/hibernate/TransactionImpl.java | 88 ++ .../repo/domain/schema/SchemaBootstrap.java | 529 +++++++ .../filefolder/FileFolderServiceImpl.java | 21 +- .../filefolder/FileFolderServiceImplTest.java | 8 +- .../repo/node/BaseNodeServiceTest.java | 5 +- .../repo/node/db/DbNodeServiceImpl.java | 6 +- .../HibernateNodeDaoServiceImpl.java | 154 +- .../index/FullIndexRecoveryComponent.java | 131 +- .../index/FullIndexRecoveryComponentTest.java | 2 +- .../index/MissingContentReindexComponent.java | 741 ++++++++++ .../repo/node/integrity/IntegrityTest.java | 17 - .../service/AnnotationTestInterface.java | 1 + .../org/alfresco/service/PublicService.java | 36 + .../org/alfresco/service/ServiceRegistry.java | 1 + .../service/cmr/action/ActionService.java | 2 + .../service/cmr/admin/AdminService.java | 3 + .../service/cmr/audit/AuditService.java | 85 ++ .../cmr/coci/CheckOutCheckInService.java | 2 + .../cmr/dictionary/DictionaryService.java | 2 + .../service/cmr/lock/LockService.java | 2 + .../service/cmr/model/FileFolderService.java | 2 + .../cmr/repository/ContentService.java | 2 + .../service/cmr/repository/CopyService.java | 2 + .../cmr/repository/MimetypeService.java | 2 + .../service/cmr/repository/NodeService.java | 2 + .../service/cmr/repository/ScriptService.java | 2 + .../service/cmr/repository/TemplateNode.java | 3 +- .../cmr/repository/TemplateService.java | 2 + .../service/cmr/rule/RuleService.java | 2 + .../service/cmr/search/CategoryService.java | 2 + .../service/cmr/search/SearchService.java | 2 + .../cmr/security/AuthenticationService.java | 2 + .../cmr/security/AuthorityService.java | 2 + .../service/cmr/security/OwnableService.java | 2 + .../cmr/security/PermissionService.java | 2 + .../service/cmr/security/PersonService.java | 2 + .../service/cmr/version/VersionService.java | 2 + .../service/cmr/view/ExporterService.java | 2 + .../service/cmr/view/ImporterService.java | 2 + .../cmr/view/RepositoryExporterService.java | 2 + .../service/cmr/workflow/WorkflowService.java | 22 + .../service/descriptor/DescriptorService.java | 1 + .../service/license/LicenseService.java | 2 + .../namespace/NamespacePrefixResolver.java | 2 + .../service/namespace/NamespaceService.java | 2 + .../transaction/TransactionService.java | 2 + .../java/org/alfresco/tools/Repository.java | 4 + source/java/org/alfresco/tools/Tool.java | 5 + .../util/debug/MethodCallLogAdvice.java | 136 -- 147 files changed, 9873 insertions(+), 1289 deletions(-) create mode 100644 config/alfresco/audit-services-context.xml create mode 100644 config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/sample.sql create mode 100644 config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.MySQLInnoDBDialect/sample.sql create mode 100644 config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaMigrate-1.3.sql create mode 100644 config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaMigrate-1.3.sql create mode 100644 config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-1.sql create mode 100644 config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-2.sql create mode 100644 config/alfresco/messages/schema-update.properties delete mode 100644 config/alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl delete mode 100644 config/alfresco/templates/content/examples/company_logos.ftl delete mode 100644 config/alfresco/templates/content/examples/example.ftl delete mode 100644 config/alfresco/templates/content/examples/my_pressreleases.ftl delete mode 100644 config/alfresco/templates/content/examples/userhome_docs.ftl delete mode 100644 config/alfresco/templates/content/examples/xpath_search.ftl create mode 100644 source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java rename source/java/org/alfresco/repo/action/executer/{ScriptActionExecutor.java => ScriptActionExecuter.java} (94%) create mode 100644 source/java/org/alfresco/repo/audit/ApplicationAuditModel.java create mode 100644 source/java/org/alfresco/repo/audit/AuditComponent.java create mode 100644 source/java/org/alfresco/repo/audit/AuditComponentImpl.java create mode 100644 source/java/org/alfresco/repo/audit/AuditConfiguration.java create mode 100644 source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java create mode 100644 source/java/org/alfresco/repo/audit/AuditDAO.java create mode 100644 source/java/org/alfresco/repo/audit/AuditException.java create mode 100644 source/java/org/alfresco/repo/audit/AuditInfo.java create mode 100644 source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java create mode 100644 source/java/org/alfresco/repo/audit/AuditMode.java create mode 100644 source/java/org/alfresco/repo/audit/AuditModel.java create mode 100644 source/java/org/alfresco/repo/audit/AuditServiceImpl.java create mode 100644 source/java/org/alfresco/repo/audit/MethodAuditModel.java create mode 100644 source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java create mode 100644 source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java create mode 100644 source/java/org/alfresco/repo/audit/RecordOptions.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditDate.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditFact.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditSource.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java create mode 100644 source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java create mode 100644 source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/AbstractFilter.java create mode 100644 source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/AuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/AuditModelException.java create mode 100644 source/java/org/alfresco/repo/audit/model/FilterSet.java create mode 100644 source/java/org/alfresco/repo/audit/model/FilterSetMode.java create mode 100644 source/java/org/alfresco/repo/audit/model/KeyFilter.java create mode 100644 source/java/org/alfresco/repo/audit/model/KeyFilterMode.java create mode 100644 source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/ParameterFilter.java create mode 100644 source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java create mode 100644 source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java create mode 100644 source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java create mode 100644 source/java/org/alfresco/repo/audit/model/XMLModelElement.java create mode 100644 source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java create mode 100644 source/java/org/alfresco/repo/domain/Server.java create mode 100644 source/java/org/alfresco/repo/domain/Transaction.java create mode 100644 source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java create mode 100644 source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml create mode 100644 source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java create mode 100644 source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java create mode 100644 source/java/org/alfresco/repo/node/index/MissingContentReindexComponent.java create mode 100644 source/java/org/alfresco/service/PublicService.java create mode 100644 source/java/org/alfresco/service/cmr/audit/AuditService.java delete mode 100644 source/java/org/alfresco/util/debug/MethodCallLogAdvice.java diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 5053cd534a..fdc46bdd75 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -355,7 +355,7 @@ - + @@ -363,10 +363,16 @@ - ${spaces.store} + ${spaces.store} - /${spaces.company_home.childname} + /${spaces.company_home.childname} + + + + + + diff --git a/config/alfresco/application-context.xml b/config/alfresco/application-context.xml index b291221eb9..7067dc190f 100644 --- a/config/alfresco/application-context.xml +++ b/config/alfresco/application-context.xml @@ -24,8 +24,10 @@ + + diff --git a/config/alfresco/audit-services-context.xml b/config/alfresco/audit-services-context.xml new file mode 100644 index 0000000000..96ab0a584e --- /dev/null +++ b/config/alfresco/audit-services-context.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + alfresco/auditConfig.xml + + + + + + + + ${dir.auditcontentstore} + + + + + + + + + + + + + + + + + + + + + + org.alfresco.repo.audit.AuditDAO + + + + + + + + + + ${server.transaction.mode.default}, PROPAGATION_REQUIRES_NEW + + + + \ No newline at end of file diff --git a/config/alfresco/auditConfig.xml b/config/alfresco/auditConfig.xml index b3408ecb3d..f2e43fbb38 100644 --- a/config/alfresco/auditConfig.xml +++ b/config/alfresco/auditConfig.xml @@ -3,7 +3,7 @@ - + @@ -174,7 +174,7 @@ - + diff --git a/config/alfresco/auditSchema.xsd b/config/alfresco/auditSchema.xsd index e76db97b11..3c5c74ae82 100644 --- a/config/alfresco/auditSchema.xsd +++ b/config/alfresco/auditSchema.xsd @@ -32,7 +32,9 @@ - + + + - + - + - - + + @@ -102,7 +104,7 @@ - + @@ -196,6 +198,7 @@ + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index c12b56d2f7..aa61568bb4 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -25,6 +25,31 @@ + + + + + + + ${db.schema.update} + + + + classpath:alfresco/dbscripts/create/1.4/${db.script.dialect}/sample.sql + + + + + + + + + + + + + + diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index a65d4ff7e1..a24976a99e 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -13,7 +13,7 @@ - + classpath:alfresco/ehcache-transactional.xml diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 90d0af668f..4e38f52100 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -60,9 +60,6 @@ - - - @@ -79,6 +76,7 @@ alfresco.messages.template-service alfresco.messages.lock-service alfresco.messages.patch-service + alfresco.messages.schema-update alfresco.messages.webdav-messages diff --git a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/sample.sql b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/sample.sql new file mode 100644 index 0000000000..ba873b987c --- /dev/null +++ b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/sample.sql @@ -0,0 +1,4 @@ +-- +-- Insert post-creation scripts here +-- This is a generic fallback for cases where specific dialects are not catered for +-- \ No newline at end of file diff --git a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.MySQLInnoDBDialect/sample.sql b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.MySQLInnoDBDialect/sample.sql new file mode 100644 index 0000000000..ec41ade051 --- /dev/null +++ b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.MySQLInnoDBDialect/sample.sql @@ -0,0 +1,4 @@ +-- +-- Insert post-creation scripts here +-- This is specific to the dialect described in the path to the file +-- \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaMigrate-1.3.sql b/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaMigrate-1.3.sql new file mode 100644 index 0000000000..7d436ee983 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaMigrate-1.3.sql @@ -0,0 +1,653 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.2.1 to V1.3 +-- +-- Author: Derek Hulley +-- ------------------------------------------------------ + +-- +-- Create temporary 1.3 schema +-- + +CREATE TABLE `T_access_control_entry` ( + `id` bigint(20) NOT NULL auto_increment, + `protocol` varchar(50) default NULL, + `identifier` varchar(100) default NULL, + `uuid` varchar(36) default NULL, + `typeUri` varchar(100) default NULL, + `typeName` varchar(100) default NULL, + `name` varchar(100) default NULL, + `recipient` varchar(100) default NULL, + `acl_id` bigint(20), + `permission_id` bigint(20), + `authority_id` varchar(100), + `allowed` bit(1) NOT NULL, + PRIMARY KEY (`id`) +); +ALTER TABLE `T_access_control_entry` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `uuid`); + +CREATE TABLE `T_access_control_list` ( + `id` bigint(20) NOT NULL auto_increment, + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `uuid` varchar(36) NOT NULL, + `inherits` bit(1) NOT NULL, + PRIMARY KEY (`id`) +); +ALTER TABLE `T_access_control_list` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `uuid`); + +CREATE TABLE `T_applied_patch` ( + `id` varchar(32) NOT NULL, + `description` text, + `fixes_from_schema` int(11) default NULL, + `fixes_to_schema` int(11) default NULL, + `applied_to_schema` int(11) default NULL, + `target_schema` int(11) default NULL, + `applied_on_date` datetime default NULL, + `applied_to_server` varchar(64) default NULL, + `was_executed` bit(1) default NULL, + `succeeded` bit(1) default NULL, + `report` text +); + +CREATE TABLE `T_auth_ext_keys` ( + `id` varchar(100) NOT NULL, + `externalKey` varchar(100) NOT NULL +); + +CREATE TABLE `T_authority` ( + `recipient` varchar(100) NOT NULL +); + +CREATE TABLE `T_child_assoc` ( + `id` bigint(20) NOT NULL auto_increment, + `parent_node_id` bigint(20) default NULL, + `parent_protocol` varchar(50) default NULL, + `parent_identifier` varchar(100) default NULL, + `parent_uuid` varchar(36) default NULL, + `child_node_id` bigint(20) default NULL, + `child_protocol` varchar(50) default NULL, + `child_identifier` varchar(100) default NULL, + `child_uuid` varchar(36) default NULL, + `type_qname` varchar(255) NOT NULL, + `qname` varchar(255) NOT NULL, + `is_primary` bit(1) default NULL, + `assoc_index` int(11) default NULL, + PRIMARY KEY (`id`) +); +ALTER TABLE `T_child_assoc` ADD INDEX `IDX_REF_PARENT`(`parent_protocol`, `parent_identifier`, `parent_uuid`); +ALTER TABLE `T_child_assoc` ADD INDEX `IDX_REF_CHILD`(`child_protocol`, `child_identifier`, `child_uuid`); + +CREATE TABLE `T_node` ( + `id` bigint(20) NOT NULL auto_increment, + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `uuid` varchar(36) NOT NULL, + `acl_id` bigint(20) default NULL, + `type_qname` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +); +ALTER TABLE `T_node` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `uuid`); + +CREATE TABLE `T_node_aspects` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `uuid` varchar(36) NOT NULL, + `node_id` bigint(20), + `qname` varchar(200) default NULL +); +ALTER TABLE `T_node_aspects` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `uuid`); + +CREATE TABLE `T_node_assoc` ( + `id` bigint(20) NOT NULL auto_increment, + `source_node_id` bigint(20) default NULL, + `source_protocol` varchar(50) default NULL, + `source_identifier` varchar(100) default NULL, + `source_uuid` varchar(36) default NULL, + `target_node_id` bigint(20) default NULL, + `target_protocol` varchar(50) default NULL, + `target_identifier` varchar(100) default NULL, + `target_uuid` varchar(36) default NULL, + `type_qname` varchar(255) NOT NULL, + PRIMARY KEY (`id`) +); +ALTER TABLE `T_node_assoc` ADD INDEX `IDX_REF_SOURCE`(`source_protocol`, `source_identifier`, `source_uuid`); +ALTER TABLE `T_node_assoc` ADD INDEX `IDX_REF_TARGET`(`target_protocol`, `target_identifier`, `target_uuid`); + +CREATE TABLE `T_node_properties` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `uuid` varchar(36) NOT NULL, + `node_id` bigint(20), + `actual_type` varchar(15) NOT NULL, + `multi_valued` bit(1) NOT NULL, + `persisted_type` varchar(15) NOT NULL, + `boolean_value` bit(1) default NULL, + `long_value` bigint(20) default NULL, + `float_value` float default NULL, + `double_value` double default NULL, + `string_value` text, + `serializable_value` blob, + `qname` varchar(200) NOT NULL +); +ALTER TABLE `t_node_properties` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `uuid`); + +CREATE TABLE `T_node_status` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `guid` varchar(36) NOT NULL, + `node_id` bigint(20) default NULL, + `change_txn_id` varchar(56) NOT NULL, + `deleted` bit(1) NOT NULL +); +ALTER TABLE `t_node_status` ADD INDEX `IDX_REF`(`protocol`, `identifier`, `guid`); + +CREATE TABLE `T_permission` ( + `id` bigint(20) NOT NULL auto_increment, + `type_qname` varchar(200) NOT NULL, + `name` varchar(100) NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `T_store` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `root_node_id` bigint(20) default NULL +); +ALTER TABLE `t_store` ADD INDEX `IDX_STORE_REF`(`protocol`, `identifier`); + +CREATE TABLE `T_version_count` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `version_count` int(11) NOT NULL +); + +-- +-- Copy data from old tables to intermediate tables +-- + +insert into T_store (protocol, identifier) + select protocol, identifier from store; + +insert into T_node (protocol, identifier, uuid, type_qname) + select protocol, identifier, guid, type_qname from node; + +update T_store tstore set root_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tstore.protocol and + tnode.identifier = tstore.identifier and + tnode.uuid = + (select ostore.root_guid from store ostore where + ostore.protocol = tstore.protocol and + ostore.identifier = tstore.identifier + ) + ); + +insert into t_version_count (protocol, identifier, version_count) + select protocol, identifier, version_count from version_count; + +insert into t_node_status (protocol, identifier, guid, change_txn_id, deleted) + select protocol, identifier, guid, change_txn_id, deleted from node_status; +update T_node_status tstatus set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tstatus.protocol and + tnode.identifier = tstatus.identifier and + tnode.uuid = tstatus.guid + ); + +insert into T_node_properties + ( + protocol, identifier, uuid, actual_type, multi_valued, persisted_type, + boolean_value, long_value, float_value, double_value, string_value, serializable_value, qname + ) + select + protocol, identifier, guid, actual_type, multi_valued, persisted_type, + boolean_value, long_value, float_value, double_value, string_value, serializable_value, qname + from node_properties; +update T_node_properties tproperties set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tproperties.protocol and + tnode.identifier = tproperties.identifier and + tnode.uuid = tproperties.uuid + ); + +insert into T_node_aspects + ( + protocol, identifier, uuid, qname + ) + select + protocol, identifier, guid, qname + from node_aspects; +update T_node_aspects taspects set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = taspects.protocol and + tnode.identifier = taspects.identifier and + tnode.uuid = taspects.uuid + ); + +insert into T_child_assoc + ( + parent_protocol, parent_identifier, parent_uuid, + child_protocol, child_identifier, child_uuid, + type_qname, qname, is_primary, assoc_index + ) + select + parent_protocol, parent_identifier, parent_guid, + child_protocol, child_identifier, child_guid, + type_qname, qname, isPrimary, assoc_index + from + child_assoc; +update T_child_assoc tassoc set parent_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.parent_protocol and + tnode.identifier = tassoc.parent_identifier and + tnode.uuid = tassoc.parent_uuid + ); +update T_child_assoc tassoc set child_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.child_protocol and + tnode.identifier = tassoc.child_identifier and + tnode.uuid = tassoc.child_uuid + ); + +insert into T_node_assoc + ( + source_protocol, source_identifier, source_uuid, + target_protocol, target_identifier, target_uuid, + type_qname + ) + select + source_protocol, source_identifier, source_guid, + target_protocol, target_identifier, target_guid, + type_qname + from + node_assoc; +update T_node_assoc tassoc set source_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.source_protocol and + tnode.identifier = tassoc.source_identifier and + tnode.uuid = tassoc.source_uuid + ); +update T_node_assoc tassoc set target_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.target_protocol and + tnode.identifier = tassoc.target_identifier and + tnode.uuid = tassoc.target_uuid + ); + +insert into T_permission + ( + type_qname, name + ) + select + CONCAT('{', type_uri, '}', type_name), name + from + permission_ref; + +insert into T_access_control_list + ( + protocol, identifier, uuid, inherits + ) + select + protocol, identifier, guid, inherits + from node_permission; +update T_node tnode set acl_id = + (select tacl.id from T_access_control_list tacl where + tacl.protocol = tnode.protocol and + tacl.identifier = tnode.identifier and + tacl.uuid = tnode.uuid + ); + +insert into T_auth_ext_keys + ( + id, externalKey + ) + select + id, externalKey + from + externalkeys; + +insert into T_authority + ( + recipient + ) + select + recipient + from + recipient; + +insert into T_access_control_entry + ( + protocol, identifier, uuid, + typeUri, typeName, name, + recipient, + allowed + ) + select + protocol, identifier, guid, + typeUri, typeName, name, + recipient, + allowed + from node_perm_entry; +update T_access_control_entry tentry + set + acl_id = + ( + select + tacl.id + from T_access_control_list tacl + join T_node tnode on tacl.id = tnode.acl_id + where + tnode.protocol = tentry.protocol and + tnode.identifier = tentry.identifier and + tnode.uuid = tentry.uuid + ); +update T_access_control_entry tentry + set + tentry.permission_id = + ( + select + tpermission.id + from T_permission tpermission + where + tpermission.type_qname = CONCAT('{', tentry.typeUri, '}', tentry.typeName) and + tpermission.name = tentry.name + ); +update T_access_control_entry tentry + set + tentry.authority_id = + ( + select + tauthority.recipient + from T_authority tauthority + where + tauthority.recipient = tentry.recipient + ); +delete from T_access_control_list where id not in (select distinct(acl_id) id from t_access_control_entry where acl_id is not null); +delete from T_access_control_entry where acl_id is null; +update T_node set acl_id = null where acl_id not in (select id from t_access_control_list); + +-- +-- Create New schema (MySQL) +-- + +SET FOREIGN_KEY_CHECKS = 0; + +DROP TABLE child_assoc; +DROP TABLE node_assoc; +DROP TABLE node_properties; +DROP TABLE node_aspects; +DROP TABLE node; +DROP TABLE node_status; +DROP TABLE version_count; +DROP TABLE store; +DROP TABLE node_perm_entry; +DROP TABLE node_permission; +DROP TABLE permission_ref; +DROP TABLE recipient; +DROP TABLE externalKeys; + +CREATE TABLE `access_control_entry` ( + `id` bigint(20) NOT NULL auto_increment, + `acl_id` bigint(20) NOT NULL, + `permission_id` bigint(20) NOT NULL, + `authority_id` varchar(100) NOT NULL, + `allowed` bit(1) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `acl_id` (`acl_id`,`permission_id`,`authority_id`), + KEY `FKF064DF7560601995` (`permission_id`), + KEY `FKF064DF75B25A50BF` (`authority_id`), + KEY `FKF064DF75B9553F6C` (`acl_id`), + CONSTRAINT `FKF064DF75B9553F6C` FOREIGN KEY (`acl_id`) REFERENCES `access_control_list` (`id`), + CONSTRAINT `FKF064DF7560601995` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`), + CONSTRAINT `FKF064DF75B25A50BF` FOREIGN KEY (`authority_id`) REFERENCES `authority` (`recipient`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `access_control_list` ( + `id` bigint(20) NOT NULL auto_increment, + `inherits` bit(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `auth_ext_keys` ( + `id` varchar(100) NOT NULL, + `externalKey` varchar(100) NOT NULL, + PRIMARY KEY (`id`,`externalKey`), + KEY `FK31D3BA097B7FDE43` (`id`), + CONSTRAINT `FK31D3BA097B7FDE43` FOREIGN KEY (`id`) REFERENCES `authority` (`recipient`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `authority` ( + `recipient` varchar(100) NOT NULL, + PRIMARY KEY (`recipient`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `child_assoc` ( + `id` bigint(20) NOT NULL auto_increment, + `parent_node_id` bigint(20) default NULL, + `child_node_id` bigint(20) default NULL, + `type_qname` varchar(255) NOT NULL, + `qname` varchar(255) NOT NULL, + `is_primary` bit(1) default NULL, + `assoc_index` int(11) default NULL, + PRIMARY KEY (`id`), + KEY `FKFFC5468E74173FF4` (`child_node_id`), + KEY `FKFFC5468E8E50E582` (`parent_node_id`), + CONSTRAINT `FKFFC5468E8E50E582` FOREIGN KEY (`parent_node_id`) REFERENCES `node` (`id`), + CONSTRAINT `FKFFC5468E74173FF4` FOREIGN KEY (`child_node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +FKFFC5468E74173FF4 + +CREATE TABLE `node` ( + `id` bigint(20) NOT NULL auto_increment, + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `uuid` varchar(36) NOT NULL, + `type_qname` varchar(255) NOT NULL, + `acl_id` bigint(20) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `protocol` (`protocol`,`identifier`,`uuid`), + KEY `FK33AE02D24ADD25` (`protocol`,`identifier`), + CONSTRAINT `FK33AE02D24ADD25` FOREIGN KEY (`protocol`, `identifier`) REFERENCES `store` (`protocol`, `identifier`), + CONSTRAINT `FK33AE02B9553F6C` FOREIGN KEY (`acl_id`) REFERENCES `access_control_list` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `node_aspects` ( + `node_id` bigint(20) NOT NULL, + `qname` varchar(200) default NULL, + KEY `FK2B91A9DE7F2C8017` (`node_id`), + CONSTRAINT `FK2B91A9DE7F2C8017` FOREIGN KEY (`node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `node_assoc` ( + `id` bigint(20) NOT NULL auto_increment, + `source_node_id` bigint(20) default NULL, + `target_node_id` bigint(20) default NULL, + `type_qname` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + KEY `FK5BAEF398B69C43F3` (`source_node_id`), + KEY `FK5BAEF398A8FC7769` (`target_node_id`), + CONSTRAINT `FK5BAEF398A8FC7769` FOREIGN KEY (`target_node_id`) REFERENCES `node` (`id`), + CONSTRAINT `FK5BAEF398B69C43F3` FOREIGN KEY (`source_node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `node_properties` ( + `node_id` bigint(20) NOT NULL, + `actual_type` varchar(15) NOT NULL, + `multi_valued` bit(1) NOT NULL, + `persisted_type` varchar(15) NOT NULL, + `boolean_value` bit(1) default NULL, + `long_value` bigint(20) default NULL, + `float_value` float default NULL, + `double_value` double default NULL, + `string_value` text, + `serializable_value` blob, + `qname` varchar(200) NOT NULL, + PRIMARY KEY (`node_id`,`qname`), + KEY `FKC962BF907F2C8017` (`node_id`), + CONSTRAINT `FKC962BF907F2C8017` FOREIGN KEY (`node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `node_status` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `guid` varchar(36) NOT NULL, + `node_id` bigint(20) default NULL, + `change_txn_id` varchar(56) NOT NULL, + PRIMARY KEY (`protocol`,`identifier`,`guid`), + KEY `FK38ECB8CF7F2C8017` (`node_id`), + CONSTRAINT `FK38ECB8CF7F2C8017` FOREIGN KEY (`node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `permission` ( + `id` bigint(20) NOT NULL auto_increment, + `type_qname` varchar(200) NOT NULL, + `name` varchar(100) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `type_qname` (`type_qname`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `store` ( + `protocol` varchar(50) NOT NULL, + `identifier` varchar(100) NOT NULL, + `root_node_id` bigint(20) default NULL, + PRIMARY KEY (`protocol`,`identifier`), + KEY `FK68AF8E122DBA5BA` (`root_node_id`), + CONSTRAINT `FK68AF8E122DBA5BA` FOREIGN KEY (`root_node_id`) REFERENCES `node` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `version_count` ( + `protocol` varchar(100) NOT NULL, + `identifier` varchar(100) NOT NULL, + `version_count` int(11) NOT NULL, + PRIMARY KEY (`protocol`,`identifier`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +-- Copy data into new schema +-- + +insert into store + ( + protocol, identifier, root_node_id + ) + select + protocol, identifier, root_node_id + from + T_store; + +insert into node + ( + id, protocol, identifier, uuid, type_qname, acl_id + ) + select + id, protocol, identifier, uuid, type_qname, acl_id + from + T_node; + +insert into version_count + ( + protocol, identifier, version_count + ) + select + protocol, identifier, version_count + from + T_version_count; + +insert into node_status + ( + protocol, identifier, guid, node_id, change_txn_id + ) + select + protocol, identifier, guid, node_id, change_txn_id + from + T_node_status; + +insert into node_properties + ( + node_id, actual_type, multi_valued, persisted_type, + boolean_value, long_value, float_value, double_value, string_value, serializable_value, qname + ) + select + node_id, actual_type, multi_valued, persisted_type, + boolean_value, long_value, float_value, double_value, string_value, serializable_value, qname + from + T_node_properties; + +insert into node_aspects + ( + node_id, qname + ) + select + node_id, qname + from + T_node_aspects; + +insert into child_assoc + ( + id, parent_node_id, child_node_id, type_qname, qname, is_primary, assoc_index + ) + select + id, parent_node_id, child_node_id, type_qname, qname, is_primary, assoc_index + from + T_child_assoc; + +insert into node_assoc + ( + id, source_node_id, target_node_id, type_qname + ) + select + id, source_node_id, target_node_id, type_qname + from + T_node_assoc; + +insert into permission + ( + id, type_qname, name + ) + select + id, type_qname, name + from + T_permission; + +insert into access_control_list + ( + id, inherits + ) + select + id, inherits + from + T_access_control_list; + +insert into auth_ext_keys + ( + id, externalKey + ) + select + id, externalKey + from + T_auth_ext_keys; + +insert into authority + ( + recipient + ) + select + recipient + from + T_authority; + +insert into access_control_entry + ( + id, acl_id, permission_id, authority_id, allowed + ) + select + id, acl_id, permission_id, authority_id, allowed + from + T_access_control_entry; + +SET FOREIGN_KEY_CHECKS = 1; + + +-- Allow longer patch identifiers + +ALTER TABLE applied_patch MODIFY id varchar(64) not null; \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaMigrate-1.3.sql b/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaMigrate-1.3.sql new file mode 100644 index 0000000000..d864606e25 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaMigrate-1.3.sql @@ -0,0 +1,636 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.2.1 to V1.3 +-- +-- For Oracle. +-- +-- Note: This script does not create a temporary +-- properties table. It updates the existing +-- table as it is not possible to insert..select +-- long raw columns in Oracle. +-- +-- Author: David Caruana +-- ------------------------------------------------------ + +-- +-- Create temporary 1.3 schema +-- + +CREATE TABLE T_access_control_entry ( + id number(19,0) NOT NULL, + protocol varchar2(50) default NULL, + identifier varchar2(100) default NULL, + uuid varchar2(36) default NULL, + typeUri varchar2(100) default NULL, + typeName varchar2(100) default NULL, + name varchar2(100) default NULL, + recipient varchar2(100) default NULL, + acl_id number(19, 0), + permission_id number(19, 0), + authority_id varchar2(100), + allowed number(1, 0) NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX IDX_ACE_REF ON T_access_control_entry (protocol, identifier, uuid); + +CREATE TABLE T_access_control_list +( + id number(19,0) not null, + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + uuid varchar2(36) NOT NULL, + inherits number(1,0) NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX IDX_ACL_REF ON T_access_control_list (protocol, identifier, uuid); + +create table T_auth_ext_keys +( + id varchar2(100) not null, + externalKey varchar2(100) not null, + primary key (id, externalKey) +); + +create table T_authority +( + recipient varchar2(100) not null, + primary key (recipient) +); + +CREATE TABLE T_child_assoc +( + id number(19,0) NOT NULL, + parent_node_id number(19,0) default NULL, + parent_protocol varchar(50) default NULL, + parent_identifier varchar(100) default NULL, + parent_uuid varchar(36) default NULL, + child_node_id number(19,0) default NULL, + child_protocol varchar(50) default NULL, + child_identifier varchar(100) default NULL, + child_uuid varchar(36) default NULL, + type_qname varchar(255) NOT NULL, + qname varchar(255) NOT NULL, + is_primary number(1,0) default NULL, + assoc_index number(10,0) default NULL, + PRIMARY KEY (id) +); +CREATE INDEX IDX_CA_PARENT ON T_child_assoc(parent_protocol, parent_identifier, parent_uuid); +CREATE INDEX IDX_CA_CHILD ON T_child_assoc(child_protocol, child_identifier, child_uuid); + +CREATE TABLE T_node +( + id number(19,0) NOT NULL, + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + uuid varchar2(36) NOT NULL, + acl_id number(19,0) default NULL, + type_qname varchar2(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX IDX_NODE_REF ON T_node(protocol, identifier, uuid); + +CREATE TABLE T_node_aspects +( + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + uuid varchar2(36) NOT NULL, + node_id number(19,0), + qname varchar2(200) default NULL +); +CREATE INDEX IDX_ASPECTS_REF ON T_node_aspects(protocol, identifier, uuid); + +CREATE TABLE T_node_assoc +( + id number(19,0) NOT NULL, + source_node_id number(19,0) default NULL, + source_protocol varchar2(50) default NULL, + source_identifier varchar2(100) default NULL, + source_uuid varchar2(36) default NULL, + target_node_id number(19,0) default NULL, + target_protocol varchar2(50) default NULL, + target_identifier varchar2(100) default NULL, + target_uuid varchar2(36) default NULL, + type_qname varchar2(255) NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX IDX_NA_SOURCE on T_node_assoc(source_protocol, source_identifier, source_uuid); +CREATE INDEX IDX_NA_TARGET on T_node_assoc(target_protocol, target_identifier, target_uuid); + +CREATE TABLE T_node_status +( + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + guid varchar2(36) NOT NULL, + node_id number(19,0) default NULL, + change_txn_id varchar2(56) NOT NULL, + deleted number(1,0) NOT NULL, + primary key (protocol, identifier, guid) +); + +CREATE TABLE T_permission +( + id number(19,0) NOT NULL, + type_qname varchar2(200) NOT NULL, + name varchar2(100) NOT NULL, + PRIMARY KEY (id), + unique (type_qname, name) +); + +CREATE TABLE T_store +( + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + root_node_id number(19,0) default NULL, + primary key (protocol, identifier) +); + +CREATE TABLE T_version_count +( + protocol varchar2(50) NOT NULL, + identifier varchar2(100) NOT NULL, + version_count number(10,0) NOT NULL, + primary key (protocol, identifier) +); + +create sequence hibernate_sequence; + + +-- +-- Copy data from old tables to intermediate tables +-- + +insert into T_store (protocol, identifier) + select protocol, identifier from store; + +insert into T_node (id, protocol, identifier, uuid, type_qname) + select hibernate_sequence.nextval, protocol, identifier, guid, type_qname from node; + +update T_store tstore set root_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tstore.protocol and + tnode.identifier = tstore.identifier and + tnode.uuid = + (select ostore.root_guid from store ostore where + ostore.protocol = tstore.protocol and + ostore.identifier = tstore.identifier + ) + ); + +insert into t_version_count (protocol, identifier, version_count) + select protocol, identifier, version_count from version_count; + +insert into t_node_status (protocol, identifier, guid, change_txn_id, deleted) + select protocol, identifier, guid, change_txn_id, deleted from node_status; +update T_node_status tstatus set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tstatus.protocol and + tnode.identifier = tstatus.identifier and + tnode.uuid = tstatus.guid + ); + + +insert into T_node_aspects + ( + protocol, identifier, uuid, qname + ) + select + protocol, identifier, guid, qname + from node_aspects; +update T_node_aspects taspects set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = taspects.protocol and + tnode.identifier = taspects.identifier and + tnode.uuid = taspects.uuid + ); + +insert into T_child_assoc + ( + id, parent_protocol, parent_identifier, parent_uuid, + child_protocol, child_identifier, child_uuid, + type_qname, qname, is_primary, assoc_index + ) + select + hibernate_sequence.nextval, parent_protocol, parent_identifier, parent_guid, + child_protocol, child_identifier, child_guid, + type_qname, qname, isPrimary, assoc_index + from + child_assoc; +update T_child_assoc tassoc set parent_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.parent_protocol and + tnode.identifier = tassoc.parent_identifier and + tnode.uuid = tassoc.parent_uuid + ); +update T_child_assoc tassoc set child_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.child_protocol and + tnode.identifier = tassoc.child_identifier and + tnode.uuid = tassoc.child_uuid + ); + +insert into T_node_assoc + ( + id, source_protocol, source_identifier, source_uuid, + target_protocol, target_identifier, target_uuid, + type_qname + ) + select + hibernate_sequence.nextval, source_protocol, source_identifier, source_guid, + target_protocol, target_identifier, target_guid, + type_qname + from + node_assoc; +update T_node_assoc tassoc set source_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.source_protocol and + tnode.identifier = tassoc.source_identifier and + tnode.uuid = tassoc.source_uuid + ); +update T_node_assoc tassoc set target_node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tassoc.target_protocol and + tnode.identifier = tassoc.target_identifier and + tnode.uuid = tassoc.target_uuid + ); + +insert into T_permission + ( + id, type_qname, name + ) + select + hibernate_sequence.nextval, '{' || type_uri || '}' || type_name, name + from + permission_ref; + +insert into T_access_control_list + ( + id, protocol, identifier, uuid, inherits + ) + select + hibernate_sequence.nextval, protocol, identifier, guid, inherits + from node_permission; +update T_node tnode set acl_id = + (select tacl.id from T_access_control_list tacl where + tacl.protocol = tnode.protocol and + tacl.identifier = tnode.identifier and + tacl.uuid = tnode.uuid + ); + +insert into T_auth_ext_keys + ( + id, externalKey + ) + select + id, externalKey + from + externalkeys; + +insert into T_authority + ( + recipient + ) + select + recipient + from + recipient; + +insert into T_access_control_entry + ( + id, protocol, identifier, uuid, + typeUri, typeName, name, + recipient, + allowed + ) + select + hibernate_sequence.nextval, e.protocol, e.identifier, e.guid, + e.typeUri, e.typeName, e.name, + e.recipient, + e.allowed + from node_perm_entry e join t_node n on e.protocol = n.protocol and e.identifier = n.identifier and e.guid = n.uuid + ; + +update T_access_control_entry tentry + set + acl_id = + ( + select + tacl.id + from T_access_control_list tacl + join T_node tnode on tacl.id = tnode.acl_id + where + tnode.protocol = tentry.protocol and + tnode.identifier = tentry.identifier and + tnode.uuid = tentry.uuid + ); +update T_access_control_entry tentry + set + tentry.permission_id = + ( + select + tpermission.id + from T_permission tpermission + where + tpermission.type_qname = '{' || tentry.typeUri || '}' || tentry.typeName and + tpermission.name = tentry.name + ); +update T_access_control_entry tentry + set + tentry.authority_id = + ( + select + tauthority.recipient + from T_authority tauthority + where + tauthority.recipient = tentry.recipient + ); +delete from T_access_control_list where id not in (select distinct(acl_id) id from t_access_control_entry where acl_id is not null); +delete from T_access_control_entry where acl_id is null; +update T_node set acl_id = null where acl_id not in (select id from t_access_control_list); + +-- +-- Create New schema (Oracle) +-- + +DROP TABLE child_assoc cascade constraints; +DROP TABLE node_assoc cascade constraints; +DROP TABLE node_aspects cascade constraints; +DROP TABLE node cascade constraints; +DROP TABLE node_status cascade constraints; +DROP TABLE version_count cascade constraints; +DROP TABLE store cascade constraints; +DROP TABLE node_perm_entry cascade constraints; +DROP TABLE node_permission cascade constraints; +DROP TABLE permission_ref cascade constraints; +DROP TABLE recipient cascade constraints; +DROP TABLE externalKeys cascade constraints; + +create table access_control_entry +( + id number(19,0) not null, + acl_id number(19,0) not null, + permission_id number(19,0) not null, + authority_id varchar2(100) not null, + allowed number(1,0) not null, + primary key (id), + unique (acl_id, permission_id, authority_id) +); + +create table access_control_list +( + id number(19,0) not null, + inherits number(1,0) not null, + primary key (id) +); + +create table auth_ext_keys +( + id varchar2(100) not null, + externalKey varchar2(100) not null, + primary key (id, externalKey) +); + +create table authority +( + recipient varchar2(100) not null, + primary key (recipient) +); + +create table child_assoc +( + id number(19,0) not null, + parent_node_id number(19,0), + child_node_id number(19,0), + type_qname varchar2(255) not null, + qname varchar2(255) not null, + is_primary number(1,0), + assoc_index number(10,0), + primary key (id) +); + +create table node +( + id number(19,0) not null, + protocol varchar2(50) not null, + identifier varchar2(100) not null, + uuid varchar2(36) not null, + type_qname varchar2(255) not null, + acl_id number(19,0), + primary key (id), + unique (protocol, identifier, uuid) +); + +create table node_aspects +( + node_id number(19,0) not null, + qname varchar2(200) +); + +create table node_assoc +( + id number(19,0) not null, + source_node_id number(19,0), + target_node_id number(19,0), + type_qname varchar2(255) not null, + primary key (id) +); + +create table node_status +( + protocol varchar2(50) not null, + identifier varchar2(100) not null, + guid varchar2(36) not null, + node_id number(19,0), + change_txn_id varchar2(56) not null, + primary key (protocol, identifier, guid) +); + +create table permission +( + id number(19,0) not null, + type_qname varchar2(200) not null, + name varchar2(100) not null, + primary key (id), + unique (type_qname, name) +); + +create table store +( + protocol varchar2(50) not null, + identifier varchar2(100) not null, + root_node_id number(19,0), + primary key (protocol, identifier) +); + +create table version_count +( + protocol varchar2(100) not null, + identifier varchar2(100) not null, + version_count number(10,0) not null, + primary key (protocol, identifier) +); + + +-- +-- Copy data into new schema +-- + +insert into store + ( + protocol, identifier, root_node_id + ) + select + protocol, identifier, root_node_id + from + T_store; + +insert into node + ( + id, protocol, identifier, uuid, type_qname, acl_id + ) + select + id, protocol, identifier, uuid, type_qname, acl_id + from + T_node; + +insert into version_count + ( + protocol, identifier, version_count + ) + select + protocol, identifier, version_count + from + T_version_count; + +insert into node_status + ( + protocol, identifier, guid, node_id, change_txn_id + ) + select + protocol, identifier, guid, node_id, change_txn_id + from + T_node_status; + + +alter table node_properties add (node_id number(19,0)); + +update node_properties tproperties set node_id = + (select tnode.id from T_node tnode where + tnode.protocol = tproperties.protocol and + tnode.identifier = tproperties.identifier and + tnode.uuid = tproperties.guid + ); + +alter table node_properties modify (node_id number(19,0) not null); +alter table node_properties drop primary key; +alter table node_properties add primary key (node_id, qname); +alter table node_properties drop column protocol; +alter table node_properties drop column identifier; +alter table node_properties drop column guid; + + +insert into node_aspects + ( + node_id, qname + ) + select + node_id, qname + from + T_node_aspects; + +insert into child_assoc + ( + id, parent_node_id, child_node_id, type_qname, qname, is_primary, assoc_index + ) + select + id, parent_node_id, child_node_id, type_qname, qname, is_primary, assoc_index + from + T_child_assoc; + +insert into node_assoc + ( + id, source_node_id, target_node_id, type_qname + ) + select + id, source_node_id, target_node_id, type_qname + from + T_node_assoc; + +insert into permission + ( + id, type_qname, name + ) + select + id, type_qname, name + from + T_permission; + +insert into access_control_list + ( + id, inherits + ) + select + id, inherits + from + T_access_control_list; + +insert into auth_ext_keys + ( + id, externalKey + ) + select + id, externalKey + from + T_auth_ext_keys; + +insert into authority + ( + recipient + ) + select + recipient + from + T_authority; + +insert into access_control_entry + ( + id, acl_id, permission_id, authority_id, allowed + ) + select + id, acl_id, permission_id, authority_id, allowed + from + T_access_control_entry; + + +-- Enable constraints + +alter table access_control_entry add constraint FKF064DF7560601995 foreign key (permission_id) references permission; +alter table access_control_entry add constraint FKF064DF75B25A50BF foreign key (authority_id) references authority; +alter table access_control_entry add constraint FKF064DF75B9553F6C foreign key (acl_id) references access_control_list; +alter table auth_ext_keys add constraint FK31D3BA097B7FDE43 foreign key (id) references authority; +alter table child_assoc add constraint FKC6EFFF3274173FF4 foreign key (child_node_id) references node; +alter table child_assoc add constraint FKC6EFFF328E50E582 foreign key (parent_node_id) references node; +alter table node add constraint FK33AE02B9553F6C foreign key (acl_id) references access_control_list; +alter table node add constraint FK33AE02D24ADD25 foreign key (protocol, identifier) references store; +alter table node_properties add constraint FKC962BF907F2C8017 foreign key (node_id) references node; +alter table node_aspects add constraint FK2B91A9DE7F2C8017 foreign key (node_id) references node; +alter table node_assoc add constraint FK5BAEF398B69C43F3 foreign key (source_node_id) references node; +alter table node_assoc add constraint FK5BAEF398A8FC7769 foreign key (target_node_id) references node; +alter table node_status add constraint FK38ECB8CF7F2C8017 foreign key (node_id) references node; +alter table store add constraint FK68AF8E122DBA5BA foreign key (root_node_id) references node; + +-- Add additional indexes +CREATE INDEX FKF064DF7560601995 ON access_control_entry (permission_id); +CREATE INDEX FKF064DF75B25A50BF ON access_control_entry (authority_id); +CREATE INDEX FKF064DF75B9553F6C ON access_control_entry (acl_id); +CREATE INDEX FK31D3BA097B7FDE43 ON auth_ext_keys (id); +CREATE INDEX FKC6EFFF3274173FF4 ON child_assoc (child_node_id); +CREATE INDEX FKC6EFFF328E50E582 ON child_assoc (parent_node_id); +CREATE INDEX FK33AE02B9553F6C ON node (acl_id); +CREATE INDEX FK33AE02D24ADD25 ON node (protocol, identifier); +CREATE INDEX FK2B91A9DE7F2C8017 ON node_aspects (node_id); +CREATE INDEX FK5BAEF398B69C43F3 ON node_assoc (source_node_id); +CREATE INDEX FK5BAEF398A8FC7769 ON node_assoc (target_node_id); +CREATE INDEX FKC962BF907F2C8017 ON node_properties (node_id); +CREATE INDEX FK38ECB8CF7F2C8017 ON node_status (node_id); +CREATE INDEX FK68AF8E122DBA5BA ON store (root_node_id); + +ALTER TABLE applied_patch MODIFY id varchar(64); diff --git a/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-1.sql b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-1.sql new file mode 100644 index 0000000000..9b14fec91c --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-1.sql @@ -0,0 +1,75 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.3 to V1.4 Part 1 +-- +-- Adds the columns required to enforce the duplicate name detection +-- +-- Author: Derek Hulley +-- ------------------------------------------------------ + +-- +-- Delete intermediate tables from previous upgrades +-- + +DROP TABLE IF EXISTS T_access_control_entry; +DROP TABLE IF EXISTS T_access_control_list; +DROP TABLE IF EXISTS T_applied_patch; +DROP TABLE IF EXISTS T_auth_ext_keys; +DROP TABLE IF EXISTS T_authority; +DROP TABLE IF EXISTS T_child_assoc; +DROP TABLE IF EXISTS T_node; +DROP TABLE IF EXISTS T_node_aspects; +DROP TABLE IF EXISTS T_node_assoc; +DROP TABLE IF EXISTS T_node_properties; +DROP TABLE IF EXISTS T_node_status; +DROP TABLE IF EXISTS T_permission; +DROP TABLE IF EXISTS T_store; +DROP TABLE IF EXISTS T_version_count; + +-- +-- Unique name constraint +-- + +-- Apply new schema changes to child assoc table +ALTER TABLE child_assoc + ADD COLUMN child_node_name VARCHAR(50) NOT NULL DEFAULT 'V1.4 upgrade' AFTER type_qname, + ADD COLUMN child_node_name_crc bigint(20) NOT NULL DEFAULT -1 AFTER child_node_name; + +UPDATE child_assoc + SET child_node_name_crc = id * -1; + +ALTER TABLE child_assoc + ADD UNIQUE INDEX IDX_CHILD_NAMECRC(parent_node_id, type_qname, child_node_name, child_node_name_crc); + +-- Apply unique index for node associations +ALTER TABLE node_assoc + ADD UNIQUE INDEX IDX_ASSOC(source_node_id, type_qname, target_node_id); + +-- +-- Rename tables to give 'alf_' prefix +-- +ALTER TABLE access_control_entry RENAME TO alf_access_control_entry; +ALTER TABLE access_control_list RENAME TO alf_access_control_list; +ALTER TABLE applied_patch RENAME TO alf_applied_patch; +ALTER TABLE auth_ext_keys RENAME TO alf_auth_ext_keys; +ALTER TABLE authority RENAME TO alf_authority; +ALTER TABLE child_assoc RENAME TO alf_child_assoc; +ALTER TABLE node RENAME TO alf_node; +ALTER TABLE node_aspects RENAME TO alf_node_aspects; +ALTER TABLE node_assoc RENAME TO alf_node_assoc; +ALTER TABLE node_properties RENAME TO alf_node_properties; +ALTER TABLE node_status RENAME TO alf_node_status; +ALTER TABLE permission RENAME TO alf_permission; +ALTER TABLE store RENAME TO alf_store; +ALTER TABLE version_count RENAME TO alf_version_count; + +-- +-- Record script finish +-- +delete from alf_applied_patch where id = 'patch.schemaUpdateScript-V1.4-1'; +insert into alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + values + ( + 'patch.schemaUpdateScript-V1.4-1', 'Manually execute script upgrade V1.4 part 1', + 0, 19, -1, 20, now(), 'UNKOWN', 1, 1, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-2.sql b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-2.sql new file mode 100644 index 0000000000..9a3c77833f --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-1.4-2.sql @@ -0,0 +1,59 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.3 to V1.4 Part 2 +-- +-- Adds the alf_transaction and alf_server tables to keep track of the sources +-- of transactions. +-- +-- Author: Derek Hulley +-- ------------------------------------------------------ + +-- +-- Create server and transaction tables +-- + +CREATE TABLE alf_server ( + id bigint(20) NOT NULL auto_increment, + ip_address varchar(15) NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY ip_address (ip_address) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +insert into alf_server (id, ip_address) values (0, '0.0.0.0'); + +CREATE TABLE alf_transaction ( + id bigint(20) NOT NULL auto_increment, + server_id bigint(20) default NULL, + change_txn_id varchar(56) NOT NULL, + PRIMARY KEY (id), + KEY FKB8761A3A9AE340B7 (server_id), + KEY IDX_CHANGE_TXN (change_txn_id), + CONSTRAINT FKB8761A3A9AE340B7 FOREIGN KEY (server_id) REFERENCES alf_server (id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +insert into alf_transaction + ( + server_id, change_txn_id + ) + select (select max(id) from alf_server), change_txn_id from alf_node_status group by change_txn_id; + +-- Alter node status +ALTER TABLE alf_node_status + ADD COLUMN transaction_id bigint(20) NOT NULL DEFAULT 0 AFTER node_id; +-- Update FK column +UPDATE alf_node_status ns SET ns.transaction_id = + ( + select t.id from alf_transaction t where t.change_txn_id = ns.change_txn_id + ); +ALTER TABLE alf_node_status + DROP COLUMN change_txn_id, + ADD CONSTRAINT FK71C2002B9E57C13D FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id); + +-- +-- Record script finish +-- +delete from alf_applied_patch where id = 'patch.schemaUpdateScript-V1.4-2'; +insert into alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + values + ( + 'patch.schemaUpdateScript-V1.4-2', 'Manually execute script upgrade V1.4 part 2', + 0, 20, -1, 21, now(), 'UNKOWN', 1, 1, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/domain/hibernate-cfg.properties b/config/alfresco/domain/hibernate-cfg.properties index 8c37e5334d..8fe1d521d4 100644 --- a/config/alfresco/domain/hibernate-cfg.properties +++ b/config/alfresco/domain/hibernate-cfg.properties @@ -1,10 +1,11 @@ # # Hibernate configuration # -hibernate.jdbc.use_streams_for_binary=true hibernate.dialect=org.hibernate.dialect.MySQLInnoDBDialect -hibernate.show_sql=false + +hibernate.jdbc.use_streams_for_binary=true hibernate.hbm2ddl.auto=update +hibernate.show_sql=false hibernate.cache.use_query_cache=true hibernate.max_fetch_depth=10 hibernate.cache.provider_class=org.alfresco.repo.cache.InternalEhCacheManagerFactoryBean diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml index 0ed693e163..84eeb38518 100644 --- a/config/alfresco/ehcache-default.xml +++ b/config/alfresco/ehcache-default.xml @@ -1,189 +1,144 @@ + + + > + + + /> + /> - - - - - - + /> + /> + /> + /> + /> + + + + + \ No newline at end of file diff --git a/config/alfresco/extension/custom-db-connection.properties.sample b/config/alfresco/extension/custom-db-connection.properties.sample index d09219b796..175328c81f 100644 --- a/config/alfresco/extension/custom-db-connection.properties.sample +++ b/config/alfresco/extension/custom-db-connection.properties.sample @@ -2,6 +2,7 @@ # Sample database connection properties # +#db.schema.update=true #db.username=alfresco #db.password=alfresco #db.pool.initial=10 diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 7f072ad138..ae6a3f0087 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -30,6 +30,9 @@ + + false + @@ -38,10 +41,15 @@ org/alfresco/repo/domain/hibernate/Node.hbm.xml org/alfresco/repo/domain/hibernate/Store.hbm.xml + org/alfresco/repo/domain/hibernate/Transaction.hbm.xml org/alfresco/repo/domain/hibernate/VersionCount.hbm.xml org/alfresco/repo/domain/hibernate/AppliedPatch.hbm.xml org/alfresco/repo/domain/hibernate/Permission.hbm.xml org/alfresco/repo/avm/hibernate/AVM.hbm.xml + + + + org/alfresco/repo/audit/hibernate/Audit.hbm.xml @@ -147,6 +155,8 @@ ${cache.strategy} ${cache.strategy} ${cache.strategy} + ${cache.strategy} + ${cache.strategy} ${cache.strategy} ${cache.strategy} @@ -154,6 +164,10 @@ ${cache.strategy} ${cache.strategy} ${cache.strategy} + + ${cache.strategy} + ${cache.strategy} + ${cache.strategy} @@ -197,21 +211,24 @@ - + + + + org.alfresco.repo.node.db.NodeDaoService @@ -226,4 +243,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/index-recovery-context.xml b/config/alfresco/index-recovery-context.xml index bad12938ae..31d9566062 100644 --- a/config/alfresco/index-recovery-context.xml +++ b/config/alfresco/index-recovery-context.xml @@ -22,17 +22,11 @@ - - - workspace://SpacesStore - workspace://lightWeightVersionStore - user://alfrescoUserStore - - + false @@ -47,4 +41,43 @@ + + + + \ No newline at end of file diff --git a/config/alfresco/messages/action-config.properties b/config/alfresco/messages/action-config.properties index 536d7e0608..5e796af107 100644 --- a/config/alfresco/messages/action-config.properties +++ b/config/alfresco/messages/action-config.properties @@ -74,10 +74,13 @@ export.generic.package.description=Alfresco Repository export. export.package.error=Failed to find temporary file for export script.title=Execute a script -script.description=Execute a JavaScript file to perform tasks such as creating new files or folders +script.description=Execute a JavaScript file to perform tasks such as creating new files or folders. + +counter.title=Increment Counter +counter.counter=Increment the counter property for the item. execute-all-rules.title=Execute all rules -execute-all-rules.description=Execute all rules on the child items +execute-all-rules.description=Execute all rules on the child items. start-workflow.title=Start Workflow start-workflow.description=This will start a workflow for the matched items. diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index d98071a699..c2a5bf70ef 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -16,6 +16,9 @@ patch.general.property_not_set=Patch property ''{0}'' has not been set on this p # Individual patch messages +patch.marker.description=Marker patch to record installations and upgrades +patch.marker.result=Marker patch applied + patch.savedSearchesFolder.description=Ensures the existence of the 'Saved Searches' folder. patch.savedSearchesFolder.result.exists=The saved searches folder already exists: {0} patch.savedSearchesFolder.result.created=The saved searches folder was successfully created: {0} diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties new file mode 100644 index 0000000000..cbf989adc2 --- /dev/null +++ b/config/alfresco/messages/schema-update.properties @@ -0,0 +1,8 @@ +# Schema update messages + +schema.update.msg.executing_script=Executing schema upgrade script: {0} +schema.update.err.update_failed=Schema auto-update failed +schema.update.err.validation_failed=Schema validation failed +schema.update.err.update_script_not_run=The following schema upgrade script needs to be executed manually: {0} +schema.update.err.script_not_found=The schema script could not be found at location {0} +schema.update.err.statement_terminator=Scripts must terminate all statements with '';'' (line {0} of {1}). \ No newline at end of file diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml index ae488986f9..f916388c92 100644 --- a/config/alfresco/model/contentModel.xml +++ b/config/alfresco/model/contentModel.xml @@ -526,6 +526,9 @@ d:int + + d:int + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 0241ae4919..dda2c969e9 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -441,12 +441,12 @@ patch.schemaUpdateScript-V1.4-1 - patch.patch.schemaUpgradeScriptPatch.description + patch.schemaUpgradeScript.description 0 19 20 - - AlfrescoSchemaUpdate-1.4-1-xxx.sql + + classpath:alfresco/dbscripts/upgrade/1.4/${db.script.dialect}/AlfrescoSchemaUpdate-1.4-1.sql @@ -472,5 +472,21 @@ + + patch.schemaUpdateScript-V1.4-2 + patch.schemaUpgradeScript.description + 0 + 20 + 21 + + classpath:alfresco/dbscripts/upgrade/1.4/${db.script.dialect}/AlfrescoSchemaUpdate-1.4-2.sql + + + + + + + + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index f46ce74a31..775a8f637d 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -2,85 +2,100 @@ - - + + - - http://www.alfresco.org - + + http://www.alfresco.org + - - + - + - - org.alfresco.service.ServiceRegistry - - - - - - - - - + + org.alfresco.service.ServiceRegistry + + + + + + + + + - + - - org.alfresco.service.ServiceRegistry - - - Repository service registry - + + org.alfresco.service.ServiceRegistry + + + Repository service registry + - + - + - + - - org.alfresco.service.descriptor.DescriptorService - - - - - - - - + + org.alfresco.service.descriptor.DescriptorService + + + + + + + + + + - + - - org.alfresco.service.descriptor.DescriptorService - - - Descriptor service - + + org.alfresco.service.descriptor.DescriptorService + + + Descriptor service + - - + + + + + + + + + false + + + + - + - - org.alfresco.service.namespace.NamespaceService - - - - - - - - - - + + org.alfresco.service.namespace.NamespaceService + + + + + + + + + + + + + @@ -93,39 +108,40 @@ - + - - org.alfresco.service.namespace.NamespaceService - - - Namespace service - + + org.alfresco.service.namespace.NamespaceService + + + Namespace service + - - + - - - - + + + - + - - org.alfresco.service.cmr.dictionary.DictionaryService - - - - - - - - - - + + org.alfresco.service.cmr.dictionary.DictionaryService + + + + + + + + + + + + + - + @@ -136,34 +152,39 @@ - + - - org.alfresco.service.cmr.dictionary.DictionaryService - - - Dictionary Service - + + org.alfresco.service.cmr.dictionary.DictionaryService + + + Dictionary Service + - - + - + - - org.alfresco.service.cmr.repository.NodeService - - - - - - - - - - + + + org.alfresco.service.ServiceDescriptor + org.alfresco.service.cmr.repository.NodeService + + + + + + + + + + + + + + - + @@ -177,36 +198,36 @@ - + - - org.alfresco.service.cmr.repository.NodeService - - - Node Service - + + org.alfresco.service.cmr.repository.NodeService + + + Node Service + - - - - + - - org.alfresco.service.cmr.repository.ContentService - - - - - - - - - - + + org.alfresco.service.cmr.repository.ContentService + + + + + + + + + + + + + - + @@ -217,60 +238,64 @@ - + - - org.alfresco.service.cmr.repository.ContentService - - - Content Service - + + org.alfresco.service.cmr.repository.ContentService + + + Content Service + - - + - + - - org.alfresco.service.cmr.repository.MimetypeService - - - - - - - - - + + org.alfresco.service.cmr.repository.MimetypeService + + + + + + + + + + + + - + - - org.alfresco.service.cmr.repository.MimetypeService - - - Mime Type Service - + + org.alfresco.service.cmr.repository.MimetypeService + + + Mime Type Service + - - + - + - - org.alfresco.service.cmr.search.SearchService - - - - - - - - - - + + org.alfresco.service.cmr.search.SearchService + + + + + + + + + + + + + - + @@ -281,34 +306,36 @@ - + - - org.alfresco.service.cmr.search.SearchService - - - Search Service - + + org.alfresco.service.cmr.search.SearchService + + + Search Service + - - + - + - - org.alfresco.service.cmr.search.CategoryService - - - - - - - - - - + + org.alfresco.service.cmr.search.CategoryService + + + + + + + + + + + + + - + @@ -319,34 +346,36 @@ - + - - org.alfresco.service.cmr.search.CategoryService - - - Category Service - + + org.alfresco.service.cmr.search.CategoryService + + + Category Service + - - + - + - - org.alfresco.service.cmr.repository.CopyService - - - - - - - - - - + + org.alfresco.service.cmr.repository.CopyService + + + + + + + + + + + + + - + @@ -357,34 +386,36 @@ - + - - org.alfresco.service.cmr.repository.CopyService - - - Copy Service - + + org.alfresco.service.cmr.repository.CopyService + + + Copy Service + - - + - + - - org.alfresco.service.cmr.lock.LockService - - - - - - - - - - + + org.alfresco.service.cmr.lock.LockService + + + + + + + + + + + + + - + @@ -395,34 +426,36 @@ - + - - org.alfresco.service.cmr.lock.LockService - - - Lock Service - + + org.alfresco.service.cmr.lock.LockService + + + Lock Service + - - + - + - - org.alfresco.service.cmr.version.VersionService - - - - - - - - - - + + org.alfresco.service.cmr.version.VersionService + + + + + + + + + + + + + - + @@ -433,34 +466,36 @@ - + - - org.alfresco.service.cmr.version.VersionService - - - Version Service - + + org.alfresco.service.cmr.version.VersionService + + + Version Service + - - + - + - - org.alfresco.service.cmr.coci.CheckOutCheckInService - - - - - - - - - - + + org.alfresco.service.cmr.coci.CheckOutCheckInService + + + + + + + + + + + + + - + @@ -471,34 +506,36 @@ - + - - org.alfresco.service.cmr.coci.CheckOutCheckInService - - - Version Service - + + org.alfresco.service.cmr.coci.CheckOutCheckInService + + + Version Service + - - + - + - - org.alfresco.service.cmr.rule.RuleService - - - - - - - - - - + + org.alfresco.service.cmr.rule.RuleService + + + + + + + + + + + + + - + @@ -509,34 +546,36 @@ - + - - org.alfresco.service.cmr.rule.RuleService - - - Rule Service - + + org.alfresco.service.cmr.rule.RuleService + + + Rule Service + - - + - + - - org.alfresco.service.cmr.view.ImporterService - - - - - - - - - - + + org.alfresco.service.cmr.view.ImporterService + + + + + + + + + + + + + - + @@ -547,32 +586,34 @@ - + - - org.alfresco.service.cmr.view.ImporterService - - - Importer Service - + + org.alfresco.service.cmr.view.ImporterService + + + Importer Service + - - + - + - - org.alfresco.service.cmr.view.ExporterService - - - - - - - - + + org.alfresco.service.cmr.view.ExporterService + + + + + + + + + + + - + @@ -583,34 +624,36 @@ - + - - org.alfresco.service.cmr.view.ExporterService - - - Exporter Service - + + org.alfresco.service.cmr.view.ExporterService + + + Exporter Service + - - + - + - - org.alfresco.service.cmr.action.ActionService - - - - - - - - - - + + org.alfresco.service.cmr.action.ActionService + + + + + + + + + + + + + - + @@ -623,32 +666,34 @@ - - org.alfresco.service.cmr.action.ActionService - - - Action Service - + + org.alfresco.service.cmr.action.ActionService + + + Action Service + - - + - - org.alfresco.service.cmr.security.PermissionService - - - - - - - - - - + + org.alfresco.service.cmr.security.PermissionService + + + + + + + + + + + + + - + @@ -661,32 +706,34 @@ - - org.alfresco.service.cmr.security.PermissionService - - - Permission Service - + + org.alfresco.service.cmr.security.PermissionService + + + Permission Service + - - - org.alfresco.service.cmr.security.AuthorityService - - - - - - - - - - + + org.alfresco.service.cmr.security.AuthorityService + + + + + + + + + + + + + - + @@ -699,32 +746,34 @@ - - org.alfresco.service.cmr.security.AuthorityService - - - Authority Service - + + org.alfresco.service.cmr.security.AuthorityService + + + Authority Service + - - - org.alfresco.service.cmr.security.OwnableService - - - - - - - - - - + + org.alfresco.service.cmr.security.OwnableService + + + + + + + + + + + + + - + @@ -737,32 +786,34 @@ - - org.alfresco.service.cmr.security.OwnableService - - - OwnableService Service - + + org.alfresco.service.cmr.security.OwnableService + + + OwnableService Service + - - - org.alfresco.service.cmr.security.PersonService - - - - - - - - - - + + org.alfresco.service.cmr.security.PersonService + + + + + + + + + + + + + - + @@ -775,32 +826,34 @@ - - org.alfresco.service.cmr.security.PersonService - - - PersonService Service - + + org.alfresco.service.cmr.security.PersonService + + + PersonService Service + - - - org.alfresco.service.cmr.security.AuthenticationService - - - - - - - - - - + + org.alfresco.service.cmr.security.AuthenticationService + + + + + + + + + + + + + - + @@ -819,32 +872,34 @@ - - org.alfresco.service.cmr.security.AuthenticationService - - - AuthenticationService Service - + + org.alfresco.service.cmr.security.AuthenticationService + + + AuthenticationService Service + - - - org.alfresco.service.cmr.repository.TemplateService - - - - - - - - - - + + org.alfresco.service.cmr.repository.TemplateService + + + + + + + + + + + + + - + @@ -857,32 +912,34 @@ - - org.alfresco.service.cmr.repository.TemplateService - - - TemplateService Service - + + org.alfresco.service.cmr.repository.TemplateService + + + TemplateService Service + - - - org.alfresco.service.cmr.repository.ScriptService - - - - - - - - - - + + org.alfresco.service.cmr.repository.ScriptService + + + + + + + + + + + + + - + @@ -895,31 +952,34 @@ - - org.alfresco.service.cmr.repository.ScriptService - - - ScriptService Service - + + org.alfresco.service.cmr.repository.ScriptService + + + ScriptService Service + - - - org.alfresco.service.cmr.model.FileFolderService - - - + + org.alfresco.service.cmr.model.FileFolderService + + + + + - - - - - + + + + + + + - + @@ -934,43 +994,16 @@ - - - - - - - org.alfresco.service.cmr.workflow.WorkflowService - - - - - - - - - - - - - - - - - ${server.transaction.mode.default} - - - - - - - org.alfresco.service.cmr.workflow.WorkflowService - - - Workflow Service - - + + + org.alfresco.service.cmr.model.FileFolderService + + + FileFolderService Service + + + @@ -982,4 +1015,85 @@ + + + + + org.alfresco.service.cmr.workflow.WorkflowService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.workflow.WorkflowService + + + Workflow Service + + + + + + + + + org.alfresco.service.cmr.audit.AuditService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + org.alfresco.service.cmr.audit.AuditService + + + Audit Service + + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index bbe50d57eb..fcf2b92d31 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -712,4 +712,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 82cdc1ceef..3bcaa68586 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -5,6 +5,8 @@ dir.root=./alf_data dir.contentstore=${dir.root}/contentstore dir.contentstore.deleted=${dir.root}/contentstore.deleted +dir.auditcontentstore=${dir.root}/audit.contentstore + # The location for lucene index files dir.indexes=${dir.root}/lucene-indexes @@ -54,6 +56,7 @@ lucene.lock.poll.interval=100 # Database configuration +db.schema.update=true db.driver=org.gjt.mm.mysql.Driver db.name=alfresco db.url=jdbc:mysql:///${db.name} diff --git a/config/alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl b/config/alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl deleted file mode 100644 index 62957c681d..0000000000 --- a/config/alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl +++ /dev/null @@ -1,42 +0,0 @@ - - - - Alfresco - Copyright (c) 2005 Alfresco Software, Inc. All rights reserved. - <#assign hostname="http://localhost:8080/alfresco"> - <#assign spaceref="${hostname}/navigate/browse/${space.nodeRef.storeRef.protocol}/${space.nodeRef.storeRef.identifier}/${space.nodeRef.id}"> - <#assign datetimeformat="EEE, dd MMM yyyy HH:mm:ss zzz"> - ${spaceref} - Recent Changes to '${space.name}' - en-us - ${date?string(datetimeformat)} - ${date?string(datetimeformat)} - 120 - Alfresco 1.2 - - ${space.name} - 32 - 32 - ${spaceref} - ${hostname}${space.icon32} - - <#assign weekms=1000*60*60*24*7> - <#list space.childrenByXPath[".//*[subtypeOf('cm:content')]"] as child> - <#if (dateCompare(child.properties["cm:modified"], date, weekms) == 1) || (dateCompare(child.properties["cm:created"], date, weekms) == 1)> - - ${child.properties.name} - ${hostname}${child.url} - - ${""?xml}${child.properties.name}${""?xml} - <#if child.properties["cm:description"]?exists && child.properties["cm:description"] != ""> - ${child.properties["cm:description"]} - - - ${child.properties["cm:modified"]?string(datetimeformat)} - ${hostname}${child.url} - - - - - - diff --git a/config/alfresco/templates/content/examples/company_logos.ftl b/config/alfresco/templates/content/examples/company_logos.ftl deleted file mode 100644 index 51befa00b1..0000000000 --- a/config/alfresco/templates/content/examples/company_logos.ftl +++ /dev/null @@ -1,20 +0,0 @@ -<#-- Table of the images found in a folder under Company Home called "Company Logos" --> -<#-- Shows each image found as inline content --> - - <#list companyhome.children as child> - <#if child.isContainer && child.name = "Company Logos"> - <#list child.children as image> - <#switch image.mimetype> - <#case "image/jpeg"> - <#case "image/gif"> - <#case "image/png"> - - - - <#break> - <#default> - - - - -
diff --git a/config/alfresco/templates/content/examples/doc_info.ftl b/config/alfresco/templates/content/examples/doc_info.ftl index 00af7cc5b0..2821edeeee 100644 --- a/config/alfresco/templates/content/examples/doc_info.ftl +++ b/config/alfresco/templates/content/examples/doc_info.ftl @@ -1,18 +1,47 @@ <#-- Shows some general info about the current document, including NodeRef and aspects applied --> -

General document info

<#if document?exists>

Current Document Info:

Name: ${document.name}
Ref: ${document.nodeRef}
Type: ${document.type}
+ DBID: ${document.properties["sys:node-dbid"]}
Content URL: /alfresco${document.url}
Locked: <#if document.isLocked>Yes<#else>No
+ <#if hasAspect(document, "cm:countable") == 1 && document.properties['cm:counter']?exists> + Counter: ${document.properties['cm:counter']}
+ Aspects: <#list document.aspects as aspect>
${aspect}
+ Properties: + + <#-- Get a list of all the property names for the document --> + <#assign props = document.properties?keys> + <#list props as t> + <#-- If the property exists --> + <#if document.properties[t]?exists> + <#-- If it is a date, format it accordingly --> + <#if document.properties[t]?is_date> + + + <#-- If it is a boolean, format it accordingly --> + <#elseif document.properties[t]?is_boolean> + + + <#-- If it is a collection, enumerate it --> + <#elseif document.properties[t]?is_enumerable> + + + <#-- Otherwise treat it as a string --> + <#else> + + + + +
${t} = ${document.properties[t]?datetime}
${t} = ${document.properties[t]?string("yes", "no")}
${t} = <#list document.properties[t] as i>${i}
${t} = ${document.properties[t]}
<#else> No document found! diff --git a/config/alfresco/templates/content/examples/example.ftl b/config/alfresco/templates/content/examples/example.ftl deleted file mode 100644 index 28628aa543..0000000000 --- a/config/alfresco/templates/content/examples/example.ftl +++ /dev/null @@ -1,47 +0,0 @@ -

=====Example Template Start=====

- -Company Home Space: ${companyhome.properties.name} -
-My Home Space: ${userhome.properties.name} -
-Company Home children count: ${companyhome.children?size} -
-Company Home first child node name: ${companyhome.children[0].properties.name} -
-Current Document Name: ${document.name} -
-Current Space Name: ${space.name} - -

List of child spaces in my Home Space:

- -<#list userhome.children as child> - <#if child.isContainer> - - - - - - - -
${child.properties.name} (${child.children?size})Path: ${child.displayPath}
- -

List of docs in my Home Space (text only content shown inline, JPG images shown as thumbnails):

- -<#list userhome.children as child> - <#if child.isDocument> - - <#if child.mimetype = "text/plain"> - - <#elseif child.mimetype = "image/jpeg"> - - - - -
${child.properties.name}
${child.content}
- -

Assoc example:

-<#if userhome.children[0].assocs["cm:contains"]?exists> - ${userhome.children[0].assocs["cm:contains"][0].name} - - -

=====Example Template End=====

diff --git a/config/alfresco/templates/content/examples/my_pressreleases.ftl b/config/alfresco/templates/content/examples/my_pressreleases.ftl deleted file mode 100644 index 1fa8d9c825..0000000000 --- a/config/alfresco/templates/content/examples/my_pressreleases.ftl +++ /dev/null @@ -1,22 +0,0 @@ -<#-- Displays a table of all the documents from a "Press Releases" folder under Company Home --> -<#-- Obviously this folder needs to exist and the docs in it should have the title and description fields set --> - - <#list companyhome.children as child> - <#if child.isContainer && child.name = "Press Releases"> - <#list child.children as doc> - <#if doc.isDocument> - - - - - - - - - - - - - - -

${doc.properties.title}

${doc.properties.description}
${doc.content}
diff --git a/config/alfresco/templates/content/examples/userhome_docs.ftl b/config/alfresco/templates/content/examples/userhome_docs.ftl deleted file mode 100644 index 180fb92aa7..0000000000 --- a/config/alfresco/templates/content/examples/userhome_docs.ftl +++ /dev/null @@ -1,15 +0,0 @@ -<#-- List of docs in the Home Space for current user --> -<#-- If the doc mimetype is plain/text then the content is shown inline --> -<#-- If the doc mimetype is JPEG then the image is shown inline as a small thumbnail image --> - -<#list userhome.children as child> - <#if child.isDocument> - - <#if child.mimetype = "text/plain"> - - <#elseif child.mimetype = "image/jpeg"> - - - - -
${child.properties.name}
${child.content}
diff --git a/config/alfresco/templates/content/examples/xpath_search.ftl b/config/alfresco/templates/content/examples/xpath_search.ftl deleted file mode 100644 index b81f9440d5..0000000000 --- a/config/alfresco/templates/content/examples/xpath_search.ftl +++ /dev/null @@ -1,26 +0,0 @@ -<#-- Shows use of the childByNamePath, childrenByXPath and childrenByLuceneSearch API --> - -

Template Documents in 'Company Home/Data Dictionary/Presentation Templates':

- -<#list companyhome.childByNamePath["Data Dictionary/Presentation Templates"].children as child> - <#if child.isDocument> - - - -
${child.properties.name}
- -

Folders in 'Company Home/Data Dictionary/Space Templates/Software Engineering Project':

- -<#list companyhome.childrenByXPath["*[@cm:name='Data Dictionary']/*[@cm:name='Space Templates']/*[@cm:name='Software Engineering Project']/*"] as child> - <#if child.isContainer> - - - -
${child.properties.name}
- -

Lucene Search - documents containing the text 'Alfresco'

- -<#list companyhome.childrenByLuceneSearch["TEXT:alfresco"] as child> - - -
${child.properties.name}
diff --git a/config/alfresco/templates/content_template_examples.xml b/config/alfresco/templates/content_template_examples.xml index f993d5a5fe..e0ae7574bc 100644 --- a/config/alfresco/templates/content_template_examples.xml +++ b/config/alfresco/templates/content_template_examples.xml @@ -7,8 +7,8 @@ true - Displays basic information about the current document - contentUrl=classpath:alfresco/templates/content/examples/doc_info.ftl|mimetype=text/plain|size=636|encoding=UTF-8 + Displays useful information about the current document + contentUrl=classpath:alfresco/templates/content/examples/doc_info.ftl|mimetype=text/plain|size=1884|encoding=UTF-8 doc_info.ftl doc_info.ftl @@ -84,20 +84,6 @@ - - - - - - - true - Renders a valid RSS2.0 XML document showing the documents in the current space created or modified in the last 7 days. The template should be configured to use the appropriate server and port before use. - contentUrl=classpath:alfresco/templates/content/examples/RSS_2.0_recent_docs.ftl|mimetype=text/plain|size=1917|encoding=UTF-8 - RSS_2.0_recent_docs.ftl - RSS_2.0_recent_docs.ftl - - - @@ -140,18 +126,4 @@ - - - - - - - true - Example of using XPath and Lucene searches within a template. - contentUrl=classpath:alfresco/templates/content/examples/xpath_search.ftl|mimetype=text/plain|size=1109|encoding=UTF-8 - xpath_search.ftl - xpath_search.ftl - - - \ No newline at end of file diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 4daccb2687..87530803c5 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -7,7 +7,7 @@ version.major=1 version.minor=4 version.revision=0 -version.label=Dev +version.label=Preview # Edition label @@ -19,4 +19,4 @@ version.build=@build-number@ # Schema number -version.schema=20 +version.schema=21 diff --git a/source/java/log4j.properties b/source/java/log4j.properties index af5a9b069d..45d5653b5c 100644 --- a/source/java/log4j.properties +++ b/source/java/log4j.properties @@ -6,6 +6,6 @@ log4j.appender.stdout.layout.ConversionPattern=%d %5p %c{1}:%m%n ### Set log levels. log4j.rootLogger=warn, stdout -log4j.logger.org.hibernate=fatal +log4j.logger.org.hibernate=error log4j.logger.org.alfresco.repo.avm=info log4j.logger.org.springframework=warn \ No newline at end of file diff --git a/source/java/org/alfresco/model/ContentModel.java b/source/java/org/alfresco/model/ContentModel.java index 4c53e07d22..72ec93e28b 100644 --- a/source/java/org/alfresco/model/ContentModel.java +++ b/source/java/org/alfresco/model/ContentModel.java @@ -77,6 +77,7 @@ public interface ContentModel static final QName PROP_SYS_VERSION_SCHEMA = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionSchema"); static final QName PROP_SYS_VERSION_EDITION = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "versionEdition"); + // // Content Model Definitions // @@ -189,6 +190,12 @@ public interface ContentModel public static final QName ASPECT_MOUNTED = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mounted"); public static final QName PROP_MOUNTPOINT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "mountpoint"); + // countable aspect + public static final QName ASPECT_COUNTABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "countable"); + public static final QName PROP_HITS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "hits"); + public static final QName PROP_COUNTER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "counter"); + + // // Application Model Definitions // diff --git a/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java b/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java new file mode 100644 index 0000000000..f779787433 --- /dev/null +++ b/source/java/org/alfresco/repo/action/executer/CounterIncrementActionExecuter.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.action.executer; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; + +/** + * Simple action to increment an integer value. The runtime NodeService is used so any user + * can increment the counter value on a node. + * + * @author Kevin Roast + */ +public class CounterIncrementActionExecuter extends ActionExecuterAbstractBase +{ + /** Runtime NodeService with no permissions protection */ + private NodeService nodeService; + + + /** + * @param nodeService The Runtime NodeService to set. + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // add the cm:countable aspect as required + if (this.nodeService.hasAspect(actionedUponNodeRef, ContentModel.ASPECT_COUNTABLE) == false) + { + // set the value to 1 by default + Map props = new HashMap(1); + props.put(ContentModel.PROP_COUNTER, 1); + this.nodeService.addAspect(actionedUponNodeRef, ContentModel.ASPECT_COUNTABLE, props); + } + else + { + // increment the value and handle possibility that no value has been set yet + int resultValue = 1; + Integer value = (Integer)this.nodeService.getProperty(actionedUponNodeRef, ContentModel.PROP_COUNTER); + if (value != null) + { + resultValue = value.intValue() + 1; + } + this.nodeService.setProperty(actionedUponNodeRef, ContentModel.PROP_COUNTER, resultValue); + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + protected void addParameterDefinitions(List paramList) + { + // none required + } +} diff --git a/source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java similarity index 94% rename from source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java rename to source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java index 1815acecd3..2a49156dee 100644 --- a/source/java/org/alfresco/repo/action/executer/ScriptActionExecutor.java +++ b/source/java/org/alfresco/repo/action/executer/ScriptActionExecuter.java @@ -32,9 +32,13 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.PersonService; /** + * Action to execute a JavaScript. The script has access to the default model. + * The actionedUponNodeRef is added to the default model as the 'document' and the owning + * NodeRef is added as the 'space'. + * * @author Kevin Roast */ -public class ScriptActionExecutor extends ActionExecuterAbstractBase +public class ScriptActionExecuter extends ActionExecuterAbstractBase { public static final String NAME = "script"; public static final String PARAM_SCRIPTREF = "script-ref"; diff --git a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java index 12247e4b77..d0f2655d69 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java @@ -60,7 +60,7 @@ public class PatchExecuter implements ApplicationListener { logger.info(I18NUtil.getMessage(MSG_CHECKING)); - Date before = new Date(System.currentTimeMillis() - 20000L); // 20 seconds ago + Date before = new Date(System.currentTimeMillis() - 60000L); // 60 seconds ago patchService.applyOutstandingPatches(); Date after = new Date(System .currentTimeMillis() + 20000L); // 20 seconds ahead diff --git a/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java index 69dfd1e092..a68dc08fec 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/SchemaUpgradeScriptPatch.java @@ -20,7 +20,7 @@ import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.service.cmr.admin.PatchException; /** - * This patch ensures that an upgrade script has been executed. Upgrade scripts + * This patch ensures that an upgrade scriptUrl has been executed. Upgrade scripts * should create an entry for the patch with the required ID and execution status * so that the code in this class is never called. If called, an exception message * is always generated. @@ -31,26 +31,37 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch { private static final String MSG_NOT_EXECUTED = "patch.schemaUpgradeScript.err.not_executed"; - private String scriptName; + private String scriptUrl; public SchemaUpgradeScriptPatch() { } + + /** + * @return Returns the URL of the scriptUrl that has to have been run + */ + public String getScriptUrl() + { + return scriptUrl; + } /** - * Set the name of the upgrade script to execute. + * Set the URL of the upgrade scriptUrl to execute. This is the full URL of the + * file, e.g. classpath:alfresco/patch/scripts/upgrade-1.4/${hibernate.dialect.class}/patchAlfrescoSchemaUpdate-1.4-2.sql + * where the ${hibernate.dialect.class} placeholder will be substituted with the Hibernate + * Dialect as configured for the system. * - * @param scriptName the script filename + * @param scriptUrl the scriptUrl filename */ - public void setScriptName(String scriptName) + public void setScriptUrl(String script) { - this.scriptName = scriptName; + this.scriptUrl = script; } protected void checkProperties() { super.checkProperties(); - checkPropertyNotNull(scriptName, "scriptName"); + checkPropertyNotNull(scriptUrl, "scriptUrl"); } /** @@ -59,6 +70,6 @@ public class SchemaUpgradeScriptPatch extends AbstractPatch @Override protected String applyInternal() throws Exception { - throw new PatchException(MSG_NOT_EXECUTED, scriptName); + throw new PatchException(MSG_NOT_EXECUTED, scriptUrl); } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java index d2525361cd..28a77aadd3 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/UniqueChildNamePatch.java @@ -216,6 +216,9 @@ public class UniqueChildNamePatch extends AbstractPatch writeLine(" Replaced with: " + usedChildName); } } + // clear the session to preserve memory + getSession().flush(); + getSession().clear(); } } diff --git a/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java b/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java new file mode 100644 index 0000000000..496873c201 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/ApplicationAuditModel.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.alfresco.service.cmr.repository.NodeRef; + +public interface ApplicationAuditModel +{ + /** + * Report if audit behaviour can be determined before the method call + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode beforeExecution(AuditMode auditMode, String application, String description, + NodeRef key, Object... args); + + /** + * Report if audit behaviour can be determined after the method call + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode afterExecution(AuditMode auditMode, String application, String description, + NodeRef key, Object... args); + + /** + * Report if audit behaviour should be invoked on error. It could be we look at the error and filter - this is not supported at the moment. + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode onError(AuditMode auditMode, String application, String description, + NodeRef key, Object... args); + + /** + * Get the optional parameters that are to be recorded + * + * @param mi + * @return + */ + public RecordOptions getAuditRecordOptions(String application); +} diff --git a/source/java/org/alfresco/repo/audit/AuditComponent.java b/source/java/org/alfresco/repo/audit/AuditComponent.java new file mode 100644 index 0000000000..3b13c7e5f5 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditComponent.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.aopalliance.intercept.MethodInvocation; + +/** + * The audit component. + * + * Used by the AuditService and AuditMethodInterceptor to insert audit entries. + * + * @author Andy Hind + */ +public interface AuditComponent +{ + /** + * Audit entry point for method interceptors. + * + * @param methodInvocation + */ + public Object audit(MethodInvocation methodInvocation) throws Throwable; + + /** + * + * @param source - + * a string that represents the application + * @param description - + * the audit entry * + * @param key - + * a node ref to use as the key for filtering etc + * @param args - + * an arbitrary list of parameters + */ + public void audit(String source, String description, NodeRef key, Object... args); + + +} diff --git a/source/java/org/alfresco/repo/audit/AuditComponentImpl.java b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java new file mode 100644 index 0000000000..e476907417 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditComponentImpl.java @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Date; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.Auditable; +import org.alfresco.service.NotAuditable; +import org.alfresco.service.cmr.repository.NodeRef; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The default audit component implementation. TODO: Implement before, after and exception filtering. At the moment these filters are ignired. TODO: Respect audit internal - at the + * moment audit internal is fixed to false. + * + * @author Andy Hind + */ +public class AuditComponentImpl implements AuditComponent +{ + /** + * The application name to use for audit entries generated by method interception around public services. + */ + private static final String SYSTEM_APPLICATION = "SystemMethodInterceptor"; + + /** + * Logging + */ + private static Log s_logger = LogFactory.getLog(AuditComponentImpl.class); + + /** + * Suspend resume auditing + */ + private static ThreadLocal auditFlag = new ThreadLocal(); + + /** + * IOC + */ + private PublicServiceIdentifier publicServiceIdentifier; + + private AuditConfiguration auditConfiguration; + + private AuditDAO auditDAO; + + private AuditDAO auditFailedDAO; + + private AuditModel auditModel; + + /** + * Keep hold of the host where the audit occurs. TODO: Check that we get the correct address ... + */ + + private InetAddress auditHost; + + public AuditComponentImpl() + { + super(); + // Initialise the host address + try + { + auditHost = InetAddress.getLocalHost(); + } + catch (UnknownHostException e) + { + s_logger.error("Failed to get local host address", e); + } + } + + /* + * IOC property setters + */ + + public void setAuditDAO(AuditDAO auditDAO) + { + this.auditDAO = auditDAO; + } + + public void setAuditFailedDAO(AuditDAO auditFailedDAO) + { + this.auditFailedDAO = auditFailedDAO; + } + + public void setAuditConfiguration(AuditConfiguration auditConfiguration) + { + this.auditConfiguration = auditConfiguration; + } + + public void setPublicServiceIdentifier(PublicServiceIdentifier publicServiceIdentifier) + { + this.publicServiceIdentifier = publicServiceIdentifier; + } + + public void setAuditModel(AuditModel auditModel) + { + this.auditModel = auditModel; + } + + public Object audit(MethodInvocation mi) throws Throwable + { + if ((auditFlag.get() == null) || (!auditFlag.get().booleanValue())) + { + try + { + auditFlag.set(Boolean.TRUE); + + Method method = mi.getMethod(); + String methodName = method.getName(); + String serviceName = publicServiceIdentifier.getPublicServiceName(mi); + if (method.isAnnotationPresent(Auditable.class)) + { + + if (serviceName != null) + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Auditing - " + serviceName + "." + methodName); + } + return auditImpl(mi); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("UnknownService." + methodName); + } + return auditImpl(mi); + } + + } + else if (method.isAnnotationPresent(NotAuditable.class)) + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Not Audited. " + serviceName + "." + methodName); + } + return mi.proceed(); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Unannotated service method " + serviceName + "." + methodName); + } + throw new RuntimeException("Unannotated service method " + serviceName + "." + methodName); + } + } + finally + { + auditFlag.set(Boolean.FALSE); + } + } + else + { + return mi.proceed(); + } + } + + /** + * Audit a method invocation + */ + public Object auditImpl(MethodInvocation mi) throws Throwable + { + AuditInfo auditInfo = new AuditInfo(auditConfiguration); + // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi); + AuditMode auditMode = AuditMode.UNSET; + try + { + auditMode = beforeInvocation(auditMode, auditInfo, mi); + Object o = mi.proceed(); + auditMode = postInvocation(auditMode, auditInfo, mi, o); + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS)) + { + auditDAO.audit(auditInfo); + } + return o; + } + catch (Throwable t) + { + auditMode = onError(auditMode, auditInfo, mi, t); + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) + { + try + { + auditFailedDAO.audit(auditInfo); + } + catch (Throwable tt) + { + throw new AuditException("Failed to audit exception", new Object[] { tt }, t); + } + } + throw t; + } + } + + /** + * Helper method to set auditable properties and to determine if auditing is required when an exception is caught in the audited method. + * + * @param auditMode + * @param auditInfo + * @param t + * @return + */ + private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Throwable t) + { + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) + { + auditInfo.setFail(true); + auditInfo.setThrowable(t); + } + + return auditMode; + } + + /** + * Helper method to set audited information after method invocation and to determine if auditing should take place based on the method return value. + * + * @param auditMode + * @param auditInfo + * @param mi + * @param returnObject + * @return + */ + private AuditMode postInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi, Object returnObject) + { + if (returnObject == null) + { + auditInfo.setReturnObject(null); + } + else if (returnObject instanceof Serializable) + { + auditInfo.setReturnObject((Serializable) returnObject); + } + else + { + auditInfo.setReturnObject(returnObject.toString()); + } + return auditMode; + } + + /** + * Set auditable information and determine if auditing is required before method invocation. This would normally be based on the method arguments. + * + * @param auditMode + * @param auditInfo + * @param mi + * @return + */ + private AuditMode beforeInvocation(AuditMode auditMode, AuditInfo auditInfo, MethodInvocation mi) + { + AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, mi); + + if (auditMode != AuditMode.NONE) + { + String methodName = mi.getMethod().getName(); + String serviceName = publicServiceIdentifier.getPublicServiceName(mi); + auditInfo.setAuditApplication(SYSTEM_APPLICATION); + auditInfo.setAuditConfiguration(auditConfiguration); + auditInfo.setAuditMethod(methodName); + auditInfo.setAuditService(serviceName); + auditInfo.setClientAddress(null); + auditInfo.setDate(new Date()); + auditInfo.setFail(false); + auditInfo.setFiltered(false); + auditInfo.setHostAddress(auditHost); + auditInfo.setKeyGUID(null); + auditInfo.setKeyPropertiesAfter(null); + auditInfo.setKeyPropertiesBefore(null); + auditInfo.setKeyStore(null); + auditInfo.setMessage(null); + if (mi.getArguments() != null) + { + Serializable[] serArgs = new Serializable[mi.getArguments().length]; + for (int i = 0; i < mi.getArguments().length; i++) + { + if (mi.getArguments()[i] == null) + { + serArgs[i] = null; + } + else if (mi.getArguments()[i] instanceof Serializable) + { + serArgs[i] = (Serializable) mi.getArguments()[i]; + } + else + { + serArgs[i] = mi.getArguments()[i].toString(); + } + } + auditInfo.setMethodArguments(serArgs); + } + auditInfo.setPath(null); + auditInfo.setReturnObject(null); + auditInfo.setSessionId(null); + auditInfo.setThrowable(null); + auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId()); + auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName()); + } + + return effectiveAuditMode; + } + + /** + * A simple audit entry Currently we ignore filtering here. + */ + public void audit(String source, String description, NodeRef key, Object... args) + { + AuditInfo auditInfo = new AuditInfo(auditConfiguration); + // RecordOptions recordOptions = auditModel.getAuditRecordOptions(mi); + AuditMode auditMode = AuditMode.UNSET; + try + { + auditMode = onApplicationAudit(auditMode, auditInfo, source, description, key, args); + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.SUCCESS)) + { + auditDAO.audit(auditInfo); + } + } + catch (Throwable t) + { + auditMode = onError(auditMode, auditInfo, t, source, description, key, args); + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) + { + try + { + auditFailedDAO.audit(auditInfo); + } + catch (Throwable tt) + { + throw new AuditException("Failed to audit exception", new Object[] { tt }, t); + } + } + throw new AuditException("Application audit failed", t); + } + } + + private AuditMode onApplicationAudit(AuditMode auditMode, AuditInfo auditInfo, String source, String description, + NodeRef key, Object... args) + { + AuditMode effectiveAuditMode = auditModel.beforeExecution(auditMode, source, description, key, args); + + if (auditMode != AuditMode.NONE) + { + if(source.equals(SYSTEM_APPLICATION)) + { + throw new AuditException("Application audit can not use the reserved identifier "+SYSTEM_APPLICATION); + } + + auditInfo.setAuditApplication(source); + auditInfo.setAuditConfiguration(auditConfiguration); + auditInfo.setAuditMethod(null); + auditInfo.setAuditService(null); + auditInfo.setClientAddress(null); + auditInfo.setDate(new Date()); + auditInfo.setFail(false); + auditInfo.setFiltered(false); + auditInfo.setHostAddress(auditHost); + auditInfo.setKeyGUID(null); + auditInfo.setKeyPropertiesAfter(null); + auditInfo.setKeyPropertiesBefore(null); + auditInfo.setKeyStore(null); + auditInfo.setMessage(description); + if (args != null) + { + Serializable[] serArgs = new Serializable[args.length]; + for (int i = 0; i < args.length; i++) + { + if (args[i] == null) + { + serArgs[i] = null; + } + else if (args[i] instanceof Serializable) + { + serArgs[i] = (Serializable) args[i]; + } + else + { + serArgs[i] = args[i].toString(); + } + } + auditInfo.setMethodArguments(serArgs); + } + auditInfo.setPath(null); + auditInfo.setReturnObject(null); + auditInfo.setSessionId(null); + auditInfo.setThrowable(null); + auditInfo.setTxId(AlfrescoTransactionSupport.getTransactionId()); + auditInfo.setUserIdentifier(AuthenticationUtil.getCurrentUserName()); + } + + return effectiveAuditMode; + } + + private AuditMode onError(AuditMode auditMode, AuditInfo auditInfo, Throwable t, String source, String description, + NodeRef key, Object... args) + { + if ((auditMode == AuditMode.ALL) || (auditMode == AuditMode.FAIL)) + { + auditInfo.setFail(true); + auditInfo.setThrowable(t); + } + + return auditMode; + + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditConfiguration.java b/source/java/org/alfresco/repo/audit/AuditConfiguration.java new file mode 100644 index 0000000000..66aba55205 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditConfiguration.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import java.io.InputStream; + +/** + * An audit configuration is xml content from an input stream. + * + * @author Andy Hind + */ +public interface AuditConfiguration +{ + + /** + * Get the XML content for the configuration as a stream. + * + * @return + */ + public abstract InputStream getInputStream(); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java b/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java new file mode 100644 index 0000000000..1b41dfd699 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditConfigurationImpl.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import java.io.InputStream; + +import org.springframework.beans.factory.InitializingBean; + +/** + * A class to read the audit configuration from the class path + * + * @author Andy Hind + */ +public class AuditConfigurationImpl implements InitializingBean, AuditConfiguration +{ + + private String config; + + public AuditConfigurationImpl() + { + super(); + } + + public void setConfig(String config) + { + this.config = config; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.getInputStream#getInputStream() + */ + /* (non-Javadoc) + * @see org.alfresco.repo.audit.AuditConfiguration#getInputStream() + */ + public InputStream getInputStream() + { + InputStream is = this.getClass().getClassLoader().getResourceAsStream(config); + return is; + } + + public void afterPropertiesSet() throws Exception + { + // Read and set up the audit configuration + + } + +} diff --git a/source/java/org/alfresco/repo/audit/AuditDAO.java b/source/java/org/alfresco/repo/audit/AuditDAO.java new file mode 100644 index 0000000000..ef307c5f93 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditDAO.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +/** + * The interface to persist audit information. + * + * @author Andy Hind + */ +public interface AuditDAO +{ + /** + * Create an audit entry. + * + * @param auditInfo + */ + public void audit(AuditInfo auditInfo); +} diff --git a/source/java/org/alfresco/repo/audit/AuditException.java b/source/java/org/alfresco/repo/audit/AuditException.java new file mode 100644 index 0000000000..c76cac4e55 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditException.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Audit related exceptions. + * + * @author Andy Hind + */ +public class AuditException extends AlfrescoRuntimeException +{ + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = -7947190775692164588L; + + public AuditException(String msgId) + { + super(msgId); + } + + public AuditException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + public AuditException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + public AuditException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + +} diff --git a/source/java/org/alfresco/repo/audit/AuditInfo.java b/source/java/org/alfresco/repo/audit/AuditInfo.java new file mode 100644 index 0000000000..f62e57a92c --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditInfo.java @@ -0,0 +1,385 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import java.io.Serializable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Date; +import java.util.Map; + +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.apache.log4j.Logger; + +/** + * A class to encapsulate audit information supplied to the DAO layer. + * + * Null entries should be stored. + * + * @author Andy Hind + */ +public class AuditInfo +{ + private static Logger s_logger = Logger.getLogger(AuditInfo.class); + + /** + * The user identifier for the person who caused this audit entry + */ + private String userIdentifier; + + /** + * The date for this audit entry + */ + private Date date; + + /** + * The transaction id in which this entry was made + */ + private String txId; + + /** + * The session is for this action + */ + private String sessionId; + + /** + * The store in which the action occured. + */ + private StoreRef keyStore; + + /** + * For a node ref, the node for the action. + */ + private String keyGUID; + + /** + * The path of the key + */ + private String keyPath; + + /** + * The audit application + * Internal uses the "System" key and will only audit method information. + */ + private String auditApplication; + + /** + * The service holding the audited method. + */ + private String auditService; + + /** + * The name of the audited method. + */ + private String auditMethod; + + /** + * Did this entry passa filter? + * If false - all entries were being recorded. + */ + private boolean filtered; + + /** + * The audit configuration in use at the time. + */ + private AuditConfiguration auditConfiguration; + + /** + * The object returned by the audited method. + */ + private Serializable returnObject; + + /** + * The arguments to the audited method. + */ + private Serializable[] methodArguments; + + /** + * Any Exception thrown by the audited method. + */ + private Throwable throwable; + + /** + * Did the audited method throw an exception? + */ + private boolean fail; + + /** + * The host address for where the audit was generated. + */ + private InetAddress hostAddress; + + private static InetAddress s_hostAddress; + + /** + * The client address causing the audit + */ + private InetAddress clientAddress; + + /** + * The properties of the key node before the method execution. + */ + private Map keyPropertiesBefore; + + /** + * The properties of the key node after the method execution. + */ + private Map keyPropertiesAfter; + + /** + * For general auditing - the audit message. + */ + private String message; + + static + { + try + { + s_hostAddress = InetAddress.getLocalHost(); + } + catch (UnknownHostException e) + { + s_logger.error(e); + s_hostAddress = null; + } + } + + public AuditInfo(AuditConfiguration auditConfiguration) + { + super(); + // Add default information + userIdentifier = AuthenticationUtil.getCurrentUserName(); + date = new Date(); + txId = AlfrescoTransactionSupport.getTransactionId(); + sessionId = "Unavailable"; + hostAddress = s_hostAddress; + } + + public String getAuditApplication() + { + return auditApplication; + } + + public void setAuditApplication(String auditApplication) + { + this.auditApplication = auditApplication; + } + + public AuditConfiguration getAuditConfiguration() + { + return auditConfiguration; + } + + public void setAuditConfiguration(AuditConfiguration auditConfiguration) + { + this.auditConfiguration = auditConfiguration; + } + + public String getAuditMethod() + { + return auditMethod; + } + + public void setAuditMethod(String auditMethod) + { + this.auditMethod = auditMethod; + } + + public String getAuditService() + { + return auditService; + } + + public void setAuditService(String auditService) + { + this.auditService = auditService; + } + + public InetAddress getClientAddress() + { + return clientAddress; + } + + public void setClientAddress(InetAddress clientAddress) + { + this.clientAddress = clientAddress; + } + + public Date getDate() + { + return date; + } + + public void setDate(Date date) + { + this.date = date; + } + + public boolean isFail() + { + return fail; + } + + public void setFail(boolean fail) + { + this.fail = fail; + } + + public boolean isFiltered() + { + return filtered; + } + + public void setFiltered(boolean filtered) + { + this.filtered = filtered; + } + + public InetAddress getHostAddress() + { + return hostAddress; + } + + public void setHostAddress(InetAddress hostAddress) + { + this.hostAddress = hostAddress; + } + + public String getKeyGUID() + { + return keyGUID; + } + + public void setKeyGUID(String keyGUID) + { + this.keyGUID = keyGUID; + } + + public Map getKeyPropertiesAfter() + { + return keyPropertiesAfter; + } + + public void setKeyPropertiesAfter(Map keyPropertiesAfter) + { + this.keyPropertiesAfter = keyPropertiesAfter; + } + + public Map getKeyPropertiesBefore() + { + return keyPropertiesBefore; + } + + public void setKeyPropertiesBefore(Map keyPropertiesBefore) + { + this.keyPropertiesBefore = keyPropertiesBefore; + } + + public StoreRef getKeyStore() + { + return keyStore; + } + + public void setKeyStore(StoreRef keyStore) + { + this.keyStore = keyStore; + } + + public String getMessage() + { + return message; + } + + public void setMessage(String message) + { + this.message = message; + } + + public Serializable[] getMethodArguments() + { + return methodArguments; + } + + public void setMethodArguments(Serializable[] methodArguments) + { + this.methodArguments = methodArguments; + } + + public String getPath() + { + return keyPath; + } + + public void setPath(String keyPath) + { + this.keyPath = keyPath; + } + + public Serializable getReturnObject() + { + return returnObject; + } + + public void setReturnObject(Serializable returnObject) + { + this.returnObject = returnObject; + } + + public String getSessionId() + { + return sessionId; + } + + public void setSessionId(String sessionId) + { + this.sessionId = sessionId; + } + + public Throwable getThrowable() + { + return throwable; + } + + public void setThrowable(Throwable throwable) + { + this.throwable = throwable; + } + + public String getTxId() + { + return txId; + } + + public void setTxId(String txId) + { + this.txId = txId; + } + + public String getUserIdentifier() + { + return userIdentifier; + } + + public void setUserIdentifier(String userIdentifier) + { + this.userIdentifier = userIdentifier; + } + +} diff --git a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java new file mode 100644 index 0000000000..3ba28246c3 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * A method interceptor to wrap method invocations with auditing. + * + * A single instance is used to wrap all services. If the single instance is disabled + * no auditing will be carried out and there will be minimal overhead. + * + * @author Andy Hind + */ +public class AuditMethodInterceptor implements MethodInterceptor +{ + //private static Log s_logger = LogFactory.getLog(AuditMethodInterceptor.class); + + private AuditComponent auditComponent; + + private boolean disabled = false; + + public AuditMethodInterceptor() + { + super(); + } + + public void setDisabled(boolean disabled) + { + this.disabled = disabled; + } + + public void setAuditComponent(AuditComponent auditComponent) + { + this.auditComponent = auditComponent; + } + + public Object invoke(MethodInvocation mi) throws Throwable + { + if(disabled) + { + return mi.proceed(); + } + else + { + return auditComponent.audit(mi); + } + + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditMode.java b/source/java/org/alfresco/repo/audit/AuditMode.java new file mode 100644 index 0000000000..eab99ed6f4 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditMode.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.alfresco.repo.audit.model.AuditModelException; + +/** + * An enum to specify the audit mode: + * + *
    + *
  1. ALL - all calls are audited + *
  2. SUCCESS - only successful calls are audited (audited in the same TX) + *
  3. FAIL - only fail calls are audited (in a new transaction) + *
  4. NONE - noting is audited + *
  5. UNSET + *
+ * + * The mode is inherited from containers if nothing is specified + * + * @author Andy Hind + */ +public enum AuditMode +{ + ALL, SUCCESS, FAIL, NONE, UNSET; + + public static AuditMode getAuditMode(String value) + { + if(value.equalsIgnoreCase("all")) + { + return AuditMode.ALL; + } + else if(value.equalsIgnoreCase("success")) + { + return AuditMode.SUCCESS; + } + else if(value.equalsIgnoreCase("fail")) + { + return AuditMode.FAIL; + } + else if(value.equalsIgnoreCase("none")) + { + return AuditMode.NONE; + } + else if(value.equalsIgnoreCase("unset")) + { + return AuditMode.UNSET; + } + else + { + throw new AuditModelException("Invalid audit mode: "+value); + } + } +} diff --git a/source/java/org/alfresco/repo/audit/AuditModel.java b/source/java/org/alfresco/repo/audit/AuditModel.java new file mode 100644 index 0000000000..451ae9e12f --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditModel.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + + +/** + * API for querying the audit model + * + * @author Andy Hind + */ +public interface AuditModel extends ApplicationAuditModel, MethodAuditModel +{ + /** + * Constants for reading the xml model definition. + */ + + /* package */final static String NAME_SPACE = "http://www.alfresco.org/model/audit/1.0"; + + /* package */final static String EL_AUDIT = "Audit"; + + /* package */final static String EL_RECORD_OPTIONS = "RecordOptions"; + + /* package */final static String EL_RECORD_PATH = "recordPath"; + + /* package */final static String EL_RECORD_FILTERS = "recordFilters"; + + /* package */final static String EL_RECORD_SER_RETURN_VAL = "recordSerializedReturnValue"; + + /* package */final static String EL_RECORD_SER_EX = "recordSerializedExceptions"; + + /* package */final static String EL_RECORD_SER_ARGS = "recordSerializedMethodArguments"; + + /* package */final static String EL_RECORD_SER_PROP_BEFORE = "recordSerializedKeyPropertiesBeforeInvocation"; + + /* package */final static String EL_RECORD_SER_PROP_AFTER = "recordSerializedKeyPropertiesAferInvocation"; + + /* package */final static String EL_FILTER = "Filter"; + + /* package */final static String EL_METHOD = "Method"; + + /* package */final static String EL_SERVICE = "Service"; + + /* package */final static String EL_APPLICATION = "Application"; + + /* package */final static String EL_EXPRESSION = "Expression"; + + /* package */final static String EL_PARAMETER_NAME = "ParameterName"; + + /* package */final static String AT_MODE = "mode"; + + /* package */final static String AT_ENABLED = "enabled"; + + /* package */final static String AT_AUDIT_INTERNAL = "auditInternal"; + + /* package */final static String AT_NAME = "name"; + + /* package */final static String AT_INVERT = "invert"; + + /* package */final static String AT_TYPE = "type"; +} diff --git a/source/java/org/alfresco/repo/audit/AuditServiceImpl.java b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java new file mode 100644 index 0000000000..b89f855f3c --- /dev/null +++ b/source/java/org/alfresco/repo/audit/AuditServiceImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import javax.transaction.UserTransaction; + +import org.alfresco.service.cmr.audit.AuditService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * The implementation of the AuditService for application auditing. + * + * @author Andy Hind + */ +public class AuditServiceImpl implements AuditService +{ + private AuditComponent auditComponent; + + public AuditServiceImpl() + { + super(); + } + + public void setAuditComponent(AuditComponent auditComponent) + { + this.auditComponent = auditComponent; + } + + public void audit(String source, String description) + { + auditComponent.audit(source, description, null, (Object[]) null); + } + + public void audit(String source, String description, NodeRef key) + { + auditComponent.audit(source, description, key, (Object[]) null); + } + + public void audit(String source, String description, Object... args) + { + auditComponent.audit(source, description, null, args); + } + + public void audit(String source, String description, NodeRef key, Object... args) + { + auditComponent.audit(source, description, key, args); + } + + public static void main(String[] args) throws Exception + { + ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + AuditService as = (AuditService) ctx.getBean("AuditService"); + + TransactionService txs = (TransactionService) ctx.getBean("transactionComponent"); + UserTransaction tx = txs.getUserTransaction(); + tx.begin(); + as.audit("AuditedApp", "First"); + as.audit("AuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id")); + as.audit("AuditedApp", "Third", new Object[]{"one", "two", "three"}); + as.audit("AuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"}); + + as.audit("UnAuditedApp", "First"); + as.audit("UnAuditedApp", "Second", new NodeRef(new StoreRef("test", "audit"), "id")); + as.audit("UnAuditedApp", "Third", new Object[]{"one", "two", "three"}); + as.audit("UnAuditedApp", "Fourth", new NodeRef(new StoreRef("test", "audit"), "id"), new Object[]{"one", "two", "three"}); + + tx.commit(); + + } +} diff --git a/source/java/org/alfresco/repo/audit/MethodAuditModel.java b/source/java/org/alfresco/repo/audit/MethodAuditModel.java new file mode 100644 index 0000000000..37b1200173 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/MethodAuditModel.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.aopalliance.intercept.MethodInvocation; + +public interface MethodAuditModel +{ + /** + * Report if audit behaviour can be determined before the method call + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi); + + /** + * Report if audit behaviour can be determined after the method call + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi); + + /** + * Report if audit behaviour should be invoked on error. It could be we look at the error and filter - this is not supported at the moment. + * + * @param auditState, + * @param mi + * @return + */ + public AuditMode onError(AuditMode auditMode, MethodInvocation mi); + + /** + * Get the optional parameters that are to be recorded + * + * @param mi + * @return + */ + public RecordOptions getAuditRecordOptions(MethodInvocation mi); +} diff --git a/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java b/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java new file mode 100644 index 0000000000..072b3a2931 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/PublicServiceIdentifier.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.aopalliance.intercept.MethodInvocation; + +/** + * This defines the API to identify the public service upon which a method invocation has been made. + * + * @author Andy Hind + */ +public interface PublicServiceIdentifier +{ + /** + * Get the name of the public service for the method invocation. + * + * @param mi + * @return + */ + public String getPublicServiceName(MethodInvocation mi); +} diff --git a/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java b/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java new file mode 100644 index 0000000000..d39f3c4881 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/PublicServiceIdentifierImpl.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.service.PublicService; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; + +/** + * Identify public services by method invocation. Look ups are cached on a thread local as they are quite expensive. All public service names end with "Service" and start with + * capital letter. This pattern is used to filter bean names. TODO: Look at pulling out all the mappings at start up. + * + * @author Andy Hind + */ +public class PublicServiceIdentifierImpl implements PublicServiceIdentifier, BeanFactoryPostProcessor +{ + private static Log s_logger = LogFactory.getLog(PublicServiceIdentifierImpl.class); + + private static ThreadLocal> methodToServiceMap = new ThreadLocal>(); + + private ConfigurableListableBeanFactory beanFactory; + + public PublicServiceIdentifierImpl() + { + super(); + } + + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + this.beanFactory = beanFactory; + } + + public String getPublicServiceName(MethodInvocation mi) + { + return getServiceName(mi); + } + + /** + * Cache service name look up. + * + * @param mi + * @return + * @throws BeansException + */ + private String getServiceName(MethodInvocation mi) throws BeansException + { + if (methodToServiceMap.get() == null) + { + methodToServiceMap.set(new HashMap()); + } + Method method = mi.getMethod(); + String serviceName = methodToServiceMap.get().get(method); + if (serviceName == null) + { + serviceName = getServiceNameImpl(mi); + methodToServiceMap.get().put(method, serviceName); + } + else + { + if (s_logger.isDebugEnabled()) + { + s_logger.debug("Cached look up for " + serviceName + "." + method.getName()); + } + } + return serviceName; + } + + /** + * Do the look up by interface type. + * + * @param mi + * @return + * @throws BeansException + */ + + private String getServiceNameImpl(MethodInvocation mi) throws BeansException + { + Class clazz = mi.getThis().getClass(); + while (clazz != null) + { + Class[] interfaces = clazz.getInterfaces(); + for (Class iFace : interfaces) + { + Class publicServiceInterface = findPublicService(iFace); + if (publicServiceInterface != null) + { + Map beans = beanFactory.getBeansOfType(publicServiceInterface); + Iterator iter = beans.entrySet().iterator(); + while (iter.hasNext()) + { + Map.Entry entry = (Map.Entry) iter.next(); + String serviceName = (String) entry.getKey(); + if ((serviceName.endsWith("Service")) + && (Character.isUpperCase(serviceName.charAt(0))) + && !serviceName.equals("DescriptorService")) + { + return serviceName; + } + } + } + + } + clazz = clazz.getSuperclass(); + } + return null; + } + + /** + * We use a marker annotation to identify public interfaces. + * The interfaces have to be walked to determine if a public interface is implemented. + * + * Only one public service interface is expected. + * + * @param clazz + * @return + */ + @SuppressWarnings("unchecked") + private Class findPublicService(Class clazz) + { + if (!clazz.isInterface()) + { + return null; + } + + if (clazz.isAnnotationPresent(PublicService.class)) + { + return clazz; + } + + Class[] classes = clazz.getInterfaces(); + for(Class implemented: classes) + { + Class answer = findPublicService(implemented); + if(answer != null) + { + return answer; + } + } + return null; + + } +} diff --git a/source/java/org/alfresco/repo/audit/RecordOptions.java b/source/java/org/alfresco/repo/audit/RecordOptions.java new file mode 100644 index 0000000000..f1194bef20 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/RecordOptions.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit; + +import org.alfresco.repo.audit.model.TrueFalseUnset; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.dom4j.Element; + +public interface RecordOptions +{ + public TrueFalseUnset getRecordFilters(); + + public TrueFalseUnset getRecordPath(); + + public TrueFalseUnset getRecordSerializedExceptions(); + + public TrueFalseUnset getRecordSerializedKeyPropertiesAfterEvaluation(); + + public TrueFalseUnset getRecordSerializedKeyPropertiesBeforeEvaluation(); + + public TrueFalseUnset getRecordSerializedMethodArguments(); + + public TrueFalseUnset getRecordSerializedReturnValue(); +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml new file mode 100644 index 0000000000..17ff8452cd --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + audit_date + from + org.alfresco.repo.audit.hibernate.AuditDateImpl as audit_date + where + audit_date.id = (select max(audit_date_2.id) from org.alfresco.repo.audit.hibernate.AuditDateImpl as audit_date_2) + + + + select + audit_config + from + org.alfresco.repo.audit.hibernate.AuditConfigImpl as audit_config + where + audit_config.id = (select max(audit_config_2.id) from org.alfresco.repo.audit.hibernate.AuditConfigImpl as audit_config_2) + + + + select + audit_store + from + org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store + where + audit_store.application = :application and + audit_store.service is null and + audit_store.method is null + + + + select + audit_store + from + org.alfresco.repo.audit.hibernate.AuditSourceImpl as audit_store + where + audit_store.application = :application and + audit_store.service = :service and + audit_store.method = :method + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java b/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java new file mode 100644 index 0000000000..62369dfd4b --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditConfig.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + + +/** + * Hibernate interface for audit configuration persistence. + * + * @author Andy Hind + */ +public interface AuditConfig +{ + /** + * Get the content URL for a copy of the configuration file. + * + * @return + */ + public abstract String getConfigURL(); + + /** + * Get the surrogate key for the configuration entry. + * The creation is managed by hibernate. + * + * @return + */ + public abstract long getId(); +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java new file mode 100644 index 0000000000..6e86b0b8ee --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditConfigImpl.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import org.alfresco.util.EqualsHelper; +import org.hibernate.Query; +import org.hibernate.Session; +import org.springframework.beans.factory.InitializingBean; + +public class AuditConfigImpl implements AuditConfig, InitializingBean +{ + /** + * The hibernate generated internal key. + */ + private long id; + + /** + * The URL to the content that contains the configuration file + */ + private String configURL; + + public AuditConfigImpl() + { + super(); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditContig#getConfigURL() + */ + public String getConfigURL() + { + return configURL; + } + + public void setConfigURL(String configURL) + { + this.configURL = configURL; + } + + + + public void afterPropertiesSet() throws Exception + { + // Read the audit configuration + + + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditContig#getId() + */ + public long getId() + { + return id; + } + + /** + * Internal setter for hibernate. + * + * @param id + */ + + @SuppressWarnings("unused") + private void setId(long id) + { + this.id = id; + } + + /** + * Helper method to get the latest audit config + */ + public static AuditConfigImpl getLatestConfig(Session session) + { + Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_CONFIG); + return (AuditConfigImpl) query.uniqueResult(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if(!(o instanceof AuditConfigImpl)) + { + return false; + } + AuditConfigImpl other = (AuditConfigImpl)o; + return EqualsHelper.nullSafeEquals(this.configURL, other.configURL); + } + + @Override + public int hashCode() + { + return configURL == null ? 0 : configURL.hashCode(); + } + + +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java b/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java new file mode 100644 index 0000000000..74f0b36ee7 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditDate.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import java.util.Date; + +/** + * Hibernate date dimension for audit roll ups + * + * @author Andy Hind + */ +public interface AuditDate +{ + + /** + * Get the date + * + * @return + */ + public abstract Date getDate(); + + /** + * Get the day of the year. + * + * @return + */ + public abstract int getDayOfYear(); + + /** + * Get the day of the month. + * + * @return + */ + public abstract int getDayOfMonth(); + + /** + * Get the day of the week + * + * @return + */ + public abstract int getDayOfWeek(); + + /** + * Get the half year; + * + * @return + */ + public abstract int getHalfYear(); + + + /** + * Get the surrogate key + * + * @return + */ + public abstract long getId(); + + /** + * Get the month + * + * @return + */ + public abstract int getMonth(); + + /** + * Get the quarter + * + * @return + */ + public abstract int getQuarter(); + + /** + * Get the week of the month. + * + * @return + */ + public abstract int getWeekOfMonth(); + + /** + * Get the week of the year. + * + * @return + */ + public abstract int getWeekOfYear(); + + /** + * Get the year. + * @return + */ + public abstract int getYear(); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java new file mode 100644 index 0000000000..a15e9c85da --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditDateImpl.java @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import org.alfresco.util.EqualsHelper; +import org.hibernate.Query; +import org.hibernate.Session; + +/** + * Hibernate persistence for a date dimension. + * + * @author Andy Hind + */ +public class AuditDateImpl implements AuditDate +{ + /** + * Surrogate key + */ + private long id; + + /** + * The date + */ + private Date date; + + /** + * The day + */ + private int dayOfYear; + + /** + * Day of month. + */ + private int dayOfMonth; + + /** + * The day of the week + */ + private int dayOfWeek; + + /** + * The week in the year + */ + private int weekOfYear; + + /** + * The week of the month + */ + private int weekOfMonth; + + /** + * The month in the year + */ + private int month; + + /** + * The quarter in the year + */ + private int quarter; + + /** + * The half year in the year + */ + private int halfYear; + + /** + * The year + */ + private int year; + + protected AuditDateImpl() + { + super(); + } + + public AuditDateImpl(Date date) + { + super(); + setDate(date); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getDate() + */ + public Date getDate() + { + return date; + } + + public void setDate(Date date) + { + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(date); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.HOUR_OF_DAY, 0); + this.date = cal.getTime(); + + this.setDayOfYear(cal.get(Calendar.DAY_OF_YEAR)); + this.setDayOfMonth(cal.get(Calendar.DAY_OF_MONTH)); + this.setDayOfWeek(cal.get(Calendar.DAY_OF_WEEK)); + this.setMonth(cal.get(Calendar.MONTH)); + this.setHalfYear(getMonth() <= Calendar.JUNE ? 0 : 1); + this.setQuarter((getMonth()/3)); + this.setWeekOfMonth(cal.get(Calendar.WEEK_OF_MONTH)); + this.setWeekOfYear(cal.get(Calendar.WEEK_OF_YEAR)); + this.setYear(cal.get(Calendar.YEAR)); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfYear() + */ + public int getDayOfYear() + { + return dayOfYear; + } + + protected void setDayOfYear(int dayOfYear) + { + this.dayOfYear = dayOfYear; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfMonth() + */ + public int getDayOfMonth() + { + return dayOfMonth; + } + + protected void setDayOfMonth(int dayOfMonth) + { + this.dayOfMonth = dayOfMonth; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getDayOfWeek() + */ + public int getDayOfWeek() + { + return dayOfWeek; + } + + protected void setDayOfWeek(int dayOfWeek) + { + this.dayOfWeek = dayOfWeek; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getHalfYear() + */ + public int getHalfYear() + { + return halfYear; + } + + protected void setHalfYear(int halfYear) + { + this.halfYear = halfYear; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getId() + */ + public long getId() + { + return id; + } + + protected void setId(long id) + { + this.id = id; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getMonth() + */ + public int getMonth() + { + return month; + } + + protected void setMonth(int month) + { + this.month = month; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getQuarter() + */ + public int getQuarter() + { + return quarter; + } + + protected void setQuarter(int quarter) + { + this.quarter = quarter; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getWeekOfMonth() + */ + public int getWeekOfMonth() + { + return weekOfMonth; + } + + protected void setWeekOfMonth(int weekOfMonth) + { + this.weekOfMonth = weekOfMonth; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getWeekOfYear() + */ + public int getWeekOfYear() + { + return weekOfYear; + } + + protected void setWeekOfYear(int weekOfYear) + { + this.weekOfYear = weekOfYear; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.audit.hibernate.AuditDate#getYear() + */ + public int getYear() + { + return year; + } + + protected void setYear(int year) + { + this.year = year; + } + + @Override + public boolean equals(Object o) + { + if(this == o) + { + return true; + } + if(!(o instanceof AuditDateImpl)) + { + return false; + } + AuditDateImpl that = (AuditDateImpl)o; + return EqualsHelper.nullSafeEquals(this.date, that.date); + } + + @Override + public int hashCode() + { + return this.date.hashCode(); + } + + /** + * Helper method to get the latest audit date + */ + public static AuditDateImpl getLatestDate(Session session) + { + Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_LAST_AUDIT_DATE); + return (AuditDateImpl) query.uniqueResult(); + } + +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java b/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java new file mode 100644 index 0000000000..17d08204d8 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditFact.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import java.util.Date; + +public interface AuditFact +{ + + public abstract String getArg1(); + + public abstract String getArg2(); + + public abstract String getArg3(); + + public abstract String getArg4(); + + public abstract String getArg5(); + + public abstract AuditConfig getAuditConfig(); + + public abstract AuditDate getAuditDate(); + + public abstract AuditSource getAuditSource(); + + public abstract String getClientInetAddress(); + + public abstract Date getDate(); + + public abstract String getException(); + + public abstract boolean isFail(); + + public abstract boolean isFiltered(); + + public abstract String getHostInetAddress(); + + public abstract long getId(); + + public abstract String getMessage(); + + public abstract String getNodeUUID(); + + public abstract String getPath(); + + public abstract String getReturnValue(); + + public abstract String getSerialisedURL(); + + public abstract String getSessionId(); + + public abstract String getStoreId(); + + public abstract String getStoreProtocol(); + + public abstract String getTransactionId(); + + public abstract String getUserId(); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java new file mode 100644 index 0000000000..3ec7f4a4ad --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditFactImpl.java @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import java.util.Date; + +/** + * An Audit fact Rely on standard equals and hash code as they should all be unique. + * + * @author Andy Hind + */ +public class AuditFactImpl implements AuditFact +{ + private long id; + + private AuditDate auditDate; + + private AuditConfig auditConfig; + + private AuditSource auditSource; + + private String userId; + + private Date date; + + private String transactionId; + + private String sessionId; + + private String storeProtocol; + + private String storeId; + + private String nodeUUID; + + private String path; + + private boolean filtered; + + private String returnValue; + + private String arg1; + + private String arg2; + + private String arg3; + + private String arg4; + + private String arg5; + + private boolean fail; + + private String serialisedURL; + + private String exception; + + private String hostInetAddress; + + private String clientInetAddress; + + private String message; + + public AuditFactImpl() + { + super(); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg1() + */ + public String getArg1() + { + return arg1; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg1(java.lang.String) + */ + public void setArg1(String arg1) + { + this.arg1 = arg1; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg2() + */ + public String getArg2() + { + return arg2; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg2(java.lang.String) + */ + public void setArg2(String arg2) + { + this.arg2 = arg2; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg3() + */ + public String getArg3() + { + return arg3; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg3(java.lang.String) + */ + public void setArg3(String arg3) + { + this.arg3 = arg3; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg4() + */ + public String getArg4() + { + return arg4; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg4(java.lang.String) + */ + public void setArg4(String arg4) + { + this.arg4 = arg4; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getArg5() + */ + public String getArg5() + { + return arg5; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setArg5(java.lang.String) + */ + public void setArg5(String arg5) + { + this.arg5 = arg5; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditConfig() + */ + public AuditConfig getAuditConfig() + { + return auditConfig; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditConfig(org.alfresco.repo.audit.hibernate.AuditConfig) + */ + public void setAuditConfig(AuditConfig auditConfig) + { + this.auditConfig = auditConfig; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditDate() + */ + public AuditDate getAuditDate() + { + return auditDate; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditDate(org.alfresco.repo.audit.hibernate.AuditDate) + */ + public void setAuditDate(AuditDate auditDate) + { + this.auditDate = auditDate; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getAuditSource() + */ + public AuditSource getAuditSource() + { + return auditSource; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setAuditSource(org.alfresco.repo.audit.hibernate.AuditSource) + */ + public void setAuditSource(AuditSource auditSource) + { + this.auditSource = auditSource; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getClientInetAddress() + */ + public String getClientInetAddress() + { + return clientInetAddress; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setClientInetAddress(java.net.InetAddress) + */ + public void setClientInetAddress(String clientInetAddress) + { + this.clientInetAddress = clientInetAddress; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getDate() + */ + public Date getDate() + { + return date; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setDate(java.util.Date) + */ + public void setDate(Date date) + { + this.date = date; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getException() + */ + public String getException() + { + return exception; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setException(java.lang.String) + */ + public void setException(String exception) + { + this.exception = exception; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#isFail() + */ + public boolean isFail() + { + return fail; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setFail(boolean) + */ + public void setFail(boolean fail) + { + this.fail = fail; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#isFiltered() + */ + public boolean isFiltered() + { + return filtered; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setFiltered(boolean) + */ + public void setFiltered(boolean filtered) + { + this.filtered = filtered; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getHostInetAddress() + */ + public String getHostInetAddress() + { + return hostInetAddress; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setHostInetAddress(java.net.InetAddress) + */ + public void setHostInetAddress(String hostInetAddress) + { + this.hostInetAddress = hostInetAddress; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getId() + */ + public long getId() + { + return id; + } + + protected void setId(long id) + { + this.id = id; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getMessage() + */ + public String getMessage() + { + return message; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setMessage(java.lang.String) + */ + public void setMessage(String message) + { + this.message = message; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getNodeGUID() + */ + public String getNodeUUID() + { + return nodeUUID; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setNodeGUID(java.lang.String) + */ + public void setNodeUUID(String nodeUUID) + { + this.nodeUUID = nodeUUID; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getPath() + */ + public String getPath() + { + return path; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setPath(java.lang.String) + */ + public void setPath(String path) + { + this.path = path; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getReturnValue() + */ + public String getReturnValue() + { + return returnValue; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setReturnValue(java.lang.String) + */ + public void setReturnValue(String returnValue) + { + this.returnValue = returnValue; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getSerialisedURL() + */ + public String getSerialisedURL() + { + return serialisedURL; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setSerialisedURL(java.lang.String) + */ + public void setSerialisedURL(String serialisedURL) + { + this.serialisedURL = serialisedURL; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getSessionId() + */ + public String getSessionId() + { + return sessionId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setSessionId(java.lang.String) + */ + public void setSessionId(String sessionId) + { + this.sessionId = sessionId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getStoreId() + */ + public String getStoreId() + { + return storeId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setStoreId(java.lang.String) + */ + public void setStoreId(String storeId) + { + this.storeId = storeId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getStoreProtocol() + */ + public String getStoreProtocol() + { + return storeProtocol; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setStoreProtocol(java.lang.String) + */ + public void setStoreProtocol(String storeProtocol) + { + this.storeProtocol = storeProtocol; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getTransactionId() + */ + public String getTransactionId() + { + return transactionId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setTransactionId(java.lang.String) + */ + public void setTransactionId(String transactionId) + { + this.transactionId = transactionId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#getUserId() + */ + public String getUserId() + { + return userId; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.audit.hibernate.AuditFact#setUserId(java.lang.String) + */ + public void setUserId(String userId) + { + this.userId = userId; + } + +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java new file mode 100644 index 0000000000..17ac1c2ef0 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSource.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +public interface AuditSource +{ + public String getApplication(); + + public long getId(); + + public String getMethod(); + + public String getService(); + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java new file mode 100644 index 0000000000..927d042317 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/AuditSourceImpl.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import org.alfresco.util.EqualsHelper; +import org.hibernate.Query; +import org.hibernate.Session; + +public class AuditSourceImpl implements AuditSource +{ + /** + * The surrogate key + */ + private long id; + + /** + * The auditing application (System for method audits) + */ + private String application; + + /** + * The audited service + */ + private String service; + + /** + * The audited method + */ + private String method; + + public AuditSourceImpl() + { + super(); + } + + public String getApplication() + { + return application; + } + + public void setApplication(String application) + { + this.application = application; + } + + public long getId() + { + return id; + } + + protected void setId(long id) + { + this.id = id; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getService() + { + return service; + } + + public void setService(String service) + { + this.service = service; + } + + public static AuditSourceImpl getApplicationSource(Session session, String application) + { + Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application); + return (AuditSourceImpl) query.uniqueResult(); + } + + public static AuditSourceImpl getApplicationSource(Session session, String application, String service, + String method) + { + Query query = session.getNamedQuery(HibernateAuditDAO.QUERY_AUDIT_METHOD_SOURCE); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_APP, application); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_SER, service); + query.setParameter(HibernateAuditDAO.QUERY_AUDIT_APP_SOURCE_MET, method); + return (AuditSourceImpl) query.uniqueResult(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(o instanceof AuditSourceImpl)) + { + return false; + } + AuditSourceImpl other = (AuditSourceImpl) o; + return EqualsHelper.nullSafeEquals(this.application, other.application) + && EqualsHelper.nullSafeEquals(this.service, other.service) + && EqualsHelper.nullSafeEquals(this.method, other.method); + } + + @Override + public int hashCode() + { + int hash = application.hashCode(); + if(service != null) + { + hash = (hash * 37) + service.hashCode(); + } + if(method != null) + { + hash = (hash * 37) + method.hashCode(); + } + return hash; + } + +} diff --git a/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java new file mode 100644 index 0000000000..b4bf563e18 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/hibernate/HibernateAuditDAO.java @@ -0,0 +1,439 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.hibernate; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.audit.AuditConfiguration; +import org.alfresco.repo.audit.AuditDAO; +import org.alfresco.repo.audit.AuditInfo; +import org.alfresco.repo.content.AbstractContentStore; +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.transaction.TransactionalDao; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.datatype.Duration; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.GUID; +import org.hibernate.Session; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Assumes mimetype and encoding sent to the content store (we are not saving this anywhere) + * + * @author Andy Hind + */ +public class HibernateAuditDAO extends HibernateDaoSupport implements AuditDAO, TransactionalDao +{ + public static final String QUERY_LAST_AUDIT_DATE = "audit.GetLatestAuditDate"; + + public static final String QUERY_LAST_AUDIT_CONFIG = "audit.GetLatestAuditConfig"; + + public static final String QUERY_AUDIT_APP_SOURCE = "audit.GetAuditSourceByApplication"; + + public static final String QUERY_AUDIT_METHOD_SOURCE = "audit.GetAuditSourceByApplicationServiceMethod"; + + public static final String QUERY_AUDIT_APP_SOURCE_APP = "application"; + + public static final String QUERY_AUDIT_APP_SOURCE_SER = "service"; + + public static final String QUERY_AUDIT_APP_SOURCE_MET = "method"; + + /** a uuid identifying this unique instance */ + private String uuid; + + private ContentStore contentStore; + + private ThreadLocal auditConfiguration = new ThreadLocal(); + + private ThreadLocal auditConfigImplId = new ThreadLocal(); + + private ThreadLocal auditDateImplId = new ThreadLocal(); + + private ThreadLocal> sourceIds = new ThreadLocal>(); + + public HibernateAuditDAO() + { + super(); + this.uuid = GUID.generate(); + } + + public ContentStore getContentStore() + { + return contentStore; + } + + public void setContentStore(ContentStore contentStore) + { + this.contentStore = contentStore; + } + + public void audit(AuditInfo auditInfo) + { + // Find/Build the configuraton entry + AuditConfigImpl auditConfig = getAuditConfig(auditInfo); + + // Find/Build any dates + AuditDateImpl auditDate = getAuditDate(auditInfo); + + // Find/Build the source + AuditSourceImpl auditSource = getAuditSource(auditInfo); + + // Build the new audit fact information + AuditFactImpl auditFact = new AuditFactImpl(); + auditFact.setAuditConfig(auditConfig); + auditFact.setAuditDate(auditDate); + auditFact.setAuditSource(auditSource); + + // Properties + + Serializable[] args = auditInfo.getMethodArguments(); + + if (args != null) + { + switch (args.length) + { + default: + case 5: + auditFact.setArg5(getStringOrNull(args[4])); + case 4: + auditFact.setArg4(getStringOrNull(args[3])); + case 3: + auditFact.setArg3(getStringOrNull(args[2])); + case 2: + auditFact.setArg2(getStringOrNull(args[1])); + case 1: + auditFact.setArg1(getStringOrNull(args[0])); + case 0: + } + } + + auditFact.setClientInetAddress(auditInfo.getClientAddress() == null ? null : auditInfo.getClientAddress() + .toString()); + auditFact.setDate(auditInfo.getDate()); + auditFact.setException(auditInfo.getThrowable() == null ? null : auditInfo.getThrowable().getMessage()); + auditFact.setFail(auditInfo.isFail()); + auditFact.setFiltered(auditInfo.isFiltered()); + auditFact.setHostInetAddress(auditInfo.getHostAddress() == null ? null : auditInfo.getHostAddress().toString()); + auditFact.setMessage(auditInfo.getMessage()); + auditFact.setNodeUUID(auditInfo.getKeyGUID()); + auditFact.setPath(auditInfo.getPath()); + auditFact.setReturnValue(auditInfo.getReturnObject() == null ? null : auditInfo.getReturnObject().toString()); + // auditFact.setSerialisedURL() + auditFact.setSessionId(auditInfo.getSessionId()); + if (auditInfo.getKeyStore() != null) + { + auditFact.setStoreId(auditInfo.getKeyStore().getIdentifier()); + auditFact.setStoreProtocol(auditInfo.getKeyStore().getProtocol()); + } + auditFact.setTransactionId(auditInfo.getTxId()); + auditFact.setUserId(auditInfo.getUserIdentifier()); + + // Save + getSession().save(auditFact); + + } + + private String getStringOrNull(Object o) + { + if (o == null) + { + return null; + } + else + { + return o.toString(); + } + } + + private AuditSourceImpl getAuditSource(AuditInfo auditInfo) + { + AuditSourceImpl auditSourceImpl; + + SourceKey sourceKey = new SourceKey(auditInfo.getAuditApplication(), auditInfo.getAuditService(), auditInfo.getAuditMethod()); + if(sourceIds.get() == null) + { + sourceIds.set(new HashMap()); + } + Long id = sourceIds.get().get(sourceKey); + if(id != null) + { + auditSourceImpl = (AuditSourceImpl) getSession().get(AuditSourceImpl.class, id.longValue()); + if(auditSourceImpl != null) + { + return auditSourceImpl; + } + } + + if ((auditInfo.getAuditService() != null) + && (auditInfo.getAuditService().length() > 0) && (auditInfo.getAuditMethod() != null) + && (auditInfo.getAuditMethod().length() > 0)) + { + auditSourceImpl = AuditSourceImpl.getApplicationSource(getSession(), auditInfo.getAuditApplication(), + auditInfo.getAuditService(), auditInfo.getAuditMethod()); + if (auditSourceImpl == null) + { + auditSourceImpl = new AuditSourceImpl(); + auditSourceImpl.setApplication(auditInfo.getAuditApplication()); + auditSourceImpl.setService(auditInfo.getAuditService()); + auditSourceImpl.setMethod(auditInfo.getAuditMethod()); + getSession().save(auditSourceImpl); + } + } + else + { + auditSourceImpl = AuditSourceImpl.getApplicationSource(getSession(), auditInfo.getAuditApplication()); + if (auditSourceImpl == null) + { + auditSourceImpl = new AuditSourceImpl(); + auditSourceImpl.setApplication(auditInfo.getAuditApplication()); + getSession().save(auditSourceImpl); + } + } + sourceIds.get().put(sourceKey, Long.valueOf(auditSourceImpl.getId())); + return auditSourceImpl; + } + + private AuditDateImpl getAuditDate(AuditInfo auditInfo) + { + Calendar cal = GregorianCalendar.getInstance(); + cal.setTime(auditInfo.getDate()); + cal.set(Calendar.MILLISECOND, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.HOUR_OF_DAY, 0); + Date required = cal.getTime(); + + AuditDateImpl auditDate; + if (auditDateImplId.get() == null) + { + auditDate = AuditDateImpl.getLatestDate(getSession()); + if (auditDate == null) + { + // The first entry ever so we just make it + auditDate = new AuditDateImpl(auditInfo.getDate()); + getSession().save(auditDate); + } + auditDateImplId.set(Long.valueOf(auditDate.getId())); + } + else + { + auditDate = (AuditDateImpl) getSession().get(AuditDateImpl.class, auditDateImplId.get().longValue()); + if ((auditDate == null) || (!required.equals(auditDate.getDate()))) + { + auditDate = AuditDateImpl.getLatestDate(getSession()); + if (auditDate == null) + { + // The first entry ever so we just make it + auditDate = new AuditDateImpl(auditInfo.getDate()); + getSession().save(auditDate); + } + auditDateImplId.set(Long.valueOf(auditDate.getId())); + } + } + while (!required.equals(auditDate.getDate())) + { + Date nextDate = Duration.add(auditDate.getDate(), new Duration("P1D")); + auditDate = new AuditDateImpl(nextDate); + getSession().save(auditDate); + auditDateImplId.set(Long.valueOf(auditDate.getId())); + } + return auditDate; + } + + private AuditConfigImpl getAuditConfig(AuditInfo auditInfo) + { + AuditConfigImpl auditConfig; + if ((auditConfiguration.get() == null) || (auditConfiguration.get() != auditInfo.getAuditConfiguration())) + { + auditConfig = AuditConfigImpl.getLatestConfig(getSession()); + if (auditConfig == null) + { + auditConfig = createNewAuditConfigImpl(auditInfo); + } + else + { + InputStream current = new BufferedInputStream(auditInfo.getAuditConfiguration().getInputStream()); + ContentReader reader = contentStore.getReader(auditConfig.getConfigURL()); + reader.setMimetype(MimetypeMap.MIMETYPE_XML); + reader.setEncoding("UTF-8"); + InputStream last = new BufferedInputStream(reader.getContentInputStream()); + int currentValue = -2; + int lastValue = -2; + try + { + while ((currentValue != -1) && (lastValue != -1) && (currentValue == lastValue)) + { + currentValue = current.read(); + lastValue = last.read(); + + } + } + catch (IOException e) + { + throw new AlfrescoRuntimeException( + "Failed to read and validate current audit configuration against the last", e); + } + if (currentValue != lastValue) + { + // Files are different - require a new entry + auditConfig = createNewAuditConfigImpl(auditInfo); + } + else + { + // No change + } + } + auditConfigImplId.set(Long.valueOf(auditConfig.getId())); + auditConfiguration.set(auditInfo.getAuditConfiguration()); + } + else + { + auditConfig = (AuditConfigImpl) getSession() + .get(AuditConfigImpl.class, auditConfigImplId.get().longValue()); + if (auditConfig == null) + { + auditConfig = createNewAuditConfigImpl(auditInfo); + } + } + return auditConfig; + } + + private AuditConfigImpl createNewAuditConfigImpl(AuditInfo auditInfo) + { + AuditConfigImpl auditConfig = new AuditConfigImpl(); + InputStream is = new BufferedInputStream(auditInfo.getAuditConfiguration().getInputStream()); + String url = AbstractContentStore.createNewUrl(); + ContentWriter writer = contentStore.getWriter(null, url); + writer.setMimetype(MimetypeMap.MIMETYPE_XML); + writer.setEncoding("UTF-8"); + writer.putContent(is); + auditConfig.setConfigURL(url); + getSession().save(auditConfig); + return auditConfig; + } + + /** + * Checks equality by type and uuid + */ + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (!(obj instanceof HibernateAuditDAO)) + { + return false; + } + HibernateAuditDAO that = (HibernateAuditDAO) obj; + return this.uuid.equals(that.uuid); + } + + /** + * @see #uuid + */ + public int hashCode() + { + return uuid.hashCode(); + } + + /** + * Does this Session contain any changes which must be synchronized with the store? + * + * @return true => changes are pending + */ + public boolean isDirty() + { + // create a callback for the task + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + return session.isDirty(); + } + }; + // execute the callback + return ((Boolean) getHibernateTemplate().execute(callback)).booleanValue(); + } + + /** + * Just flushes the session + */ + public void flush() + { + getSession().flush(); + } + + static class SourceKey + { + String application; + + String service; + + String method; + + SourceKey(String application, String service, String method) + { + this.application = application; + this.service = service; + this.method = method; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (!(this instanceof SourceKey)) + { + return false; + } + SourceKey other = (SourceKey) o; + return EqualsHelper.nullSafeEquals(this.application, other.application) + && EqualsHelper.nullSafeEquals(this.service, other.service) + && EqualsHelper.nullSafeEquals(this.method, other.method); + } + + @Override + public int hashCode() + { + int hash = application.hashCode(); + if (service != null) + { + hash = (hash * 37) + service.hashCode(); + } + if (method != null) + { + hash = (hash * 37) + method.hashCode(); + } + return hash; + } + } +} diff --git a/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java new file mode 100644 index 0000000000..7349e44be0 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AbstractAuditEntry.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. Licensed under the Mozilla Public License version 1.1 with a permitted attribution clause. You may obtain a copy of the License at + * http://www.alfresco.org/legal/license.txt Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditMode; +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.repo.audit.PublicServiceIdentifier; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Element; + +public abstract class AbstractAuditEntry +{ + private static Log s_logger = LogFactory.getLog(AbstractAuditEntry.class); + + private RecordOptionsImpl recordOptions = null; + + private AbstractFilter filter = null; + + private AuditMode auditMode = AuditMode.UNSET; + + private TrueFalseUnset enabled = TrueFalseUnset.UNSET; + + private TrueFalseUnset auditInternal = TrueFalseUnset.UNSET; + + private AbstractAuditEntry parent; + + private PublicServiceIdentifier publicServiceIdentifier; + + public AbstractAuditEntry() + { + super(); + } + + PublicServiceIdentifier getPublicServiceIdentifier() + { + return publicServiceIdentifier; + } + + public void setPublicServiceIdentifier(PublicServiceIdentifier publicServiceIdentifier) + { + this.publicServiceIdentifier = publicServiceIdentifier; + } + + void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver) + { + this.parent = parent; + + Attribute auditModeAttribute = element.attribute(AuditModel.AT_MODE); + if (auditModeAttribute != null) + { + auditMode = AuditMode.getAuditMode(auditModeAttribute.getValue()); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Audit Mode = "+auditMode); + } + + + Attribute enabledAttribute = element.attribute(AuditModel.AT_ENABLED); + if (enabledAttribute != null) + { + enabled = TrueFalseUnset.getTrueFalseUnset(enabledAttribute.getValue()); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Enabled = "+enabled); + } + + Attribute auditInternalAttribute = element.attribute(AuditModel.AT_AUDIT_INTERNAL); + if (auditInternalAttribute != null) + { + auditInternal = TrueFalseUnset.getTrueFalseUnset(auditInternalAttribute.getValue()); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Audit Internal = "+auditInternal); + } + + // Make record options + Element recordOptionElement = element.element(AuditModel.EL_RECORD_OPTIONS); + if (recordOptionElement != null) + { + recordOptions = new RecordOptionsImpl(); + recordOptions.configure(recordOptionElement, namespacePrefixResolver); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Record Options = "+recordOptions); + } + + // Make filters + Element filterElement = element.element(AuditModel.EL_FILTER); + if (filterElement != null) + { + filter = AbstractFilter.createFilter(filterElement, namespacePrefixResolver); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Filter = "+filter); + } + + } + + /* package */TrueFalseUnset getAuditInternal() + { + return auditInternal; + } + + /* package */AuditMode getAuditMode() + { + return auditMode; + } + + /* package */TrueFalseUnset getEnabled() + { + return enabled; + } + + /* package */AbstractFilter getFilter() + { + return filter; + } + + /* package */AbstractAuditEntry getParent() + { + return parent; + } + + /* package */RecordOptionsImpl getRecordOptions() + { + return recordOptions; + } + + protected AuditMode getEffectiveAuditMode() + { + AuditMode auditMode; + if (checkEnabled() == TrueFalseUnset.TRUE) + { + auditMode = getAuditModeOrParentAuditMode(); + } + else + { + auditMode = AuditMode.NONE; + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("... Effective audit mode is = "+auditMode); + } + return auditMode; + } + + private AuditMode getAuditModeOrParentAuditMode() + { + AuditMode auditMode = getAuditMode(); + if(s_logger.isDebugEnabled()) + { + s_logger.debug("... ... audit mode is = "+auditMode); + } + if (auditMode == AuditMode.UNSET) + { + if (getParent() == null) + { + return AuditMode.UNSET; + } + else + { + return getParent().getAuditModeOrParentAuditMode(); + } + } + else + { + return auditMode; + } + + } + + private TrueFalseUnset checkEnabled() + { + TrueFalseUnset effective = getEnabled(); + if (getParent() != null) + { + if ((getParent().checkEnabled() == TrueFalseUnset.TRUE) && (effective != TrueFalseUnset.FALSE)) + { + return TrueFalseUnset.TRUE; + } + } + else + { + if (effective == TrueFalseUnset.TRUE) + { + return TrueFalseUnset.TRUE; + } + } + return TrueFalseUnset.FALSE; + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/AbstractFilter.java b/source/java/org/alfresco/repo/audit/model/AbstractFilter.java new file mode 100644 index 0000000000..1ee1ac0f38 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AbstractFilter.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Element; + +/** + * The base class for filtering. + * + * This supports negating the filter, ie NOT. + * + * @author Andy Hind + */ +public abstract class AbstractFilter implements XMLModelElement +{ + private static Log s_logger = LogFactory.getLog(AbstractFilter.class); + + private boolean invert = false; + + public AbstractFilter() + { + super(); + } + + public static AbstractFilter createFilter(Element filterElement, NamespacePrefixResolver namespacePrefixResolver) + { + AbstractFilter filter; + + Attribute typeAttribute = filterElement.attribute(AuditModel.AT_TYPE); + if (typeAttribute == null) + { + throw new AuditModelException("A filter must specify it concrete type using xsi:type"); + } + if (typeAttribute.getStringValue().endsWith("FilterSet")) + { + filter = new FilterSet(); + } + else if (typeAttribute.getStringValue().endsWith("KeyFilter")) + { + filter = new KeyFilter(); + } + else if (typeAttribute.getStringValue().endsWith("ParameterFilter")) + { + filter = new ParameterFilter(); + } + else + { + throw new AuditModelException( + "Invalid filter type. It must be one of: FilterSet, KeyFilter, ParameterFilter "); + } + + filter.configure(filterElement, namespacePrefixResolver); + return filter; + } + + public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver) + { + Attribute invertAttribute = element.attribute(AuditModel.AT_INVERT); + if (invertAttribute != null) + { + invert = Boolean.valueOf(invertAttribute.getStringValue()).booleanValue(); + } + else + { + invert = false; + } + } + + /* package */boolean isInvert() + { + return invert; + } +} diff --git a/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java b/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java new file mode 100644 index 0000000000..aa200227ab --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AbstractNamedAuditEntry.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Element; + +public abstract class AbstractNamedAuditEntry extends AbstractAuditEntry +{ + private static Log s_logger = LogFactory.getLog(AbstractNamedAuditEntry.class); + + private String name; + + public AbstractNamedAuditEntry() + { + super(); + } + + @Override + void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver) + { + Attribute nameAttribute = element.attribute(AuditModel.AT_NAME); + if (nameAttribute != null) + { + name = nameAttribute.getStringValue(); + } + else + { + throw new AuditModelException("The name attribute is mandatory"); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Name = "+name); + } + + super.configure(parent, element, namespacePrefixResolver); + + } + + /* package */String getName() + { + return name; + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java b/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java new file mode 100644 index 0000000000..fb67f825b2 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/ApplicationAuditEntry.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.ApplicationAuditModel; +import org.alfresco.repo.audit.AuditMode; +import org.alfresco.repo.audit.RecordOptions; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class ApplicationAuditEntry extends AbstractNamedAuditEntry implements ApplicationAuditModel +{ + private static Log s_logger = LogFactory.getLog(ApplicationAuditEntry.class); + + public ApplicationAuditEntry() + { + super(); + } + + public AuditMode beforeExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Evaluating if application is audited ..."+application); + } + return getEffectiveAuditMode(); + } + + public AuditMode afterExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + throw new UnsupportedOperationException(); + } + + public AuditMode onError(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + throw new UnsupportedOperationException(); + } + + public RecordOptions getAuditRecordOptions(String application) + { + throw new UnsupportedOperationException(); + } + + +} diff --git a/source/java/org/alfresco/repo/audit/model/AuditEntry.java b/source/java/org/alfresco/repo/audit/model/AuditEntry.java new file mode 100644 index 0000000000..68678d200c --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AuditEntry.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.repo.audit.AuditConfiguration; +import org.alfresco.repo.audit.AuditMode; +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.repo.audit.RecordOptions; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Document; +import org.dom4j.DocumentException; +import org.dom4j.Element; +import org.dom4j.io.SAXReader; +import org.springframework.beans.factory.InitializingBean; + +public class AuditEntry extends AbstractAuditEntry implements InitializingBean, AuditModel +{ + private static Log s_logger = LogFactory.getLog(AuditEntry.class); + + private Map services = new HashMap(); + + private Map applications = new HashMap(); + + private AuditConfiguration auditConfiguration; + + private NamespacePrefixResolver namespacePrefixResolver; + + public AuditEntry() + { + super(); + } + + public AuditConfiguration getAuditConfiguration() + { + return auditConfiguration; + } + + public void setAuditConfiguration(AuditConfiguration auditConfiguration) + { + this.auditConfiguration = auditConfiguration; + } + + public void setNamespacePrefixResolver(NamespacePrefixResolver namespacePrefixResolver) + { + this.namespacePrefixResolver = namespacePrefixResolver; + } + + public void afterPropertiesSet() throws Exception + { + Document document = createDocument(); + Element root = document.getRootElement(); + // Check it is the correct thing + configure(null, root, namespacePrefixResolver); + } + + + + @Override + void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver) + { + if (!element.getNamespaceURI().equals(AuditModel.NAME_SPACE)) + { + throw new AuditModelException("Audit model has incorrect name space"); + } + if (!element.getName().equals(AuditModel.EL_AUDIT)) + { + throw new AuditModelException("Audit model has incorrect root node"); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Audit configuration"); + } + super.configure(parent, element, namespacePrefixResolver); + + // Add services + + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Adding services ..."); + } + for (Iterator nsit = element.elementIterator(AuditModel.EL_SERVICE); nsit.hasNext(); /**/) + { + Element serviceElement = (Element) nsit.next(); + ServiceAuditEntry service = new ServiceAuditEntry(); + service.configure(this, serviceElement, namespacePrefixResolver); + services.put(service.getName(), service); + } + + // Add Applications + + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Adding applications ..."); + } + for (Iterator nsit = element.elementIterator(AuditModel.EL_APPLICATION); nsit.hasNext(); /**/) + { + Element applicationElement = (Element) nsit.next(); + ApplicationAuditEntry application = new ApplicationAuditEntry(); + application.configure(this, applicationElement, namespacePrefixResolver); + applications.put(application.getName(), application); + } + } + + public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi) + { + String serviceName = getPublicServiceIdentifier().getPublicServiceName(mi); + ServiceAuditEntry service = services.get(serviceName); + if(service != null) + { + return service.beforeExecution(auditMode, mi); + } + else + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("No specific audit entry for service "+serviceName); + } + return getEffectiveAuditMode(); + + } + } + + public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public RecordOptions getAuditRecordOptions(MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public AuditMode onError(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + private Document createDocument() + { + InputStream is = auditConfiguration.getInputStream(); + if (is == null) + { + throw new AuditModelException("Audit configuration could not be opened"); + } + SAXReader reader = new SAXReader(); + try + { + Document document = reader.read(is); + is.close(); + return document; + } + catch (DocumentException e) + { + throw new AuditModelException("Failed to create audit model document ", e); + } + catch (IOException e) + { + throw new AuditModelException("Failed to close audit model document ", e); + } + + } + + public AuditMode beforeExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + ApplicationAuditEntry aae = applications.get(application); + if(aae != null) + { + return aae.beforeExecution(auditMode, application, description, key, args); + } + else + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("No specific audit entry for application "+application); + } + return getEffectiveAuditMode(); + + } + } + + public AuditMode afterExecution(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + throw new UnsupportedOperationException(); + } + + public AuditMode onError(AuditMode auditMode, String application, String description, NodeRef key, Object... args) + { + throw new UnsupportedOperationException(); + } + + public RecordOptions getAuditRecordOptions(String application) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelException.java b/source/java/org/alfresco/repo/audit/model/AuditModelException.java new file mode 100644 index 0000000000..7c72529429 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/AuditModelException.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * Exceptions from the audit model package. + * + * @author Andy Hind + */ +public class AuditModelException extends AlfrescoRuntimeException +{ + + /** + * Comment for serialVersionUID + */ + private static final long serialVersionUID = -2527034441058184109L; + + public AuditModelException(String msgId) + { + super(msgId); + } + + public AuditModelException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + public AuditModelException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + public AuditModelException(String msgId, Object[] msgParams, Throwable cause) + { + super(msgId, msgParams, cause); + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/FilterSet.java b/source/java/org/alfresco/repo/audit/model/FilterSet.java new file mode 100644 index 0000000000..d30b52fb84 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/FilterSet.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Element; + +/** + * This groups a set of filters together using AND or OR. They are evaluated in definition order with short cut evaluation if possible. The default beahviour is to or Filters + * together. + * + * @author Andy Hind + */ +public class FilterSet extends AbstractFilter implements XMLModelElement +{ + private static Log s_logger = LogFactory.getLog(FilterSet.class); + + private List filters = new ArrayList(); + + private FilterSetMode mode = FilterSetMode.OR; + + public FilterSet() + { + super(); + } + + @Override + public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver) + { + super.configure(element, namespacePrefixResolver); + + // Mode + Attribute modeAttribute = element.attribute(AuditModel.AT_MODE); + if (modeAttribute != null) + { + mode = FilterSetMode.getFilterSetMode(modeAttribute.getStringValue()); + } + + // Filters + + for (Iterator nsit = element.elementIterator(AuditModel.EL_FILTER); nsit.hasNext(); /**/) + { + Element filterElement = (Element) nsit.next(); + AbstractFilter filter = AbstractFilter.createFilter(filterElement, namespacePrefixResolver); + filters.add(filter); + } + + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/FilterSetMode.java b/source/java/org/alfresco/repo/audit/model/FilterSetMode.java new file mode 100644 index 0000000000..57dc90ee63 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/FilterSetMode.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +/** + * The enum to define if elements of a filter set are combined using AND or OR. + * + * @author Andy Hind + */ +public enum FilterSetMode +{ + AND, OR; + + public static FilterSetMode getFilterSetMode(String value) + { + if(value.equalsIgnoreCase("or")) + { + return FilterSetMode.OR; + } + else if(value.equalsIgnoreCase("or")) + { + return FilterSetMode.AND; + } + else + { + throw new AuditModelException("Invalid FilterSetMode: "+value); + } + } +} diff --git a/source/java/org/alfresco/repo/audit/model/KeyFilter.java b/source/java/org/alfresco/repo/audit/model/KeyFilter.java new file mode 100644 index 0000000000..ddacc9f5e9 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/KeyFilter.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Attribute; +import org.dom4j.Element; + +public class KeyFilter extends AbstractFilter +{ + private static Log s_logger = LogFactory.getLog(KeyFilter.class); + + private String expression; + + private KeyFilterMode keyFilterMode; + + public KeyFilter() + { + super(); + } + + @Override + public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver) + { + super.configure(element, namespacePrefixResolver); + + // Filter mode + Attribute keyFilterTypeAttribute = element.attribute(AuditModel.AT_MODE); + if(keyFilterTypeAttribute != null) + { + keyFilterMode = KeyFilterMode.getKeyFilterMode(keyFilterTypeAttribute.getStringValue()); + } + else + { + keyFilterMode = KeyFilterMode.ALL; + } + + // Expression + + Element expressionElement = element.element(AuditModel.EL_EXPRESSION); + if(expressionElement == null) + { + throw new AuditModelException("An expression is mandatory for a key filter"); + } + else + { + expression = expressionElement.getText(); + } + } + + + +} diff --git a/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java b/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java new file mode 100644 index 0000000000..23ef6d6e39 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/KeyFilterMode.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +/** + * This enum defines the type of restriction to apply to filter based on the key node ref. + * + * This restriction can be based upon: + * + *
    + *
  1. The path to the node + *
  2. The type of the node + *
  3. The presence of an aspect + *
  4. The NodeRef of the node + *
  5. An XPATH expression evaluated in the context of the node with the return tested for the node. + * e.g. ".[@cm:content = 'woof']" + *
  6. A simple value for equality tests given a non node argument + *
  7. The protocol of the store containing the node + *
  8. The identifier of the store containing the node + *
  9. Or no restriction + *
+ * + * @author Andy Hind + */ +public enum KeyFilterMode +{ + PATH, TYPE, ASPECT, NODE_REF, ALL, XPATH, VALUE, STORE_PROTOCOL, STORE_IDENTIFIER; + + public static KeyFilterMode getKeyFilterMode(String value) + { + if(value.equalsIgnoreCase("path")) + { + return KeyFilterMode.PATH; + } + else if(value.equalsIgnoreCase("type")) + { + return KeyFilterMode.TYPE; + } + else if(value.equalsIgnoreCase("aspect")) + { + return KeyFilterMode.ASPECT; + } + else if(value.equalsIgnoreCase("node_ref")) + { + return KeyFilterMode.NODE_REF; + } + else if(value.equalsIgnoreCase("all")) + { + return KeyFilterMode.ALL; + } + else if(value.equalsIgnoreCase("xpath")) + { + return KeyFilterMode.XPATH; + } + else if(value.equalsIgnoreCase("value")) + { + return KeyFilterMode.VALUE; + } + else if(value.equalsIgnoreCase("store_protocol")) + { + return KeyFilterMode.STORE_PROTOCOL; + } + else if(value.equalsIgnoreCase("store_identifier")) + { + return KeyFilterMode.STORE_IDENTIFIER; + } + else + { + throw new AuditModelException("Unknown KeyFilterMode: "+value); + } + } +} diff --git a/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java new file mode 100644 index 0000000000..a59d6b7a72 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/MethodAuditEntry.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditMode; +import org.alfresco.repo.audit.MethodAuditModel; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class MethodAuditEntry extends AbstractNamedAuditEntry implements MethodAuditModel +{ + private static Log s_logger = LogFactory.getLog(MethodAuditEntry.class); + + public MethodAuditEntry() + { + super(); + // TODO Auto-generated constructor stub + } + + public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi) + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Evaluating if method is audited ..."+((ServiceAuditEntry)getParent()).getName()+"."+getName()); + } + return getEffectiveAuditMode(); + } + + public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public AuditMode onError(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public RecordOptionsImpl getAuditRecordOptions(MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/ParameterFilter.java b/source/java/org/alfresco/repo/audit/model/ParameterFilter.java new file mode 100644 index 0000000000..6bfe56d70a --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/ParameterFilter.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Element; + +public class ParameterFilter extends KeyFilter implements XMLModelElement +{ + private static Log s_logger = LogFactory.getLog(ParameterFilter.class); + + private QName parameterName; + + public ParameterFilter() + { + super(); + } + + @Override + public void configure(Element element, NamespacePrefixResolver namespacePrefixResolver) + { + super.configure(element, namespacePrefixResolver); + + Element parameterNameElement = element.element(AuditModel.EL_PARAMETER_NAME); + if(parameterNameElement == null) + { + throw new AuditModelException("A parameter is mandatory for a parameter filter"); + } + else + { + String stringQName = parameterNameElement.getStringValue(); + if (stringQName.charAt(1) == '{') + { + parameterName = QName.createQName(stringQName); + } + else + { + parameterName = QName.createQName(stringQName); + } + } + } + + +} diff --git a/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java b/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java new file mode 100644 index 0000000000..5b9de6ec7a --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/RecordOptionsImpl.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.repo.audit.RecordOptions; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Element; + +public class RecordOptionsImpl implements XMLModelElement, RecordOptions +{ + private static Log s_logger = LogFactory.getLog(RecordOptionsImpl.class); + + private TrueFalseUnset recordPath = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordFilters = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordSerializedReturnValue = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordSerializedExceptions = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordSerializedMethodArguments = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordSerializedKeyPropertiesBeforeEvaluation = TrueFalseUnset.UNSET; + + private TrueFalseUnset recordSerializedKeyPropertiesAfterEvaluation = TrueFalseUnset.UNSET; + + public RecordOptionsImpl() + { + super(); + } + + public static RecordOptionsImpl mergeRecordOptions(RecordOptions primary, RecordOptions secondary) + { + RecordOptionsImpl answer = new RecordOptionsImpl(); + setOptions(answer, primary, true); + setOptions(answer, secondary, false); + return answer; + } + + private static void setOptions(RecordOptionsImpl on, RecordOptions from, boolean force) + { + if(force || on.recordFilters.equals( TrueFalseUnset.UNSET)) + { + on.recordFilters = from.getRecordFilters(); + } + if(force || on.recordPath.equals( TrueFalseUnset.UNSET)) + { + on.recordPath = from.getRecordPath(); + } + if(force || on.recordSerializedExceptions.equals( TrueFalseUnset.UNSET)) + { + on.recordSerializedExceptions = from.getRecordSerializedExceptions(); + } + if(force || on.recordSerializedKeyPropertiesAfterEvaluation.equals( TrueFalseUnset.UNSET)) + { + on.recordSerializedKeyPropertiesAfterEvaluation = from.getRecordSerializedKeyPropertiesAfterEvaluation(); + } + if(force || on.recordSerializedKeyPropertiesBeforeEvaluation.equals( TrueFalseUnset.UNSET)) + { + on.recordSerializedKeyPropertiesBeforeEvaluation = from.getRecordSerializedKeyPropertiesBeforeEvaluation(); + } + if(force || on.recordSerializedMethodArguments.equals( TrueFalseUnset.UNSET)) + { + on.recordSerializedMethodArguments = from.getRecordSerializedMethodArguments(); + } + if(force || on.recordSerializedReturnValue.equals( TrueFalseUnset.UNSET)) + { + on.recordSerializedReturnValue = from.getRecordSerializedReturnValue(); + } + } + + public TrueFalseUnset getRecordFilters() + { + return recordFilters; + } + + public TrueFalseUnset getRecordPath() + { + return recordPath; + } + + public TrueFalseUnset getRecordSerializedExceptions() + { + return recordSerializedExceptions; + } + + public TrueFalseUnset getRecordSerializedKeyPropertiesAfterEvaluation() + { + return recordSerializedKeyPropertiesAfterEvaluation; + } + + public TrueFalseUnset getRecordSerializedKeyPropertiesBeforeEvaluation() + { + return recordSerializedKeyPropertiesBeforeEvaluation; + } + + public TrueFalseUnset getRecordSerializedMethodArguments() + { + return recordSerializedMethodArguments; + } + + public TrueFalseUnset getRecordSerializedReturnValue() + { + return recordSerializedReturnValue; + } + + public void configure(Element recordOptionElement, NamespacePrefixResolver namespacePrefixResolver) + { + Element recordFiltersElement = recordOptionElement.element(AuditModel.EL_RECORD_FILTERS); + if (recordFiltersElement != null) + { + recordFilters = TrueFalseUnset.getTrueFalseUnset(recordFiltersElement.getStringValue()); + } + + Element recordPathElement = recordOptionElement.element(AuditModel.EL_RECORD_PATH); + if (recordPathElement != null) + { + recordPath = TrueFalseUnset.getTrueFalseUnset(recordPathElement.getStringValue()); + } + + Element recordSerAgrsElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_ARGS); + if (recordSerAgrsElement != null) + { + recordSerializedMethodArguments = TrueFalseUnset.getTrueFalseUnset(recordSerAgrsElement.getStringValue()); + } + + Element recordSerExElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_EX); + if (recordSerExElement != null) + { + recordSerializedExceptions = TrueFalseUnset.getTrueFalseUnset(recordSerExElement.getStringValue()); + } + + Element recordSerPropAfterElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_PROP_AFTER); + if (recordSerPropAfterElement != null) + { + recordSerializedKeyPropertiesAfterEvaluation = TrueFalseUnset.getTrueFalseUnset(recordSerPropAfterElement + .getStringValue()); + } + + Element recordSerPropBeforeElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_PROP_BEFORE); + if (recordSerPropBeforeElement != null) + { + recordSerializedKeyPropertiesBeforeEvaluation = TrueFalseUnset.getTrueFalseUnset(recordSerPropBeforeElement + .getStringValue()); + } + + Element recordSerRetElement = recordOptionElement.element(AuditModel.EL_RECORD_SER_RETURN_VAL); + if (recordSerRetElement != null) + { + recordSerializedReturnValue = TrueFalseUnset.getTrueFalseUnset(recordSerRetElement.getStringValue()); + } + + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(); + builder.append("Record Options("); + builder.append("Filters=").append(getRecordFilters()); + builder.append(",Path=").append(getRecordPath()); + builder.append(",Exception=").append(getRecordSerializedExceptions()); + builder.append(",PropertiesBefore=").append(getRecordSerializedKeyPropertiesAfterEvaluation()); + builder.append(",PropertiesAfter=").append(getRecordSerializedKeyPropertiesBeforeEvaluation()); + builder.append(",Args=").append(getRecordSerializedMethodArguments()); + builder.append(",Return=").append(getRecordSerializedReturnValue()); + builder.append(")"); + return builder.toString(); + } + + +} diff --git a/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java new file mode 100644 index 0000000000..764f004044 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/ServiceAuditEntry.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.alfresco.repo.audit.AuditMode; +import org.alfresco.repo.audit.AuditModel; +import org.alfresco.repo.audit.MethodAuditModel; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.dom4j.Element; + +public class ServiceAuditEntry extends AbstractNamedAuditEntry implements MethodAuditModel +{ + private static Log s_logger = LogFactory.getLog(ServiceAuditEntry.class); + + private Map methods = new HashMap(); + + public ServiceAuditEntry() + { + super(); + } + + @Override + void configure(AbstractAuditEntry parent, Element element, NamespacePrefixResolver namespacePrefixResolver) + { + super.configure(parent, element, namespacePrefixResolver); + + // Add Methods + + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Adding methods to service "+getName()); + } + for (Iterator nsit = element.elementIterator(AuditModel.EL_METHOD); nsit.hasNext(); /**/) + { + Element methodElement = (Element) nsit.next(); + MethodAuditEntry method = new MethodAuditEntry(); + method.configure(this, methodElement, namespacePrefixResolver); + methods.put(method.getName(), method); + } + if(s_logger.isDebugEnabled()) + { + s_logger.debug("...added methods for service "+getName()); + } + } + + public AuditMode beforeExecution(AuditMode auditMode, MethodInvocation mi) + { + String methodName = mi.getMethod().getName(); + MethodAuditEntry method = methods.get(methodName); + if (method != null) + { + return method.beforeExecution(auditMode, mi); + } + else + { + if(s_logger.isDebugEnabled()) + { + s_logger.debug("Evaluating if service is audited (no specific setting) for "+getName()+"."+methodName); + } + return getEffectiveAuditMode(); + } + } + + public AuditMode afterExecution(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public AuditMode onError(AuditMode auditMode, MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + + public RecordOptionsImpl getAuditRecordOptions(MethodInvocation mi) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java b/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java new file mode 100644 index 0000000000..6f2eb54368 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/TrueFalseUnset.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +/** + * An enum for the values + *
    + *
  1. TRUE + *
  2. FALSE + *
  3. UNSET + *
+ * + * @author Andy Hind + */ +public enum TrueFalseUnset +{ + TRUE, FALSE, UNSET; + + public static TrueFalseUnset getTrueFalseUnset(String value) + { + if(value.equalsIgnoreCase("true")) + { + return TrueFalseUnset.TRUE; + } + else if(value.equalsIgnoreCase("false")) + { + return TrueFalseUnset.FALSE; + } + else if(value.equalsIgnoreCase("unset")) + { + return TrueFalseUnset.UNSET; + } + else + { + throw new AuditModelException("Invalid value for TrueFalseUnset: "+value); + } + } +} diff --git a/source/java/org/alfresco/repo/audit/model/XMLModelElement.java b/source/java/org/alfresco/repo/audit/model/XMLModelElement.java new file mode 100644 index 0000000000..11c3293232 --- /dev/null +++ b/source/java/org/alfresco/repo/audit/model/XMLModelElement.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.audit.model; + +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.dom4j.Element; + +public interface XMLModelElement +{ + void configure(Element element, NamespacePrefixResolver namespacePrefixResolver ); +} diff --git a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java index 95fdb60f6c..46f01e3bbd 100644 --- a/source/java/org/alfresco/repo/cache/EhCacheAdapter.java +++ b/source/java/org/alfresco/repo/cache/EhCacheAdapter.java @@ -100,13 +100,6 @@ public class EhCacheAdapter public void clear() { - try - { - cache.removeAll(); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("Failed to clear cache", e); - } + cache.removeAll(); } } diff --git a/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java new file mode 100644 index 0000000000..f3dc89a82a --- /dev/null +++ b/source/java/org/alfresco/repo/cache/EhCacheManagerFactoryBean.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.cache; + +import java.io.IOException; + +import net.sf.ehcache.CacheException; +import net.sf.ehcache.CacheManager; + +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.Resource; + +/** + * This is virtually a copy of the Springframework version, with the exception + * that it uses the newer constructors for the EHCacheManager + * instances. + * + * @author Derek Hulley + */ +public class EhCacheManagerFactoryBean implements FactoryBean, InitializingBean, DisposableBean +{ + protected final Log logger = LogFactory.getLog(EhCacheManagerFactoryBean.class); + + private Resource configLocation; + private CacheManager cacheManager; + + /** + * + * @param configLocation a resource location using the file: or classpath: prefix + */ + public void setConfigLocation(Resource configLocation) + { + this.configLocation = configLocation; + } + + public void afterPropertiesSet() throws IOException, CacheException + { + PropertyCheck.mandatory(this, "configLocation", configLocation); + + logger.info("Initializing EHCache CacheManager"); + this.cacheManager = new CacheManager(this.configLocation.getURL()); + } + + public Object getObject() + { + return this.cacheManager; + } + + public Class getObjectType() + { + return (this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class); + } + + public boolean isSingleton() + { + return true; + } + + public void destroy() + { + logger.info("Shutting down EHCache CacheManager"); + this.cacheManager.shutdown(); + } +} diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java index 3348d3f097..f2080be2b4 100644 --- a/source/java/org/alfresco/repo/cache/TransactionalCache.java +++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java @@ -16,7 +16,6 @@ */ package org.alfresco.repo.cache; -import java.io.IOException; import java.io.Serializable; import java.util.List; @@ -438,15 +437,8 @@ public class TransactionalCache // and also serves to ensure that the shared cache will be ignored // for the remainder of the transaction txnData.isClearOn = true; - try - { - txnData.updatedItemsCache.removeAll(); - txnData.removedItemsCache.removeAll(); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("Failed to clear caches", e); - } + txnData.updatedItemsCache.removeAll(); + txnData.removedItemsCache.removeAll(); } else // no transaction { diff --git a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java index 73fae3b2e7..15e27d8478 100644 --- a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracter.java @@ -29,7 +29,6 @@ import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.poi.poifs.eventfilesystem.POIFSReader; import org.apache.poi.poifs.eventfilesystem.POIFSReaderEvent; @@ -130,7 +129,8 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter private static final String ENCODING_UNICODE = "001F"; private static final String SUBSTG_MESSAGEBODY = "1000"; - private static final String SUBSTG_RECIPIENTEMAIL = "39FE"; + private static final String SUBSTG_RECIPIENTEMAIL = "39FE"; // 7bit email address + private static final String SUBSTG_RECIPIENTSEARCH = "300B"; // address 'search' variant private static final String SUBSTG_RECEIVEDEMAIL = "0076"; private static final String SUBSTG_SENDEREMAIL = "0C1F"; private static final String SUBSTG_DATE = "0047"; @@ -159,6 +159,27 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter { receipientEmails.get().add(convertExchangeAddress(extractText())); } + else if (type.equals(SUBSTG_RECIPIENTSEARCH)) + { + String email = extractText(ENCODING_TEXT); + int smptIndex = email.indexOf("SMTP:"); + if (smptIndex != -1) + { + /* also may be used for SUBSTG_RECIPIENTTRANSPORT = "5FF7"; + with search for SMPT followed by a null char */ + + // this is a secondary mechanism for encoding a receipient email address + // the 7 bit email address may not have been set by Outlook - so this is needed instead + // handle null character at end of string + int endIndex = email.length(); + if (email.codePointAt(email.length() - 1) == 0) + { + endIndex--; + } + email = email.substring(smptIndex + 5, endIndex); + receipientEmails.get().add(email); + } + } else if (type.equals(SUBSTG_RECEIVEDEMAIL)) { destination.put(ContentModel.PROP_ADDRESSEE, convertExchangeAddress(extractText())); @@ -169,8 +190,8 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter } else if (type.equals(SUBSTG_DATE)) { - // the date is not really plain text - but it's easier to parse as such - String date = extractText(); + // the date is not "really" plain text - but it's appropriate to parse as such + String date = extractText(ENCODING_TEXT); int valueIndex = date.indexOf("l="); if (valueIndex != -1) { @@ -203,15 +224,28 @@ public class MailMetadataExtracter extends AbstractMetadataExtracter */ private String extractText() throws IOException + { + return extractText(this.encoding); + } + + /** + * Extract the text from the stream based on the encoding + * + * @return String + * + * @throws IOException + */ + private String extractText(String encoding) + throws IOException { byte[] data = new byte[stream.available()]; stream.read(data); - if (this.encoding.equals(ENCODING_TEXT) || this.encoding.equals(ENCODING_BINARY)) + if (encoding.equals(ENCODING_TEXT) || encoding.equals(ENCODING_BINARY)) { return new String(data); } - else if (this.encoding.equals(ENCODING_UNICODE)) + else if (encoding.equals(ENCODING_UNICODE)) { // convert double-byte encoding to single byte for String conversion byte[] b = new byte[data.length >> 1]; diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index f0e1b7be09..8172f86235 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; @@ -31,6 +32,7 @@ import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyScope; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; +import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -401,6 +403,23 @@ public class CopyServiceImpl implements CopyService } } + // if the parent node is the same, then remove the name property - it will have to + // be changed by the client code + AssociationDefinition assocDef = dictionaryService.getAssociation(destinationAssocTypeQName); + if (!assocDef.isChild()) + { + throw new AlfrescoRuntimeException("Association is not a child association: " + destinationAssocTypeQName); + } + else + { + ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef; + if (!childAssocDef.getDuplicateChildNamesAllowed()) + { + // duplicate children are not allowed. + properties.remove(ContentModel.PROP_NAME); + } + } + // Create the new node ChildAssociationRef destinationChildAssocRef = this.nodeService.createNode( destinationParent, diff --git a/source/java/org/alfresco/repo/domain/NodeStatus.java b/source/java/org/alfresco/repo/domain/NodeStatus.java index 4df06d8cf1..53969c0a69 100644 --- a/source/java/org/alfresco/repo/domain/NodeStatus.java +++ b/source/java/org/alfresco/repo/domain/NodeStatus.java @@ -41,9 +41,9 @@ public interface NodeStatus public void setNode(Node node); - public String getChangeTxnId(); + public Transaction getTransaction(); - public void setChangeTxnId(String txnId); + public void setTransaction(Transaction transaction); public boolean isDeleted(); } diff --git a/source/java/org/alfresco/repo/domain/Server.java b/source/java/org/alfresco/repo/domain/Server.java new file mode 100644 index 0000000000..f07c70b423 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/Server.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain; + +/** + * Interface for persistent server objects. These persist + * details of the servers that have committed transactions to the + * database, for instance. + * + * @author Derek Hulley + */ +public interface Server +{ + public Long getId(); + + public String getIpAddress(); + + public void setIpAddress(String ipAddress); +} diff --git a/source/java/org/alfresco/repo/domain/Transaction.java b/source/java/org/alfresco/repo/domain/Transaction.java new file mode 100644 index 0000000000..42633a70e7 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/Transaction.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain; + +/** + * Interface for persistent transaction objects. + * + * @author Derek Hulley + */ +public interface Transaction +{ + public Long getId(); + + public String getChangeTxnId(); + + public void setChangeTxnId(String changeTxnId); + + public Server getServer(); + + public void setServer(Server server); +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java index 47014f7587..fc9f9535ba 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java @@ -31,8 +31,10 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.Server; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.StoreKey; +import org.alfresco.repo.domain.Transaction; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.StoreRef; @@ -53,8 +55,11 @@ import org.hibernate.exception.ConstraintViolationException; public class HibernateNodeTest extends BaseSpringTest { private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/HibernateNodeTest"; + private static int i = 0; private Store store; + private Server server; + private Transaction transaction; public HibernateNodeTest() { @@ -68,6 +73,18 @@ public class HibernateNodeTest extends BaseSpringTest store.setKey(storeKey); // persist so that it is present in the hibernate cache getSession().save(store); + + server = (Server) getSession().get(ServerImpl.class, new Long(1)); + if (server == null) + { + server = new ServerImpl(); + server.setIpAddress("" + "i_" + System.currentTimeMillis()); + getSession().save(server); + } + transaction = new TransactionImpl(); + transaction.setServer(server); + transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + getSession().save(transaction); } protected void onTearDownInTransaction() @@ -108,7 +125,7 @@ public class HibernateNodeTest extends BaseSpringTest // create the node status NodeStatus nodeStatus = new NodeStatusImpl(); nodeStatus.setKey(key); - nodeStatus.setChangeTxnId("txn:123"); + nodeStatus.setTransaction(transaction); getSession().save(nodeStatus); // create a new Node @@ -131,7 +148,7 @@ public class HibernateNodeTest extends BaseSpringTest node = nodeStatus.getNode(); assertNotNull("Node was not attached to status", node); // change the values - nodeStatus.setChangeTxnId("txn:456"); + transaction.setChangeTxnId("txn:456"); // delete the node getSession().delete(node); @@ -351,7 +368,7 @@ public class HibernateNodeTest extends BaseSpringTest NodeStatus containerNodeStatus = new NodeStatusImpl(); containerNodeStatus.setKey(containerNodeKey); containerNodeStatus.setNode(containerNode); - containerNodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + containerNodeStatus.setTransaction(transaction); getSession().save(containerNodeStatus); // make content node 1 Node contentNode1 = new NodeImpl(); @@ -366,7 +383,7 @@ public class HibernateNodeTest extends BaseSpringTest NodeStatus contentNodeStatus1 = new NodeStatusImpl(); contentNodeStatus1.setKey(contentNodeKey1); contentNodeStatus1.setNode(contentNode1); - contentNodeStatus1.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + contentNodeStatus1.setTransaction(transaction); getSession().save(contentNodeStatus1); // make content node 2 Node contentNode2 = new NodeImpl(); @@ -381,7 +398,7 @@ public class HibernateNodeTest extends BaseSpringTest NodeStatus contentNodeStatus2 = new NodeStatusImpl(); contentNodeStatus2.setKey(contentNodeKey2); contentNodeStatus2.setNode(contentNode2); - contentNodeStatus2.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + contentNodeStatus2.setTransaction(transaction); getSession().save(contentNodeStatus2); // create an association to content 1 ChildAssoc assoc1 = new ChildAssocImpl(); diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index 79485306d3..15fe0ed614 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -113,6 +113,16 @@ + + - - - - - - - - - - - - + + + + + + + + + + - @@ -309,25 +316,26 @@ select distinct - status.changeTxnId + transaction.changeTxnId from - org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + org.alfresco.repo.domain.hibernate.TransactionImpl as transaction where - status.changeTxnId > :currentTxnId + transaction.changeTxnId > :currentTxnId order by - status.changeTxnId + transaction.changeTxnId select - count(status.changeTxnId) + count(transaction.changeTxnId) from org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + join status.transaction as transaction where status.key.protocol = :storeProtocol and status.key.identifier = :storeIdentifier and status.node.id is not null and - status.changeTxnId = :changeTxnId + transaction.changeTxnId = :changeTxnId @@ -335,11 +343,12 @@ status from org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + join status.transaction as transaction where status.key.protocol = :storeProtocol and status.key.identifier = :storeIdentifier and status.node.id is not null and - status.changeTxnId = :changeTxnId + transaction.changeTxnId = :changeTxnId @@ -347,11 +356,12 @@ status from org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + join status.transaction as transaction where status.key.protocol = :storeProtocol and status.key.identifier = :storeIdentifier and status.node.id is null and - status.changeTxnId = :changeTxnId + transaction.changeTxnId = :changeTxnId diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java index 77ea062ebb..e5ceca5843 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeStatusImpl.java @@ -21,6 +21,7 @@ import java.io.Serializable; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; +import org.alfresco.repo.domain.Transaction; import org.alfresco.util.EqualsHelper; /** @@ -34,15 +35,16 @@ public class NodeStatusImpl implements NodeStatus, Serializable private NodeKey key; private Node node; - private String changeTxnId; + private Transaction transaction; + @Override public String toString() { StringBuilder sb = new StringBuilder(50); sb.append("NodeStatus") .append("[key=").append(key) .append(", node=").append(node == null ? null : node.getNodeRef()) - .append(", txn=").append(changeTxnId) + .append(", txn=").append(transaction) .append("]"); return sb.toString(); } @@ -85,14 +87,14 @@ public class NodeStatusImpl implements NodeStatus, Serializable this.node = node; } - public String getChangeTxnId() + public Transaction getTransaction() { - return changeTxnId; + return transaction; } - public void setChangeTxnId(String txnId) + public void setTransaction(Transaction transaction) { - this.changeTxnId = txnId; + this.transaction = transaction; } public boolean isDeleted() diff --git a/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java new file mode 100644 index 0000000000..0fab53f8a5 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/ServerImpl.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain.hibernate; + +import java.io.Serializable; + +import org.alfresco.repo.domain.Server; + +/** + * Bean containing all the persistence data representing a Server. + *

+ * This implementation of the {@link org.alfresco.repo.domain.Service Service} interface is + * Hibernate specific. + * + * @author Derek Hulley + */ +public class ServerImpl extends LifecycleAdapter implements Server, Serializable +{ + private static final long serialVersionUID = 8063452519040344479L; + + private Long id; + private String ipAddress; + + public ServerImpl() + { + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(50); + sb.append("Server") + .append("[id=").append(id) + .append(", ipAddress=").append(ipAddress) + .append("]"); + return sb.toString(); + } + + public Long getId() + { + return id; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setId(Long id) + { + this.id = id; + } + + public String getIpAddress() + { + return ipAddress; + } + + public void setIpAddress(String ipAddress) + { + this.ipAddress = ipAddress; + } +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml new file mode 100644 index 0000000000..e770f3f26c --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + server + from + org.alfresco.repo.domain.hibernate.ServerImpl as server + where + server.ipAddress = :ipAddress + + + diff --git a/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java new file mode 100644 index 0000000000..21a26946ea --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/TransactionImpl.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain.hibernate; + +import java.io.Serializable; + +import org.alfresco.repo.domain.Server; +import org.alfresco.repo.domain.Transaction; + +/** + * Bean containing all the persistence data representing a Transaction. + *

+ * This implementation of the {@link org.alfresco.repo.domain.Transaction Transaction} interface is + * Hibernate specific. + * + * @author Derek Hulley + */ +public class TransactionImpl extends LifecycleAdapter implements Transaction, Serializable +{ + private static final long serialVersionUID = -8264339795578077552L; + + private Long id; + private String changeTxnId; + private Server server; + + public TransactionImpl() + { + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(50); + sb.append("Transaction") + .append("[id=").append(id) + .append(", changeTxnId=").append(changeTxnId) + .append("]"); + return sb.toString(); + } + + public Long getId() + { + return id; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setId(Long id) + { + this.id = id; + } + + public String getChangeTxnId() + { + return changeTxnId; + } + + public void setChangeTxnId(String changeTransactionId) + { + this.changeTxnId = changeTransactionId; + } + + public Server getServer() + { + return server; + } + + public void setServer(Server server) + { + this.server = server; + } +} diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java new file mode 100644 index 0000000000..e17a13e6ae --- /dev/null +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2006 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.domain.schema; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.io.Writer; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch; +import org.alfresco.util.TempFileProvider; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.Dialect; +import org.hibernate.tool.hbm2ddl.DatabaseMetadata; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.orm.hibernate3.LocalSessionFactoryBean; +import org.springframework.util.ResourceUtils; + +/** + * Bootstraps the schema and schema update. The schema is considered missing if the applied patch table + * is not present, and the schema is considered empty if the applied patch table is empty. + * + * @author Derek Hulley + */ +public class SchemaBootstrap implements ApplicationListener +{ + /** The placeholder for the configured Dialect class name: ${db.script.dialect} */ + private static final String PLACEHOLDER_SCRIPT_DIALECT = "\\$\\{db\\.script\\.dialect\\}"; + + private static final String MSG_EXECUTING_SCRIPT = "schema.update.msg.executing_script"; + private static final String ERR_UPDATE_FAILED = "schema.update.err.update_failed"; + private static final String ERR_VALIDATION_FAILED = "schema.update.err.validation_failed"; + private static final String ERR_SCRIPT_NOT_RUN = "schema.update.err.update_script_not_run"; + private static final String ERR_SCRIPT_NOT_FOUND = "schema.update.err.script_not_found"; + private static final String ERR_STATEMENT_TERMINATOR = "schema.update.err.statement_terminator"; + + private static Log logger = LogFactory.getLog(SchemaBootstrap.class); + + private LocalSessionFactoryBean localSessionFactory; + private String schemaOuputFilename; + private boolean updateSchema; + private List postCreateScriptUrls; + private List validateUpdateScriptPatches; + private List applyUpdateScriptPatches; + + public SchemaBootstrap() + { + postCreateScriptUrls = new ArrayList(1); + validateUpdateScriptPatches = new ArrayList(4); + applyUpdateScriptPatches = new ArrayList(4); + } + + public void setLocalSessionFactory(LocalSessionFactoryBean localSessionFactory) throws BeansException + { + this.localSessionFactory = localSessionFactory; + } + + /** + * Set this to output the full database creation script + * + * @param schemaOuputFilename the name of a file to dump the schema to, or null to ignore + */ + public void setSchemaOuputFilename(String schemaOuputFilename) + { + this.schemaOuputFilename = schemaOuputFilename; + } + + /** + * Set whether to modify the schema or not. Either way, the schema will be validated. + * + * @param updateSchema true to update and validate the schema, otherwise false to just + * validate the schema. Default is true. + */ + public void setUpdateSchema(boolean updateSchema) + { + this.updateSchema = updateSchema; + } + + /** + * Set the scripts that must be executed after the schema has been created. + * + * @param postCreateScriptUrls file URLs + * + * @see #PLACEHOLDER_SCRIPT_DIALECT + */ + public void setPostCreateScriptUrls(List postUpdateScriptUrls) + { + this.postCreateScriptUrls = postUpdateScriptUrls; + } + + /** + * Set the schema script patches that must have been applied. These will not be + * applied to the database. These can be used where the script cannot be + * applied automatically or where a particular upgrade path is no longer supported. + * For example, at version 3.0, the upgrade scripts for version 1.4 may be considered + * unsupported - this doesn't prevent the manual application of the scripts, though. + * + * @param applyUpdateScriptPatches a list of schema patches to check + */ + public void setValidateUpdateScriptPatches(List scriptPatches) + { + this.validateUpdateScriptPatches = scriptPatches; + } + + /** + * Set the schema script patches that may be executed during an update. + * + * @param applyUpdateScriptPatches a list of schema patches to check + */ + public void setApplyUpdateScriptPatches(List scriptPatches) + { + this.applyUpdateScriptPatches = scriptPatches; + } + + public void onApplicationEvent(ApplicationEvent event) + { + if (!(event instanceof ContextRefreshedEvent)) + { + // only work on startup + return; + } + + // do everything in a transaction + Session session = getLocalSessionFactory().openSession(); + Transaction transaction = session.beginTransaction(); + try + { + // make sure that we don't autocommit + Connection connection = session.connection(); + connection.setAutoCommit(false); + + Configuration cfg = localSessionFactory.getConfiguration(); + // dump the schema, if required + if (schemaOuputFilename != null) + { + File schemaOutputFile = new File(schemaOuputFilename); + dumpSchemaCreate(cfg, schemaOutputFile); + } + + // update the schema, if required + if (updateSchema) + { + updateSchema(cfg, session, connection); + } + + // verify that all patches have been applied correctly + checkSchemaPatchScripts(cfg, session, connection, validateUpdateScriptPatches, false); // check scripts + checkSchemaPatchScripts(cfg, session, connection, applyUpdateScriptPatches, false); // check scripts + + // all done successfully + transaction.commit(); + } + catch (Throwable e) + { + try { transaction.rollback(); } catch (Throwable ee) {} + if (updateSchema) + { + throw new AlfrescoRuntimeException(ERR_UPDATE_FAILED, e); + } + else + { + throw new AlfrescoRuntimeException(ERR_VALIDATION_FAILED, e); + } + } + } + + private void dumpSchemaCreate(Configuration cfg, File schemaOutputFile) + { + // if the file exists, delete it + if (schemaOutputFile.exists()) + { + schemaOutputFile.delete(); + } + SchemaExport schemaExport = new SchemaExport(cfg) + .setFormat(true) + .setHaltOnError(true) + .setOutputFile(schemaOutputFile.getAbsolutePath()) + .setDelimiter(";"); + schemaExport.execute(false, false, false, true); + } + + private SessionFactory getLocalSessionFactory() + { + return (SessionFactory) localSessionFactory.getObject(); + } + + /** + * @return Returns the number of applied patches + */ + private int countAppliedPatches(Connection connection) throws Exception + { + Statement stmt = connection.createStatement(); + try + { + ResultSet rs = stmt.executeQuery("select count(id) from alf_applied_patch"); + rs.next(); + int count = rs.getInt(1); + return count; + } + catch (Throwable e) + { + // we'll try another table name + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + // for pre-1.4 databases, the table was named differently + stmt = connection.createStatement(); + try + { + ResultSet rs = stmt.executeQuery("select count(id) from applied_patch"); + rs.next(); + int count = rs.getInt(1); + return count; + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + } + + /** + * @return Returns the number of applied patches + */ + private boolean didPatchSucceed(Connection connection, String patchId) throws Exception + { + Statement stmt = connection.createStatement(); + try + { + ResultSet rs = stmt.executeQuery("select succeeded from alf_applied_patch where id = '" + patchId + "'"); + if (!rs.next()) + { + return false; + } + boolean succeeded = rs.getBoolean(1); + return succeeded; + } + catch (Throwable e) + { + // we'll try another table name + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + // for pre-1.4 databases, the table was named differently + stmt = connection.createStatement(); + try + { + ResultSet rs = stmt.executeQuery("select succeeded from applied_patch where id = '" + patchId + "'"); + if (!rs.next()) + { + return false; + } + boolean succeeded = rs.getBoolean(1); + return succeeded; + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + } + + /** + * Builds the schema from scratch or applies the necessary patches to the schema. + */ + private void updateSchema(Configuration cfg, Session session, Connection connection) throws Exception + { + boolean create = false; + try + { + countAppliedPatches(connection); + } + catch (Throwable e) + { + create = true; + } + if (create) + { + // the applied patch table is missing - we assume that all other tables are missing + // perform a full update using Hibernate-generated statements + File tempFile = TempFileProvider.createTempFile("AlfrescoSchemaCreate", ".sql"); + dumpSchemaCreate(cfg, tempFile); + executeScriptFile(cfg, connection, tempFile); + // execute post-create scripts (not patches) + for (String scriptUrl : this.postCreateScriptUrls) + { + executeScriptUrl(cfg, connection, scriptUrl); + } + } + else + { + // we have a database, so just run the update scripts + checkSchemaPatchScripts(cfg, session, connection, validateUpdateScriptPatches, false); // check for scripts that must have been run + checkSchemaPatchScripts(cfg, session, connection, applyUpdateScriptPatches, true); // execute scripts as required + // let Hibernate do any required updates + File tempFile = null; + Writer writer = null; + try + { + final Dialect dialect = Dialect.getDialect(cfg.getProperties()); + DatabaseMetadata metadata = new DatabaseMetadata(connection, dialect); + String[] sqls = cfg.generateSchemaUpdateScript(dialect, metadata); + if (sqls.length > 0) + { + tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate", ".sql"); + writer = new BufferedWriter(new FileWriter(tempFile)); + for (String sql : sqls) + { + writer.append(sql); + writer.append(";\n"); + } + } + } + finally + { + if (writer != null) + { + try {writer.close();} catch (Throwable e) {} + } + } + // execute if there were changes raised by Hibernate + if (tempFile != null) + { + executeScriptFile(cfg, connection, tempFile); + } + } + } + + /** + * Check that the necessary scripts have been executed against the database + */ + private void checkSchemaPatchScripts( + Configuration cfg, + Session session, + Connection connection, + List scriptPatches, + boolean apply) throws Exception + { + // first check if there have been any applied patches + int appliedPatchCount = countAppliedPatches(connection); + if (appliedPatchCount == 0) + { + // This is a new schema, so upgrade scripts are irrelevant + // and patches will not have been applied yet + return; + } + + for (SchemaUpgradeScriptPatch patch : scriptPatches) + { + final String patchId = patch.getId(); + final String scriptUrl = patch.getScriptUrl(); + + // check if the script was successfully executed + boolean wasSuccessfullyApplied = didPatchSucceed(connection, patchId); + if (wasSuccessfullyApplied) + { + // nothing to do - it has been done before + continue; + } + else if (!apply) + { + // the script was not run and may not be run automatically + throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_RUN, scriptUrl); + } + // it wasn't run and it can be run now + executeScriptUrl(cfg, connection, scriptUrl); + } + } + + private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception + { + Dialect dialect = Dialect.getDialect(cfg.getProperties()); + File scriptFile = getScriptFile(dialect.getClass(), scriptUrl); + // check that it exists + if (scriptFile == null) + { + throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_FOUND, scriptUrl); + } + // now execute it + executeScriptFile(cfg, connection, scriptFile); + } + + /** + * Replaces the dialect placeholder in the script URL and attempts to find a file for + * it. If not found, the dialect hierarchy will be walked until a compatible script is + * found. This makes it possible to have scripts that are generic to all dialects. + * + * @return Returns the file if found, otherwise null + */ + private File getScriptFile(Class dialectClazz, String scriptUrl) throws Exception + { + // replace the dialect placeholder + String dialectScriptUrl = scriptUrl.replaceAll(PLACEHOLDER_SCRIPT_DIALECT, dialectClazz.getName()); + // get a handle on the resource + try + { + File scriptFile = ResourceUtils.getFile(dialectScriptUrl); + if (scriptFile.exists()) + { + // found a compatible dialect version + return scriptFile; + } + } + catch (FileNotFoundException e) + { + // doesn't exist + } + // it wasn't found. Get the superclass of the dialect and try again + Class superClazz = dialectClazz.getSuperclass(); + if (Dialect.class.isAssignableFrom(superClazz)) + { + // we still have a Dialect - try again + return getScriptFile(superClazz, scriptUrl); + } + else + { + // we have exhausted all options + return null; + } + } + + private void executeScriptFile(Configuration cfg, Connection connection, File scriptFile) throws Exception + { + logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptFile)); + + BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(scriptFile), "UTF8")); + try + { + int line = 0; + // loop through all statements + StringBuilder sb = new StringBuilder(1024); + while(true) + { + String sql = reader.readLine(); + line++; + + if (sql == null) + { + // nothing left in the file + break; + } + + // trim it + sql = sql.trim(); + if (sql.length() == 0 || + sql.startsWith( "--" ) || + sql.startsWith( "//" ) || + sql.startsWith( "/*" ) ) + { + if (sb.length() > 0) + { + // we have an unterminated statement + throw AlfrescoRuntimeException.create(ERR_STATEMENT_TERMINATOR, (line - 1), scriptFile); + } + // there has not been anything to execute - it's just a comment line + continue; + } + // have we reached the end of a statement? + boolean execute = false; + if (sql.endsWith(";")) + { + sql = sql.substring(0, sql.length() - 1); + execute = true; + } + // append to the statement being built up + sb.append(" ").append(sql); + // execute, if required + if (execute) + { + Statement stmt = connection.createStatement(); + try + { + sql = sb.toString(); + if (logger.isDebugEnabled()) + { + logger.debug("Executing statment: " + sql); + } + stmt.execute(sql); + sb = new StringBuilder(1024); + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + } + } + } + finally + { + try { reader.close(); } catch (Throwable e) {} + } + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 18b1922b4e..4fe89d582b 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -586,13 +586,20 @@ public class FileFolderServiceImpl implements FileFolderService } else { - // copy the node - targetNodeRef = copyService.copy( - sourceNodeRef, - targetParentRef, - assocRef.getTypeQName(), - qname, - true); + try + { + // copy the node + targetNodeRef = copyService.copy( + sourceNodeRef, + targetParentRef, + assocRef.getTypeQName(), + qname, + true); + } + catch (DuplicateChildNodeNameException e) + { + throw new FileExistsException(targetParentRef, newName); + } } // Only update the name if it has changed diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index d42eccd354..279cdb5922 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -336,6 +336,10 @@ public class FileFolderServiceImplTest extends TestCase // make sure that it is an immediate child of the root List checkFileInfos = fileFolderService.search(workingRootNodeRef, NAME_L1_FOLDER_A, false); assertEquals("Folder not copied to root", 1, checkFileInfos.size()); + // copy properly + FileInfo checkFileInfo = fileFolderService.copy(folderToCopyRef, null, "new name"); + checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false); + assertEquals("Folder not renamed in root", 1, checkFileInfos.size()); // attempt illegal copy (existing) try { @@ -346,10 +350,6 @@ public class FileFolderServiceImplTest extends TestCase { // expected } - // copy properly - FileInfo checkFileInfo = fileFolderService.copy(folderToCopyRef, null, "new name"); - checkFileInfos = fileFolderService.search(workingRootNodeRef, checkFileInfo.getName(), false); - assertEquals("Folder not renamed in root", 1, checkFileInfos.size()); } public void testCreateFolder() throws Exception diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index 0a5709f410..f6de57d293 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -1630,10 +1630,7 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest NodeRef defRef = pathDefRef.getChildRef(); // now browse down using the node service - NodeRef checkParentRef = nodeService.getChildByName(rootNodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, parentRef.getId()); - assertNotNull("First level, non-named node not found", checkParentRef); - assertEquals(parentRef, checkParentRef); - NodeRef checkAbcRef = nodeService.getChildByName(checkParentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc"); + NodeRef checkAbcRef = nodeService.getChildByName(parentRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "abc"); assertNotNull("Second level, named node 'ABC' not found", checkAbcRef); assertEquals(abcRef, checkAbcRef); NodeRef checkDefRef = nodeService.getChildByName(checkAbcRef, ASSOC_TYPE_QNAME_TEST_CONTAINS, "def"); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 9972e889a2..85a214f845 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -149,7 +149,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl else { return new NodeRef.Status( - nodeStatus.getChangeTxnId(), + nodeStatus.getTransaction().getChangeTxnId(), nodeStatus.isDeleted()); } } @@ -1440,11 +1440,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // update old status NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true); oldNodeStatus.setNode(null); - oldNodeStatus.setChangeTxnId(txnId); + oldNodeStatus.getTransaction().setChangeTxnId(txnId); // create the new status NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true); newNodeStatus.setNode(nodeToMove); - newNodeStatus.setChangeTxnId(txnId); + newNodeStatus.getTransaction().setChangeTxnId(txnId); } } diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index 6ecccd65d1..b2872cdeaf 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -16,9 +16,14 @@ */ package org.alfresco.repo.node.db.hibernate; +import java.io.Serializable; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.zip.CRC32; import org.alfresco.error.AlfrescoRuntimeException; @@ -28,13 +33,17 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; +import org.alfresco.repo.domain.Server; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.StoreKey; +import org.alfresco.repo.domain.Transaction; import org.alfresco.repo.domain.hibernate.ChildAssocImpl; import org.alfresco.repo.domain.hibernate.NodeAssocImpl; import org.alfresco.repo.domain.hibernate.NodeImpl; import org.alfresco.repo.domain.hibernate.NodeStatusImpl; +import org.alfresco.repo.domain.hibernate.ServerImpl; import org.alfresco.repo.domain.hibernate.StoreImpl; +import org.alfresco.repo.domain.hibernate.TransactionImpl; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionalDao; @@ -71,9 +80,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs"; private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs"; private static final String QUERY_GET_CONTENT_DATA_STRINGS = "node.GetContentDataStrings"; + private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; /** a uuid identifying this unique instance */ - private String uuid; + private final String uuid; + + private final ReadLock serverReadLock; + private final WriteLock serverWriteLock; + private Server server; /** * @@ -81,6 +95,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public HibernateNodeDaoServiceImpl() { this.uuid = GUID.generate(); + + ReentrantReadWriteLock serverReadWriteLock = new ReentrantReadWriteLock(); + serverReadLock = serverReadWriteLock.readLock(); + serverWriteLock = serverReadWriteLock.writeLock(); } /** @@ -108,6 +126,93 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements return uuid.hashCode(); } + /** + * Gets/creates the server instance to use for the life of this instance + */ + private Server getServer() + { + // get readlock + serverReadLock.lock(); + try + { + if (server != null) + { + return server; + } + } + finally + { + serverReadLock.unlock(); + } + // get the write lock + serverWriteLock.lock(); + try + { + final String ipAddress = InetAddress.getLocalHost().getHostAddress(); + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SERVER_BY_IPADDRESS) + .setString("ipAddress", ipAddress); + return query.uniqueResult(); + } + }; + server = (Server) getHibernateTemplate().execute(callback); + // create it if it doesn't exist + if (server == null) + { + server = new ServerImpl(); + server.setIpAddress(ipAddress); + try + { + getSession().save(server); + } + catch (DataIntegrityViolationException e) + { + // get it again + server = (Server) getHibernateTemplate().execute(callback); + if (server == null) + { + throw new AlfrescoRuntimeException("Unable to create server instance: " + ipAddress); + } + } + } + return server; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException("Failed to create server instance", e); + } + finally + { + serverWriteLock.unlock(); + } + } + + private static final String RESOURCE_KEY_TRANSACTION_ID = "hibernate.transaction.id"; + private Transaction getCurrentTransaction() + { + Transaction transaction = null; + Serializable txnId = (Serializable) AlfrescoTransactionSupport.getResource(RESOURCE_KEY_TRANSACTION_ID); + if (txnId == null) + { + // no transaction instance has been bound to the transaction + transaction = new TransactionImpl(); + transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + transaction.setServer(getServer()); + txnId = getHibernateTemplate().save(transaction); + // bind the id + AlfrescoTransactionSupport.bindResource(RESOURCE_KEY_TRANSACTION_ID, txnId); + } + else + { + transaction = (Transaction) getHibernateTemplate().get(TransactionImpl.class, txnId); + } + return transaction; + } + /** * Does this Session contain any changes which must be * synchronized with the store? @@ -218,7 +323,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { status = new NodeStatusImpl(); status.setKey(nodeKey); - status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + status.setTransaction(getCurrentTransaction()); getHibernateTemplate().save(status); } // done @@ -237,7 +342,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } else { - status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + status.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); } } @@ -259,13 +364,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements // If that is the case, then the session has to be flushed so that the database // constraints aren't violated as the node creation will write to the database to // get an ID - if (status.getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId())) + if (status.getTransaction().getChangeTxnId().equals(AlfrescoTransactionSupport.getTransactionId())) { // flush getHibernateTemplate().flush(); } } - + // build a concrete node based on a bootstrap type Node node = new NodeImpl(); // set other required properties @@ -277,7 +382,11 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements // set required status properties status.setNode(node); - status.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + // assign a transaction + if (status.getTransaction() == null) + { + status.setTransaction(getCurrentTransaction()); + } // persist the nodestatus getHibernateTemplate().save(status); @@ -331,7 +440,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeRef nodeRef = node.getNodeRef(); NodeStatus nodeStatus = getNodeStatus(nodeRef, true); nodeStatus.setNode(null); - nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); + nodeStatus.getTransaction().setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); // finally delete the node getHibernateTemplate().delete(node); // flush to ensure constraints can't be violated @@ -371,7 +480,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { /* * This initial child association creation will fail IFF there is already - * an association of the given type between the two nodes. For new association + * an association of the given type and name between the two nodes. For new association * creation, this can only occur if two transactions attempt to create a secondary * child association between the same two nodes. As this is unlikely, it is * appropriate to just throw a runtime exception and let the second transaction @@ -383,28 +492,18 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements * if the association is recreated subsequently. */ - String uuid = childNode.getUuid(); + // assign a random name to the node + String randomName = GUID.generate(); ChildAssoc assoc = new ChildAssocImpl(); assoc.setTypeQName(assocTypeQName); - assoc.setChildNodeName(getShortName(uuid)); - assoc.setChildNodeNameCrc(getCrc(uuid)); + assoc.setChildNodeName(randomName); + assoc.setChildNodeNameCrc(-1L); // random names compete only with each other assoc.setQname(qname); assoc.setIsPrimary(isPrimary); assoc.buildAssociation(parentNode, childNode); // persist it, catching the duplicate child name - try - { - getHibernateTemplate().save(assoc); - } - catch (DataIntegrityViolationException e) - { - throw new AlfrescoRuntimeException("An association already exists between the two nodes: \n" + - " parent: " + parentNode.getId() + "\n" + - " child: " + childNode.getId() + "\n" + - " assoc: " + assocTypeQName, - e); - } + getHibernateTemplate().save(assoc); // done return assoc; } @@ -422,17 +521,22 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements */ String childNameNew = null; + long crc = -1; if (childName == null) { - childNameNew = childAssoc.getChild().getUuid(); + // random names compete only with each other, i.e. not at all + childNameNew = GUID.generate(); + crc = -1; } else { + // assigned names compete exactly childNameNew = childName.toLowerCase(); + crc = getCrc(childNameNew); } final String childNameNewShort = getShortName(childNameNew); - final long childNameNewCrc = getCrc(childNameNew); + final long childNameNewCrc = crc; // check if the name has changed if (childAssoc.getChildNodeNameCrc() == childNameNewCrc) diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java index 3000634953..54fb4c460b 100644 --- a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java +++ b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java @@ -62,13 +62,13 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport; * database is static then the L2 cache usage can be set to use * the NORMAL mode. REFRESH should be * used where the server will still be accessed from some clients - * despite the database changing. + * despite the database changing. NORMAL can be used + * in the case of the caches being clustered, i.e. the caches will + * not be out of date w.r.t. the database. * *

  • - * This process should not run continuously on a live - * server as it would be performing unecessary work. - * If it was left running, however, it would not - * lead to data corruption or such-like. Use the + * This process should only be used continuously where the index + * transactions are following the database transactions. Use the * {@link #setRunContinuously(boolean) runContinuously} property * to change this behaviour. *
  • @@ -91,7 +91,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I private static boolean started = false; /** The current transaction ID being processed */ private static String currentTxnId = START_TXN_ID; - /** kept to notify the thread that it should quite */ + /** kept to notify the thread that it should quit */ private boolean killThread = false; /** provides transactions to atomically index each missed transaction */ @@ -104,8 +104,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I private SearchService searcher; /** the component giving direct access to node instances */ private NodeService nodeService; - /** the stores to reindex */ - private List storeRefs; /** set this to run the index recovery component */ private boolean executeFullRecovery; /** set this on to keep checking for new transactions and never stop */ @@ -125,8 +123,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I public FullIndexRecoveryComponent() { - this.storeRefs = new ArrayList(2); - this.killThread = false; this.executeFullRecovery = false; this.runContinuously = false; @@ -193,21 +189,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I this.nodeService = nodeService; } - /** - * Set the stores that need reindexing - * - * @param storeRefStrings a list of strings representing store references - */ - public void setStores(List storeRefStrings) - { - storeRefs.clear(); - for (String storeRefStr : storeRefStrings) - { - StoreRef storeRef = new StoreRef(storeRefStr); - storeRefs.add(storeRef); - } - } - /** * Set this to true to initiate the full index recovery. *

    @@ -299,6 +280,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I { public Object doWork() { + List storeRefs = nodeService.getStores(); // reindex each store for (StoreRef storeRef : storeRefs) { @@ -352,8 +334,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I if (logger.isDebugEnabled()) { logger.debug("Full index recovery thread started: \n" + - " continuous: " + runContinuously + "\n" + - " stores: " + storeRefs); + " continuous: " + runContinuously); } } } @@ -377,8 +358,8 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I // reindex nodes List txnsIndexed = FullIndexRecoveryComponent.this.reindexNodes(); // reindex missing content - @SuppressWarnings("unused") - int missingContentCount = FullIndexRecoveryComponent.this.reindexMissingContent(); +// @SuppressWarnings("unused") +// int missingContentCount = FullIndexRecoveryComponent.this.reindexMissingContent(); // check if the process should terminate if (txnsIndexed.size() == 0 && !runContinuously) { @@ -417,73 +398,6 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I } } - /** - * @return Returns the number of documents reindexed - */ - private int reindexMissingContent() - { - int count = 0; - for (StoreRef storeRef : storeRefs) - { - count += reindexMissingContent(storeRef); - } - return count; - } - - /** - * @param storeRef the store to check for missing content - * @return Returns the number of documents reindexed - */ - private int reindexMissingContent(StoreRef storeRef) - { - SearchParameters sp = new SearchParameters(); - sp.addStore(storeRef); - - // search for it in the index - String query = "TEXT:" + LuceneIndexerImpl.NOT_INDEXED_CONTENT_MISSING; - sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery(query); - ResultSet results = null; - try - { - results = searcher.query(sp); - - int count = 0; - // loop over the results and get the details of the nodes that have missing content - List assocRefs = results.getChildAssocRefs(); - for (ChildAssociationRef assocRef : assocRefs) - { - final NodeRef childNodeRef = assocRef.getChildRef(); - // prompt for a reindex - it might fail again, but we just keep plugging away - TransactionWork reindexWork = new TransactionWork() - { - public Object doWork() - { - indexer.updateNode(childNodeRef); - return null; - } - }; - TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork); - count++; - } - // done - if (logger.isDebugEnabled()) - { - logger.debug("Reindexed missing content: \n" + - " store: " + storeRef + "\n" + - " node count: " + count); - } - return count; - } - finally - { - if (results != null) - { - results.close(); - } - } - } - /** * @return Returns the transaction ID just reindexed, i.e. where some work was performed */ @@ -572,16 +486,16 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I getSession().setCacheMode(l2CacheMode); // reindex each store - for (StoreRef storeRef : storeRefs) - { - if (!nodeService.exists(storeRef)) - { - // the store is not present - continue; - } - // reindex for store - reindexNodes(storeRef, changeTxnId); - } +// for (StoreRef storeRef : storeRefs) +// { +// if (!nodeService.exists(storeRef)) +// { +// // the store is not present +// continue; +// } +// // reindex for store +// reindexNodes(storeRef, changeTxnId); +// } // done return null; } @@ -675,10 +589,10 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I }; /** - * Retrieve all transaction IDs that are greater than the given transaction ID. + * Retrieve next 50 transaction IDs that are greater than the given transaction ID. * * @param currentTxnId the transaction ID that must be less than all returned results - * @return Returns an ordered list of transaction IDs + * @return Returns an ordered list of the next 50 transaction IDs */ @SuppressWarnings("unchecked") public List getNextChangeTxnIds(final String currentTxnId) @@ -689,6 +603,7 @@ public class FullIndexRecoveryComponent extends HibernateDaoSupport implements I { Query query = session.getNamedQuery(QUERY_GET_NEXT_CHANGE_TXN_IDS); query.setString("currentTxnId", currentTxnId) + .setMaxResults(50) .setReadOnly(true); return query.list(); } diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java index d39df7a8fc..bfd8e4a0ab 100644 --- a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java +++ b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java @@ -123,7 +123,7 @@ public class FullIndexRecoveryComponentTest extends TestCase String txnId = TransactionUtil.executeInNonPropagatingUserTransaction(txnService, dropNodeIndexWork); indexRecoverer.setExecuteFullRecovery(true); - indexRecoverer.setStores(storeRefStrings); +// indexRecoverer.setStores(storeRefStrings); // reindex indexRecoverer.reindex(); diff --git a/source/java/org/alfresco/repo/node/index/MissingContentReindexComponent.java b/source/java/org/alfresco/repo/node/index/MissingContentReindexComponent.java new file mode 100644 index 0000000000..2f1b115e62 --- /dev/null +++ b/source/java/org/alfresco/repo/node/index/MissingContentReindexComponent.java @@ -0,0 +1,741 @@ +///* +// * Copyright (C) 2005-2006 Alfresco, Inc. +// * +// * Licensed under the Mozilla Public License version 1.1 +// * with a permitted attribution clause. You may obtain a +// * copy of the License at +// * +// * http://www.alfresco.org/legal/license.txt +// * +// * Unless required by applicable law or agreed to in writing, +// * software distributed under the License is distributed on an +// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +// * either express or implied. See the License for the specific +// * language governing permissions and limitations under the +// * License. +// */ +//package org.alfresco.repo.node.index; +// +//import java.util.ArrayList; +//import java.util.List; +// +//import org.alfresco.error.AlfrescoRuntimeException; +//import org.alfresco.model.ContentModel; +//import org.alfresco.repo.domain.NodeStatus; +//import org.alfresco.repo.search.Indexer; +//import org.alfresco.repo.search.impl.lucene.LuceneIndexerImpl; +//import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; +//import org.alfresco.repo.transaction.TransactionUtil; +//import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +//import org.alfresco.service.cmr.repository.ChildAssociationRef; +//import org.alfresco.service.cmr.repository.NodeRef; +//import org.alfresco.service.cmr.repository.NodeService; +//import org.alfresco.service.cmr.repository.StoreRef; +//import org.alfresco.service.cmr.search.ResultSet; +//import org.alfresco.service.cmr.search.SearchParameters; +//import org.alfresco.service.cmr.search.SearchService; +//import org.alfresco.service.transaction.TransactionService; +//import org.apache.commons.logging.Log; +//import org.apache.commons.logging.LogFactory; +//import org.hibernate.CacheMode; +//import org.hibernate.Query; +//import org.hibernate.Session; +//import org.springframework.orm.hibernate3.HibernateCallback; +//import org.springframework.orm.hibernate3.support.HibernateDaoSupport; +// +///** +// * Ensures that the FTS indexing picks up on any outstanding documents that +// * require indexing. +// *

    +// * This component must be used as a singleton (one per VM) and may only be +// * called to reindex once. It will start a thread that processes all available +// * transactions and keeps checking to ensure that the index is up to date with +// * the latest database changes. +// *

    +// * The following points are important: +// *

      +// *
    • +// * By default, the Hibernate L2 cache is used during processing. +// * This can be disabled by either disabling the L2 cache globally +// * for the server (not recommended) or by setting the +// * {@link #setL2CacheMode(String) l2CacheMode} property. If the +// * database is static then the L2 cache usage can be set to use +// * the NORMAL mode. REFRESH should be +// * used where the server will still be accessed from some clients +// * despite the database changing. NORMAL can be used +// * in the case of the caches being clustered, i.e. the caches will +// * not be out of date w.r.t. the database. +// *
    • +// *
    • +// * This process should only be used continuously where the index +// * transactions are following the database transactions. Use the +// * {@link #setRunContinuously(boolean) runContinuously} property +// * to change this behaviour. +// *
    • +// *
    +// * +// * @author Derek Hulley +// */ +//public class MissingContentReindexComponent extends HibernateDaoSupport implements IndexRecovery +//{ +// public static final String QUERY_GET_NEXT_CHANGE_TXN_IDS = "node.GetNextChangeTxnIds"; +// public static final String QUERY_GET_CHANGED_NODE_STATUSES = "node.GetChangedNodeStatuses"; +// public static final String QUERY_GET_DELETED_NODE_STATUSES = "node.GetDeletedNodeStatuses"; +// public static final String QUERY_GET_CHANGED_NODE_STATUSES_COUNT = "node.GetChangedNodeStatusesCount"; +// +// private static final String START_TXN_ID = "000"; +// +// private static Log logger = LogFactory.getLog(FullIndexRecoveryComponent.class); +// +// /** ensures that this process is kicked off once per VM */ +// private static boolean started = false; +// /** The current transaction ID being processed */ +// private static String currentTxnId = START_TXN_ID; +// /** kept to notify the thread that it should quite */ +// private boolean killThread = false; +// +// /** provides transactions to atomically index each missed transaction */ +// private TransactionService transactionService; +// /** the component to index the node hierarchy */ +// private Indexer indexer; +// /** the FTS indexer that we will prompt to pick up on any un-indexed text */ +// private FullTextSearchIndexer ftsIndexer; +// /** the component providing searches of the indexed nodes */ +// private SearchService searcher; +// /** the component giving direct access to node instances */ +// private NodeService nodeService; +// /** set this to run the index recovery component */ +// private boolean executeFullRecovery; +// /** set this on to keep checking for new transactions and never stop */ +// private boolean runContinuously; +// /** set the time to wait between checking indexes */ +// private long waitTime; +// /** controls how the L2 cache is used */ +// private CacheMode l2CacheMode; +// +// /** +// * @return Returns the ID of the current (or last) transaction processed +// */ +// public static String getCurrentTransactionId() +// { +// return currentTxnId; +// } +// +// public FullIndexRecoveryComponent() +// { +// this.killThread = false; +// this.executeFullRecovery = false; +// this.runContinuously = false; +// this.waitTime = 1000L; +// this.l2CacheMode = CacheMode.REFRESH; +// +// // ensure that we kill the thread when the VM is shutting down +// Runnable shutdownRunnable = new Runnable() +// { +// public void run() +// { +// killThread = true; +// }; +// }; +// Thread shutdownThread = new Thread(shutdownRunnable); +// Runtime.getRuntime().addShutdownHook(shutdownThread); +// } +// +// /** +// * @return Returns true if the component has already been started +// */ +// public static boolean isStarted() +// { +// return started; +// } +// +// /** +// * @param transactionService provide transactions to index each missed transaction +// */ +// public void setTransactionService(TransactionService transactionService) +// { +// this.transactionService = transactionService; +// } +// +// /** +// * @param indexer the indexer that will be index +// */ +// public void setIndexer(Indexer indexer) +// { +// this.indexer = indexer; +// } +// +// /** +// * @param ftsIndexer the FTS background indexer +// */ +// public void setFtsIndexer(FullTextSearchIndexer ftsIndexer) +// { +// this.ftsIndexer = ftsIndexer; +// } +// +// /** +// * @param searcher component providing index searches +// */ +// public void setSearcher(SearchService searcher) +// { +// this.searcher = searcher; +// } +// +// /** +// * @param nodeService provides information about nodes for indexing +// */ +// public void setNodeService(NodeService nodeService) +// { +// this.nodeService = nodeService; +// } +// +// /** +// * Set this to true to initiate the full index recovery. +// *

    +// * This used to default to true but is now false. Set this +// * if the potentially long-running process of checking and fixing the +// * indexes must be started. +// * +// * @param executeFullRecovery +// */ +// public void setExecuteFullRecovery(boolean executeFullRecovery) +// { +// this.executeFullRecovery = executeFullRecovery; +// } +// +// /** +// * Set this to ensure that the process continuously checks for new transactions. +// * If not, it will permanently terminate once it catches up with the current +// * transactions. +// * +// * @param runContinuously true to never cease looking for new transactions +// */ +// public void setRunContinuously(boolean runContinuously) +// { +// this.runContinuously = runContinuously; +// } +// +// /** +// * Set the time to wait between checking for new transaction changes in the database. +// * +// * @param waitTime the time to wait in milliseconds +// */ +// public void setWaitTime(long waitTime) +// { +// this.waitTime = waitTime; +// } +// +// /** +// * Set the hibernate cache mode by name +// * +// * @see org.hibernate.CacheMode +// */ +// public void setL2CacheMode(String l2CacheModeStr) +// { +// if (l2CacheModeStr.equals("GET")) +// { +// l2CacheMode = CacheMode.GET; +// } +// else if (l2CacheModeStr.equals("IGNORE")) +// { +// l2CacheMode = CacheMode.IGNORE; +// } +// else if (l2CacheModeStr.equals("NORMAL")) +// { +// l2CacheMode = CacheMode.NORMAL; +// } +// else if (l2CacheModeStr.equals("PUT")) +// { +// l2CacheMode = CacheMode.PUT; +// } +// else if (l2CacheModeStr.equals("REFRESH")) +// { +// l2CacheMode = CacheMode.REFRESH; +// } +// else +// { +// throw new IllegalArgumentException("Unrecognised Hibernate L2 cache mode: " + l2CacheModeStr); +// } +// } +// +// /** +// * Ensure that the index is up to date with the current state of the persistence layer. +// * The full list of unique transaction change IDs is retrieved and used to detect +// * which are not present in the index. All the node changes and deletions for the +// * remaining transactions are then indexed. +// */ +// public synchronized void reindex() +// { +// if (FullIndexRecoveryComponent.started) +// { +// throw new AlfrescoRuntimeException +// ("Only one FullIndexRecoveryComponent may be used per VM and it may only be called once"); +// } +// +// // ensure that we don't redo this work +// FullIndexRecoveryComponent.started = true; +// +// // work to mark the stores for full text reindexing +// TransactionWork ftsReindexWork = new TransactionWork() +// { +// public Object doWork() +// { +// List storeRefs = nodeService.getStores(); +// // reindex each store +// for (StoreRef storeRef : storeRefs) +// { +// // check if the store exists +// if (!nodeService.exists(storeRef)) +// { +// // store does not exist +// if (logger.isDebugEnabled()) +// { +// logger.debug("Skipping reindex of non-existent store: " + storeRef); +// } +// continue; +// } +// +// // prompt FTS to reindex the store +// ftsIndexer.requiresIndex(storeRef); +// } +// // done +// if (logger.isDebugEnabled()) +// { +// logger.debug("Prompted FTS index on stores: " + storeRefs); +// } +// return null; +// } +// }; +// TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, ftsReindexWork); +// +// // start full index recovery, if necessary +// if (!this.executeFullRecovery) +// { +// if (logger.isDebugEnabled()) +// { +// logger.debug("Full index recovery is off - quitting"); +// } +// } +// else +// { +// // set the state of the reindex +// FullIndexRecoveryComponent.currentTxnId = START_TXN_ID; +// +// // start a stateful thread that will begin processing the reindexing the transactions +// Runnable runnable = new ReindexRunner(); +// Thread reindexThread = new Thread(runnable); +// // make it a daemon thread +// reindexThread.setDaemon(true); +// // it should not be a high priority +// reindexThread.setPriority(Thread.MIN_PRIORITY); +// // start it +// reindexThread.start(); +// +// if (logger.isDebugEnabled()) +// { +// logger.debug("Full index recovery thread started: \n" + +// " continuous: " + runContinuously); +// } +// } +// } +// +// /** +// * Stateful thread runnable that executes reindex calls. +// * +// * @see FullIndexRecoveryComponent#reindexNodes() +// * +// * @author Derek Hulley +// */ +// private class ReindexRunner implements Runnable +// { +// public void run() +// { +// // keep this thread going permanently +// while (!killThread) +// { +// try +// { +// // reindex nodes +// List txnsIndexed = FullIndexRecoveryComponent.this.reindexNodes(); +// // reindex missing content +// @SuppressWarnings("unused") +// int missingContentCount = FullIndexRecoveryComponent.this.reindexMissingContent(); +// // check if the process should terminate +// if (txnsIndexed.size() == 0 && !runContinuously) +// { +// // the thread has caught up with all the available work and should not +// // run continuously +// if (logger.isDebugEnabled()) +// { +// logger.debug("Thread quitting - no more available indexing to do: \n" + +// " last txn: " + FullIndexRecoveryComponent.getCurrentTransactionId()); +// } +// break; +// } +// // brief pause +// synchronized(FullIndexRecoveryComponent.this) +// { +// FullIndexRecoveryComponent.this.wait(waitTime); +// } +// } +// catch (InterruptedException e) +// { +// // ignore +// } +// catch (Throwable e) +// { +// if (killThread) +// { +// // the shutdown may have caused the exception - ignore it +// } +// else +// { +// // we are still a go; report it +// logger.error("Reindex failure", e); +// } +// } +// } +// } +// } +// +// /** +// * @return Returns the number of documents reindexed +// */ +// private int reindexMissingContent() +// { +// int count = 0; +// for (StoreRef storeRef : storeRefs) +// { +// count += reindexMissingContent(storeRef); +// } +// return count; +// } +// +// /** +// * @param storeRef the store to check for missing content +// * @return Returns the number of documents reindexed +// */ +// private int reindexMissingContent(StoreRef storeRef) +// { +// SearchParameters sp = new SearchParameters(); +// sp.addStore(storeRef); +// +// // search for it in the index +// String query = "TEXT:" + LuceneIndexerImpl.NOT_INDEXED_CONTENT_MISSING; +// sp.setLanguage(SearchService.LANGUAGE_LUCENE); +// sp.setQuery(query); +// ResultSet results = null; +// try +// { +// results = searcher.query(sp); +// +// int count = 0; +// // loop over the results and get the details of the nodes that have missing content +// List assocRefs = results.getChildAssocRefs(); +// for (ChildAssociationRef assocRef : assocRefs) +// { +// final NodeRef childNodeRef = assocRef.getChildRef(); +// // prompt for a reindex - it might fail again, but we just keep plugging away +// TransactionWork reindexWork = new TransactionWork() +// { +// public Object doWork() +// { +// indexer.updateNode(childNodeRef); +// return null; +// } +// }; +// TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork); +// count++; +// } +// // done +// if (logger.isDebugEnabled()) +// { +// logger.debug("Reindexed missing content: \n" + +// " store: " + storeRef + "\n" + +// " node count: " + count); +// } +// return count; +// } +// finally +// { +// if (results != null) +// { +// results.close(); +// } +// } +// } +// +// /** +// * @return Returns the transaction ID just reindexed, i.e. where some work was performed +// */ +// private List reindexNodes() +// { +// // get a list of all transactions still requiring a check +// List txnsToCheck = getNextChangeTxnIds(FullIndexRecoveryComponent.currentTxnId); +// +// // loop over each transaction +// for (String changeTxnId : txnsToCheck) +// { +// reindexNodes(changeTxnId); +// } +// +// // done +// return txnsToCheck; +// } +// +// /** +// * Reindexes changes specific to the change transaction ID. +// *

    +// * All exceptions are absorbed. +// */ +// private void reindexNodes(final String changeTxnId) +// { +// /* +// * This must execute each within its own transaction. +// * The cache size is therefore not an issue. +// */ +// TransactionWork reindexWork = new TransactionWork() +// { +// public Object doWork() throws Exception +// { +// // perform the work in a Hibernate callback +// HibernateCallback callback = new ReindexCallback(changeTxnId); +// getHibernateTemplate().execute(callback); +// // done +// return null; +// } +// }; +// try +// { +// TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork); +// } +// catch (Throwable e) +// { +// logger.error("Transaction reindex failed: \n" + +// " txn: " + changeTxnId, +// e); +// } +// finally +// { +// // Up the current transaction now, in case the process fails at this point. +// // This will prevent the transaction from being processed again. +// // This applies to failures as well, which should be dealt with externally +// // and having the entire process start again, e.g. such as a system reboot +// currentTxnId = changeTxnId; +// } +// } +// +// /** +// * Stateful inner class that implements a single reindex call for a given store +// * and transaction. +// *

    +// * It must be called within its own transaction. +// * +// * @author Derek Hulley +// */ +// private class ReindexCallback implements HibernateCallback +// { +// private final String changeTxnId; +// +// public ReindexCallback(String changeTxnId) +// { +// this.changeTxnId = changeTxnId; +// } +// +// /** +// * Changes the L2 cache usage before reindexing for each store +// * +// * @see #reindexNodes(StoreRef, String) +// */ +// public Object doInHibernate(Session session) +// { +// // set the way the L2 cache is used +// getSession().setCacheMode(l2CacheMode); +// +// // reindex each store +// for (StoreRef storeRef : storeRefs) +// { +// if (!nodeService.exists(storeRef)) +// { +// // the store is not present +// continue; +// } +// // reindex for store +// reindexNodes(storeRef, changeTxnId); +// } +// // done +// return null; +// } +// +// private void reindexNodes(StoreRef storeRef, String changeTxnId) +// { +// // check if we need to perform this operation +// SearchParameters sp = new SearchParameters(); +// sp.addStore(storeRef); +// +// // search for it in the index +// String query = "TX:\"" + changeTxnId + "\""; +// sp.setLanguage(SearchService.LANGUAGE_LUCENE); +// sp.setQuery(query); +// ResultSet results = null; +// try +// { +// results = searcher.query(sp); +// // did the index have any of these changes? +// if (results.length() > 0) +// { +// // the transaction has an entry in the index - assume that it was +// // atomically correct +// if (logger.isDebugEnabled()) +// { +// logger.debug("Transaction present in index - no indexing required: \n" + +// " store: " + storeRef + "\n" + +// " txn: " + changeTxnId); +// } +// return; +// } +// } +// finally +// { +// if (results != null) +// { +// results.close(); +// } +// } +// // the index has no record of this +// // were there any changes, or is it all just deletions? +// int changedCount = getChangedNodeStatusesCount(storeRef, changeTxnId); +// if (changedCount == 0) +// { +// // no nodes were changed in the transaction, i.e. they are only deletions +// // the index is quite right not to have any entries for the transaction +// if (logger.isDebugEnabled()) +// { +// logger.debug("Transaction only has deletions - no indexing required: \n" + +// " store: " + storeRef + "\n" + +// " txn: " + changeTxnId); +// } +// return; +// } +// +// // process the deletions relevant to the txn and the store +// List deletedNodeStatuses = getDeletedNodeStatuses(storeRef, changeTxnId); +// for (NodeStatus status : deletedNodeStatuses) +// { +// NodeRef nodeRef = new NodeRef(storeRef, status.getKey().getGuid()); +// // only the child node ref is relevant +// ChildAssociationRef assocRef = new ChildAssociationRef( +// ContentModel.ASSOC_CHILDREN, +// null, +// null, +// nodeRef); +// indexer.deleteNode(assocRef); +// } +// +// // process additions +// List changedNodeStatuses = getChangedNodeStatuses(storeRef, changeTxnId); +// for (NodeStatus status : changedNodeStatuses) +// { +// NodeRef nodeRef = new NodeRef(storeRef, status.getKey().getGuid()); +// // get the primary assoc for the node +// ChildAssociationRef primaryAssocRef = nodeService.getPrimaryParent(nodeRef); +// // reindex +// indexer.createNode(primaryAssocRef); +// } +// +// // done +// if (logger.isDebugEnabled()) +// { +// logger.debug("Transaction reindexed: \n" + +// " store: " + storeRef + "\n" + +// " txn: " + changeTxnId + "\n" + +// " deletions: " + deletedNodeStatuses.size() + "\n" + +// " modifications: " + changedNodeStatuses.size()); +// } +// } +// }; +// +// /** +// * Retrieve all transaction IDs that are greater than the given transaction ID. +// * +// * @param currentTxnId the transaction ID that must be less than all returned results +// * @return Returns an ordered list of transaction IDs +// */ +// @SuppressWarnings("unchecked") +// public List getNextChangeTxnIds(final String currentTxnId) +// { +// HibernateCallback callback = new HibernateCallback() +// { +// public Object doInHibernate(Session session) +// { +// Query query = session.getNamedQuery(QUERY_GET_NEXT_CHANGE_TXN_IDS); +// query.setString("currentTxnId", currentTxnId) +// .setReadOnly(true); +// return query.list(); +// } +// }; +// List queryResults = (List) getHibernateTemplate().execute(callback); +// // done +// return queryResults; +// } +// +// @SuppressWarnings("unchecked") +// public int getChangedNodeStatusesCount(final StoreRef storeRef, final String changeTxnId) +// { +// HibernateCallback callback = new HibernateCallback() +// { +// public Object doInHibernate(Session session) +// { +// Query query = session.getNamedQuery(QUERY_GET_CHANGED_NODE_STATUSES_COUNT); +// query.setString("storeProtocol", storeRef.getProtocol()) +// .setString("storeIdentifier", storeRef.getIdentifier()) +// .setString("changeTxnId", changeTxnId) +// .setReadOnly(true); +// return query.uniqueResult(); +// } +// }; +// Integer changeCount = (Integer) getHibernateTemplate().execute(callback); +// // done +// return changeCount.intValue(); +// } +// +// @SuppressWarnings("unchecked") +// public List getChangedNodeStatuses(final StoreRef storeRef, final String changeTxnId) +// { +// HibernateCallback callback = new HibernateCallback() +// { +// public Object doInHibernate(Session session) +// { +// Query query = session.getNamedQuery(QUERY_GET_CHANGED_NODE_STATUSES); +// query.setString("storeProtocol", storeRef.getProtocol()) +// .setString("storeIdentifier", storeRef.getIdentifier()) +// .setString("changeTxnId", changeTxnId) +// .setReadOnly(true); +// return query.list(); +// } +// }; +// List queryResults = (List) getHibernateTemplate().execute(callback); +// // done +// return queryResults; +// } +// +// @SuppressWarnings("unchecked") +// public List getDeletedNodeStatuses(final StoreRef storeRef, final String changeTxnId) +// { +// HibernateCallback callback = new HibernateCallback() +// { +// public Object doInHibernate(Session session) +// { +// Query query = session.getNamedQuery(QUERY_GET_DELETED_NODE_STATUSES); +// query.setString("storeProtocol", storeRef.getProtocol()) +// .setString("storeIdentifier", storeRef.getIdentifier()) +// .setString("changeTxnId", changeTxnId) +// .setReadOnly(true); +// return query.list(); +// } +// }; +// List queryResults = (List) getHibernateTemplate().execute(callback); +// // done +// return queryResults; +// } +//} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java index 036ddbf033..93c9ac253f 100644 --- a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java +++ b/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java @@ -307,23 +307,6 @@ public class IntegrityTest extends TestCase logger.error("Method commented out: testRemoveSourcesOfMandatoryAssocs"); } - public void testDuplicateTargetAssocs() throws Exception - { - NodeRef parent = createNode("source", TEST_TYPE_WITH_CHILD_ASSOCS, null); - NodeRef child1 = createNode("child1", TEST_TYPE_WITHOUT_ANYTHING, null); - NodeRef child2 = createNode("child2", TEST_TYPE_WITHOUT_ANYTHING, null); - NodeRef child3 = createNode("child3", TEST_TYPE_WITHOUT_ANYTHING, null); - - // satisfy the one-to-one - nodeService.addChild(parent, child3, TEST_ASSOC_CHILD_ONE_ONE, QName.createQName(NAMESPACE, "mandatoryChild")); - - // create the non-duplicate assocs - nodeService.addChild(parent, child1, TEST_ASSOC_CHILD_ZEROMANY_ZEROMANY, QName.createQName(NAMESPACE, "dupli_cate")); - nodeService.addChild(parent, child2, TEST_ASSOC_CHILD_ZEROMANY_ZEROMANY, QName.createQName(NAMESPACE, "dupli_cate")); - - checkIntegrityExpectFailure("Failed to detect duplicate association names", 1); - } - public void testCreateSourceOfAssocsWithMandatoryTargetsPresent() throws Exception { NodeRef source = createNode("abc", TEST_TYPE_WITH_ASSOCS, null); diff --git a/source/java/org/alfresco/service/AnnotationTestInterface.java b/source/java/org/alfresco/service/AnnotationTestInterface.java index 495e8c12bb..1d6eb34cc5 100644 --- a/source/java/org/alfresco/service/AnnotationTestInterface.java +++ b/source/java/org/alfresco/service/AnnotationTestInterface.java @@ -21,6 +21,7 @@ package org.alfresco.service; * * @author Andy Hind */ +@PublicService public interface AnnotationTestInterface { @Auditable() diff --git a/source/java/org/alfresco/service/PublicService.java b/source/java/org/alfresco/service/PublicService.java new file mode 100644 index 0000000000..7fc7359cda --- /dev/null +++ b/source/java/org/alfresco/service/PublicService.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifically indicate that an interface defines a public service. + * + * This is a marker annotation. + * + * @author Andy Hind + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface PublicService +{ + +} diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 3fc80541da..7e893e3789 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -52,6 +52,7 @@ import org.alfresco.service.transaction.TransactionService; * * @author David Caruana */ +@PublicService public interface ServiceRegistry { // Service Bean Names diff --git a/source/java/org/alfresco/service/cmr/action/ActionService.java b/source/java/org/alfresco/service/cmr/action/ActionService.java index cbfe96ccfd..171051fc94 100644 --- a/source/java/org/alfresco/service/cmr/action/ActionService.java +++ b/source/java/org/alfresco/service/cmr/action/ActionService.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -28,6 +29,7 @@ import org.alfresco.service.cmr.repository.NodeRef; * * @author Roy Wetherall */ +@PublicService public interface ActionService { /** diff --git a/source/java/org/alfresco/service/cmr/admin/AdminService.java b/source/java/org/alfresco/service/cmr/admin/AdminService.java index f9ca1e90d7..fdbbb262b5 100644 --- a/source/java/org/alfresco/service/cmr/admin/AdminService.java +++ b/source/java/org/alfresco/service/cmr/admin/AdminService.java @@ -18,12 +18,15 @@ package org.alfresco.service.cmr.admin; import java.util.List; +import org.alfresco.service.PublicService; + /** * General administration tasks and system information. * * @since 1.2 * @author Derek Hulley */ +@PublicService public interface AdminService { // public List getPatches(); diff --git a/source/java/org/alfresco/service/cmr/audit/AuditService.java b/source/java/org/alfresco/service/cmr/audit/AuditService.java new file mode 100644 index 0000000000..8ad7456d87 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/audit/AuditService.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.service.cmr.audit; + +import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * The public API by which applications can create audit entries. + * This does not affect auditing using method interceptors. + * The information recorded can not be confused between the two. + * + * This API could be used by an audit action. + * + * @author Andy Hind + */ +@PublicService +public interface AuditService +{ + + /** + * Add an application audit entry. + * + * @param source - + * a string that represents the application + * @param description - + * the audit entry + */ + @NotAuditable + public void audit(String source, String description); + + /** + * + * @param source - + * a string that represents the application + * @param description - + * the audit entry + * @param key - + * a node ref to use as the key for filtering etc + */ + @NotAuditable + public void audit(String source, String description, NodeRef key); + + /** + * + * @param source - + * a string that represents the application + * @param description - + * the audit entry + * @param args - + * an arbitrary list of parameters + */ + @NotAuditable + public void audit(String source, String description, Object... args); + + /** + * + * @param source - + * a string that represents the application + * @param description - + * the audit entry * + * @param key - + * a node ref to use as the key for filtering etc + * @param args - + * an arbitrary list of parameters + */ + @NotAuditable + public void audit(String source, String description, NodeRef key, Object... args); + +} diff --git a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java index 64b58437fd..c6549ae507 100644 --- a/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java +++ b/source/java/org/alfresco/service/cmr/coci/CheckOutCheckInService.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.Map; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -29,6 +30,7 @@ import org.alfresco.service.namespace.QName; * * @author Roy Wetherall */ +@PublicService public interface CheckOutCheckInService { /** diff --git a/source/java/org/alfresco/service/cmr/dictionary/DictionaryService.java b/source/java/org/alfresco/service/cmr/dictionary/DictionaryService.java index b939d3c72f..33053ed4cb 100644 --- a/source/java/org/alfresco/service/cmr/dictionary/DictionaryService.java +++ b/source/java/org/alfresco/service/cmr/dictionary/DictionaryService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.dictionary; import java.util.Collection; import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; import org.alfresco.service.namespace.QName; @@ -35,6 +36,7 @@ import org.alfresco.service.namespace.QName; * * @author David Caruana */ +@PublicService public interface DictionaryService { diff --git a/source/java/org/alfresco/service/cmr/lock/LockService.java b/source/java/org/alfresco/service/cmr/lock/LockService.java index 7687e2b072..fdab4e49e9 100644 --- a/source/java/org/alfresco/service/cmr/lock/LockService.java +++ b/source/java/org/alfresco/service/cmr/lock/LockService.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.List; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -29,6 +30,7 @@ import org.alfresco.service.cmr.repository.StoreRef; * * @author Roy Wetherall */ +@PublicService public interface LockService { /** diff --git a/source/java/org/alfresco/service/cmr/model/FileFolderService.java b/source/java/org/alfresco/service/cmr/model/FileFolderService.java index 2b939f4fe2..6dcbfbd4bf 100644 --- a/source/java/org/alfresco/service/cmr/model/FileFolderService.java +++ b/source/java/org/alfresco/service/cmr/model/FileFolderService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.model; import java.util.List; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; @@ -32,6 +33,7 @@ import org.alfresco.service.namespace.QName; * * @author Derek Hulley */ +@PublicService public interface FileFolderService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/ContentService.java b/source/java/org/alfresco/service/cmr/repository/ContentService.java index c1c032a6ab..f13f66192e 100644 --- a/source/java/org/alfresco/service/cmr/repository/ContentService.java +++ b/source/java/org/alfresco/service/cmr/repository/ContentService.java @@ -18,6 +18,7 @@ package org.alfresco.service.cmr.repository; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.namespace.QName; @@ -42,6 +43,7 @@ import org.alfresco.service.namespace.QName; * * @author Derek Hulley */ +@PublicService public interface ContentService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/CopyService.java b/source/java/org/alfresco/service/cmr/repository/CopyService.java index 62e365d10f..801071df37 100644 --- a/source/java/org/alfresco/service/cmr/repository/CopyService.java +++ b/source/java/org/alfresco/service/cmr/repository/CopyService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.repository; import java.util.List; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.namespace.QName; /** @@ -29,6 +30,7 @@ import org.alfresco.service.namespace.QName; * * @author Roy Wetherall */ +@PublicService public interface CopyService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/MimetypeService.java b/source/java/org/alfresco/service/cmr/repository/MimetypeService.java index 6475373993..9f5d791d86 100644 --- a/source/java/org/alfresco/service/cmr/repository/MimetypeService.java +++ b/source/java/org/alfresco/service/cmr/repository/MimetypeService.java @@ -21,6 +21,7 @@ import java.util.Map; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; /** @@ -29,6 +30,7 @@ import org.alfresco.service.NotAuditable; * @author Derek Hulley * */ +@PublicService public interface MimetypeService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/NodeService.java b/source/java/org/alfresco/service/cmr/repository/NodeService.java index 96b192cf52..130e67f4ab 100644 --- a/source/java/org/alfresco/service/cmr/repository/NodeService.java +++ b/source/java/org/alfresco/service/cmr/repository/NodeService.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.dictionary.InvalidAspectException; import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.namespace.QName; @@ -32,6 +33,7 @@ import org.alfresco.service.namespace.QNamePattern; * * @author Derek Hulley */ +@PublicService public interface NodeService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/ScriptService.java b/source/java/org/alfresco/service/cmr/repository/ScriptService.java index 3c6b9dcc88..ad742600f5 100644 --- a/source/java/org/alfresco/service/cmr/repository/ScriptService.java +++ b/source/java/org/alfresco/service/cmr/repository/ScriptService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.repository; import java.util.Map; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.namespace.QName; /** @@ -36,6 +37,7 @@ import org.alfresco.service.namespace.QName; * * @author Kevin Roast */ +@PublicService public interface ScriptService { /** diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java index 74560b1ffd..4372604290 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java @@ -671,8 +671,7 @@ public final class TemplateNode implements Serializable if (this.services.getNodeService().exists(nodeRef)) { return "Node Type: " + getType() + - "\nNode Properties: " + this.getProperties().toString() + - "\nNode Aspects: " + this.getAspects().toString(); + "\tNode Ref: " + this.nodeRef.toString(); } else { diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateService.java b/source/java/org/alfresco/service/cmr/repository/TemplateService.java index 5ba59bba24..55a4824f2e 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateService.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.repository; import java.io.Writer; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** * Template Service. @@ -34,6 +35,7 @@ import org.alfresco.service.Auditable; * * @author Kevin Roast */ +@PublicService public interface TemplateService { /** diff --git a/source/java/org/alfresco/service/cmr/rule/RuleService.java b/source/java/org/alfresco/service/cmr/rule/RuleService.java index f14c871ee0..09ecd3b6af 100644 --- a/source/java/org/alfresco/service/cmr/rule/RuleService.java +++ b/source/java/org/alfresco/service/cmr/rule/RuleService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.rule; import java.util.List; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.repository.NodeRef; @@ -27,6 +28,7 @@ import org.alfresco.service.cmr.repository.NodeRef; * * @author Roy Wetherall */ +@PublicService public interface RuleService { /** diff --git a/source/java/org/alfresco/service/cmr/search/CategoryService.java b/source/java/org/alfresco/service/cmr/search/CategoryService.java index 7651b93f19..2f07a53a37 100644 --- a/source/java/org/alfresco/service/cmr/search/CategoryService.java +++ b/source/java/org/alfresco/service/cmr/search/CategoryService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.search; import java.util.Collection; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -37,6 +38,7 @@ import org.alfresco.service.namespace.QName; * @author Andy Hind * */ +@PublicService public interface CategoryService { /** diff --git a/source/java/org/alfresco/service/cmr/search/SearchService.java b/source/java/org/alfresco/service/cmr/search/SearchService.java index 9f5a90998e..8387c4ff44 100644 --- a/source/java/org/alfresco/service/cmr/search/SearchService.java +++ b/source/java/org/alfresco/service/cmr/search/SearchService.java @@ -20,6 +20,7 @@ import java.io.Serializable; import java.util.List; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.Path; @@ -38,6 +39,7 @@ import org.alfresco.service.namespace.QName; * @author Andy hind * */ +@PublicService public interface SearchService { public static final String LANGUAGE_LUCENE = "lucene"; diff --git a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java index 65420fe4bb..de4177e5e6 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthenticationService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthenticationService.java @@ -20,6 +20,7 @@ import java.util.Set; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** * The authentication service defines the API for managing authentication information @@ -28,6 +29,7 @@ import org.alfresco.service.Auditable; * @author Andy Hind * */ +@PublicService public interface AuthenticationService { /** diff --git a/source/java/org/alfresco/service/cmr/security/AuthorityService.java b/source/java/org/alfresco/service/cmr/security/AuthorityService.java index 0febd7b08d..5cf6172715 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthorityService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthorityService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.security; import java.util.Set; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** * The service that encapsulates authorities granted to users. @@ -34,6 +35,7 @@ import org.alfresco.service.Auditable; * * @author Andy Hind */ +@PublicService public interface AuthorityService { /** diff --git a/source/java/org/alfresco/service/cmr/security/OwnableService.java b/source/java/org/alfresco/service/cmr/security/OwnableService.java index 1c77740e86..6bdc685062 100644 --- a/source/java/org/alfresco/service/cmr/security/OwnableService.java +++ b/source/java/org/alfresco/service/cmr/security/OwnableService.java @@ -17,6 +17,7 @@ package org.alfresco.service.cmr.security; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -24,6 +25,7 @@ import org.alfresco.service.cmr.repository.NodeRef; * * @author Andy Hind */ +@PublicService public interface OwnableService { /** diff --git a/source/java/org/alfresco/service/cmr/security/PermissionService.java b/source/java/org/alfresco/service/cmr/security/PermissionService.java index ea772ca324..6a4f385774 100644 --- a/source/java/org/alfresco/service/cmr/security/PermissionService.java +++ b/source/java/org/alfresco/service/cmr/security/PermissionService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.security; import java.util.Set; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -29,6 +30,7 @@ import org.alfresco.service.namespace.QName; * * @author Andy Hind */ +@PublicService public interface PermissionService { public static final String ROLE_PREFIX = "ROLE_"; diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java index c985085742..2f7665c41d 100644 --- a/source/java/org/alfresco/service/cmr/security/PersonService.java +++ b/source/java/org/alfresco/service/cmr/security/PersonService.java @@ -21,6 +21,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -36,6 +37,7 @@ import org.alfresco.service.namespace.QName; * * @author Andy Hind */ +@PublicService public interface PersonService { /** diff --git a/source/java/org/alfresco/service/cmr/version/VersionService.java b/source/java/org/alfresco/service/cmr/version/VersionService.java index 07baf95e42..b9fb010268 100644 --- a/source/java/org/alfresco/service/cmr/version/VersionService.java +++ b/source/java/org/alfresco/service/cmr/version/VersionService.java @@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Map; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.AspectMissingException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -31,6 +32,7 @@ import org.alfresco.service.namespace.QName; * * @author Roy Wetherall */ +@PublicService public interface VersionService { /** diff --git a/source/java/org/alfresco/service/cmr/view/ExporterService.java b/source/java/org/alfresco/service/cmr/view/ExporterService.java index 9c91198d75..6a8f7c1437 100644 --- a/source/java/org/alfresco/service/cmr/view/ExporterService.java +++ b/source/java/org/alfresco/service/cmr/view/ExporterService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.view; import java.io.OutputStream; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** @@ -26,6 +27,7 @@ import org.alfresco.service.Auditable; * * @author David Caruana */ +@PublicService public interface ExporterService { /** diff --git a/source/java/org/alfresco/service/cmr/view/ImporterService.java b/source/java/org/alfresco/service/cmr/view/ImporterService.java index 5c9e22c6db..f0d3e0faaf 100644 --- a/source/java/org/alfresco/service/cmr/view/ImporterService.java +++ b/source/java/org/alfresco/service/cmr/view/ImporterService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.view; import java.io.Reader; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** @@ -27,6 +28,7 @@ import org.alfresco.service.Auditable; * @author David Caruana * */ +@PublicService public interface ImporterService { diff --git a/source/java/org/alfresco/service/cmr/view/RepositoryExporterService.java b/source/java/org/alfresco/service/cmr/view/RepositoryExporterService.java index ab027f3013..3d13b38873 100644 --- a/source/java/org/alfresco/service/cmr/view/RepositoryExporterService.java +++ b/source/java/org/alfresco/service/cmr/view/RepositoryExporterService.java @@ -19,6 +19,7 @@ package org.alfresco.service.cmr.view; import java.io.File; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -28,6 +29,7 @@ import org.alfresco.service.cmr.repository.StoreRef; * * @author davidc */ +@PublicService public interface RepositoryExporterService { diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java index fe46a1c143..d1c22d461d 100644 --- a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -21,6 +21,8 @@ import java.io.Serializable; import java.util.List; import java.util.Map; +import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -32,6 +34,7 @@ import org.alfresco.service.namespace.QName; * * @author davidc */ +@PublicService public interface WorkflowService { // @@ -46,6 +49,7 @@ public interface WorkflowService * @param mimetype the mimetype of the workflow definition * @return workflow deployment descriptor */ + @Auditable(parameters = {"engineId", "workflowDefinition", "mimetype"}) public WorkflowDeployment deployDefinition(String engineId, InputStream workflowDefinition, String mimetype); /** @@ -57,6 +61,7 @@ public interface WorkflowService * @param workflowDefinition the content object containing the definition * @return workflow deployment descriptor */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"workflowDefinition"}) public WorkflowDeployment deployDefinition(NodeRef workflowDefinition); /** @@ -70,6 +75,7 @@ public interface WorkflowService * @param mimetype the mimetype of the definition * @return true => already deployed */ + @Auditable(parameters = {"engineId", "workflowDefinition", "mimetype"}) public boolean isDefinitionDeployed(String engineId, InputStream workflowDefinition, String mimetype); /** @@ -79,6 +85,7 @@ public interface WorkflowService * * @param workflowDefinitionId the id of the definition to undeploy */ + @Auditable(parameters = {"workflowDefinitionId"}) public void undeployDefinition(String workflowDefinitionId); /** @@ -86,6 +93,7 @@ public interface WorkflowService * * @return the deployed workflow definitions */ + @Auditable public List getDefinitions(); /** @@ -94,6 +102,7 @@ public interface WorkflowService * @param workflowDefinitionId the workflow definition id * @return the deployed workflow definition */ + @Auditable(parameters = {"workflowDefinitionId"}) public WorkflowDefinition getDefinitionById(String workflowDefinitionId); /** @@ -117,6 +126,7 @@ public interface WorkflowService * @param parameters the initial set of parameters used to populate the "Start Task" properties * @return the initial workflow path */ + @Auditable(parameters = {"workflowDefinitionId", "parameters"}) public WorkflowPath startWorkflow(String workflowDefinitionId, Map parameters); /** @@ -126,6 +136,7 @@ public interface WorkflowService * @param templateDefinition the node representing the Start Task properties * @return the initial workflow path */ + @Auditable(parameters = {"templateDefinition"}) public WorkflowPath startWorkflowFromTemplate(NodeRef templateDefinition); /** @@ -134,6 +145,7 @@ public interface WorkflowService * @param workflowDefinitionId the workflow definition id * @return the list of "in-fligth" workflow instances */ + @Auditable(parameters = {"workflowDefinitionId"}) public List getActiveWorkflows(String workflowDefinitionId); /** @@ -142,6 +154,7 @@ public interface WorkflowService * @param workflowId workflow instance id * @return the list of workflow paths */ + @Auditable(parameters = {"workflowId"}) public List getWorkflowPaths(String workflowId); /** @@ -150,6 +163,7 @@ public interface WorkflowService * @param workflowId the workflow instance to cancel * @return an updated representation of the workflow instance */ + @Auditable(parameters = {"workflowId"}) public WorkflowInstance cancelWorkflow(String workflowId); /** @@ -159,6 +173,7 @@ public interface WorkflowService * @param transition the transition to follow (or null, for the default transition) * @return the updated workflow path */ + @Auditable(parameters = {"pathId", "transitionId"}) public WorkflowPath signal(String pathId, String transitionId); /** @@ -167,6 +182,7 @@ public interface WorkflowService * @param pathId the path id * @return the list of associated tasks */ + @Auditable(parameters = {"pathId"}) public List getTasksForWorkflowPath(String pathId); @@ -180,6 +196,7 @@ public interface WorkflowService * @param taskId the task id * @return the task */ + @Auditable(parameters = {"taskId"}) public WorkflowTask getTaskById(String taskId); /** @@ -189,6 +206,7 @@ public interface WorkflowService * @param state filter by specified workflow task state * @return the list of assigned tasks */ + @Auditable(parameters = {"authority", "state"}) public List getAssignedTasks(String authority, WorkflowTaskState state); /** @@ -197,6 +215,7 @@ public interface WorkflowService * @param authority the authority * @return the list of pooled tasks */ + @Auditable(parameters = {"authority"}) public List getPooledTasks(String authority); /** @@ -208,6 +227,7 @@ public interface WorkflowService * @param remove the map of items to dis-associate with the task (or null, if none to remove) * @return the update task */ + @Auditable(parameters = {"taskId", "properties", "add", "remove"}) public WorkflowTask updateTask(String taskId, Map properties, Map> add, Map> remove); /** @@ -217,6 +237,7 @@ public interface WorkflowService * @param transition the task transition to take on completion (or null, for the default transition) * @return the updated task */ + @Auditable(parameters = {"taskId", "transitionId"}) public WorkflowTask endTask(String taskId, String transitionId); /** @@ -227,6 +248,7 @@ public interface WorkflowService * @param container (optional) a pre-created container (e.g. folder, versioned folder or layered folder) * @return the workflow package */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"container"}) public NodeRef createPackage(NodeRef container); } diff --git a/source/java/org/alfresco/service/descriptor/DescriptorService.java b/source/java/org/alfresco/service/descriptor/DescriptorService.java index 75c3af7be7..148f292706 100644 --- a/source/java/org/alfresco/service/descriptor/DescriptorService.java +++ b/source/java/org/alfresco/service/descriptor/DescriptorService.java @@ -26,6 +26,7 @@ import org.alfresco.service.license.LicenseDescriptor; * @author David Caruana * */ +// This is not a public service in the normal sense public interface DescriptorService { /** diff --git a/source/java/org/alfresco/service/license/LicenseService.java b/source/java/org/alfresco/service/license/LicenseService.java index f325ac73c5..15cdaf405e 100644 --- a/source/java/org/alfresco/service/license/LicenseService.java +++ b/source/java/org/alfresco/service/license/LicenseService.java @@ -17,6 +17,7 @@ package org.alfresco.service.license; import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; /** @@ -24,6 +25,7 @@ import org.alfresco.service.NotAuditable; * * @author davidc */ +@PublicService public interface LicenseService { diff --git a/source/java/org/alfresco/service/namespace/NamespacePrefixResolver.java b/source/java/org/alfresco/service/namespace/NamespacePrefixResolver.java index ea94b5e3d3..ee460349f3 100644 --- a/source/java/org/alfresco/service/namespace/NamespacePrefixResolver.java +++ b/source/java/org/alfresco/service/namespace/NamespacePrefixResolver.java @@ -19,6 +19,7 @@ package org.alfresco.service.namespace; import java.util.Collection; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; /** * The NamespacePrefixResolver provides a mapping between @@ -26,6 +27,7 @@ import org.alfresco.service.Auditable; * * @author David Caruana */ +@PublicService public interface NamespacePrefixResolver { /** diff --git a/source/java/org/alfresco/service/namespace/NamespaceService.java b/source/java/org/alfresco/service/namespace/NamespaceService.java index 2d2cf70c32..7bac210a62 100644 --- a/source/java/org/alfresco/service/namespace/NamespaceService.java +++ b/source/java/org/alfresco/service/namespace/NamespaceService.java @@ -17,6 +17,7 @@ package org.alfresco.service.namespace; import org.alfresco.service.Auditable; +import org.alfresco.service.PublicService; @@ -28,6 +29,7 @@ import org.alfresco.service.Auditable; * * @author David Caruana */ +@PublicService public interface NamespaceService extends NamespacePrefixResolver { /** Default Namespace URI */ diff --git a/source/java/org/alfresco/service/transaction/TransactionService.java b/source/java/org/alfresco/service/transaction/TransactionService.java index b1ee1d7c6f..61d5f9d60c 100644 --- a/source/java/org/alfresco/service/transaction/TransactionService.java +++ b/source/java/org/alfresco/service/transaction/TransactionService.java @@ -19,6 +19,7 @@ package org.alfresco.service.transaction; import javax.transaction.UserTransaction; import org.alfresco.service.NotAuditable; +import org.alfresco.service.PublicService; /** * Contract for retrieving access to a user transaction. @@ -29,6 +30,7 @@ import org.alfresco.service.NotAuditable; * * @author David Caruana */ +@PublicService public interface TransactionService { /** diff --git a/source/java/org/alfresco/tools/Repository.java b/source/java/org/alfresco/tools/Repository.java index ce6c960fe3..6524e45dc9 100644 --- a/source/java/org/alfresco/tools/Repository.java +++ b/source/java/org/alfresco/tools/Repository.java @@ -46,6 +46,10 @@ public class Repository extends Tool context.setHelp(true); break; } + else if (args[i].equals("-verbose")) + { + context.setVerbose(true); + } else if (args[i].equals("-user")) { i++; diff --git a/source/java/org/alfresco/tools/Tool.java b/source/java/org/alfresco/tools/Tool.java index 903dd9a97f..ae0fb5112f 100644 --- a/source/java/org/alfresco/tools/Tool.java +++ b/source/java/org/alfresco/tools/Tool.java @@ -183,6 +183,7 @@ public abstract class Tool */ public final void start(String[] args) { + long startTime = System.nanoTime(); int status = -1; try @@ -202,7 +203,11 @@ public abstract class Tool logInfo(getToolName()); initialiseRepository(); login(); + long loginTime = System.nanoTime(); + logInfo("Time to login "+((loginTime - startTime)/1000000000f)+" seconds"); status = execute(); + long executeTime = System.nanoTime(); + logInfo("Time to execute "+((executeTime - loginTime)/1000000000f)+" seconds"); logInfo(getToolName() + " successfully completed."); } } diff --git a/source/java/org/alfresco/util/debug/MethodCallLogAdvice.java b/source/java/org/alfresco/util/debug/MethodCallLogAdvice.java deleted file mode 100644 index a80048bf3c..0000000000 --- a/source/java/org/alfresco/util/debug/MethodCallLogAdvice.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2005 Alfresco, Inc. - * - * Licensed under the Mozilla Public License version 1.1 - * with a permitted attribution clause. You may obtain a - * copy of the License at - * - * http://www.alfresco.org/legal/license.txt - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific - * language governing permissions and limitations under the - * License. - */ -package org.alfresco.util.debug; - -import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.aopalliance.intercept.MethodInterceptor; -import org.aopalliance.intercept.MethodInvocation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Performs writing to DEBUG of incoming arguments and outgoing results for a method call.
    - * If the method invocation throws an exception, then the incoming arguments are - * logged to DEBUG as well.
    - * The implementation adds very little overhead to a normal method - * call by only building log messages when required. - *

    - * The logging is done against the logger retrieved using the names: - *

    - *

    - *      org.alfresco.util.debug.MethodCallLogAdvice
    - *         AND
    - *      targetClassName
    - *      targetClassName.methodName
    - *      targetClassName.methodName.exception
    - * 
    - *

    - * The following examples show how to control the log levels: - *

    - *

    - *      org.alfresco.util.debug.MethodCallLogAdvice=DEBUG   # activate method logging
    - *          AND
    - *      x.y.MyClass=DEBUG                           # log debug for all method calls on MyClass
    - *      x.y.MyClass.doSomething=DEBUG               # log debug for all doSomething method calls
    - *      x.y.MyClass.doSomething.exception=DEBUG     # only log debug for doSomething() upon exception
    - * 
    - *

    - * - * @author Derek Hulley - */ -public class MethodCallLogAdvice implements MethodInterceptor -{ - private static final Log logger = LogFactory.getLog(MethodCallLogAdvice.class); - - public Object invoke(MethodInvocation invocation) throws Throwable - { - if (logger.isDebugEnabled()) - { - return invokeWithLogging(invocation); - } - else - { - // no logging required - return invocation.proceed(); - } - } - - /** - * Only executes logging code if logging is required - */ - private Object invokeWithLogging(MethodInvocation invocation) throws Throwable - { - String methodName = invocation.getMethod().getName(); - String className = invocation.getMethod().getDeclaringClass().getName(); - - // execute as normal - try - { - Object ret = invocation.proceed(); - // logging - Log methodLogger = LogFactory.getLog(className + "." + methodName); - if (methodLogger.isDebugEnabled()) - { - // log success - StringBuffer sb = getInvocationInfo(className, methodName, invocation.getArguments()); - sb.append(" Result: ").append(ret); - methodLogger.debug(sb); - } - // done - return ret; - } - catch (Throwable e) - { - Log exceptionLogger = LogFactory.getLog(className + "." + methodName + ".exception"); - if (exceptionLogger.isDebugEnabled()) - { - StringBuffer sb = getInvocationInfo(className, methodName, invocation.getArguments()); - sb.append(" Failure: ").append(e.getClass().getName()).append(" - ").append(e.getMessage()); - exceptionLogger.debug(sb); - } - // rethrow - throw e; - } - } - - /** - * Return format: - *

    -     *      Method: className#methodName
    -     *         Argument: arg0
    -     *         Argument: arg1
    -     *         ...
    -     *         Argument: argN {newline}
    -     * 
    - * - * @param className - * @param methodName - * @param args - * @return Returns a StringBuffer containing the details of a method call - */ - private StringBuffer getInvocationInfo(String className, String methodName, Object[] args) - { - StringBuffer sb = new StringBuffer(250); - sb.append("\nMethod: ").append(className).append("#").append(methodName).append("\n"); - sb.append(" Transaction: ").append(AlfrescoTransactionSupport.getTransactionId()).append("\n"); - for (Object arg : args) - { - sb.append(" Argument: ").append(arg).append("\n"); - } - return sb; - } -}