diff --git a/config/alfresco/avm-services-context.xml b/config/alfresco/avm-services-context.xml index 8f2d936bce..6f5cf4a8a0 100644 --- a/config/alfresco/avm-services-context.xml +++ b/config/alfresco/avm-services-context.xml @@ -101,6 +101,9 @@ + + + @@ -128,6 +131,9 @@ + + + @@ -232,6 +238,9 @@ + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index b3efd3f861..e84d9ca40c 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -36,7 +36,7 @@ classpath:alfresco/dbscripts/create/2.1/${db.script.dialect}/AlfrescoPostCreate-2.1-FKIndexes.sql - classpath:alfresco/dbscripts/create/1.4/${db.script.dialect}/post-create-indexes-02.sql + classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-Constraints.sql diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index bac83fe525..c20a810ad3 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -27,6 +27,45 @@ + + + + + + + + + + + + + + defaultCache + + + + + + + + + + + + + + + + + org.alfresco.cache.qnameEntityTransactionalCache + + + 100 + + + diff --git a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes-02.sql b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes-02.sql index 4b5926d4b1..b56282bfbe 100644 --- a/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes-02.sql +++ b/config/alfresco/dbscripts/create/1.4/org.hibernate.dialect.Dialect/post-create-indexes-02.sql @@ -5,6 +5,4 @@ -- -- Association QNames -CREATE INDEX idx_ca_type_qname ON alf_child_assoc (type_qname); CREATE INDEX idx_ca_qname ON alf_child_assoc (qname); -CREATE INDEX idx_na_type_qname ON alf_node_assoc (type_qname); diff --git a/config/alfresco/dbscripts/create/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-2.1-FKIndexes.sql b/config/alfresco/dbscripts/create/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-2.1-FKIndexes.sql index c4e01b517a..692232ef41 100644 --- a/config/alfresco/dbscripts/create/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-2.1-FKIndexes.sql +++ b/config/alfresco/dbscripts/create/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoPostCreate-2.1-FKIndexes.sql @@ -7,8 +7,4 @@ -- Please contact support@alfresco.com if you need assistance with the upgrade. -- --- Remove pointless duplicated FK indexes -ALTER TABLE alf_global_attributes DROP INDEX FK64D0B9CF69B9F16A; - - -- The MySQL dialects apply the FK indexes by default diff --git a/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Constraints.sql b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Constraints.sql new file mode 100644 index 0000000000..7d14e7fdef --- /dev/null +++ b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Constraints.sql @@ -0,0 +1,20 @@ +-- +-- Title: Post-Create Constraints +-- Database: Generic +-- Since: V2.2 Schema 82 +-- Author: Derek Hulley +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +ALTER TABLE alf_node_aspects ADD CONSTRAINT fk_alf_na_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_na_qn ON alf_node_aspects (qname_id); + +ALTER TABLE alf_node_properties ADD CONSTRAINT fk_alf_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_np_qn ON alf_node_properties (qname_id); + +ALTER TABLE avm_aspects_new ADD CONSTRAINT fk_avm_na_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_na_qn ON avm_aspects_new (qname_id); + +ALTER TABLE avm_node_properties_new ADD CONSTRAINT fk_avm_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_np_qn ON avm_node_properties_new (qname_id); diff --git a/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.1-FKIndexes.sql b/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.1-FKIndexes.sql index 2b293de85f..a08cf214cd 100644 --- a/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.1-FKIndexes.sql +++ b/config/alfresco/dbscripts/upgrade/2.1/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.1-FKIndexes.sql @@ -9,7 +9,9 @@ -- The MySQL InnoDB Dialect has special support for foreign keys. -- Remove pointless duplicated FK indexes -ALTER TABLE alf_global_attributes DROP INDEX FK64D0B9CF69B9F16A;(optional) +ALTER TABLE alf_global_attributes DROP INDEX FK64D0B9CF69B9F16A;(optional) +ALTER TABLE alf_node DROP INDEX FK60EFB62696FBEE85;(optional) +ALTER TABLE alf_child_assoc DROP INDEX FKFFC5468E96FBEE85;(optional) -- -- Record script finish diff --git a/config/alfresco/dbscripts/upgrade/2.2-QNames/upgrade-mysql-qnames.sql b/config/alfresco/dbscripts/upgrade/2.2-QNames/upgrade-mysql-qnames.sql new file mode 100644 index 0000000000..d6a9b76fa7 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/2.2-QNames/upgrade-mysql-qnames.sql @@ -0,0 +1,274 @@ +-- Create static namespace and qname tables +CREATE TABLE alf_namespace +( + id BIGINT NOT NULL AUTO_INCREMENT, + version BIGINT NOT NULL, + uri VARCHAR(100) NOT NULL, + PRIMARY KEY (id), + UNIQUE (uri) +) ENGINE=InnoDB; + +CREATE TABLE alf_qname +( + id BIGINT NOT NULL AUTO_INCREMENT, + version BIGINT NOT NULL, + ns_id BIGINT NOT NULL, + local_name VARCHAR(200) NOT NULL, + PRIMARY KEY (id), + UNIQUE (ns_id, local_name) +) ENGINE=InnoDB; + +create index fk_alf_qname_ns on alf_qname (ns_id); +ALTER TABLE alf_qname + add constraint fk_alf_qname_ns foreign key (ns_id) references alf_namespace (id); + +-- Create temporary table for dynamic (child) QNames +CREATE TABLE t_qnames_dyn +( + qname varchar(255) NOT NULL, + namespace varchar(255) +) ENGINE=InnoDB; +ALTER TABLE t_qnames_dyn ADD INDEX TQND_IDX_QN (qname); +ALTER TABLE t_qnames_dyn ADD INDEX TQND_IDX_NS (namespace); + +-- Populate the table with the child association paths +INSERT INTO t_qnames_dyn (qname) +( + SELECT qname FROM alf_CHILD_ASSOC +); +-- Extract the Namespace +UPDATE t_qnames_dyn SET namespace = SUBSTR(SUBSTRING_INDEX(qname, '}', 1), 2); +-- Move the namespaces to the their new home +INSERT INTO alf_namespace (uri, version) +( + SELECT + distinct(x.namespace), 1 + FROM + ( + SELECT t.namespace, n.uri FROM t_qnames_dyn t LEFT OUTER JOIN alf_namespace n ON (n.uri = t.namespace) + ) x + WHERE + x.uri IS NULL +); +-- We can get trash the temp table +DROP TABLE t_qnames_dyn; + +-- Create temporary table to hold static QNames +CREATE TABLE t_qnames +( + qname varchar(255) NOT NULL, + namespace varchar(255), + localname varchar(255) +) ENGINE=InnoDB; +ALTER TABLE t_qnames ADD INDEX TQN_IDX_QN (qname); +ALTER TABLE t_qnames ADD INDEX TQN_IDX_NS (namespace); +ALTER TABLE t_qnames ADD INDEX TQN_IDX_LN (localname); + +-- Populate the table with all known static QNames +INSERT INTO t_qnames (qname) +( + SELECT type_qname FROM alf_node +); +INSERT INTO t_qnames (qname) +( + SELECT qname FROM alf_node_aspects +); +INSERT INTO t_qnames (qname) +( + SELECT qname FROM alf_node_properties +); +INSERT INTO t_qnames (qname) +( + SELECT name FROM avm_aspects_new +); +INSERT INTO t_qnames (qname) +( + SELECT qname FROM avm_node_properties_new +); +INSERT INTO t_qnames (qname) +( + SELECT qname FROM avm_store_properties +); +INSERT INTO t_qnames (qname) +( + SELECT type_qname FROM alf_node_assoc +); +INSERT INTO t_qnames (qname) +( + SELECT type_qname FROM alf_child_assoc +); +-- Extract the namespace and localnames from the QNames +UPDATE t_qnames SET namespace = SUBSTR(SUBSTRING_INDEX(qname, '}', 1), 2); +UPDATE t_qnames SET localname = SUBSTRING_INDEX(qname, '}', -1); +-- Move the Namespaces to their new home +INSERT INTO alf_namespace (uri, version) +( + SELECT + distinct(x.namespace), 1 + FROM + ( + SELECT t.namespace, n.uri FROM t_qnames t LEFT OUTER JOIN alf_namespace n ON (n.uri = t.namespace) + ) x + WHERE + x.uri IS NULL +); +-- Move the Localnames to their new home +INSERT INTO alf_qname (ns_id, local_name, version) +( + SELECT + x.ns_id, x.t_localname, 1 + FROM + ( + SELECT n.id AS ns_id, t.localname AS t_localname, q.local_name AS q_localname + FROM t_qnames t + JOIN alf_namespace n ON (n.uri = t.namespace) + LEFT OUTER JOIN alf_qname q ON (q.local_name = t.localname) + ) x + WHERE + q_localname IS NULL + GROUP BY x.ns_id, x.t_localname +); +-- We can get trash the temp table +DROP TABLE t_qnames; + +-- +-- DATA REPLACEMENT: alf_node.type_qname +-- +ALTER TABLE alf_node ADD COLUMN type_qname_id BIGINT NULL AFTER uuid; +UPDATE alf_node n set n.type_qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = n.type_qname +); +ALTER TABLE alf_node DROP COLUMN type_qname; +ALTER TABLE alf_node MODIFY COLUMN type_qname_id BIGINT NOT NULL AFTER uuid; +ALTER TABLE alf_node ADD CONSTRAINT fk_alf_n_tqname FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_n_tqname ON alf_node (type_qname_id); + +-- +-- DATA REPLACEMENT: alf_node_aspects.qname +-- +-- ALTER TABLE alf_node_aspects DROP PRIMARY KEY;(optional) +ALTER TABLE alf_node_aspects ADD COLUMN qname_id BIGINT NULL AFTER node_id; +UPDATE alf_node_aspects na set na.qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = na.qname +); +ALTER TABLE alf_node_aspects DROP COLUMN qname; +ALTER TABLE alf_node_aspects MODIFY COLUMN qname_id BIGINT NOT NULL AFTER node_id; +ALTER TABLE alf_node_aspects ADD CONSTRAINT fk_alf_na_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_na_qn ON alf_node_aspects (qname_id); +ALTER TABLE alf_node_aspects ADD PRIMARY KEY (node_id, qname_id); + +-- +-- DATA REPLACEMENT: alf_node_properties.qname +-- +ALTER TABLE alf_node_properties DROP PRIMARY KEY; +ALTER TABLE alf_node_properties ADD COLUMN qname_id BIGINT NULL AFTER node_id; +UPDATE alf_node_properties np set np.qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = np.qname +); +ALTER TABLE alf_node_properties DROP COLUMN qname; +ALTER TABLE alf_node_properties MODIFY COLUMN qname_id BIGINT NOT NULL AFTER node_id; +ALTER TABLE alf_node_properties ADD CONSTRAINT fk_alf_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_np_qn ON alf_node_properties (qname_id); +ALTER TABLE alf_node_properties ADD PRIMARY KEY (node_id, qname_id); + +-- +-- DATA REPLACEMENT: avm_aspects_new.name (aka qname) +-- +ALTER TABLE avm_aspects_new DROP PRIMARY KEY; +ALTER TABLE avm_aspects_new ADD COLUMN qname_id BIGINT NULL AFTER id; +UPDATE avm_aspects_new na set na.qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = na.name +); +ALTER TABLE avm_aspects_new DROP COLUMN name; +ALTER TABLE avm_aspects_new MODIFY COLUMN qname_id BIGINT NOT NULL AFTER id; +ALTER TABLE avm_aspects_new ADD CONSTRAINT fk_avm_na_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_na_qn ON avm_aspects_new (qname_id); +ALTER TABLE avm_aspects_new ADD PRIMARY KEY (id, qname_id); + +-- +-- DATA REPLACEMENT: avm_node_properties_new.qname +-- +ALTER TABLE avm_node_properties_new DROP PRIMARY KEY; +ALTER TABLE avm_node_properties_new ADD COLUMN qname_id BIGINT NULL AFTER node_id; +UPDATE avm_node_properties_new np set np.qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = np.qname +); +ALTER TABLE avm_node_properties_new DROP COLUMN qname; +ALTER TABLE avm_node_properties_new MODIFY COLUMN qname_id BIGINT NOT NULL AFTER node_id; +ALTER TABLE avm_node_properties_new ADD CONSTRAINT fk_avm_np_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_np_qn ON avm_node_properties_new (qname_id); +ALTER TABLE avm_node_properties_new ADD PRIMARY KEY (node_id, qname_id); + +-- +-- DATA REPLACEMENT: avm_store_properties.qname +-- +ALTER TABLE avm_store_properties ADD COLUMN qname_id BIGINT NULL AFTER avm_store_id; +UPDATE avm_store_properties np set np.qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = np.qname +); +ALTER TABLE avm_store_properties DROP COLUMN qname; +ALTER TABLE avm_store_properties MODIFY COLUMN qname_id BIGINT NOT NULL AFTER avm_store_id; +ALTER TABLE avm_store_properties ADD CONSTRAINT fk_avm_sp_qname FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_sp_qname ON avm_store_properties (qname_id); + +-- +-- DATA REPLACEMENT: alf_child_assoc.type_qname +-- +ALTER TABLE alf_child_assoc ADD COLUMN type_qname_id BIGINT NULL AFTER child_node_id; +UPDATE alf_child_assoc ca set ca.type_qname_id = +( + SELECT q.id + FROM alf_qname q + JOIN alf_namespace ns ON (q.ns_id = ns.id) + WHERE CONCAT('{', ns.uri, '}', q.local_name) = ca.type_qname +); +ALTER TABLE alf_child_assoc DROP COLUMN type_qname; +ALTER TABLE alf_child_assoc MODIFY COLUMN type_qname_id BIGINT NOT NULL AFTER child_node_id; +ALTER TABLE alf_child_assoc ADD CONSTRAINT fk_alf_ca_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_ca_tqn ON alf_child_assoc (type_qname_id); + +-- +-- DATA REPLACEMENT: alf_child_assoc.qname +-- +-- Namespace +ALTER TABLE alf_child_assoc ADD COLUMN qname_ns_id BIGINT NULL AFTER type_qname_id; +UPDATE alf_child_assoc ca set ca.qname_ns_id = +( + SELECT ns.id + FROM alf_namespace ns + WHERE SUBSTR(SUBSTRING_INDEX(qname, '}', 1), 2) = ns.uri +); +ALTER TABLE alf_child_assoc MODIFY COLUMN qname_ns_id BIGINT NOT NULL AFTER type_qname_id; +ALTER TABLE alf_child_assoc ADD CONSTRAINT fk_alf_ca_qn_ns FOREIGN KEY (qname_ns_id) REFERENCES alf_namespace (id); +CREATE INDEX fk_alf_ca_qn_ns ON alf_child_assoc (qname_ns_id); +-- LocalName +ALTER TABLE alf_child_assoc ADD COLUMN qname_localname VARCHAR(200) NULL AFTER qname_ns_id; +UPDATE alf_child_assoc ca set ca.qname_localname = SUBSTRING_INDEX(qname, '}', -1); +ALTER TABLE alf_child_assoc MODIFY COLUMN qname_localname VARCHAR(200) NOT NULL AFTER qname_ns_id; +CREATE INDEX idx_alf_ca_qn_ln ON alf_child_assoc (qname_localname); +-- Drop old column +ALTER TABLE alf_child_assoc DROP COLUMN qname; diff --git a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.2-ACL.sql b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.2-ACL.sql index 591eaa0875..b4e4e07865 100644 --- a/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.2-ACL.sql +++ b/config/alfresco/dbscripts/upgrade/2.2/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoSchemaUpdate-2.2-ACL.sql @@ -150,5 +150,5 @@ INSERT INTO alf_applied_patch VALUES ( 'patch.db-V2.2-ACL', 'Manually executed script upgrade V2.2: Update acl schema', - 0, 76, -1, 77, null, 'UNKOWN', 1, 1, 'Script completed' + 0, 84, -1, 85, null, 'UNKOWN', 1, 1, 'Script completed' ); \ No newline at end of file diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 6d16d55ba8..73d5e84139 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -39,6 +39,7 @@ + org/alfresco/repo/domain/hibernate/QName.hbm.xml org/alfresco/repo/domain/hibernate/Node.hbm.xml org/alfresco/repo/domain/hibernate/Store.hbm.xml org/alfresco/repo/domain/hibernate/Transaction.hbm.xml @@ -149,6 +150,8 @@ + ${cache.strategy} + ${cache.strategy} ${cache.strategy} ${cache.strategy} ${cache.strategy} @@ -192,6 +195,15 @@ + + + + + + + + + @@ -275,6 +287,9 @@ + + + @@ -309,7 +324,6 @@ - diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 8e33baf8f0..08068b9fc5 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -159,6 +159,9 @@ + + + diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index 6ab42d25aa..4242a00f18 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -841,6 +841,9 @@ + + + @@ -852,6 +855,9 @@ + + + diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AVMAspectsPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AVMAspectsPatch.java index 968bb6c723..e41d4e0184 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AVMAspectsPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AVMAspectsPatch.java @@ -31,6 +31,9 @@ import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.avm.AVMAspectName; import org.alfresco.repo.avm.AVMAspectNameDAO; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; +import org.alfresco.service.namespace.QName; /** * Patches from old style aspect storage for AVM to new style. @@ -41,12 +44,18 @@ public class AVMAspectsPatch extends AbstractPatch private static final String MSG_SUCCESS = "patch.AVMAspects.result"; private AVMAspectNameDAO fAVMAspectDAO; + private QNameDAO qnameDAO; public void setAvmAspectNameDAO(AVMAspectNameDAO dao) { fAVMAspectDAO = dao; } + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + /* (non-Javadoc) * @see org.alfresco.repo.admin.patch.AbstractPatch#applyInternal() */ @@ -57,7 +66,9 @@ public class AVMAspectsPatch extends AbstractPatch while (iter.hasNext()) { AVMAspectName aspect = iter.next(); - aspect.getNode().getAspects().add(aspect.getName()); + QName aspectQName = aspect.getName(); + QNameEntity aspectQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectQName); + aspect.getNode().getAspects().add(aspectQNameEntity.getId()); fAVMAspectDAO.delete(aspect); } return I18NUtil.getMessage(MSG_SUCCESS); diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AVMPropertiesPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AVMPropertiesPatch.java index 2cc51df15e..40192e26ce 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AVMPropertiesPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AVMPropertiesPatch.java @@ -31,6 +31,8 @@ import org.alfresco.i18n.I18NUtil; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.avm.AVMNodeProperty; import org.alfresco.repo.avm.AVMNodePropertyDAO; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.service.namespace.QName; /** * Patch more remapping AVM properties. @@ -40,8 +42,14 @@ public class AVMPropertiesPatch extends AbstractPatch { private static final String MSG_SUCCESS = "patch.AVMProperties.result"; + private QNameDAO qnameDAO; private AVMNodePropertyDAO fAVMNodePropertyDAO; + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + public void setAvmNodePropertyDAO(AVMNodePropertyDAO dao) { fAVMNodePropertyDAO = dao; @@ -57,7 +65,9 @@ public class AVMPropertiesPatch extends AbstractPatch while (iter.hasNext()) { AVMNodeProperty prop = iter.next(); - prop.getNode().getProperties().put(prop.getName(), prop.getValue()); + QName propertyQName = prop.getName(); + Long propertyQNameEntityId = qnameDAO.getOrCreateQNameEntity(propertyQName).getId(); + prop.getNode().getProperties().put(propertyQNameEntityId, prop.getValue()); fAVMNodePropertyDAO.delete(prop.getNode(), prop.getName()); } return I18NUtil.getMessage(MSG_SUCCESS); diff --git a/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java index eef9a218ef..2007dcdfb2 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/NodePropertySerializablePatch.java @@ -33,7 +33,6 @@ import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; -import org.alfresco.service.namespace.QName; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.SessionFactory; @@ -104,9 +103,9 @@ public class NodePropertySerializablePatch extends AbstractPatch { Node node = iterator.next(); // retrieve the node properties - Map properties = node.getProperties(); + Map properties = node.getProperties(); // check each property - for (Map.Entry entry : properties.entrySet()) + for (Map.Entry entry : properties.entrySet()) { PropertyValue propertyValue = entry.getValue(); if (propertyValue.getSerializableValue() == null) diff --git a/source/java/org/alfresco/repo/attributes/hibernate/Attributes.hbm.xml b/source/java/org/alfresco/repo/attributes/hibernate/Attributes.hbm.xml index 2503d115bd..367025745a 100644 --- a/source/java/org/alfresco/repo/attributes/hibernate/Attributes.hbm.xml +++ b/source/java/org/alfresco/repo/attributes/hibernate/Attributes.hbm.xml @@ -14,7 +14,7 @@ - - + - + - + - + - + diff --git a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml index 843a9637b7..6307b0f666 100644 --- a/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml +++ b/source/java/org/alfresco/repo/audit/hibernate/Audit.hbm.xml @@ -49,9 +49,9 @@ - - - + + + diff --git a/source/java/org/alfresco/repo/avm/AVMDAOs.java b/source/java/org/alfresco/repo/avm/AVMDAOs.java index d71dd2fcc1..f277a8eb54 100644 --- a/source/java/org/alfresco/repo/avm/AVMDAOs.java +++ b/source/java/org/alfresco/repo/avm/AVMDAOs.java @@ -7,6 +7,7 @@ import org.alfresco.repo.attributes.AttributeDAO; import org.alfresco.repo.attributes.GlobalAttributeEntryDAO; import org.alfresco.repo.attributes.ListEntryDAO; import org.alfresco.repo.attributes.MapEntryDAO; +import org.alfresco.repo.domain.QNameDAO; /** * This is the (shudder) global context for AVM. It a rendezvous @@ -44,6 +45,11 @@ public class AVMDAOs */ public AVMNodeDAO fAVMNodeDAO; + /** + * The QName DAO + */ + public QNameDAO fQNameDAO; + /** * The AVMStore DAO. */ @@ -102,6 +108,11 @@ public class AVMDAOs fAVMNodeDAO = nodeDAO; } + public void setQnameDAO(QNameDAO qnameDAO) + { + this.fQNameDAO = qnameDAO; + } + /** * @param childEntryDAO the fChildEntryDAO to set */ diff --git a/source/java/org/alfresco/repo/avm/AVMNode.java b/source/java/org/alfresco/repo/avm/AVMNode.java index 26f5bee91d..4f6dd7fc7a 100644 --- a/source/java/org/alfresco/repo/avm/AVMNode.java +++ b/source/java/org/alfresco/repo/avm/AVMNode.java @@ -28,7 +28,6 @@ import java.util.Set; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; -import org.alfresco.service.namespace.QName; /** * The Interface for versionable objects. @@ -153,35 +152,35 @@ public interface AVMNode /** * Set a property. - * @param name The name of the property. - * @param value The value to set. + * @param qnameEntityId the ID of the QName to set + * @param value The value to set. */ - public void setProperty(QName name, PropertyValue value); + public void setProperty(Long qnameEntityId, PropertyValue value); /** * Set a collection of properties on this node. - * @param properties The Map of QNames to PropertyValues. + * @param properties The Map of QName entity IDs to PropertyValues. */ - public void setProperties(Map properties); + public void setProperties(Map properties); /** * Get a property by name. * @param name The name of the property to get. * @return A PropertyValue */ - public PropertyValue getProperty(QName name); + public PropertyValue getProperty(Long name); /** * Get all the properties associated with this node. * @return A Map of QNames to PropertyValues. */ - public Map getProperties(); + public Map getProperties(); /** * Delete a property from this node. - * @param name The name of the property. + * @param qnameEntityId the ID of the QName to delete */ - public void deleteProperty(QName name); + public void deleteProperty(Long qnameEntityId); /** * Delete all properties from this node. @@ -232,15 +231,15 @@ public interface AVMNode /** * Get the Aspects that this node has. - * @return A Set of Aspects names. + * @return A Set of Aspects IDs. */ - public Set getAspects(); + public Set getAspects(); /** * Add properties to those that already exist. * @param properties The properties to add. */ - public void addProperties(Map properties); + public void addProperties(Map properties); /** * Get the Basic Attributes on this node. diff --git a/source/java/org/alfresco/repo/avm/AVMNodeImpl.java b/source/java/org/alfresco/repo/avm/AVMNodeImpl.java index a7ee61b1bb..50b8d26584 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeImpl.java @@ -34,7 +34,6 @@ import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.permissions.ACLCopyMode; import org.alfresco.service.cmr.avm.AVMReadOnlyException; -import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -47,7 +46,7 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable { private static Log fgLogger = LogFactory.getLog(AVMNodeImpl.class); - protected static final boolean DEBUG = true; + protected static final boolean DEBUG = fgLogger.isDebugEnabled(); /** * The Object ID. @@ -92,17 +91,17 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable /** * The Aspects that belong to this node. */ - private Set fAspects; + private Set fAspects; - private Map fProperties; + private Map fProperties; /** * Default constructor. */ protected AVMNodeImpl() { - fAspects = new HashSet(); - fProperties = new HashMap(); + fAspects = new HashSet(); + fProperties = new HashMap(); } /** @@ -113,8 +112,8 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable protected AVMNodeImpl(long id, AVMStore store) { - fAspects = new HashSet(); - fProperties = new HashMap(); + fAspects = new HashSet(); + fProperties = new HashMap(); fID = id; fVersionID = -1; fIsRoot = false; @@ -351,8 +350,8 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable */ protected void copyProperties(AVMNode other) { - fProperties = new HashMap(); - for (Map.Entry entry : other.getProperties().entrySet()) + fProperties = new HashMap(); + for (Map.Entry entry : other.getProperties().entrySet()) { fProperties.put(entry.getKey(), entry.getValue()); } @@ -364,7 +363,7 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable */ protected void copyAspects(AVMNode other) { - fAspects = new HashSet(other.getAspects()); + fAspects = new HashSet(other.getAspects()); } protected void copyACLs(AVMNode other, Long parentAcl, ACLCopyMode mode) @@ -392,18 +391,18 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable * @param name The name of the property. * @param value The value to set. */ - public void setProperty(QName name, PropertyValue value) + public void setProperty(Long qnameEntityId, PropertyValue value) { if (DEBUG) { checkReadOnly(); } - fProperties.put(name, value); + fProperties.put(qnameEntityId, value); } - public void addProperties(Map properties) + public void addProperties(Map properties) { - for (Map.Entry entry : properties.entrySet()) + for (Map.Entry entry : properties.entrySet()) { fProperties.put(entry.getKey(), entry.getValue()); } @@ -413,7 +412,7 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable * Set a collection of properties on this node. * @param properties The Map of QNames to PropertyValues. */ - public void setProperties(Map properties) + public void setProperties(Map properties) { fProperties = properties; } @@ -423,16 +422,15 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable * @param name The name of the property. * @return The PropertyValue or null if non-existent. */ - public PropertyValue getProperty(QName name) + public PropertyValue getProperty(Long qnameEntityId) { - return fProperties.get(name); + return fProperties.get(qnameEntityId); } /** - * Get all the properties associated with this node. - * @return A Map of QNames to PropertyValues. + * {@inheritDoc} */ - public Map getProperties() + public Map getProperties() { return fProperties; } @@ -441,13 +439,13 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable * Delete a property from this node. * @param name The name of the property. */ - public void deleteProperty(QName name) + public void deleteProperty(Long qnameEntityId) { if (DEBUG) { checkReadOnly(); } - fProperties.remove(name); + fProperties.remove(qnameEntityId); } /** @@ -521,7 +519,7 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable /* (non-Javadoc) * @see org.alfresco.repo.avm.AVMNode#getAspects() */ - public Set getAspects() + public Set getAspects() { return fAspects; } @@ -530,7 +528,7 @@ public abstract class AVMNodeImpl implements AVMNode, Serializable * Set the aspects on this node. * @param aspects */ - public void setAspects(Set aspects) + public void setAspects(Set aspects) { fAspects = aspects; } diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 1caa4ad5f3..818485f108 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -39,6 +39,7 @@ import org.alfresco.model.WCMModel; import org.alfresco.repo.content.ContentStore; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.QNameDAO; import org.alfresco.repo.security.permissions.ACLCopyMode; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; @@ -105,6 +106,8 @@ public class AVMRepository * The Lookup Cache instance. */ private LookupCache fLookupCache; + + private QNameDAO qnameDAO; private AVMStoreDAO fAVMStoreDAO; @@ -214,6 +217,11 @@ public class AVMRepository fPurgeVersionTxnListener = listener; } + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + public void setAvmStoreDAO(AVMStoreDAO dao) { fAVMStoreDAO = dao; @@ -2318,7 +2326,7 @@ public class AVMRepository Map results = new HashMap(); for (AVMStoreProperty prop : matches) { - results.put(prop.getName(), prop.getValue()); + results.put(prop.getName().getQName(), prop.getValue()); } return results; } @@ -2346,7 +2354,7 @@ public class AVMRepository pairs = new HashMap(); results.put(storeName, pairs); } - pairs.put(prop.getName(), prop.getValue()); + pairs.put(prop.getName().getQName(), prop.getValue()); } return results; } @@ -3096,7 +3104,10 @@ public class AVMRepository { throw new AccessDeniedException("Not allowed to read properties: " + desc); } - return node.getProperties(); + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + @SuppressWarnings("unchecked") + Map converted = (Map) qnameDAO.convertIdMapToQNameMap(node.getProperties()); + return converted; } public ContentData getContentDataForRead(AVMNodeDescriptor desc) @@ -3129,8 +3140,10 @@ public class AVMRepository { throw new AccessDeniedException("Not allowed to read properties: " + desc); } - Set aspects = node.getAspects(); - return aspects; + Set aspectIds = node.getAspects(); + // Convert to QNames + Set aspectQNames = qnameDAO.convertIdsToQNames(aspectIds); + return aspectQNames; } /** @@ -3163,22 +3176,32 @@ public class AVMRepository } PermissionContext context = new PermissionContext(type); context.addDynamicAuthorityAssignment(node.getBasicAttributes().getOwner(), PermissionService.OWNER_AUTHORITY); - context.getAspects().addAll(node.getAspects()); - Map props = node.getProperties(); - Map properties = new HashMap(5); - for (Map.Entry entry : props.entrySet()) + // Pass in node aspects + Set nodeAspectQNameIds = node.getAspects(); + Set contextQNames = context.getAspects(); + for (Long nodeAspectQNameId : nodeAspectQNameIds) { - PropertyDefinition def = fDictionaryService.getProperty(entry.getKey()); + QName qname = qnameDAO.getQName(nodeAspectQNameId); + contextQNames.add(qname); + } + // Pass in node properties + Map nodeProperties = node.getProperties(); + Map contextProperties = new HashMap(5); + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + for (Map.Entry entry : nodeProperties.entrySet()) + { + QName qname = qnameDAO.getQName(entry.getKey()); + PropertyDefinition def = fDictionaryService.getProperty(qname); if (def == null) { - properties.put(entry.getKey(), entry.getValue().getValue(DataTypeDefinition.ANY)); + contextProperties.put(qname, entry.getValue().getValue(DataTypeDefinition.ANY)); } else { - properties.put(entry.getKey(), entry.getValue().getValue(def.getDataType().getName())); + contextProperties.put(qname, entry.getValue().getValue(def.getDataType().getName())); } } - context.getProperties().putAll(properties); + context.getProperties().putAll(contextProperties); Long aclId = null; if(acl != null) { diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index a95f26f64d..a2fc6ddb8b 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -101,7 +101,7 @@ import org.alfresco.util.Pair; */ public class AVMServiceTest extends AVMServiceTestBase { - public void testRename6() + public void testRename6() throws Exception { try { @@ -133,11 +133,11 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - public void testSpacesInStoreNames() + public void testSpacesInStoreNames() throws Exception { try { @@ -148,11 +148,11 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - public void testHeadPathsInLayers() + public void testHeadPathsInLayers() throws Exception { try { @@ -176,14 +176,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Minimal testing of Locking Aware service. */ - public void testLockingAwareService() + public void testLockingAwareService() throws Exception { AVMService oldService = fService; fService = (AVMService)fContext.getBean("AVMLockingAwareService"); @@ -218,7 +218,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } finally { @@ -232,7 +232,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test version by date lookup. */ - public void testVersionByDate() + public void testVersionByDate() throws Exception { try { @@ -256,14 +256,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test properties. */ - public void testProperties() + public void testProperties() throws Exception { try { @@ -355,14 +355,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test getStoreVersionRootPaths(). */ - public void testGetStoreVersionRootPaths() + public void testGetStoreVersionRootPaths() throws Exception { try { @@ -394,13 +394,13 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - public void testForceCopyDeleted() + public void testForceCopyDeleted() throws Exception { try { @@ -413,11 +413,11 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - public void testLayerSnapshots() + public void testLayerSnapshots() throws Exception { try { @@ -438,11 +438,11 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - public void testBranchLayerSnapshot() + public void testBranchLayerSnapshot() throws Exception { try { @@ -475,14 +475,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test Deployment. */ - public void testDeployment() + public void testDeployment() throws Exception { try { @@ -587,11 +587,11 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } - protected void runQueriesForCreateAndDeploy(String store) + protected void runQueriesForCreateAndDeploy(String store) throws Exception { StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); @@ -1061,7 +1061,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test of GUIDs on AVM Nodes. */ - public void testGuids() + public void testGuids() throws Exception { try { @@ -1074,14 +1074,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test the revert to version action. */ - public void testRevertToVersionAction() + public void testRevertToVersionAction() throws Exception { try { @@ -1168,14 +1168,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test version numbering. */ - public void testVersionNumbering() + public void testVersionNumbering() throws Exception { try { @@ -1197,14 +1197,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test relinking of nodes to history. */ - public void testHistoryRelink() + public void testHistoryRelink() throws Exception { try { @@ -1230,14 +1230,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test renaming a store. */ - public void testRenameStore() + public void testRenameStore() throws Exception { try { @@ -1251,14 +1251,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test copy. */ - public void testCopy() + public void testCopy() throws Exception { try { @@ -1346,7 +1346,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } @@ -1681,7 +1681,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test cyclic lookup behavior. */ - public void testCyclicLookup() + public void testCyclicLookup() throws Exception { try { @@ -1707,14 +1707,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test getting all paths for a node. */ - public void testGetPaths() + public void testGetPaths() throws Exception { try { @@ -1744,14 +1744,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test partial flatten. */ - public void testPartialFlatten() + public void testPartialFlatten() throws Exception { try { @@ -1773,14 +1773,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test getIndirection. */ - public void testGetIndirection() + public void testGetIndirection() throws Exception { try { @@ -1798,14 +1798,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test the revert list action. */ - public void testRevertListAction() + public void testRevertListAction() throws Exception { try { @@ -1844,14 +1844,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test the undo list action. */ - public void testUndoListAction() + public void testUndoListAction() throws Exception { try { @@ -1889,14 +1889,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(); - fail(); + throw e; } } /** * Test the promote action. */ - public void testPromoteAction() + public void testPromoteAction() throws Exception { try { @@ -1931,14 +1931,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test a noodle update. */ - public void testNoodleUpdate() + public void testNoodleUpdate() throws Exception { try { @@ -1957,14 +1957,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test the SimpleAVMSubmitAction. */ - public void testSubmitAction() + public void testSubmitAction() throws Exception { try { @@ -2005,14 +2005,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test one argument remove. */ - public void testOneArgRemove() + public void testOneArgRemove() throws Exception { try { @@ -2187,7 +2187,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test that non head version sources are update correctly. */ - public void testVersionUpdate() + public void testVersionUpdate() throws Exception { try { @@ -2215,14 +2215,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test that an update forces a snapshot on the source. */ - public void testUpdateSnapshot() + public void testUpdateSnapshot() throws Exception { try { @@ -2246,14 +2246,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test that branching forces a snapshot on the source repository. */ - public void testBranchSnapshot() + public void testBranchSnapshot() throws Exception { try { @@ -2269,14 +2269,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test bulk update. */ - public void testBulkUpdate() + public void testBulkUpdate() throws Exception { try { @@ -2319,14 +2319,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test the flatten operation, with a little bit of compare and update. */ - public void testFlatten() + public void testFlatten() throws Exception { try { @@ -2355,14 +2355,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test of Descriptor indirection field. */ - public void testDescriptorIndirection() + public void testDescriptorIndirection() throws Exception { try { @@ -2383,14 +2383,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test AVMSyncService update. */ - public void testUpdate() + public void testUpdate() throws Exception { try { @@ -2540,14 +2540,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test link AVMService call. */ - public void testLink() + public void testLink() throws Exception { try { @@ -2618,14 +2618,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test goofy paths. */ - public void testGoofyPaths() + public void testGoofyPaths() throws Exception { try { @@ -2636,14 +2636,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test getting deleted names. */ - public void testGetDeleted() + public void testGetDeleted() throws Exception { try { @@ -2664,14 +2664,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test directly contained listing. */ - public void testListingDirect() + public void testListingDirect() throws Exception { try { @@ -2698,14 +2698,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test layering info. */ - public void testLayeringInfo() + public void testLayeringInfo() throws Exception { try { @@ -2743,14 +2743,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Another test of renaming in a layer. */ - public void testRenameLayer2() + public void testRenameLayer2() throws Exception { try { @@ -2836,14 +2836,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Yet another test around rename in layers. */ - public void testRenameLayer3() + public void testRenameLayer3() throws Exception { try { @@ -2893,14 +2893,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test the uncover operation. */ - public void testUncover() + public void testUncover() throws Exception { try { @@ -2969,14 +2969,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Another test of renaming in a layer. */ - public void testRenameLayer4() + public void testRenameLayer4() throws Exception { try { @@ -3031,14 +3031,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test branching within branches. */ - public void testBranchesInBranches() + public void testBranchesInBranches() throws Exception { try { @@ -3093,14 +3093,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test layers inside of layers. */ - public void testLayersInLayers() + public void testLayersInLayers() throws Exception { try { @@ -3206,14 +3206,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test behavior when one branches a layer. */ - public void testLayerAndBranch() + public void testLayerAndBranch() throws Exception { try { @@ -3303,14 +3303,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test scenario in which something is renamed from inside one independent layer to another. */ - public void testRenameLayerToLayer() + public void testRenameLayerToLayer() throws Exception { try { @@ -3355,21 +3355,21 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test Nothing. Just make sure set up works. */ - public void testNothing() + public void testNothing() throws Exception { } /** * Test making a simple directory. */ - public void testCreateDirectory() + public void testCreateDirectory() throws Exception { try { @@ -3393,14 +3393,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a file. */ - public void testCreateFile() + public void testCreateFile() throws Exception { try { @@ -3472,14 +3472,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a branch. */ - public void testCreateBranch() + public void testCreateBranch() throws Exception { try { @@ -3501,14 +3501,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a layer. */ - public void testCreateLayer() + public void testCreateLayer() throws Exception { try { @@ -3532,14 +3532,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a layered file. */ - public void testCreateLayeredFile() + public void testCreateLayeredFile() throws Exception { try { @@ -3570,14 +3570,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test rename. */ - public void testRename() + public void testRename() throws Exception { try { @@ -3744,14 +3744,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test remove. */ - public void testRemove() + public void testRemove() throws Exception { try { @@ -3915,14 +3915,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test branching from one AVMStore to another. */ - public void testBranchAcross() + public void testBranchAcross() throws Exception { try { @@ -3952,14 +3952,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a layer across AVMStores. */ - public void testLayerAcross() + public void testLayerAcross() throws Exception { try { @@ -3991,14 +3991,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test rename across AVMStores. */ - public void testRenameAcross() + public void testRenameAcross() throws Exception { try { @@ -4017,14 +4017,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test COW in various circumstances. */ - public void testDeepCOW() + public void testDeepCOW() throws Exception { try { @@ -4073,14 +4073,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test branching and layering interaction. */ - public void testBranchAndLayer() + public void testBranchAndLayer() throws Exception { try { @@ -4122,14 +4122,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test basic Layering. */ - public void testLayering() + public void testLayering() throws Exception { try { @@ -4190,14 +4190,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test rename within a layer. */ - public void testRenameInLayer() + public void testRenameInLayer() throws Exception { try { @@ -4234,14 +4234,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test behavior of multiply layers not in register. */ - public void testMultiLayerUnregistered() + public void testMultiLayerUnregistered() throws Exception { try { @@ -4281,14 +4281,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test makePrimary. */ - public void testMakePrimary() + public void testMakePrimary() throws Exception { try { @@ -4339,14 +4339,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test retargeting a directory. */ - public void testRetarget() + public void testRetarget() throws Exception { try { @@ -4401,14 +4401,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test rename between branches. */ - public void testRenameBranchToBranch() + public void testRenameBranchToBranch() throws Exception { try { @@ -4463,14 +4463,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test a branch being created in a layer. */ - public void testBranchIntoLayer() + public void testBranchIntoLayer() throws Exception { try { @@ -4519,14 +4519,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test renaming into a layer. */ - public void testRenameIntoLayer() + public void testRenameIntoLayer() throws Exception { try { @@ -4576,14 +4576,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test proper indirection behavior. */ - public void testIndirectionBehavior() + public void testIndirectionBehavior() throws Exception { try { @@ -4649,14 +4649,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test reading of versioned content via a layer. */ - public void testVersionedRead() + public void testVersionedRead() throws Exception { try { @@ -4684,14 +4684,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test rename of an overlayed directory contained in an overlayed directory. */ - public void testRenameLayerInLayer() + public void testRenameLayerInLayer() throws Exception { try { @@ -4715,14 +4715,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Yet another rename from layer to layer test. */ - public void testAnotherRename() + public void testAnotherRename() throws Exception { try { @@ -4769,12 +4769,12 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } - protected void runQueriesForInitialRenameStructure(String store) + protected void runQueriesForInitialRenameStructure(String store) throws Exception { StoreRef storeRef = AVMNodeConverter.ToStoreRef(store); @@ -4994,7 +4994,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test rename behavior of an overlayed file withing a layer. */ - public void testFileRenameLayer() + public void testFileRenameLayer() throws Exception { try { @@ -5021,7 +5021,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } @@ -5033,13 +5033,13 @@ public class AVMServiceTest extends AVMServiceTestBase * "main:/a/b/c/foo", "r"); byte [] buff = new byte[256]; assertTrue(file.read(buff) >= 20); file.close(); file = * fService.getRandomAccess(-1, "main:/a/b/c/bar", "rw"); for (int i = 0; i < 256; i++) { buff[i] = (byte)i; } * file.write(buff); file.close(); fService.createSnapshot("main", null, null); } catch (Exception e) { - * e.printStackTrace(System.err); fail(); } } + * e.printStackTrace(System.err); throw e; } } */ /** * Test COW during long operations. */ - public void testCOWLongOps() + public void testCOWLongOps() throws Exception { try { @@ -5062,14 +5062,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test new lookup methods. */ - public void testLookup() + public void testLookup() throws Exception { try { @@ -5087,7 +5087,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } @@ -5095,7 +5095,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test AVMStore functions. */ - public void testAVMStore() + public void testAVMStore() throws Exception { try { @@ -5120,14 +5120,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test opacity and manipulations. */ - public void testOpacity() + public void testOpacity() throws Exception { try { @@ -5169,14 +5169,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test common ancestor. */ - public void testCommonAncestor() + public void testCommonAncestor() throws Exception { try { @@ -5195,7 +5195,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } @@ -5203,7 +5203,7 @@ public class AVMServiceTest extends AVMServiceTestBase /** * Test properties on stores. */ - public void testStoreProperties() + public void testStoreProperties() throws Exception { try { @@ -5235,14 +5235,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test Aspect Name storage. */ - public void testAspectNames() + public void testAspectNames() throws Exception { try { @@ -5305,14 +5305,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test case insensitivity. */ - public void testCaseInsensitive() + public void testCaseInsensitive() throws Exception { try { @@ -5330,14 +5330,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test ACLs. */ - public void testACLs() + public void testACLs() throws Exception { try { @@ -5359,14 +5359,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test FileFolderService with AVM. */ - public void testFileFolderService() + public void testFileFolderService() throws Exception { try { @@ -5398,21 +5398,21 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test overwriting without snapshots in between. */ - public void testOverwrite() + public void testOverwrite() throws Exception { try { setupBasicTree(); class TxnCallback implements RetryingTransactionHelper.RetryingTransactionCallback { - public Object execute() + public Object execute() throws Exception { try { @@ -5462,8 +5462,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (IOException e) { e.printStackTrace(); - fail(); - return null; + throw e; } } } @@ -5475,14 +5474,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test creating a file over a ghost. */ - public void testCreateOverDeleted() + public void testCreateOverDeleted() throws Exception { try { @@ -5493,14 +5492,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } /** * Test lookup and listing of deleted files. */ - public void testDeleted() + public void testDeleted() throws Exception { try { @@ -5548,14 +5547,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + } } /** * Test Store property querying. */ - public void testStorePropertyQuerying() + public void testStorePropertyQuerying() throws Exception { try { @@ -5581,14 +5580,14 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + } } /** * Test AVMSyncService resetLayer. */ - public void testResetLayer() + public void testResetLayer() throws Exception { try { @@ -5604,7 +5603,7 @@ public class AVMServiceTest extends AVMServiceTestBase catch (Exception e) { e.printStackTrace(System.err); - fail(); + throw e; } } diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index ae17679e40..493c81088d 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -43,6 +43,8 @@ import org.alfresco.repo.avm.util.SimplePath; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.security.permissions.ACLCopyMode; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMException; @@ -367,11 +369,26 @@ public class AVMStoreImpl implements AVMStore, Serializable dir.putChild(name, newDir); if (aspects != null) { - newDir.getAspects().addAll(aspects); + // Convert the aspect QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Set aspectQNameEntityIds = newDir.getAspects(); + for (QName aspectQName : aspects) + { + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectQName).getId(); + aspectQNameEntityIds.add(qnameEntityId); + } } if (properties != null) { - newDir.getProperties().putAll(properties); + // Convert the property QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); + for (Map.Entry entry : properties.entrySet()) + { + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); + } + newDir.getProperties().putAll(propertiesByQNameEntityId); } DbAccessControlList acl = dir.getAcl(); newDir.setAcl(acl != null ? acl.getCopy(acl.getId(), ACLCopyMode.INHERIT) : null); @@ -503,11 +520,26 @@ public class AVMStoreImpl implements AVMStore, Serializable "UTF-8")); if (aspects != null) { - file.getAspects().addAll(aspects); + // Convert the aspect QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Set aspectQNameEntityIds = file.getAspects(); + for (QName aspectQName : aspects) + { + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectQName).getId(); + aspectQNameEntityIds.add(qnameEntityId); + } } if (properties != null) { - file.getProperties().putAll(properties); + // Convert the property QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); + for (Map.Entry entry : properties.entrySet()) + { + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); + } + file.getProperties().putAll(propertiesByQNameEntityId); } DbAccessControlList acl = dir.getAcl(); file.setAcl(acl != null ? acl.getCopy(acl.getId(), ACLCopyMode.INHERIT) : null); @@ -1159,7 +1191,9 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to write: " + path); } - node.setProperty(name, value); + QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(name); + + node.setProperty(qnameEntity.getId(), value); node.setGuid(GUID.generate()); } @@ -1180,7 +1214,18 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to write: " + path); } - node.addProperties(properties); + if (properties != null) + { + // Convert the property QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); + for (Map.Entry entry : properties.entrySet()) + { + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); + } + node.addProperties(propertiesByQNameEntityId); + } node.setGuid(GUID.generate()); } @@ -1203,8 +1248,19 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to read: " + path); } - PropertyValue prop = node.getProperty(name); - return prop; + // Convert the QName + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(name); + if (qnameEntity == null) + { + // No such QName + return null; + } + else + { + PropertyValue prop = node.getProperty(qnameEntity.getId()); + return prop; + } } /** @@ -1225,8 +1281,12 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to read: " + path); } - Map props = node.getProperties(); - return props; + Map props = node.getProperties(); + // Convert to QNames + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + @SuppressWarnings("unchecked") + Map convertedProps = (Map) qnameDAO.convertIdMapToQNameMap(props); + return convertedProps; } /** @@ -1247,7 +1307,17 @@ public class AVMStoreImpl implements AVMStore, Serializable throw new AccessDeniedException("Not allowed to write: " + path); } node.setGuid(GUID.generate()); - node.deleteProperty(name); + + // convert the QName + QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getQNameEntity(name); + if (qnameEntity == null) + { + // No such property + } + else + { + node.deleteProperty(qnameEntity.getId()); + } } /** @@ -1277,9 +1347,10 @@ public class AVMStoreImpl implements AVMStore, Serializable */ public void setProperty(QName name, PropertyValue value) { + QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(name); AVMStoreProperty prop = new AVMStorePropertyImpl(); prop.setStore(this); - prop.setName(name); + prop.setName(qnameEntity); prop.setValue(value); AVMDAOs.Instance().fAVMStorePropertyDAO.save(prop); } @@ -1322,7 +1393,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Map retVal = new HashMap(); for (AVMStoreProperty prop : props) { - retVal.put(prop.getName(), prop.getValue()); + retVal.put(prop.getName().getQName(), prop.getValue()); } return retVal; } @@ -1454,7 +1525,11 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to write: " + path); } - node.getAspects().add(aspectName); + // Convert the aspect QNames to entities + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectName).getId(); + // Convert the + node.getAspects().add(qnameEntityId); node.setGuid(GUID.generate()); } @@ -1476,8 +1551,10 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to read properties: " + path); } - Set aspects = node.getAspects(); - return aspects; + Set aspects = node.getAspects(); + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Set aspectQNames = qnameDAO.convertIdsToQNames(aspects); + return aspectQNames; } /** @@ -1497,13 +1574,20 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to write properties: " + path); } - node.getAspects().remove(aspectName); + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + // Get the persistent ID for the QName and remove from the set + QNameEntity qnameEntity = qnameDAO.getQNameEntity(aspectName); + if (qnameEntity != null) + { + node.getAspects().remove(qnameEntity.getId()); + } AspectDefinition def = RawServices.Instance().getDictionaryService().getAspect(aspectName); Map properties = def.getProperties(); for (QName name : properties.keySet()) { - node.getProperties().remove(name); + QNameEntity propertyQNameEntity = qnameDAO.getQNameEntity(name); + node.getProperties().remove(propertyQNameEntity.getId()); } node.setGuid(GUID.generate()); } @@ -1527,8 +1611,17 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to read properties: " + path); } - boolean has = node.getAspects().contains(aspectName); - return has; + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + // Get the persistent ID for the QName and remove from the set + QNameEntity qnameEntity = qnameDAO.getQNameEntity(aspectName); + if (qnameEntity != null) + { + return node.getAspects().contains(qnameEntity.getId()); + } + else + { + return false; + } } /** @@ -1630,9 +1723,13 @@ public class AVMStoreImpl implements AVMStore, Serializable toLink.changeAncestor(child); toLink.setVersionID(child.getVersionID() + 1); // TODO This really shouldn't be here. Leaking layers. - toLink.getAspects().add(WCMModel.ASPECT_REVERTED); + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + QNameEntity revertedQNameEntity = qnameDAO.getOrCreateQNameEntity(WCMModel.ASPECT_REVERTED); + toLink.getAspects().add(revertedQNameEntity.getId()); PropertyValue value = new PropertyValue(null, toRevertTo.getId()); - toLink.setProperty(WCMModel.PROP_REVERTED_ID, value); + + QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(WCMModel.PROP_REVERTED_ID); + toLink.setProperty(qnameEntity.getId(), value); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/avm/AVMStoreProperty.java b/source/java/org/alfresco/repo/avm/AVMStoreProperty.java index ce4dfc60ef..d2e3e38a55 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreProperty.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreProperty.java @@ -24,7 +24,7 @@ package org.alfresco.repo.avm; import org.alfresco.repo.domain.PropertyValue; -import org.alfresco.service.namespace.QName; +import org.alfresco.repo.domain.QNameEntity; /** * Arbitrary properties associated with AVMStores. @@ -46,15 +46,15 @@ public interface AVMStoreProperty /** * Set the name of the property. - * @param name The QName for the property. + * @param qnameEntity The QNameEntity for the property. */ - public void setName(QName name); + public void setName(QNameEntity qnameEntity); /** * Get the name of this property. - * @return The QName of this property. + * @return The QNameEntity of this property. */ - public QName getName(); + public QNameEntity getName(); /** * Set the actual property value. diff --git a/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java b/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java index af24dbf993..73b551334d 100644 --- a/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java @@ -26,7 +26,7 @@ package org.alfresco.repo.avm; import java.io.Serializable; import org.alfresco.repo.domain.PropertyValue; -import org.alfresco.service.namespace.QName; +import org.alfresco.repo.domain.QNameEntity; /** * Simple bean to hold properties attached to AVMStores. @@ -49,7 +49,7 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable /** * The name of the property. */ - private QName fName; + private QNameEntity name; /** * The actual PropertyValue. @@ -61,21 +61,19 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable } /** - * Get the name of the property. - * @return The QName for the property. + * {@inheritDoc} */ - public QName getName() + public QNameEntity getName() { - return fName; + return name; } /** - * Set the name of the property. - * @param name The QName of the property. + * {@inheritDoc} */ - public void setName(QName name) + public void setName(QNameEntity name) { - fName = name; + this.name = name; } /** @@ -144,13 +142,13 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable return false; } AVMStoreProperty o = (AVMStoreProperty)other; - return fStore.equals(o.getStore()) && fName.equals(o.getName()); + return fStore.equals(o.getStore()) && name.equals(o.getName()); } @Override public int hashCode() { - return fStore.hashCode() + fName.hashCode(); + return fStore.hashCode() + name.hashCode(); } } diff --git a/source/java/org/alfresco/repo/avm/PlainFileNodeImpl.java b/source/java/org/alfresco/repo/avm/PlainFileNodeImpl.java index 59f327c42b..80a6fc9b7c 100644 --- a/source/java/org/alfresco/repo/avm/PlainFileNodeImpl.java +++ b/source/java/org/alfresco/repo/avm/PlainFileNodeImpl.java @@ -24,7 +24,6 @@ package org.alfresco.repo.avm; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -36,7 +35,6 @@ import org.alfresco.service.cmr.avm.AVMException; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.namespace.QName; /** @@ -119,8 +117,8 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode public PlainFileNodeImpl(AVMStore store, BasicAttributes attrs, ContentData content, - Map props, - Set aspects, + Map props, + Set aspects, DbAccessControlList acl, int versionID, Long parentAcl, ACLCopyMode mode) { @@ -131,7 +129,7 @@ class PlainFileNodeImpl extends FileNodeImpl implements PlainFileNode AVMDAOs.Instance().fAVMNodeDAO.save(this); AVMDAOs.Instance().fAVMNodeDAO.flush(); addProperties(props); - setAspects(new HashSet(aspects)); + setAspects(new HashSet(aspects)); if (acl != null) { setAcl(acl.getCopy(parentAcl, mode)); diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml b/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml index ed9109a46f..beb38e0328 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml +++ b/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml @@ -2,7 +2,7 @@ - + + + + + + + + + + + @@ -192,27 +233,9 @@ - + + - - - - - - - - - - - - - - - - - - - @@ -229,17 +252,8 @@ - - - - - - - - + + diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java b/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java index ac583fcd87..1e89041624 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java +++ b/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java @@ -28,6 +28,8 @@ import java.util.List; import org.alfresco.repo.avm.AVMStore; import org.alfresco.repo.avm.AVMStoreProperty; import org.alfresco.repo.avm.AVMStorePropertyDAO; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.service.namespace.QName; import org.hibernate.Query; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; @@ -38,6 +40,16 @@ import org.springframework.orm.hibernate3.support.HibernateDaoSupport; */ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMStorePropertyDAO { + private QNameDAO qnameDAO; + + /** + * Set the DAO for accessing QName entities + */ + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + /** * Persist a property. * @param prop The AVMStoreProperty to persist. @@ -55,11 +67,25 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto */ public AVMStoreProperty get(AVMStore store, QName name) { - Query query = - getSession().createQuery("from AVMStorePropertyImpl asp where asp.store = :store and asp.name = :name"); - query.setEntity("store", store); - query.setParameter("name", name); - return (AVMStoreProperty)query.uniqueResult(); + QNameEntity qnameEntity = qnameDAO.getQNameEntity(name); + if (qnameEntity == null) + { + // No such QName + return null; + } + else + { + Query query = + getSession().createQuery( + "select asp " + + "from AVMStorePropertyImpl asp " + + "where " + + "asp.store = :store and " + + "asp.name = :name"); + query.setEntity("store", store); + query.setParameter("name", qnameEntity); + return (AVMStoreProperty)query.uniqueResult(); + } } /** @@ -85,12 +111,30 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto @SuppressWarnings("unchecked") public List queryByKeyPattern(AVMStore store, QName keyPattern) { + // Get the URI and LocalName parts + String uri = keyPattern.getNamespaceURI(); + if (uri == null || uri.length() == 0) + { + uri = "%"; + } + String localName = keyPattern.getLocalName(); + if (localName == null || localName.length() == 0) + { + localName = "%"; + } Query query = getSession().createQuery( + "select asp " + "from AVMStorePropertyImpl asp " + - "where asp.store = :store and asp.name like :name"); + "join asp.name name " + + "join name.namespace namespace " + + "where " + + "asp.store = :store and " + + "namespace.uri like :uri and " + + "name.localName like :localName"); query.setEntity("store", store); - query.setParameter("name", keyPattern); + query.setParameter("uri", uri); + query.setParameter("localName", localName); return (List)query.list(); } @@ -102,11 +146,28 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto @SuppressWarnings("unchecked") public List queryByKeyPattern(QName keyPattern) { + // Get the URI and LocalName parts + String uri = keyPattern.getNamespaceURI(); + if (uri == null || uri.length() == 0) + { + uri = "%"; + } + String localName = keyPattern.getLocalName(); + if (localName == null || localName.length() == 0) + { + localName = "%"; + } Query query = getSession().createQuery( - "from AVMStorePropertyImpl asp " + - "where asp.name like :name"); - query.setParameter("name", keyPattern); + "select asp " + + "from AVMStorePropertyImpl asp " + + "join asp.name name " + + "join name.namespace namespace " + + "where " + + "namespace.uri like :uri and " + + "name.localName like :localName"); + query.setParameter("uri", uri); + query.setParameter("localName", localName); return (List)query.list(); } @@ -126,12 +187,16 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto */ public void delete(AVMStore store, QName name) { - Query delete = - getSession().createQuery("delete from AVMStorePropertyImpl asp " + - "where asp.store = :store and asp.name = :name"); - delete.setEntity("store", store); - delete.setParameter("name", name); - delete.executeUpdate(); + QNameEntity qnameEntity = qnameDAO.getQNameEntity(name); + if (qnameEntity != null) + { + Query delete = + getSession().createQuery("delete from AVMStorePropertyImpl asp " + + "where asp.store = :store and asp.name = :name"); + delete.setEntity("store", store); + delete.setParameter("name", qnameEntity); + delete.executeUpdate(); + } } /** diff --git a/source/java/org/alfresco/repo/domain/ChildAssoc.java b/source/java/org/alfresco/repo/domain/ChildAssoc.java index af2562c710..2522b3c481 100644 --- a/source/java/org/alfresco/repo/domain/ChildAssoc.java +++ b/source/java/org/alfresco/repo/domain/ChildAssoc.java @@ -54,9 +54,22 @@ public interface ChildAssoc extends Comparable * {@link #getChild() child} nodes to maintain the inverse association sets */ public void removeAssociation(); - + + /** + * A convenience method to get a reference to this association. + * + * @return Returns a reference to this association + */ public ChildAssociationRef getChildAssocRef(); + /** + * A convenience method to aggregate the qualified name's namespace and localname + * into a single qualified name. + * + * @return Returns the qualified name of the association + */ + public QName getQname(); + public Long getId(); /** @@ -67,17 +80,16 @@ public interface ChildAssoc extends Comparable public Node getParent(); public Node getChild(); + + /** + * @return Returns the type of the association + */ + public QNameEntity getTypeQName(); /** - * @return Returns the qualified name of the association type + * @param typeQName the association's dictionary type */ - public QName getTypeQName(); - - /** - * @param assocTypeQName the qualified name of the association type as defined - * in the data dictionary - */ - public void setTypeQName(QName assocTypeQName); + public void setTypeQName(QNameEntity typeQName); /** * @return Returns the child node name. This may be truncated, in which case it @@ -102,15 +114,25 @@ public interface ChildAssoc extends Comparable public void setChildNodeNameCrc(long crc); /** - * @return Returns the qualified name of this association + * @return Returns the namespace of the association's local QName */ - public QName getQname(); + public NamespaceEntity getQnameNamespace(); + + /** + * @param namespace the namespace of the association's local QName + */ + public void setQnameNamespace(NamespaceEntity namespace); + + /** + * @return Returns the localname of the association's local QName + */ + public String getQnameLocalName(); /** - * @param qname the qualified name of the association + * @param localName the localname of the association's local QName */ - public void setQname(QName qname); - + public void setQnameLocalName(String localName); + public boolean getIsPrimary(); public void setIsPrimary(boolean isPrimary); diff --git a/source/java/org/alfresco/repo/domain/NamespaceEntity.java b/source/java/org/alfresco/repo/domain/NamespaceEntity.java new file mode 100644 index 0000000000..041a4a3d97 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/NamespaceEntity.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain; + +/** + * Represents a persistable Namespace entity. + * + * @author Derek Hulley + * @since 2.2 + */ +public interface NamespaceEntity +{ + Long getId(); + + String getUri(); + + void setUri(String namespaceUrl); +} diff --git a/source/java/org/alfresco/repo/domain/Node.java b/source/java/org/alfresco/repo/domain/Node.java index 3983a97ac7..d95854a79f 100644 --- a/source/java/org/alfresco/repo/domain/Node.java +++ b/source/java/org/alfresco/repo/domain/Node.java @@ -28,7 +28,6 @@ import java.util.Map; import java.util.Set; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; /** * Interface for persistent node objects. @@ -64,13 +63,13 @@ public interface Node public void setUuid(String uuid); - public QName getTypeQName(); + public QNameEntity getTypeQName(); - public void setTypeQName(QName typeQName); + public void setTypeQName(QNameEntity typeQName); - public Set getAspects(); + public Set getAspects(); - public Map getProperties(); + public Map getProperties(); public DbAccessControlList getAccessControlList(); diff --git a/source/java/org/alfresco/repo/domain/NodeAssoc.java b/source/java/org/alfresco/repo/domain/NodeAssoc.java index 69270a1ebd..8147783ae2 100644 --- a/source/java/org/alfresco/repo/domain/NodeAssoc.java +++ b/source/java/org/alfresco/repo/domain/NodeAssoc.java @@ -25,7 +25,6 @@ package org.alfresco.repo.domain; import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.namespace.QName; /** * Represents a generic association between two nodes. The association is named @@ -62,12 +61,12 @@ public interface NodeAssoc public Node getTarget(); /** - * @return Returns the qualified name of this association type + * @return Returns the type of the association */ - public QName getTypeQName(); - + public QNameEntity getTypeQName(); + /** - * @param qname the qualified name of the association type + * @param typeQName the association's dictionary type */ - public void setTypeQName(QName qname); + public void setTypeQName(QNameEntity typeQName); } diff --git a/source/java/org/alfresco/repo/domain/PropertyValue.java b/source/java/org/alfresco/repo/domain/PropertyValue.java index bc917bf6d3..333aba4fd3 100644 --- a/source/java/org/alfresco/repo/domain/PropertyValue.java +++ b/source/java/org/alfresco/repo/domain/PropertyValue.java @@ -74,6 +74,12 @@ public class PropertyValue implements Cloneable, Serializable { NULL { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(0); + } + @Override Serializable convert(Serializable value) { @@ -82,6 +88,12 @@ public class PropertyValue implements Cloneable, Serializable }, BOOLEAN { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(1); + } + @Override Serializable convert(Serializable value) { @@ -90,6 +102,12 @@ public class PropertyValue implements Cloneable, Serializable }, INTEGER { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(2); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -104,6 +122,12 @@ public class PropertyValue implements Cloneable, Serializable }, LONG { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(3); + } + @Override Serializable convert(Serializable value) { @@ -112,6 +136,12 @@ public class PropertyValue implements Cloneable, Serializable }, FLOAT { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(4); + } + @Override Serializable convert(Serializable value) { @@ -120,6 +150,12 @@ public class PropertyValue implements Cloneable, Serializable }, DOUBLE { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(5); + } + @Override Serializable convert(Serializable value) { @@ -128,6 +164,12 @@ public class PropertyValue implements Cloneable, Serializable }, STRING { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(6); + } + /** * Strings longer than the maximum of 1024 characters will be serialized. */ @@ -153,6 +195,12 @@ public class PropertyValue implements Cloneable, Serializable }, DATE { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(7); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -167,6 +215,12 @@ public class PropertyValue implements Cloneable, Serializable }, DB_ATTRIBUTE { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(8); + } + /** class that is able to convert from persisted attributes to normal attributes */ private AttributeConverter attributeConverter = new AttributeConverter(); @@ -179,6 +233,12 @@ public class PropertyValue implements Cloneable, Serializable }, SERIALIZABLE { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(9); + } + @Override Serializable convert(Serializable value) { @@ -187,6 +247,12 @@ public class PropertyValue implements Cloneable, Serializable }, MLTEXT { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(10); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -201,6 +267,12 @@ public class PropertyValue implements Cloneable, Serializable }, CONTENT { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(11); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -215,6 +287,12 @@ public class PropertyValue implements Cloneable, Serializable }, NODEREF { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(12); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -229,6 +307,12 @@ public class PropertyValue implements Cloneable, Serializable }, CHILD_ASSOC_REF { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(13); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -243,6 +327,12 @@ public class PropertyValue implements Cloneable, Serializable }, ASSOC_REF { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(14); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -257,6 +347,12 @@ public class PropertyValue implements Cloneable, Serializable }, QNAME { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(15); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -271,6 +367,12 @@ public class PropertyValue implements Cloneable, Serializable }, PATH { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(16); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -285,6 +387,12 @@ public class PropertyValue implements Cloneable, Serializable }, LOCALE { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(17); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -299,6 +407,12 @@ public class PropertyValue implements Cloneable, Serializable }, VERSION_NUMBER { + @Override + public Integer getOrdinalNumber() + { + return Integer.valueOf(18); + } + @Override protected ValueType getPersistedType(Serializable value) { @@ -312,6 +426,11 @@ public class PropertyValue implements Cloneable, Serializable } }; + /** + * @return Returns the manually-maintained ordinal number for the value + */ + public abstract Integer getOrdinalNumber(); + /** * Override if the type gets persisted in a different format. * @@ -440,9 +559,14 @@ public class PropertyValue implements Cloneable, Serializable /** a mapping from a property type QName to the corresponding value type */ private static Map valueTypesByPropertyType; + /** + * a mapping of {@link ValueType} ordinal number to the enum. This is manually maintained + * and MUST NOT BE CHANGED FOR EXISTING VALUES. + */ + private static Map valueTypesByOrdinalNumber; static { - valueTypesByPropertyType = new HashMap(17); + valueTypesByPropertyType = new HashMap(37); valueTypesByPropertyType.put(DataTypeDefinition.ANY, ValueType.SERIALIZABLE); valueTypesByPropertyType.put(DataTypeDefinition.BOOLEAN, ValueType.BOOLEAN); valueTypesByPropertyType.put(DataTypeDefinition.INT, ValueType.INTEGER); @@ -461,6 +585,21 @@ public class PropertyValue implements Cloneable, Serializable valueTypesByPropertyType.put(DataTypeDefinition.PATH, ValueType.PATH); valueTypesByPropertyType.put(DataTypeDefinition.QNAME, ValueType.QNAME); valueTypesByPropertyType.put(DataTypeDefinition.LOCALE, ValueType.LOCALE); + + valueTypesByOrdinalNumber = new HashMap(37); + for (ValueType valueType : ValueType.values()) + { + Integer ordinalNumber = valueType.getOrdinalNumber(); + if (valueTypesByOrdinalNumber.containsKey(ordinalNumber)) + { + throw new RuntimeException("ValueType has duplicate ordinal number: " + valueType); + } + else if (ordinalNumber.intValue() == -1) + { + throw new RuntimeException("ValueType doesn't have an ordinal number: " + valueType); + } + valueTypesByOrdinalNumber.put(ordinalNumber, valueType); + } } /** the type of the property, prior to serialization persistence */ @@ -627,14 +766,19 @@ public class PropertyValue implements Cloneable, Serializable return sb.toString(); } - public String getActualType() + public Integer getActualType() { - return actualType.toString(); + return actualType == null ? null : actualType.getOrdinalNumber(); } - public void setActualType(String actualType) + public void setActualType(Integer actualType) { - this.actualType = ValueType.valueOf(actualType); + ValueType type = PropertyValue.valueTypesByOrdinalNumber.get(actualType); + if (type == null) + { + logger.error("Unknown property actual type ordinal number: " + actualType); + } + this.actualType = type; } public boolean isMultiValued() @@ -647,13 +791,18 @@ public class PropertyValue implements Cloneable, Serializable this.isMultiValued = isMultiValued; } - public String getPersistedType() + public Integer getPersistedType() { - return persistedType.toString(); + return persistedType == null ? null : persistedType.getOrdinalNumber(); } - public void setPersistedType(String persistedType) + public void setPersistedType(Integer persistedType) { - this.persistedType = ValueType.valueOf(persistedType); + ValueType type = PropertyValue.valueTypesByOrdinalNumber.get(persistedType); + if (type == null) + { + logger.error("Unknown property persisted type ordinal number: " + persistedType); + } + this.persistedType = type; } /** diff --git a/source/java/org/alfresco/repo/domain/QNameDAO.java b/source/java/org/alfresco/repo/domain/QNameDAO.java new file mode 100644 index 0000000000..7e0d25fbde --- /dev/null +++ b/source/java/org/alfresco/repo/domain/QNameDAO.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain; + +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.service.namespace.QName; + +/** + * Data abstraction layer for QName and Namespace entities. + * + * @author Derek Hulley + * @since 2.1 + */ +public interface QNameDAO +{ + /** + * @param id the unique ID of the entity + * @return the namespace entity (never null) + * @throws AlfrescoRuntimeException if the ID provided is invalid + */ + NamespaceEntity getNamespaceEntity(Long id); + + /** + * @param namespaceUri the namespace URI to query for + * @return the namespace entity of null if it doesn't exist + */ + NamespaceEntity getNamespaceEntity(String namespaceUri); + + /** + * Get an existing instance matching the URI or create one if necessary. + * Note that this method should be treated as a write method and should not + * be used in the context of read-only or query methods. + * + * @param namespaceUri the namespace URI to create + * @return the existing namespace entity if found or a new one + */ + NamespaceEntity getOrCreateNamespaceEntity(String namespaceUri); + + /** + * @param namespaceUri the namespace URI to create + * @return Returns the new instance + */ + NamespaceEntity newNamespaceEntity(String namespaceUri); + + /** + * @param id the unique ID of the entity + * @return the QName entity (never null) + * @throws AlfrescoRuntimeException if the ID provided is invalid + */ + QNameEntity getQNameEntity(Long id); + + /** + * @param id the unique ID of the entity + * @return the QName (never null) + * @throws AlfrescoRuntimeException if the ID provided is invalid + */ + QName getQName(Long id); + + /** + * @param qname the QName to query for + * @return the QName entity of null if it doesn't exist + */ + QNameEntity getQNameEntity(QName qname); + + /** + * Get an existing instance matching the QName or create one if necessary. + * Note that this method should be treated as a write method and should not + * be used in the context of read-only or query methods. + * + * @param qname the QName to query for + * @return an existing QName entity if found or a new one + */ + QNameEntity getOrCreateQNameEntity(QName qname); + + /** + * @param qname the QName to create + * @return the new instance + */ + QNameEntity newQNameEntity(QName qname); + + Set convertIdsToQNames(Set ids); + + Map convertIdMapToQNameMap(Map idMap); +} diff --git a/source/java/org/alfresco/repo/domain/QNameDAOTest.java b/source/java/org/alfresco/repo/domain/QNameDAOTest.java new file mode 100644 index 0000000000..5ab3a4c0ce --- /dev/null +++ b/source/java/org/alfresco/repo/domain/QNameDAOTest.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import junit.framework.TestCase; + +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.GUID; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; + +/** + * @see QNameDAO + * + * + * @author Derek Hulley + * @since 2.2 + */ +public class QNameDAOTest extends TestCase +{ + private static Log logger = LogFactory.getLog(QNameDAOTest.class); + + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private RetryingTransactionHelper retryingTransactionHelper; + private QNameDAO dao; + + @Override + public void setUp() throws Exception + { + ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + retryingTransactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); + dao = (QNameDAO) ctx.getBean("qnameDAO"); + } + + @Override + public void tearDown() throws Exception + { + + } + + public void testNewNamespace() throws Exception + { + final String namespaceUri = GUID.generate(); + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public NamespaceEntity execute() throws Throwable + { + NamespaceEntity namespace = dao.getNamespaceEntity(namespaceUri); + assertNull("Namespace should not exist yet", namespace); + // Now make it + namespace = dao.newNamespaceEntity(namespaceUri); + assertNotNull("Namespace should now exist", dao.getNamespaceEntity(namespaceUri)); + // Done + return namespace; + } + }; + retryingTransactionHelper.doInTransaction(callback); + } + + public void testNewQName() throws Exception + { + final String namespaceUri = GUID.generate(); + final String localName = GUID.generate(); + final QName qname = QName.createQName(namespaceUri, localName); + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public QNameEntity execute() throws Throwable + { + QNameEntity qnameEntity = dao.getQNameEntity(qname); + assertNull("QName should not exist yet", qnameEntity); + // Now make it + qnameEntity = dao.newQNameEntity(qname); + assertNotNull("QName should now exist", dao.getQNameEntity(qname)); + // Done + return qnameEntity; + } + }; + retryingTransactionHelper.doInTransaction(callback); + } + + /** + * Forces a bunch of threads to attempt QName creation at exactly the same time + * for their first attempt. The subsequent retries should all succeed by + * finding the QNameEntity. + */ + public void testConcurrentQName() throws Throwable + { + final Random random = new Random(); + final String namespaceUri = GUID.generate(); + int threadCount = 50; + final CountDownLatch readyLatch = new CountDownLatch(threadCount); + final CountDownLatch startLatch = new CountDownLatch(1); + final CountDownLatch doneLatch = new CountDownLatch(threadCount); + final List errors = Collections.synchronizedList(new ArrayList(0)); + final RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public QNameEntity execute() throws Throwable + { + String threadName = Thread.currentThread().getName(); + // We use a common namespace and assign one of a limited set of random numbers + // as the localname. The transaction waits a bit before going for the commit + // so as to ensure a good few others are trying the same thing. + String localName = "" + random.nextInt(10); + QName qname = QName.createQName(namespaceUri, localName); + QNameEntity qnameEntity = dao.getQNameEntity(qname); + if (qnameEntity == null) + { + // Notify that we are ready + logger.debug("Thread " + threadName + " is READY"); + readyLatch.countDown(); + // Wait for start signal + startLatch.await(); + logger.debug("Thread " + threadName + " is GO"); + // Go + try + { + // This could fail with concurrency, but that's what we're testing + logger.debug("Thread " + threadName + " is CREATING " + qname); + qnameEntity = dao.newQNameEntity(qname); + } + catch (Throwable e) + { + logger.debug("Failed to create QNameEntity. Might retry.", e); + throw e; + } + finally + { + // Notify the counter that this thread is done + logger.debug("Thread " + threadName + " is DONE"); + doneLatch.countDown(); + } + } + else + { + throw new RuntimeException("QName entity should not exist"); + } + assertNotNull("QName should now exist", qnameEntity); + // Done + return qnameEntity; + } + }; + Runnable runnable = new Runnable() + { + public void run() + { + try + { + retryingTransactionHelper.doInTransaction(callback); + } + catch (Throwable e) + { + logger.error("Error escaped from retry", e); + errors.add(e); + } + } + }; + // Fire a bunch of threads off + for (int i = 0; i < threadCount; i++) + { + Thread thread = new Thread(runnable, getName() + "-" + i); + thread.setDaemon(true); // Just in case there are complications + thread.start(); + } + // Wait for threads to be ready + readyLatch.await(5, TimeUnit.SECONDS); + // Let the threads go + startLatch.countDown(); + // Wait for them all to be done (within limits) + doneLatch.await(threadCount, TimeUnit.SECONDS); + if (doneLatch.getCount() > 0) + { + fail("Still waiting for threads to finish"); + } + // Check if there are errors + if (errors.size() > 0) + { + throw errors.get(0); + } + } +} diff --git a/source/java/org/alfresco/repo/domain/QNameEntity.java b/source/java/org/alfresco/repo/domain/QNameEntity.java new file mode 100644 index 0000000000..e7ce61ce4c --- /dev/null +++ b/source/java/org/alfresco/repo/domain/QNameEntity.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain; + +import org.alfresco.service.namespace.QName; + +/** + * Represents a persistable QName entity. + * + * @author Derek Hulley + * @since 2.2 + */ +public interface QNameEntity +{ + Long getId(); + + NamespaceEntity getNamespace(); + + void setNamespace(NamespaceEntity namespace); + + String getLocalName(); + + void setLocalName(String localName); + + QName getQName(); +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java index 389092da64..ec1095342d 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java @@ -30,7 +30,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.NamespaceEntity; import org.alfresco.repo.domain.Node; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; @@ -46,16 +48,18 @@ public class ChildAssocImpl implements ChildAssoc, Serializable private Long version; private Node parent; private Node child; - private QName typeQName; + private QNameEntity typeQName; + private NamespaceEntity qnameNamespace; + private String qnameLocalName; private String childNodeName; private long childNodeNameCrc; - private QName qName; private boolean isPrimary; private int index; private transient ReadLock refReadLock; private transient WriteLock refWriteLock; private transient ChildAssociationRef childAssocRef; + private transient QName qname; public ChildAssocImpl() { @@ -71,15 +75,17 @@ public class ChildAssocImpl implements ChildAssoc, Serializable // add the forward associations this.setParent(parentNode); this.setChild(childNode); -// childNode.getParentAssocs().add(this); } public void removeAssociation() { -// // maintain inverse assoc from child node to this instance -// this.getChild().getParentAssocs().remove(this); } + /** + * {@inheritDoc} + *

