diff --git a/config/alfresco/auditConfig.xml b/config/alfresco/auditConfig.xml index e68bef07ee..f639baff74 100644 --- a/config/alfresco/auditConfig.xml +++ b/config/alfresco/auditConfig.xml @@ -2,7 +2,6 @@ - diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index 2d6444b057..7ea83ead81 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -35,7 +35,7 @@ - classpath:alfresco/dbscripts/create/1.4/${db.script.dialect}/sample.sql + classpath:alfresco/dbscripts/create/1.4/${db.script.dialect}/post-create-indexes.sql @@ -160,13 +160,13 @@ jbpm alfresco/workflow/review_processdefinition.xml text/xml - true + false jbpm alfresco/workflow/adhoc_processdefinition.xml text/xml - true + false diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 46f76c6af6..c2a7980704 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -23,31 +23,25 @@ - - + + ${db.driver} - + ${db.url} - + ${db.username} ${db.password} - + ${db.pool.initial} - + ${db.pool.max} - - ${db.pool.maxIdleTime} - - - 1 - @@ -78,6 +72,7 @@ alfresco.messages.patch-service alfresco.messages.schema-update alfresco.messages.webdav-messages + alfresco.messages.copy-service @@ -490,6 +485,9 @@ + + + diff --git a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes.sql b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes.sql new file mode 100644 index 0000000000..d2bc282c82 --- /dev/null +++ b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes.sql @@ -0,0 +1,3 @@ +-- +-- Add post-creation indexes. (Generic Schema 1.4) +-- 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 deleted file mode 100644 index ba873b987c..0000000000 --- a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/sample.sql +++ /dev/null @@ -1,4 +0,0 @@ --- --- 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.Oracle9Dialect/post-create-indexes.sql b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Oracle9Dialect/post-create-indexes.sql new file mode 100644 index 0000000000..826399ad97 --- /dev/null +++ b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Oracle9Dialect/post-create-indexes.sql @@ -0,0 +1,24 @@ +-- +-- Add post-creation indexes. (Oracle Schema 1.4) +-- +CREATE INDEX FKFFF41F9960601995 ON alf_access_control_entry (permission_id); +CREATE INDEX FKFFF41F99B25A50BF ON alf_access_control_entry (authority_id); +CREATE INDEX FKFFF41F99B9553F6C ON alf_access_control_entry (acl_id); +CREATE INDEX FK8A749A657B7FDE43 ON alf_auth_ext_keys (id); +CREATE INDEX FKFFC5468E74173FF4 ON alf_child_assoc (child_node_id); +CREATE INDEX FKFFC5468E8E50E582 ON alf_child_assoc (parent_node_id); +CREATE INDEX FK60EFB626B9553F6C ON alf_node (acl_id); +CREATE INDEX FK60EFB626D24ADD25 ON alf_node (protocol, identifier); +CREATE INDEX FK7D4CF8EC7F2C8017 ON alf_node_properties (node_id); +CREATE INDEX FKD654E027F2C8017 ON alf_node_aspects (node_id); +CREATE INDEX FKE1A550BCB69C43F3 ON alf_node_assoc (source_node_id); +CREATE INDEX FKE1A550BCA8FC7769 ON alf_node_assoc (target_node_id); +CREATE INDEX FK71C2002B7F2C8017 ON alf_node_status (node_id); +CREATE INDEX FKBD4FF53D22DBA5BA ON alf_store (root_node_id); + +-- +-- New audit tables +-- +CREATE INDEX FKEAD1817484342E39 ON alf_audit_fact (audit_date_id); +CREATE INDEX FKEAD18174A0F9B8D9 ON alf_audit_fact (audit_source_id); +CREATE INDEX FKEAD18174F524CFD7 ON alf_audit_fact (audit_conf_id); 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 deleted file mode 100644 index 7d436ee983..0000000000 --- a/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaMigrate-1.3.sql +++ /dev/null @@ -1,653 +0,0 @@ --- ------------------------------------------------------ --- 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 deleted file mode 100644 index d864606e25..0000000000 --- a/config/alfresco/dbscripts/upgrade/1.3/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaMigrate-1.3.sql +++ /dev/null @@ -1,636 +0,0 @@ --- ------------------------------------------------------ --- 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.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-1.sql b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-1.sql new file mode 100644 index 0000000000..f2fdd876b9 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-1.sql @@ -0,0 +1,92 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.3 to V1.4 Part 1 (Oracle) +-- +-- Adds the columns required to enforce the duplicate name detection +-- +-- Author: Derek Hulley +-- ------------------------------------------------------ + +-- +-- Unique name constraint +-- + +-- Apply new schema changes to child assoc table +ALTER TABLE child_assoc ADD + ( + child_node_name VARCHAR2(50 CHAR) DEFAULT 'V1.4 upgrade' NOT NULL, + child_node_name_crc NUMBER(19,0) DEFAULT -1 NOT NULL + ); + +UPDATE child_assoc + SET child_node_name_crc = id * -1; + +CREATE UNIQUE INDEX IDX_CHILD_NAMECRC ON child_assoc (parent_node_id, type_qname, child_node_name, child_node_name_crc); + +-- Apply unique index for node associations +CREATE UNIQUE INDEX IDX_ASSOC ON node_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; + +-- +-- The table renames will cause Hibernate to rehash the FK constraint names +-- +ALTER TABLE alf_access_control_entry RENAME CONSTRAINT FKF064DF7560601995 TO FKFFF41F9960601995; +ALTER TABLE alf_access_control_entry RENAME CONSTRAINT FKF064DF75B25A50BF TO FKFFF41F99B25A50BF; +ALTER TABLE alf_access_control_entry RENAME CONSTRAINT FKF064DF75B9553F6C TO FKFFF41F99B9553F6C; +ALTER TABLE alf_auth_ext_keys RENAME CONSTRAINT FK31D3BA097B7FDE43 TO FK8A749A657B7FDE43; +ALTER TABLE alf_child_assoc RENAME CONSTRAINT FKC6EFFF3274173FF4 TO FKFFC5468E74173FF4; +ALTER TABLE alf_child_assoc RENAME CONSTRAINT FKC6EFFF328E50E582 TO FKFFC5468E8E50E582; +ALTER TABLE alf_node RENAME CONSTRAINT FK33AE02B9553F6C TO FK60EFB626B9553F6C; +ALTER TABLE alf_node RENAME CONSTRAINT FK33AE02D24ADD25 TO FK60EFB626D24ADD25; +ALTER TABLE alf_node_properties RENAME CONSTRAINT FKC962BF907F2C8017 TO FK7D4CF8EC7F2C8017; +ALTER TABLE alf_node_aspects RENAME CONSTRAINT FK2B91A9DE7F2C8017 TO FKD654E027F2C8017; +ALTER TABLE alf_node_assoc RENAME CONSTRAINT FK5BAEF398B69C43F3 TO FKE1A550BCB69C43F3; +ALTER TABLE alf_node_assoc RENAME CONSTRAINT FK5BAEF398A8FC7769 TO FKE1A550BCA8FC7769; +ALTER TABLE alf_node_status RENAME CONSTRAINT FK38ECB8CF7F2C8017 TO FK71C2002B7F2C8017; +ALTER TABLE alf_store RENAME CONSTRAINT FK68AF8E122DBA5BA TO FKBD4FF53D22DBA5BA; + +-- +-- Rename the indexes to keep in synch with the new table names. For Oracle, Hibernate doesn't create or add these +-- +ALTER INDEX FKF064DF7560601995 RENAME TO FKFFF41F9960601995; +ALTER INDEX FKF064DF75B25A50BF RENAME TO FKFFF41F99B25A50BF; +ALTER INDEX FKF064DF75B9553F6C RENAME TO FKFFF41F99B9553F6C; +ALTER INDEX FK31D3BA097B7FDE43 RENAME TO FK8A749A657B7FDE43; +ALTER INDEX FKC6EFFF3274173FF4 RENAME TO FKFFC5468E74173FF4; +ALTER INDEX FKC6EFFF328E50E582 RENAME TO FKFFC5468E8E50E582; +ALTER INDEX FK33AE02B9553F6C RENAME TO FK60EFB626B9553F6C; +ALTER INDEX FK33AE02D24ADD25 RENAME TO FK60EFB626D24ADD25; +ALTER INDEX FKC962BF907F2C8017 RENAME TO FK7D4CF8EC7F2C8017; +ALTER INDEX FK2B91A9DE7F2C8017 RENAME TO FKD654E027F2C8017; +ALTER INDEX FK5BAEF398B69C43F3 RENAME TO FKE1A550BCB69C43F3; +ALTER INDEX FK5BAEF398A8FC7769 RENAME TO FKE1A550BCA8FC7769; +ALTER INDEX FK38ECB8CF7F2C8017 RENAME TO FK71C2002B7F2C8017; +ALTER INDEX FK68AF8E122DBA5BA RENAME TO FKBD4FF53D22DBA5BA; + +-- +-- 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, sysdate, 'UNKOWN', 1, 1, 'Script completed' + ); diff --git a/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-2.sql b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-2.sql new file mode 100644 index 0000000000..e5cfb444c1 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/1.4/org.hibernate.dialect.Oracle9Dialect/AlfrescoSchemaUpdate-1.4-2.sql @@ -0,0 +1,69 @@ +-- ------------------------------------------------------ +-- Alfresco Schema conversion V1.3 to V1.4 Part 2 (Oracle) +-- +-- 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 number(19,0) not null, + ip_address varchar2(15 char) not null, + primary key (id), + unique (ip_address) +); +insert into alf_server (id, ip_address) values (0, '0.0.0.0'); + +create table alf_transaction +( + id number(19,0) not null, + server_id number(19,0), + change_txn_id varchar2(56 char) not null, + primary key (id) +); +create index CHANGE_TXN_ID on alf_transaction (change_txn_id); +alter table alf_transaction add constraint FKB8761A3A9AE340B7 foreign key (server_id) references alf_server; +create index FKB8761A3A9AE340B7 on alf_transaction (server_id); + +insert into alf_transaction + ( + id, server_id, change_txn_id + ) + select + hibernate_sequence.nextval, + (select max(id) from alf_server), + change_txn_id + from alf_node_status; + +-- Alter node status +alter table alf_node_status add + ( + transaction_id number(19,0) DEFAULT 0 NOT NULL + ); +-- 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 and rownum = 1 + ); +alter table alf_node_status DROP COLUMN change_txn_id; +alter table alf_node_status ADD CONSTRAINT FK71C2002B9E57C13D FOREIGN KEY (transaction_id) REFERENCES alf_transaction (id); +create index FK71C2002B9E57C13D on alf_node_status (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, sysdate, 'UNKOWN', 1, 1, 'Script completed' + ); diff --git a/config/alfresco/desktop/Alfresco.exe b/config/alfresco/desktop/Alfresco.exe index 2bc8239ab1..1b2dfaf205 100644 Binary files a/config/alfresco/desktop/Alfresco.exe and b/config/alfresco/desktop/Alfresco.exe differ diff --git a/config/alfresco/desktop/showDetails.js b/config/alfresco/desktop/showDetails.js new file mode 100644 index 0000000000..0173a110b3 --- /dev/null +++ b/config/alfresco/desktop/showDetails.js @@ -0,0 +1,26 @@ +// Main action + +function runAction() +{ + var urlStr = webURL + "navigate/showDocDetails/workspace/SpacesStore/" + deskParams.getTarget(0).getNode().getId() + + "?ticket=" + deskParams.getTicket(); + + return urlStr; +} + +// Run the action +// +// Response :- +// Success - no return or return 0, or "0," +// For error or control response then return a string :- +// Error - "1," +// FileNotFound - "2," +// AccessDenied - "3," +// BadParameter - "4, +// NotWorkingCopy - "5," +// NoSuchAction - "6, +// LaunchURL - "7," +// CommandLine - "8," + +var response = "7," + runAction(); +response; diff --git a/config/alfresco/desktop/urlLink.js b/config/alfresco/desktop/urlLink.js new file mode 100644 index 0000000000..f498fb1549 --- /dev/null +++ b/config/alfresco/desktop/urlLink.js @@ -0,0 +1,29 @@ +// Main action + +function runAction() +{ + out.println("URL link to " + deskParams.getFolder()); + + var urlStr = webURL + "navigate/browse/workspace/SpacesStore/" + deskParams.getFolderNode().getId() + + "?ticket=" + deskParams.getTicket(); + out.println( " url=" + urlStr); + + return urlStr; +} + +// Run the action +// +// Response :- +// Success - no return or return 0, or "0," +// For error or control response then return a string :- +// Error - "1," +// FileNotFound - "2," +// AccessDenied - "3," +// BadParameter - "4, +// NotWorkingCopy - "5," +// NoSuchAction - "6, +// LaunchURL - "7," +// CommandLine - "8," + +var response = "7," + runAction(); +response; diff --git a/config/alfresco/extension/index-recovery-context.xml.sample b/config/alfresco/extension/index-tracking-context.xml.sample similarity index 53% rename from config/alfresco/extension/index-recovery-context.xml.sample rename to config/alfresco/extension/index-tracking-context.xml.sample index 415831e277..5c1343bec2 100644 --- a/config/alfresco/extension/index-recovery-context.xml.sample +++ b/config/alfresco/extension/index-tracking-context.xml.sample @@ -3,6 +3,39 @@ + + + + + + org.alfresco.repo.node.index.IndexRecoveryJob + + + + + + + + + + + + + + + 0,30 * * * * ? + + + + + + true + + + __AlfrescoClient.url - http://localhost:8080/alfresco/ + http://${localname}:8080/alfresco/ - - - - - - + + + + + + + + + diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index 392be9325f..a171b3642e 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -171,8 +171,8 @@ - - + + diff --git a/config/alfresco/messages/copy-service.properties b/config/alfresco/messages/copy-service.properties new file mode 100644 index 0000000000..ce041e4bae --- /dev/null +++ b/config/alfresco/messages/copy-service.properties @@ -0,0 +1,3 @@ +# copy service externalised display strings + +copy_service.copy_of_label=Copy of {0} \ No newline at end of file diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index c2a5bf70ef..76e1aeaa53 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -27,8 +27,8 @@ patch.savedSearchesPermission.description=Sets required permissions on 'Saved Se patch.savedSearchesPermission.result.applied=Granted CONTRIBUTOR role to EVERYONE on ''Saved Searches'' folder: {0}. patch.savedSearchesPermission.err.not_found='Saved Searches' folder could not be found. -patch.updatePermissionData.description=Update permission entries from 'folder' to 'cmobject'. -patch.updatePermissionData.upgrade=Please follow an upgrade path via server version 1.2.1 +patch.updatePermissionData.description=Update permissions from 'folder' to 'cmobject' [JIRA: AR-344]. +patch.updatePermissionData.result=Changed {0} 'folder' access control entries to 'cmobject'. patch.authoritiesFolder.description=Ensures the existence of the user authorities folder [JIRA: AR-497]. @@ -39,7 +39,7 @@ patch.fixNodeSerializableValues.description=Ensure that property values are not patch.fixNodeSerializableValues.result=Fixed {0} node property serialized values patch.updateGuestPermission.description=Rename guest permission from 'Guest' to 'Consumer' -patch.updateGuestPermission.upgrade=Please follow an upgrade path via server version 1.2.1 +patch.updateGuestPermission.result=Changed {0} 'Guest' access control entries to 'Consumer'. patch.categoryRootPermission.description=Sets required permissions on 'Category Root' folder. patch.categoryRootPermission.result=Granted CONSUMER role to GUEST on ''Category Root'' folder: {0}. @@ -52,7 +52,7 @@ patch.spacesRootPermission.description=Change Spaces store root permission from patch.spacesRootPermission.result=Updated Spaces store root permission from 'Consumer' to 'Read' patch.contentPermission.description=Update permission entries from 'cm:content' to 'sys:base'. -patch.contentPermission.upgrade=Please follow an upgrade path via server version 1.2.1 +patch.contentPermission.result=Changed {0} 'cm:content' access control entries to 'sys:base'. patch.forumsIcons.description=Updates forums icon references patch.forumsIcons.result=Updated {0} icon references diff --git a/config/alfresco/messages/schema-update.properties b/config/alfresco/messages/schema-update.properties index 5cebba18a2..f927e17562 100644 --- a/config/alfresco/messages/schema-update.properties +++ b/config/alfresco/messages/schema-update.properties @@ -1,6 +1,7 @@ # Schema update messages schema.update.msg.executing_script=Executing database script: {0} +schema.update.err.statement_failed=Statement execution failed:\n SQL: {0}\n Error: {1}\n File: {2}\n Line: {3} 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} diff --git a/config/alfresco/messages/workflow-interpreter-help.properties b/config/alfresco/messages/workflow-interpreter-help.properties new file mode 100644 index 0000000000..398b1b772b --- /dev/null +++ b/config/alfresco/messages/workflow-interpreter-help.properties @@ -0,0 +1 @@ +workflow_console.help=alfresco/messages/workflow-interpreter-help.txt diff --git a/config/alfresco/messages/workflow-interpreter-help.txt b/config/alfresco/messages/workflow-interpreter-help.txt new file mode 100644 index 0000000000..d8ed5405e6 --- /dev/null +++ b/config/alfresco/messages/workflow-interpreter-help.txt @@ -0,0 +1,174 @@ +## +## Meta commands +## + +ok> help + + List this help. + +ok> r + + Repeat last command. + +ok> user [] + + Switch to specified . If is omitted, the currently + selected user is shown. + +ok> use + + Show current workflow context. + +## +## Workflow Definition Commands +## + +ok> deploy + + Deploy workflow definition to Alfresco server. + + class path to workflow definition. + +ok> redeploy + + Redeploy the last workflow definition. + +ok> show definitions + + List all deployed workflow definitions. + +ok> use definition [] + + Switch to use the workflow definition identified by . If + is ommited, the currently selected workflow definition + is shown. + +## +## Variable Commands +## + +ok> var + + Show all defined variables. + +ok> var [*]= + + Define or update a variable. + + variable name + [*] if specified, define a collection + variable value (comma-seperate to specify a list of values) + + e.g. + + set bpm:assignee*=admin,fred + set wf:notifyMe=true + +ok> var [*] person + + Define or update a (cm:person) node ref variable. + + variable name + [*] if specified, define a collection + variable value (comma-seperate to specify a list of values) + + e.g. + + set bpm:assignee* person admin,fred + +ok> var = + + Delete an existing variable. + + variable name + +## +## Workflow Commands +## + +ok> start []]* + + Start a new workflow using the currently selected workflow definition. Start + Task parameters are provided as name/value pairs or references to pre-defined + variables. + + e.g. + + start bpm:assignee=david wf:predefined + +ok> show workflows + + Display the list of active workflows for the currently selected workflow + definition. + +ok> use workflow + + Use the specified . + +ok> show paths [] + + Display the workflow paths for the specified . If + is omitted, the paths for the currently started workflow are shown. + +ok> show transitions [] + + Display all available transitions for the specified . If + is omitted, the transitions for the currently started workflow + are shown. + +ok> signal [] + + Signal transition on specified . If is omitted, the + default transition is taken. + +ok> desc workflow + + Describe the specified . + +ok> end workflow + + End (cancel) the specified . + +## +## Task Commands +## + +ok> show my tasks + + List tasks assigned to the currently selected user. + +ok> show my completed + + List tasks completed by the currently selected user. + +ok> show tasks [] + + List tasks associated with the specified workflow . If is + omitted, the tasks associated with the currently selected workflow path are + shown. + +ok> desc task + + Describe the task identified by . + +ok> update task []]* + + Update the state of the specified . Task properties are provided as + name/value pairs or references to pre-defined variables. + + variable name + [*] if specified, define a collection + variable value (comma-seperate to specify a list of values) + + e.g. + + update task jbpm$122 bpm:assignee=fred wf:notifyMe=false + +ok> end task [] + + End the task identified by . If is omitted, the + default transition is taken. + +## +## end +## diff --git a/config/alfresco/model/bpmModel.xml b/config/alfresco/model/bpmModel.xml index 09d9b4498a..d88eaf5a7e 100644 --- a/config/alfresco/model/bpmModel.xml +++ b/config/alfresco/model/bpmModel.xml @@ -187,13 +187,14 @@ d:text - + + d:text - workflow_item_read_actions + read_package_item_actions @@ -247,6 +248,29 @@ + + + + add_package_item_actions + + + + edit_and_remove_package_item_actions + + + + + + + + + + + + + + + @@ -262,24 +286,29 @@ + + + + + - + + - - workflow_collection_actions - + + + false + false + + + cm:person + true + true + + - - workflow_item_collection_actions - - - - - - - - - + + diff --git a/config/alfresco/network-protocol-context.xml b/config/alfresco/network-protocol-context.xml index 10fa179cfe..f5d0408e00 100644 --- a/config/alfresco/network-protocol-context.xml +++ b/config/alfresco/network-protocol-context.xml @@ -57,6 +57,7 @@ + diff --git a/config/alfresco/public-services-context.xml b/config/alfresco/public-services-context.xml index d93bb54e85..364fd29a21 100644 --- a/config/alfresco/public-services-context.xml +++ b/config/alfresco/public-services-context.xml @@ -80,6 +80,9 @@ + + + @@ -124,6 +128,9 @@ + + + diff --git a/config/alfresco/public-services-security-context.xml b/config/alfresco/public-services-security-context.xml index fcf2b92d31..1ed94603a2 100644 --- a/config/alfresco/public-services-security-context.xml +++ b/config/alfresco/public-services-security-context.xml @@ -325,7 +325,7 @@ org.alfresco.service.cmr.repository.NodeService.getStores=AFTER_ACL_NODE.sys:base.Read org.alfresco.service.cmr.repository.NodeService.createStore=ACL_METHOD.ROLE_ADMINISTRATOR - org.alfresco.service.cmr.repository.NodeService.exists=ACL_NODE.0.sys:base.Read + org.alfresco.service.cmr.repository.NodeService.exists=ACL_ALLOW org.alfresco.service.cmr.repository.NodeService.getNodeStatus=ACL_NODE.0.sys:base.Read org.alfresco.service.cmr.repository.NodeService.getRootNode=ACL_NODE.0.sys:base.Read org.alfresco.service.cmr.repository.NodeService.createNode=ACL_NODE.0.sys:base.CreateChildren @@ -374,7 +374,7 @@ org.alfresco.service.cmr.model.FileFolderService.listFolders=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read org.alfresco.service.cmr.model.FileFolderService.search=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read org.alfresco.service.cmr.model.FileFolderService.searchSimple=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.Read - org.alfresco.service.cmr.model.FileFolderService.rename=ACL_PARENT.0.sys:base.CreateChildren,AFTER_ACL_NODE.sys:base.WriteProperties + org.alfresco.service.cmr.model.FileFolderService.rename=AFTER_ACL_NODE.sys:base.WriteProperties org.alfresco.service.cmr.model.FileFolderService.move=ACL_NODE.0.sys:base.DeleteNode,ACL_NODE.1.sys:base.CreateChildren org.alfresco.service.cmr.model.FileFolderService.copy=ACL_NODE.0.sys:base.Read,ACL_NODE.1.sys:base.CreateChildren org.alfresco.service.cmr.model.FileFolderService.create=ACL_NODE.0.sys:base.CreateChildren diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 49b3e270fd..7f121adff3 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -65,7 +65,7 @@ db.username=alfresco db.password=alfresco db.pool.initial=10 db.pool.max=20 -db.pool.maxIdleTime=120 +db.pool.maxIdleTime=120000 # Email configuration diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index 79955d57a9..d5697b927f 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -21,6 +21,9 @@ + + + false diff --git a/config/alfresco/templates/content/examples/show_audit.ftl b/config/alfresco/templates/content/examples/show_audit.ftl new file mode 100644 index 0000000000..d439d7c9ee --- /dev/null +++ b/config/alfresco/templates/content/examples/show_audit.ftl @@ -0,0 +1,173 @@ + <#-- Shows some general audit info about the current document --> + <#if document?exists> +

Current Docuement Audit Info

+ Name: ${document.name}
+ + + + + + + + + + + + + + + + + + + <#list document.auditTrail as t> + + + + <#if t.auditService?exists> + + <#else> + + + <#if t.auditMethod?exists> + + <#else> + + + + <#if t.fail?exists> + + <#else> + + + <#if t.message?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[0]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[1]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[2]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[3]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[4]?exists> + + <#else> + + + <#if t.returnObjectAsString?exists> + + <#else> + + + <#if t.throwableAsString?exists> + + <#else> + + + + + +
User NameApplicationServiceMethodTimestampFailedMessageArg 1Arg 2Arg 3Arg 4Arg 5ReturnThowableTX
${t.userIdentifier}${t.auditApplication}${t.auditService} ${t.auditMethod} ${t.date}${t.fail?string("FAILED", "OK")} ${t.message} ${t.methodArgumentsAsStrings[0]} ${t.methodArgumentsAsStrings[1]} ${t.methodArgumentsAsStrings[2]} ${t.methodArgumentsAsStrings[3]} ${t.methodArgumentsAsStrings[4]} ${t.returnObjectAsString} ${t.throwableAsString} ${t.txId}
+ <#elseif space?exists> +

Current Space Audit Info:

+ Name: ${space.name}
+ + + + + + + + + + + + + + + + + + + + <#list space.auditTrail as t> + + + + <#if t.auditService?exists> + + <#else> + + + <#if t.auditMethod?exists> + + <#else> + + + + <#if t.fail?exists> + + <#else> + + + <#if t.message?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[0]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[1]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[2]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[3]?exists> + + <#else> + + + <#if t.methodArgumentsAsStrings[4]?exists> + + <#else> + + + <#if t.returnObjectAsString?exists> + + <#else> + + + <#if t.throwableAsString?exists> + + <#else> + + + + + +
User NameApplicationServiceMethodTimestampFailedMessageArg 1Arg 2Arg 3Arg 4Arg 5ReturnThowableTX
${t.userIdentifier}${t.auditApplication}${t.auditService} ${t.auditMethod} ${t.date}${t.fail?string("FAILED", "OK")} ${t.message} ${t.methodArgumentsAsStrings[0]} ${t.methodArgumentsAsStrings[1]} ${t.methodArgumentsAsStrings[2]} ${t.methodArgumentsAsStrings[3]} ${t.methodArgumentsAsStrings[4]} ${t.returnObjectAsString} ${t.throwableAsString} ${t.txId}
+ \ No newline at end of file diff --git a/config/alfresco/templates/content_template_examples.xml b/config/alfresco/templates/content_template_examples.xml index 85ac61c026..ed43602646 100644 --- a/config/alfresco/templates/content_template_examples.xml +++ b/config/alfresco/templates/content_template_examples.xml @@ -140,4 +140,18 @@ + + + + + + + true + Displays the audit trail for an object. + contentUrl=classpath:alfresco/templates/content/examples/show_audit.ftl|mimetype=text/plain|size=6134|encoding=UTF-8 + show_audit.ftl + show_audit.ftl + + + \ No newline at end of file diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index 468f5d350a..ae0f4a3ece 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -23,26 +23,33 @@ +
+ + + + + + + + + + alfresco.messages.workflow-interpreter-help + + + + - - true - - - - - - - - - - + true + + + @@ -73,7 +80,7 @@ - + diff --git a/config/alfresco/workflow/workflowModel.xml b/config/alfresco/workflow/workflowModel.xml index 73fdd9e553..30dbf7b625 100644 --- a/config/alfresco/workflow/workflowModel.xml +++ b/config/alfresco/workflow/workflowModel.xml @@ -19,19 +19,18 @@ bpm:startTask + + bpm:assignee + bpm:workflowTask - - - workflow_item_edit_actions + edit_package_item_actions - - @@ -39,21 +38,29 @@ - bpm:startTask - d:boolean false - + + bpm:assignee + bpm:workflowTask + + + add_package_item_actions + + + edit_package_item_actions + + diff --git a/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp b/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp index 31f08bcf3e..b35d4e6922 100644 --- a/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp +++ b/source/cpp/CAlfrescoApp/source/alfresco/Alfresco.cpp @@ -309,7 +309,7 @@ DesktopResponse AlfrescoInterface::runAction(AlfrescoActionInfo& action, Desktop // Build the run action I/O control request DataBuffer reqbuf( 1024); - DataBuffer respbuf( 256); + DataBuffer respbuf( 4096); reqbuf.putFixedString( IOSignature, IOSignatureLen); reqbuf.putString( action.getName()); diff --git a/source/java/org/alfresco/filesys/CIFSServer.java b/source/java/org/alfresco/filesys/CIFSServer.java index b62f92fff4..34fa3fe64a 100644 --- a/source/java/org/alfresco/filesys/CIFSServer.java +++ b/source/java/org/alfresco/filesys/CIFSServer.java @@ -26,12 +26,11 @@ import org.alfresco.filesys.netbios.server.NetBIOSNameServer; import org.alfresco.filesys.server.NetworkServer; import org.alfresco.filesys.server.config.ServerConfiguration; import org.alfresco.filesys.smb.server.SMBServer; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; /** @@ -41,7 +40,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * * @author GKSpencer */ -public class CIFSServer implements ApplicationListener +public class CIFSServer extends AbstractLifecycleBean { private static final Log logger = LogFactory.getLog("org.alfresco.smb.server"); @@ -81,29 +80,6 @@ public class CIFSServer implements ApplicationListener return (filesysConfig != null && filesysConfig.isSMBServerEnabled()); } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) - { - if (event instanceof ContextRefreshedEvent) - { - try - { - startServer(); - } - catch (SocketException e) - { - throw new AlfrescoRuntimeException("Failed to start CIFS server", e); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("Failed to start CIFS server", e); - } - } - } - /** * Start the CIFS server components * @@ -264,5 +240,27 @@ public class CIFSServer implements ApplicationListener System.exit(1); } + @Override + protected void onBootstrap(ApplicationEvent event) + { + try + { + startServer(); + } + catch (SocketException e) + { + throw new AlfrescoRuntimeException("Failed to start CIFS server", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to start CIFS server", e); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + stopServer(); + } } diff --git a/source/java/org/alfresco/filesys/FTPServer.java b/source/java/org/alfresco/filesys/FTPServer.java index f77cb0b67d..270b209373 100644 --- a/source/java/org/alfresco/filesys/FTPServer.java +++ b/source/java/org/alfresco/filesys/FTPServer.java @@ -24,11 +24,11 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.ftp.FTPNetworkServer; import org.alfresco.filesys.server.NetworkServer; import org.alfresco.filesys.server.config.ServerConfiguration; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -39,7 +39,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; * * @author GKSpencer */ -public class FTPServer implements ApplicationListener +public class FTPServer extends AbstractLifecycleBean { private static final Log logger = LogFactory.getLog("org.alfresco.ftp.server"); @@ -79,29 +79,6 @@ public class FTPServer implements ApplicationListener return (filesysConfig != null && filesysConfig.isFTPServerEnabled()); } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) - { - if (event instanceof ContextRefreshedEvent) - { - try - { - startServer(); - } - catch (SocketException e) - { - throw new AlfrescoRuntimeException("Failed to start FTP server", e); - } - catch (IOException e) - { - throw new AlfrescoRuntimeException("Failed to start FTP server", e); - } - } - } - /** * Start the FTP server components * @@ -251,4 +228,28 @@ public class FTPServer implements ApplicationListener } System.exit(1); } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + try + { + startServer(); + } + catch (SocketException e) + { + throw new AlfrescoRuntimeException("Failed to start FTP server", e); + } + catch (IOException e) + { + throw new AlfrescoRuntimeException("Failed to start FTP server", e); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + stopServer(); + } + } diff --git a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java index 16a9daa11c..afd55c32a8 100644 --- a/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java +++ b/source/java/org/alfresco/filesys/ftp/FTPSrvSession.java @@ -852,7 +852,7 @@ public class FTPSrvSession extends SrvSession implements Runnable // DEBUG if ( logger.isDebugEnabled()) - logger.debug("Logon failed", ex); + logger.debug("Logon failed for user " + cInfo.getUserName()); } // Check if the logon was successful diff --git a/source/java/org/alfresco/filesys/server/auth/AlfrescoAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/AlfrescoAuthenticator.java index 817e7b361b..f26a8bfdd2 100644 --- a/source/java/org/alfresco/filesys/server/auth/AlfrescoAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/AlfrescoAuthenticator.java @@ -17,10 +17,17 @@ package org.alfresco.filesys.server.auth; import java.security.NoSuchAlgorithmException; +import net.sf.acegisecurity.Authentication; import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.auth.AuthContext; +import org.alfresco.filesys.server.auth.CifsAuthenticator; +import org.alfresco.filesys.server.auth.ClientInfo; +import org.alfresco.filesys.server.auth.NTLanManAuthContext; import org.alfresco.filesys.smb.server.SMBSrvSession; +import org.alfresco.filesys.util.HexDump; import org.alfresco.repo.security.authentication.NTLMMode; +import org.alfresco.repo.security.authentication.ntlm.NTLMPassthruToken; /** * Alfresco Authenticator Class @@ -88,10 +95,7 @@ public class AlfrescoAuthenticator extends CifsAuthenticator { // Use the existing authentication token - if ( client.isGuest()) - m_authComponent.setGuestUserAsCurrentUser(); - else - m_authComponent.setCurrentUser(mapUserNameToPerson(client.getUserName())); + m_authComponent.setCurrentUser(client.getUserName()); // Debug @@ -107,7 +111,7 @@ public class AlfrescoAuthenticator extends CifsAuthenticator int authSts = AUTH_DISALLOW; - if ( client.isGuest() || client.getUserName().equalsIgnoreCase(GUEST_USERNAME)) + if ( client.isGuest() || client.getUserName().equalsIgnoreCase(getGuestUserName())) { // Check if guest logons are allowed @@ -140,7 +144,13 @@ public class AlfrescoAuthenticator extends CifsAuthenticator authSts = doMD4UserAuthentication(client, sess, alg); } - + else + { + // Perform passthru authentication password check + + authSts = doPassthruUserAuthentication(client, sess, alg); + } + // Check if the logon status indicates a guest logon if ( authSts == AUTH_GUEST) @@ -172,6 +182,64 @@ public class AlfrescoAuthenticator extends CifsAuthenticator return authSts; } + /** + * Return an authentication context for the new session + * + * @return AuthContext + */ + public AuthContext getAuthContext( SMBSrvSession sess) + { + // Check if the client is already authenticated, and it is not a null logon + + AuthContext authCtx = null; + + if ( sess.hasAuthenticationContext() && sess.hasAuthenticationToken() && + sess.getClientInformation().getLogonType() != ClientInfo.LogonNull) + { + // Return the previous challenge, user is already authenticated + + authCtx = (NTLanManAuthContext) sess.getAuthenticationContext(); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Re-using existing challenge, already authenticated"); + } + else if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) + { + // Create a new authentication context for the session + + authCtx = new NTLanManAuthContext(); + sess.setAuthenticationContext( authCtx); + } + else + { + // Create an authentication token for the session + + NTLMPassthruToken authToken = new NTLMPassthruToken(); + + // Run the first stage of the passthru authentication to get the challenge + + m_authComponent.authenticate( authToken); + + // Save the authentication token for the second stage of the authentication + + sess.setAuthenticationToken(authToken); + + // Get the challenge from the token + + if ( authToken.getChallenge() != null) + { + authCtx = new NTLanManAuthContext( authToken.getChallenge().getBytes()); + sess.setAuthenticationContext( authCtx); + } + } + + // Return the authentication context + + return authCtx; + } + /** * Perform MD4 user authentication * @@ -217,6 +285,20 @@ public class AlfrescoAuthenticator extends CifsAuthenticator // Validate the password byte[] clientHash = client.getPassword(); + if ( clientHash == null || clientHash.length != 24) + { + // Use the secondary password hash from the client + + clientHash = client.getANSIPassword(); + + // DEBUG + + if ( logger.isDebugEnabled()) + { + logger.debug( "Using secondary password hash - " + HexDump.hexString(clientHash)); + logger.debug( " Local hash - " + HexDump.hexString( localHash)); + } + } if ( clientHash == null || clientHash.length != localHash.length) return CifsAuthenticator.AUTH_BADPASSWORD; @@ -229,7 +311,7 @@ public class AlfrescoAuthenticator extends CifsAuthenticator // Set the current user to be authenticated, save the authentication token - client.setAuthenticationToken( m_authComponent.setCurrentUser(mapUserNameToPerson(client.getUserName()))); + client.setAuthenticationToken( m_authComponent.setCurrentUser(client.getUserName())); // Get the users home folder node, if available @@ -259,4 +341,101 @@ public class AlfrescoAuthenticator extends CifsAuthenticator return allowGuest() ? CifsAuthenticator.AUTH_GUEST : CifsAuthenticator.AUTH_DISALLOW; } + + /** + * Perform passthru user authentication + * + * @param client Client information + * @param sess Server session + * @param alg Encryption algorithm + * @return int + */ + private final int doPassthruUserAuthentication(ClientInfo client, SrvSession sess, int alg) + { + // Get the authentication token for the session + + NTLMPassthruToken authToken = (NTLMPassthruToken) sess.getAuthenticationToken(); + + if ( authToken == null) + return CifsAuthenticator.AUTH_DISALLOW; + + // Get the appropriate hashed password for the algorithm + + int authSts = CifsAuthenticator.AUTH_DISALLOW; + byte[] hashedPassword = null; + + if ( alg == NTLM1) + hashedPassword = client.getPassword(); + else if ( alg == LANMAN) + hashedPassword = client.getANSIPassword(); + else + { + // Invalid/unsupported algorithm specified + + return CifsAuthenticator.AUTH_DISALLOW; + } + + // Set the username and hashed password in the authentication token + + authToken.setUserAndPassword( client.getUserName(), hashedPassword, alg); + + // Authenticate the user + + Authentication genAuthToken = null; + + try + { + // Run the second stage of the passthru authentication + + genAuthToken = m_authComponent.authenticate( authToken); + + // Check if the user has been logged on as a guest + + if (authToken.isGuestLogon()) + { + + // Check if the local server allows guest access + + if (allowGuest() == true) + { + + // Allow the user access as a guest + + authSts = CifsAuthenticator.AUTH_GUEST; + } + } + else + { + + // Allow the user full access to the server + + authSts = CifsAuthenticator.AUTH_ALLOW; + } + + // Set the current user to be authenticated, save the authentication token + + client.setAuthenticationToken( genAuthToken); + + // Get the users home folder node, if available + + getHomeFolderForUser( client); + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Auth token " + genAuthToken); + } + catch ( Exception ex) + { + logger.error("Error during passthru authentication", ex); + } + + // Clear the authentication token + + sess.setAuthenticationToken(null); + + // Return the authentication status + + return authSts; + } } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java index 7815a7cf8f..3effae1145 100644 --- a/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/CifsAuthenticator.java @@ -470,7 +470,7 @@ public abstract class CifsAuthenticator // Store the client maximum buffer size, maximum multiplexed requests count and client // capability flags - sess.setClientMaximumBufferSize(maxBufSize); + sess.setClientMaximumBufferSize(maxBufSize != 0 ? maxBufSize : SMBSrvSession.DefaultBufferSize); sess.setClientMaximumMultiplex(maxMpx); sess.setClientCapabilities(capabs); diff --git a/source/java/org/alfresco/filesys/server/auth/ClientInfo.java b/source/java/org/alfresco/filesys/server/auth/ClientInfo.java index 56eec14aeb..421c840583 100644 --- a/source/java/org/alfresco/filesys/server/auth/ClientInfo.java +++ b/source/java/org/alfresco/filesys/server/auth/ClientInfo.java @@ -71,6 +71,10 @@ public class ClientInfo private Authentication m_authToken; + // Authentication ticket, used for web access without having to re-authenticate + + private String m_authTicket; + // Home folder node private NodeRef m_homeNode; @@ -286,6 +290,26 @@ public class ClientInfo { return m_authToken; } + + /** + * Check if the client has an authentication ticket + * + * @return boolean + */ + public final boolean hasAuthenticationTicket() + { + return m_authTicket != null ? true : false; + } + + /** + * Return the authentication ticket + * + * @return String + */ + public final String getAuthenticationTicket() + { + return m_authTicket; + } /** * Check if the client has a home folder node @@ -409,6 +433,16 @@ public class ClientInfo { m_authToken = token; } + + /** + * Set the authentication ticket + * + * @param ticket String + */ + public final void setAuthenticationTicket(String ticket) + { + m_authTicket = ticket; + } /** * Set the home folder node @@ -448,6 +482,12 @@ public class ClientInfo str.append(",token="); str.append(getAuthenticationToken()); } + + if ( hasAuthenticationTicket()) + { + str.append(",ticket="); + str.append(getAuthenticationTicket()); + } if (isGuest()) str.append(",Guest"); diff --git a/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java index c5647faff4..4f034ee1c7 100644 --- a/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java +++ b/source/java/org/alfresco/filesys/server/auth/EnterpriseCifsAuthenticator.java @@ -584,7 +584,7 @@ public class EnterpriseCifsAuthenticator extends CifsAuthenticator implements Ca // Store the client maximum buffer size, maximum multiplexed requests count and client capability flags - sess.setClientMaximumBufferSize(maxBufSize); + sess.setClientMaximumBufferSize(maxBufSize != 0 ? maxBufSize : SMBSrvSession.DefaultBufferSize); sess.setClientMaximumMultiplex(maxMpx); sess.setClientCapabilities(capabs); diff --git a/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java b/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java deleted file mode 100644 index 5b77d4171d..0000000000 --- a/source/java/org/alfresco/filesys/server/auth/ntlm/AlfrescoAuthenticator.java +++ /dev/null @@ -1,427 +0,0 @@ -/* - * 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.filesys.server.auth.ntlm; - -import java.security.NoSuchAlgorithmException; -import net.sf.acegisecurity.Authentication; - -import org.alfresco.filesys.server.SrvSession; -import org.alfresco.filesys.server.auth.AuthContext; -import org.alfresco.filesys.server.auth.CifsAuthenticator; -import org.alfresco.filesys.server.auth.ClientInfo; -import org.alfresco.filesys.server.auth.NTLanManAuthContext; -import org.alfresco.filesys.smb.server.SMBSrvSession; -import org.alfresco.filesys.util.DataPacker; -import org.alfresco.repo.security.authentication.NTLMMode; -import org.alfresco.repo.security.authentication.ntlm.NTLMPassthruToken; - -/** - * Alfresco Authenticator Class - * - *

The Alfresco authenticator implementation enables user level security mode using the Alfresco authentication - * component. - * - *

Note: Switching off encrypted password support will cause later NT4 service pack releases and - * Win2000 to refuse to connect to the server without a registry update on the client. - * - * @author GKSpencer - */ -public class AlfrescoAuthenticator extends CifsAuthenticator -{ - /** - * Default Constructor - * - *

Default to user mode security with encrypted password support. - */ - public AlfrescoAuthenticator() - { - } - - /** - * Validate that the authentication component supports the required mode - * - * @return boolean - */ - protected boolean validateAuthenticationMode() - { - // Make sure the authentication component supports MD4 hashed passwords or passthru mode - - if ( m_authComponent.getNTLMMode() != NTLMMode.MD4_PROVIDER && - m_authComponent.getNTLMMode() != NTLMMode.PASS_THROUGH) - return false; - return true; - } - - /** - * Authenticate a user - * - * @param client Client information - * @param sess Server session - * @param alg Encryption algorithm - */ - public int authenticateUser(ClientInfo client, SrvSession sess, int alg) - { - // Check if this is an SMB/CIFS null session logon. - // - // The null session will only be allowed to connect to the IPC$ named pipe share. - - if (client.isNullSession() && sess instanceof SMBSrvSession) - { - // Debug - - if ( logger.isDebugEnabled()) - logger.debug("Null CIFS logon allowed"); - - return CifsAuthenticator.AUTH_ALLOW; - } - - // Check if the client is already authenticated, and it is not a null logon - - if ( client.getAuthenticationToken() != null && client.getLogonType() != ClientInfo.LogonNull) - { - // Use the existing authentication token - - m_authComponent.setCurrentUser(client.getUserName()); - - // Debug - - if ( logger.isDebugEnabled()) - logger.debug("Re-using existing authentication token"); - - // Return the authentication status - - return client.getLogonType() != ClientInfo.LogonGuest ? AUTH_ALLOW : AUTH_GUEST; - } - - // Check if this is a guest logon - - int authSts = AUTH_DISALLOW; - - if ( client.isGuest() || client.getUserName().equalsIgnoreCase(getGuestUserName())) - { - // Check if guest logons are allowed - - if ( allowGuest() == false) - return AUTH_DISALLOW; - - // Get a guest authentication token - - doGuestLogon( client, sess); - - // Indicate logged on as guest - - authSts = AUTH_GUEST; - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Authenticated user " + client.getUserName() + " sts=" + getStatusAsString(authSts)); - - // Return the guest status - - return authSts; - } - - // Check if MD4 or passthru mode is configured - - else if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) - { - // Perform local MD4 password check - - authSts = doMD4UserAuthentication(client, sess, alg); - } - else - { - // Perform passthru authentication password check - - authSts = doPassthruUserAuthentication(client, sess, alg); - } - - // Check if the logon status indicates a guest logon - - if ( authSts == AUTH_GUEST) - { - // Only allow the guest logon if user mapping is enabled - - if ( mapUnknownUserToGuest()) - { - // Logon as guest, setup the security context - - doGuestLogon( client, sess); - } - else - { - // Do not allow the guest logon - - authSts = AUTH_DISALLOW; - } - } - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Authenticated user " + client.getUserName() + " sts=" + getStatusAsString(authSts) + - " via " + (m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER ? "MD4" : "Passthru")); - - // Return the authentication status - - return authSts; - } - - /** - * Return an authentication context for the new session - * - * @return AuthContext - */ - public AuthContext getAuthContext( SMBSrvSession sess) - { - // Check if the client is already authenticated, and it is not a null logon - - AuthContext authCtx = null; - - if ( sess.hasAuthenticationContext() && sess.hasAuthenticationToken() && - sess.getClientInformation().getLogonType() != ClientInfo.LogonNull) - { - // Return the previous challenge, user is already authenticated - - authCtx = (NTLanManAuthContext) sess.getAuthenticationContext(); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Re-using existing challenge, already authenticated"); - } - else if ( m_authComponent.getNTLMMode() == NTLMMode.MD4_PROVIDER) - { - // Create a new authentication context for the session - - authCtx = new NTLanManAuthContext(); - sess.setAuthenticationContext( authCtx); - } - else - { - // Create an authentication token for the session - - NTLMPassthruToken authToken = new NTLMPassthruToken(); - - // Run the first stage of the passthru authentication to get the challenge - - m_authComponent.authenticate( authToken); - - // Save the authentication token for the second stage of the authentication - - sess.setAuthenticationToken(authToken); - - // Get the challenge from the token - - if ( authToken.getChallenge() != null) - { - authCtx = new NTLanManAuthContext( authToken.getChallenge().getBytes()); - sess.setAuthenticationContext( authCtx); - } - } - - // Return the authentication context - - return authCtx; - } - - /** - * Perform MD4 user authentication - * - * @param client Client information - * @param sess Server session - * @param alg Encryption algorithm - * @return int - */ - private final int doMD4UserAuthentication(ClientInfo client, SrvSession sess, int alg) - { - // Get the stored MD4 hashed password for the user, or null if the user does not exist - - String md4hash = m_authComponent.getMD4HashedPassword(client.getUserName()); - - if ( md4hash != null) - { - // Check if the client has supplied an NTLM hashed password, if not then do not allow access - - if ( client.getPassword() == null) - return CifsAuthenticator.AUTH_BADPASSWORD; - - try - { - // Generate the local encrypted password using the challenge that was sent to the client - - byte[] p21 = new byte[21]; - byte[] md4byts = m_md4Encoder.decodeHash(md4hash); - System.arraycopy(md4byts, 0, p21, 0, 16); - - // Get the challenge that was sent to the client - - NTLanManAuthContext authCtx = null; - - if ( sess.hasAuthenticationContext() && sess.getAuthenticationContext() instanceof NTLanManAuthContext) - authCtx = (NTLanManAuthContext) sess.getAuthenticationContext(); - else - return CifsAuthenticator.AUTH_DISALLOW; - - // Generate the local hash of the password using the same challenge - - byte[] localHash = getEncryptor().doNTLM1Encryption(p21, authCtx.getChallenge()); - - // Validate the password - - byte[] clientHash = client.getPassword(); - - if ( clientHash == null || clientHash.length != localHash.length) - return CifsAuthenticator.AUTH_BADPASSWORD; - - for ( int i = 0; i < clientHash.length; i++) - { - if ( clientHash[i] != localHash[i]) - return CifsAuthenticator.AUTH_BADPASSWORD; - } - - // Set the current user to be authenticated, save the authentication token - - client.setAuthenticationToken( m_authComponent.setCurrentUser(client.getUserName())); - - // Get the users home folder node, if available - - getHomeFolderForUser( client); - - // Passwords match, grant access - - return CifsAuthenticator.AUTH_ALLOW; - } - catch (NoSuchAlgorithmException ex) - { - } - - // Error during password check, do not allow access - - return CifsAuthenticator.AUTH_DISALLOW; - } - - // Check if this is an SMB/CIFS null session logon. - // - // The null session will only be allowed to connect to the IPC$ named pipe share. - - if (client.isNullSession() && sess instanceof SMBSrvSession) - return CifsAuthenticator.AUTH_ALLOW; - - // User does not exist, check if guest access is allowed - - return allowGuest() ? CifsAuthenticator.AUTH_GUEST : CifsAuthenticator.AUTH_DISALLOW; - } - - /** - * Perform passthru user authentication - * - * @param client Client information - * @param sess Server session - * @param alg Encryption algorithm - * @return int - */ - private final int doPassthruUserAuthentication(ClientInfo client, SrvSession sess, int alg) - { - // Get the authentication token for the session - - NTLMPassthruToken authToken = (NTLMPassthruToken) sess.getAuthenticationToken(); - - if ( authToken == null) - return CifsAuthenticator.AUTH_DISALLOW; - - // Get the appropriate hashed password for the algorithm - - int authSts = CifsAuthenticator.AUTH_DISALLOW; - byte[] hashedPassword = null; - - if ( alg == NTLM1) - hashedPassword = client.getPassword(); - else if ( alg == LANMAN) - hashedPassword = client.getANSIPassword(); - else - { - // Invalid/unsupported algorithm specified - - return CifsAuthenticator.AUTH_DISALLOW; - } - - // Set the username and hashed password in the authentication token - - authToken.setUserAndPassword( client.getUserName(), hashedPassword, alg); - - // Authenticate the user - - Authentication genAuthToken = null; - - try - { - // Run the second stage of the passthru authentication - - genAuthToken = m_authComponent.authenticate( authToken); - - // Check if the user has been logged on as a guest - - if (authToken.isGuestLogon()) - { - - // Check if the local server allows guest access - - if (allowGuest() == true) - { - - // Allow the user access as a guest - - authSts = CifsAuthenticator.AUTH_GUEST; - } - } - else - { - - // Allow the user full access to the server - - authSts = CifsAuthenticator.AUTH_ALLOW; - } - - // Set the current user to be authenticated, save the authentication token - - client.setAuthenticationToken( genAuthToken); - - // Get the users home folder node, if available - - getHomeFolderForUser( client); - - // DEBUG - - if ( logger.isDebugEnabled()) - logger.debug("Auth token " + genAuthToken); - } - catch ( Exception ex) - { - logger.error("Error during passthru authentication", ex); - } - - // Clear the authentication token - - sess.setAuthenticationToken(null); - - // Return the authentication status - - return authSts; - } -} \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/server/auth/ntlm/NTLMLogonDetails.java b/source/java/org/alfresco/filesys/server/auth/ntlm/NTLMLogonDetails.java index c5c8f7f314..4615408bd5 100644 --- a/source/java/org/alfresco/filesys/server/auth/ntlm/NTLMLogonDetails.java +++ b/source/java/org/alfresco/filesys/server/auth/ntlm/NTLMLogonDetails.java @@ -39,6 +39,10 @@ public class NTLMLogonDetails private String m_authSrvAddr; + // Date/time authentication was started + + private long m_createTime; + // Date/time the user was authenticated private long m_authTime; @@ -61,6 +65,7 @@ public class NTLMLogonDetails */ public NTLMLogonDetails() { + m_createTime = System.currentTimeMillis(); } /** @@ -74,6 +79,8 @@ public class NTLMLogonDetails */ public NTLMLogonDetails(String user, String wks, String domain, boolean guest, String authSrv) { + m_createTime = System.currentTimeMillis(); + setDetails(user, wks, domain, guest, authSrv); } @@ -117,6 +124,16 @@ public class NTLMLogonDetails return m_authSrvAddr; } + /** + * Return the date/time the authentication was started + * + * @return long + */ + public final long createdAt() + { + return m_createTime; + } + /** * Return the date/time the user was authenticated * diff --git a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java index 46c7e16839..a48040b950 100644 --- a/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java +++ b/source/java/org/alfresco/filesys/server/config/ServerConfiguration.java @@ -81,11 +81,10 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; /** *

@@ -93,7 +92,7 @@ import org.springframework.context.event.ContextRefreshedEvent; * * @author Gary K. Spencer */ -public class ServerConfiguration implements ApplicationListener +public class ServerConfiguration extends AbstractLifecycleBean { // Debug logging @@ -425,18 +424,6 @@ public class ServerConfiguration implements ApplicationListener return initialised; } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) - { - if (event instanceof ContextRefreshedEvent) - { - init(); - } - } - /** * Initialize the configuration using the configuration service */ @@ -1791,9 +1778,7 @@ public class ServerConfiguration implements ApplicationListener // Load the Alfresco authenticator dynamically - auth = loadAuthenticatorClass("org.alfresco.filesys.server.auth.ntlm.AlfrescoAuthenticator"); - if ( auth == null) - auth = loadAuthenticatorClass("org.alfresco.filesys.server.auth.AlfrescoAuthenticator"); + auth = loadAuthenticatorClass("org.alfresco.filesys.server.auth.AlfrescoAuthenticator"); if ( auth == null) throw new AlfrescoRuntimeException("Failed to load Alfresco authenticator"); @@ -3359,4 +3344,17 @@ public class ServerConfiguration implements ApplicationListener return srvAuth; } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + init(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NO-OP + } + } \ No newline at end of file diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java index 049e5d4e7d..7ce6b8705f 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentDiskDriver.java @@ -18,6 +18,7 @@ package org.alfresco.filesys.smb.server.repo; import java.io.FileNotFoundException; import java.io.IOException; +import java.net.InetAddress; import java.util.List; import javax.transaction.UserTransaction; @@ -58,7 +59,6 @@ import org.alfresco.filesys.util.WildCard; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.NodeRef; @@ -66,6 +66,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.transaction.TransactionService; @@ -91,6 +92,10 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface private static final String KEY_ROOT_PATH = "rootPath"; private static final String KEY_RELATIVE_PATH = "relativePath"; + // Token name to substitute current servers DNS name or TCP/IP address into the webapp URL + + private static final String TokenLocalName = "${localname}"; + // Services and helpers private CifsHelper cifsHelper; @@ -102,6 +107,7 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface private PermissionService permissionService; private AuthenticationComponent authComponent; + private AuthenticationService authService; // Service registry for desktop actions @@ -127,6 +133,16 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface return this.cifsHelper; } + /** + * Return the authentication service + * + * @return AuthenticationService + */ + public final AuthenticationService getAuthenticationService() + { + return authService; + } + /** * Return the transaction service * @@ -185,7 +201,7 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface { return this.serviceRegistry; } - + /** * @param contentService the content service */ @@ -255,6 +271,16 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface { this.authComponent = authComponent; } + + /** + * Set the authentication service + * + * @param authService AuthenticationService + */ + public void setAuthenticationService(AuthenticationService authService) + { + this.authService = authService; + } /** * Parse and validate the parameter string and create a device context object for this instance @@ -418,7 +444,39 @@ public class ContentDiskDriver implements DiskInterface, IOCtlInterface if ( pseudoName.getValue().endsWith(".url") == false) throw new DeviceContextException("URL link file must end with .url, " + pseudoName.getValue()); - // Set the URL link file name and web path + // Check if the URL path name contains the local name token + + int pos = path.indexOf(TokenLocalName); + if (pos != -1) + { + + // Get the local server name + + String srvName = "localhost"; + + try + { + srvName = InetAddress.getLocalHost().getHostName(); + } + catch ( Exception ex) + { + } + + // Rebuild the host name substituting the token with the local server name + + StringBuilder hostStr = new StringBuilder(); + + hostStr.append( path.substring(0, pos)); + hostStr.append(srvName); + + pos += TokenLocalName.length(); + if (pos < path.length()) + hostStr.append( path.substring(pos)); + + path = hostStr.toString(); + } + + // Set the URL link file name and web path context.setURLFileName( pseudoName.getValue()); context.setURLPrefix( path); diff --git a/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java index d9cb60e028..f83cad068a 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/ContentIOControlHandler.java @@ -19,6 +19,7 @@ package org.alfresco.filesys.smb.server.repo; import java.io.FileNotFoundException; import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.auth.ClientInfo; import org.alfresco.filesys.server.filesys.IOControlNotImplementedException; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.server.filesys.TreeConnection; @@ -30,10 +31,12 @@ import org.alfresco.filesys.smb.server.repo.ContentDiskDriver; import org.alfresco.filesys.smb.server.repo.IOControlHandler; import org.alfresco.filesys.util.DataBuffer; import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.transaction.TransactionService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -85,6 +88,16 @@ public class ContentIOControlHandler implements IOControlHandler return contentDriver.getCifsHelper(); } + /** + * Return the authentication service + * + * @return AuthenticationService + */ + public final AuthenticationService getAuthenticationService() + { + return contentDriver.getAuthenticationService(); + } + /** * Return the transaction service * @@ -511,6 +524,11 @@ public class ContentIOControlHandler implements IOControlHandler // Start a transaction sess.beginTransaction( getTransactionService(), true); + + // Get an authentication ticket for the client, or validate the existing ticket. The ticket can be used when + // generating URLs for the client-side application so that the user does not have to re-authenticate + + getTicketForClient( sess); // Get the list of targets for the action @@ -624,4 +642,65 @@ public class ContentIOControlHandler implements IOControlHandler return respBuf; } + + /** + * Get, or validate, an authentication ticket for the client + * + * @param sess SrvSession + */ + private final void getTicketForClient(SrvSession sess) + { + // Get the client information and check if there is a ticket allocated + + ClientInfo cInfo = sess.getClientInformation(); + if ( cInfo == null) + return; + + boolean needTicket = true; + + if ( cInfo.hasAuthenticationTicket()) + { + // Validate the existing ticket, it may have expired + + try + { + // Validate the existing ticket + + getAuthenticationService().validate( cInfo.getAuthenticationTicket()); + needTicket = false; + } + catch ( AuthenticationException ex) + { + // Invalidate the current ticket + + try + { + getAuthenticationService().invalidateTicket( cInfo.getAuthenticationTicket()); + cInfo.setAuthenticationTicket( null); + } + catch (Exception ex2) + { + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Error during invalidate ticket", ex2); + } + + // DEBUG + + if ( logger.isDebugEnabled()) + logger.debug("Auth ticket expired or invalid"); + } + } + + // Check if a ticket needs to be allocated + + if ( needTicket == true) + { + // Allocate a new ticket and store in the client information for this session + + String ticket = getAuthenticationService().getCurrentTicket(); + cInfo.setAuthenticationTicket( ticket); + } + } } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java index 1bcc759564..e5ca0a28e7 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopAction.java @@ -19,10 +19,12 @@ package org.alfresco.filesys.smb.server.repo; import java.io.File; import java.io.UnsupportedEncodingException; +import java.net.InetAddress; import java.net.URL; import java.net.URLDecoder; import org.alfresco.config.ConfigElement; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.filesys.server.filesys.DiskSharedDevice; import org.alfresco.filesys.smb.server.repo.pseudo.LocalPseudoFile; import org.alfresco.filesys.smb.server.repo.pseudo.PseudoFile; @@ -88,7 +90,11 @@ public abstract class DesktopAction { public static final int StsLaunchURL = 7; public static final int StsCommandLine = 8; - // Action name + // Token name to substitute current servers DNS name or TCP/IP address into the webapp URL + + private static final String TokenLocalName = "${localname}"; + + // Action name private String m_name; @@ -109,6 +115,10 @@ public abstract class DesktopAction { private ContentDiskDriver m_contentDriver; private ContentContext m_contentContext; + // Webapp URL + + private String m_webappURL; + // Debug enable flag private boolean m_debug; @@ -254,6 +264,26 @@ public abstract class DesktopAction { return m_debug; } + /** + * Check if the webapp URL is set + * + * @return boolean + */ + public final boolean hasWebappURL() + { + return m_webappURL != null ? true : false; + } + + /** + * Return the webapp URL + * + * @return String + */ + public final String getWebappURL() + { + return m_webappURL; + } + /** * Initialize the desktop action * @@ -348,7 +378,50 @@ public abstract class DesktopAction { if ( findConfigElement("noConfirm", global, config) != null && hasPreProcessAction(PreConfirmAction)) setPreProcessActions(getPreProcessActions() - PreConfirmAction); + + // Check if the webapp URL has been specified + + ConfigElement webURL = findConfigElement("webpath", global, config); + if ( webURL != null) + { + // Check if the path name contains the local name token + + String webPath = webURL.getValue(); + if ( webPath.endsWith("/") == false) + webPath = webPath + "/"; + int pos = webPath.indexOf(TokenLocalName); + if (pos != -1) + { + + // Get the local server name + + String srvName = "localhost"; + + try + { + srvName = InetAddress.getLocalHost().getHostName(); + } + catch ( Exception ex) + { + } + + // Rebuild the host name substituting the token with the local server name + + StringBuilder hostStr = new StringBuilder(); + + hostStr.append(webPath.substring(0, pos)); + hostStr.append(srvName); + + pos += TokenLocalName.length(); + if (pos < webPath.length()) + hostStr.append(webPath.substring(pos)); + + webPath = hostStr.toString(); + setWebappURL( webPath); + } + } + // Check if debug output is enabled for the action ConfigElement debug = findConfigElement("debug", global, config); @@ -511,6 +584,16 @@ public abstract class DesktopAction { m_debug = ena; } + /** + * Set the webapp URL + * + * @param urlStr String + */ + public final void setWebappURL(String urlStr) + { + m_webappURL = urlStr; + } + /** * Equality check * diff --git a/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java b/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java index 1a2e294135..6a45926dc2 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/DesktopParams.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; import org.alfresco.filesys.server.SrvSession; +import org.alfresco.filesys.server.auth.ClientInfo; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.service.cmr.repository.NodeRef; @@ -88,6 +89,19 @@ public class DesktopParams { { return m_session; } + + /** + * Return the authentication ticket for the user/session + * + * @return String + */ + public final String getTicket() + { + ClientInfo cInfo = m_session.getClientInformation(); + if ( cInfo != null) + return cInfo.getAuthenticationTicket(); + return null; + } /** * Return the working directory node diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/JavaScriptDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/JavaScriptDesktopAction.java index 303ff36272..1a361e62f2 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/desk/JavaScriptDesktopAction.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/JavaScriptDesktopAction.java @@ -284,6 +284,11 @@ public class JavaScriptDesktopAction extends DesktopAction { model.put("deskParams", params); model.put("out", System.out); + // Add the webapp URL, if valid + + if ( hasWebappURL()) + model.put("webURL", getWebappURL()); + // Start a transaction params.getSession().beginTransaction(getTransactionService(), false); diff --git a/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java b/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java index ca63252963..2a91523ae7 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/desk/URLDesktopAction.java @@ -16,6 +16,9 @@ */ package org.alfresco.filesys.smb.server.repo.desk; +import java.net.InetAddress; +import java.net.UnknownHostException; + import org.alfresco.filesys.smb.server.repo.DesktopAction; import org.alfresco.filesys.smb.server.repo.DesktopParams; import org.alfresco.filesys.smb.server.repo.DesktopResponse; @@ -45,8 +48,29 @@ public class URLDesktopAction extends DesktopAction { @Override public DesktopResponse runAction(DesktopParams params) { - // Return a URL in the status message + // Get the local IP address - return new DesktopResponse(StsLaunchURL, "http://www.alfresco.com"); + String ipAddr = null; + + try + { + ipAddr = InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException ex) + { + } + + // Return a URL in the status message to browse to the folder node + + StringBuilder urlStr = new StringBuilder(); + + urlStr.append( "http://"); + urlStr.append(ipAddr); + urlStr.append(":8080/alfresco/navigate/browse/workspace/SpacesStore/"); + urlStr.append( params.getFolderNode().getId()); + urlStr.append("?ticket="); + urlStr.append(params.getTicket()); + + return new DesktopResponse(StsLaunchURL, urlStr.toString()); } } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/MemoryNetworkFile.java b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/MemoryNetworkFile.java index 76d7cafe63..8eb403b31f 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/MemoryNetworkFile.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/MemoryNetworkFile.java @@ -19,7 +19,6 @@ package org.alfresco.filesys.smb.server.repo.pseudo; import java.io.IOException; -import org.alfresco.filesys.server.filesys.AccessDeniedException; import org.alfresco.filesys.server.filesys.FileInfo; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.smb.SeekType; @@ -222,9 +221,7 @@ public class MemoryNetworkFile extends NetworkFile */ public void truncateFile(long siz) throws IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot truncate pseudo file"); + // Allow the truncate, do not alter the pseduo file data } /** @@ -236,9 +233,7 @@ public class MemoryNetworkFile extends NetworkFile */ public void writeFile(byte[] buf, int len, int pos) throws java.io.IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot write to pseudo file"); + // Allow the write, just do not do anything } /** @@ -252,8 +247,6 @@ public class MemoryNetworkFile extends NetworkFile */ public void writeFile(byte[] buf, int len, int pos, long offset) throws java.io.IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot write to pseudo file"); + // Allow the write, just do not do anything } } diff --git a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/PseudoNetworkFile.java b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/PseudoNetworkFile.java index 0ae631897c..4bcd2cefe0 100644 --- a/source/java/org/alfresco/filesys/smb/server/repo/pseudo/PseudoNetworkFile.java +++ b/source/java/org/alfresco/filesys/smb/server/repo/pseudo/PseudoNetworkFile.java @@ -21,7 +21,6 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import org.alfresco.filesys.server.filesys.AccessDeniedException; import org.alfresco.filesys.server.filesys.NetworkFile; import org.alfresco.filesys.smb.SeekType; @@ -272,9 +271,7 @@ public class PseudoNetworkFile extends NetworkFile */ public void truncateFile(long siz) throws IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot truncate pseudo file"); + // Allow the truncate, just do not do anything } /** @@ -286,9 +283,7 @@ public class PseudoNetworkFile extends NetworkFile */ public void writeFile(byte[] buf, int len, int pos) throws java.io.IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot write to pseudo file"); + // Allow the write, just do not do anything } /** @@ -302,8 +297,6 @@ public class PseudoNetworkFile extends NetworkFile */ public void writeFile(byte[] buf, int len, int pos, long offset) throws java.io.IOException { - // Do not allow the file to be written to - - throw new AccessDeniedException("Cannot write to pseudo file"); + // Allow the write, just do not do anything } } diff --git a/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java b/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java index a9d3b3e9bf..8f973ed183 100644 --- a/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java +++ b/source/java/org/alfresco/jcr/exporter/JCRSystemXMLExporter.java @@ -178,7 +178,7 @@ public class JCRSystemXMLExporter implements Exporter Value[] mixinValues = mixinTypes.getValues(); for (int i = 0; i < mixinValues.length; i++) { - value(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME, mixinValues[i], i); + value(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME, mixinValues[i].getString(), i); } endProperty(nodeRef, JCRMixinTypesProperty.PROPERTY_NAME); diff --git a/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java b/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java index 1c1d92ebc4..229a844a9e 100644 --- a/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/CopyActionExecuter.java @@ -162,7 +162,7 @@ public class CopyActionExecuter extends ActionExecuterAbstractBase else { // Create a new copy of the node - this.copyService.copy( + this.copyService.copyAndRename( actionedUponNodeRef, destinationParent, destinationAssocTypeQName, diff --git a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java index 4375b6e101..b921a3e0f7 100644 --- a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; @@ -329,6 +330,9 @@ public abstract class AbstractPatch implements Patch { public String doWork() throws Exception { + // downgrade integrity checking + IntegrityChecker.setWarnInTransaction(); + String report = applyInternal(); // done return report; @@ -389,7 +393,8 @@ public abstract class AbstractPatch implements Patch /** * This method does the work. All transactions and thread-safety will be taken care of by this class. - * Any exception will result in the transaction being rolled back. + * Any exception will result in the transaction being rolled back. Integrity checks are downgraded + * for the duration of the transaction. * * @return Returns the report (only success messages). * @see #apply() diff --git a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java index d0f2655d69..dbb5e0b592 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchExecuter.java @@ -21,11 +21,10 @@ import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.i18n.I18NUtil; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; /** * This component is responsible for ensuring that patches are applied @@ -33,7 +32,7 @@ import org.springframework.context.event.ContextRefreshedEvent; * * @author Derek Hulley */ -public class PatchExecuter implements ApplicationListener +public class PatchExecuter extends AbstractLifecycleBean { private static final String MSG_CHECKING = "patch.executer.checking"; private static final String MSG_NO_PATCHES_REQUIRED = "patch.executer.no_patches_required"; @@ -101,16 +100,16 @@ public class PatchExecuter implements ApplicationListener } } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) + @Override + protected void onBootstrap(ApplicationEvent event) { - if (event instanceof ContextRefreshedEvent) - { - applyOutstandingPatches(); - } + applyOutstandingPatches(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java new file mode 100644 index 0000000000..7f0cc516de --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java @@ -0,0 +1,169 @@ +/* + * 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.admin.patch.impl; + +import java.util.List; + +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.domain.DbAccessControlEntry; +import org.alfresco.repo.domain.DbPermission; +import org.alfresco.repo.domain.hibernate.DbPermissionImpl; +import org.alfresco.service.namespace.QName; +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Provides common functionality to change a permission type and/or name. + * + * @author Derek Hulley + */ +public abstract class AbstractPermissionChangePatch extends AbstractPatch +{ + private HibernateHelper helper; + + public AbstractPermissionChangePatch() + { + helper = new HibernateHelper(); + } + + public void setSessionFactory(SessionFactory sessionFactory) + { + this.helper.setSessionFactory(sessionFactory); + } + + /** + * Helper method to rename (move) a permission. This involves checking for the existence of the + * new permission and then moving all the entries to point to the new permission. + * + * @param oldTypeQName the old permission type + * @param oldName the old permission name + * @param newTypeQName the new permission type + * @param newName the new permission name + * @return Returns the number of permission entries modified + */ + protected int renamePermission(QName oldTypeQName, String oldName, QName newTypeQName, String newName) + { + return helper.createAndUpdatePermission(oldTypeQName, oldName, newTypeQName, newName); + } + + /** Helper to get a permission entity */ + private static class GetPermissionCallback implements HibernateCallback + { + private QName typeQName; + private String name; + public GetPermissionCallback(QName typeQName, String name) + { + this.typeQName = typeQName; + this.name = name; + } + public Object doInHibernate(Session session) + { + // flush any outstanding entities + session.flush(); + + Query query = session.getNamedQuery(HibernateHelper.QUERY_GET_PERMISSION); + query.setParameter("permissionTypeQName", typeQName) + .setString("permissionName", name); + return query.uniqueResult(); + } + } + + private static class HibernateHelper extends HibernateDaoSupport + { + private static final String QUERY_GET_PERMISSION = "permission.GetPermission"; + private static final String QUERY_GET_ENTRIES_TO_CHANGE = "permission.patch.GetAccessControlEntriesToChangePermissionOn"; + + public int createAndUpdatePermission( + final QName oldTypeQName, + final String oldName, + final QName newTypeQName, + final String newName) + { + if (oldTypeQName.equals(newTypeQName) && oldName.equals(newName)) + { + throw new IllegalArgumentException("Cannot move permission to itself: " + oldTypeQName + "-" + oldName); + } + + HibernateCallback getNewPermissionCallback = new GetPermissionCallback(newTypeQName, newName); + DbPermission permission = (DbPermission) getHibernateTemplate().execute(getNewPermissionCallback); + if (permission == null) + { + // create the permission + permission = new DbPermissionImpl(); + permission.setTypeQname(newTypeQName); + permission.setName(newName); + // save + getHibernateTemplate().save(permission); + } + final DbPermission newPermission = permission; + // now update all entries that refer to the old permission + HibernateCallback updateEntriesCallback = new HibernateCallback() + { + private static final int MAX_RESULTS = 1000; + @SuppressWarnings("unchecked") + public Object doInHibernate(Session session) + { + int count = 0; + while (true) + { + // flush any outstanding entities + session.flush(); + + Query query = session.getNamedQuery(HibernateHelper.QUERY_GET_ENTRIES_TO_CHANGE); + query.setParameter("oldTypeQName", oldTypeQName) + .setParameter("oldName", oldName) + .setMaxResults(MAX_RESULTS); + List entries = (List) query.list(); + // if there are no results, then we're done + if (entries.size() == 0) + { + break; + } + for (DbAccessControlEntry entry : entries) + { + entry.setPermission(newPermission); + count++; + session.evict(entry); + } + // flush and evict all the entries + session.flush(); + for (DbAccessControlEntry entry : entries) + { + session.evict(entry); + } + // next set of results + } + // done + return count; + } + }; + int updateCount = (Integer) getHibernateTemplate().execute(updateEntriesCallback); + // now delete the old permission + HibernateCallback getOldPermissionCallback = new GetPermissionCallback(oldTypeQName, oldName); + DbPermission oldPermission = (DbPermission) getHibernateTemplate().execute(getOldPermissionCallback); + if (oldPermission != null) + { + getHibernateTemplate().delete(oldPermission); + } + // done + return updateCount; + } + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ActionRuleDecouplingPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ActionRuleDecouplingPatch.java index 8f4aab96c8..71d86005a3 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ActionRuleDecouplingPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ActionRuleDecouplingPatch.java @@ -54,6 +54,10 @@ public class ActionRuleDecouplingPatch extends AbstractPatch for (NodeRef origRuleNodeRef : resultSet.getNodeRefs()) { // Check that this rule need updated + if (!this.nodeService.exists(origRuleNodeRef)) + { + continue; + } Map origProperties = this.nodeService.getProperties(origRuleNodeRef); if (origProperties.containsKey(RuleModel.PROP_EXECUTE_ASYNC) == false) { @@ -79,21 +83,21 @@ public class ActionRuleDecouplingPatch extends AbstractPatch Map newProperties = this.nodeService.getProperties(newRuleNodeRef); // Set the rule type, execute async and applyToChildren properties on the rule - String ruleType = (String)origProperties.get(RuleModel.PROP_RULE_TYPE); + Serializable ruleType = origProperties.get(RuleModel.PROP_RULE_TYPE); origProperties.remove(RuleModel.PROP_RULE_TYPE); newProperties.put(RuleModel.PROP_RULE_TYPE, ruleType); - Boolean executeAsync = (Boolean)origProperties.get(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY); + Serializable executeAsync = origProperties.get(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY); origProperties.remove(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY); newProperties.put(RuleModel.PROP_EXECUTE_ASYNC, executeAsync); - Boolean applyToChildren = (Boolean)origProperties.get(RuleModel.PROP_APPLY_TO_CHILDREN); + Serializable applyToChildren = origProperties.get(RuleModel.PROP_APPLY_TO_CHILDREN); origProperties.remove(RuleModel.PROP_APPLY_TO_CHILDREN); newProperties.put(RuleModel.PROP_APPLY_TO_CHILDREN, applyToChildren); origProperties.remove(QName.createQName(RuleModel.RULE_MODEL_URI, "owningNodeRef")); // Move the action and description values from the composite action onto the rule - String title = (String)origProperties.get(ActionModel.PROP_ACTION_TITLE); + Serializable title = origProperties.get(ActionModel.PROP_ACTION_TITLE); origProperties.remove(ActionModel.PROP_ACTION_TITLE); - String description = (String)origProperties.get(ActionModel.PROP_ACTION_DESCRIPTION); + Serializable description = origProperties.get(ActionModel.PROP_ACTION_DESCRIPTION); origProperties.remove(ActionModel.PROP_ACTION_DESCRIPTION); newProperties.put(ContentModel.PROP_TITLE, title); newProperties.put(ContentModel.PROP_DESCRIPTION, description); diff --git a/source/java/org/alfresco/repo/admin/patch/impl/ContentPermissionPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/ContentPermissionPatch.java index 6f428ccd92..031589d26c 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/ContentPermissionPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/ContentPermissionPatch.java @@ -16,33 +16,40 @@ */ package org.alfresco.repo.admin.patch.impl; -import org.alfresco.repo.admin.patch.AbstractPatch; -import org.alfresco.service.cmr.admin.PatchException; -import org.hibernate.SessionFactory; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; /** * Roles defined in permissionsDefinition.xml moved from cm:content to sys:base. - * This effects the data stored in the node_perm_entry table. - *

- * WILL NOT EXECUTE ANYMORE + * This effects the data stored in the permission table. * * @author Derek Hulley */ -public class ContentPermissionPatch extends AbstractPatch +public class ContentPermissionPatch extends AbstractPermissionChangePatch { - private static final String MSG_UPGRADE = "patch.contentPermission.upgrade"; - - public ContentPermissionPatch() - { - } - - public void setSessionFactory(SessionFactory sessionFactory) - { - } + private static final String MSG_SUCCESS = "patch.contentPermission.result"; + private static final QName TYPE_QNAME_OLD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "content"); + private static final QName TYPE_QNAME_NEW = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "base"); + private static final String[] NAMES = new String[] {"Execute", "ReadContent", "WriteContent", "ExecuteContent"}; + @Override protected String applyInternal() throws Exception { - throw new PatchException(MSG_UPGRADE); + int updateCount = 0; + for (String permissionName : NAMES) + { + updateCount += super.renamePermission( + ContentPermissionPatch.TYPE_QNAME_OLD, + permissionName, + ContentPermissionPatch.TYPE_QNAME_NEW, + permissionName); + } + + // build the result message + String msg = I18NUtil.getMessage(MSG_SUCCESS, updateCount); + // done + return msg; } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/PermissionDataPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/PermissionDataPatch.java index 8c815a7d1f..5d18fb4549 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/PermissionDataPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/PermissionDataPatch.java @@ -16,35 +16,42 @@ */ package org.alfresco.repo.admin.patch.impl; -import org.alfresco.repo.admin.patch.AbstractPatch; -import org.alfresco.service.cmr.admin.PatchException; -import org.hibernate.SessionFactory; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; /** * The roles defined in permissionsDefinition.xml moved from cm:folder to cm:cmobject. - * This effects the data stored in the node_perm_entry table. + * This effects the data stored in the permission table. *

* JIRA: {@link http://www.alfresco.org/jira/browse/AR-344 AR-344} - *

- * WILL NOT EXECUTE ANYMORE * * @author Derek Hulley */ -public class PermissionDataPatch extends AbstractPatch +public class PermissionDataPatch extends AbstractPermissionChangePatch { - private static final String MSG_UPGRADE = "patch.updatePermissionData.upgrade"; - - public PermissionDataPatch() - { - } - - public void setSessionFactory(SessionFactory sessionFactory) - { - } + private static final String MSG_SUCCESS = "patch.updatePermissionData.result"; + private static final QName TYPE_QNAME_OLD = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder"); + private static final QName TYPE_QNAME_NEW = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "cmobject"); + private static final String[] NAMES = new String[] {"Coordinator", "Contributor", "Editor", "Guest"}; + @Override protected String applyInternal() throws Exception { - throw new PatchException(MSG_UPGRADE); + int updateCount = 0; + for (String permissionName : NAMES) + { + updateCount += super.renamePermission( + PermissionDataPatch.TYPE_QNAME_OLD, + permissionName, + PermissionDataPatch.TYPE_QNAME_NEW, + permissionName); + } + + // build the result message + String msg = I18NUtil.getMessage(MSG_SUCCESS, updateCount); + // done + return msg; } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/UpdateGuestPermissionPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/UpdateGuestPermissionPatch.java index 58fd936812..374a393835 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/UpdateGuestPermissionPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/UpdateGuestPermissionPatch.java @@ -16,32 +16,27 @@ */ package org.alfresco.repo.admin.patch.impl; -import org.alfresco.repo.admin.patch.AbstractPatch; -import org.alfresco.service.cmr.admin.PatchException; -import org.hibernate.SessionFactory; +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; /** * The permission 'Guest' has been renamed to 'Consumer'. - *

- * WILL NOT EXECUTE ANYMORE * * @author David Caruana + * @author Derek Hulley */ -public class UpdateGuestPermissionPatch extends AbstractPatch +public class UpdateGuestPermissionPatch extends AbstractPermissionChangePatch { - private static final String MSG_UPGRADE = "patch.updateGuestPermission.upgrade"; - - public UpdateGuestPermissionPatch() - { - } - - public void setSessionFactory(SessionFactory sessionFactory) - { - } + private static final String MSG_SUCCESS = "patch.updateGuestPermission.result"; @Override protected String applyInternal() throws Exception { - throw new PatchException(MSG_UPGRADE); + int updateCount = super.renamePermission(ContentModel.TYPE_CMOBJECT, "Guest", ContentModel.TYPE_CMOBJECT, "Consumer"); + + // build the result message + String msg = I18NUtil.getMessage(MSG_SUCCESS, updateCount); + // done + return msg; } } diff --git a/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java b/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java new file mode 100644 index 0000000000..4148f74de8 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/util/ImportFileUpdater.java @@ -0,0 +1,573 @@ +/** + * + */ +package org.alfresco.repo.admin.patch.util; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ActionModel; +import org.alfresco.repo.rule.RuleModel; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.util.GUID; +import org.dom4j.io.OutputFormat; +import org.dom4j.io.XMLWriter; +import org.xml.sax.helpers.AttributesImpl; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +/** + * Updates a XML import file to be compatable with the current version of the repository. + * + * @author royw + */ +public class ImportFileUpdater +{ + /** Indent size **/ + private static int INDENT_SIZE = 2; + + /** The destination export version number **/ + private static String EXPORT_VERSION = "1.4.0"; + + /** Element names **/ + private static String NAME_EXPORTER_VERSION = "exporterVersion"; + private static String NAME_RULE = "rule"; + + /** The current import version number **/ + private String version; + + /** + * Updates the passed import file into the equivalent 1.4 format. + * + * @param source the source import file + * @param destination the destination import file + */ + public void updateImportFile(String source, String destination) + { + XmlPullParser reader = getReader(source); + XMLWriter writer = getWriter(destination); + + try + { + // Start the documentation + writer.startDocument(); + + // Start reading the document + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) + { + if (eventType == XmlPullParser.START_TAG) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, new OutputChildren()); + } + eventType = reader.next(); + } + + // End and close the document + writer.endDocument(); + writer.close(); + } + catch (Exception exception) + { + throw new AlfrescoRuntimeException("Unable to update import file.", exception); + } + + } + + /** + * Get the reader for the source import file + * + * @param source the source import file + * @return the XML pull parser used to read the file + */ + private XmlPullParser getReader(String source) + { + try + { + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(System.getProperty(XmlPullParserFactory.PROPERTY_NAME), null); + factory.setNamespaceAware(true); + + XmlPullParser xpp = factory.newPullParser(); + xpp.setInput(new FileReader(source)); + + return xpp; + } + catch (XmlPullParserException exception) + { + throw new AlfrescoRuntimeException("Unable to update import file.", exception); + } + catch (FileNotFoundException fileNotFound) + { + throw new AlfrescoRuntimeException("The source file could not be loaded.", fileNotFound); + } + } + + /** + * Get the writer for the import file + * + * @param destination the destination XML import file + * @return the XML writer + */ + private XMLWriter getWriter(String destination) + { + try + { + // Define output format + OutputFormat format = OutputFormat.createPrettyPrint(); + format.setNewLineAfterDeclaration(false); + format.setIndentSize(INDENT_SIZE); + format.setEncoding("UTF-8"); + + return new XMLWriter(new FileOutputStream(destination), format); + } + catch (Exception exception) + { + throw new AlfrescoRuntimeException("Unable to create XML writer.", exception); + } + } + + private void outputCurrentElement(XmlPullParser reader, XMLWriter writer, Work work) + throws Exception + { + outputCurrentElement(reader, writer, work, true); + } + + private void outputCurrentElement(XmlPullParser reader, XMLWriter writer, Work work, boolean checkForCallbacks) + throws Exception + { + if (checkForCallbacks == false || checkForCallbacks(reader, writer) == false) + { + // Get the name details of the element + String name = reader.getName(); + String namespace = reader.getNamespace(); + String prefix = reader.getPrefix(); + + // Sort out namespaces + Map nss = new HashMap(); + int nsStart = reader.getNamespaceCount(reader.getDepth()-1); + int nsEnd = reader.getNamespaceCount(reader.getDepth()); + for (int i = nsStart; i < nsEnd; i++) + { + String nsPrefix = reader.getNamespacePrefix(i); + String ns = reader.getNamespaceUri(i); + nss.put(nsPrefix, ns); + } + + // Sort out attributes + AttributesImpl attributes = new AttributesImpl(); + for (int i = 0; i < reader.getAttributeCount(); i++) + { + String attributeName = reader.getAttributeName(i); + String attributeNamespace = reader.getAttributeNamespace(i); + String attributePrefix = reader.getAttributePrefix(i); + String attributeType = reader.getAttributeType(i); + String attributeValue = reader.getAttributeValue(i); + + attributes.addAttribute(attributeNamespace, attributeName, attributePrefix+":"+attributeName, attributeType, attributeValue); + } + + // Start the namespace prefixes + for (Map.Entry entry : nss.entrySet()) + { + writer.startPrefixMapping(entry.getKey(), entry.getValue()); + } + + // Write the start of the element + writer.startElement(namespace, name, prefix+":"+name, attributes); + + // Do the work + work.doWork(reader, writer); + + // Write the end of the element + writer.endElement(namespace, name, prefix+":"+name); + + // End the namespace prefixes + for (String nsPrefix : nss.keySet()) + { + writer.endPrefixMapping(nsPrefix); + } + } + } + + private boolean checkForCallbacks(XmlPullParser reader, XMLWriter writer) + throws Exception + { + boolean result = false; + if (reader.getName().equals(NAME_EXPORTER_VERSION) == true) + { + new ImportVersionLabelCallback().doCallback(reader, writer); + result = true; + } + else if (reader.getName().equals(NAME_RULE) == true) + { + if (this.version.startsWith("1.3") == true) + { + new RuleCallback().doCallback(reader, writer); + result = true; + } + } + return result; + } + + private interface Work + { + void doWork(XmlPullParser reader, XMLWriter writer) + throws Exception; + } + + private class OutputChildren implements Work + { + public void doWork(XmlPullParser reader, XMLWriter writer) + throws Exception + { + // Deal with the contents of the tag + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + if (eventType == XmlPullParser.START_TAG) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, new OutputChildren()); + } + else if (eventType == XmlPullParser.TEXT) + { + // Write the text to the output file + writer.write(reader.getText()); + } + } + } + } + + @SuppressWarnings("unused") + private class IgnoreChildren implements Work + { + public void doWork(XmlPullParser reader, XMLWriter writer) + throws Exception + { + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + if (eventType == XmlPullParser.START_TAG) + { + doWork(reader, writer); + } + } + } + } + + private interface ImportUpdaterCallback + { + void doCallback(XmlPullParser reader, XMLWriter writer) + throws Exception; + } + + private class ImportVersionLabelCallback implements ImportUpdaterCallback + { + public void doCallback(XmlPullParser reader, XMLWriter writer) + throws Exception + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, + new Work() + { + public void doWork(XmlPullParser reader, XMLWriter writer) throws Exception + { + reader.next(); + ImportFileUpdater.this.version = reader.getText(); + writer.write(EXPORT_VERSION); + reader.next(); + } + }, false); + } + } + + private class RuleCallback implements ImportUpdaterCallback + { + public void doCallback(XmlPullParser reader, XMLWriter writer) + throws Exception + { + // Get the name details of the element + String name = reader.getName(); + String namespace = reader.getNamespace(); + String prefix = reader.getPrefix(); + + // Rename the child assoc appropriately + AttributesImpl attributes = new AttributesImpl(); + String attributeName = reader.getAttributeName(0); + String attributeNamespace = reader.getAttributeNamespace(0); + String attributePrefix = reader.getAttributePrefix(0); + String attributeType = reader.getAttributeType(0); + String attributeValue = reader.getAttributeValue(0) + GUID.generate(); + attributes.addAttribute(attributeNamespace, attributeName, attributePrefix+":"+attributeName, attributeType, attributeValue); + + // Output the rules element + writer.startElement(namespace, name, prefix+":"+name, attributes); + + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + if (eventType == XmlPullParser.START_TAG) + { + String childName = reader.getName(); + if (childName.equals("aspects") == true) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, + new Work() + { + public void doWork(XmlPullParser reader, XMLWriter writer) throws Exception + { + // Add titled aspect + writer.startElement( + ContentModel.ASPECT_TITLED.getNamespaceURI(), + ContentModel.ASPECT_TITLED.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.ASPECT_TITLED.getLocalName(), + new AttributesImpl()); + writer.endElement( + ContentModel.ASPECT_TITLED.getNamespaceURI(), + ContentModel.ASPECT_TITLED.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.ASPECT_TITLED.getLocalName()); + + // Read the rest of the elements and output + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + if (eventType == XmlPullParser.START_TAG) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, new OutputChildren()); + } + } + } + + }, false); + } + else if (childName.equals("properties") == true) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, + new Work() + { + public void doWork(XmlPullParser reader, XMLWriter writer) throws Exception + { + int eventType = reader.getEventType(); + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + if (eventType == XmlPullParser.START_TAG) + { + String propName = reader.getName(); + if (propName.equals("actionDescription") == true) + { + writer.startElement( + ContentModel.PROP_DESCRIPTION.getNamespaceURI(), + ContentModel.PROP_DESCRIPTION.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.PROP_DESCRIPTION.getLocalName(), + new AttributesImpl()); + + // Output the value within + new OutputChildren().doWork(reader, writer); + + writer.endElement( + ContentModel.PROP_DESCRIPTION.getNamespaceURI(), + ContentModel.PROP_DESCRIPTION.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.PROP_DESCRIPTION.getLocalName()); + eventType = reader.next(); + + } + else if (propName.equals("actionTitle") == true) + { + writer.startElement( + ContentModel.PROP_TITLE.getNamespaceURI(), + ContentModel.PROP_TITLE.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.PROP_TITLE.getLocalName(), + new AttributesImpl()); + + // Output the value within + new OutputChildren().doWork(reader, writer); + + writer.endElement( + ContentModel.PROP_TITLE.getNamespaceURI(), + ContentModel.PROP_TITLE.getLocalName(), + NamespaceService.CONTENT_MODEL_PREFIX + ":" + ContentModel.PROP_TITLE.getLocalName()); + eventType = reader.next(); + } + else if (propName.equals("executeAsynchronously") == true) + { + writer.startElement( + RuleModel.PROP_EXECUTE_ASYNC.getNamespaceURI(), + RuleModel.PROP_EXECUTE_ASYNC.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.PROP_EXECUTE_ASYNC.getLocalName(), + new AttributesImpl()); + + // Output the value within + new OutputChildren().doWork(reader, writer); + + writer.endElement( + RuleModel.PROP_EXECUTE_ASYNC.getNamespaceURI(), + RuleModel.PROP_EXECUTE_ASYNC.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.PROP_EXECUTE_ASYNC.getLocalName()); + eventType = reader.next(); + } + else if (propName.equals("ruleType") == true) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, + new Work() + { + public void doWork(XmlPullParser reader, XMLWriter writer) throws Exception + { + // Output the elements that contain a multi values property + writer.startElement(NamespaceService.REPOSITORY_VIEW_1_0_URI, "values", "view:values", new AttributesImpl()); + writer.startElement(NamespaceService.REPOSITORY_VIEW_1_0_URI, "value", "view:value", new AttributesImpl()); + + // Output the value within + new OutputChildren().doWork(reader, writer); + + // End the multi values elements + writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "value", "view:value"); + writer.endElement(NamespaceService.REPOSITORY_VIEW_PREFIX, "values", "view:values"); + } + }, false); + } + else if (propName.equals("definitionName") == true) + { + // Skip past next end + while (eventType != XmlPullParser.END_TAG) + { + eventType = reader.next(); + } + eventType = reader.next(); + } + else + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, new OutputChildren()); + } + } + } + + // Output value for the disabled property + writer.startElement( + RuleModel.PROP_DISABLED.getNamespaceURI(), + RuleModel.PROP_DISABLED.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.PROP_DISABLED.getLocalName(), + new AttributesImpl()); + writer.write("false"); + writer.endElement( + RuleModel.PROP_DISABLED.getNamespaceURI(), + RuleModel.PROP_DISABLED.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.PROP_DISABLED.getLocalName()); + } + }, false); + } + else if (childName.equals("associations") == true) + { + ImportFileUpdater.this.outputCurrentElement(reader, writer, + new Work() + { + public void doWork(XmlPullParser reader, XMLWriter writer) throws Exception + { + // + writer.startElement( + RuleModel.ASSOC_ACTION.getNamespaceURI(), + RuleModel.ASSOC_ACTION.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.ASSOC_ACTION.getLocalName(), + new AttributesImpl()); + + // + AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute(NamespaceService.REPOSITORY_VIEW_1_0_URI, "childName", "view:childName", null, "rule:action"); + writer.startElement( + ActionModel.TYPE_COMPOSITE_ACTION.getNamespaceURI(), + ActionModel.TYPE_COMPOSITE_ACTION.getLocalName(), + ActionModel.ACTION_MODEL_PREFIX+ ":" + ActionModel.TYPE_COMPOSITE_ACTION.getLocalName(), + attributes); + + // + writer.startElement( + NamespaceService.REPOSITORY_VIEW_1_0_URI, + "properties", + "view:properties", + new AttributesImpl()); + + // composite-action + writer.startElement( + ActionModel.PROP_DEFINITION_NAME.getNamespaceURI(), + ActionModel.PROP_DEFINITION_NAME.getLocalName(), + ActionModel.ACTION_MODEL_PREFIX + ":" + ActionModel.PROP_DEFINITION_NAME.getLocalName(), + new AttributesImpl()); + writer.write("composite-action"); + writer.endElement( + ActionModel.PROP_DEFINITION_NAME.getNamespaceURI(), + ActionModel.PROP_DEFINITION_NAME.getLocalName(), + ActionModel.ACTION_MODEL_PREFIX + ":" + ActionModel.PROP_DEFINITION_NAME.getLocalName()); + + // + writer.endElement( + NamespaceService.REPOSITORY_VIEW_1_0_URI, + "properties", + "view:properties"); + + // + writer.startElement( + NamespaceService.REPOSITORY_VIEW_1_0_URI, + "associations", + "view:associations", + new AttributesImpl()); + + // Output the association details + new OutputChildren().doWork(reader, writer); + + // + writer.endElement( + NamespaceService.REPOSITORY_VIEW_1_0_URI, + "associations", + "view:associations"); + + // + writer.endElement( + ActionModel.TYPE_COMPOSITE_ACTION.getNamespaceURI(), + ActionModel.TYPE_COMPOSITE_ACTION.getLocalName(), + ActionModel.ACTION_MODEL_PREFIX+ ":" + ActionModel.TYPE_COMPOSITE_ACTION.getLocalName()); + + // + writer.endElement( + RuleModel.ASSOC_ACTION.getNamespaceURI(), + RuleModel.ASSOC_ACTION.getLocalName(), + RuleModel.RULE_MODEL_PREFIX + ":" + RuleModel.ASSOC_ACTION.getLocalName()); + } + }, false); + } + else + { + // Output anything else that might be hanging araound + ImportFileUpdater.this.outputCurrentElement(reader, writer, new OutputChildren()); + } + } + } + + // End the rules element + writer.endElement(namespace, name, prefix+":"+name); + } + } + + public static void main(String[] args) + { + if (args.length == 2) + { + ImportFileUpdater util = new ImportFileUpdater(); + util.updateImportFile(args[0], args[1]); + } + else + { + System.out.println(" ImportFileUpdater "); + System.out.println(" source - 1.3 import file name to be updated"); + System.out.println(" destination - name of the generated 1.4 import file"); + } + } + +} diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index d9a0f7e59e..34e5436456 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; @@ -43,6 +44,7 @@ import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.CopyServiceException; +import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -65,8 +67,12 @@ import org.alfresco.util.ParameterCheck; */ public class CopyServiceImpl implements CopyService { + /** I18N labels */ + private String COPY_OF_LABEL = "copy_service.copy_of_label"; + /** The node service */ private NodeService nodeService; + private NodeService internalNodeService; /** The dictionary service*/ private DictionaryService dictionaryService; @@ -99,6 +105,16 @@ public class CopyServiceImpl implements CopyService { this.nodeService = nodeService; } + + /** + * Sets the internal node service + * + * @param internalNodeService the internal node service + */ + public void setInternalNodeService(NodeService internalNodeService) + { + this.internalNodeService = internalNodeService; + } /** * Sets the dictionary service @@ -233,7 +249,32 @@ public class CopyServiceImpl implements CopyService return copy; } - + + public NodeRef copyAndRename(NodeRef sourceNodeRef, NodeRef destinationParent, QName destinationAssocTypeQName, QName destinationQName, boolean copyChildren) + { + // Make a note of the source name and do the copy + String sourceName = (String)this.internalNodeService.getProperty(sourceNodeRef, ContentModel.PROP_NAME); + NodeRef copy = copy(sourceNodeRef, destinationParent, destinationAssocTypeQName, destinationQName, copyChildren); + + // Do the rename, iterating until a non-duplicate name is found + boolean bDone = false; + while (bDone == false) + { + try + { + this.internalNodeService.setProperty(copy, ContentModel.PROP_NAME, sourceName); + bDone = true; + } + catch(DuplicateChildNodeNameException exception) + { + sourceName = I18NUtil.getMessage(COPY_OF_LABEL, sourceName); + } + } + + // Return the copy + return copy; + } + /** * Invokes the copy complete policy for the node reference provided * diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java b/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java index 973ff0c827..426d157706 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java @@ -104,6 +104,7 @@ public class CopyServiceImplTest extends BaseSpringTest private static final QName TEST_MANDATORY_ASPECT_QNAME = QName.createQName(TEST_TYPE_NAMESPACE, "testMandatoryAspect"); private static final QName PROP5_QNAME_MANDATORY = QName.createQName(TEST_TYPE_NAMESPACE, "prop5Mandatory"); + private static final String TEST_NAME = "testName"; private static final String TEST_VALUE_1 = "testValue1"; private static final String TEST_VALUE_2 = "testValue2"; private static final String TEST_VALUE_3 = "testValue3"; @@ -239,6 +240,7 @@ public class CopyServiceImplTest extends BaseSpringTest private Map createTypePropertyBag() { Map result = new HashMap(); + result.put(ContentModel.PROP_NAME, TEST_NAME); result.put(PROP1_QNAME_MANDATORY, TEST_VALUE_1); result.put(PROP2_QNAME_OPTIONAL, TEST_VALUE_2); result.put(PROP5_QNAME_MANDATORY, TEST_VALUE_3); @@ -624,6 +626,31 @@ public class CopyServiceImplTest extends BaseSpringTest assertNotNull(value); assertEquals(nodeTwoCopy, value); } + + public void testCopyAndRename() + { + // Check a normal copy with no dup restrictions + NodeRef copy = this.copyService.copyAndRename( + this.sourceNodeRef, + this.rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}copyAssoc"), + false); + checkCopiedNode(this.sourceNodeRef, copy, true, true, false); + assertTrue(TEST_NAME.equals(this.nodeService.getProperty(copy, ContentModel.PROP_NAME))); + + // Create a folder and content node + Map propsFolder = new HashMap(1); + propsFolder.put(ContentModel.PROP_NAME, "tempFolder"); + NodeRef folderNode = this.nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{test}tempFolder"), ContentModel.TYPE_FOLDER, propsFolder).getChildRef(); + Map props = new HashMap(1); + props.put(ContentModel.PROP_NAME, TEST_NAME); + NodeRef contentNode = this.nodeService.createNode(folderNode, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}renametest"), ContentModel.TYPE_CONTENT, props).getChildRef(); + + // Now copy the content node with the duplicate name restriction + NodeRef contentCopy = this.copyService.copy(contentNode, folderNode, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}bobbins"), false); + assertFalse(TEST_NAME.equals(this.nodeService.getProperty(contentCopy, ContentModel.PROP_NAME))); + } /** * Check that the copied node contains the state we are expecting diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java index 158b3fae5b..fc813f1bc4 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java @@ -41,16 +41,12 @@ import org.alfresco.service.license.LicenseService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.io.Resource; @@ -59,12 +55,10 @@ import org.springframework.core.io.Resource; * * @author David Caruana */ -public class DescriptorServiceImpl implements DescriptorService, ApplicationListener, InitializingBean, ApplicationContextAware, DisposableBean +public class DescriptorServiceImpl extends AbstractLifecycleBean implements DescriptorService, InitializingBean { private static Log logger = LogFactory.getLog(DescriptorServiceImpl.class); - private ApplicationContext applicationContext; - private Properties serverProperties; private ImporterBootstrap systemBootstrap; @@ -78,14 +72,6 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList private Descriptor installedRepoDescriptor; - /* (non-Javadoc) - * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) - */ - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException - { - this.applicationContext = applicationContext; - } - /** * Sets the server descriptor from a resource file * @@ -163,36 +149,36 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList return (licenseService == null) ? null : licenseService.getLicense(); } - /** - * @param event - */ - public void onApplicationEvent(ApplicationEvent event) + @Override + protected void onBootstrap(ApplicationEvent event) { - if (event instanceof ContextRefreshedEvent) + // initialise the repository descriptor + // note: this requires that the repository schema has already been initialised + TransactionWork createDescriptorWork = new TransactionUtil.TransactionWork() { - // initialise the repository descriptor - // note: this requires that the repository schema has already been initialised - TransactionWork createDescriptorWork = new TransactionUtil.TransactionWork() + public Descriptor doWork() { - public Descriptor doWork() - { - // initialise license service (if installed) - initialiseLicenseService(); - - // verify license, but only if license component is installed - licenseService.verifyLicense(); - - // persist the server descriptor values - updateCurrentRepositoryDescriptor(serverDescriptor); + // initialise license service (if installed) + initialiseLicenseService(); + + // verify license, but only if license component is installed + licenseService.verifyLicense(); + + // persist the server descriptor values + updateCurrentRepositoryDescriptor(serverDescriptor); - // return the repository installed descriptor - return createInstalledRepositoryDescriptor(); - } - }; - installedRepoDescriptor = TransactionUtil.executeInUserTransaction(transactionService, createDescriptorWork); - } + // return the repository installed descriptor + return createInstalledRepositoryDescriptor(); + } + }; + installedRepoDescriptor = TransactionUtil.executeInUserTransaction(transactionService, createDescriptorWork); } + @Override + protected void onShutdown(ApplicationEvent event) + { + } + /** * Initialise Descriptors */ @@ -202,13 +188,6 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList serverDescriptor = createServerDescriptor(); } - /** - * Destruction hook - */ - public void destroy() throws Exception - { - } - /** * Create server descriptor * @@ -358,7 +337,7 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList // be declaratively taken out in an installed environment. Class licenseComponentClass = Class.forName("org.alfresco.license.LicenseComponent"); Constructor constructor = licenseComponentClass.getConstructor(new Class[] { ApplicationContext.class} ); - licenseService = (LicenseService)constructor.newInstance(new Object[] { applicationContext }); + licenseService = (LicenseService)constructor.newInstance(new Object[] { getApplicationContext() }); } catch (ClassNotFoundException e) { @@ -766,4 +745,5 @@ public class DescriptorServiceImpl implements DescriptorService, ApplicationList return serverProperties.getProperty(key, ""); } } + } diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java b/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java index 13d77fff3b..bd802737cc 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java @@ -23,11 +23,10 @@ import java.util.Map; import org.alfresco.service.descriptor.Descriptor; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.license.LicenseDescriptor; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; /** @@ -35,7 +34,7 @@ import org.springframework.context.event.ContextRefreshedEvent; * * @author davidc */ -public class DescriptorStartupLog implements ApplicationListener +public class DescriptorStartupLog extends AbstractLifecycleBean { // Logger private static final Log logger = LogFactory.getLog(DescriptorService.class); @@ -52,82 +51,6 @@ public class DescriptorStartupLog implements ApplicationListener } - /** - * @param event - */ - public void onApplicationEvent(ApplicationEvent event) - { - if (event instanceof ContextRefreshedEvent) - { - // - // log output of VM stats - // - Map properties = System.getProperties(); - String version = (properties.get("java.runtime.version") == null) ? "unknown" : (String)properties.get("java.runtime.version"); - long maxHeap = Runtime.getRuntime().maxMemory(); - float maxHeapMB = maxHeap / 1024l; - maxHeapMB = maxHeapMB / 1024l; - if (logger.isInfoEnabled()) - { - logger.info(String.format("Alfresco JVM - v%s; maximum heap size %.3fMB", version, maxHeapMB)); - } - if (logger.isWarnEnabled()) - { - if (version.startsWith("1.2") || version.startsWith("1.3") || version.startsWith("1.4")) - { - logger.warn(String.format("Alfresco JVM - WARNING - v1.5 is required; currently using v%s", version)); - } - if (maxHeapMB < 500) - { - logger.warn(String.format("Alfresco JVM - WARNING - maximum heap size %.3fMB is less than recommended 512MB", maxHeapMB)); - } - } - - // Log License Descriptors (if applicable) - LicenseDescriptor license = descriptorService.getLicenseDescriptor(); - if (license != null && logger.isInfoEnabled()) - { - String subject = license.getSubject(); - String msg = "Alfresco license: " + subject; - String holder = getHolderOrganisation(license.getHolder()); - if (holder != null) - { - msg += " granted to " + holder; - } - Date validUntil = license.getValidUntil(); - if (validUntil != null) - { - Integer days = license.getDays(); - Integer remainingDays = license.getRemainingDays(); - - msg += " limited to " + days + " days expiring " + validUntil + " (" + remainingDays + " days remaining)"; - } - else - { - msg += " (does not expire)"; - } - - - logger.info(msg); - } - - // Log Repository Descriptors - if (logger.isInfoEnabled()) - { - Descriptor serverDescriptor = descriptorService.getServerDescriptor(); - Descriptor installedRepoDescriptor = descriptorService.getInstalledRepositoryDescriptor(); - String serverEdition = serverDescriptor.getEdition(); - String serverVersion = serverDescriptor.getVersion(); - int serverSchemaVersion = serverDescriptor.getSchema(); - String installedRepoVersion = installedRepoDescriptor.getVersion(); - int installedSchemaVersion = installedRepoDescriptor.getSchema(); - logger.info(String.format("Alfresco started (%s): Current version %s schema %d - Installed version %s schema %d", - serverEdition, serverVersion, serverSchemaVersion, installedRepoVersion, installedSchemaVersion)); - } - } - } - - /** * Get Organisation from Principal * @@ -156,5 +79,83 @@ public class DescriptorStartupLog implements ApplicationListener return holder; } + + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // + // log output of VM stats + // + Map properties = System.getProperties(); + String version = (properties.get("java.runtime.version") == null) ? "unknown" : (String)properties.get("java.runtime.version"); + long maxHeap = Runtime.getRuntime().maxMemory(); + float maxHeapMB = maxHeap / 1024l; + maxHeapMB = maxHeapMB / 1024l; + if (logger.isInfoEnabled()) + { + logger.info(String.format("Alfresco JVM - v%s; maximum heap size %.3fMB", version, maxHeapMB)); + } + if (logger.isWarnEnabled()) + { + if (version.startsWith("1.2") || version.startsWith("1.3") || version.startsWith("1.4")) + { + logger.warn(String.format("Alfresco JVM - WARNING - v1.5 is required; currently using v%s", version)); + } + if (maxHeapMB < 500) + { + logger.warn(String.format("Alfresco JVM - WARNING - maximum heap size %.3fMB is less than recommended 512MB", maxHeapMB)); + } + } + + // Log License Descriptors (if applicable) + LicenseDescriptor license = descriptorService.getLicenseDescriptor(); + if (license != null && logger.isInfoEnabled()) + { + String subject = license.getSubject(); + String msg = "Alfresco license: " + subject; + String holder = getHolderOrganisation(license.getHolder()); + if (holder != null) + { + msg += " granted to " + holder; + } + Date validUntil = license.getValidUntil(); + if (validUntil != null) + { + Integer days = license.getDays(); + Integer remainingDays = license.getRemainingDays(); + + msg += " limited to " + days + " days expiring " + validUntil + " (" + remainingDays + " days remaining)"; + } + else + { + msg += " (does not expire)"; + } + + + logger.info(msg); + } + + // Log Repository Descriptors + if (logger.isInfoEnabled()) + { + Descriptor serverDescriptor = descriptorService.getServerDescriptor(); + Descriptor installedRepoDescriptor = descriptorService.getInstalledRepositoryDescriptor(); + String serverEdition = serverDescriptor.getEdition(); + String serverVersion = serverDescriptor.getVersion(); + int serverSchemaVersion = serverDescriptor.getSchema(); + String installedRepoVersion = installedRepoDescriptor.getVersion(); + int installedSchemaVersion = installedRepoDescriptor.getSchema(); + logger.info(String.format("Alfresco started (%s): Current version %s schema %d - Installed version %s schema %d", + serverEdition, serverVersion, serverSchemaVersion, installedRepoVersion, installedSchemaVersion)); + } + } + + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java index 1552ef870f..35190344ee 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java @@ -16,6 +16,7 @@ */ package org.alfresco.repo.dictionary; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -272,5 +273,26 @@ public class DictionaryDAOTest extends TestCase assertFalse(test5); } - + + public void testPropertyOverride() + { + TypeDefinition type1 = service.getType(QName.createQName(TEST_URL, "overridetype1")); + Map props1 = type1.getProperties(); + PropertyDefinition prop1 = props1.get(QName.createQName(TEST_URL, "propoverride")); + String def1 = prop1.getDefaultValue(); + assertEquals("one", def1); + + TypeDefinition type2 = service.getType(QName.createQName(TEST_URL, "overridetype2")); + Map props2 = type2.getProperties(); + PropertyDefinition prop2 = props2.get(QName.createQName(TEST_URL, "propoverride")); + String def2 = prop2.getDefaultValue(); + assertEquals("two", def2); + + TypeDefinition type3 = service.getType(QName.createQName(TEST_URL, "overridetype3")); + Map props3 = type3.getProperties(); + PropertyDefinition prop3 = props3.get(QName.createQName(TEST_URL, "propoverride")); + String def3 = prop3.getDefaultValue(); + assertEquals("three", def3); + } + } diff --git a/source/java/org/alfresco/repo/dictionary/TestModel.java b/source/java/org/alfresco/repo/dictionary/TestModel.java index d2bcb4be54..240be4ce7b 100644 --- a/source/java/org/alfresco/repo/dictionary/TestModel.java +++ b/source/java/org/alfresco/repo/dictionary/TestModel.java @@ -43,6 +43,8 @@ public class TestModel bootstrapModels.add("alfresco/model/systemModel.xml"); bootstrapModels.add("alfresco/model/contentModel.xml"); bootstrapModels.add("alfresco/model/applicationModel.xml"); + bootstrapModels.add("alfresco/model/bpmModel.xml"); + bootstrapModels.add("alfresco/workflow/workflowModel.xml"); // include models specified on command line for (String arg: args) diff --git a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml index ba51937ef7..500c04df53 100644 --- a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml +++ b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml @@ -188,6 +188,33 @@ + + + + d:text + one + + + + + + test:overridetype1 + + + two + + + + + + test:overridetype2 + + + three + + + + diff --git a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml index c79ac4586d..87d9900356 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml @@ -144,4 +144,14 @@ ace.authority.recipient = :authorityRecipient + + select + entry + from + org.alfresco.repo.domain.hibernate.DbAccessControlEntryImpl entry + where + entry.permission.typeQname = :oldTypeQName and + entry.permission.name = :oldName + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml index 61d3314746..46e95aef57 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml @@ -70,6 +70,14 @@ status.key.identifier = :identifier + + select + max(txn.id) + from + org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + join status.transaction as txn + + select count(txn.id) @@ -90,6 +98,21 @@ ]]> + + :lastTxnId and + server.ipAddress != :serverIpAddress + order by + txn.id + ]]> + + select count(status.key.guid) @@ -98,9 +121,7 @@ join status.transaction as txn where txn.id = :txnId and - status.node is not null and - status.key.protocol = :protocol and - status.key.identifier = :identifier + status.node is not null @@ -111,9 +132,7 @@ join status.transaction as txn where txn.id = :txnId and - status.node is null and - status.key.protocol = :protocol and - status.key.identifier = :identifier + status.node is null diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index 64d718da6f..9d977ad485 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -25,7 +25,9 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Writer; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.ResultSet; +import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; @@ -33,6 +35,9 @@ 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.repo.content.filestore.FileContentWriter; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -45,8 +50,6 @@ 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.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; @@ -58,12 +61,13 @@ import org.springframework.orm.hibernate3.LocalSessionFactoryBean; * * @author Derek Hulley */ -public class SchemaBootstrap implements ApplicationListener +public class SchemaBootstrap extends AbstractLifecycleBean { /** 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_STATEMENT_FAILED = "schema.update.err.statement_failed"; 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"; @@ -148,58 +152,6 @@ public class SchemaBootstrap implements ApplicationListener 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 @@ -220,39 +172,78 @@ public class SchemaBootstrap implements ApplicationListener return (SessionFactory) localSessionFactory.getObject(); } + private static class NoSchemaException extends Exception + { + private static final long serialVersionUID = 5574280159910824660L; + } + /** * @return Returns the number of applied patches */ private int countAppliedPatches(Connection connection) throws Exception { - Statement stmt = connection.createStatement(); + DatabaseMetaData dbMetadata = connection.getMetaData(); + + ResultSet tableRs = dbMetadata.getTables(null, null, "%", null); + boolean newPatchTable = false; + boolean oldPatchTable = false; 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 + while (tableRs.next()) + { + String tableName = tableRs.getString("TABLE_NAME"); + if (tableName.equalsIgnoreCase("applied_patch")) + { + oldPatchTable = true; + break; + } + else if (tableName.equalsIgnoreCase("alf_applied_patch")) + { + newPatchTable = true; + break; + } + } } finally { - try { stmt.close(); } catch (Throwable e) {} + try { tableRs.close(); } catch (Throwable e) {e.printStackTrace(); } } - // for pre-1.4 databases, the table was named differently - stmt = connection.createStatement(); - try + + if (newPatchTable) { - ResultSet rs = stmt.executeQuery("select count(id) from applied_patch"); - rs.next(); - int count = rs.getInt(1); - return count; + 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; + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } } - finally + else if (oldPatchTable) { - try { stmt.close(); } catch (Throwable e) {} + // found the old style table name + Statement 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) {} + } + } + else + { + // The applied patches table is not present + throw new NoSchemaException(); } } @@ -308,22 +299,21 @@ public class SchemaBootstrap implements ApplicationListener { countAppliedPatches(connection); } - catch (Throwable e) + catch (NoSchemaException e) { create = true; } + // Get the dialect + final Dialect dialect = Dialect.getDialect(cfg.getProperties()); + String dialectStr = dialect.getClass().getName(); + if (create) { - // Get the dialect - final Dialect dialect = Dialect.getDialect(cfg.getProperties()); - String dialectStr = dialect.getClass().getName(); - // 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-" + dialectStr + "-", ".sql"); dumpSchemaCreate(cfg, tempFile); - FileInputStream tempInputStream = new FileInputStream(tempFile); - executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath()); + executeScriptFile(cfg, connection, tempFile, tempFile.getPath()); // execute post-create scripts (not patches) for (String scriptUrl : this.postCreateScriptUrls) { @@ -340,12 +330,11 @@ public class SchemaBootstrap implements ApplicationListener 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"); + tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate-" + dialectStr + "-", ".sql"); writer = new BufferedWriter(new FileWriter(tempFile)); for (String sql : sqls) { @@ -364,8 +353,7 @@ public class SchemaBootstrap implements ApplicationListener // execute if there were changes raised by Hibernate if (tempFile != null) { - InputStream tempInputStream = new FileInputStream(tempFile); - executeScriptFile(cfg, connection, tempInputStream, tempFile.getPath()); + executeScriptFile(cfg, connection, tempFile, tempFile.getPath()); } } } @@ -414,14 +402,27 @@ public class SchemaBootstrap implements ApplicationListener private void executeScriptUrl(Configuration cfg, Connection connection, String scriptUrl) throws Exception { Dialect dialect = Dialect.getDialect(cfg.getProperties()); + String dialectStr = dialect.getClass().getName(); InputStream scriptInputStream = getScriptInputStream(dialect.getClass(), scriptUrl); // check that it exists if (scriptInputStream == null) { throw AlfrescoRuntimeException.create(ERR_SCRIPT_NOT_FOUND, scriptUrl); } + // write the script to a temp location for future and failure reference + File tempFile = null; + try + { + tempFile = TempFileProvider.createTempFile("AlfrescoSchemaUpdate-" + dialectStr + "-", ".sql"); + ContentWriter writer = new FileContentWriter(tempFile); + writer.putContent(scriptInputStream); + } + finally + { + try { scriptInputStream.close(); } catch (Throwable e) {} // usually a duplicate close + } // now execute it - executeScriptFile(cfg, connection, scriptInputStream, scriptUrl); + executeScriptFile(cfg, connection, tempFile, scriptUrl); } /** @@ -463,11 +464,12 @@ public class SchemaBootstrap implements ApplicationListener private void executeScriptFile( Configuration cfg, Connection connection, - InputStream scriptInputStream, + File scriptFile, String scriptUrl) throws Exception { logger.info(I18NUtil.getMessage(MSG_EXECUTING_SCRIPT, scriptUrl)); + InputStream scriptInputStream = new FileInputStream(scriptFile); BufferedReader reader = new BufferedReader(new InputStreamReader(scriptInputStream, "UTF8")); try { @@ -512,21 +514,9 @@ public class SchemaBootstrap implements ApplicationListener // 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) {} - } + sql = sb.toString(); + executeStatement(connection, sql, line, scriptFile); + sb = new StringBuilder(1024); } } } @@ -536,4 +526,85 @@ public class SchemaBootstrap implements ApplicationListener try { scriptInputStream.close(); } catch (Throwable e) {} } } + + /** + * Execute the given SQL statement, absorbing exceptions that we expect during + * schema creation or upgrade. + */ + private void executeStatement(Connection connection, String sql, int line, File file) throws Exception + { + Statement stmt = connection.createStatement(); + try + { + if (logger.isDebugEnabled()) + { + logger.debug("Executing statment: " + sql); + } + stmt.execute(sql); + } + catch (SQLException e) + { + String msg = I18NUtil.getMessage(ERR_STATEMENT_FAILED, sql, e.getMessage(), file.getAbsolutePath(), line); + // ignore exceptions generated by the creation of indexes that already exist + logger.error(msg); + throw e; + } + finally + { + try { stmt.close(); } catch (Throwable e) {} + } + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // 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); + } + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP + } } diff --git a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java index 12556807e0..6eb6ca27fb 100644 --- a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java +++ b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java @@ -46,6 +46,7 @@ import org.alfresco.service.cmr.view.Location; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -53,8 +54,6 @@ import org.apache.commons.logging.impl.Log4JLogger; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.util.FileCopyUtils; /** @@ -62,7 +61,7 @@ import org.springframework.util.FileCopyUtils; * * @author David Caruana */ -public class ImporterBootstrap implements ApplicationListener +public class ImporterBootstrap extends AbstractLifecycleBean { // View Properties (used in setBootstrapViews) public static final String VIEW_PATH_PROPERTY = "path"; @@ -643,16 +642,16 @@ public class ImporterBootstrap implements ApplicationListener return true; } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) + @Override + protected void onBootstrap(ApplicationEvent event) { - if (event instanceof ContextRefreshedEvent) - { - bootstrap(); - } + bootstrap(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP } } diff --git a/source/java/org/alfresco/repo/importer/system/SystemExporterImporter.java b/source/java/org/alfresco/repo/importer/system/SystemExporterImporter.java index 20a3271d59..adbc5320ba 100644 --- a/source/java/org/alfresco/repo/importer/system/SystemExporterImporter.java +++ b/source/java/org/alfresco/repo/importer/system/SystemExporterImporter.java @@ -22,7 +22,7 @@ import java.util.List; import org.alfresco.repo.admin.patch.PatchDaoService; import org.alfresco.repo.domain.AppliedPatch; -import org.alfresco.repo.domain.hibernate.VersionCounterDaoComponentImpl; +import org.alfresco.repo.version.common.counter.VersionCounterService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -37,7 +37,7 @@ public class SystemExporterImporter // dependencies private NodeService nodeService; private PatchDaoService patchDao; - private VersionCounterDaoComponentImpl versionCounterDao; + private VersionCounterService versionCounterService; public void setNodeService(NodeService nodeService) @@ -50,9 +50,9 @@ public class SystemExporterImporter this.patchDao = patchDaoService; } - public void setVersionDao(VersionCounterDaoComponentImpl versionCounterDao) + public void setVersionCounterService(VersionCounterService versionCounterService) { - this.versionCounterDao = versionCounterDao; + this.versionCounterService = versionCounterService; } @@ -89,7 +89,7 @@ public class SystemExporterImporter for (StoreRef storeRef : storeRefs) { VersionCounterInfo versionCounterInfo = new VersionCounterInfo(); - int versionCount = versionCounterDao.currentVersionNumber(storeRef); + int versionCount = versionCounterService.currentVersionNumber(storeRef); versionCounterInfo.storeRef = storeRef.toString(); versionCounterInfo.count = versionCount; systemInfo.versionCounters.add(versionCounterInfo); @@ -128,7 +128,7 @@ public class SystemExporterImporter for (VersionCounterInfo versionCounterInfo : systemInfo.versionCounters) { StoreRef storeRef = new StoreRef(versionCounterInfo.storeRef); - versionCounterDao.setVersionNumber(storeRef, versionCounterInfo.count); + versionCounterService.setVersionNumber(storeRef, versionCounterInfo.count); } } diff --git a/source/java/org/alfresco/repo/importer/system/SystemInfoBootstrap.java b/source/java/org/alfresco/repo/importer/system/SystemInfoBootstrap.java index e764c2e751..5983a32379 100644 --- a/source/java/org/alfresco/repo/importer/system/SystemInfoBootstrap.java +++ b/source/java/org/alfresco/repo/importer/system/SystemInfoBootstrap.java @@ -26,9 +26,8 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; /** @@ -36,7 +35,7 @@ import org.springframework.context.event.ContextRefreshedEvent; * * @author davidc */ -public class SystemInfoBootstrap implements ApplicationListener +public class SystemInfoBootstrap extends AbstractLifecycleBean { // dependencies private TransactionService transactionService; @@ -177,16 +176,16 @@ public class SystemInfoBootstrap implements ApplicationListener return true; } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) + @Override + protected void onBootstrap(ApplicationEvent event) { - if (event instanceof ContextRefreshedEvent) - { - bootstrap(); - } + bootstrap(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP } } diff --git a/source/java/org/alfresco/repo/jscript/Actions.java b/source/java/org/alfresco/repo/jscript/Actions.java index b371b1b5aa..9021fe1abf 100644 --- a/source/java/org/alfresco/repo/jscript/Actions.java +++ b/source/java/org/alfresco/repo/jscript/Actions.java @@ -28,32 +28,32 @@ import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.namespace.QName; import org.mozilla.javascript.Scriptable; - +import org.mozilla.javascript.Wrapper; /** * Scripted Action service for describing and executing actions against Nodes. - * + * * @author davidc */ public final class Actions implements Scopeable { /** Repository Service Registry */ private ServiceRegistry services; - + /** Root scope for this object */ private Scriptable scope; - /** * Constructor * - * @param services repository service registry + * @param services + * repository service registry */ public Actions(ServiceRegistry services) { this.services = services; } - + /** * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) */ @@ -61,11 +61,11 @@ public final class Actions implements Scopeable { this.scope = scope; } - + /** * Gets the list of registered action names * - * @return the registered action names + * @return the registered action names */ public String[] getRegistered() { @@ -79,17 +79,18 @@ public final class Actions implements Scopeable } return registered; } - + public String[] jsGet_registered() { return getRegistered(); } - + /** * Create an Action * - * @param actionName the action name - * @return the action + * @param actionName + * the action name + * @return the action */ public ScriptAction create(String actionName) { @@ -104,8 +105,7 @@ public final class Actions implements Scopeable } return scriptAction; } - - + /** * Scriptable Action * @@ -114,23 +114,25 @@ public final class Actions implements Scopeable public final class ScriptAction implements Serializable, Scopeable { private static final long serialVersionUID = 5794161358406531996L; - + /** Root scope for this object */ - private Scriptable scope; + private Scriptable scope; /** Converter with knowledge of action parameter values */ private ActionValueConverter converter; - + /** Action state */ private Action action; + private ActionDefinition actionDef; + private ScriptableParameterMap parameters = null; - - + /** * Construct * - * @param action Alfresco action + * @param action + * Alfresco action */ public ScriptAction(Action action, ActionDefinition actionDef) { @@ -138,7 +140,7 @@ public final class Actions implements Scopeable this.actionDef = actionDef; this.converter = new ActionValueConverter(); } - + /** * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) */ @@ -146,28 +148,25 @@ public final class Actions implements Scopeable { this.scope = scope; } - + /** * Returns the action name * - * @return action name + * @return action name */ public String getName() { return this.actionDef.getName(); } - + public String jsGet_name() { return getName(); } - + /** - * Return all the properties known about this node. - * - * The Map returned implements the Scriptable interface to allow access to the properties via - * JavaScript associative array access. This means properties of a node can be access thus: - * node.properties["name"] + * Return all the properties known about this node. The Map returned implements the Scriptable interface to allow access to the properties via JavaScript associative array + * access. This means properties of a node can be access thus: node.properties["name"] * * @return Map of properties for this Node. */ @@ -187,17 +186,18 @@ public final class Actions implements Scopeable this.parameters.setModified(false); } return this.parameters; - } - + } + public Map jsGet_parameters() { return getParameters(); } - + /** * Execute action * - * @param node the node to execute action upon + * @param node + * the node to execute action upon */ @SuppressWarnings("synthetic-access") public void execute(Node node) @@ -206,7 +206,7 @@ public final class Actions implements Scopeable { Map actionParams = action.getParameterValues(); actionParams.clear(); - + for (Map.Entry entry : this.parameters.entrySet()) { // perform the conversion from script wrapper object to repo serializable values @@ -217,7 +217,7 @@ public final class Actions implements Scopeable } services.getActionService().executeAction(action, node.getNodeRef()); } - + /** * Value converter with specific knowledge of action parameters * @@ -227,10 +227,12 @@ public final class Actions implements Scopeable { /** * Convert Action Parameter for Script usage - * - * @param paramName parameter name - * @param value value to convert - * @return converted value + * + * @param paramName + * parameter name + * @param value + * value to convert + * @return converted value */ @SuppressWarnings("synthetic-access") public Serializable convertActionParamForScript(String paramName, Serializable value) @@ -238,7 +240,7 @@ public final class Actions implements Scopeable ParameterDefinition paramDef = actionDef.getParameterDefintion(paramName); if (paramDef != null && paramDef.getType().equals(DataTypeDefinition.QNAME)) { - return ((QName)value).toPrefixString(services.getNamespaceService()); + return ((QName) value).toPrefixString(services.getNamespaceService()); } else { @@ -249,17 +251,45 @@ public final class Actions implements Scopeable /** * Convert Action Parameter for Java usage * - * @param paramName parameter name - * @param value value to convert - * @return converted value + * @param paramName + * parameter name + * @param value + * value to convert + * @return converted value */ @SuppressWarnings("synthetic-access") public Serializable convertActionParamForRepo(String paramName, Serializable value) { ParameterDefinition paramDef = actionDef.getParameterDefintion(paramName); + if (paramDef != null && paramDef.getType().equals(DataTypeDefinition.QNAME)) { - return QName.createQName((String)value, services.getNamespaceService()); + if (value instanceof Wrapper) + { + // unwrap a Java object from a JavaScript wrapper + // recursively call this method to convert the unwrapped value + return convertActionParamForRepo(paramName, (Serializable) ((Wrapper) value).unwrap()); + } + else + { + if (value instanceof String) + { + String stringQName = (String) value; + if (stringQName.startsWith("{")) + { + return QName.createQName(stringQName); + + } + else + { + return QName.createQName(stringQName, services.getNamespaceService()); + } + } + else + { + return value; + } + } } else { @@ -269,39 +299,41 @@ public final class Actions implements Scopeable } } - /** * Scripted Parameter map with modified flag. - * + * * @author davidc */ - public static final class ScriptableParameterMap extends ScriptableHashMap + public static final class ScriptableParameterMap extends ScriptableHashMap { private static final long serialVersionUID = 574661815973241554L; - private boolean modified = false; + private boolean modified = false; /** * Is this a modified parameter map? * - * @return true => modified + * @return true => modified */ - /*package*/ boolean isModified() + /* package */boolean isModified() { return modified; } - + /** * Set explicitly whether this map is modified * - * @param modified true => modified, false => not modified + * @param modified + * true => modified, false => not modified */ - /*package*/ void setModified(boolean modified) + /* package */void setModified(boolean modified) { this.modified = modified; } - - /* (non-Javadoc) + + /* + * (non-Javadoc) + * * @see org.mozilla.javascript.Scriptable#getClassName() */ @Override @@ -310,7 +342,9 @@ public final class Actions implements Scopeable return "ScriptableParameterMap"; } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.mozilla.javascript.Scriptable#delete(java.lang.String) */ @Override @@ -320,7 +354,9 @@ public final class Actions implements Scopeable setModified(true); } - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see org.mozilla.javascript.Scriptable#put(java.lang.String, org.mozilla.javascript.Scriptable, java.lang.Object) */ @Override @@ -330,5 +366,5 @@ public final class Actions implements Scopeable setModified(true); } } - + } diff --git a/source/java/org/alfresco/repo/jscript/CategoryNode.java b/source/java/org/alfresco/repo/jscript/CategoryNode.java new file mode 100644 index 0000000000..fc44e54e0e --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/CategoryNode.java @@ -0,0 +1,206 @@ +/* + * 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.jscript; + +import java.util.Collection; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.namespace.QName; +import org.mozilla.javascript.Scriptable; + +/** + * Category Nodes from the classification helper have special support. + * + * @author Andy Hind + */ +public class CategoryNode extends Node +{ + /** + * Constructor + * + * @param nodeRef + * @param services + * @param resolver + */ + public CategoryNode(NodeRef nodeRef, ServiceRegistry services, TemplateImageResolver resolver) + { + super(nodeRef, services, resolver); + } + + /** + * Constructor + * + * @param nodeRef + * @param services + * @param resolver + * @param scope + */ + public CategoryNode(NodeRef nodeRef, ServiceRegistry services, TemplateImageResolver resolver, Scriptable scope) + { + super(nodeRef, services, resolver, scope); + } + + /** + * @return all the members of a category + */ + public Node[] getCategoryMembers() + { + return buildNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.MEMBERS, CategoryService.Depth.ANY)); + } + + public Node[] jsGet_categoryMembers() + { + return getCategoryMembers(); + } + + /** + * @return all the subcategories of a category + */ + public CategoryNode[] getSubCategories() + { + return buildCategoryNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.ANY)); + } + + public CategoryNode[] jsGet_subCategories() + { + return getSubCategories(); + } + + /** + * @return members and subcategories of a category + */ + public Node[] getMembersAndSubCategories() + { + return buildMixedNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.ALL, CategoryService.Depth.ANY)); + } + + public Node[] jsGet_membersAndSubCategories() + { + return getMembersAndSubCategories(); + } + + /** + * @return all the immediate member of a category + */ + public Node[] getImmediateCategoryMembers() + { + return buildNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.MEMBERS, CategoryService.Depth.IMMEDIATE)); + } + + public Node[] jsGet_immediateCategoryMembers() + { + return getImmediateCategoryMembers(); + } + + /** + * @return all the immediate subcategories of a category + */ + public CategoryNode[] getImmediateSubCategories() + { + return buildCategoryNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE)); + } + + public CategoryNode[] jsGet_immediateSubCategories() + { + return getImmediateSubCategories(); + } + + /** + * @return immediate members and subcategories of a category + */ + public Node[] getImmediateMembersAndSubCategories() + { + return buildMixedNodes(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.ALL, CategoryService.Depth.IMMEDIATE)); + } + + public Node[] jsGet_immediateMembersAndSubCategories() + { + return getImmediateMembersAndSubCategories(); + } + + /** + * Create a new subcategory + * + * @param name Of the category to create + * + * @return CategoryNode + */ + public CategoryNode createSubCategory(String name) + { + return new CategoryNode(services.getCategoryService().createCategory(getNodeRef(), name), this.services, this.imageResolver, this.scope); + } + + /** + * Remove this category + */ + public void removeCategory() + { + services.getCategoryService().deleteCategory(getNodeRef()); + } + + @Override + public boolean isCategory() + { + return true; + } + + private CategoryNode[] buildCategoryNodes(Collection cars) + { + CategoryNode[] categoryNodes = new CategoryNode[cars.size()]; + int i = 0; + for (ChildAssociationRef car : cars) + { + categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, this.imageResolver, this.scope); + } + return categoryNodes; + } + + private Node[] buildNodes(Collection cars) + { + Node[] nodes = new Node[cars.size()]; + int i = 0; + for (ChildAssociationRef car : cars) + { + nodes[i++] = new Node(car.getChildRef(), this.services, this.imageResolver, this.scope); + } + return nodes; + } + + private Node[] buildMixedNodes(Collection cars) + { + Node[] nodes = new Node[cars.size()]; + int i = 0; + for (ChildAssociationRef car : cars) + { + QName type = services.getNodeService().getType(car.getChildRef()); + if (services.getDictionaryService().isSubClass(type, ContentModel.TYPE_CATEGORY)) + { + nodes[i++] = new CategoryNode(car.getChildRef(), this.services, this.imageResolver, this.scope); + } + else + { + nodes[i++] = new Node(car.getChildRef(), this.services, this.imageResolver, this.scope); + } + } + return nodes; + } +} diff --git a/source/java/org/alfresco/repo/jscript/CategoryTemplateNode.java b/source/java/org/alfresco/repo/jscript/CategoryTemplateNode.java new file mode 100644 index 0000000000..2db629bcc2 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/CategoryTemplateNode.java @@ -0,0 +1,202 @@ +/* + * 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.jscript; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.alfresco.service.cmr.repository.TemplateNode; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.namespace.QName; + +/** + * Category Nodes from the classification helper have special support. + */ +public class CategoryTemplateNode extends TemplateNode +{ + /** + * Constructor + * + * @param nodeRef + * @param services + * @param resolver + */ + public CategoryTemplateNode(NodeRef nodeRef, ServiceRegistry services, TemplateImageResolver resolver) + { + super(nodeRef, services, resolver); + } + + @Override + public boolean getIsCategory() + { + return true; + } + + /** + * @return all the member of a category + */ + public List getCategoryMembers() + { + if (getIsCategory()) + { + return buildTemplateNodeList(services.getCategoryService().getChildren(getNodeRef(), + CategoryService.Mode.MEMBERS, CategoryService.Depth.ANY)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * @return all the subcategories of a category + */ + public List getSubCategories() + { + if (getIsCategory()) + { + return buildCategoryNodeList(services.getCategoryService().getChildren(getNodeRef(), + CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.ANY)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * @return members and subcategories of a category + */ + public List getMembersAndSubCategories() + { + if (getIsCategory()) + { + + return buildMixedNodeList(services.getCategoryService().getChildren(getNodeRef(), CategoryService.Mode.ALL, + CategoryService.Depth.ANY)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * @return all the immediate member of a category + */ + public List getImmediateCategoryMembers() + { + if (getIsCategory()) + { + return buildTemplateNodeList(services.getCategoryService().getChildren(getNodeRef(), + CategoryService.Mode.MEMBERS, CategoryService.Depth.IMMEDIATE)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * @return all the immediate subcategories of a category + */ + public List getImmediateSubCategories() + { + if (getIsCategory()) + { + return buildCategoryNodeList(services.getCategoryService().getChildren(getNodeRef(), + CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * @return immediate members and subcategories of a category + */ + public List getImmediateMembersAndSubCategories() + { + if (getIsCategory()) + { + return buildMixedNodeList(services.getCategoryService().getChildren(getNodeRef(), + CategoryService.Mode.ALL, CategoryService.Depth.IMMEDIATE)); + } + else + { + return Collections.emptyList(); + } + } + + /** + * Support to build node lists from category service API calls. + * + * @param childRefs + * + * @return List of TemplateNode + */ + private List buildTemplateNodeList(Collection childRefs) + { + List answer = new ArrayList(childRefs.size()); + for (ChildAssociationRef ref : childRefs) + { + // create our Node representation from the NodeRef + TemplateNode child = new TemplateNode(ref.getChildRef(), this.services, this.imageResolver); + answer.add(child); + } + return answer; + } + + private List buildCategoryNodeList(Collection childRefs) + { + List answer = new ArrayList(childRefs.size()); + for (ChildAssociationRef ref : childRefs) + { + // create our Node representation from the NodeRef + CategoryTemplateNode child = new CategoryTemplateNode(ref.getChildRef(), this.services, this.imageResolver); + answer.add(child); + } + return answer; + } + + private List buildMixedNodeList(Collection cars) + { + List nodes = new ArrayList(cars.size()); + int i = 0; + for (ChildAssociationRef car : cars) + { + QName type = services.getNodeService().getType(car.getChildRef()); + if (services.getDictionaryService().isSubClass(type, ContentModel.TYPE_CATEGORY)) + { + nodes.add(new CategoryTemplateNode(car.getChildRef(), this.services, this.imageResolver)); + } + else + { + nodes.add(new TemplateNode(car.getChildRef(), this.services, this.imageResolver)); + } + } + return nodes; + } +} diff --git a/source/java/org/alfresco/repo/jscript/Classification.java b/source/java/org/alfresco/repo/jscript/Classification.java new file mode 100644 index 0000000000..c093bf630d --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Classification.java @@ -0,0 +1,144 @@ +/* + * 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.jscript; + +import java.util.Collection; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.mozilla.javascript.Scriptable; + +/** + * Support class for finding categories, finding root nodes for categories and creating root categories. + * + * @author Andy Hind + */ +public final class Classification implements Scopeable +{ + @SuppressWarnings("unused") + private Scriptable scope; + + private ServiceRegistry services; + + @SuppressWarnings("unused") + private TemplateImageResolver imageResolver; + + private StoreRef storeRef; + + public Classification(ServiceRegistry services, StoreRef storeRef, TemplateImageResolver imageResolver) + { + this.services = services; + this.imageResolver = imageResolver; + this.storeRef = storeRef; + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + /** + * Find all the category nodes in a given classification. + * + * @param aspect + * @return + */ + public CategoryNode[] getAllCategoryNodes(String aspect) + { + return buildCategoryNodes(services.getCategoryService().getCategories(storeRef, createQName(aspect), + CategoryService.Depth.ANY)); + } + + /** + * Get all the aspects that define a classification. + * + * @return + */ + public String[] getAllClassificationAspects() + { + Collection aspects = services.getCategoryService().getClassificationAspects(); + String[] answer = new String[aspects.size()]; + int i = 0; + for (QName qname : aspects) + { + answer[i++] = qname.toPrefixString(this.services.getNamespaceService()); + } + return answer; + } + + public String[] jsGet_allClassificationAspects() + { + return getAllClassificationAspects(); + } + + /** + * Create a root category in a classification. + * + * @param aspect + * @param name + */ + public void createRootCategory(String aspect, String name) + { + services.getCategoryService().createRootCategory(storeRef, createQName(aspect), name); + } + + /** + * Get the root categories in a classification. + * + * @param aspect + * @return + */ + public CategoryNode[] getRootCategories(String aspect) + { + return buildCategoryNodes(services.getCategoryService().getRootCategories(storeRef, createQName(aspect))); + } + + private CategoryNode[] buildCategoryNodes(Collection cars) + { + CategoryNode[] categoryNodes = new CategoryNode[cars.size()]; + int i = 0; + for (ChildAssociationRef car : cars) + { + categoryNodes[i++] = new CategoryNode(car.getChildRef(), this.services, this.imageResolver, this.scope); + } + return categoryNodes; + } + + private QName createQName(String s) + { + QName qname; + if (s.indexOf(QName.NAMESPACE_BEGIN) != -1) + { + qname = QName.createQName(s); + } + else + { + qname = QName.createQName(s, this.services.getNamespaceService()); + } + return qname; + } + +} diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index daeb82ed3f..71acd81311 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -92,7 +92,7 @@ public class Node implements Serializable, Scopeable private final static String FOLDER_BROWSE_URL = "/navigate/browse/{0}/{1}/{2}"; /** Root scope for this object */ - private Scriptable scope; + protected Scriptable scope; /** Node Value Converter */ private NodeValueConverter converter = null; @@ -110,18 +110,17 @@ public class Node implements Serializable, Scopeable private Node[] children = null; /** The properties of this node */ private ScriptableQNameMap properties = null; - private ServiceRegistry services = null; + protected ServiceRegistry services = null; private NodeService nodeService = null; private Boolean isDocument = null; private Boolean isContainer = null; private String displayPath = null; - private TemplateImageResolver imageResolver = null; + protected TemplateImageResolver imageResolver = null; private Node parent = null; private ChildAssociationRef primaryParentAssoc = null; // NOTE: see the reset() method when adding new cached members! - // ------------------------------------------------------------------------------ // Construction @@ -464,6 +463,20 @@ public class Node implements Serializable, Scopeable return isDocument(); } + /** + * @return true if the Node is a Category + */ + public boolean isCategory() + { + // this valid is overriden by the CategoryNode sub-class + return false; + } + + public boolean jsGet_isCategory() + { + return isCategory(); + } + /** * @return The list of aspects applied to this node */ @@ -890,7 +903,8 @@ public class Node implements Serializable, Scopeable this.services.getPermissionService().deletePermission(this.nodeRef, authority, permission); } - // ------------- + + // ------------------------------------------------------------------------------ // Ownership API /** @@ -1145,7 +1159,7 @@ public class Node implements Serializable, Scopeable { if (destination != null) { - NodeRef copyRef = this.services.getCopyService().copy( + NodeRef copyRef = this.services.getCopyService().copyAndRename( this.nodeRef, destination.getNodeRef(), ContentModel.ASSOC_CONTAINS, diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java index 9e501435c3..e711e7eb4e 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java @@ -220,25 +220,24 @@ public class RhinoScriptService implements ScriptService // add useful util objects model.put("actions", new Actions(services)); model.put("logger", new ScriptLogger()); + model.put("utils", new ScriptUtils()); // insert supplied object model into root of the default scope + for (String key : model.keySet()) { - for (String key : model.keySet()) + // set the root scope on appropriate objects + // this is used to allow native JS object creation etc. + Object obj = model.get(key); + if (obj instanceof Scopeable) { - // set the root scope on appropriate objects - // this is used to allow native JS object creation etc. - Object obj = model.get(key); - if (obj instanceof Scopeable) - { - ((Scopeable)obj).setScope(scope); - } - - // convert/wrap each object to JavaScript compatible - Object jsObject = Context.javaToJS(obj, scope); - - // insert into the root scope ready for access by the script - ScriptableObject.putProperty(scope, key, jsObject); + ((Scopeable)obj).setScope(scope); } + + // convert/wrap each object to JavaScript compatible + Object jsObject = Context.javaToJS(obj, scope); + + // insert into the root scope ready for access by the script + ScriptableObject.putProperty(scope, key, jsObject); } // execute the script @@ -343,6 +342,10 @@ public class RhinoScriptService implements ScriptService model.put("search", new Search(services, companyHome.getStoreRef(), resolver)); + model.put("session", new Session(services, resolver)); + + model.put("classification", new Classification(services, companyHome.getStoreRef(), resolver)); + return model; } } diff --git a/source/java/org/alfresco/repo/jscript/ScriptUtils.java b/source/java/org/alfresco/repo/jscript/ScriptUtils.java new file mode 100644 index 0000000000..853b3fe593 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/ScriptUtils.java @@ -0,0 +1,56 @@ +/* + * 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.jscript; + +import org.mozilla.javascript.Scriptable; + +/** + * Place for general and miscellenous utility functions not already found in generic JavaScript. + * + * @author Kevin Roast + */ +public final class ScriptUtils implements Scopeable +{ + /** Root scope for this object */ + private Scriptable scope; + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + /** + * Function to pad a string with zero '0' characters to the required length + * + * @param s String to pad with leading zero '0' characters + * @param len Length to pad to + * + * @return padded string or the original if already at >=len characters + */ + public String pad(String s, int len) + { + String result = s; + for (int i=0; i<(len - s.length()); i++) + { + result = "0" + result; + } + return result; + } +} diff --git a/source/java/org/alfresco/repo/jscript/Session.java b/source/java/org/alfresco/repo/jscript/Session.java new file mode 100644 index 0000000000..de54dfab33 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Session.java @@ -0,0 +1,79 @@ +/* + * 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.jscript; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.mozilla.javascript.Scriptable; + +/** + * Support object for session level properties etc. + *

+ * Provides access to the user's authentication ticket. + * + * @author Andy Hind + */ +public class Session implements Scopeable +{ + + @SuppressWarnings("unused") + private static Log logger = LogFactory.getLog(Session.class); + + @SuppressWarnings("unused") + private Scriptable scope; + + private ServiceRegistry services; + + @SuppressWarnings("unused") + private TemplateImageResolver imageResolver; + + public Session(ServiceRegistry services, TemplateImageResolver imageResolver) + { + this.services = services; + this.imageResolver = imageResolver; + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + /** + * Get the user's authentication ticket. + * + * @return + */ + public String getTicket() + { + return services.getAuthenticationService().getCurrentTicket(); + } + + /** + * Expose the user's authentication ticket as JavaScipt property. + * + * @return + */ + public String jsGet_ticket() + { + return getTicket(); + } +} diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 6d5eff338d..950571e8c6 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -503,7 +503,7 @@ public class FileFolderServiceImpl implements FileFolderService */ public FileInfo rename(NodeRef sourceNodeRef, String newName) throws FileExistsException, FileNotFoundException { - return move(sourceNodeRef, null, newName); + return moveOrCopy(sourceNodeRef, null, newName, true); } /** @@ -815,12 +815,25 @@ public class FileFolderServiceImpl implements FileFolderService for (int i = 0; i < folderCount; i++) { String pathElement = pathElements.get(i); - FileInfo pathElementInfo = getPathElementInfo(currentPath, rootNodeRef, parentNodeRef, pathElement, true); - parentNodeRef = pathElementInfo.getNodeRef(); + NodeRef folderNodeRef = searchSimple(parentNodeRef, pathElement); + if (folderNodeRef == null) + { + StringBuilder sb = new StringBuilder(128); + sb.append("Folder not found: " + currentPath); + throw new FileNotFoundException(sb.toString()); + } + parentNodeRef = folderNodeRef; } // we have resolved the folder path - resolve the last component String pathElement = pathElements.get(pathElements.size() - 1); - FileInfo result = getPathElementInfo(currentPath, rootNodeRef, parentNodeRef, pathElement, false); + NodeRef fileNodeRef = searchSimple(parentNodeRef, pathElement); + if (fileNodeRef == null) + { + StringBuilder sb = new StringBuilder(128); + sb.append("File not found: " + currentPath); + throw new FileNotFoundException(sb.toString()); + } + FileInfo result = getFileInfo(fileNodeRef); // found it if (logger.isDebugEnabled()) { @@ -831,42 +844,6 @@ public class FileFolderServiceImpl implements FileFolderService } return result; } - - /** - * Helper method to dig down a level for a node based on name - */ - private FileInfo getPathElementInfo( - StringBuilder currentPath, - NodeRef rootNodeRef, - NodeRef parentNodeRef, - String pathElement, - boolean folderOnly) throws FileNotFoundException - { - currentPath.append("/").append(pathElement); - - boolean includeFiles = (folderOnly ? false : true); - List pathElementInfos = search(parentNodeRef, pathElement, includeFiles, true, false); - // check - if (pathElementInfos.size() == 0) - { - StringBuilder sb = new StringBuilder(128); - sb.append(folderOnly ? "Folder" : "File or folder").append(" not found: \n") - .append(" root: ").append(rootNodeRef).append("\n") - .append(" path: ").append(currentPath); - throw new FileNotFoundException(sb.toString()); - } - else if (pathElementInfos.size() > 1) - { - // we have detected a duplicate name - warn, but allow - StringBuilder sb = new StringBuilder(128); - sb.append("Duplicate file or folder found: \n") - .append(" root: ").append(rootNodeRef).append("\n") - .append(" path: ").append(currentPath); - logger.warn(sb); - } - FileInfo pathElementInfo = pathElementInfos.get(0); - return pathElementInfo; - } public FileInfo getFileInfo(NodeRef nodeRef) { diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index c44fb7fca7..2e102f7aa0 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -236,11 +236,14 @@ public interface NodeDaoService */ public List getPropertyValuesByActualType(DataTypeDefinition actualDataTypeDefinition); - public Transaction getLastTxn(final StoreRef storeRef); - public int getTxnUpdateCountForStore(final StoreRef storeRef, final long txnId); - public int getTxnDeleteCountForStore(final StoreRef storeRef, final long txnId); + public Transaction getTxnById(long txnId); + public Transaction getLastTxn(); + public Transaction getLastTxnForStore(final StoreRef storeRef); + public int getTxnUpdateCount(final long txnId); + public int getTxnDeleteCount(final long txnId); public int getTransactionCount(); - public List getNextTxns(final Transaction lastTxn, final int count); + public List getNextTxns(final long lastTxnId, final int count); + public List getNextRemoteTxns(final long lastTxnId, final int count); public List getTxnChangesForStore(final StoreRef storeRef, final long txnId); public List getTxnChanges(final long 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 e849b82834..cc702dc894 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -18,6 +18,7 @@ package org.alfresco.repo.node.db.hibernate; import java.io.Serializable; import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -97,6 +98,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private final String uuid; private static TransactionAwareSingleton serverIdSingleton = new TransactionAwareSingleton(); + private final String ipAddress; /** * @@ -104,6 +106,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public HibernateNodeDaoServiceImpl() { this.uuid = GUID.generate(); + try + { + ipAddress = InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + throw new AlfrescoRuntimeException("Failed to get server IP address", e); + } } /** @@ -148,7 +158,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } try { - final String ipAddress = InetAddress.getLocalHost().getHostAddress(); HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) @@ -994,16 +1003,46 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements /* * Queries for transactions */ + private static final String QUERY_GET_LAST_TXN_ID = "txn.GetLastTxnId"; private static final String QUERY_GET_LAST_TXN_ID_FOR_STORE = "txn.GetLastTxnIdForStore"; private static final String QUERY_GET_TXN_UPDATE_COUNT_FOR_STORE = "txn.GetTxnUpdateCountForStore"; private static final String QUERY_GET_TXN_DELETE_COUNT_FOR_STORE = "txn.GetTxnDeleteCountForStore"; private static final String QUERY_COUNT_TRANSACTIONS = "txn.CountTransactions"; private static final String QUERY_GET_NEXT_TXNS = "txn.GetNextTxns"; + private static final String QUERY_GET_NEXT_REMOTE_TXNS = "txn.GetNextRemoteTxns"; private static final String QUERY_GET_TXN_CHANGES_FOR_STORE = "txn.GetTxnChangesForStore"; private static final String QUERY_GET_TXN_CHANGES = "txn.GetTxnChanges"; + public Transaction getTxnById(long txnId) + { + return (Transaction) getSession().get(TransactionImpl.class, new Long(txnId)); + } + @SuppressWarnings("unchecked") - public Transaction getLastTxn(final StoreRef storeRef) + public Transaction getLastTxn() + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(QUERY_GET_LAST_TXN_ID); + query.setMaxResults(1) + .setReadOnly(true); + return query.uniqueResult(); + } + }; + Long txnId = (Long) getHibernateTemplate().execute(callback); + Transaction txn = null; + if (txnId != null) + { + txn = (Transaction) getSession().get(TransactionImpl.class, txnId); + } + // done + return txn; + } + + @SuppressWarnings("unchecked") + public Transaction getLastTxnForStore(final StoreRef storeRef) { HibernateCallback callback = new HibernateCallback() { @@ -1028,7 +1067,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } @SuppressWarnings("unchecked") - public int getTxnUpdateCountForStore(final StoreRef storeRef, final long txnId) + public int getTxnUpdateCount(final long txnId) { HibernateCallback callback = new HibernateCallback() { @@ -1036,9 +1075,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session.getNamedQuery(QUERY_GET_TXN_UPDATE_COUNT_FOR_STORE); query.setLong("txnId", txnId) - .setString("protocol", storeRef.getProtocol()) - .setString("identifier", storeRef.getIdentifier()) - .setMaxResults(1) .setReadOnly(true); return query.uniqueResult(); } @@ -1049,7 +1085,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } @SuppressWarnings("unchecked") - public int getTxnDeleteCountForStore(final StoreRef storeRef, final long txnId) + public int getTxnDeleteCount(final long txnId) { HibernateCallback callback = new HibernateCallback() { @@ -1057,9 +1093,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session.getNamedQuery(QUERY_GET_TXN_DELETE_COUNT_FOR_STORE); query.setLong("txnId", txnId) - .setString("protocol", storeRef.getProtocol()) - .setString("identifier", storeRef.getIdentifier()) - .setMaxResults(1) .setReadOnly(true); return query.uniqueResult(); } @@ -1088,14 +1121,12 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } @SuppressWarnings("unchecked") - public List getNextTxns(final Transaction lastTxn, final int count) + public List getNextTxns(final long lastTxnId, final int count) { HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) { - long lastTxnId = (lastTxn == null) ? -1L : lastTxn.getId(); - Query query = session.getNamedQuery(QUERY_GET_NEXT_TXNS); query.setLong("lastTxnId", lastTxnId) .setMaxResults(count) @@ -1108,6 +1139,26 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements return results; } + @SuppressWarnings("unchecked") + public List getNextRemoteTxns(final long lastTxnId, final int count) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session.getNamedQuery(QUERY_GET_NEXT_REMOTE_TXNS); + query.setLong("lastTxnId", lastTxnId) + .setString("serverIpAddress", ipAddress) + .setMaxResults(count) + .setReadOnly(true); + return query.list(); + } + }; + List results = (List) getHibernateTemplate().execute(callback); + // done + return results; + } + @SuppressWarnings("unchecked") public List getTxnChangesForStore(final StoreRef storeRef, final long txnId) { diff --git a/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java b/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java index c715264264..2d4f597540 100644 --- a/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java +++ b/source/java/org/alfresco/repo/node/index/AbstractReindexComponent.java @@ -16,20 +16,30 @@ */ package org.alfresco.repo.node.index; +import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.sf.acegisecurity.Authentication; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.Transaction; import org.alfresco.repo.node.db.NodeDaoService; import org.alfresco.repo.search.Indexer; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.TransactionComponent; 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.repository.NodeRef.Status; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; import org.alfresco.util.PropertyCheck; import org.alfresco.util.VmShutdownListener; @@ -224,4 +234,229 @@ public abstract class AbstractReindexComponent implements IndexRecovery } } } + + /** + * Gets the last indexed transaction working back from the provided index. + * This method can be used to hunt for a starting point for indexing of + * transactions not yet in the index. + */ + protected long getLastIndexedTxn(long lastTxnId) + { + // get the last transaction + long lastFoundTxnId = lastTxnId + 10L; + boolean found = false; + while (!found && lastFoundTxnId >= 0) + { + // reduce the transaction ID + lastFoundTxnId = lastFoundTxnId - 10L; + // break out as soon as we find a transaction that is in the index + found = isTxnIdPresentInIndex(lastFoundTxnId); + if (found) + { + break; + } + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Found last index txn before " + lastTxnId + ": " + lastFoundTxnId); + } + return lastFoundTxnId; + } + + protected boolean isTxnIdPresentInIndex(long txnId) + { + if (logger.isDebugEnabled()) + { + logger.debug("Checking for transaction in index: " + txnId); + } + + Transaction txn = nodeDaoService.getTxnById(txnId); + if (txn == null) + { + return true; + } + + // count the changes in the transaction + int updateCount = nodeDaoService.getTxnUpdateCount(txnId); + int deleteCount = nodeDaoService.getTxnDeleteCount(txnId); + if (logger.isDebugEnabled()) + { + logger.debug("Transaction has " + updateCount + " updates and " + deleteCount + " deletes: " + txnId); + } + + // get the stores + boolean found = false; + List storeRefs = nodeService.getStores(); + for (StoreRef storeRef : storeRefs) + { + boolean inStore = isTxnIdPresentInIndex(storeRef, txn, updateCount, deleteCount); + if (inStore) + { + // found in a particular store + found = true; + break; + } + } + // done + if (logger.isDebugEnabled()) + { + logger.debug("Transaction " + txnId + " was " + (found ? "found" : "not found") + " in indexes."); + } + return found; + } + + /** + * @return Returns true if the given transaction is indexed in the in the + */ + private boolean isTxnIdPresentInIndex(StoreRef storeRef, Transaction txn, int updateCount, int deleteCount) + { + long txnId = txn.getId(); + String changeTxnId = txn.getChangeTxnId(); + // do the most update check, which is most common + if (updateCount > 0) + { + ResultSet results = null; + try + { + SearchParameters sp = new SearchParameters(); + sp.addStore(storeRef); + // search for it in the index, sorting with youngest first, fetching only 1 + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("TX:" + LuceneQueryParser.escape(changeTxnId)); + sp.setLimit(1); + + results = searcher.query(sp); + + if (results.length() > 0) + { + if (logger.isDebugEnabled()) + { + logger.debug("Index has results for txn (OK): " + txnId); + } + return true; // there were updates/creates and results for the txn were found + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Index has no results for txn (Index out of date): " + txnId); + } + return false; + } + } + finally + { + if (results != null) { results.close(); } + } + } + // there have been deletes, so we have to ensure that none of the nodes deleted are present in the index + // get all node refs for the transaction + List nodeRefs = nodeDaoService.getTxnChangesForStore(storeRef, txnId); + for (NodeRef nodeRef : nodeRefs) + { + if (logger.isDebugEnabled()) + { + logger.debug("Searching for node in index: \n" + + " node: " + nodeRef + "\n" + + " txn: " + txnId); + } + // we know that these are all deletions + ResultSet results = null; + try + { + SearchParameters sp = new SearchParameters(); + sp.addStore(storeRef); + // search for it in the index, sorting with youngest first, fetching only 1 + sp.setLanguage(SearchService.LANGUAGE_LUCENE); + sp.setQuery("ID:" + LuceneQueryParser.escape(nodeRef.toString())); + sp.setLimit(1); + + results = searcher.query(sp); + + if (results.length() == 0) + { + // no results, as expected + if (logger.isDebugEnabled()) + { + logger.debug(" --> Node not found (OK)"); + } + continue; + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug(" --> Node found (Index out of date)"); + } + return false; + } + } + finally + { + if (results != null) { results.close(); } + } + } + + // all tests passed + if (logger.isDebugEnabled()) + { + logger.debug("Index is in synch with transaction: " + txnId); + } + return true; + } + + /** + * Perform a full reindexing of the given transaction in the context of a completely + * new transaction. + * + * @param txnId the transaction identifier + */ + protected void reindexTransaction(final long txnId) + { + if (logger.isDebugEnabled()) + { + logger.debug("Reindexing transaction: " + txnId); + } + + TransactionWork reindexWork = new TransactionWork() + { + public Object doWork() throws Exception + { + // get the node references pertinent to the transaction + List nodeRefs = nodeDaoService.getTxnChanges(txnId); + // reindex each node + for (NodeRef nodeRef : nodeRefs) + { + Status nodeStatus = nodeService.getNodeStatus(nodeRef); + if (nodeStatus == null) + { + // it's not there any more + continue; + } + if (nodeStatus.isDeleted()) // node deleted + { + // only the child node ref is relevant + ChildAssociationRef assocRef = new ChildAssociationRef( + ContentModel.ASSOC_CHILDREN, + null, + null, + nodeRef); + indexer.deleteNode(assocRef); + } + else // node created + { + // get the primary assoc for the node + ChildAssociationRef primaryAssocRef = nodeService.getPrimaryParent(nodeRef); + // reindex + indexer.createNode(primaryAssocRef); + } + } + // done + return null; + } + }; + TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork, true); + // done + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java index e67c1648bf..f7fc4047f1 100644 --- a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java +++ b/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponent.java @@ -21,27 +21,25 @@ import java.util.List; import org.alfresco.i18n.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.Transaction; -import org.alfresco.repo.search.impl.lucene.LuceneQueryParser; 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.StoreRef; import org.alfresco.service.cmr.repository.NodeRef.Status; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Component to check and recover the indexes. + * Component to check and recover the indexes. By default, the server is + * put into read-only mode during the reindex process in order to prevent metadata changes. + * This is not critical and can be {@link #setLockServer(boolean) switched off} if the + * server is required immediately. * * @author Derek Hulley */ public class FullIndexRecoveryComponent extends AbstractReindexComponent { - private static final String ERR_STORE_NOT_UP_TO_DATE = "index.recovery.store_not_up_to_date"; + private static final String ERR_INDEX_OUT_OF_DATE = "index.recovery.out_of_date"; private static final String MSG_RECOVERY_STARTING = "index.recovery.starting"; private static final String MSG_RECOVERY_COMPLETE = "index.recovery.complete"; private static final String MSG_RECOVERY_PROGRESS = "index.recovery.progress"; @@ -51,17 +49,25 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent public static enum RecoveryMode { - /** Do nothing - not even a check */ + /** Do nothing - not even a check. */ NONE, - /** Perform a quick check on the state of the indexes only */ + /** + * Perform a quick check on the state of the indexes only. + */ VALIDATE, - /** Performs a quick validation and then starts a full pass-through on failure */ + /** + * Performs a validation and starts a quick recovery, if necessary. + */ AUTO, - /** Performs a full pass-through of all recorded transactions to ensure that the indexes are up to date */ + /** + * Performs a full pass-through of all recorded transactions to ensure that the indexes + * are up to date. + */ FULL; } private RecoveryMode recoveryMode; + private boolean lockServer; public FullIndexRecoveryComponent() { @@ -69,7 +75,8 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent } /** - * Set the type of recovery to perform. + * Set the type of recovery to perform. Default is {@link RecoveryMode#VALIDATE to validate} + * the indexes only. * * @param recoveryMode one of the {@link RecoveryMode } values */ @@ -77,7 +84,18 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent { this.recoveryMode = RecoveryMode.valueOf(recoveryMode); } - + + /** + * Set this on to put the server into READ-ONLY mode for the duration of the index recovery. + * The default is true, i.e. the server will be locked against further updates. + * + * @param lockServer true to force the server to be read-only + */ + public void setLockServer(boolean lockServer) + { + this.lockServer = lockServer; + } + @Override protected void reindexImpl() { @@ -99,25 +117,22 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent } else // validate first { - List storeRefs = nodeService.getStores(); - for (StoreRef storeRef : storeRefs) + Transaction txn = nodeDaoService.getLastTxn(); + if (txn == null) { - // get the last txn ID in the database - Transaction txn = nodeDaoService.getLastTxn(storeRef); - boolean lastChangeTxnIdInIndex = isTxnIdPresentInIndex(storeRef, txn); - if (lastChangeTxnIdInIndex) - { - // this store is good - continue; - } - // this store isn't up to date - String msg = I18NUtil.getMessage(ERR_STORE_NOT_UP_TO_DATE, storeRef); + // no transactions - just bug out + return; + } + long txnId = txn.getId(); + boolean txnInIndex = isTxnIdPresentInIndex(txnId); + if (!txnInIndex) + { + String msg = I18NUtil.getMessage(ERR_INDEX_OUT_OF_DATE); logger.warn(msg); - // the store is out of date - validation failed + // this store isn't up to date if (recoveryMode == RecoveryMode.VALIDATE) { - // next store - continue; + // the store is out of date - validation failed } else if (recoveryMode == RecoveryMode.AUTO) { @@ -130,8 +145,11 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent boolean allowWrite = !transactionService.isReadOnly(); try { - // set the server into read-only mode - transactionService.setAllowWrite(false); + if (lockServer) + { + // set the server into read-only mode + transactionService.setAllowWrite(false); + } // do we need to perform a full recovery if (fullRecoveryRequired) @@ -160,8 +178,9 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent Transaction lastTxn = null; while(true) { + long lastTxnId = (lastTxn == null) ? -1L : lastTxn.getId().longValue(); List nextTxns = nodeDaoService.getNextTxns( - lastTxn, + lastTxnId, MAX_TRANSACTIONS_PER_ITERATION); // reindex each transaction @@ -256,125 +275,4 @@ public class FullIndexRecoveryComponent extends AbstractReindexComponent TransactionUtil.executeInNonPropagatingUserTransaction(transactionService, reindexWork, true); // done } - - private boolean isTxnIdPresentInIndex(StoreRef storeRef, Transaction txn) - { - if (logger.isDebugEnabled()) - { - logger.debug("Checking for transaction in index: \n" + - " store: " + storeRef + "\n" + - " txn: " + txn); - } - - String changeTxnId = txn.getChangeTxnId(); - // count the changes in the transaction - int updateCount = nodeDaoService.getTxnUpdateCountForStore(storeRef, txn.getId()); - int deleteCount = nodeDaoService.getTxnDeleteCountForStore(storeRef, txn.getId()); - if (logger.isDebugEnabled()) - { - logger.debug("Transaction has " + updateCount + " updates and " + deleteCount + " deletes: " + txn); - } - - // do the most update check, which is most common - if (deleteCount == 0 && updateCount == 0) - { - if (logger.isDebugEnabled()) - { - logger.debug("No changes in transaction: " + txn); - } - // there's nothing to check for - return true; - } - else if (updateCount > 0) - { - ResultSet results = null; - try - { - SearchParameters sp = new SearchParameters(); - sp.addStore(storeRef); - // search for it in the index, sorting with youngest first, fetching only 1 - sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("TX:" + LuceneQueryParser.escape(changeTxnId)); - sp.setLimit(1); - - results = searcher.query(sp); - - if (results.length() > 0) - { - if (logger.isDebugEnabled()) - { - logger.debug("Index has results for txn (OK): " + txn); - } - return true; // there were updates/creates and results for the txn were found - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Index has no results for txn (Index out of date): " + txn); - } - return false; - } - } - finally - { - if (results != null) { results.close(); } - } - } - // there have been deletes, so we have to ensure that none of the nodes deleted are present in the index - // get all node refs for the transaction - Long txnId = txn.getId(); - List nodeRefs = nodeDaoService.getTxnChangesForStore(storeRef, txnId); - for (NodeRef nodeRef : nodeRefs) - { - if (logger.isDebugEnabled()) - { - logger.debug("Searching for node in index: \n" + - " node: " + nodeRef + "\n" + - " txn: " + txn); - } - // we know that these are all deletions - ResultSet results = null; - try - { - SearchParameters sp = new SearchParameters(); - sp.addStore(storeRef); - // search for it in the index, sorting with youngest first, fetching only 1 - sp.setLanguage(SearchService.LANGUAGE_LUCENE); - sp.setQuery("ID:" + LuceneQueryParser.escape(nodeRef.toString())); - sp.setLimit(1); - - results = searcher.query(sp); - - if (results.length() == 0) - { - // no results, as expected - if (logger.isDebugEnabled()) - { - logger.debug(" --> Node not found (OK)"); - } - continue; - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug(" --> Node found (Index out of date)"); - } - return false; - } - } - finally - { - if (results != null) { results.close(); } - } - } - - // all tests passed - if (logger.isDebugEnabled()) - { - logger.debug("Index is in synch with transaction: " + txn); - } - return true; - } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTracker.java b/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTracker.java new file mode 100644 index 0000000000..5f0c49e95a --- /dev/null +++ b/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTracker.java @@ -0,0 +1,119 @@ +/* + * 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.List; + +import org.alfresco.repo.domain.Transaction; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Component to check and recover the indexes. + * + * @author Derek Hulley + */ +public class IndexRemoteTransactionTracker extends AbstractReindexComponent +{ + private static Log logger = LogFactory.getLog(IndexRemoteTransactionTracker.class); + + private boolean remoteOnly; + private long currentTxnId; + + public IndexRemoteTransactionTracker() + { + remoteOnly = true; + currentTxnId = -1L; + } + + /** + * Set whether or not this component should only track remote transactions. + * By default, it is true, but under certain test conditions, it may + * be desirable to track local transactions too; e.g. during testing of clustering + * when running multiple instances on the same machine. + * + * @param remoteOnly true to reindex only those transactions that were + * committed to the database by a remote server. + */ + public void setRemoteOnly(boolean remoteOnly) + { + this.remoteOnly = remoteOnly; + } + + + + @Override + protected void reindexImpl() + { + if (currentTxnId < 0) + { + // initialize the starting point + Transaction lastTxn = nodeDaoService.getLastTxn(); + if (lastTxn == null) + { + // there is nothing to do + return; + } + long lastTxnId = lastTxn.getId(); + currentTxnId = getLastIndexedTxn(lastTxnId); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Performing index tracking from txn " + currentTxnId); + } + + while (true) + { + // get next transactions to index + List txns = getNextTransactions(currentTxnId); + if (txns.size() == 0) + { + // we've caught up + break; + } + // break out if the VM is shutting down + if (isShuttingDown()) + { + break; + } + // reindex all "foreign" or "local" transactions, one at a time + for (Transaction txn : txns) + { + long txnId = txn.getId(); + reindexTransaction(txnId); + currentTxnId = txnId; + } + } + } + + private static final int MAX_TXN_COUNT = 1000; + private List getNextTransactions(long currentTxnId) + { + List txns = null; + if (remoteOnly) + { + txns = nodeDaoService.getNextRemoteTxns(currentTxnId, MAX_TXN_COUNT); + } + else + { + txns = nodeDaoService.getNextTxns(currentTxnId, MAX_TXN_COUNT); + } + // done + return txns; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTrackerTest.java b/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTrackerTest.java new file mode 100644 index 0000000000..a33ff0bf40 --- /dev/null +++ b/source/java/org/alfresco/repo/node/index/IndexRemoteTransactionTrackerTest.java @@ -0,0 +1,125 @@ +/* + * 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 junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.ContentStore; +import org.alfresco.repo.node.db.NodeDaoService; +import org.alfresco.repo.search.Indexer; +import org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexer; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.TransactionComponent; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileFolderService; +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.SearchService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * @see org.alfresco.repo.node.index.IndexRemoteTransactionTracker + * + * @author Derek Hulley + */ +@SuppressWarnings("unused") +public class IndexRemoteTransactionTrackerTest extends TestCase +{ + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private AuthenticationComponent authenticationComponent; + private SearchService searchService; + private NodeService nodeService; + private FileFolderService fileFolderService; + private ContentStore contentStore; + private FullTextSearchIndexer ftsIndexer; + private Indexer indexer; + private NodeRef rootNodeRef; + + private IndexRemoteTransactionTracker indexTracker; + + public void setUp() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + searchService = serviceRegistry.getSearchService(); + nodeService = serviceRegistry.getNodeService(); + fileFolderService = serviceRegistry.getFileFolderService(); + authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl"); + contentStore = (ContentStore) ctx.getBean("fileContentStore"); + ftsIndexer = (FullTextSearchIndexer) ctx.getBean("LuceneFullTextSearchIndexer"); + + indexer = (Indexer) ctx.getBean("indexerComponent"); + NodeDaoService nodeDaoService = (NodeDaoService) ctx.getBean("nodeDaoService"); + TransactionService transactionService = serviceRegistry.getTransactionService(); + indexTracker = new IndexRemoteTransactionTracker(); + indexTracker.setAuthenticationComponent(authenticationComponent); + indexTracker.setFtsIndexer(ftsIndexer); + indexTracker.setIndexer(indexer); + indexTracker.setNodeDaoService(nodeDaoService); + indexTracker.setNodeService(nodeService); + indexTracker.setSearcher(searchService); + indexTracker.setTransactionComponent((TransactionComponent)transactionService); + + // authenticate + authenticationComponent.setSystemUserAsCurrentUser(); + + // disable indexing + TransactionWork createNodeWork = new TransactionWork() + { + public ChildAssociationRef doWork() throws Exception + { + StoreRef storeRef = new StoreRef("test", getName() + "-" + System.currentTimeMillis()); + NodeRef rootNodeRef = null; + if (!nodeService.exists(storeRef)) + { + nodeService.createStore(storeRef.getProtocol(), storeRef.getIdentifier()); + } + rootNodeRef = nodeService.getRootNode(storeRef); + // create another node + ChildAssociationRef childAssocRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.ALFRESCO_URI, "xyz"), + ContentModel.TYPE_FOLDER); + // remove the node from the index + indexer.deleteNode(childAssocRef); + return childAssocRef; + } + }; + ChildAssociationRef childAssocRef = TransactionUtil.executeInUserTransaction(transactionService, createNodeWork); + } + + public void testSetup() throws Exception + { + + } + + public synchronized void testStartup() throws Exception + { + indexTracker.reindex(); + indexTracker.reindex(); + } +} diff --git a/source/java/org/alfresco/repo/rule/BaseRuleTest.java b/source/java/org/alfresco/repo/rule/BaseRuleTest.java index 332f0495dd..d15d1ea7d6 100644 --- a/source/java/org/alfresco/repo/rule/BaseRuleTest.java +++ b/source/java/org/alfresco/repo/rule/BaseRuleTest.java @@ -118,7 +118,8 @@ public class BaseRuleTest extends BaseSpringTest this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent"); this.authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent"); - authenticationComponent.setSystemUserAsCurrentUser(); + //authenticationComponent.setSystemUserAsCurrentUser(); + authenticationComponent.setCurrentUser("admin"); // Get the rule type this.ruleType = this.ruleService.getRuleType(RULE_TYPE_NAME); diff --git a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index c00ed1e840..0374d8092e 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -149,7 +149,8 @@ public class RuleServiceCoverageTest extends TestCase this.authenticationComponent = (AuthenticationComponent)applicationContext.getBean("authenticationComponent"); //authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName()); - authenticationComponent.setSystemUserAsCurrentUser(); + //authenticationComponent.setSystemUserAsCurrentUser(); + authenticationComponent.setCurrentUser("admin"); this.testStoreRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); this.rootNodeRef = this.nodeService.getRootNode(this.testStoreRef); diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index 9bf1eea747..6d0b9420b7 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -40,6 +40,8 @@ import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleServiceException; import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; @@ -92,6 +94,11 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ private DictionaryService dictionaryService; + /** + * The permission service + */ + private PermissionService permissionService; + /** * The action service implementation which we need for some things. */ @@ -179,6 +186,16 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService this.dictionaryService = dictionaryService; } + /** + * Set the permission service + * + * @param permissionService the permission service + */ + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + /** * Set the global rules disabled flag * @@ -572,49 +589,56 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ public void saveRule(NodeRef nodeRef, Rule rule) { - disableRules(); - try - { - if (this.nodeService.exists(nodeRef) == false) - { - throw new RuleServiceException("The node does not exist."); - } - - NodeRef ruleNodeRef = rule.getNodeRef(); - if (ruleNodeRef == null) - { - if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false) - { - // Add the actionable aspect - this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null); - } - - // Create the action node - ruleNodeRef = this.nodeService.createNode( - getSavedRuleFolderRef(nodeRef), - ContentModel.ASSOC_CONTAINS, - QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()), - RuleModel.TYPE_RULE).getChildRef(); - - // Set the rule node reference and the owning node reference - rule.setNodeRef(ruleNodeRef); - } - - // Update the properties of the rule - this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_TITLE, rule.getTitle()); - this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_DESCRIPTION, rule.getDescription()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously()); - this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled()); - - // Save the rule's action - saveAction(ruleNodeRef, rule); - } - finally - { - enableRules(); - } + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) + { + disableRules(); + try + { + if (this.nodeService.exists(nodeRef) == false) + { + throw new RuleServiceException("The node does not exist."); + } + + NodeRef ruleNodeRef = rule.getNodeRef(); + if (ruleNodeRef == null) + { + if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false) + { + // Add the actionable aspect + this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null); + } + + // Create the action node + ruleNodeRef = this.nodeService.createNode( + getSavedRuleFolderRef(nodeRef), + ContentModel.ASSOC_CONTAINS, + QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()), + RuleModel.TYPE_RULE).getChildRef(); + + // Set the rule node reference and the owning node reference + rule.setNodeRef(ruleNodeRef); + } + + // Update the properties of the rule + this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_TITLE, rule.getTitle()); + this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_DESCRIPTION, rule.getDescription()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously()); + this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled()); + + // Save the rule's action + saveAction(ruleNodeRef, rule); + } + finally + { + enableRules(); + } + } + else + { + throw new RuleServiceException("Insufficient permissions to save a rule."); + } } /** @@ -667,22 +691,29 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ public void removeRule(NodeRef nodeRef, Rule rule) { - if (this.nodeService.exists(nodeRef) == true && - this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) { - disableRules(nodeRef); - try - { - NodeRef ruleNodeRef = rule.getNodeRef(); - if (ruleNodeRef != null) - { - this.nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef); - } - } - finally - { - enableRules(nodeRef); - } + if (this.nodeService.exists(nodeRef) == true && + this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + { + disableRules(nodeRef); + try + { + NodeRef ruleNodeRef = rule.getNodeRef(); + if (ruleNodeRef != null) + { + this.nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef); + } + } + finally + { + enableRules(nodeRef); + } + } + } + else + { + throw new RuleServiceException("Insufficient permissions to remove a rule."); } } @@ -691,20 +722,27 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService */ public void removeAllRules(NodeRef nodeRef) { - if (this.nodeService.exists(nodeRef) == true && - this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) { - NodeRef folder = getSavedRuleFolderRef(nodeRef); - if (folder != null) - { - List ruleChildAssocs = this.nodeService.getChildAssocs( - folder, - RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX); - for (ChildAssociationRef ruleChildAssoc : ruleChildAssocs) - { - this.nodeService.removeChild(folder, ruleChildAssoc.getChildRef()); - } - } + if (this.nodeService.exists(nodeRef) == true && + this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + { + NodeRef folder = getSavedRuleFolderRef(nodeRef); + if (folder != null) + { + List ruleChildAssocs = this.nodeService.getChildAssocs( + folder, + RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX); + for (ChildAssociationRef ruleChildAssoc : ruleChildAssocs) + { + this.nodeService.removeChild(folder, ruleChildAssoc.getChildRef()); + } + } + } + } + else + { + throw new RuleServiceException("Insufficient permissions to remove a rule."); } } diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java index 10f34d4820..cdadf1e724 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java @@ -27,6 +27,8 @@ import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator; import org.alfresco.repo.action.executer.ImageTransformActionExecuter; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.repo.transaction.TransactionUtil.TransactionWork; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.repository.ContentWriter; @@ -34,7 +36,10 @@ import org.alfresco.service.cmr.repository.CyclicChildRelationshipException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.apache.commons.digester.SetRootRule; /** @@ -44,7 +49,17 @@ import org.alfresco.service.namespace.QName; */ public class RuleServiceImplTest extends BaseRuleTest { - + AuthenticationService authenticationService; + PermissionService permissionService; + + @Override + protected void onSetUpInTransaction() throws Exception + { + super.onSetUpInTransaction(); + this.permissionService = (PermissionService)this.applicationContext.getBean("permissionService"); + this.authenticationService = (AuthenticationService)this.applicationContext.getBean("authenticationService"); + } + /** * Test get rule type */ @@ -296,6 +311,59 @@ public class RuleServiceImplTest extends BaseRuleTest ContentModel.TYPE_CONTAINER).getChildRef(); } + public void testRuleServicePermissionsConsumer() + { + this.authenticationService.createAuthentication("conUser", "password".toCharArray()); + this.permissionService.setPermission(this.nodeRef, "conUser", PermissionService.CONSUMER, true); + this.permissionService.setInheritParentPermissions(this.nodeRef, true); + + this.authenticationService.authenticate("conUser", "password".toCharArray()); + Rule rule = createTestRule(); + try + { + this.ruleService.saveRule(this.nodeRef, rule); + // Fail + fail("Consumers cannot create rules."); + } + catch (Exception exception) + { + // Ok + } + + } + + public void testRuleServicePermissionsEditor() + { + this.authenticationService.createAuthentication("editorUser", "password".toCharArray()); + this.permissionService.setPermission(this.nodeRef, "editorUser", PermissionService.EDITOR, true); + this.permissionService.setInheritParentPermissions(this.nodeRef, true); + + this.authenticationService.authenticate("editorUser", "password".toCharArray()); + Rule rule = createTestRule(); + try + { + this.ruleService.saveRule(this.nodeRef, rule); + // Fail + fail("Editors cannot create rules."); + } + catch (Exception exception) + { + // Ok + } + } + + public void testRuleServicePermissionsCoordinator() + { + this.authenticationService.createAuthentication("coordUser", "password".toCharArray()); + this.permissionService.setPermission(this.nodeRef, "coordUser", PermissionService.COORDINATOR, true); + this.permissionService.setInheritParentPermissions(this.nodeRef, true); + + this.authenticationService.authenticate("admin", "admin".toCharArray()); + Rule rule2 = createTestRule(); + this.ruleService.saveRule(this.nodeRef, rule2); + this.authenticationService.clearCurrentSecurityContext(); + } + /** * Tests the rule inheritance within the store, checking that the cache is reset correctly when * rules are added and removed. diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java index 7a9b576895..cf3fa13ae6 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneSearcherImpl2.java @@ -49,6 +49,9 @@ import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.alfresco.util.ISO9075; import org.alfresco.util.SearchLanguageConversion; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.index.TermEnum; import org.apache.lucene.search.Hits; import org.apache.lucene.search.Query; import org.apache.lucene.search.Searcher; @@ -230,7 +233,7 @@ public class LuceneSearcherImpl2 extends LuceneBase2 implements LuceneSearcher2 switch (sd.getSortType()) { case FIELD: - if (searcher.getReader().getFieldNames().contains(sd.getField())) + if (fieldHasTerm(searcher.getReader(), sd.getField())) { fields[index++] = new SortField(sd.getField(), !sd.isAscending()); } @@ -308,6 +311,35 @@ public class LuceneSearcherImpl2 extends LuceneBase2 implements LuceneSearcher2 } } + private static boolean fieldHasTerm(IndexReader indexReader, String field) + { + try + { + TermEnum termEnum = indexReader.terms(new Term(field, "")); + try + { + if (termEnum.next()) + { + Term first = termEnum.term(); + return first.field().equals(field); + } + else + { + return false; + } + } + finally + { + termEnum.close(); + } + } + catch (IOException e) + { + throw new SearcherException("Could not find terms for sort field ", e); + } + + } + public ResultSet query(StoreRef store, String language, String query) { return query(store, language, query, null, null); diff --git a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java index 1549425147..0713337710 100644 --- a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java +++ b/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthority.java @@ -16,6 +16,8 @@ */ package org.alfresco.repo.security.permissions.dynamic; +import java.io.Serializable; + import org.alfresco.model.ContentModel; import org.alfresco.repo.security.permissions.DynamicAuthority; import org.alfresco.service.cmr.lock.LockService; @@ -44,9 +46,13 @@ public class LockOwnerDynamicAuthority implements DynamicAuthority, Initializing } if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) { - NodeRef original = DefaultTypeConverter.INSTANCE.convert( - NodeRef.class, nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE)); - if (nodeService.exists(original)) + NodeRef original = null; + Serializable reference = nodeService.getProperty(nodeRef, ContentModel.PROP_COPY_REFERENCE); + if (reference != null) + { + original = DefaultTypeConverter.INSTANCE.convert(NodeRef.class, reference); + } + if (original != null && nodeService.exists(original)) { return (lockService.getLockStatus(original) == LockStatus.LOCK_OWNER); } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java index fef50dd7f4..18e75a1423 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoter.java @@ -44,7 +44,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; /** - * * @author andyh */ @@ -153,8 +152,8 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean if ((attribute.getAttribute() != null) && (attribute.getAttribute().startsWith(ACL_NODE) || attribute.getAttribute().startsWith(ACL_PARENT) - || attribute.getAttribute().startsWith(ACL_ALLOW) - || attribute.getAttribute().startsWith(ACL_METHOD))) + || attribute.getAttribute().startsWith(ACL_ALLOW) || attribute.getAttribute().startsWith( + ACL_METHOD))) { return true; } @@ -165,14 +164,11 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean } /** - * This implementation supports only MethodSecurityInterceptor, - * because it queries the presented MethodInvocation. + * This implementation supports only MethodSecurityInterceptor, because it queries the presented MethodInvocation. * * @param clazz * the secure object - * - * @return true if the secure object is - * MethodInvocation, false otherwise + * @return true if the secure object is MethodInvocation, false otherwise */ public boolean supports(Class clazz) { @@ -253,7 +249,15 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean testNodeRef = (NodeRef) invocation.getArguments()[cad.parameter]; if (log.isDebugEnabled()) { - log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + if (nodeService.exists(testNodeRef)) + { + log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + } + else + { + log.debug("\tPermission test on non-existing node " +testNodeRef); + } + } } else if (ChildAssociationRef.class.isAssignableFrom(params[cad.parameter])) @@ -263,7 +267,14 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean testNodeRef = ((ChildAssociationRef) invocation.getArguments()[cad.parameter]).getChildRef(); if (log.isDebugEnabled()) { - log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + if (nodeService.exists(testNodeRef)) + { + log.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); + } + else + { + log.debug("\tPermission test on non-existing node " + testNodeRef); + } } } } @@ -284,6 +295,14 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean testNodeRef = nodeService.getPrimaryParent(child).getParentRef(); if (log.isDebugEnabled()) { + if (nodeService.exists(testNodeRef)) + { + log.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); + } + else + { + log.debug("\tPermission test for parent on non-existing node " + testNodeRef); + } log.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); } } @@ -295,8 +314,17 @@ public class ACLEntryVoter implements AccessDecisionVoter, InitializingBean testNodeRef = ((ChildAssociationRef) invocation.getArguments()[cad.parameter]).getParentRef(); if (log.isDebugEnabled()) { - log.debug("\tPermission test for parent on child assoc ref for node " - + nodeService.getPath(testNodeRef)); + if (nodeService.exists(testNodeRef)) + { + log.debug("\tPermission test for parent on child assoc ref for node " + + nodeService.getPath(testNodeRef)); + } + else + { + log.debug("\tPermission test for parent on child assoc ref for non existing node " + + testNodeRef); + } + } } diff --git a/source/java/org/alfresco/repo/template/Classification.java b/source/java/org/alfresco/repo/template/Classification.java new file mode 100644 index 0000000000..3c55a5d0f6 --- /dev/null +++ b/source/java/org/alfresco/repo/template/Classification.java @@ -0,0 +1,123 @@ +/* + * 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.template; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.jscript.CategoryTemplateNode; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.TemplateImageResolver; +import org.alfresco.service.cmr.repository.TemplateNode; +import org.alfresco.service.cmr.search.CategoryService; +import org.alfresco.service.namespace.QName; + +/** + * Support for finding classifications and their root categories. + * + * @author Andy Hind + */ +public final class Classification +{ + private ServiceRegistry services; + private TemplateImageResolver imageResolver; + private StoreRef storeRef; + + public Classification(StoreRef storeRef, ServiceRegistry services, TemplateImageResolver imageResolver) + { + this.storeRef = storeRef; + this.services = services; + this.imageResolver = imageResolver; + } + + /** + * Find all the category nodes in a given classification. + * + * @param aspect + * + * @return all the category nodes in a given classification. + */ + public List getAllCategoryNodes(String aspect) + { + return buildCategoryNodes(services.getCategoryService().getCategories(storeRef, createQName(aspect), + CategoryService.Depth.ANY)); + } + + /** + * Find all the category nodes in a given classification. + * + * @param aspect + * + * @return all the category nodes in a given classification. + */ + public List getAllCategoryNodes(QName aspect) + { + return buildCategoryNodes(services.getCategoryService().getCategories(storeRef, aspect, + CategoryService.Depth.ANY)); + } + + /** + * @return all the aspects that define a classification. + */ + public List getAllClassificationAspects() + { + Collection aspects = services.getCategoryService().getClassificationAspects(); + ArrayList answer = new ArrayList(aspects.size()); + answer.addAll(aspects); + return answer; + } + + /** + * Get the root categories in a classification. + * + * @param aspect + * + * @return List of TemplateNode + */ + public List getRootCategories(String aspect) + { + return buildCategoryNodes(services.getCategoryService().getRootCategories(storeRef, createQName(aspect))); + } + + + private List buildCategoryNodes(Collection cars) + { + ArrayList categoryNodes = new ArrayList(cars.size()); + for (ChildAssociationRef car : cars) + { + categoryNodes.add(new CategoryTemplateNode(car.getChildRef(), this.services, this.imageResolver)); + } + return categoryNodes; + } + + private QName createQName(String s) + { + QName qname; + if (s.indexOf(QName.NAMESPACE_BEGIN) != -1) + { + qname = QName.createQName(s); + } + else + { + qname = QName.createQName(s, this.services.getNamespaceService()); + } + return qname; + } +} diff --git a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java index 2ba4994524..6aae071809 100644 --- a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java +++ b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java @@ -301,6 +301,13 @@ public class FreeMarkerProcessor implements TemplateProcessor // current date/time is useful to have and isn't supplied by FreeMarker by default model.put("date", new Date()); + // Session support + model.put("session", new Session(services, imageResolver)); + + // Classification support + + model.put("classification", new Classification(companyHome.getStoreRef(), services, imageResolver)); + // add custom method objects model.put("hasAspect", new HasAspectMethod()); model.put("message", new I18NMessageMethod()); diff --git a/source/java/org/alfresco/repo/template/Session.java b/source/java/org/alfresco/repo/template/Session.java new file mode 100644 index 0000000000..e1007de8c9 --- /dev/null +++ b/source/java/org/alfresco/repo/template/Session.java @@ -0,0 +1,50 @@ +/* + * 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.template; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.TemplateImageResolver; + +/** + * Support session information in free marker templates. + * + * @author Andy Hind + */ +public class Session +{ + + private ServiceRegistry services; + + @SuppressWarnings("unused") + private TemplateImageResolver imageResolver; + + public Session(ServiceRegistry services, TemplateImageResolver imageResolver) + { + this.services = services; + this.imageResolver = imageResolver; + } + + /** + * Get the current authentication ticket. + * + * @return + */ + public String getTicket() + { + return services.getAuthenticationService().getCurrentTicket(); + } +} diff --git a/source/java/org/alfresco/repo/version/common/counter/VersionCounterService.java b/source/java/org/alfresco/repo/version/common/counter/VersionCounterService.java index fa41e2e373..c45aeb35ec 100644 --- a/source/java/org/alfresco/repo/version/common/counter/VersionCounterService.java +++ b/source/java/org/alfresco/repo/version/common/counter/VersionCounterService.java @@ -59,4 +59,16 @@ public interface VersionCounterService * @param storeRef the store reference */ public void resetVersionNumber(StoreRef storeRef); + + /** + * Sets the version number for a specified store. + * + * WARNING: calling this method will completely reset the current + * version count for the specified store and cannot be undone. + * + * @param storeRef the store reference + * @param versionCount the new version count + */ + public void setVersionNumber(StoreRef storeRef, int versionCount); + } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index 0755746654..a6c3d20d3c 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -31,11 +31,10 @@ import org.alfresco.service.cmr.workflow.WorkflowDeployment; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.AbstractLifecycleBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.core.io.ClassPathResource; @@ -44,7 +43,7 @@ import org.springframework.core.io.ClassPathResource; * * @author davidc */ -public class WorkflowDeployer implements ApplicationListener +public class WorkflowDeployer extends AbstractLifecycleBean { // Logging support private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow"); @@ -222,16 +221,16 @@ public class WorkflowDeployer implements ApplicationListener } } - /* - * (non-Javadoc) - * @see org.springframework.context.ApplicationListener#onApplicationEvent(org.springframework.context.ApplicationEvent) - */ - public void onApplicationEvent(ApplicationEvent event) + @Override + protected void onBootstrap(ApplicationEvent event) { - if (event instanceof ContextRefreshedEvent) - { - deploy(); - } + deploy(); + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP } } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java b/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java new file mode 100644 index 0000000000..90eda03c7d --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/WorkflowInterpreter.java @@ -0,0 +1,707 @@ +/* + * 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.workflow; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowDeployment; +import org.alfresco.service.cmr.workflow.WorkflowInstance; +import org.alfresco.service.cmr.workflow.WorkflowPath; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.cmr.workflow.WorkflowTransition; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; +import org.springframework.core.io.ClassPathResource; + +/** + * An interactive console for Workflows. + * + * @author davidc + */ +public class WorkflowInterpreter +{ + // Service dependencies + private WorkflowService workflowService; + private NamespaceService namespaceService; + private PersonService personService; + + /** + * The reader for interaction. + */ + private BufferedReader fIn; + + /** + * Current context + */ + private WorkflowDefinition currentWorkflowDef = null; + private WorkflowPath currentPath = null; + private String currentDeploy = null; + + /** + * Last command issued + */ + private String lastCommand = null; + + /** + * Variables + */ + private Map vars = new HashMap(); + + + /** + * Main entry point. + * + * Syntax: AVMInteractiveConsole storage (new|old). + */ + public static void main(String[] args) + { + ApplicationContext context = ApplicationContextHelper.getApplicationContext(); + WorkflowInterpreter console = (WorkflowInterpreter)context.getBean("workflowInterpreter"); + AuthenticationUtil.setSystemUserAsCurrentUser(); + console.rep(); + System.exit(0); + } + + /** + * Make up a new console. + */ + public WorkflowInterpreter() + { + fIn = new BufferedReader(new InputStreamReader(System.in)); + } + + /** + * @param workflowService The Workflow Service + */ + public void setWorkflowService(WorkflowService workflowService) + { + this.workflowService = workflowService; + } + + /** + * @param namespaceService namespaceService + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param personService personService + */ + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + /** + * A Read-Eval-Print loop. + */ + public void rep() + { + while (true) + { + System.out.print("ok> "); + try + { + String line = fIn.readLine(); + if (line.equals("exit")) + { + return; + } + long startms = System.currentTimeMillis(); + System.out.print(interpretCommand(line)); + System.out.println("" + (System.currentTimeMillis() - startms) + "ms"); + } + catch (Exception e) + { + e.printStackTrace(System.err); + System.out.println(""); + } + } + } + + /** + * Interpret a single command using the BufferedReader passed in for any data needed. + * + * @param line The unparsed command + * @return The textual output of the command. + */ + public String interpretCommand(String line) + throws IOException + { + String[] command = line.split(" "); + if (command.length == 0) + { + command = new String[1]; + command[0] = line; + } + + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(bout); + + // repeat last command? + if (command[0].equals("r")) + { + if (lastCommand == null) + { + return "No command entered yet."; + } + return "repeating command " + lastCommand + "\n\n" + interpretCommand(lastCommand); + } + + // remember last command + lastCommand = line; + + // execute command + if (command[0].equals("help")) + { + String helpFile = I18NUtil.getMessage("workflow_console.help"); + ClassPathResource helpResource = new ClassPathResource(helpFile); + byte[] helpBytes = new byte[500]; + InputStream helpStream = helpResource.getInputStream(); + try + { + int read = helpStream.read(helpBytes); + while (read != -1) + { + bout.write(helpBytes, 0, read); + read = helpStream.read(helpBytes); + } + } + finally + { + helpStream.close(); + } + } + + else if (command[0].equals("show")) + { + if (command.length < 2) + { + return "Syntax Error.\n"; + } + + else if (command[1].equals("definitions")) + { + List defs = workflowService.getDefinitions(); + for (WorkflowDefinition def : defs) + { + out.println("id: " + def.id + " , name: " + def.name + " , title: " + def.title + " , version: " + def.version); + } + } + + else if (command[1].equals("workflows")) + { + if (currentWorkflowDef == null) + { + return "workflow definition not in use. Enter command use .\n"; + } + List workflows = workflowService.getActiveWorkflows(currentWorkflowDef.id); + for (WorkflowInstance workflow : workflows) + { + out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title); + } + } + + else if (command[1].equals("paths")) + { + String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id; + if (workflowId == null) + { + return "Syntax Error. Workflow Id not specified.\n"; + } + List paths = workflowService.getWorkflowPaths(workflowId); + for (WorkflowPath path : paths) + { + out.println("path id: " + path.id + " , node: " + path.node.name); + } + } + + else if (command[1].equals("tasks")) + { + String pathId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.id; + if (pathId == null) + { + return "Syntax Error. Path Id not specified.\n"; + } + List tasks = workflowService.getTasksForWorkflowPath(pathId); + for (WorkflowTask task : tasks) + { + out.println("task id: " + task.id + " , name: " + task.name + " , properties: " + task.properties.size()); + } + } + + else if (command[1].equals("transitions")) + { + String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id; + if (workflowId == null) + { + return "Syntax Error. Workflow Id not specified.\n"; + } + List paths = workflowService.getWorkflowPaths(workflowId); + for (WorkflowPath path : paths) + { + out.println("path: " + path.id + " , node: " + path.node.name + " , active: " + path.active); + List tasks = workflowService.getTasksForWorkflowPath(path.id); + for (WorkflowTask task : tasks) + { + out.println(" task id: " + task.id + " , name: " + task.name + " , properties: " + task.properties.size()); + } + for (WorkflowTransition transition : path.node.transitions) + { + out.println(" transition id: " + ((transition.id == null || transition.id.equals("")) ? "[default]" : transition.id) + " , title: " + transition.title); + } + } + } + + else if (command[1].equals("my")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + + if (command[2].equals("tasks")) + { + out.println(AuthenticationUtil.getCurrentUserName() + ":"); + List tasks = workflowService.getAssignedTasks(AuthenticationUtil.getCurrentUserName(), WorkflowTaskState.IN_PROGRESS); + for (WorkflowTask task : tasks) + { + out.println("id: " + task.id + " , name: " + task.name + " , properties: " + task.properties.size() + " , workflow: " + task.path.instance.id + " , path: " + task.path.id); + } + } + + else if (command[2].equals("completed")) + { + out.println(AuthenticationUtil.getCurrentUserName() + ":"); + List tasks = workflowService.getAssignedTasks(AuthenticationUtil.getCurrentUserName(), WorkflowTaskState.COMPLETED); + for (WorkflowTask task : tasks) + { + out.println("id: " + task.id + " , name " + task.name + " , properties: " + task.properties.size() + " , workflow: " + task.path.instance.id + " , path: " + task.path.id); + } + } + else + { + return "Syntax Error.\n"; + } + } + else + { + return "Syntax Error.\n"; + } + } + + else if (command[0].equals("desc")) + { + if (command.length < 2) + { + return "Syntax Error.\n"; + } + + if (command[1].equals("task")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + WorkflowTask task = workflowService.getTaskById(command[2]); + out.println("id: " + task.id); + out.println("name: " + task.name); + out.println("title: " + task.title); + out.println("description: " + task.description); + out.println("state: " + task.state); + out.println("path: " + task.path.id); + out.println("transitions: " + task.path.node.transitions.length); + for (WorkflowTransition transition : task.path.node.transitions) + { + out.println(" transition: " + ((transition == null || transition.id.equals("")) ? "[default]" : transition.id) + " , title: " + transition.title + " , desc: " + transition.description); + } + out.println("properties: " + task.properties.size()); + for (Map.Entry prop : task.properties.entrySet()) + { + out.println(" " + prop.getKey() + " = " + prop.getValue()); + } + } + + else if (command[1].equals("workflow")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + WorkflowInstance workflow = workflowService.getWorkflowById(command[2]); + out.println("definition: " + workflow.definition.name); + out.println("id: " + workflow.id); + out.println("description: " + workflow.description); + out.println("active: " + workflow.active); + out.println("start date: " + workflow.startDate); + out.println("end date: " + workflow.endDate); + out.println("initiator: " + workflow.initiator); + out.println("context: " + workflow.context); + out.println("package: " + workflow.workflowPackage); + } + else + { + return "Syntax Error.\n"; + } + } + + else if (command[0].equals("deploy")) + { + if (command.length != 2) + { + return "Syntax Error.\n"; + } + ClassPathResource workflowDef = new ClassPathResource(command[1]); + WorkflowDeployment deployment = workflowService.deployDefinition("jbpm", workflowDef.getInputStream(), MimetypeMap.MIMETYPE_XML); + WorkflowDefinition def = deployment.definition; + for (String problem : deployment.problems) + { + out.println(problem); + } + out.println("deployed definition id: " + def.id + " , name: " + def.name + " , title: " + def.title + " , version: " + def.version); + currentDeploy = command[1]; + out.print(interpretCommand("use " + def.id)); + } + + else if (command[0].equals("redeploy")) + { + if (currentDeploy == null) + { + return "nothing to redeploy\n"; + } + out.print(interpretCommand("deploy " + currentDeploy)); + } + + else if (command[0].equals("use")) + { + if (command.length == 1) + { + out.println("definition: " + ((currentWorkflowDef == null) ? "None" : currentWorkflowDef.id + " , name: " + currentWorkflowDef.title)); + out.println("workflow: " + ((currentPath == null) ? "None" : currentPath.instance.id + " , active: " + currentPath.instance.active)); + out.println("path: " + ((currentPath == null) ? "None" : currentPath.id + " , node: " + currentPath.node.title)); + } + else if (command.length > 1) + { + if (command[1].equals("definition")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + WorkflowDefinition def = workflowService.getDefinitionById(command[2]); + if (def == null) + { + return "Not found.\n"; + } + currentWorkflowDef = def; + currentPath = null; + out.print(interpretCommand("use")); + } + + else if (command[1].equals("workflow")) + { + if (command.length != 3) + { + return "Syntax Error.\n"; + } + WorkflowInstance instance = workflowService.getWorkflowById(command[2]); + currentWorkflowDef = instance.definition; + currentPath = workflowService.getWorkflowPaths(instance.id).get(0); + out.print(interpretCommand("use")); + } + else + { + return "Syntax Error.\n"; + } + } + + } + + else if (command[0].equals("user")) + { + if (command.length == 2) + { + AuthenticationUtil.setCurrentUser(command[1]); + } + out.println("using user " + AuthenticationUtil.getCurrentUserName()); + } + + else if (command[0].equals("start")) + { + Map params = new HashMap(); + for (int i = 1; i < command.length; i++) + { + String[] param = command[i].split("="); + QName qname = QName.createQName(param[0], namespaceService); + if (param.length == 1) + { + if (!vars.containsKey(qname)) + { + return "var " + qname + " not found.\n"; + } + params.put(qname, vars.get(qname)); + } + else if (param.length == 2) + { + params.put(qname, param[1]); + } + else + { + return "Syntax Error.\n"; + } + } + WorkflowPath path = workflowService.startWorkflow(currentWorkflowDef.id, params); + out.println("started workflow id: " + path.instance.id + ", path: " + path.id + " , node: " + path.node.name + " , def: " + path.instance.definition.title); + currentPath = path; + } + + else if (command[0].equals("update")) + { + if (command.length < 3) + { + return "Syntax Error.\n"; + } + + if (command[1].equals("task")) + { + if (command.length < 4) + { + return "Syntax Error.\n"; + } + Map params = new HashMap(); + for (int i = 3; i < command.length; i++) + { + String[] param = command[i].split("="); + QName qname = QName.createQName(param[0], namespaceService); + if (param.length == 1) + { + if (!vars.containsKey(qname)) + { + return "var " + qname + " not found.\n"; + } + params.put(qname, vars.get(qname)); + } + else if (param.length == 2) + { + params.put(qname, param[1]); + } + else + { + return "Syntax Error.\n"; + } + } + WorkflowTask task = workflowService.updateTask(command[2], params, null, null); + out.println("updated task id: " + command[2] + ", properties: " + task.properties.size()); + } + else + { + return "Syntax Error.\n"; + } + } + + else if (command[0].equals("signal")) + { + if (command.length < 2) + { + return "Syntax Error.\n"; + } + WorkflowPath path = workflowService.signal(command[1], (command.length == 3) ? command[2] : null); + out.println("signal sent - path id: " + path.id + " , node: " + path.node.name); + } + + else if (command[0].equals("end")) + { + if (command.length < 3) + { + return "Syntax Error.\n"; + } + if (command[1].equals("task")) + { + WorkflowTask task = workflowService.endTask(command[2], (command.length == 4) ? command[3] : null); + out.println("signal sent - path id: " + task.path.id + " , node: " + task.path.node.name); + } + else if (command[1].equals("workflow")) + { + String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id; + if (workflowId == null) + { + return "Syntax Error. Workflow Id not specified.\n"; + } + workflowService.cancelWorkflow(workflowId); + out.println("cancelled workflow" + workflowId); + } + else + { + return "Syntax Error.\n"; + } + } + + else if (command[0].equals("var")) + { + if (command.length == 1) + { + for (Map.Entry entry : vars.entrySet()) + { + out.println(entry.getKey() + " = " + entry.getValue()); + } + } + else if (command.length == 2) + { + String[] param = command[1].split("="); + if (param.length == 0) + { + return "Syntax Error.\n"; + } + if (param.length == 1) + { + QName qname = QName.createQName(param[0], namespaceService); + vars.remove(qname); + out.println("deleted var " + qname); + } + else if (param.length == 2) + { + boolean multi = false; + if (param[0].endsWith("*")) + { + param[0] = param[0].substring(0, param[0].length() -1); + multi = true; + } + QName qname = QName.createQName(param[0], namespaceService); + String[] strValues = param[1].split(","); + if (!multi && strValues.length > 1) + { + return "Syntax Error.\n"; + } + if (!multi) + { + vars.put(qname, strValues[0]); + } + else + { + List values = new ArrayList(); + for (String strValue : strValues) + { + values.add(strValue); + } + vars.put(qname, (Serializable)values); + } + out.println("set var " + qname + " = " + vars.get(qname)); + } + else + { + return "Syntax Error.\n"; + } + } + else if (command.length == 4) + { + if (command[2].equals("person")) + { + boolean multi = false; + if (command[1].endsWith("*")) + { + command[1] = command[1].substring(0, command[1].length() -1); + multi = true; + } + QName qname = QName.createQName(command[1], namespaceService); + String[] strValues = command[3].split(","); + if (!multi && strValues.length > 1) + { + return "Syntax Error.\n"; + } + if (!multi) + { + NodeRef auth = personService.getPerson(strValues[0]); + vars.put(qname, auth); + } + else + { + List values = new ArrayList(); + for (String strValue : strValues) + { + NodeRef auth = personService.getPerson(strValue); + values.add(auth); + } + vars.put(qname, (Serializable)values); + } + out.println("set var " + qname + " = " + vars.get(qname)); + } + else + { + return "Syntax Error.\n"; + } + } + else + { + return "Syntax Error.\n"; + } + } + + else + { + return "Syntax Error.\n"; + } + + out.flush(); + String retVal = new String(bout.toByteArray()); + out.close(); + return retVal; + } + + + /** + * Get currently used workflow definition + * + * @return workflow definition + */ + public WorkflowDefinition getCurrentWorkflowDef() + { + return currentWorkflowDef; + } + + /** + * Get current user name + * + * @return user name + */ + public String getCurrentUserName() + { + return AuthenticationUtil.getCurrentUserName(); + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java index dc8737b20d..f0dca684f4 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowPackageImpl.java @@ -25,6 +25,7 @@ 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.search.SearchService; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -47,6 +48,7 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent private SearchService searchService; private NodeService nodeService; private NamespaceService namespaceService; + private PermissionService permissionService; private NodeRef systemWorkflowContainer = null; @@ -74,6 +76,11 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent this.nodeService = nodeService; } + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + /** * @param namespaceService namespace service */ @@ -113,6 +120,8 @@ public class WorkflowPackageImpl implements WorkflowPackageComponent QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, containerName); ChildAssociationRef childRef = nodeService.createNode(packages, ContentModel.ASSOC_CONTAINS, qname, ContentModel.TYPE_SYSTEM_FOLDER); container = childRef.getChildRef(); + // TODO: For now, grant full access to everyone + permissionService.setPermission(container, PermissionService.ALL_AUTHORITIES, PermissionService.ALL_PERMISSIONS, true); isSystemPackage = true; } diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java index 9e1cfbabf0..74a293728f 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngine.java @@ -311,10 +311,25 @@ public class JBPMEngine extends BPMEngine /* (non-Javadoc) * @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitionById(java.lang.String) */ - public WorkflowDefinition getDefinitionById(String workflowDefinitionId) + public WorkflowDefinition getDefinitionById(final String workflowDefinitionId) { - // TODO - throw new UnsupportedOperationException(); + try + { + return (WorkflowDefinition)jbpmTemplate.execute(new JbpmCallback() + { + public Object doInJbpm(JbpmContext context) + { + // retrieve process + GraphSession graphSession = context.getGraphSession(); + ProcessDefinition processDefinition = graphSession.getProcessDefinition(getJbpmId(workflowDefinitionId)); + return processDefinition == null ? null : createWorkflowDefinition(processDefinition); + } + }); + } + catch(JbpmException e) + { + throw new WorkflowException("Failed to retrieve workflow definition '" + workflowDefinitionId + "'", e); + } } /* (non-Javadoc) @@ -1714,7 +1729,7 @@ public class JBPMEngine extends BPMEngine workflowTransition.id = transition.getName(); Node node = transition.getFrom(); workflowTransition.isDefault = node.getDefaultLeavingTransition().equals(transition); - if (workflowTransition.id.length() == 0) + if (workflowTransition.id == null || workflowTransition.id.length() == 0) { workflowTransition.title = getLabel(DEFAULT_TRANSITION_LABEL, TITLE_LABEL, workflowTransition.id); workflowTransition.description = getLabel(DEFAULT_TRANSITION_LABEL, DESC_LABEL, workflowTransition.title); diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java index 3e927d4bba..47c9465e0d 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java +++ b/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java @@ -341,8 +341,10 @@ public class JBPMEngineTest extends BaseSpringTest public void testSignal() { + Map parameters = new HashMap(); + parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef); WorkflowDefinition workflowDef = getTestDefinition(); - WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, null); + WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters); assertNotNull(path); WorkflowPath updatedPath = workflowComponent.signal(path.id, path.node.transitions[1].id); assertNotNull(updatedPath); @@ -374,6 +376,26 @@ public class JBPMEngineTest extends BaseSpringTest } + public void xtestMultiAssign() + { + WorkflowDefinition workflowDef = getTestDefinition(); + List bpm_assignees = new ArrayList(); + bpm_assignees.add("admin"); + bpm_assignees.add("bob"); + bpm_assignees.add("fred"); + Map parameters = new HashMap(); + parameters.put(QName.createQName(NamespaceService.BPM_MODEL_1_0_URI, "assignees"), (Serializable)bpm_assignees); + parameters.put(QName.createQName(NamespaceService.DEFAULT_URI, "testNode"), testNodeRef); + WorkflowPath path = workflowComponent.startWorkflow(workflowDef.id, parameters); + assertNotNull(path); + List tasks = workflowComponent.getTasksForWorkflowPath(path.id); + assertNotNull(tasks); + assertEquals(1, tasks.size()); + WorkflowTask updatedTask = taskComponent.endTask(tasks.get(0).id, "multi"); + assertNotNull(updatedTask); + } + + public void testEndTask() { WorkflowDefinition workflowDef = getTestDefinition(); diff --git a/source/java/org/alfresco/service/cmr/repository/CopyService.java b/source/java/org/alfresco/service/cmr/repository/CopyService.java index d69e77e82a..857fb7431a 100644 --- a/source/java/org/alfresco/service/cmr/repository/CopyService.java +++ b/source/java/org/alfresco/service/cmr/repository/CopyService.java @@ -74,6 +74,7 @@ public interface CopyService * @param destinationAssocTypeQName the type of the new child assoc * @param destinationQName the qualified name of the child association from the * parent to the new node + * @param copyChildren indicates that the children of the node should also be copied * * @return the new node reference */ @@ -85,10 +86,32 @@ public interface CopyService QName destinationQName, boolean copyChildren); + /** + * @see CopyService#copy(NodeRef, NodeRef, QName, QName, boolean) + * + * Ensures the copy name is the same as the origional or is renamed to prevent duplicate names. + * + * @param sourceNodeRef the node reference used as the source of the copy + * @param destinationParent the intended parent of the new node + * @param destinationAssocTypeQName the type of the new child assoc + * @param destinationQName the qualified name of the child association from the + * parent to the new node + * @param copyChildren indicates that the children of the node should also be copied + * + * @return the new node reference + */ + @Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "destinationParent", "destinationAssocTypeQName", "destinationQName", "copyChildren"}) + public NodeRef copyAndRename( + NodeRef sourceNodeRef, + NodeRef destinationParent, + QName destinationAssocTypeQName, + QName destinationQName, + boolean copyChildren); + /** * By default children of the source node are not copied. * - * @see NodeCopyService#copy(NodeRef, NodeRef, QName, QName, boolean) + * @see CopyService#copy(NodeRef, NodeRef, QName, QName, boolean) * * @param sourceNodeRef the node reference used as the source of the copy * @param destinationParent the intended parent of the new node @@ -142,4 +165,5 @@ public interface CopyService */ @Auditable(key = Auditable.Key.ARG_0, parameters = {"nodeRef"}) public List getCopies(NodeRef nodeRef); + } diff --git a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java index 6d2a05b55c..bd3ce6bea0 100644 --- a/source/java/org/alfresco/service/cmr/repository/TemplateNode.java +++ b/source/java/org/alfresco/service/cmr/repository/TemplateNode.java @@ -60,7 +60,7 @@ import freemarker.ext.dom.NodeModel; * * @author Kevin Roast */ -public final class TemplateNode implements Serializable +public class TemplateNode implements Serializable { private static final long serialVersionUID = 1234390333739034171L; @@ -78,7 +78,7 @@ public final class TemplateNode implements Serializable private Map> assocs = null; /** Cached values */ - private NodeRef nodeRef; + protected NodeRef nodeRef; private String name; private QName type; private String path; @@ -87,15 +87,16 @@ public final class TemplateNode implements Serializable private QNameMap properties; private List permissions = null; private boolean propsRetrieved = false; - private ServiceRegistry services = null; + protected ServiceRegistry services = null; private Boolean isDocument = null; private Boolean isContainer = null; private String displayPath = null; private String mimetype = null; private Long size = null; - private TemplateImageResolver imageResolver = null; + protected TemplateImageResolver imageResolver = null; private TemplateNode parent = null; private ChildAssociationRef primaryParentAssoc = null; + private Boolean isCategory = null; // ------------------------------------------------------------------------------ @@ -360,6 +361,20 @@ public final class TemplateNode implements Serializable return locked; } + /** + * @return true if the node is a Category instance + */ + public boolean getIsCategory() + { + if (isCategory == null) + { + DictionaryService dd = this.services.getDictionaryService(); + isCategory = Boolean.valueOf(dd.isSubClass(getType(), ContentModel.TYPE_CATEGORY)); + } + + return isCategory.booleanValue(); + } + /** * @return the parent node */ @@ -482,17 +497,17 @@ public final class TemplateNode implements Serializable */ public NodeModel getXmlNodeModel() { - try - { - return NodeModel.parse(new InputSource(new StringReader(getContent()))); - } - catch (Throwable err) - { - if (logger.isDebugEnabled()) - logger.debug(err.getMessage(), err); - - return null; - } + try + { + return NodeModel.parse(new InputSource(new StringReader(getContent()))); + } + catch (Throwable err) + { + if (logger.isDebugEnabled()) + logger.debug(err.getMessage(), err); + + return null; + } } /** @@ -661,14 +676,18 @@ public final class TemplateNode implements Serializable } + // ------------------------------------------------------------------------------ // Audit API - + /** + * @return a list of AuditInfo objects describing the Audit Trail for this node instance + */ public List getAuditTrail() { return this.services.getAuditService().getAuditTrail(this.nodeRef); } + // ------------------------------------------------------------------------------ // Misc helpers