+ * This method is thread-safe and lazily creates the required references, if required. + */ public ChildAssociationRef getChildAssocRef() { boolean trashReference = false; @@ -114,9 +120,9 @@ public class ChildAssocImpl implements ChildAssoc, Serializable if (childAssocRef == null || trashReference) { childAssocRef = new ChildAssociationRef( - this.typeQName, + this.typeQName.getQName(), parent.getNodeRef(), - this.qName, + this.getQname(), child.getNodeRef(), this.isPrimary, index); @@ -129,6 +135,43 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } + /** + * {@inheritDoc} + *

+ * This method is thread-safe and lazily creates the required references, if required. + */ + public QName getQname() + { + // first check if it is available + refReadLock.lock(); + try + { + if (qname != null) + { + return qname; + } + } + finally + { + refReadLock.unlock(); + } + // get write lock + refWriteLock.lock(); + try + { + // double check + if (qname == null ) + { + qname = QName.createQName(qnameNamespace.getUri(), qnameLocalName); + } + return qname; + } + finally + { + refWriteLock.unlock(); + } + } + public boolean equals(Object obj) { if (obj == null) @@ -164,7 +207,7 @@ public class ChildAssocImpl implements ChildAssoc, Serializable .append(", child=").append(child.getId()) .append(", child name=").append(childNodeName) .append(", child name crc=").append(childNodeNameCrc) - .append(", assoc type=").append(getTypeQName()) + .append(", assoc type=").append(getTypeQName().getQName()) .append(", assoc name=").append(getQname()) .append(", isPrimary=").append(isPrimary) .append("]"); @@ -278,12 +321,12 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } - public QName getTypeQName() + public QNameEntity getTypeQName() { return typeQName; } - - public void setTypeQName(QName typeQName) + + public void setTypeQName(QNameEntity typeQName) { refWriteLock.lock(); try @@ -297,6 +340,46 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } + public NamespaceEntity getQnameNamespace() + { + return qnameNamespace; + } + + public void setQnameNamespace(NamespaceEntity qnameNamespace) + { + refWriteLock.lock(); + try + { + this.qnameNamespace = qnameNamespace; + this.childAssocRef = null; + this.qname = null; + } + finally + { + refWriteLock.unlock(); + } + } + + public String getQnameLocalName() + { + return qnameLocalName; + } + + public void setQnameLocalName(String qnameLocalName) + { + refWriteLock.lock(); + try + { + this.qnameLocalName = qnameLocalName; + this.childAssocRef = null; + this.qname = null; + } + finally + { + refWriteLock.unlock(); + } + } + public String getChildNodeName() { return childNodeName; @@ -317,25 +400,6 @@ public class ChildAssocImpl implements ChildAssoc, Serializable this.childNodeNameCrc = crc; } - public QName getQname() - { - return qName; - } - - public void setQname(QName qname) - { - refWriteLock.lock(); - try - { - this.qName = qname; - this.childAssocRef = null; - } - finally - { - refWriteLock.unlock(); - } - } - public boolean getIsPrimary() { return isPrimary; diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java index 34a694d38f..26d5d75a78 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateNodeTest.java @@ -37,15 +37,17 @@ import javax.transaction.UserTransaction; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.NamespaceEntity; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.domain.Server; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.StoreKey; import org.alfresco.repo.domain.Transaction; -import org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; @@ -60,7 +62,6 @@ import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.exception.ConstraintViolationException; import org.hibernate.exception.GenericJDBCException; -import org.springframework.orm.hibernate3.HibernateCallback; /** * Test persistence and retrieval of Hibernate-specific implementations of the @@ -76,6 +77,23 @@ public class HibernateNodeTest extends BaseSpringTest private Store store; private Server server; private Transaction transaction; + private NamespaceEntity cmNamespaceEntity; + private NamespaceEntity emptyNamespaceEntity; + private QNameEntity cmObjectQNameEntity; + private QNameEntity containerQNameEntity; + private QNameEntity contentQNameEntity; + private QNameEntity type1QNameEntity; + private QNameEntity type2QNameEntity; + private QNameEntity type3QNameEntity; + private QNameEntity aspect1QNameEntity; + private QNameEntity aspect2QNameEntity; + private QNameEntity aspect3QNameEntity; + private QNameEntity aspect4QNameEntity; + private QNameEntity prop1QNameEntity; + private QNameEntity propNameQNameEntity; + private QNameEntity propAuthorQNameEntity; + private QNameEntity propArchivedByQNameEntity; + private QNameEntity aspectAuditableQNameEntity; public HibernateNodeTest() { @@ -101,6 +119,27 @@ public class HibernateNodeTest extends BaseSpringTest transaction.setServer(server); transaction.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId()); getSession().save(transaction); + + // Create a QName for node type + QNameDAO qnameDAO = (QNameDAO) applicationContext.getBean("qnameDAO"); + + cmNamespaceEntity = qnameDAO.getOrCreateNamespaceEntity(NamespaceService.CONTENT_MODEL_1_0_URI); + emptyNamespaceEntity = qnameDAO.getOrCreateNamespaceEntity(""); + cmObjectQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.TYPE_CMOBJECT); + containerQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.TYPE_CONTAINER); + contentQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.TYPE_CONTENT); + type1QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "type1")); + type2QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "type2")); + type3QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "type3")); + aspect1QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "aspect1")); + aspect2QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "aspect2")); + aspect3QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "aspect3")); + aspect4QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "aspect4")); + prop1QNameEntity = qnameDAO.getOrCreateQNameEntity(QName.createQName(TEST_NAMESPACE, "prop1")); + propNameQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_NAME); + propAuthorQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_AUTHOR); + propArchivedByQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_BY); + aspectAuditableQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_AUDITABLE); } protected void onTearDownInTransaction() @@ -121,7 +160,7 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); + node.setTypeQName(containerQNameEntity); // now it should work Serializable id = getSession().save(node); @@ -148,7 +187,7 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); + node.setTypeQName(containerQNameEntity); Serializable nodeId = getSession().save(node); // This should all be fine. The node does not HAVE to have a status. @@ -195,12 +234,12 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTAINER); + node.setTypeQName(containerQNameEntity); // give it a property map - Map propertyMap = new HashMap(5); + Map propertyMap = new HashMap(5); QName propertyQName = QName.createQName("{}A"); PropertyValue propertyValue = new PropertyValue(DataTypeDefinition.TEXT, "AAA"); - propertyMap.put(propertyQName, propertyValue); + propertyMap.put(prop1QNameEntity.getId(), propertyValue); node.getProperties().putAll(propertyMap); // persist it Serializable id = getSession().save(node); @@ -212,7 +251,7 @@ public class HibernateNodeTest extends BaseSpringTest propertyMap = node.getProperties(); assertNotNull("Map not persisted", propertyMap); // ensure that the value is present - assertNotNull("Property value not present in map", QName.createQName("{}A")); + assertNotNull("Property value not present in map", propertyMap.get(prop1QNameEntity.getId())); } /** @@ -225,19 +264,15 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CMOBJECT); + node.setTypeQName(cmObjectQNameEntity); // add some aspects - QName aspect1 = QName.createQName(TEST_NAMESPACE, "1"); - QName aspect2 = QName.createQName(TEST_NAMESPACE, "2"); - QName aspect3 = QName.createQName(TEST_NAMESPACE, "3"); - QName aspect4 = QName.createQName(TEST_NAMESPACE, "4"); - Set aspects = node.getAspects(); - aspects.add(aspect1); - aspects.add(aspect2); - aspects.add(aspect3); - aspects.add(aspect4); - assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4)); + Set aspects = node.getAspects(); + aspects.add(aspect1QNameEntity.getId()); + aspects.add(aspect2QNameEntity.getId()); + aspects.add(aspect3QNameEntity.getId()); + aspects.add(aspect4QNameEntity.getId()); + assertFalse("Set did not eliminate duplicate aspect qname", aspects.add(aspect4QNameEntity.getId())); // persist Serializable id = getSession().save(node); @@ -258,20 +293,21 @@ public class HibernateNodeTest extends BaseSpringTest Node contentNode = new NodeImpl(); contentNode.setStore(store); contentNode.setUuid(GUID.generate()); - contentNode.setTypeQName(ContentModel.TYPE_CONTENT); + contentNode.setTypeQName(contentQNameEntity); Serializable contentNodeId = getSession().save(contentNode); // make a container node Node containerNode = new NodeImpl(); containerNode.setStore(store); containerNode.setUuid(GUID.generate()); - containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); + containerNode.setTypeQName(containerQNameEntity); Serializable containerNodeId = getSession().save(containerNode); // create an association to the content ChildAssoc assoc1 = new ChildAssocImpl(); assoc1.setIsPrimary(true); - assoc1.setTypeQName(QName.createQName(null, "type1")); - assoc1.setQname(QName.createQName(null, "number1")); + assoc1.setTypeQName(type1QNameEntity); + assoc1.setQnameNamespace(emptyNamespaceEntity); + assoc1.setQnameLocalName("number1"); assoc1.setChildNodeName("number1"); assoc1.setChildNodeNameCrc(1); assoc1.buildAssociation(containerNode, contentNode); @@ -280,8 +316,9 @@ public class HibernateNodeTest extends BaseSpringTest // make another association between the same two parent and child nodes ChildAssoc assoc2 = new ChildAssocImpl(); assoc2.setIsPrimary(true); - assoc2.setTypeQName(QName.createQName(null, "type2")); - assoc2.setQname(QName.createQName(null, "number2")); + assoc2.setTypeQName(type2QNameEntity); + assoc2.setQnameNamespace(emptyNamespaceEntity); + assoc2.setQnameLocalName("number2"); assoc2.setChildNodeName("number2"); assoc2.setChildNodeNameCrc(2); assoc2.buildAssociation(containerNode, contentNode); @@ -335,31 +372,31 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTENT); + node.setTypeQName(contentQNameEntity); Serializable nodeId = getSession().save(node); // add some aspects to the node - Set aspects = node.getAspects(); - aspects.add(ContentModel.ASPECT_AUDITABLE); + Set aspects = node.getAspects(); + aspects.add(aspectAuditableQNameEntity.getId()); // add some properties - Map properties = node.getProperties(); - properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + Map properties = node.getProperties(); + properties.put(propNameQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); // check that the session hands back the same instance Node checkNode = (Node) getSession().get(NodeImpl.class, nodeId); assertNotNull(checkNode); assertTrue("Node retrieved was not same instance", checkNode == node); - Set checkAspects = checkNode.getAspects(); + Set checkAspects = checkNode.getAspects(); assertTrue("Aspect set retrieved was not the same instance", checkAspects == aspects); assertEquals("Incorrect number of aspects", 1, checkAspects.size()); - QName checkQName = (QName) checkAspects.toArray()[0]; - assertTrue("QName retrieved was not the same instance", checkQName == ContentModel.ASPECT_AUDITABLE); + Long checkQNameId = (Long) checkAspects.toArray()[0]; + assertEquals("QName retrieved was not the same instance", aspectAuditableQNameEntity.getId(), checkQNameId); - Map checkProperties = checkNode.getProperties(); + Map checkProperties = checkNode.getProperties(); assertTrue("Propery map retrieved was not the same instance", checkProperties == properties); - assertTrue("Property not found", checkProperties.containsKey(ContentModel.PROP_NAME)); + assertTrue("Property not found", checkProperties.containsKey(propNameQNameEntity.getId())); flushAndClear(); // commit the transaction @@ -423,7 +460,7 @@ public class HibernateNodeTest extends BaseSpringTest Node node = new NodeImpl(); node.setStore(store); node.setUuid(GUID.generate()); - node.setTypeQName(ContentModel.TYPE_CONTENT); + node.setTypeQName(contentQNameEntity); Long nodeId = (Long) getSession().save(node); // Record the ID @@ -434,12 +471,12 @@ public class HibernateNodeTest extends BaseSpringTest /* flushAndClear(); */ // add some aspects to the node - Set aspects = node.getAspects(); - aspects.add(ContentModel.ASPECT_AUDITABLE); + Set aspects = node.getAspects(); + aspects.add(aspectAuditableQNameEntity.getId()); // add some properties - Map properties = node.getProperties(); - properties.put(ContentModel.PROP_NAME, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + Map properties = node.getProperties(); + properties.put(propNameQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); } // Commit the transaction txn.commit(); @@ -472,14 +509,14 @@ public class HibernateNodeTest extends BaseSpringTest for (Long nodeId : nodeIds) { Node node = (Node) session.get(NodeImpl.class, nodeId); - Set aspects = node.getAspects(); - Map properties = node.getProperties(); - if (!aspects.contains(ContentModel.ASPECT_AUDITABLE)) + Set aspects = node.getAspects(); + Map properties = node.getProperties(); + if (!aspects.contains(aspectAuditableQNameEntity.getId())) { // Missing the aspect incorrectAspectCount++; } - if (!properties.containsKey(ContentModel.PROP_NAME)) + if (!properties.containsKey(propNameQNameEntity.getId())) { // Missing property incorrectPropertyCount++; @@ -511,10 +548,10 @@ public class HibernateNodeTest extends BaseSpringTest Node containerNode = new NodeImpl(); containerNode.setStore(store); containerNode.setUuid(GUID.generate()); - containerNode.setTypeQName(ContentModel.TYPE_CONTAINER); - containerNode.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - containerNode.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - containerNode.getAspects().add(ContentModel.ASPECT_AUDITABLE); + containerNode.setTypeQName(containerQNameEntity); + containerNode.getProperties().put(propAuthorQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + containerNode.getProperties().put(propArchivedByQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + containerNode.getAspects().add(aspectAuditableQNameEntity.getId()); Serializable containerNodeId = getSession().save(containerNode); NodeKey containerNodeKey = new NodeKey(containerNode.getNodeRef()); NodeStatus containerNodeStatus = new NodeStatusImpl(); @@ -526,10 +563,10 @@ public class HibernateNodeTest extends BaseSpringTest Node contentNode1 = new NodeImpl(); contentNode1.setStore(store); contentNode1.setUuid(GUID.generate()); - contentNode1.setTypeQName(ContentModel.TYPE_CONTENT); - contentNode1.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode1.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode1.getAspects().add(ContentModel.ASPECT_AUDITABLE); + contentNode1.setTypeQName(contentQNameEntity); + contentNode1.getProperties().put(propAuthorQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode1.getProperties().put(propArchivedByQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode1.getAspects().add(aspectAuditableQNameEntity.getId()); Serializable contentNode1Id = getSession().save(contentNode1); NodeKey contentNodeKey1 = new NodeKey(contentNode1.getNodeRef()); NodeStatus contentNodeStatus1 = new NodeStatusImpl(); @@ -541,11 +578,11 @@ public class HibernateNodeTest extends BaseSpringTest Node contentNode2 = new NodeImpl(); contentNode2.setStore(store); contentNode2.setUuid(GUID.generate()); - contentNode2.setTypeQName(ContentModel.TYPE_CONTENT); + contentNode2.setTypeQName(contentQNameEntity); Serializable contentNode2Id = getSession().save(contentNode2); - contentNode2.getProperties().put(ContentModel.PROP_AUTHOR, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode2.getProperties().put(ContentModel.PROP_ARCHIVED_BY, new PropertyValue(DataTypeDefinition.TEXT, "ABC")); - contentNode2.getAspects().add(ContentModel.ASPECT_AUDITABLE); + contentNode2.getProperties().put(propAuthorQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode2.getProperties().put(propArchivedByQNameEntity.getId(), new PropertyValue(DataTypeDefinition.TEXT, "ABC")); + contentNode2.getAspects().add(aspectAuditableQNameEntity.getId()); NodeKey contentNodeKey2 = new NodeKey(contentNode2.getNodeRef()); NodeStatus contentNodeStatus2 = new NodeStatusImpl(); contentNodeStatus2.setKey(contentNodeKey2); @@ -555,8 +592,9 @@ public class HibernateNodeTest extends BaseSpringTest // create an association to content 1 ChildAssoc assoc1 = new ChildAssocImpl(); assoc1.setIsPrimary(true); - assoc1.setTypeQName(QName.createQName(null, "type1")); - assoc1.setQname(QName.createQName(null, "number1")); + assoc1.setTypeQName(type1QNameEntity); + assoc1.setQnameNamespace(emptyNamespaceEntity); + assoc1.setQnameLocalName("number1"); assoc1.setChildNodeName("number1"); assoc1.setChildNodeNameCrc(1); assoc1.buildAssociation(containerNode, contentNode1); @@ -564,8 +602,9 @@ public class HibernateNodeTest extends BaseSpringTest // create an association to content 2 ChildAssoc assoc2 = new ChildAssocImpl(); assoc2.setIsPrimary(true); - assoc2.setTypeQName(QName.createQName(null, "type2")); - assoc2.setQname(QName.createQName(null, "number2")); + assoc2.setTypeQName(type2QNameEntity); + assoc2.setQnameNamespace(emptyNamespaceEntity); + assoc2.setQnameLocalName("number2"); assoc2.setChildNodeName("number2"); assoc2.setChildNodeNameCrc(2); assoc2.buildAssociation(containerNode, contentNode2); @@ -594,8 +633,9 @@ public class HibernateNodeTest extends BaseSpringTest contentNode2 = contentNodeStatus2.getNode(); ChildAssoc assoc3 = new ChildAssocImpl(); assoc3.setIsPrimary(false); - assoc3.setTypeQName(QName.createQName(null, "type3")); - assoc3.setQname(QName.createQName(null, "number3")); + assoc3.setTypeQName(type3QNameEntity); + assoc3.setQnameNamespace(emptyNamespaceEntity); + assoc3.setQnameLocalName("number3"); assoc3.setChildNodeName("number3"); assoc3.setChildNodeNameCrc(2); assoc3.buildAssociation(containerNode, contentNode2); // check whether the children are pulled in for this @@ -612,13 +652,13 @@ public class HibernateNodeTest extends BaseSpringTest Node parentNode = new NodeImpl(); parentNode.setStore(store); parentNode.setUuid(GUID.generate()); - parentNode.setTypeQName(ContentModel.TYPE_CONTAINER); + parentNode.setTypeQName(containerQNameEntity); Long nodeIdOne = (Long) getSession().save(parentNode); // Create child node Node childNode = new NodeImpl(); childNode.setStore(store); childNode.setUuid(GUID.generate()); - childNode.setTypeQName(ContentModel.TYPE_CONTENT); + childNode.setTypeQName(contentQNameEntity); Long nodeIdTwo = (Long) getSession().save(childNode); // Get them into the database getSession().flush(); @@ -631,8 +671,9 @@ public class HibernateNodeTest extends BaseSpringTest ChildAssoc assoc = new ChildAssocImpl(); assoc.buildAssociation(parentNode, childNode); assoc.setIsPrimary(false); - assoc.setTypeQName(QName.createQName(null, "TYPE")); - assoc.setQname(QName.createQName(null, "" + System.nanoTime())); + assoc.setTypeQName(type1QNameEntity); + assoc.setQnameNamespace(emptyNamespaceEntity); + assoc.setQnameLocalName("" + System.nanoTime()); assoc.setChildNodeName(GUID.generate()); // It must be unique assoc.setChildNodeNameCrc(-1L); Long assocId = (Long) getSession().save(assoc); diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java new file mode 100644 index 0000000000..20fb4aa5cd --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.hibernate; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.domain.NamespaceEntity; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; +import org.alfresco.service.namespace.QName; +import org.hibernate.Query; +import org.hibernate.Session; +import org.springframework.orm.hibernate3.HibernateCallback; +import org.springframework.orm.hibernate3.support.HibernateDaoSupport; + +/** + * Hibernate-specific implementation of the QName and Namespace DAO interface. + * + * @author Derek Hulley + * @since 2.1 + */ +public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameDAO +{ + private static final String QUERY_GET_NS_BY_URI = "qname.GetNamespaceByUri"; + private static final String QUERY_GET_QNAME_BY_URI_AND_LOCALNAME = "qname.GetQNameByUriAndLocalName"; + + private SimpleCache qnameEntityCache; + + public void setQnameEntityCache(SimpleCache qnameEntityCache) + { + this.qnameEntityCache = qnameEntityCache; + } + + public NamespaceEntity getNamespaceEntity(Long id) + { + NamespaceEntity namespaceEntity = (NamespaceEntity) getSession().get(NamespaceEntityImpl.class, id); + if (namespaceEntity == null) + { + throw new AlfrescoRuntimeException("The NamespaceEntity ID " + id + " doesn't exist."); + } + return namespaceEntity; + } + + public NamespaceEntity getNamespaceEntity(final String namespaceUri) + { + // TODO: Use a cache if external use becomes common + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateQNameDAOImpl.QUERY_GET_NS_BY_URI) + .setString("namespaceUri", namespaceUri); + return query.uniqueResult(); + } + }; + NamespaceEntity result = (NamespaceEntity) getHibernateTemplate().execute(callback); + // Done + return result; + } + + public NamespaceEntity getOrCreateNamespaceEntity(String namespaceUri) + { + NamespaceEntity result = getNamespaceEntity(namespaceUri); + if (result == null) + { + result = newNamespaceEntity(namespaceUri); + } + return result; + } + + public NamespaceEntity newNamespaceEntity(String namespaceUri) + { + NamespaceEntity namespace = new NamespaceEntityImpl(); + namespace.setUri(namespaceUri); + // Persist + getSession().save(namespace); + // Done + return namespace; + } + + public QNameEntity getQNameEntity(Long id) + { + QNameEntity qnameEntity = (QNameEntity) getSession().get(QNameEntityImpl.class, id); + if (qnameEntity == null) + { + throw new AlfrescoRuntimeException("The QNameEntity ID " + id + " doesn't exist."); + } + return qnameEntity; + } + + public QName getQName(Long id) + { + // TODO: Explore caching options here + QNameEntity qnameEntity = getQNameEntity(id); + if (qnameEntity == null) + { + return null; + } + else + { + return qnameEntity.getQName(); + } + } + + public QNameEntity getQNameEntity(final QName qname) + { + QNameEntity result; + // First check the cache + Long id = qnameEntityCache.get(qname); + if (id == null) + { + // It's not in the cache, so query + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateQNameDAOImpl.QUERY_GET_QNAME_BY_URI_AND_LOCALNAME) + .setString("namespaceUri", qname.getNamespaceURI()) + .setString("localName", qname.getLocalName()); + return query.uniqueResult(); + } + }; + result = (QNameEntity) getHibernateTemplate().execute(callback); + if (result != null) + { + id = result.getId(); + // We found something, so we can add it to the cache + qnameEntityCache.put(qname, id); + } + } + else + { + // Found in the cache. Load using the ID. + result = getQNameEntity(id); + if (result == null) + { + // It is not available, so we need to go the query route. + // But first remove the cache entry + qnameEntityCache.remove(qname); + // Recurse, but this time there is no cache entry + return getQNameEntity(qname); + } + } + // Done + return result; + } + + public QNameEntity getOrCreateQNameEntity(QName qname) + { + QNameEntity result = getQNameEntity(qname); + if (result == null) + { + result = newQNameEntity(qname); + } + return result; + } + + public QNameEntity newQNameEntity(QName qname) + { + final String namespaceUri = qname.getNamespaceURI(); + final String localName = qname.getLocalName(); + NamespaceEntity namespace = getNamespaceEntity(namespaceUri); + if (namespace == null) + { + namespace = newNamespaceEntity(namespaceUri); + } + QNameEntity qnameEntity = new QNameEntityImpl(); + qnameEntity.setNamespace(namespace); + qnameEntity.setLocalName(localName); + // Persist + Long id = (Long) getSession().save(qnameEntity); + // Update the cache + qnameEntityCache.put(qname, id); + // Done + return qnameEntity; + } + + public Set convertIdsToQNames(Set ids) + { + Set qnames = new HashSet(ids.size() * 2 + 1); + for (Long id : ids) + { + QName qname = getQName(id); // Never null + qnames.add(qname); + } + return qnames; + } + + public Map convertIdMapToQNameMap(Map idMap) + { + Map qnameMap = new HashMap(idMap.size() + 3); + for (Map.Entry entry : idMap.entrySet()) + { + QName qname = getQName(entry.getKey()); + qnameMap.put(qname, entry.getValue()); + } + return qnameMap; + } +} diff --git a/source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java new file mode 100644 index 0000000000..76f4278f0c --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/NamespaceEntityImpl.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.hibernate; + +import java.io.Serializable; + +import org.alfresco.repo.domain.NamespaceEntity; +import org.alfresco.repo.domain.QNameEntity; + +/** + * Hibernate-specific implementation of the domain entity QnameEntity. + * + * @author Derek Hulley + */ +public class NamespaceEntityImpl implements NamespaceEntity, Serializable +{ + private static final long serialVersionUID = -6781559184013949845L; + + private Long id; + private Long version; + private String uri; + + public NamespaceEntityImpl() + { + } + + /** + * @see #getStoreRef()() + */ + public String toString() + { + return uri; + } + + /** + * @see #getKey() + */ + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (obj == this) + { + return true; + } + else if (!(obj instanceof QNameEntity)) + { + return false; + } + NamespaceEntity that = (NamespaceEntity) obj; + return (this.getUri().equals(that.getUri())); + } + + /** + * @see #getKey() + */ + public int hashCode() + { + return uri.hashCode(); + } + + public Long getId() + { + return id; + } + + /** + * For Hibernate use. + */ + @SuppressWarnings("unused") + private void setId(Long id) + { + this.id = id; + } + + public Long getVersion() + { + return version; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setVersion(Long version) + { + this.version = version; + } + + public String getUri() + { + return uri; + } + + public void setUri(String uri) + { + this.uri = uri; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index a67c6e0cb5..4857ad89d9 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -6,7 +6,6 @@ - @@ -41,12 +41,23 @@ - + + - - + + - + + - - + @@ -87,22 +98,9 @@ sort="unsorted" optimistic-lock="true" cascade="delete" > - - + + - - - - + + + + + @@ -200,6 +224,7 @@ name="source" class="org.alfresco.repo.domain.hibernate.NodeImpl" optimistic-lock="false" + foreign-key="fk_alf_na_snode" lazy="false" fetch="join" not-null="true" > @@ -210,12 +235,23 @@ name="target" class="org.alfresco.repo.domain.hibernate.NodeImpl" optimistic-lock="false" + foreign-key="fk_alf_na_tnode" lazy="false" fetch="join" not-null="true" > - + + @@ -235,7 +271,7 @@ assoc.childNodeName = :newName, assoc.childNodeNameCrc = :newNameCrc where - assoc.id = :childAssocId + assoc = :childAssoc @@ -244,7 +280,7 @@ from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where - assoc.child.id = :childId + assoc.child = :child order by assoc.index, assoc.id @@ -258,9 +294,9 @@ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc join assoc.child as child where - assoc.parent.id = :parentId and + assoc.parent = :parent and assoc.isPrimary = true and - status.node.id = child.id + status.node = child @@ -269,10 +305,11 @@ from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where - assoc.parent.id = :parentId and - assoc.child.id = :childId and + assoc.parent = :parent and + assoc.child = :child and assoc.typeQName = :typeQName and - assoc.qname = :qname + assoc.qnameNamespace = :qnameNamespace and + assoc.qnameLocalName = :qnameLocalName order by assoc.index, assoc.id @@ -284,7 +321,7 @@ from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where - assoc.parent.id = :parentId + assoc.parent = :parent order by assoc.index, assoc.id @@ -296,7 +333,7 @@ from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where - assoc.parent.id = :parentId and + assoc.parent = :parent and assoc.childNodeName = :childNodeName and assoc.childNodeNameCrc = :childNodeNameCrc @@ -307,7 +344,7 @@ from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where - assoc.parent.id = :parentId and + assoc.parent = :parent and assoc.typeQName = :typeQName and assoc.childNodeName = :childNodeName and assoc.childNodeNameCrc = :childNodeNameCrc @@ -319,7 +356,8 @@ select assoc.typeQName, - assoc.qname, + assoc.qnameNamespace, + assoc.qnameLocalName, assoc.isPrimary, assoc.index, child.id, @@ -331,7 +369,7 @@ join assoc.parent as parent join assoc.child as child where - assoc.parent.id = :parentId + assoc.parent = :parent order by assoc.index, assoc.id @@ -340,7 +378,8 @@ select assoc.typeQName, - assoc.qname, + assoc.qnameNamespace, + assoc.qnameLocalName, assoc.isPrimary, assoc.index, child.id, @@ -352,8 +391,9 @@ join assoc.parent as parent join assoc.child as child where - assoc.parent.id = :parentId and - assoc.qname = :childAssocQName + assoc.parent = :parent and + assoc.qnameNamespace = :qnameNamespace and + assoc.qnameLocalName = :qnameLocalName order by assoc.index, assoc.id @@ -365,8 +405,8 @@ from org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc where - assoc.source.id = :sourceId and - assoc.target.id = :targetId and + assoc.source = :source and + assoc.target = :target and assoc.typeQName = :assocTypeQName @@ -376,8 +416,8 @@ from org.alfresco.repo.domain.hibernate.NodeAssocImpl as assoc where - assoc.source.id = :nodeId or - assoc.target.id = :nodeId + assoc.source = :node or + assoc.target = :node @@ -388,7 +428,7 @@ join assoc.source as source join assoc.target as target where - assoc.source.id = :sourceId + assoc.source = :source @@ -399,7 +439,7 @@ join assoc.source as source join assoc.target as target where - assoc.target.id = :targetId + assoc.target = :target @@ -414,7 +454,7 @@ prop.actualType = :actualTypeString or prop.actualType = 'SERIALIZABLE' ) and - prop.persistedType != 'NULL' + prop.persistedType != 0 diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java index a66387d384..c21a7ecad2 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java @@ -31,8 +31,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.service.cmr.repository.AssociationRef; -import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** @@ -48,7 +48,7 @@ public class NodeAssocImpl implements NodeAssoc, Serializable private Long version; private Node source; private Node target; - private QName typeQName; + private QNameEntity typeQName; private transient ReadLock refReadLock; private transient WriteLock refWriteLock; @@ -103,7 +103,7 @@ public class NodeAssocImpl implements NodeAssoc, Serializable { nodeAssocRef = new AssociationRef( getSource().getNodeRef(), - this.typeQName, + this.typeQName.getQName(), getTarget().getNodeRef()); } return nodeAssocRef; @@ -222,12 +222,12 @@ public class NodeAssocImpl implements NodeAssoc, Serializable } } - public QName getTypeQName() + public QNameEntity getTypeQName() { return typeQName; } - public void setTypeQName(QName typeQName) + public void setTypeQName(QNameEntity typeQName) { refWriteLock.lock(); try diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java index 655da3ccb1..c651827328 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java @@ -38,9 +38,9 @@ import org.alfresco.repo.domain.ChildAssoc; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.domain.Store; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** @@ -59,10 +59,10 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable private Long version; private Store store; private String uuid; - private QName typeQName; - private Set aspects; + private QNameEntity typeQName; + private Set aspects; private Collection parentAssocs; - private Map properties; + private Map properties; private DbAccessControlList accessControlList; private transient ReadLock refReadLock; @@ -71,9 +71,9 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable public NodeImpl() { - aspects = new HashSet(5); + aspects = new HashSet(5); parentAssocs = new HashSet(5); - properties = new HashMap(5); + properties = new HashMap(5); ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); refReadLock = lock.readLock(); @@ -219,17 +219,17 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable } } - public QName getTypeQName() + public QNameEntity getTypeQName() { return typeQName; } - public void setTypeQName(QName typeQName) + public void setTypeQName(QNameEntity typeQName) { this.typeQName = typeQName; } - public Set getAspects() + public Set getAspects() { return aspects; } @@ -238,7 +238,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable * For Hibernate use */ @SuppressWarnings("unused") - private void setAspects(Set aspects) + private void setAspects(Set aspects) { this.aspects = aspects; } @@ -257,7 +257,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable this.parentAssocs = parentAssocs; } - public Map getProperties() + public Map getProperties() { return properties; } @@ -266,7 +266,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable * For Hibernate use */ @SuppressWarnings("unused") - private void setProperties(Map properties) + private void setProperties(Map properties) { this.properties = properties; } 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 9d738ef5e2..58ba8056d4 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml @@ -5,6 +5,9 @@ 'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'> + + + + column="permission_id" + foreign-key="fk_alf_ace_perm" + lazy="no-proxy" + fetch="select" + optimistic-lock="true" + not-null="true"/> - - + + + column="context_id" + foreign-key="fk_alf_ace_ctx" + lazy="no-proxy" + fetch="select" + optimistic-lock="true" + not-null="false"/> diff --git a/source/java/org/alfresco/repo/domain/hibernate/QName.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/QName.hbm.xml new file mode 100644 index 0000000000..f4d9c7974a --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/QName.hbm.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + namespace + from + org.alfresco.repo.domain.hibernate.NamespaceEntityImpl as namespace + where + namespace.uri = :namespaceUri + + + + select + qname + from + org.alfresco.repo.domain.hibernate.QNameEntityImpl as qname + join qname.namespace as namespace + where + namespace.uri = :namespaceUri and + qname.localName = :localName + + + diff --git a/source/java/org/alfresco/repo/domain/hibernate/QNameEntityImpl.java b/source/java/org/alfresco/repo/domain/hibernate/QNameEntityImpl.java new file mode 100644 index 0000000000..4d1a62fe74 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/hibernate/QNameEntityImpl.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.domain.hibernate; + +import java.io.Serializable; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.alfresco.repo.domain.NamespaceEntity; +import org.alfresco.repo.domain.QNameEntity; +import org.alfresco.service.namespace.QName; + +/** + * Hibernate-specific implementation of the domain entity QnameEntity. + * + * @author Derek Hulley + */ +public class QNameEntityImpl implements QNameEntity, Serializable +{ + private static final long serialVersionUID = -4211902156023915846L; + + private Long id; + private Long version; + private NamespaceEntity namespace; + private String localName; + + private transient ReadLock refReadLock; + private transient WriteLock refWriteLock; + private transient QName qname; + + public QNameEntityImpl() + { + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + refReadLock = lock.readLock(); + refWriteLock = lock.writeLock(); + } + + /** + * Lazily constructs a QName instance referencing this entity + */ + public QName getQName() + { + // first check if it is available + refReadLock.lock(); + try + { + if (qname != null) + { + return qname; + } + } + finally + { + refReadLock.unlock(); + } + // get write lock + refWriteLock.lock(); + try + { + // double check + if (qname == null ) + { + String namespaceUri = namespace.getUri(); + qname = QName.createQName(namespaceUri, localName); + } + return qname; + } + finally + { + refWriteLock.unlock(); + } + } + + /** + * @see #getStoreRef()() + */ + public String toString() + { + return getQName().toString(); + } + + /** + * @see #getKey() + */ + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (obj == this) + { + return true; + } + else if (!(obj instanceof QNameEntity)) + { + return false; + } + QNameEntity that = (QNameEntity) obj; + return (this.getQName().equals(that.getQName())); + } + + /** + * @see #getKey() + */ + public int hashCode() + { + return getQName().hashCode(); + } + + public Long getId() + { + return id; + } + + /** + * For Hibernate use. + */ + @SuppressWarnings("unused") + private void setId(Long id) + { + this.id = id; + } + + public Long getVersion() + { + return version; + } + + /** + * For Hibernate use + */ + @SuppressWarnings("unused") + private void setVersion(Long version) + { + this.version = version; + } + + public NamespaceEntity getNamespace() + { + return namespace; + } + + public void setNamespace(NamespaceEntity namespace) + { + refWriteLock.lock(); + try + { + this.namespace = namespace; + this.qname = null; + } + finally + { + refWriteLock.unlock(); + } + } + + public String getLocalName() + { + return localName; + } + + public void setLocalName(String localName) + { + refWriteLock.lock(); + try + { + this.localName = localName; + this.qname = null; + } + finally + { + refWriteLock.unlock(); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml index 3904139249..9674f1c5ae 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Store.hbm.xml @@ -25,6 +25,7 @@ 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 fb6d7d14e4..2e57ec2d15 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Transaction.hbm.xml @@ -26,6 +26,7 @@ name="server" class="org.alfresco.repo.domain.hibernate.ServerImpl" column="server_id" + foreign-key="fk_alf_txn_svr" lazy="proxy" fetch="select" unique="false" diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index bf227f7415..f30506e07b 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -52,6 +52,7 @@ import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryException; @@ -1056,6 +1057,43 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest } } + /** + * Makes a read-only transaction and then looks for a property using a non-existent QName. + * The QName persistence must not lazily create QNameEntity instances for queries. + */ + public void testGetUnknownProperty() throws Exception + { + // commit to keep the root node + setComplete(); + endTransaction(); + + RetryingTransactionCallback createCallback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + NodeRef nodeRef = nodeService.createNode( + rootNodeRef, + ASSOC_TYPE_QNAME_TEST_CHILDREN, + QName.createQName("pathA"), + ContentModel.TYPE_CONTAINER).getChildRef(); + return nodeRef; + } + }; + final NodeRef nodeRef = retryingTransactionHelper.doInTransaction(createCallback, false, true); + + RetryingTransactionCallback testCallback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + QName ficticiousQName = QName.createQName(GUID.generate(), GUID.generate()); + Serializable value = nodeService.getProperty(nodeRef, ficticiousQName); + assertNull("Didn't expect a value back", value); + return null; + } + }; + retryingTransactionHelper.doInTransaction(testCallback, true, true); + } + /** * Ensures that the type you get out of a d:any property is the type that you * put in. diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 95259bf91d..fce563a171 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -44,6 +44,8 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodeStatus; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.domain.Store; import org.alfresco.repo.node.AbstractNodeServiceImpl; import org.alfresco.repo.node.StoreArchiveMap; @@ -91,6 +93,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private static Log logger = LogFactory.getLog(DbNodeServiceImpl.class); private static Log loggerPaths = LogFactory.getLog(DbNodeServiceImpl.class.getName() + ".paths"); + private QNameDAO qnameDAO; private NodeDaoService nodeDaoService; private StoreArchiveMap storeArchiveMap; private NodeService avmNodeService; @@ -101,6 +104,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl storeArchiveMap = new StoreArchiveMap(); // in case it is not set } + /** + * Set the component for creating QName entities. + */ + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + public void setNodeDaoService(NodeDaoService nodeDaoService) { this.nodeDaoService = nodeDaoService; @@ -426,14 +437,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl List defaultAspectDefs = classDefinition.getDefaultAspects(); // add all the aspects to the node - Set nodeAspects = node.getAspects(); + Set nodeAspects = node.getAspects(); for (AspectDefinition defaultAspectDef : defaultAspectDefs) { - QName aspectTypeQName = defaultAspectDef.getName(); - invokeBeforeAddAspect(nodeRef, aspectTypeQName); - nodeAspects.add(aspectTypeQName); + QName defaultAspectQName = defaultAspectDef.getName(); + QNameEntity defaultAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(defaultAspectDef.getName()); + invokeBeforeAddAspect(nodeRef, defaultAspectQName); + nodeAspects.add(defaultAspectQNameEntity.getId()); addDefaultPropertyValues(defaultAspectDef, properties); - invokeOnAddAspect(nodeRef, aspectTypeQName); + invokeOnAddAspect(nodeRef, defaultAspectQName); // Now add any default aspects for this aspect addDefaultAspects(defaultAspectDef, node, properties); @@ -465,8 +477,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl boolean movingStore = !nodeToMoveRef.getStoreRef().equals(newParentRef.getStoreRef()); // data needed for policy invocation - QName nodeToMoveTypeQName = nodeToMove.getTypeQName(); - Set nodeToMoveAspects = nodeToMove.getAspects(); + QName nodeToMoveTypeQName = nodeToMove.getTypeQName().getQName(); + Set nodeToMoveAspects = nodeToMove.getAspects(); // Invoke policy behaviour if (movingStore) @@ -510,9 +522,15 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // invoke policy behaviour if (movingStore) { + Set nodeToMoveAspectQNames = new HashSet(17); + for (Long qnameEntityId : nodeToMoveAspects) + { + QName nodeToMoveAspectQName = qnameDAO.getQNameEntity(qnameEntityId).getQName(); + nodeToMoveAspectQNames.add(nodeToMoveAspectQName); + } // TODO for now indicate that the node has been archived to prevent the version history from being removed // in the future a onMove policy could be added and remove the need for onDelete and onCreate to be fired here - invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspects, true); + invokeOnDeleteNode(oldAssocRef, nodeToMoveTypeQName, nodeToMoveAspectQNames, true); invokeOnCreateNode(newAssoc.getChildAssocRef()); } else @@ -556,7 +574,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public QName getType(NodeRef nodeRef) throws InvalidNodeRefException { Node node = getNodeNotNull(nodeRef); - return node.getTypeQName(); + return node.getTypeQName().getQName(); } /** @@ -575,8 +593,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Invoke policies invokeBeforeUpdateNode(nodeRef); + // Ensure that we have a QName entity to represent the type + QNameEntity typeQNameEntity = qnameDAO.getOrCreateQNameEntity(typeQName); // Get the node and set the new type - node.setTypeQName(typeQName); + node.setTypeQName(typeQNameEntity); // Add the default aspects to the node (update the properties with any new default values) Map properties = this.getPropertiesImpl(node); @@ -626,8 +646,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Set the property values back on the node setProperties(nodeRef, nodeProperties); + // Get the persistale QNameEntity for the aspect + QNameEntity aspectTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectTypeQName); // physically attach the aspect to the node - if (node.getAspects().add(aspectTypeQName) == true) + if (node.getAspects().add(aspectTypeQNameEntity.getId()) == true) { // Invoke policy behaviours invokeOnUpdateNode(nodeRef); @@ -652,9 +674,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // get the node Node node = getNodeNotNull(nodeRef); - Set nodeAspects = node.getAspects(); + Set nodeAspects = node.getAspects(); - if (!nodeAspects.contains(aspectTypeQName)) + // Get the persistale QNameEntity for the aspect + QNameEntity aspectTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectTypeQName); + if (!nodeAspects.contains(aspectTypeQNameEntity.getId())) { // The aspect isn't present so just leave it return; @@ -665,13 +689,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl invokeBeforeRemoveAspect(nodeRef, aspectTypeQName); // remove the aspect, if present - node.getAspects().remove(aspectTypeQName); + nodeAspects.remove(aspectTypeQNameEntity.getId()); - Map nodeProperties = node.getProperties(); + Map nodeProperties = node.getProperties(); Map propertyDefs = aspectDef.getProperties(); - for (QName propertyName : propertyDefs.keySet()) + for (QName propertyQName : propertyDefs.keySet()) { - nodeProperties.remove(propertyName); + QNameEntity propertyQNameEntity = qnameDAO.getOrCreateQNameEntity(propertyQName); + nodeProperties.remove(propertyQNameEntity.getId()); } // Remove child associations @@ -680,7 +705,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (ChildAssoc childAssoc : childAssocs) { // Ignore if the association type is not defined by the aspect - QName childAssocQName = childAssoc.getTypeQName(); + QName childAssocQName = childAssoc.getTypeQName().getQName(); if (!childAssocDefs.containsKey(childAssocQName)) { continue; @@ -695,7 +720,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (NodeAssoc nodeAssoc : nodeAssocs) { // Ignore if the association type is not defined by the aspect - QName nodeAssocQName = nodeAssoc.getTypeQName(); + QName nodeAssocQName = nodeAssoc.getTypeQName().getQName(); if (!assocDefs.containsKey(nodeAssocQName)) { continue; @@ -717,11 +742,17 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * * @see Node#getAspects() */ - public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException + public boolean hasAspect(NodeRef nodeRef, QName aspectQName) throws InvalidNodeRefException, InvalidAspectException { + QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQName); + if (aspectQNameEntity == null) + { + // There is no persisted, fixed QName like this + return false; + } Node node = getNodeNotNull(nodeRef); - Set aspectQNames = node.getAspects(); - boolean hasAspect = aspectQNames.contains(aspectRef); + Set aspectQNames = node.getAspects(); + boolean hasAspect = aspectQNames.contains(aspectQNameEntity.getId()); // done return hasAspect; } @@ -729,10 +760,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException { Node node = getNodeNotNull(nodeRef); - Set aspectQNames = node.getAspects(); + Set aspectQNameEntities = node.getAspects(); // copy the set to ensure initialization - Set ret = new HashSet(aspectQNames.size()); - ret.addAll(aspectQNames); + Set ret = new HashSet(aspectQNameEntities.size()); + for (Long aspectQNameEntityId : aspectQNameEntities) + { + QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQNameEntityId); + ret.add(aspectQNameEntity.getQName()); + } // done return ret; } @@ -751,13 +786,17 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef); // get type and aspect QNames as they will be unavailable after the delete - QName nodeTypeQName = node.getTypeQName(); - Set nodeAspectQNames = node.getAspects(); + QName nodeTypeQName = node.getTypeQName().getQName(); + Set nodeAspectQNameEntityIds = node.getAspects(); + // Get QNameEntity for subsequent checks + QNameEntity aspectTempQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_TEMPORARY); + QNameEntity aspectWorkingCopyQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_WORKING_COPY); + // check if we need to archive the node StoreRef archiveStoreRef = null; - if (nodeAspectQNames.contains(ContentModel.ASPECT_TEMPORARY) || - nodeAspectQNames.contains(ContentModel.ASPECT_WORKING_COPY)) + if (nodeAspectQNameEntityIds.contains(aspectTempQNameEntity.getId()) || + nodeAspectQNameEntityIds.contains(aspectWorkingCopyQNameEntity.getId())) { // The node is either temporary or a working copy. // It can not be archived. @@ -770,7 +809,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // remove tenant domain - to retrieve archive store from map archiveStoreRef = storeArchiveMap.getArchiveMap().get(storeRef); // get the type and check if we need archiving - TypeDefinition typeDef = dictionaryService.getType(node.getTypeQName()); + TypeDefinition typeDef = dictionaryService.getType(nodeTypeQName); if (typeDef == null || !typeDef.isArchive() || archiveStoreRef == null) { requiresDelete = true; @@ -782,7 +821,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // perform a normal deletion nodeDaoService.deleteNode(node, true); // Invoke policy behaviours - invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames, false); + Set nodeToDeleteAspectQNames = new HashSet(17); + for (Long qnameEntityId : nodeAspectQNameEntityIds) + { + QName nodeToDeleteAspectQName = qnameDAO.getQNameEntity(qnameEntityId).getQName(); + nodeToDeleteAspectQNames.add(nodeToDeleteAspectQName); + } + invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeToDeleteAspectQNames, false); } else { @@ -971,14 +1016,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private Map getPropertiesImpl(Node node) throws InvalidNodeRefException { - Map propDefs = dictionaryService.getPropertyDefs(node.getTypeQName()); - - Map nodeProperties = node.getProperties(); + Map propDefs = dictionaryService.getPropertyDefs(node.getTypeQName().getQName()); + Map nodeProperties = node.getProperties(); Map ret = new HashMap(nodeProperties.size()); // copy values - for (Map.Entry entry: nodeProperties.entrySet()) + for (Map.Entry entry: nodeProperties.entrySet()) { - QName propertyQName = entry.getKey(); + Long propertyQNameId = entry.getKey(); + QName propertyQName = qnameDAO.getQNameEntity(propertyQNameId).getQName(); PropertyValue propertyValue = entry.getValue(); // get the property definition PropertyDefinition propertyDef = propDefs.get(propertyQName); @@ -1024,28 +1069,37 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return node.getId(); } - Map properties = node.getProperties(); - PropertyValue propertyValue = properties.get(qname); - - // check if we need to provide a spoofed name - if (propertyValue == null && qname.equals(ContentModel.PROP_NAME)) + // Get the QName entity + QNameEntity qnameEntity = qnameDAO.getQNameEntity(qname); + if (qnameEntity == null) { - return nodeRef.getId(); + // There is no persisted, fixed QName like this + return null; } - - // get the property definition - PropertyDefinition propertyDef = dictionaryService.getProperty(qname); - - if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) && - (propertyValue != null) && (propertyValue.getStringValue() != null)) + else { - propertyValue.setStringValue(tenantService.getBaseName(new NodeRef(propertyValue.getStringValue())).toString()); + Map properties = node.getProperties(); + PropertyValue propertyValue = properties.get(qnameEntity.getId()); + + // check if we need to provide a spoofed name + if (propertyValue == null && qname.equals(ContentModel.PROP_NAME)) + { + return nodeRef.getId(); + } + + // Convert any NodeRefs using multi-tenant translation + PropertyDefinition propertyDef = dictionaryService.getProperty(qname); + if ((propertyDef != null) && (propertyDef.getDataType().getName().equals(DataTypeDefinition.NODE_REF)) && + (propertyValue != null) && (propertyValue.getStringValue() != null)) + { + propertyValue.setStringValue(tenantService.getBaseName(new NodeRef(propertyValue.getStringValue())).toString()); + } + + // convert to the correct type + Serializable value = makeSerializableValue(propertyDef, propertyValue); + // done + return value; } - - // convert to the correct type - Serializable value = makeSerializableValue(propertyDef, propertyValue); - // done - return value; } /** @@ -1095,17 +1149,19 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl extractIntrinsicProperties(node, properties); // copy properties onto node - Map nodeProperties = node.getProperties(); + Map nodeProperties = node.getProperties(); nodeProperties.clear(); // check the property type and copy the values across for (QName propertyQName : properties.keySet()) { + QNameEntity propertyQNameEntity = qnameDAO.getOrCreateQNameEntity(propertyQName); PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName); + // Get the value to persist Serializable value = properties.get(propertyQName); // get a persistable value PropertyValue propertyValue = makePropertyValue(propertyDef, value); - nodeProperties.put(propertyQName, propertyValue); + nodeProperties.put(propertyQNameEntity.getId(), propertyValue); } // update the node status @@ -1160,11 +1216,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { NodeRef nodeRef = node.getNodeRef(); - Map properties = node.getProperties(); + Map properties = node.getProperties(); PropertyDefinition propertyDef = dictionaryService.getProperty(qname); + // Get the persistable key + QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(qname); // get a persistable value PropertyValue propertyValue = makePropertyValue(propertyDef, value); - properties.put(qname, propertyValue); + properties.put(qnameEntity.getId(), propertyValue); // update the node status nodeDaoService.recordChangeId(nodeRef); @@ -1185,11 +1243,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Invoke policy behaviours invokeBeforeUpdateNode(nodeRef); + // Get the persistable QNameEntity + QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(qname); // Get the values before Map propertiesBefore = getPropertiesImpl(node); // Remove the property - Map properties = node.getProperties(); - properties.remove(qname); + Map properties = node.getProperties(); + properties.remove(qnameEntity.getId()); // Get the values afterwards Map propertiesAfter = getPropertiesImpl(node); @@ -1234,8 +1294,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl List results = new ArrayList(parentAssocs.size()); for (ChildAssoc assoc : parentAssocs) { + QName assocTypeQName = assoc.getTypeQName().getQName(); + QName assocQName = assoc.getQname(); // does the qname match the pattern? - if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName())) + if (!qnamePattern.isMatch(assocQName) || !typeQNamePattern.isMatch(assocTypeQName)) { // no match - ignore continue; @@ -1391,8 +1453,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl List nodeAssocRefs = new ArrayList(assocs.size()); for (NodeAssoc assoc : assocs) { + QName assocTypeQName = assoc.getTypeQName().getQName(); // check qname pattern - if (!qnamePattern.isMatch(assoc.getTypeQName())) + if (!qnamePattern.isMatch(assocTypeQName)) { continue; // the assoc name doesn't match the pattern given } @@ -1410,8 +1473,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl List nodeAssocRefs = new ArrayList(assocs.size()); for (NodeAssoc assoc : assocs) { + QName assocTypeQName = assoc.getTypeQName().getQName(); // check qname pattern - if (!qnamePattern.isMatch(assoc.getTypeQName())) + if (!qnamePattern.isMatch(assocTypeQName)) { continue; // the assoc name doesn't match the pattern given } @@ -1452,7 +1516,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl boolean hasParents = parentAssocs.size() > 0; // does the current node have a root aspect? boolean isRoot = hasAspect(currentNodeRef, ContentModel.ASPECT_ROOT); - boolean isStoreRoot = currentNode.getTypeQName().equals(ContentModel.TYPE_STOREROOT); + boolean isStoreRoot = currentNode.getTypeQName().getQName().equals(ContentModel.TYPE_STOREROOT); // look for a root. If we only want the primary root, then ignore all but the top-level root. if (isRoot && !(primaryOnly && hasParents)) // exclude primary search with parents present @@ -1482,8 +1546,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } if (first != null) { - // mimic an association that would appear if the current node was below - // the root node + // mimic an association that would appear if the current node was below the root node // or if first beneath the root node it will make the real thing ChildAssociationRef updateAssocRef = new ChildAssociationRef( isStoreRoot ? ContentModel.ASSOC_CHILDREN : first.getRef().getTypeQName(), @@ -1531,7 +1594,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl NodeRef childRef = tenantService.getBaseName(assoc.getChild().getNodeRef()); boolean isPrimary = assoc.getIsPrimary(); // build a real association reference - ChildAssociationRef assocRef = new ChildAssociationRef(assoc.getTypeQName(), parentRef, qname, childRef, isPrimary, -1); + ChildAssociationRef assocRef = new ChildAssociationRef( + assoc.getTypeQName().getQName(), + parentRef, + qname, + childRef, + isPrimary, + -1); // Ordering is not important here: We are building distinct paths upwards Path.Element element = new Path.ChildAssocElement(assocRef); // create a new path that builds on the current path @@ -1612,41 +1681,51 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef) { + QNameEntity aspectQNameEntityArchived = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_ARCHIVED); + QNameEntity propQNameEntityOwner = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_OWNER); + QNameEntity propQNameEntityCreator = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_CREATOR); + QNameEntity propQNameArchivedBy = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_BY); + QNameEntity propQNameArchivedDate = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_DATE); + QNameEntity propQNameArchivedOriginalParentAssoc = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); + NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false); Node node = nodeStatus.getNode(); ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node); // add the aspect - Set aspects = node.getAspects(); - aspects.add(ContentModel.ASPECT_ARCHIVED); - Map properties = node.getProperties(); + Set aspects = node.getAspects(); + aspects.add(aspectQNameEntityArchived.getId()); + Map properties = node.getProperties(); PropertyValue archivedByProperty = makePropertyValue( dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_BY), AuthenticationUtil.getCurrentUserName()); - properties.put(ContentModel.PROP_ARCHIVED_BY, archivedByProperty); + properties.put(propQNameArchivedBy.getId(), archivedByProperty); PropertyValue archivedDateProperty = makePropertyValue( dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_DATE), new Date()); - properties.put(ContentModel.PROP_ARCHIVED_DATE, archivedDateProperty); + properties.put(propQNameArchivedDate.getId(), archivedDateProperty); PropertyValue archivedPrimaryParentNodeRefProperty = makePropertyValue( dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), primaryParentAssoc.getChildAssocRef()); - properties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, archivedPrimaryParentNodeRefProperty); - PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_OWNER); - PropertyValue originalCreatorProperty = properties.get(ContentModel.PROP_CREATOR); + properties.put(propQNameArchivedOriginalParentAssoc.getId(), archivedPrimaryParentNodeRefProperty); + PropertyValue originalOwnerProperty = properties.get(propQNameEntityOwner.getId()); + PropertyValue originalCreatorProperty = properties.get(propQNameEntityCreator.getId()); if (originalOwnerProperty != null || originalCreatorProperty != null) { + QNameEntity propQNameArchivedOriginalOwner = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); properties.put( - ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, + propQNameArchivedOriginalOwner.getId(), originalOwnerProperty != null ? originalOwnerProperty : originalCreatorProperty); } // change the node ownership - aspects.add(ContentModel.ASPECT_OWNABLE); + QNameEntity ownableAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_OWNABLE); + aspects.add(ownableAspectQNameEntity.getId()); PropertyValue newOwnerProperty = makePropertyValue( dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER), AuthenticationUtil.getCurrentUserName()); - properties.put(ContentModel.PROP_OWNER, newOwnerProperty); + QNameEntity propQNameOwner = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_OWNER); + properties.put(propQNameOwner.getId(), newOwnerProperty); // move the node NodeRef archiveStoreRootNodeRef = getRootNode(archiveStoreRef); @@ -1853,33 +1932,38 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // add archived aspect - node.getAspects().add(ContentModel.ASPECT_ARCHIVED_ASSOCS); + QNameEntity archivedAssocsAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_ARCHIVED_ASSOCS); + node.getAspects().add(archivedAssocsAspectQNameEntity.getId()); // set properties - Map properties = node.getProperties(); + Map properties = node.getProperties(); if (archivedParentAssocRefs.size() > 0) { + QNameEntity propQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); PropertyValue propertyValue = makePropertyValue(propertyDef, archivedParentAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS, propertyValue); + properties.put(propQNameEntity.getId(), propertyValue); } if (archivedChildAssocRefs.size() > 0) { + QNameEntity propQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS); PropertyValue propertyValue = makePropertyValue(propertyDef, archivedChildAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_CHILD_ASSOCS, propertyValue); + properties.put(propQNameEntity.getId(), propertyValue); } if (archivedSourceAssocRefs.size() > 0) { + QNameEntity propQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS); PropertyValue propertyValue = makePropertyValue(propertyDef, archivedSourceAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_SOURCE_ASSOCS, propertyValue); + properties.put(propQNameEntity.getId(), propertyValue); } if (archivedTargetAssocRefs.size() > 0) { + QNameEntity propQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); PropertyDefinition propertyDef = dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS); PropertyValue propertyValue = makePropertyValue(propertyDef, archivedTargetAssocRefs); - properties.put(ContentModel.PROP_ARCHIVED_TARGET_ASSOCS, propertyValue); + properties.put(propQNameEntity.getId(), propertyValue); } } @@ -1899,33 +1983,41 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) { + QNameEntity aspectQNameEntityArchived = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_ARCHIVED); + QNameEntity propQNameEntityOwner = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_OWNER); + QNameEntity propQNameEntityOrigParentAssoc = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); + QNameEntity propQNameEntityArchivedBy = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_BY); + QNameEntity propQNameEntityArchivedDate = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_DATE); + QNameEntity propQNameEntityOrigOwner = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); + NodeStatus archivedNodeStatus = getNodeStatusNotNull(archivedNodeRef); Node archivedNode = archivedNodeStatus.getNode(); - Set aspects = archivedNode.getAspects(); - Map properties = archivedNode.getProperties(); + Set aspects = archivedNode.getAspects(); + Map properties = archivedNode.getProperties(); // the node must be a top-level archive node - if (!aspects.contains(ContentModel.ASPECT_ARCHIVED)) + if (!aspects.contains(aspectQNameEntityArchived.getId())) { throw new AlfrescoRuntimeException("The node to archive is not an archive node"); } ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) makeSerializableValue( dictionaryService.getProperty(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC), - properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC)); - PropertyValue originalOwnerProperty = properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); + properties.get(propQNameEntityOrigParentAssoc.getId())); + PropertyValue originalOwnerProperty = properties.get(propQNameEntityOrigOwner.getId()); // remove the archived aspect removeAspect(archivedNodeRef, ContentModel.ASPECT_ARCHIVED); // allow policy to fire, e.g. for DictionaryModelType - properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); - properties.remove(ContentModel.PROP_ARCHIVED_BY); - properties.remove(ContentModel.PROP_ARCHIVED_DATE); - properties.remove(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); + properties.remove(propQNameEntityOrigParentAssoc.getId()); + properties.remove(propQNameEntityArchivedBy.getId()); + properties.remove(propQNameEntityArchivedDate.getId()); + properties.remove(propQNameEntityOrigOwner.getId()); // restore the original ownership if (originalOwnerProperty != null) { - aspects.add(ContentModel.ASPECT_OWNABLE); - properties.put(ContentModel.PROP_OWNER, originalOwnerProperty); + QNameEntity ownableAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_OWNABLE); + aspects.add(ownableAspectQNameEntity.getId()); + properties.put(propQNameEntityOwner.getId(), originalOwnerProperty); } if (destinationParentNodeRef == null) @@ -1958,7 +2050,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (NodeStatus restoreNodeStatus : restoreNodeStatusesById.values()) { Node restoreNode = restoreNodeStatus.getNode(); - restoreAssocs(restoreNode); + restoreAssocs(restoreNode, propQNameEntityOrigParentAssoc); } // the node reference has changed due to the store move @@ -1975,11 +2067,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return restoredNodeRef; } - private void restoreAssocs(Node node) + private void restoreAssocs(Node node, QNameEntity propQNameEntityOrigParentAssoc) { NodeRef nodeRef = node.getNodeRef(); // set properties - Map properties = node.getProperties(); + Map properties = node.getProperties(); // restore parent associations Collection parentAssocRefs = (Collection) getProperty( @@ -2004,7 +2096,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl assocTypeQName, assocRef.getQName()); } - properties.remove(ContentModel.PROP_ARCHIVED_PARENT_ASSOCS); + properties.remove(propQNameEntityOrigParentAssoc.getId()); } // make sure that the node name uniqueness is enforced @@ -2087,8 +2179,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private void setChildUniqueName(Node childNode) { // get the name property - Map properties = childNode.getProperties(); - PropertyValue nameValue = properties.get(ContentModel.PROP_NAME); + Map properties = childNode.getProperties(); + QNameEntity nameQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_NAME); + PropertyValue nameValue = properties.get(nameQNameEntity.getId()); String useName = null; if (nameValue == null) { @@ -2103,7 +2196,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl Collection parentAssocs = nodeDaoService.getParentAssocs(childNode); for (ChildAssoc assoc : parentAssocs) { - QName assocTypeQName = assoc.getTypeQName(); + QName assocTypeQName = assoc.getTypeQName().getQName(); AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName); if (!assocDef.isChild()) { 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 a1d15f4951..54826a2296 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -29,6 +29,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -39,11 +40,14 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.ChildAssoc; +import org.alfresco.repo.domain.NamespaceEntity; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodeKey; import org.alfresco.repo.domain.NodeStatus; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.domain.QNameDAO; +import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.domain.Server; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.StoreKey; @@ -120,6 +124,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements /** Log to trace parent association caching: classname + .ParentAssocsCache */ private static Log loggerParentAssocsCache = LogFactory.getLog(HibernateNodeDaoServiceImpl.class.getName() + ".ParentAssocsCache"); + private QNameDAO qnameDAO; /** A cache for more performant lookups of the parent associations */ private SimpleCache> parentAssocsCache; private boolean isDebugEnabled = logger.isDebugEnabled(); @@ -192,6 +197,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements return uuid.hashCode(); } + /** + * Set the component for creating QName entities. + */ + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + /** * Set the transaction-aware cache to store parent associations by child node id * @@ -536,12 +549,15 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } } + // Get the node's qname + QNameEntity nodeTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(nodeTypeQName); + // build a concrete node based on a bootstrap type Node node = new NodeImpl(); // set other required properties node.setStore(store); node.setUuid(uuid); - node.setTypeQName(nodeTypeQName); + node.setTypeQName(nodeTypeQNameEntity); // persist the node getHibernateTemplate().save(node); @@ -684,12 +700,18 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { // assign a random name to the node String randomName = GUID.generate(); + + // Get the association type's qname + QNameEntity assocTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(assocTypeQName); + // Get the qname components + NamespaceEntity assocNamespaceEntity = qnameDAO.getOrCreateNamespaceEntity(qname.getNamespaceURI()); ChildAssoc assoc = new ChildAssocImpl(); - assoc.setTypeQName(assocTypeQName); + assoc.setTypeQName(assocTypeQNameEntity); assoc.setChildNodeName(randomName); assoc.setChildNodeNameCrc(-1L); // random names compete only with each other - assoc.setQname(qname); + assoc.setQnameNamespace(assocNamespaceEntity); + assoc.setQnameLocalName(qname.getLocalName()); assoc.setIsPrimary(isPrimary); // maintain inverse sets assoc.buildAssociation(parentNode, childNode); @@ -785,7 +807,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_ID_BY_NAME) - .setLong("parentId", parentNode.getId()) + .setParameter("parent", parentNode) .setParameter("childNodeName", childNameNewShort) .setLong("childNodeNameCrc", childNameNewCrc); return query.uniqueResult(); @@ -804,7 +826,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } throw new DuplicateChildNodeNameException( parentNode.getNodeRef(), - childAssoc.getTypeQName(), + childAssoc.getTypeQName().getQName(), childName); } // We got past that, so we can just update the entity and know that no other transaction @@ -831,7 +853,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_PRIMARY_CHILD_NODE_STATUSES) - .setLong("parentId", parentNode.getId()); + .setParameter("parent", parentNode); return query.list(); } }; @@ -848,7 +870,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS) - .setLong("parentId", parentNode.getId()); + .setParameter("parent", parentNode); return query.list(); } }; @@ -865,7 +887,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS) - .setLong("parentId", parentNode.getId()); + .setParameter("parent", parentNode); return query.list(); } }; @@ -882,10 +904,18 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { public Object doInHibernate(Session session) { + NamespaceEntity qnameNamespaceEntity = qnameDAO.getNamespaceEntity(assocQName.getNamespaceURI()); + String qnameLocalName = assocQName.getLocalName(); + if (qnameNamespaceEntity == null) + { + // There can be no match; + return Collections.emptyList(); + } Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS_BY_QNAME) - .setLong("parentId", parentNode.getId()) - .setParameter("childAssocQName", assocQName); + .setParameter("parent", parentNode) + .setParameter("qnameNamespace", qnameNamespaceEntity) + .setParameter("qnameLocalName", qnameLocalName); return query.list(); } }; @@ -906,18 +936,19 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements NodeRef parentNodeRef = tenantService.getBaseName(parentNode.getNodeRef()); for (Object[] row : queryResults) { - String childProtocol = (String) row[5]; - String childIdentifier = (String) row[6]; - String childUuid = (String) row[7]; + String childProtocol = (String) row[6]; + String childIdentifier = (String) row[7]; + String childUuid = (String) row[8]; NodeRef childNodeRef = tenantService.getBaseName(new NodeRef(new StoreRef(childProtocol, childIdentifier), childUuid)); - QName assocTypeQName = (QName) row[0]; - QName assocQName = (QName) row[1]; - Boolean assocIsPrimary = (Boolean) row[2]; - Integer assocIndex = (Integer) row[3]; + QNameEntity assocTypeQNameEntity = (QNameEntity) row[0]; + NamespaceEntity assocQNameNamespace = (NamespaceEntity) row[1]; + String assocQNameLocalName = (String) row[2]; + Boolean assocIsPrimary = (Boolean) row[3]; + Integer assocIndex = (Integer) row[4]; ChildAssociationRef assocRef = new ChildAssociationRef( - assocTypeQName, + assocTypeQNameEntity.getQName(), parentNodeRef, - assocQName, + QName.createQName(assocQNameNamespace.getUri(), assocQNameLocalName), childNodeRef, assocIsPrimary.booleanValue(), assocIndex.intValue()); @@ -936,12 +967,21 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { public Object doInHibernate(Session session) { + QNameEntity typeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + NamespaceEntity qnameNamespaceEntity = qnameDAO.getNamespaceEntity(qname.getNamespaceURI()); + String qnameLocalName = qname.getLocalName(); + if (typeQNameEntity == null || qnameNamespaceEntity == null) + { + // There can be no match; + return null; + } Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS_BY_ALL) - .setLong("parentId", parentNode.getId()) - .setLong("childId", childNode.getId()) - .setParameter("typeQName", assocTypeQName) - .setParameter("qname", qname); + .setParameter("parent", parentNode) + .setParameter("child", childNode) + .setParameter("typeQName", typeQNameEntity) + .setParameter("qnameNamespace", qnameNamespaceEntity) + .setParameter("qnameLocalName", qnameLocalName); return query.uniqueResult(); } }; @@ -960,12 +1000,21 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { public Object doInHibernate(Session session) { + QNameEntity typeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + NamespaceEntity qnameNamespaceEntity = qnameDAO.getNamespaceEntity(qname.getNamespaceURI()); + String qnameLocalName = qname.getLocalName(); + if (typeQNameEntity == null || qnameNamespaceEntity == null) + { + // There can be no match; + return Collections.emptyList(); + } Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS_BY_ALL) - .setLong("parentId", parentNode.getId()) - .setLong("childId", childNode.getId()) - .setParameter("typeQName", assocTypeQName) - .setParameter("qname", qname); + .setParameter("parent", parentNode) + .setParameter("child", childNode) + .setParameter("typeQName", typeQNameEntity) + .setParameter("qnameNamespace", qnameNamespaceEntity) + .setParameter("qnameLocalName", qnameLocalName); return query.list(); } }; @@ -984,13 +1033,19 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { public Object doInHibernate(Session session) { + QNameEntity typeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + if (typeQNameEntity == null) + { + // There can be no match; + return null; + } String childNameLower = childName.toLowerCase(); String childNameShort = getShortName(childNameLower); long childNameLowerCrc = getCrc(childNameLower); Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME) - .setLong("parentId", parentNode.getId()) - .setParameter("typeQName", assocTypeQName) + .setParameter("parent", parentNode) + .setParameter("typeQName", typeQNameEntity) .setParameter("childNodeName", childNameShort) .setLong("childNodeNameCrc", childNameLowerCrc); return query.uniqueResult(); @@ -1078,7 +1133,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements * @return Returns the parent associations without any interpretation */ @SuppressWarnings("unchecked") - private Collection getParentAssocsInternal(Node childNode) + private Collection getParentAssocsInternal(final Node childNode) { final Long childNodeId = childNode.getId(); List parentAssocs = null; @@ -1125,7 +1180,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_PARENT_ASSOCS) - .setLong("childId", childNodeId); + .setParameter("child", childNode); return query.list(); } }; @@ -1240,8 +1295,11 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public NodeAssoc newNodeAssoc(Node sourceNode, Node targetNode, QName assocTypeQName) { + // Get the assoc type QNameEntity + QNameEntity assocTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(assocTypeQName); + NodeAssoc assoc = new NodeAssocImpl(); - assoc.setTypeQName(assocTypeQName); + assoc.setTypeQName(assocTypeQNameEntity); assoc.buildAssociation(sourceNode, targetNode); // persist try @@ -1269,7 +1327,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOCS_TO_AND_FROM) - .setLong("nodeId", node.getId()); + .setParameter("node", node); return query.list(); } }; @@ -1286,11 +1344,17 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { public Object doInHibernate(Session session) { + QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + if (assocTypeQNameEntity == null) + { + // There can be no match; + return null; + } Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC) - .setLong("sourceId", sourceNode.getId()) - .setLong("targetId", targetNode.getId()) - .setParameter("assocTypeQName", assocTypeQName); + .setParameter("source", sourceNode) + .setParameter("target", targetNode) + .setParameter("assocTypeQName", assocTypeQNameEntity); return query.uniqueResult(); } }; @@ -1307,7 +1371,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_TARGET_ASSOCS) - .setLong("sourceId", sourceNode.getId()); + .setParameter("source", sourceNode); return query.list(); } }; @@ -1324,7 +1388,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_SOURCE_ASSOCS) - .setLong("targetId", targetNode.getId()); + .setParameter("target", targetNode); return query.list(); } }; @@ -1362,7 +1426,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Node node = (Node) results.get()[0]; // loop through all the node properties - Map properties = node.getProperties(); + Map properties = node.getProperties(); for (PropertyValue propertyValue : properties.values()) { // ignore nulls diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java index 62e0c7c017..6de802d775 100644 --- a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java +++ b/source/java/org/alfresco/repo/transaction/RetryingTransactionHelper.java @@ -218,7 +218,7 @@ public class RetryingTransactionHelper { if (requiresNew) { - txn = txnService.getNonPropagatingUserTransaction(); + txn = txnService.getNonPropagatingUserTransaction(readOnly); } else {