diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 5c0565f77e..ed811bcd2c 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -63,6 +63,42 @@ + + + + + + + + + + + + + + org.alfresco.cache.namespaceEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.namespaceEntityTransactionalCache + + + 100 + + + diff --git a/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql index 4d4ceb5396..6599634fd8 100644 --- a/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql +++ b/config/alfresco/dbscripts/create/2.2/org.hibernate.dialect.Dialect/AlfrescoPostCreate-2.2-Extra.sql @@ -15,21 +15,39 @@ -- Explicit indexes and constraints not declared in the mappings -- +CREATE INDEX fk_alf_cass_tqn ON alf_child_assoc (type_qname_id); +ALTER TABLE alf_child_assoc ADD CONSTRAINT fk_alf_cass_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); + +CREATE INDEX fk_alf_cass_qnns ON alf_child_assoc (qname_ns_id); +ALTER TABLE alf_child_assoc ADD CONSTRAINT fk_alf_cass_qnns FOREIGN KEY (qname_ns_id) REFERENCES alf_namespace (id); + +CREATE INDEX fk_alf_node_tqn ON alf_node (type_qname_id); +ALTER TABLE alf_node ADD CONSTRAINT fk_alf_node_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); + CREATE INDEX fk_alf_nasp_qn ON alf_node_aspects (qname_id); ALTER TABLE alf_node_aspects ADD CONSTRAINT fk_alf_nasp_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_alf_nass_tqn ON alf_node_assoc (type_qname_id); +ALTER TABLE alf_node_assoc ADD CONSTRAINT fk_alf_nass_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); + CREATE INDEX fk_alf_nprop_qn ON alf_node_properties (qname_id); ALTER TABLE alf_node_properties ADD CONSTRAINT fk_alf_nprop_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); CREATE INDEX fk_alf_nprop_loc ON alf_node_properties (locale_id); ALTER TABLE alf_node_properties ADD CONSTRAINT fk_alf_nprop_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id); +CREATE INDEX fk_alf_perm_tqn ON alf_permission (type_qname_id); +ALTER TABLE alf_permission ADD CONSTRAINT fk_alf_perm_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id); + CREATE INDEX fk_avm_nasp_qn ON avm_aspects (qname_id); ALTER TABLE avm_aspects ADD CONSTRAINT fk_avm_nasp_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); CREATE INDEX fk_avm_nprop_qn ON avm_node_properties (qname_id); ALTER TABLE avm_node_properties ADD CONSTRAINT fk_avm_nprop_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id); +CREATE INDEX fk_avm_sprop_qname ON avm_store_properties (qname_id); +ALTER TABLE avm_store_properties ADD CONSTRAINT fk_avm_sprop_qname FOREIGN KEY (qname_id) REFERENCES alf_qname (id); + CREATE INDEX idx_avm_hl_revpk ON avm_history_links (descendent, ancestor); CREATE INDEX idx_avm_vr_revuq ON avm_version_roots (avm_store_id, version_id); diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index 8348be39c8..6522262255 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -222,6 +222,9 @@ + + + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index cb4e1463b4..d9d294310e 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -236,7 +236,7 @@ user.name.caseSensitive=false avm.remote.idlestream.timeout=30000 # ECM content usages/quotas -system.usages.enabled=false +system.usages.enabled=true # Repository endpoint - used by Activity Service repo.remote.endpoint.url=http://localhost:8080/alfresco/service diff --git a/config/alfresco/scheduled-jobs-context.xml b/config/alfresco/scheduled-jobs-context.xml index 1db34c306a..16c5f57c1a 100644 --- a/config/alfresco/scheduled-jobs-context.xml +++ b/config/alfresco/scheduled-jobs-context.xml @@ -306,8 +306,9 @@ - - + + + ${spaces.store} @@ -320,7 +321,13 @@ - + + + 50 + + + 50 + ${system.usages.enabled} @@ -345,11 +352,9 @@ - @@ -380,13 +385,11 @@ - - + 0 diff --git a/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java index d78701e4e0..c1aece6ac5 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/AbstractPermissionChangePatch.java @@ -27,7 +27,6 @@ package org.alfresco.repo.admin.patch.impl; import org.alfresco.repo.admin.patch.AbstractPatch; import org.alfresco.repo.domain.DbPermission; import org.alfresco.repo.domain.QNameDAO; -import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.domain.hibernate.DbPermissionImpl; import org.alfresco.service.namespace.QName; import org.hibernate.Query; @@ -78,11 +77,11 @@ public abstract class AbstractPermissionChangePatch extends AbstractPatch /** Helper to get a permission entity */ private static class GetPermissionCallback implements HibernateCallback { - private QNameEntity typeQNameEntity; + private Long typeQNameId; private String name; - public GetPermissionCallback(QNameEntity typeQNameEntity, String name) + public GetPermissionCallback(Long typeQNameId, String name) { - this.typeQNameEntity = typeQNameEntity; + this.typeQNameId = typeQNameId; this.name = name; } public Object doInHibernate(Session session) @@ -91,7 +90,7 @@ public abstract class AbstractPermissionChangePatch extends AbstractPatch session.flush(); Query query = session.getNamedQuery(HibernateHelper.QUERY_GET_PERMISSION); - query.setParameter("permissionTypeQName", typeQNameEntity) + query.setLong("permissionTypeQNameId", typeQNameId) .setString("permissionName", name); return query.uniqueResult(); } @@ -120,23 +119,23 @@ public abstract class AbstractPermissionChangePatch extends AbstractPatch } // Get the QName entities - QNameEntity oldTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(oldTypeQName); - QNameEntity newTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(newTypeQName); + Long oldTypeQNameId = qnameDAO.getOrCreateQName(oldTypeQName).getFirst(); + Long newTypeQNameId = qnameDAO.getOrCreateQName(newTypeQName).getFirst(); - HibernateCallback getNewPermissionCallback = new GetPermissionCallback(oldTypeQNameEntity, oldName); + HibernateCallback getNewPermissionCallback = new GetPermissionCallback(oldTypeQNameId, oldName); DbPermission permission = (DbPermission) getHibernateTemplate().execute(getNewPermissionCallback); if (permission == null) { // create the permission permission = new DbPermissionImpl(); - permission.setTypeQName(newTypeQNameEntity); + permission.setTypeQNameId(newTypeQNameId); permission.setName(newName); // save getHibernateTemplate().save(permission); } else { - permission.setTypeQName(newTypeQNameEntity); + permission.setTypeQNameId(newTypeQNameId); permission.setName(newName); } // done diff --git a/source/java/org/alfresco/repo/admin/patch/impl/CalendarModelUriPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/CalendarModelUriPatch.java index 81566bd1e9..f528e8d155 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/CalendarModelUriPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/CalendarModelUriPatch.java @@ -76,7 +76,7 @@ public class CalendarModelUriPatch extends AbstractPatch protected String applyInternal() throws Exception { // modify namespace for all calendar entries - qnameDAO.updateNamespaceEntity(URI_BEFORE, URI_AFTER); + qnameDAO.updateNamespace(URI_BEFORE, URI_AFTER); // reindex the calendar entries int count = reindex("TYPE:\\{" + QueryParser.escape(URI_BEFORE) + "\\}*", importerBootstrap.getStoreRef()); diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 69f21648ca..0a2ec1ad9c 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -2335,6 +2335,7 @@ public class AVMRepository * The sql 'like' pattern, inserted into a QName. * @return A Map of the matching key value pairs. */ + @SuppressWarnings("unchecked") public Map queryStorePropertyKey(String store, QName keyPattern) { AVMStore st = getAVMStoreByName(store); @@ -2342,15 +2343,14 @@ public class AVMRepository { throw new AVMNotFoundException("Store not found."); } - List matches = - fAVMStorePropertyDAO.queryByKeyPattern(st, - keyPattern); - Map results = new HashMap(); + List matches = fAVMStorePropertyDAO.queryByKeyPattern(st, keyPattern); + Map matchesMap = new HashMap(); for (AVMStoreProperty prop : matches) { - results.put(prop.getName().getQName(), prop.getValue()); + matchesMap.put(prop.getQnameId(), prop.getValue()); } - return results; + Map propertyMap = (Map) qnameDAO.convertIdMapToQNameMap(matchesMap); + return propertyMap; } /** @@ -2363,20 +2363,19 @@ public class AVMRepository public Map> queryStoresPropertyKeys(QName keyPattern) { - List matches = - fAVMStorePropertyDAO.queryByKeyPattern(keyPattern); - Map> results = - new HashMap>(); + List matches = fAVMStorePropertyDAO.queryByKeyPattern(keyPattern); + Map> results = new HashMap>(); for (AVMStoreProperty prop : matches) { String storeName = prop.getStore().getName(); + QName propQName = qnameDAO.getQName(prop.getQnameId()).getSecond(); Map pairs = null; if ((pairs = results.get(storeName)) == null) { pairs = new HashMap(); results.put(storeName, pairs); } - pairs.put(prop.getName().getQName(), prop.getValue()); + pairs.put(propQName, prop.getValue()); } return results; } @@ -3203,20 +3202,16 @@ public class AVMRepository context.addDynamicAuthorityAssignment(AVMNodeUnwrapper.Unwrap(node).getBasicAttributes().getOwner(), PermissionService.OWNER_AUTHORITY); // Pass in node aspects - Set nodeAspectQNameIds = node.getAspects(); + Set nodeAspectQNames = qnameDAO.convertIdsToQNames(node.getAspects()); Set contextQNames = context.getAspects(); - for (Long nodeAspectQNameId : nodeAspectQNameIds) - { - QName qname = qnameDAO.getQName(nodeAspectQNameId); - contextQNames.add(qname); - } + contextQNames.addAll(nodeAspectQNames); // 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()); + QName qname = qnameDAO.getQName(entry.getKey()).getSecond(); PropertyDefinition def = fDictionaryService.getProperty(qname); if (def == null) { diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 9610231256..8b5a3e186c 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -42,9 +42,8 @@ import org.alfresco.repo.avm.util.RawServices; 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.ACLCopyMode; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.avm.AVMBadArgumentException; import org.alfresco.service.cmr.avm.AVMException; @@ -373,7 +372,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Set aspectQNameEntityIds = newDir.getAspects(); for (QName aspectQName : aspects) { - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectQName).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(aspectQName).getFirst(); aspectQNameEntityIds.add(qnameEntityId); } } @@ -384,7 +383,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); for (Map.Entry entry : properties.entrySet()) { - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(entry.getKey()).getFirst(); propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); } newDir.getProperties().putAll(propertiesByQNameEntityId); @@ -524,7 +523,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Set aspectQNameEntityIds = file.getAspects(); for (QName aspectQName : aspects) { - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectQName).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(aspectQName).getFirst(); aspectQNameEntityIds.add(qnameEntityId); } } @@ -535,7 +534,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); for (Map.Entry entry : properties.entrySet()) { - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(entry.getKey()).getFirst(); propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); } file.getProperties().putAll(propertiesByQNameEntityId); @@ -1217,9 +1216,9 @@ public class AVMStoreImpl implements AVMStore, Serializable { throw new AccessDeniedException("Not allowed to write: " + path); } - QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(name); + Long qnameEntityId = AVMDAOs.Instance().fQNameDAO.getOrCreateQName(name).getFirst(); - node.setProperty(qnameEntity.getId(), value); + node.setProperty(qnameEntityId, value); node.setGuid(GUID.generate()); } @@ -1247,7 +1246,7 @@ public class AVMStoreImpl implements AVMStore, Serializable Map propertiesByQNameEntityId = new HashMap(properties.size() * 2 + 1); for (Map.Entry entry : properties.entrySet()) { - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(entry.getKey()).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(entry.getKey()).getFirst(); propertiesByQNameEntityId.put(qnameEntityId, entry.getValue()); } node.addProperties(propertiesByQNameEntityId); @@ -1276,15 +1275,15 @@ public class AVMStoreImpl implements AVMStore, Serializable } // Convert the QName QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; - QNameEntity qnameEntity = qnameDAO.getOrCreateQNameEntity(name); - if (qnameEntity == null) + Pair qnamePair = qnameDAO.getQName(name); + if (qnamePair == null) { // No such QName return null; } else { - PropertyValue prop = node.getProperty(qnameEntity.getId()); + PropertyValue prop = node.getProperty(qnamePair.getFirst()); return prop; } } @@ -1335,14 +1334,15 @@ public class AVMStoreImpl implements AVMStore, Serializable node.setGuid(GUID.generate()); // convert the QName - QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getQNameEntity(name); - if (qnameEntity == null) + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Pair qnamePair = qnameDAO.getQName(name); + if (qnamePair == null) { // No such property } else { - node.deleteProperty(qnameEntity.getId()); + node.deleteProperty(qnamePair.getFirst()); } } @@ -1373,10 +1373,10 @@ public class AVMStoreImpl implements AVMStore, Serializable */ public void setProperty(QName name, PropertyValue value) { - QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(name); + Long qnameEntityId = AVMDAOs.Instance().fQNameDAO.getOrCreateQName(name).getFirst(); AVMStoreProperty prop = new AVMStorePropertyImpl(); prop.setStore(this); - prop.setName(qnameEntity); + prop.setQnameId(qnameEntityId); prop.setValue(value); AVMDAOs.Instance().fAVMStorePropertyDAO.save(prop); } @@ -1412,16 +1412,21 @@ public class AVMStoreImpl implements AVMStore, Serializable * Get all the properties associated with this node. * @return A Map of the properties. */ + @SuppressWarnings("unchecked") public Map getProperties() { - List props = - AVMDAOs.Instance().fAVMStorePropertyDAO.get(this); - Map retVal = new HashMap(); + List props = AVMDAOs.Instance().fAVMStorePropertyDAO.get(this); + + Map propsIdMap = new HashMap(props.size() + 7); for (AVMStoreProperty prop : props) { - retVal.put(prop.getName().getQName(), prop.getValue()); + propsIdMap.put(prop.getQnameId(), prop.getValue()); } - return retVal; + // Mass-convert + QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; + Map propsQNameMap = (Map) qnameDAO.convertIdMapToQNameMap(propsIdMap); + + return propsQNameMap; } /** @@ -1553,7 +1558,7 @@ public class AVMStoreImpl implements AVMStore, Serializable } // Convert the aspect QNames to entities QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; - Long qnameEntityId = qnameDAO.getOrCreateQNameEntity(aspectName).getId(); + Long qnameEntityId = qnameDAO.getOrCreateQName(aspectName).getFirst(); // Convert the node.getAspects().add(qnameEntityId); node.setGuid(GUID.generate()); @@ -1602,18 +1607,19 @@ public class AVMStoreImpl implements AVMStore, Serializable } 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) + Pair qnamePair = qnameDAO.getQName(aspectName); + if (qnamePair != null) { - node.getAspects().remove(qnameEntity.getId()); + node.getAspects().remove(qnamePair.getFirst()); } AspectDefinition def = RawServices.Instance().getDictionaryService().getAspect(aspectName); - Map properties = - def.getProperties(); - for (QName name : properties.keySet()) + Map properties = def.getProperties(); + Set propertyQNameIds = qnameDAO.convertQNamesToIds(properties.keySet(), false); + + Map nodeProperties = node.getProperties(); + for (Long propertyQNameId : propertyQNameIds) { - QNameEntity propertyQNameEntity = qnameDAO.getQNameEntity(name); - node.getProperties().remove(propertyQNameEntity.getId()); + nodeProperties.remove(propertyQNameId); } node.setGuid(GUID.generate()); } @@ -1639,10 +1645,10 @@ public class AVMStoreImpl implements AVMStore, Serializable } 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) + Pair qnamePair = qnameDAO.getQName(aspectName); + if (qnamePair != null) { - return node.getAspects().contains(qnameEntity.getId()); + return node.getAspects().contains(qnamePair.getFirst()); } else { @@ -1750,12 +1756,12 @@ public class AVMStoreImpl implements AVMStore, Serializable toLink.setVersionID(child.getVersionID() + 1); // TODO This really shouldn't be here. Leaking layers. QNameDAO qnameDAO = AVMDAOs.Instance().fQNameDAO; - QNameEntity revertedQNameEntity = qnameDAO.getOrCreateQNameEntity(WCMModel.ASPECT_REVERTED); - toLink.getAspects().add(revertedQNameEntity.getId()); + Pair revertedQNamePair = qnameDAO.getOrCreateQName(WCMModel.ASPECT_REVERTED); + toLink.getAspects().add(revertedQNamePair.getFirst()); PropertyValue value = new PropertyValue(null, toRevertTo.getId()); - QNameEntity qnameEntity = AVMDAOs.Instance().fQNameDAO.getOrCreateQNameEntity(WCMModel.PROP_REVERTED_ID); - toLink.setProperty(qnameEntity.getId(), value); + Pair qnamePair = AVMDAOs.Instance().fQNameDAO.getOrCreateQName(WCMModel.PROP_REVERTED_ID); + toLink.setProperty(qnamePair.getFirst(), value); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/avm/AVMStoreProperty.java b/source/java/org/alfresco/repo/avm/AVMStoreProperty.java index d2e3e38a55..b9474efe19 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreProperty.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreProperty.java @@ -24,7 +24,6 @@ package org.alfresco.repo.avm; import org.alfresco.repo.domain.PropertyValue; -import org.alfresco.repo.domain.QNameEntity; /** * Arbitrary properties associated with AVMStores. @@ -45,16 +44,18 @@ public interface AVMStoreProperty public AVMStore getStore(); /** - * Set the name of the property. - * @param qnameEntity The QNameEntity for the property. + * Set the property type. + * + * @param qnameId the ID of the property QName */ - public void setName(QNameEntity qnameEntity); + public void setQnameId(Long qnameId); /** - * Get the name of this property. - * @return The QNameEntity of this property. + * Get the property type. + * + * @return returns the ID of the property QName */ - public QNameEntity getName(); + public Long getQnameId(); /** * 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 73b551334d..8aac37943b 100644 --- a/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStorePropertyImpl.java @@ -26,7 +26,6 @@ package org.alfresco.repo.avm; import java.io.Serializable; import org.alfresco.repo.domain.PropertyValue; -import org.alfresco.repo.domain.QNameEntity; /** * Simple bean to hold properties attached to AVMStores. @@ -49,7 +48,7 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable /** * The name of the property. */ - private QNameEntity name; + private Long qnameId; /** * The actual PropertyValue. @@ -63,17 +62,17 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable /** * {@inheritDoc} */ - public QNameEntity getName() + public Long getQnameId() { - return name; + return qnameId; } /** * {@inheritDoc} */ - public void setName(QNameEntity name) + public void setQnameId(Long qnameId) { - this.name = name; + this.qnameId = qnameId; } /** @@ -142,13 +141,13 @@ class AVMStorePropertyImpl implements AVMStoreProperty, Serializable return false; } AVMStoreProperty o = (AVMStoreProperty)other; - return fStore.equals(o.getStore()) && name.equals(o.getName()); + return fStore.equals(o.getStore()) && qnameId.equals(o.getQnameId()); } @Override public int hashCode() { - return fStore.hashCode() + name.hashCode(); + return fStore.hashCode() + qnameId.hashCode(); } } 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 d3766d6c22..9d36df097e 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml +++ b/source/java/org/alfresco/repo/avm/hibernate/AVM.hbm.xml @@ -49,12 +49,12 @@ - + - + @@ -151,16 +151,7 @@ - + @@ -342,4 +333,48 @@ ]]> + + + SELECT + {asp.*} + FROM + avm_store_properties asp + JOIN alf_qname qn ON (qn.id = asp.qname_id) + JOIN alf_namespace ns ON (ns.id = qn.ns_id) + WHERE + ns.uri LIKE :uri AND + qn.local_name LIKE :localName + + + + + SELECT + {asp.*} + FROM + avm_store_properties asp + JOIN alf_qname qn ON (qn.id = asp.qname_id) + JOIN alf_namespace ns ON (ns.id = qn.ns_id) + WHERE + asp.avm_store_id = :storeId AND + ns.uri LIKE :uri AND + qn.local_name LIKE :localName + + + + DELETE + FROM + avm_store_properties asp + WHERE + asp.avm_store_id = :storeId AND + asp.qname_id = :qnameId + + + + DELETE + FROM + avm_store_properties asp + WHERE + asp.avm_store_id = :storeId + + diff --git a/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java b/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java index 8e5c067111..4db40dada3 100644 --- a/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java +++ b/source/java/org/alfresco/repo/avm/hibernate/AVMStorePropertyDAOHibernate.java @@ -29,8 +29,8 @@ 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.alfresco.util.Pair; import org.hibernate.Query; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; @@ -67,8 +67,8 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto */ public AVMStoreProperty get(AVMStore store, QName name) { - QNameEntity qnameEntity = qnameDAO.getQNameEntity(name); - if (qnameEntity == null) + Pair qnamePair = qnameDAO.getQName(name); + if (qnamePair == null) { // No such QName return null; @@ -81,9 +81,9 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto "from AVMStorePropertyImpl asp " + "where " + "asp.store = :store and " + - "asp.name = :name"); + "asp.qnameId = :qnameId"); query.setEntity("store", store); - query.setEntity("name", qnameEntity); + query.setLong("qnameId", qnamePair.getFirst()); return (AVMStoreProperty)query.uniqueResult(); } } @@ -122,19 +122,20 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto { localName = "%"; } - Query query = - getSession().createQuery( - "select asp " + - "from AVMStorePropertyImpl asp " + - "join asp.name name " + - "join name.namespace namespace " + - "where " + - "asp.store = :store and " + - "namespace.safeUri like :uri and " + - "name.localName like :localName"); - query.setEntity("store", store); + Query query = getSession().getNamedQuery("avm.GetStorePropertiesByStoreAndKeyPattern"); + query.setLong("storeId", store.getId()); query.setParameter("uri", uri); query.setParameter("localName", localName); +// Query query = +// getSession().createQuery( +// "select asp " + +// "from AVMStorePropertyImpl asp " + +// "join asp.name name " + +// "join name.namespace namespace " + +// "where " + +// "asp.store = :store and " + +// "namespace.safeUri like :uri and " + +// "name.localName like :localName"); return (List)query.list(); } @@ -157,17 +158,20 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto { localName = "%"; } - Query query = - getSession().createQuery( - "select asp " + - "from AVMStorePropertyImpl asp " + - "join asp.name name " + - "join name.namespace namespace " + - "where " + - "namespace.safeUri like :uri and " + - "name.localName like :localName"); - query.setParameter("uri", uri); - query.setParameter("localName", localName); + Query query = getSession().getNamedQuery("avm.GetStorePropertiesByKeyPattern"); + query.setParameter("uri", uri); + query.setParameter("localName", localName); +// Query query = +// getSession().createQuery( +// "select asp " + +// "from AVMStorePropertyImpl asp " + +// "join asp.name name " + +// "join name.namespace namespace " + +// "where " + +// "namespace.safeUri like :uri and " + +// "name.localName like :localName"); +// query.setParameter("uri", uri); +// query.setParameter("localName", localName); return (List)query.list(); } @@ -187,14 +191,12 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto */ public void delete(AVMStore store, QName name) { - QNameEntity qnameEntity = qnameDAO.getQNameEntity(name); - if (qnameEntity != null) + Pair qnamePair = qnameDAO.getQName(name); + if (qnamePair != 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); + Query delete = getSession().getNamedQuery("avm.DeleteStorePropertiesByType"); + delete.setParameter("storeId", store.getId()); + delete.setParameter("qnameId", qnamePair.getFirst()); delete.executeUpdate(); } } @@ -205,9 +207,8 @@ class AVMStorePropertyDAOHibernate extends HibernateDaoSupport implements AVMSto */ public void delete(AVMStore store) { - Query delete = - getSession().createQuery("delete from AVMStorePropertyImpl asp where asp.store = :store"); - delete.setEntity("store", store); + Query delete = getSession().getNamedQuery("avm.DeleteStoreProperties"); + delete.setParameter("storeId", store.getId()); delete.executeUpdate(); } } diff --git a/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformer.java b/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformer.java index dfd36ef5ba..f29af61c96 100644 --- a/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformer.java +++ b/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformer.java @@ -156,6 +156,10 @@ public class OpenOfficeContentTransformer extends AbstractContentTransformer2 { return false; } + else if (targetMimetype.equals(MimetypeMap.MIMETYPE_FLASH)) + { + return false; + } MimetypeService mimetypeService = getMimetypeService(); String sourceExtension = mimetypeService.getExtension(sourceMimetype); diff --git a/source/java/org/alfresco/repo/domain/ChildAssoc.java b/source/java/org/alfresco/repo/domain/ChildAssoc.java index 2522b3c481..bf67a03295 100644 --- a/source/java/org/alfresco/repo/domain/ChildAssoc.java +++ b/source/java/org/alfresco/repo/domain/ChildAssoc.java @@ -58,18 +58,43 @@ public interface ChildAssoc extends Comparable /** * A convenience method to get a reference to this association. * + * @param qnameDAO the DAO to resolve the qname references * @return Returns a reference to this association */ - public ChildAssociationRef getChildAssocRef(); + public ChildAssociationRef getChildAssocRef(QNameDAO qnameDAO); + /** + * Convenience method to get the association's type + * + * @param qnameDAO the helper DAO + * @return the association's type QName + */ + public QName getTypeQName(QNameDAO qnameDAO); + + /** + * Convenience method to set the association's type + * + * @param qnameDAO the helper DAO + * @param typeQName the association's type QName + */ + public void setTypeQName(QNameDAO qnameDAO, QName typeQName); + /** * 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 QName getQName(QNameDAO qnameDAO); + /** + * Convenience method to set the association's qname + * + * @param qnameDAO the helper DAO + * @param qname the association's QName + */ + public void setQName(QNameDAO qnameDAO, QName qname); + public Long getId(); /** @@ -84,12 +109,12 @@ public interface ChildAssoc extends Comparable /** * @return Returns the type of the association */ - public QNameEntity getTypeQName(); + public Long getTypeQNameId(); /** - * @param typeQName the association's dictionary type + * @param typeQNameId the association's dictionary type */ - public void setTypeQName(QNameEntity typeQName); + public void setTypeQNameId(Long typeQNameId); /** * @return Returns the child node name. This may be truncated, in which case it @@ -116,12 +141,12 @@ public interface ChildAssoc extends Comparable /** * @return Returns the namespace of the association's local QName */ - public NamespaceEntity getQnameNamespace(); + public Long getQnameNamespaceId(); /** - * @param namespace the namespace of the association's local QName + * @param namespaceId the namespace of the association's local QName */ - public void setQnameNamespace(NamespaceEntity namespace); + public void setQnameNamespaceId(Long namespaceId); /** * @return Returns the localname of the association's local QName diff --git a/source/java/org/alfresco/repo/domain/DbPermission.java b/source/java/org/alfresco/repo/domain/DbPermission.java index d05e1ba19f..076b447fb0 100644 --- a/source/java/org/alfresco/repo/domain/DbPermission.java +++ b/source/java/org/alfresco/repo/domain/DbPermission.java @@ -26,6 +26,8 @@ package org.alfresco.repo.domain; import java.io.Serializable; +import org.alfresco.service.namespace.QName; + /** * The interface against which permission references are persisted in hibernate. * @@ -33,6 +35,14 @@ import java.io.Serializable; */ public interface DbPermission extends Serializable { + /** + * Convenience method to get the type QName of the permission + * + * @param qnameDAO helper DAO + * @return the permission's type QName + */ + public QName getTypeQName(QNameDAO qnameDAO); + /** * @return Returns the automatically assigned ID */ @@ -46,12 +56,12 @@ public interface DbPermission extends Serializable /** * @return Returns the qualified name of this permission */ - public QNameEntity getTypeQName(); + public Long getTypeQNameId(); /** - * @param qname the entity representing the qname for this instance + * @param typeQNameId the ID of the QName for this instance */ - public void setTypeQName(QNameEntity typeQNameEntity); + public void setTypeQNameId(Long typeQNameId); /** * @return Returns the permission name @@ -64,7 +74,7 @@ public interface DbPermission extends Serializable public void setName(String name); /** - * @return Returns a key combining the {@link #getTypeQname() type} + * @return Returns a key combining the {@link #getTypeQnameId() type} * and {@link #getName() name} */ public DbPermissionKey getKey(); diff --git a/source/java/org/alfresco/repo/domain/DbPermissionKey.java b/source/java/org/alfresco/repo/domain/DbPermissionKey.java index 7b89cdfc15..a2f2338995 100644 --- a/source/java/org/alfresco/repo/domain/DbPermissionKey.java +++ b/source/java/org/alfresco/repo/domain/DbPermissionKey.java @@ -26,7 +26,6 @@ package org.alfresco.repo.domain; import java.io.Serializable; -import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** @@ -38,23 +37,23 @@ public class DbPermissionKey implements Serializable { private static final long serialVersionUID = -1667797216480779296L; - private QName typeQname; + private Long typeQNameId; private String name; public DbPermissionKey() { } - public DbPermissionKey(QName typeQname, String name) + public DbPermissionKey(Long typeQNameId, String name) { - this.typeQname = typeQname; + this.typeQNameId = typeQNameId; this.name = name; } public String toString() { return ("DbPermissionKey" + - "[ type=" + typeQname + + "[ type=" + typeQNameId + ", name=" + name + "]"); } @@ -75,23 +74,23 @@ public class DbPermissionKey implements Serializable return false; } DbPermissionKey that = (DbPermissionKey) obj; - return (EqualsHelper.nullSafeEquals(this.typeQname, that.typeQname) + return (EqualsHelper.nullSafeEquals(this.typeQNameId, that.typeQNameId) && EqualsHelper.nullSafeEquals(this.name, that.name) ); } - public QName getTypeQname() + public Long getTypeQNameId() { - return typeQname; + return typeQNameId; } /** * Tamper-proof method only to be used by introspectors */ @SuppressWarnings("unused") - private void setTypeQname(QName typeQname) + private void setTypeQName(Long typeQNameId) { - this.typeQname = typeQname; + this.typeQNameId = typeQNameId; } public String getName() diff --git a/source/java/org/alfresco/repo/domain/Node.java b/source/java/org/alfresco/repo/domain/Node.java index c403ffe960..e1279a524a 100644 --- a/source/java/org/alfresco/repo/domain/Node.java +++ b/source/java/org/alfresco/repo/domain/Node.java @@ -28,6 +28,7 @@ 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. @@ -44,6 +45,22 @@ public interface Node * @return Returns the reference to this node */ public NodeRef getNodeRef(); + + /** + * Convenience method to get the node's type + * + * @param qnameDAO the helper DAO + * @return the node's type QName + */ + public QName getTypeQName(QNameDAO qnameDAO); + + /** + * Convenience method to set the node's type + * + * @param qnameDAO the helper DAO + * @param qname the node's type QName + */ + public void setTypeQName(QNameDAO qnameDAO, QName qname); /** * @return Returns the auto-generated ID @@ -71,9 +88,9 @@ public interface Node public void setDeleted(boolean deleted); - public QNameEntity getTypeQName(); + public Long getTypeQNameId(); - public void setTypeQName(QNameEntity typeQName); + public void setTypeQNameId(Long typeQNameId); public DbAccessControlList getAccessControlList(); diff --git a/source/java/org/alfresco/repo/domain/NodeAssoc.java b/source/java/org/alfresco/repo/domain/NodeAssoc.java index 8147783ae2..cfaf6c1663 100644 --- a/source/java/org/alfresco/repo/domain/NodeAssoc.java +++ b/source/java/org/alfresco/repo/domain/NodeAssoc.java @@ -25,6 +25,7 @@ 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 @@ -47,7 +48,29 @@ public interface NodeAssoc */ public void buildAssociation(Node sourceNode, Node targetNode); - public AssociationRef getNodeAssocRef(); + /** + * Convenience method to retrieve the association's reference + * + * @param qnameDAO helper DAO + * @return the association's reference + */ + public AssociationRef getNodeAssocRef(QNameDAO qnameDAO); + + /** + * Convenience method to retrieve the association's type QName + * + * @param qnameDAO helper DAO + * @return the association's type QName + */ + public QName getTypeQName(QNameDAO qnameDAO); + + /** + * Convenience method to set the association's type + * + * @param qnameDAO the helper DAO + * @param typeQName the association's type QName + */ + public void setTypeQName(QNameDAO qnameDAO, QName typeQName); public Long getId(); @@ -63,10 +86,10 @@ public interface NodeAssoc /** * @return Returns the type of the association */ - public QNameEntity getTypeQName(); + public Long getTypeQNameId(); /** - * @param typeQName the association's dictionary type + * @param typeQNameId the association's dictionary type */ - public void setTypeQName(QNameEntity typeQName); + public void setTypeQNameId(Long typeQNameId); } diff --git a/source/java/org/alfresco/repo/domain/QNameDAO.java b/source/java/org/alfresco/repo/domain/QNameDAO.java index 06f7f7aa44..ce433f084e 100644 --- a/source/java/org/alfresco/repo/domain/QNameDAO.java +++ b/source/java/org/alfresco/repo/domain/QNameDAO.java @@ -41,16 +41,16 @@ public interface QNameDAO { /** * @param id the unique ID of the entity - * @return the namespace entity (never null) + * @return the namespace pair (id, uri) * @throws AlfrescoRuntimeException if the ID provided is invalid */ - NamespaceEntity getNamespaceEntity(Long id); + Pair getNamespace(Long id); /** * @param namespaceUri the namespace URI to query for - * @return the namespace entity of null if it doesn't exist + * @return the namespace pair (id, uri) or null if it doesn't exist */ - NamespaceEntity getNamespaceEntity(String namespaceUri); + Pair getNamespace(String namespaceUri); /** * Get an existing instance matching the URI or create one if necessary. @@ -58,45 +58,38 @@ public interface QNameDAO * 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 + * @return the existing namespace pair (id, uri) or a new one */ - NamespaceEntity getOrCreateNamespaceEntity(String namespaceUri); + Pair getOrCreateNamespace(String namespaceUri); /** * @param namespaceUri the namespace URI to create - * @return Returns the new instance + * @return the new namespace pair (id, uri) */ - NamespaceEntity newNamespaceEntity(String namespaceUri); + Pair newNamespace(String namespaceUri); /** * Modifies an existing namespace URI. If the new URI already exists, then no - * new entity is created and a concurrency + * new entity is created. * * @param oldNamespaceUri the old namespace URI * @param newNamespaceUri the new namespace URI * @throws AlfrescoRuntimeException if the new namespace is in use */ - void updateNamespaceEntity(String oldNamespaceUri, String newNamespaceUri); + void updateNamespace(String oldNamespaceUri, String newNamespaceUri); /** * @param id the unique ID of the entity - * @return the QName entity (never null) + * @return the QName pair (id, qname) (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); + Pair getQName(Long id); /** * @param qname the QName to query for - * @return the QName entity of null if it doesn't exist + * @return the QName pair (id, qname) or null if it doesn't exist */ - QNameEntity getQNameEntity(QName qname); + Pair getQName(QName qname); /** * Get an existing instance matching the QName or create one if necessary. @@ -104,17 +97,15 @@ public interface QNameDAO * 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 + * @return the QName pair (id, qname) or a new one */ - QNameEntity getOrCreateQNameEntity(QName qname); - - Pair getOrCreateQNamePair(QName qname); + Pair getOrCreateQName(QName qname); /** * @param qname the QName to create - * @return the new instance + * @return the new QName pair (id, qname) */ - QNameEntity newQNameEntity(QName qname); + Pair newQName(QName qname); Set convertIdsToQNames(Set ids); diff --git a/source/java/org/alfresco/repo/domain/QNameDAOTest.java b/source/java/org/alfresco/repo/domain/QNameDAOTest.java index 0a54a78c1f..af716b3631 100644 --- a/source/java/org/alfresco/repo/domain/QNameDAOTest.java +++ b/source/java/org/alfresco/repo/domain/QNameDAOTest.java @@ -39,6 +39,7 @@ import org.alfresco.service.ServiceRegistry; import org.alfresco.service.namespace.QName; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.GUID; +import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; @@ -76,17 +77,17 @@ public class QNameDAOTest extends TestCase public void testNewNamespace() throws Exception { final String namespaceUri = GUID.generate(); - RetryingTransactionCallback callback = new RetryingTransactionCallback() + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { - public NamespaceEntity execute() throws Throwable + public Pair execute() throws Throwable { - NamespaceEntity namespace = dao.getNamespaceEntity(namespaceUri); - assertNull("Namespace should not exist yet", namespace); + Pair namespacePair = dao.getNamespace(namespaceUri); + assertNull("Namespace should not exist yet", namespacePair); // Now make it - namespace = dao.newNamespaceEntity(namespaceUri); - assertNotNull("Namespace should now exist", dao.getNamespaceEntity(namespaceUri)); + namespacePair = dao.newNamespace(namespaceUri); + assertNotNull("Namespace should now exist", dao.getNamespace(namespaceUri)); // Done - return namespace; + return namespacePair; } }; retryingTransactionHelper.doInTransaction(callback); @@ -102,17 +103,17 @@ public class QNameDAOTest extends TestCase { public Object execute() throws Throwable { - dao.getOrCreateNamespaceEntity(namespaceUriBefore); + dao.getOrCreateNamespace(namespaceUriBefore); // Get a QName that has the URI - QNameEntity qnameEntityBefore = dao.getOrCreateQNameEntity(qnameBefore); + Long qnameIdBefore = dao.getOrCreateQName(qnameBefore).getFirst(); // Now modify the namespace - dao.updateNamespaceEntity(namespaceUriBefore, namespaceUriAfter); + dao.updateNamespace(namespaceUriBefore, namespaceUriAfter); // The old qname must be gone - assertNull("QName must be gone as the URI was renamed", dao.getQNameEntity(qnameBefore)); + assertNull("QName must be gone as the URI was renamed", dao.getQName(qnameBefore)); // The new QName must be present and with the same ID - QNameEntity qnameEntityAfter = dao.getQNameEntity(qnameAfter); - assertNotNull("Expected QName with new URI to exist.", qnameEntityAfter); - assertEquals("QName changed ID unexpectedly.", qnameEntityBefore.getId(), qnameEntityAfter.getId()); + Pair qnamePairAfter = dao.getQName(qnameAfter); + assertNotNull("Expected QName with new URI to exist.", qnamePairAfter); + assertEquals("QName changed ID unexpectedly.", qnameIdBefore, qnamePairAfter.getFirst()); // Done return null; } @@ -125,17 +126,17 @@ public class QNameDAOTest extends TestCase final String namespaceUri = GUID.generate(); final String localName = GUID.generate(); final QName qname = QName.createQName(namespaceUri, localName); - RetryingTransactionCallback callback = new RetryingTransactionCallback() + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { - public QNameEntity execute() throws Throwable + public Pair execute() throws Throwable { - QNameEntity qnameEntity = dao.getQNameEntity(qname); - assertNull("QName should not exist yet", qnameEntity); + Pair qnamePair = dao.getQName(qname); + assertNull("QName should not exist yet", qnamePair); // Now make it - qnameEntity = dao.newQNameEntity(qname); - assertNotNull("QName should now exist", dao.getQNameEntity(qname)); + qnamePair = dao.newQName(qname); + assertNotNull("QName should now exist", dao.getQName(qname)); // Done - return qnameEntity; + return qnamePair; } }; retryingTransactionHelper.doInTransaction(callback); @@ -146,27 +147,27 @@ public class QNameDAOTest extends TestCase final String namespaceUri = GUID.generate(); final String localName = GUID.generate(); final QName qname = QName.createQName(namespaceUri, localName); - RetryingTransactionCallback callback = new RetryingTransactionCallback() + RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { - public QNameEntity execute() throws Throwable + public Pair execute() throws Throwable { - QNameEntity qnameEntity = dao.getQNameEntity(qname); - assertNull("QName should not exist yet", qnameEntity); + Pair qnamePair = dao.getQName(qname); + assertNull("QName should not exist yet", qnamePair); // Now make it - qnameEntity = dao.newQNameEntity(qname); - assertNotNull("QName should now exist", dao.getQNameEntity(qname)); + qnamePair = dao.newQName(qname); + assertNotNull("QName should now exist", dao.getQName(qname)); // Done - return qnameEntity; + return qnamePair; } }; retryingTransactionHelper.doInTransaction(callback); - callback = new RetryingTransactionCallback() + callback = new RetryingTransactionCallback>() { - public QNameEntity execute() throws Throwable + public Pair execute() throws Throwable { for (int i = 0; i < 1000; i++) { - dao.getQNameEntity(qname); + dao.getQName(qname); } return null; } @@ -194,8 +195,8 @@ public class QNameDAOTest extends TestCase public Object execute() throws Throwable { // Create QNames with lowercase values - dao.getOrCreateQNameEntity(namespaceUriLowerQName); - dao.getOrCreateQNameEntity(localNameLowerQName); + dao.getOrCreateQName(namespaceUriLowerQName); + dao.getOrCreateQName(localNameLowerQName); // Done return null; } @@ -206,21 +207,21 @@ public class QNameDAOTest extends TestCase public Object execute() throws Throwable { // Check namespace case-insensitivity - QNameEntity namespaceUriLowerQNameEntity = dao.getQNameEntity(namespaceUriLowerQName); - assertNotNull(namespaceUriLowerQNameEntity); - QNameEntity namespaceUriUpperQNameEntity = dao.getOrCreateQNameEntity(namespaceUriUpperQName); - assertNotNull(namespaceUriUpperQNameEntity); + Pair namespaceUriLowerQNamePair = dao.getQName(namespaceUriLowerQName); + assertNotNull(namespaceUriLowerQNamePair); + Pair namespaceUriUpperQNamePair = dao.getOrCreateQName(namespaceUriUpperQName); + assertNotNull(namespaceUriUpperQNamePair); assertEquals( "Didn't resolve case-insensitively on namespace", - namespaceUriUpperQNameEntity.getId(), namespaceUriUpperQNameEntity.getId()); + namespaceUriLowerQNamePair.getFirst(), namespaceUriUpperQNamePair.getFirst()); // Check localname case-insensitivity - QNameEntity localNameLowerQNameEntity = dao.getQNameEntity(localNameLowerQName); - assertNotNull(localNameLowerQNameEntity); - QNameEntity localNameUpperQNameEntity = dao.getOrCreateQNameEntity(localNameUpperQName); - assertNotNull(localNameUpperQNameEntity); + Pair localNameLowerQNamePair = dao.getQName(localNameLowerQName); + assertNotNull(localNameLowerQNamePair); + Pair localNameUpperQNamePair = dao.getOrCreateQName(localNameUpperQName); + assertNotNull(localNameUpperQNamePair); assertEquals( "Didn't resolve case-insensitively on local-name", - localNameLowerQNameEntity.getId(), localNameUpperQNameEntity.getId()); + localNameLowerQNamePair.getFirst(), localNameUpperQNamePair.getFirst()); // Done return null; } @@ -242,9 +243,9 @@ public class QNameDAOTest extends TestCase 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() + final RetryingTransactionCallback> callback = new RetryingTransactionCallback>() { - public QNameEntity execute() throws Throwable + public Pair execute() throws Throwable { String threadName = Thread.currentThread().getName(); // We use a common namespace and assign one of a limited set of random numbers @@ -252,8 +253,8 @@ public class QNameDAOTest extends TestCase // 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) + Pair qnamePair = dao.getQName(qname); + if (qnamePair == null) { // Notify that we are ready logger.debug("Thread " + threadName + " is READY"); @@ -266,7 +267,7 @@ public class QNameDAOTest extends TestCase { // This could fail with concurrency, but that's what we're testing logger.debug("Thread " + threadName + " is CREATING " + qname); - qnameEntity = dao.newQNameEntity(qname); + qnamePair = dao.newQName(qname); } catch (Throwable e) { @@ -280,12 +281,12 @@ public class QNameDAOTest extends TestCase // it is quite possible that the entity was created as the ready latch // is released after five seconds } - assertNotNull("QName should now exist", qnameEntity); + assertNotNull("QName should now exist", qnamePair); // Notify the counter that this thread is done logger.debug("Thread " + threadName + " is DONE"); doneLatch.countDown(); // Done - return qnameEntity; + return qnamePair; } }; Runnable runnable = new Runnable() diff --git a/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java b/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java index 3baa3a240f..548b910b2c 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/AclDaoComponentImpl.java @@ -44,7 +44,6 @@ import org.alfresco.repo.domain.DbAuthority; import org.alfresco.repo.domain.DbPermission; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.QNameDAO; -import org.alfresco.repo.domain.QNameEntity; import org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl; import org.alfresco.repo.security.permissions.ACEType; import org.alfresco.repo.security.permissions.ACLCopyMode; @@ -63,6 +62,7 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; +import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.CacheMode; @@ -646,7 +646,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo for (Map result : results) { DbAccessControlListMember member = (DbAccessControlListMember) result.get("member"); - if ((exclude != null) && excluder.matches(result, depth)) + if ((exclude != null) && excluder.matches(qnameDAO, result, depth)) { getHibernateTemplate().delete(member); removed = true; @@ -1174,10 +1174,11 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo // context.setClassContext(entry.getContext().getClassContext()); // context.setKVPContext(entry.getContext().getKvpContext()); // context.setPropertyContext(entry.getContext().getPropertyContext()); - // sacEntry.setContext(context); - // } - DbPermission perm = (DbPermission) getSession().get(DbPermissionImpl.class, permissionId); - SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(perm.getTypeQName().getQName(), perm.getName()); + // sacEntry.setContext(context); + // } + DbPermission perm = (DbPermission)getSession().get(DbPermissionImpl.class, permissionId); + QName permTypeQName = qnameDAO.getQName(perm.getTypeQNameId()).getSecond(); // Has an ID so must exist + SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(permTypeQName, perm.getName()); sacEntry.setPermission(permissionRefernce); sacEntry.setPosition(position); @@ -1400,14 +1401,14 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo final QName permissionQName = ace.getPermission().getQName(); final String permissionName = ace.getPermission().getName(); - final QNameEntity permissionQNameEntity = qnameDAO.getOrCreateQNameEntity(permissionQName); + final Pair permissionQNamePair = qnameDAO.getOrCreateQName(permissionQName); callback = new HibernateCallback() { public Object doInHibernate(Session session) { Query query = session.getNamedQuery(QUERY_GET_PERMISSION); - query.setParameter("permissionTypeQName", permissionQNameEntity); + query.setParameter("permissionTypeQNameId", permissionQNamePair.getFirst()); query.setParameter("permissionName", permissionName); return query.uniqueResult(); } @@ -1416,7 +1417,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo if (permission == null) { DbPermissionImpl newPermission = new DbPermissionImpl(); - newPermission.setTypeQName(permissionQNameEntity); + newPermission.setTypeQNameId(permissionQNamePair.getFirst()); newPermission.setName(permissionName); permission = newPermission; getHibernateTemplate().save(newPermission); @@ -1750,7 +1751,8 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo entry.setContext(context); } DbPermission perm = member.getAccessControlEntry().getPermission(); - SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(perm.getTypeQName().getQName(), perm.getName()); + QName permTypeQName = qnameDAO.getQName(perm.getTypeQNameId()).getSecond(); // Has an ID so must exist + SimplePermissionReference permissionRefernce = SimplePermissionReference.getPermissionReference(permTypeQName, perm.getName()); entry.setPermission(permissionRefernce); entry.setPosition(Integer.valueOf(0)); @@ -1804,7 +1806,7 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo this.pattern = pattern; } - boolean matches(Map result, int position) + boolean matches(QNameDAO qnameDAO, Map result, int position) { if (pattern == null) { @@ -1848,7 +1850,8 @@ public class AclDaoComponentImpl extends HibernateDaoSupport implements AclDaoCo { DbPermission permission = (DbPermission) result.get("permission"); final QName patternQName = pattern.getPermission().getQName(); - if ((patternQName != null) && (!patternQName.equals(permission.getTypeQName().getQName()))) + final QName permTypeQName = qnameDAO.getQName(permission.getTypeQNameId()).getSecond(); // Has an ID so must exist + if ((patternQName != null) && (!patternQName.equals(permTypeQName))) { return false; } diff --git a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java index 098bfb0781..2cb47a848c 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/ChildAssocImpl.java @@ -30,9 +30,8 @@ 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.repo.domain.QNameDAO; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; @@ -48,8 +47,8 @@ public class ChildAssocImpl implements ChildAssoc, Serializable private Long version; private Node parent; private Node child; - private QNameEntity typeQName; - private NamespaceEntity qnameNamespace; + private Long typeQNameId; + private Long qnameNamespaceId; private String qnameLocalName; private String childNodeName; private long childNodeNameCrc; @@ -59,6 +58,7 @@ public class ChildAssocImpl implements ChildAssoc, Serializable private transient ReadLock refReadLock; private transient WriteLock refWriteLock; private transient ChildAssociationRef childAssocRef; + private transient QName typeQName; private transient QName qname; public ChildAssocImpl() @@ -86,7 +86,7 @@ public class ChildAssocImpl implements ChildAssoc, Serializable *

* This method is thread-safe and lazily creates the required references, if required. */ - public ChildAssociationRef getChildAssocRef() + public ChildAssociationRef getChildAssocRef(QNameDAO qnameDAO) { boolean trashReference = false; // first check if it is available @@ -119,10 +119,19 @@ public class ChildAssocImpl implements ChildAssoc, Serializable // double check if (childAssocRef == null || trashReference) { + if (typeQName == null) + { + typeQName = qnameDAO.getQName(this.typeQNameId).getSecond(); + } + if (qname == null ) + { + String qnameNamespace = qnameDAO.getNamespace(qnameNamespaceId).getSecond(); + qname = QName.createQName(qnameNamespace, qnameLocalName); + } childAssocRef = new ChildAssociationRef( - this.typeQName.getQName(), + typeQName, parent.getNodeRef(), - this.getQname(), + qname, child.getNodeRef(), this.isPrimary, index); @@ -135,12 +144,55 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } + /** + * {@inheritDoc} + */ + public QName getTypeQName(QNameDAO qnameDAO) + { + refReadLock.lock(); + try + { + if (typeQName != null) + { + return typeQName; + } + } + finally + { + refReadLock.unlock(); + } + refWriteLock.lock(); + try + { + typeQName = qnameDAO.getQName(typeQNameId).getSecond(); + return typeQName; + } + finally + { + refWriteLock.unlock(); + } + } + + public void setTypeQName(QNameDAO qnameDAO, QName typeQName) + { + Long typeQNameId = qnameDAO.getOrCreateQName(typeQName).getFirst(); + refWriteLock.lock(); + try + { + setTypeQNameId(typeQNameId); + } + finally + { + refWriteLock.unlock(); + } + } + /** * {@inheritDoc} *

* This method is thread-safe and lazily creates the required references, if required. */ - public QName getQname() + public QName getQName(QNameDAO qnameDAO) { // first check if it is available refReadLock.lock(); @@ -162,7 +214,8 @@ public class ChildAssocImpl implements ChildAssoc, Serializable // double check if (qname == null ) { - qname = QName.createQName(qnameNamespace.getUri(), qnameLocalName); + String qnameNamespace = qnameDAO.getNamespace(qnameNamespaceId).getSecond(); + qname = QName.createQName(qnameNamespace, qnameLocalName); } return qname; } @@ -172,6 +225,24 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } + public void setQName(QNameDAO qnameDAO, QName qname) + { + String assocQNameNamespace = qname.getNamespaceURI(); + String assocQNameLocalName = qname.getLocalName(); + Long assocQNameNamespaceId = qnameDAO.getOrCreateNamespace(assocQNameNamespace).getFirst(); + // get write lock + refWriteLock.lock(); + try + { + setQnameNamespaceId(assocQNameNamespaceId); + setQnameLocalName(assocQNameLocalName); + } + finally + { + refWriteLock.unlock(); + } + } + public boolean equals(Object obj) { if (obj == null) @@ -187,10 +258,19 @@ public class ChildAssocImpl implements ChildAssoc, Serializable return false; } ChildAssoc that = (ChildAssoc) obj; - return (EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName()) - && EqualsHelper.nullSafeEquals(this.getQname(), that.getQname()) - && EqualsHelper.nullSafeEquals(this.getChild(), that.getChild()) - && EqualsHelper.nullSafeEquals(this.getParent(), that.getParent())); + if (EqualsHelper.nullSafeEquals(id, that.getId())) + { + return true; + } + else + { + return (EqualsHelper.nullSafeEquals(this.getParent(), that.getParent()) + && EqualsHelper.nullSafeEquals(this.typeQNameId, that.getTypeQNameId()) + && EqualsHelper.nullSafeEquals(this.getChild(), that.getChild()) + && EqualsHelper.nullSafeEquals(this.qnameLocalName, that.getQnameLocalName()) + && EqualsHelper.nullSafeEquals(this.qnameNamespaceId, that.getQnameNamespaceId()) + ); + } } public int hashCode() @@ -207,8 +287,9 @@ 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().getQName()) - .append(", assoc name=").append(getQname()) + .append(", assoc type=").append(typeQNameId) + .append(", assoc qname ns=").append(qnameNamespaceId) + .append(", assoc qname localname=").append(qnameLocalName) .append(", isPrimary=").append(isPrimary) .append("]"); return sb.toString(); @@ -321,18 +402,19 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } - public QNameEntity getTypeQName() + public Long getTypeQNameId() { - return typeQName; + return typeQNameId; } - public void setTypeQName(QNameEntity typeQName) + public void setTypeQNameId(Long typeQNameId) { refWriteLock.lock(); try { - this.typeQName = typeQName; + this.typeQNameId = typeQNameId; this.childAssocRef = null; + this.typeQName = null; } finally { @@ -340,17 +422,17 @@ public class ChildAssocImpl implements ChildAssoc, Serializable } } - public NamespaceEntity getQnameNamespace() + public Long getQnameNamespaceId() { - return qnameNamespace; + return qnameNamespaceId; } - public void setQnameNamespace(NamespaceEntity qnameNamespace) + public void setQnameNamespaceId(Long qnameNamespaceId) { refWriteLock.lock(); try { - this.qnameNamespace = qnameNamespace; + this.qnameNamespaceId = qnameNamespaceId; this.childAssocRef = null; this.qname = null; } diff --git a/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java b/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java index d5bd81f381..05c804706f 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/DbPermissionImpl.java @@ -25,10 +25,13 @@ 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.DbPermission; import org.alfresco.repo.domain.DbPermissionKey; -import org.alfresco.repo.domain.QNameEntity; +import org.alfresco.repo.domain.QNameDAO; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; import org.hibernate.Session; @@ -44,12 +47,19 @@ public class DbPermissionImpl implements DbPermission, Serializable private Long id; private Long version; - private QNameEntity typeQName; + private Long typeQNameId; private String name; + private transient ReadLock refReadLock; + private transient WriteLock refWriteLock; + private transient QName typeQName; + public DbPermissionImpl() { super(); + ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); + refReadLock = lock.readLock(); + refWriteLock = lock.writeLock(); } @Override @@ -59,7 +69,7 @@ public class DbPermissionImpl implements DbPermission, Serializable sb.append("DbPermissionImpl") .append("[ id=").append(id) .append(", version=").append(version) - .append(", typeQName=").append(typeQName.getQName()) + .append(", typeQName=").append(typeQNameId) .append(", name=").append(getName()) .append("]"); return sb.toString(); @@ -77,13 +87,41 @@ public class DbPermissionImpl implements DbPermission, Serializable return false; } DbPermission other = (DbPermission) o; - return (EqualsHelper.nullSafeEquals(typeQName, other.getTypeQName())) && (EqualsHelper.nullSafeEquals(name, other.getName())); + return (EqualsHelper.nullSafeEquals(typeQNameId, other.getTypeQNameId())) + && (EqualsHelper.nullSafeEquals(name, other.getName()) + ); } @Override public int hashCode() { - return typeQName.hashCode() + (37 * name.hashCode()); + return typeQNameId.hashCode() + (37 * name.hashCode()); + } + + public QName getTypeQName(QNameDAO qnameDAO) + { + refReadLock.lock(); + try + { + if (typeQName != null) + { + return typeQName; + } + } + finally + { + refReadLock.unlock(); + } + refWriteLock.lock(); + try + { + typeQName = qnameDAO.getQName(typeQNameId).getSecond(); + return typeQName; + } + finally + { + refWriteLock.unlock(); + } } public Long getId() @@ -114,14 +152,23 @@ public class DbPermissionImpl implements DbPermission, Serializable this.version = version; } - public QNameEntity getTypeQName() + public Long getTypeQNameId() { - return typeQName; + return typeQNameId; } - public void setTypeQName(QNameEntity typeQName) + public void setTypeQNameId(Long typeQNameId) { - this.typeQName = typeQName; + refWriteLock.lock(); + try + { + this.typeQNameId = typeQNameId; + this.typeQName = null; + } + finally + { + refWriteLock.unlock(); + } } public String getName() @@ -136,7 +183,7 @@ public class DbPermissionImpl implements DbPermission, Serializable public DbPermissionKey getKey() { - return new DbPermissionKey(typeQName.getQName(), name); + return new DbPermissionKey(typeQNameId, name); } /** diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java index 617cd36b3f..026413c9fd 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateQNameDAOImpl.java @@ -24,6 +24,7 @@ */ package org.alfresco.repo.domain.hibernate; +import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -56,26 +57,68 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD 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) + private static final Long CACHE_NULL_LONG = Long.MIN_VALUE; + private SimpleCache namespaceEntityCache; + private SimpleCache qnameEntityCache; + + /** + * Set the cache that maintains the ID-Namespace mappings and vice-versa. + * + * @param namespaceEntityCache the cache + */ + public void setNamespaceEntityCache(SimpleCache namespaceEntityCache) + { + this.namespaceEntityCache = namespaceEntityCache; + } + + /** + * Set the cache that maintains the ID-QName mappings and vice-versa. + * + * @param qnameEntityCache the cache + */ + public void setQnameEntityCache(SimpleCache qnameEntityCache) { this.qnameEntityCache = qnameEntityCache; } - public NamespaceEntity getNamespaceEntity(Long id) + public Pair getNamespace(Long id) { + // Check the cache + String uri = (String) namespaceEntityCache.get(id); + if (uri != null) + { + return new Pair(id, uri); + } + // Get it from the DB NamespaceEntity namespaceEntity = (NamespaceEntity) getSession().get(NamespaceEntityImpl.class, id); if (namespaceEntity == null) { throw new AlfrescoRuntimeException("The NamespaceEntity ID " + id + " doesn't exist."); } - return namespaceEntity; + uri = namespaceEntity.getUri(); + // Cache it + namespaceEntityCache.put(uri, id); + namespaceEntityCache.put(id, uri); + // Done + return new Pair(id, uri); } - public NamespaceEntity getNamespaceEntity(final String namespaceUri) + public Pair getNamespace(final String namespaceUri) { - // TODO: Use a cache if external use becomes common + // Check the cache + Long id = (Long) namespaceEntityCache.get(namespaceUri); + if (id != null) + { + if (id.equals(CACHE_NULL_LONG)) + { + return null; + } + else + { + return new Pair(id, namespaceUri); + } + } + // Get it from the DB HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) @@ -89,21 +132,35 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD } }; NamespaceEntity result = (NamespaceEntity) getHibernateTemplate().execute(callback); - // Done - return result; - } - - public NamespaceEntity getOrCreateNamespaceEntity(String namespaceUri) - { - NamespaceEntity result = getNamespaceEntity(namespaceUri); if (result == null) { - result = newNamespaceEntity(namespaceUri); + // Cache it + namespaceEntityCache.put(namespaceUri, CACHE_NULL_LONG); + // Done + return null; + } + else + { + id = result.getId(); + // Cache it + namespaceEntityCache.put(id, namespaceUri); + namespaceEntityCache.put(namespaceUri, id); + // Done + return new Pair(id, namespaceUri); + } + } + + public Pair getOrCreateNamespace(String namespaceUri) + { + Pair result = getNamespace(namespaceUri); + if (result == null) + { + result = newNamespace(namespaceUri); } return result; } - public NamespaceEntity newNamespaceEntity(String namespaceUri) + public Pair newNamespace(String namespaceUri) { if (logger.isDebugEnabled()) { @@ -112,25 +169,29 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD NamespaceEntity namespace = new NamespaceEntityImpl(); namespace.setUri(namespaceUri); // Persist - getSession().save(namespace); + Long id = (Long) getSession().save(namespace); + // Cache it + namespaceEntityCache.put(id, namespaceUri); + namespaceEntityCache.put(namespaceUri, id); // Done - return namespace; + return new Pair(id, namespaceUri); } - public void updateNamespaceEntity(String oldNamespaceUri, String newNamespaceUri) + public void updateNamespace(String oldNamespaceUri, String newNamespaceUri) { // First check for clashes - if (getNamespaceEntity(newNamespaceUri) != null) + if (getNamespace(newNamespaceUri) != null) { throw new AlfrescoRuntimeException("Namespace URI '" + newNamespaceUri + "' already exists."); } // Get the old one - NamespaceEntity oldNamespaceEntity = getNamespaceEntity(oldNamespaceUri); - if (oldNamespaceEntity == null) + Pair oldNamespacePair = getNamespace(oldNamespaceUri); + if (oldNamespacePair == null) { // Nothing to rename return; } + NamespaceEntity oldNamespaceEntity = (NamespaceEntity) getSession().load(NamespaceEntityImpl.class, oldNamespacePair.getFirst()); oldNamespaceEntity.setUri(newNamespaceUri); // Flush to force early failure getSession().flush(); @@ -139,111 +200,88 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD // Done } - public QNameEntity getQNameEntity(Long id) + public Pair getQName(Long id) { + // Check the cache + QName qname = (QName) qnameEntityCache.get(id); + if (qname != null) + { + return new Pair(id, qname); + } QNameEntity qnameEntity = (QNameEntity) getSession().get(QNameEntityImpl.class, id); if (qnameEntity == null) { throw new AlfrescoRuntimeException("The QNameEntity ID " + id + " doesn't exist."); } - return qnameEntity; + qname = qnameEntity.getQName(); + // Cache it + qnameEntityCache.put(id, qname); + qnameEntityCache.put(qname, id); + // Done + return new Pair(id, qname); } - public QName getQName(Long id) + public Pair getQName(final QName qname) { - // TODO: Explore caching options here - QNameEntity qnameEntity = getQNameEntity(id); - if (qnameEntity == null) + // Check the cache + Long id = (Long) qnameEntityCache.get(qname); + if (id != 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() + if (id.equals(CACHE_NULL_LONG)) { - public Object doInHibernate(Session session) - { - String namespaceUri = qname.getNamespaceURI(); - String oracleSafeUri = (namespaceUri.length() == 0) ? NamespaceEntityImpl.EMPTY_URI_SUBSTITUTE : namespaceUri; - - Query query = session - .getNamedQuery(HibernateQNameDAOImpl.QUERY_GET_QNAME_BY_URI_AND_LOCALNAME) - .setString("namespaceUri", oracleSafeUri) - .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); + return null; } else { - qnameEntityCache.put(qname, -1L); + return new Pair(id, qname); } } - else if (id == -1L) + QNameEntity result; + // It's not in the cache, so query + HibernateCallback callback = new HibernateCallback() { + public Object doInHibernate(Session session) + { + String namespaceUri = qname.getNamespaceURI(); + String oracleSafeUri = (namespaceUri.length() == 0) ? NamespaceEntityImpl.EMPTY_URI_SUBSTITUTE : namespaceUri; + + Query query = session + .getNamedQuery(HibernateQNameDAOImpl.QUERY_GET_QNAME_BY_URI_AND_LOCALNAME) + .setString("namespaceUri", oracleSafeUri) + .setString("localName", qname.getLocalName()); + return query.uniqueResult(); + } + }; + result = (QNameEntity) getHibernateTemplate().execute(callback); + if (result == null) + { + // Cache it + qnameEntityCache.put(qname, CACHE_NULL_LONG); + // Done return null; } else { - // Found in the cache. Load using the ID. - // Don't use the method that throws an exception as the cache might be invalid. - result = (QNameEntity) getSession().get(QNameEntityImpl.class, 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); - } + id = result.getId(); + // Cache it + qnameEntityCache.put(id, qname); + qnameEntityCache.put(qname, id); + // Done + return new Pair(id, qname); } - // Done - return result; } - public QNameEntity getOrCreateQNameEntity(QName qname) + public Pair getOrCreateQName(QName qname) { - QNameEntity result = getQNameEntity(qname); + Pair result = getQName(qname); if (result == null) { - result = newQNameEntity(qname); + result = newQName(qname); } return result; } - public Pair getOrCreateQNamePair(QName qname) - { - Long id = qnameEntityCache.get(qname); - if (id == null) - { - // It is not cached - QNameEntity qnameEntity = getOrCreateQNameEntity(qname); - id = qnameEntity.getId(); - } - Pair qnamePair = new Pair(id, qname); - // Done - return qnamePair; - } - - public QNameEntity newQNameEntity(QName qname) + public Pair newQName(QName qname) { if (logger.isDebugEnabled()) { @@ -251,11 +289,10 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD } final String namespaceUri = qname.getNamespaceURI(); final String localName = qname.getLocalName(); - NamespaceEntity namespace = getNamespaceEntity(namespaceUri); - if (namespace == null) - { - namespace = newNamespaceEntity(namespaceUri); - } + // Get the namespace + Pair namespacePair = getOrCreateNamespace(namespaceUri); + NamespaceEntity namespace = (NamespaceEntity) getSession().load(NamespaceEntityImpl.class, namespacePair.getFirst()); + // Create the QNameEntity QNameEntity qnameEntity = new QNameEntityImpl(); qnameEntity.setNamespace(namespace); qnameEntity.setLocalName(localName); @@ -263,8 +300,9 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD Long id = (Long) getSession().save(qnameEntity); // Update the cache qnameEntityCache.put(qname, id); + qnameEntityCache.put(id, qname); // Done - return qnameEntity; + return new Pair(id, qname); } public Set convertIdsToQNames(Set ids) @@ -272,7 +310,7 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD Set qnames = new HashSet(ids.size() * 2 + 1); for (Long id : ids) { - QName qname = getQName(id); // Never null + QName qname = getQName(id).getSecond(); // getQName(id) is never null qnames.add(qname); } return qnames; @@ -283,7 +321,7 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD Map qnameMap = new HashMap(idMap.size() + 3); for (Map.Entry entry : idMap.entrySet()) { - QName qname = getQName(entry.getKey()); + QName qname = getQName(entry.getKey()).getSecond(); // getQName(id) is never null qnameMap.put(qname, entry.getValue()); } return qnameMap; @@ -303,19 +341,19 @@ public class HibernateQNameDAOImpl extends HibernateDaoSupport implements QNameD Long qnameEntityId = null; if (create) { - qnameEntityId = getOrCreateQNameEntity(qname).getId(); + qnameEntityId = getOrCreateQName(qname).getFirst(); // getOrCreateQName(qname) is never null } else { - QNameEntity qnameEntity = getQNameEntity(qname); - if (qnameEntity == null) + Pair qnamePair = getQName(qname); + if (qnamePair == null) { // No such qname and we are not creating one continue; } else { - qnameEntityId = qnameEntity.getId(); + qnameEntityId = qnamePair.getFirst(); } } if (qnameEntityId != null) diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateSessionHelperTest.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateSessionHelperTest.java index 8979ed51b4..99cd47df51 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateSessionHelperTest.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateSessionHelperTest.java @@ -7,7 +7,6 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.Node; 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.transaction.AlfrescoTransactionSupport; @@ -93,7 +92,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertFalse(SessionSizeResourceManager.isDisableInTransaction()); QNameDAO qnameDAO = (QNameDAO) getApplicationContext().getBean("qnameDAO"); - QNameEntity baseQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.TYPE_BASE); + Long baseQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_BASE).getFirst(); StoreImpl store = new StoreImpl(); store.setProtocol(StoreRef.PROTOCOL_WORKSPACE); @@ -124,35 +123,35 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(1, helper.getMarks().size()); - Node n1 = createNode(transaction, store, "1", baseQNameEntity); + Node n1 = createNode(transaction, store, "1", baseQNameId); assertEquals(5, getSession().getStatistics().getEntityCount()); helper.mark(); assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(2, helper.getMarks().size()); - Node n2 = createNode(transaction, store, "2", baseQNameEntity); + Node n2 = createNode(transaction, store, "2", baseQNameId); assertEquals(6, getSession().getStatistics().getEntityCount()); helper.mark(); assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(3, helper.getMarks().size()); - Node n3 = createNode(transaction, store, "3", baseQNameEntity); + Node n3 = createNode(transaction, store, "3", baseQNameId); assertEquals(7, getSession().getStatistics().getEntityCount()); helper.mark(); assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(4, helper.getMarks().size()); - Node n4 = createNode(transaction, store, "4", baseQNameEntity); + Node n4 = createNode(transaction, store, "4", baseQNameId); assertEquals(8, getSession().getStatistics().getEntityCount()); helper.mark(); assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(5, helper.getMarks().size()); - Node n5 = createNode(transaction, store, "5", baseQNameEntity); + Node n5 = createNode(transaction, store, "5", baseQNameId); assertEquals(9, getSession().getStatistics().getEntityCount()); @@ -321,7 +320,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertFalse(SessionSizeResourceManager.isDisableInTransaction()); QNameDAO qnameDAO = (QNameDAO) getApplicationContext().getBean("qnameDAO"); - QNameEntity baseQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.TYPE_BASE); + Long baseQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_BASE).getFirst(); StoreImpl store = new StoreImpl(); store.setProtocol(StoreRef.PROTOCOL_WORKSPACE); @@ -354,7 +353,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(1, helper.getMarks().size()); - Node n1 = createNode(transaction, store, "1", baseQNameEntity); + Node n1 = createNode(transaction, store, "1", baseQNameId); assertEquals(5, getSession().getStatistics().getEntityCount()); helper.mark("Two"); @@ -362,7 +361,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(2, helper.getMarks().size()); - Node n2 = createNode(transaction, store, "2", baseQNameEntity); + Node n2 = createNode(transaction, store, "2", baseQNameId); assertEquals(6, getSession().getStatistics().getEntityCount()); helper.mark("Three"); @@ -370,7 +369,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(3, helper.getMarks().size()); - Node n3 = createNode(transaction, store, "3", baseQNameEntity); + Node n3 = createNode(transaction, store, "3", baseQNameId); assertEquals(7, getSession().getStatistics().getEntityCount()); helper.mark("Four"); @@ -378,7 +377,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(4, helper.getMarks().size()); - Node n4 = createNode(transaction, store, "4", baseQNameEntity); + Node n4 = createNode(transaction, store, "4", baseQNameId); assertEquals(8, getSession().getStatistics().getEntityCount()); helper.mark("Five"); @@ -386,7 +385,7 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertTrue(SessionSizeResourceManager.isDisableInTransaction()); assertEquals(5, helper.getMarks().size()); - Node n5 = createNode(transaction, store, "5", baseQNameEntity); + Node n5 = createNode(transaction, store, "5", baseQNameId); assertEquals(9, getSession().getStatistics().getEntityCount()); @@ -541,13 +540,13 @@ public class HibernateSessionHelperTest extends BaseSpringTest assertNull(helper.getCurrentMark()); } - private Node createNode(TransactionImpl transaction, Store store, String uuid, QNameEntity typeQNameEntity) + private Node createNode(TransactionImpl transaction, Store store, String uuid, Long typeQNameId) { // Create the Node Node node = new NodeImpl(); node.setStore(store); node.setUuid(uuid); - node.setTypeQName(typeQNameEntity); + node.setTypeQNameId(typeQNameId); node.setTransaction(transaction); node.setDeleted(false); node.getAuditableProperties().setAuditValues("system", new Date(), false); 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 363649c94e..f440230d19 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -80,17 +80,8 @@ cascade="none" /> - - + + - + @@ -178,17 +169,7 @@ not-null="true" > - - + @@ -205,17 +186,7 @@ not-null="true" > - - + @@ -252,17 +223,7 @@ not-null="true" > - - + @@ -308,11 +269,15 @@ select - assoc + assoc, + parent, + child from org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc + join assoc.parent as parent + join assoc.child as child where - assoc.child.id = :childId + child.id = :childId order by assoc.index, assoc.id @@ -363,8 +328,8 @@ where assoc.parent.id = :parentId and assoc.child.id = :childId and - assoc.typeQName = :typeQName and - assoc.qnameNamespace = :qnameNamespace and + assoc.typeQNameId = :typeQNameId and + assoc.qnameNamespaceId = :qnameNamespaceId and assoc.qnameLocalName = :qnameLocalName order by assoc.index, @@ -390,7 +355,7 @@ org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc where assoc.parent.id = :parentId and - assoc.typeQName = :typeQName and + assoc.typeQNameId = :typeQNameId and assoc.childNodeName = :childNodeName and assoc.childNodeNameCrc = :childNodeNameCrc order by @@ -401,8 +366,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -425,8 +390,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -441,7 +406,7 @@ join child.store as store where parent.id = :parentId and - assoc.qnameNamespace = :qnameNamespace and + assoc.qnameNamespaceId = :qnameNamespaceId and assoc.qnameLocalName = :qnameLocalName order by assoc.index, @@ -451,8 +416,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -467,7 +432,7 @@ join child.store as store where parent.id = :parentId and - assoc.typeQName.id in (:childAssocTypeQNameIds) + assoc.typeQNameId in (:childAssocTypeQNameIds) order by assoc.index, assoc.id @@ -476,8 +441,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -492,8 +457,8 @@ join child.store as store where parent.id = :parentId and - assoc.typeQName = :typeQName and - assoc.qnameNamespace = :qnameNamespace and + assoc.typeQNameId = :typeQNameId and + assoc.qnameNamespaceId = :qnameNamespaceId and assoc.qnameLocalName = :qnameLocalName order by assoc.index, @@ -503,8 +468,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -519,7 +484,7 @@ join child.store as store where parent.id = :parentId and - child.typeQName in (:childTypeQNameEntities) + child.typeQNameId in (:childTypeQNameIds) order by assoc.index, assoc.id @@ -528,8 +493,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -553,8 +518,8 @@ select assoc.id, - assoc.typeQName, - assoc.qnameNamespace, + assoc.typeQNameId, + assoc.qnameNamespaceId, assoc.qnameLocalName, assoc.isPrimary, assoc.index, @@ -606,7 +571,7 @@ join node.aspects as aspects where node.id > :minNodeId and - aspects.id = :aspectQName + aspects.id = :aspectQNameId order by node.id @@ -619,7 +584,7 @@ where assoc.source.id = :sourceId and assoc.target.id = :targetId and - assoc.typeQName = :assocTypeQName + assoc.typeQNameId = :assocTypeQNameId @@ -669,19 +634,60 @@ p.string_value = :propStringValue - - select - node.id, - node.store.protocol, - node.store.identifier, - node.uuid - from - org.alfresco.repo.domain.hibernate.NodeImpl as node - where - node.store.protocol = :storeProtocol and - node.store.identifier = :storeIdentifier and - node.auditableProperties.auditCreator = :userName - + + + + + SELECT + p1.string_value AS owner, + n.audit_creator AS creator, + p2.string_value AS contenturl + FROM + alf_node n + JOIN alf_store s ON (s.id = n.store_id AND n.type_qname_id = :contentTypeQNameID) + LEFT JOIN alf_node_properties p1 ON (p1.node_id = n.id AND p1.qname_id = :ownerPropQNameID) + JOIN alf_node_properties p2 ON (p2.node_id = n.id AND p2.qname_id = :contentPropQNameID) + WHERE + s.protocol = :storeProtocol AND + s.identifier = :storeIdentifier AND + (p1.string_value != 'System' OR (p1.string_value IS NULL AND n.audit_creator != 'System')) + + + + + + SELECT + p1.string_value AS username, + n.uuid AS uuid + FROM + alf_node n + JOIN alf_store s ON (s.id = n.store_id AND n.type_qname_id = :personTypeQNameID) + JOIN alf_node_properties p1 ON (p1.node_id = n.id AND p1.qname_id = :usernamePropQNameID) + LEFT JOIN alf_node_properties p2 ON (p2.node_id = n.id AND p2.qname_id = :sizeCurrentPropQNameID) + WHERE + s.protocol = :storeProtocol AND + s.identifier = :storeIdentifier AND + (p2.persisted_type_n IS NULL OR p2.persisted_type_n = 0) AND + p1.string_value != 'System' + + + + + + SELECT + p1.string_value AS username, + n.uuid AS uuid + FROM + alf_node n + JOIN alf_store s ON (s.id = n.store_id AND n.type_qname_id = :personTypeQNameID) + JOIN alf_node_properties p1 ON (p1.node_id = n.id AND p1.qname_id = :usernamePropQNameID) + LEFT JOIN alf_node_properties p2 ON (p2.node_id = n.id AND p2.qname_id = :sizeCurrentPropQNameID) + WHERE + s.protocol = :storeProtocol AND + s.identifier = :storeIdentifier AND + (p2.persisted_type_n != 0 AND p2.persisted_type_n IS NOT NULL) AND + p1.string_value != 'System' + select @@ -708,20 +714,4 @@ props.serializableValue is not null - - :lastAssocId and - assoc.typeQName = :assocTypeQName - order by - assoc.id - ]]> - - diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java index c21a7ecad2..e08835ee26 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeAssocImpl.java @@ -31,8 +31,9 @@ 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.repo.domain.QNameDAO; import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** @@ -48,11 +49,12 @@ public class NodeAssocImpl implements NodeAssoc, Serializable private Long version; private Node source; private Node target; - private QNameEntity typeQName; + private Long typeQNameId; private transient ReadLock refReadLock; private transient WriteLock refWriteLock; private transient AssociationRef nodeAssocRef; + private transient QName typeQName; public NodeAssocImpl() { @@ -68,7 +70,7 @@ public class NodeAssocImpl implements NodeAssoc, Serializable this.setSource(sourceNode); } - public AssociationRef getNodeAssocRef() + public AssociationRef getNodeAssocRef(QNameDAO qnameDAO) { boolean trashReference = false; // first check if it is available @@ -98,12 +100,16 @@ public class NodeAssocImpl implements NodeAssoc, Serializable refWriteLock.lock(); try { + if (typeQName == null) + { + typeQName = qnameDAO.getQName(typeQNameId).getSecond(); + } // double check if (nodeAssocRef == null || trashReference) { nodeAssocRef = new AssociationRef( getSource().getNodeRef(), - this.typeQName.getQName(), + this.typeQName, getTarget().getNodeRef()); } return nodeAssocRef; @@ -114,13 +120,54 @@ public class NodeAssocImpl implements NodeAssoc, Serializable } } + public QName getTypeQName(QNameDAO qnameDAO) + { + refReadLock.lock(); + try + { + if (typeQName != null) + { + return typeQName; + } + } + finally + { + refReadLock.unlock(); + } + // get write lock + refWriteLock.lock(); + try + { + typeQName = qnameDAO.getQName(typeQNameId).getSecond(); + return typeQName; + } + finally + { + refWriteLock.unlock(); + } + } + + public void setTypeQName(QNameDAO qnameDAO, QName typeQName) + { + Long typeQNameId = qnameDAO.getOrCreateQName(typeQName).getFirst(); + refWriteLock.lock(); + try + { + setTypeQNameId(typeQNameId); + } + finally + { + refWriteLock.unlock(); + } + } + public String toString() { StringBuffer sb = new StringBuffer(32); sb.append("NodeAssoc") .append("[ source=").append(source) .append(", target=").append(target) - .append(", name=").append(getTypeQName()) + .append(", type=").append(typeQNameId) .append("]"); return sb.toString(); } @@ -140,14 +187,22 @@ public class NodeAssocImpl implements NodeAssoc, Serializable return false; } NodeAssoc that = (NodeAssoc) obj; - return (EqualsHelper.nullSafeEquals(this.getTypeQName(), that.getTypeQName()) - && EqualsHelper.nullSafeEquals(this.getTarget(), that.getTarget()) - && EqualsHelper.nullSafeEquals(this.getSource(), that.getSource())); + if (EqualsHelper.nullSafeEquals(this.typeQNameId, that.getId())) + { + return true; + } + else + { + return (EqualsHelper.nullSafeEquals(this.typeQNameId, that.getTypeQNameId()) + && EqualsHelper.nullSafeEquals(this.getTarget(), that.getTarget()) + && EqualsHelper.nullSafeEquals(this.getSource(), that.getSource()) + ); + } } public int hashCode() { - return (typeQName == null ? 0 : typeQName.hashCode()); + return (typeQNameId == null ? 0 : typeQNameId.hashCode()); } public Long getId() @@ -222,18 +277,19 @@ public class NodeAssocImpl implements NodeAssoc, Serializable } } - public QNameEntity getTypeQName() + public Long getTypeQNameId() { - return typeQName; + return typeQNameId; } - public void setTypeQName(QNameEntity typeQName) + public void setTypeQNameId(Long typeQNameId) { refWriteLock.lock(); try { - this.typeQName = typeQName; + this.typeQNameId = typeQNameId; this.nodeAssocRef = null; + this.typeQName = null; } finally { diff --git a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java index 8ee5e30a71..2ec723b4fd 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java +++ b/source/java/org/alfresco/repo/domain/hibernate/NodeImpl.java @@ -38,10 +38,11 @@ import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodePropertyValue; import org.alfresco.repo.domain.PropertyMapKey; -import org.alfresco.repo.domain.QNameEntity; +import org.alfresco.repo.domain.QNameDAO; import org.alfresco.repo.domain.Store; import org.alfresco.repo.domain.Transaction; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** @@ -60,7 +61,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable private Long version; private Store store; private String uuid; - private QNameEntity typeQName; + private Long typeQNameId; private Transaction transaction; private boolean deleted; private DbAccessControlList accessControlList; @@ -71,6 +72,7 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable private transient ReadLock refReadLock; private transient WriteLock refWriteLock; private transient NodeRef nodeRef; + private transient QName typeQName; public NodeImpl() { @@ -118,6 +120,46 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable } } + public QName getTypeQName(QNameDAO qnameDAO) + { + refReadLock.lock(); + try + { + if (typeQName != null) + { + return typeQName; + } + } + finally + { + refReadLock.unlock(); + } + refWriteLock.lock(); + try + { + typeQName = qnameDAO.getQName(typeQNameId).getSecond(); + return typeQName; + } + finally + { + refWriteLock.unlock(); + } + } + + public void setTypeQName(QNameDAO qnameDAO, QName qname) + { + refWriteLock.lock(); + try + { + Long typeQNameId = qnameDAO.getOrCreateQName(qname).getFirst(); + setTypeQNameId(typeQNameId); + } + finally + { + refWriteLock.unlock(); + } + } + /** * @see #getNodeRef() */ @@ -249,14 +291,23 @@ public class NodeImpl extends LifecycleAdapter implements Node, Serializable this.deleted = deleted; } - public QNameEntity getTypeQName() + public Long getTypeQNameId() { - return typeQName; + return typeQNameId; } - public void setTypeQName(QNameEntity typeQName) + public void setTypeQNameId(Long typeQNameId) { - this.typeQName = typeQName; + refWriteLock.lock(); + try + { + this.typeQNameId = typeQNameId; + this.typeQName = null; + } + finally + { + refWriteLock.unlock(); + } } public DbAccessControlList getAccessControlList() 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 aa362af817..916c504cbe 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Permission.hbm.xml @@ -173,17 +173,8 @@ - - + + @@ -275,7 +266,7 @@ from org.alfresco.repo.domain.hibernate.DbPermissionImpl as permission where - permission.typeQName = :permissionTypeQName and + permission.typeQNameId = :permissionTypeQNameId and permission.name = :permissionName diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 8d0482915a..785ccde60b 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -105,7 +105,7 @@ public class FileFolderServiceImpl implements FileFolderService private MimetypeService mimetypeService; // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID) - private List systemPaths; + private List systemPaths; /** * Default constructor diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index 36e53e97e4..dbda9fba3c 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -36,6 +36,9 @@ import junit.framework.TestCase; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.M2Type; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.service.ServiceRegistry; @@ -83,6 +86,7 @@ public class FileFolderServiceImplTest extends TestCase private TransactionService transactionService; private NodeService nodeService; private FileFolderService fileFolderService; + private DictionaryDAO dictionaryDAO; private UserTransaction txn; private NodeRef rootNodeRef; private NodeRef workingRootNodeRef; @@ -94,6 +98,7 @@ public class FileFolderServiceImplTest extends TestCase transactionService = serviceRegistry.getTransactionService(); nodeService = serviceRegistry.getNodeService(); fileFolderService = serviceRegistry.getFileFolderService(); + dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO"); AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); // start the transaction @@ -408,6 +413,32 @@ public class FileFolderServiceImplTest extends TestCase { rollbackTxn.rollback(); } + // Create a cm:folder derived type + try + { + rollbackTxn = transactionService.getNonPropagatingUserTransaction(); + rollbackTxn.begin(); + // Create a new model + String testNs = "http://www.alfresco.org/model/test111/1.0"; + M2Model testModel = M2Model.createModel("t111:filefolderserviceimpltest"); + testModel.createNamespace(testNs, "t111"); + testModel.createImport(NamespaceService.DICTIONARY_MODEL_1_0_URI, NamespaceService.DICTIONARY_MODEL_PREFIX); + testModel.createImport(NamespaceService.SYSTEM_MODEL_1_0_URI, NamespaceService.SYSTEM_MODEL_PREFIX); + testModel.createImport(NamespaceService.CONTENT_MODEL_1_0_URI, NamespaceService.CONTENT_MODEL_PREFIX); + + M2Type testType = testModel.createType("t111:subfolder"); + testType.setParentName("cm:" + ContentModel.TYPE_FOLDER.getLocalName()); + dictionaryDAO.putModel(testModel); + fileFolderService.create(parentFolderRef, "Legal subtype of folder", QName.createQName(testNs, "subfolder")); + } + catch (Throwable e) + { + throw new Exception("Legal subtype of cm:folder not allowed.", e); + } + finally + { + rollbackTxn.rollback(); + } // create a file FileInfo fileInfo = fileFolderService.create(parentFolderRef, "newFile", ContentModel.TYPE_CONTENT); diff --git a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java index eb631a8992..b4188cc8a5 100644 --- a/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/AbstractNodeServiceImpl.java @@ -556,25 +556,17 @@ public abstract class AbstractNodeServiceImpl implements NodeService } /** - * Generates a GUID for the node using either the creation properties or just by - * generating a value randomly. + * Fetches any pre-defined node uuid from the properties, but does not generate a new uuid. * * @param preCreationProperties the properties that will be applied to the node - * @return Returns the ID to create the node with + * @return Returns the ID to create the node with, or null if a standard GUID should be used */ protected String generateGuid(Map preCreationProperties) { String uuid = (String) preCreationProperties.get(ContentModel.PROP_NODE_UUID); - if (uuid == null) + if (uuid != null && uuid.length() > 50) { - uuid = GUID.generate(); - } - else - { - if (uuid.length() > 50) - { - throw new IllegalArgumentException("Explicit UUID may not be greater than 50 characters: " + uuid); - } + throw new IllegalArgumentException("Explicit UUID may not be greater than 50 characters: " + uuid); } // done return uuid; diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index 534a26f62c..7fe5b41339 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -87,8 +87,11 @@ public interface NodeDaoService public NodeRef.Status getNodeRefStatus(NodeRef nodeRef); /** + * Create a new node. Note that allowing the uuid to be assigned by passing in a null + * is more efficient. + * * @param storeRef the store to which the node must belong - * @param uuid the node store-unique identifier + * @param uuid the node store-unique identifier, or null to assign a GUID * @param nodeTypeQName the type of the node * @return Returns a new node Id of the given type and attached to the store * @throws InvalidTypeException if the node type is invalid or if the node type @@ -394,19 +397,49 @@ public interface NodeDaoService NodePropertyHandler handler); /** - * Iterate over all nodes that have a given creator + * Interface used to iterate over object array results + */ + public interface ObjectArrayQueryCallback + { + boolean handle(Object[] array); + } + + /** + * Iterate over all content nodes to get owner/creator and content url (in order to extract content size) * * @param storeRef the store to search in - * @param userName the user to match * @param handler the callback to use while iterating over the URLs - * @return Returns the values for the given owner + * @return Returns the values for the given owner, creator and content url */ @DirtySessionAnnotation(markDirty=true) - public void getNodesWithCreatorAndStore( + public void getContentUrlsForStore( StoreRef storeRef, - String userName, - NodeRefQueryCallback resultsCallback); - + ObjectArrayQueryCallback resultsCallback); + + /** + * Iterate over all person nodes to get users without a calculated usage + * + * @param storeRef the store to search in + * @param handler the callback to use while iterating over the people + * @return Returns the values for username and person node uuid (excluding System) + */ + @DirtySessionAnnotation(markDirty=true) + public void getUsersWithoutUsage( + StoreRef storeRef, + ObjectArrayQueryCallback resultsCallback); + + /** + * Iterate over all person nodes to get users with a calculated usage + * + * @param storeRef the store to search in + * @param handler the callback to use while iterating over the people + * @return Returns the values for the username and person node uuid (excluding System) + */ + @DirtySessionAnnotation(markDirty=true) + public void getUsersWithUsage( + StoreRef storeRef, + ObjectArrayQueryCallback resultsCallback); + /** * Iterate over all property values for the given type definition. This will also dig out values that * were persisted as type d:any. 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 93dbccc5b0..162feb9e96 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -50,14 +50,12 @@ import org.alfresco.repo.domain.AuditableProperties; import org.alfresco.repo.domain.ChildAssoc; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.LocaleDAO; -import org.alfresco.repo.domain.NamespaceEntity; import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodeAssoc; import org.alfresco.repo.domain.NodePropertyValue; import org.alfresco.repo.domain.PropertyMapKey; 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.Transaction; @@ -72,12 +70,12 @@ import org.alfresco.repo.domain.hibernate.ServerImpl; import org.alfresco.repo.domain.hibernate.StoreImpl; import org.alfresco.repo.domain.hibernate.TransactionImpl; import org.alfresco.repo.node.db.NodeDaoService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.ACLType; import org.alfresco.repo.security.permissions.AccessControlListProperties; import org.alfresco.repo.security.permissions.SimpleAccessControlListProperties; import org.alfresco.repo.security.permissions.impl.AclChange; import org.alfresco.repo.security.permissions.impl.AclDaoComponent; -import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionAwareSingleton; import org.alfresco.repo.transaction.TransactionalDao; @@ -111,7 +109,6 @@ import org.hibernate.Query; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; -import org.hibernate.criterion.Criterion; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.springframework.dao.DataIntegrityViolationException; @@ -146,7 +143,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private static final String QUERY_GET_TARGET_ASSOCS = "node.GetTargetAssocs"; private static final String QUERY_GET_SOURCE_ASSOCS = "node.GetSourceAssocs"; private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_STRING_AND_STORE = "node.GetNodesWithPropertyValuesByStringAndStore"; - private static final String QUERY_GET_NODES_WITH_CREATOR_AND_STORE = "node.GetNodesWithCreatorAndStore"; + private static final String QUERY_GET_CONTENT_URLS_FOR_STORE = "node.GetContentUrlsForStore"; + private static final String QUERY_GET_USERS_WITHOUT_USAGE = "node.GetUsersWithoutUsage"; + private static final String QUERY_GET_USERS_WITH_USAGE = "node.GetUsersWithUsage"; private static final String QUERY_GET_NODES_WITH_PROPERTY_VALUES_BY_ACTUAL_TYPE = "node.GetNodesWithPropertyValuesByActualType"; private static final String QUERY_GET_SERVER_BY_IPADDRESS = "server.getServerByIpAddress"; @@ -629,12 +628,12 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements // create and assign a root node Node rootNode = newNode( store, - GUID.generate(), + null, ContentModel.TYPE_STOREROOT); store.setRootNode(rootNode); // Add the root aspect - QNameEntity rootAspectQNameEntity = qnameDAO.getOrCreateQNameEntity(ContentModel.ASPECT_ROOT); - rootNode.getAspects().add(rootAspectQNameEntity.getId()); + Pair rootAspectQNamePair = qnameDAO.getOrCreateQName(ContentModel.ASPECT_ROOT); + rootNode.getAspects().add(rootAspectQNamePair.getFirst()); // Assign permissions to the root node SimpleAccessControlListProperties properties = DMPermissionsDaoComponentImpl.getDefaultProperties(); @@ -801,21 +800,26 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private Node newNode(Store store, String uuid, QName nodeTypeQName) throws InvalidTypeException { - // Get the qname for the node type - QNameEntity nodeTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(nodeTypeQName); - - // Get any existing Node. A node with this UUID may have existed before, but must be marked - // deleted; otherwise it will be considered live and valid - Node node = getNodeOrNull(store, uuid); - // If there is already a node attached, then there is a clash + Node node = null; + if (uuid != null) + { + // Get any existing Node. A node with this UUID may have existed before, but must be marked + // deleted; otherwise it will be considered live and valid + node = getNodeOrNull(store, uuid); + } + else + { + uuid = GUID.generate(); + } if (node != null) { if (!node.getDeleted()) { + // If there is already an undeleted node, then there is a clash throw new InvalidNodeRefException("Live Node exists: " + node.getNodeRef(), node.getNodeRef()); } // Set clean values - node.setTypeQName(nodeTypeQNameEntity); + node.setTypeQName(qnameDAO, nodeTypeQName); node.setDeleted(false); node.setAccessControlList(null); // Record node change @@ -827,7 +831,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements node = new NodeImpl(); node.setStore(store); node.setUuid(uuid); - node.setTypeQName(nodeTypeQNameEntity); + node.setTypeQName(qnameDAO, nodeTypeQName); node.setDeleted(false); node.setAccessControlList(null); // Record node change @@ -905,8 +909,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public QName getNodeType(Long nodeId) { Node node = getNodeNotNull(nodeId); - QNameEntity nodeTypeQNameEntity = node.getTypeQName(); - return nodeTypeQNameEntity.getQName(); + QName nodeTypeQName = node.getTypeQName(qnameDAO); + return nodeTypeQName; } public void setNodeStatus(Long nodeId) @@ -998,7 +1002,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Node oldNodeDummy = new NodeImpl(); oldNodeDummy.setStore(storeBefore); oldNodeDummy.setUuid(uuidBefore); - oldNodeDummy.setTypeQName(node.getTypeQName()); + oldNodeDummy.setTypeQNameId(node.getTypeQNameId()); recordNodeDelete(oldNodeDummy); // Persist getHibernateTemplate().save(oldNodeDummy); @@ -1009,12 +1013,16 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements storeAndNodeIdCache.put(nodeRefBefore, oldNodeDummy.getId()); } - if (nodeTypeQName != null && !nodeTypeQName.equals(node.getTypeQName().getQName())) + // Only update the node type if it is changing + if (nodeTypeQName != null) { - QNameEntity nodeTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(nodeTypeQName); - node.setTypeQName(nodeTypeQNameEntity); - // We will need to record the change - recordNodeUpdate(node); + Long nodeTypeQNameId = qnameDAO.getOrCreateQName(nodeTypeQName).getFirst(); + if (!nodeTypeQNameId.equals(node.getTypeQNameId())) + { + node.setTypeQNameId(nodeTypeQNameId); + // We will need to record the change + recordNodeUpdate(node); + } } } @@ -1037,8 +1045,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } } - QNameEntity propertyQNameEntity = qnameDAO.getQNameEntity(propertyQName); - if (propertyQNameEntity == null) + Pair propertyQNamePair = qnameDAO.getQName(propertyQName); + if (propertyQNamePair == null) { return null; } @@ -1084,7 +1092,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } PropertyDefinition propertyDef = dictionaryService.getProperty(qname); - Long qnameId = qnameDAO.getOrCreateQNameEntity(qname).getId(); + Long qnameId = qnameDAO.getOrCreateQName(qname).getFirst(); Map persistableProperties = new HashMap(3); @@ -1241,12 +1249,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Set nodeAspects = node.getAspects(); // Convert - Set nodeAspectQNames = new HashSet(nodeAspects.size(), 1.0F); - for (Long qnameEntityId : nodeAspects) - { - QName nodeAspectQName = qnameDAO.getQName(qnameEntityId); - nodeAspectQNames.add(nodeAspectQName); - } + Set nodeAspectQNames = qnameDAO.convertIdsToQNames(nodeAspects); // Add sys:referenceable nodeAspectQNames.add(ContentModel.ASPECT_REFERENCEABLE); @@ -1262,14 +1265,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements aspectQNames = new HashSet(aspectQNames); // Remove sys:referenceable aspectQNames.remove(ContentModel.ASPECT_REFERENCEABLE); + + // Convert + Set aspectQNameIds = qnameDAO.convertQNamesToIds(aspectQNames, true); + // Add them Set nodeAspects = node.getAspects(); - - for (QName aspectQName : aspectQNames) - { - QNameEntity aspectQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectQName); - nodeAspects.add(aspectQNameEntity.getId()); - } + nodeAspects.addAll(aspectQNameIds); // Record change ID recordNodeUpdate(node); @@ -1285,13 +1287,12 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements // Handle cm:auditable aspectQNames.remove(ContentModel.ASPECT_AUDITABLE); + // Convert + Set aspectQNameIds = qnameDAO.convertQNamesToIds(aspectQNames, false); + + // Remove them Set nodeAspects = node.getAspects(); - - for (QName aspectQName : aspectQNames) - { - QNameEntity aspectQNameEntity = qnameDAO.getOrCreateQNameEntity(aspectQName); - nodeAspects.remove(aspectQNameEntity.getId()); - } + nodeAspects.removeAll(aspectQNameIds); // Record change ID recordNodeUpdate(node); @@ -1310,14 +1311,14 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements private boolean hasNodeAspect(Node node, QName aspectQName) { - QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQName); - if (aspectQNameEntity == null) + Pair aspectQNamePair = qnameDAO.getQName(aspectQName); + if (aspectQNamePair == null) { return false; } Set nodeAspects = node.getAspects(); - return nodeAspects.contains(aspectQNameEntity.getId()); + return nodeAspects.contains(aspectQNamePair.getFirst()); } /** @@ -1533,20 +1534,15 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Node parentNode = (Node) getSession().get(NodeImpl.class, parentNodeId); Node childNode = (Node) getSession().get(NodeImpl.class, childNodeId); - QNameEntity assocTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(assocTypeQName); - String assocQNameNamespace = assocQName.getNamespaceURI(); - String assocQNameLocalName = assocQName.getLocalName(); - NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getOrCreateNamespaceEntity(assocQNameNamespace); // assign a random name to the node String name = GUID.generate(); ChildAssoc assoc = new ChildAssocImpl(); - assoc.setTypeQName(assocTypeQNameEntity); + assoc.setTypeQName(qnameDAO, assocTypeQName); assoc.setChildNodeName(name); assoc.setChildNodeNameCrc(-1L); // random names compete only with each other - assoc.setQnameNamespace(assocQNameNamespaceEntity); - assoc.setQnameLocalName(assocQNameLocalName); + assoc.setQName(qnameDAO, assocQName); assoc.setIsPrimary(isPrimary); assoc.setIndex(-1); // maintain inverse sets @@ -1591,7 +1587,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements recordNodeUpdate(childNode); // done - return new Pair(assocId, assoc.getChildAssocRef()); + return new Pair(assocId, assoc.getChildAssocRef(qnameDAO)); } public void setChildNameUnique(final Long childAssocId, String childName) @@ -1672,7 +1668,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } throw new DuplicateChildNodeNameException( parentNode.getNodeRef(), - childAssoc.getTypeQName().getQName(), + childAssoc.getTypeQName(qnameDAO), childName); } @@ -1702,10 +1698,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final Node newParentNode = getNodeNotNull(parentNodeId); final Node newChildNode = getNodeNotNull(childNodeId); final NodeRef newChildNodeRef = newChildNode.getNodeRef(); - QNameEntity assocTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(assocTypeQName); - String assocQNameNamespace = assocQName.getNamespaceURI(); - String assocQNameLocalName = assocQName.getLocalName(); - NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getOrCreateNamespaceEntity(assocQNameNamespace); // Reset the cm:name duplicate handling. This has to be redone, if required. String name = GUID.generate(); @@ -1713,9 +1705,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements childAssoc.setChildNodeNameCrc(-1L); childAssoc.buildAssociation(newParentNode, newChildNode); - childAssoc.setTypeQName(assocTypeQNameEntity); - childAssoc.setQnameNamespace(assocQNameNamespaceEntity); - childAssoc.setQnameLocalName(assocQNameLocalName); + childAssoc.setTypeQName(qnameDAO, assocTypeQName); + childAssoc.setQName(qnameDAO, assocQName); if (index >= 0) { childAssoc.setIndex(index); @@ -1787,7 +1778,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } // Done - return new Pair(childAssocId, childAssoc.getChildAssocRef()); + return new Pair(childAssocId, childAssoc.getChildAssocRef(qnameDAO)); } /** @@ -1927,9 +1918,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements @SuppressWarnings("unchecked") public void getChildAssocs(final Long parentNodeId, final QName assocQName, ChildAssocRefQueryCallback resultsCallback) { - final NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getNamespaceEntity(assocQName.getNamespaceURI()); + final Pair assocQNameNamespacePair = qnameDAO.getNamespace(assocQName.getNamespaceURI()); final String assocQNameLocalName = assocQName.getLocalName(); - if (assocQNameNamespaceEntity == null) + if (assocQNameNamespacePair == null) { // There can't be any matches return; @@ -1943,7 +1934,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS_BY_QNAME) .setLong("parentId", parentNodeId) - .setParameter("qnameNamespace", assocQNameNamespaceEntity) + .setLong("qnameNamespaceId", assocQNameNamespacePair.getFirst()) .setString("qnameLocalName", assocQNameLocalName); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.scroll(ScrollMode.FORWARD_ONLY); @@ -1971,16 +1962,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements ChildAssocRefQueryCallback resultsCallback) { // Convert the type QNames to entities - final List assocTypeQNameIds = new ArrayList(assocTypeQNames.size()); - for (QName assocTypeQName : assocTypeQNames) - { - QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); - if (assocTypeQNameEntity == null) - { - continue; - } - assocTypeQNameIds.add(assocTypeQNameEntity.getId()); - } + + final Set assocTypeQNameSet = new HashSet(assocTypeQNames); + final Set assocTypeQNameIds = qnameDAO.convertQNamesToIds(assocTypeQNameSet, false); // Shortcut if there are no assoc types if (assocTypeQNameIds.size() == 0) { @@ -2024,11 +2008,11 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Node parentNode = getNodeNotNull(parentNodeId); - final QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); - final NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getNamespaceEntity(assocQName.getNamespaceURI()); + final Pair assocTypeQNamePair = qnameDAO.getQName(assocTypeQName); + final Pair assocQNameNamespacePair = qnameDAO.getNamespace(assocQName.getNamespaceURI()); final String assocQNameLocalName = assocQName.getLocalName(); // Shortcut if possible - if (assocTypeQNameEntity == null || assocQNameNamespaceEntity == null) + if (assocTypeQNamePair == null || assocQNameNamespacePair == null) { return; } @@ -2040,8 +2024,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS_BY_TYPEQNAME_AND_QNAME) .setLong("parentId", parentNodeId) - .setParameter("typeQName", assocTypeQNameEntity) - .setParameter("qnameNamespace", assocQNameNamespaceEntity) + .setLong("typeQNameId", assocTypeQNamePair.getFirst()) + .setLong("qnameNamespaceId", assocQNameNamespacePair.getFirst()) .setString("qnameLocalName", assocQNameLocalName); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.scroll(ScrollMode.FORWARD_ONLY); @@ -2071,19 +2055,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Node parentNode = getNodeNotNull(parentNodeId); // Get the IDs for all the QNames we are after - final List childNodeTypeQNameEntities = new ArrayList(childNodeTypeQNames.size()); - for (QName childNodeTypeQName : childNodeTypeQNames) - { - QNameEntity qnameEntity = qnameDAO.getQNameEntity(childNodeTypeQName); - if (qnameEntity == null) - { - // No such QName persisted so ignore it - continue; - } - childNodeTypeQNameEntities.add(qnameEntity); - } + final Set childNodeTypeQNameIds = qnameDAO.convertQNamesToIds(childNodeTypeQNames, false); // Shortcut if there are no QNames available - if (childNodeTypeQNameEntities.size() == 0) + if (childNodeTypeQNameIds.size() == 0) { return; } @@ -2095,7 +2069,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_REFS_BY_CHILD_TYPEQNAME) .setLong("parentId", parentNodeId) - .setParameterList("childTypeQNameEntities", childNodeTypeQNameEntities); + .setParameterList("childTypeQNameIds", childNodeTypeQNameIds); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.scroll(ScrollMode.FORWARD_ONLY); } @@ -2178,9 +2152,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements public Pair getChildAssoc(final Long parentNodeId, final QName assocTypeQName, final String childName) { - final QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + final Pair assocTypeQNamePair = qnameDAO.getQName(assocTypeQName); // Shortcut - if (assocTypeQNameEntity == null) + if (assocTypeQNamePair == null) { return null; } @@ -2195,8 +2169,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME) .setLong("parentId", parentNodeId) - .setParameter("typeQName", assocTypeQNameEntity) - .setParameter("childNodeName", childNameShort) + .setLong("typeQNameId", assocTypeQNamePair.getFirst()) + .setString("childNodeName", childNameShort) .setLong("childNodeNameCrc", childNameLowerCrc); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.uniqueResult(); @@ -2209,7 +2183,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } else { - return new Pair(childAssoc.getId(), childAssoc.getChildAssocRef()); + return new Pair(childAssoc.getId(), childAssoc.getChildAssocRef(qnameDAO)); } } @@ -2220,11 +2194,11 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final QName assocQName) { - final QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); - final NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getNamespaceEntity(assocQName.getNamespaceURI()); + final Pair assocTypeQNamePair = qnameDAO.getQName(assocTypeQName); + final Pair assocQNameNamespacePair = qnameDAO.getNamespace(assocQName.getNamespaceURI()); final String assocQNameLocalName = assocQName.getLocalName(); // Shortcut if possible - if (assocTypeQNameEntity == null || assocQNameNamespaceEntity == null) + if (assocTypeQNamePair == null || assocQNameNamespacePair == null) { return null; } @@ -2237,8 +2211,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS_BY_ALL) .setLong("parentId", parentNodeId) .setLong("childId", childNodeId) - .setParameter("typeQName", assocTypeQNameEntity) - .setParameter("qnameNamespace", assocQNameNamespaceEntity) + .setLong("typeQNameId", assocTypeQNamePair.getFirst()) + .setParameter("qnameNamespaceId", assocQNameNamespacePair.getFirst()) .setParameter("qnameLocalName", assocQNameLocalName); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.uniqueResult(); @@ -2251,7 +2225,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } else { - return new Pair(childAssoc.getId(), childAssoc.getChildAssocRef()); + return new Pair(childAssoc.getId(), childAssoc.getChildAssocRef(qnameDAO)); } } @@ -2279,11 +2253,10 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Object[] row = results.get(); Long assocId = (Long) row[0]; - QNameEntity assocTypeQNameEntity = (QNameEntity) row[1]; - QName assocTypeQName = assocTypeQNameEntity.getQName(); - NamespaceEntity assocQNameNamespaceEntity = (NamespaceEntity) row[2]; + QName assocTypeQName = qnameDAO.getQName((Long) row[1]).getSecond(); + String assocQNameNamespace = qnameDAO.getNamespace((Long) row[2]).getSecond(); String assocQNameLocalName = (String) row[3]; - QName assocQName = QName.createQName(assocQNameNamespaceEntity.getUri(), assocQNameLocalName); + QName assocQName = QName.createQName(assocQNameNamespace, assocQNameLocalName); Boolean assocIsPrimary = (Boolean) row[4]; Integer assocIndex = (Integer) row[5]; Long childNodeId = (Long) row[6]; @@ -2311,7 +2284,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements for (NodeAssoc nodeAssoc : queryResults) { Long nodeAssocId = nodeAssoc.getId(); - AssociationRef assocRef = nodeAssoc.getNodeAssocRef(); + AssociationRef assocRef = nodeAssoc.getNodeAssocRef(qnameDAO); refs.add(new Pair(nodeAssocId, assocRef)); } return refs; @@ -2323,9 +2296,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final int count, NodeRefQueryCallback resultsCallback) { - final QNameEntity aspectQNameEntity = qnameDAO.getQNameEntity(aspectQName); + final Pair aspectQNamePair = qnameDAO.getQName(aspectQName); // Shortcut - if (aspectQNameEntity == null) + if (aspectQNamePair == null) { return; } @@ -2336,7 +2309,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Query query = session .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODES_WITH_ASPECT) - .setParameter("aspectQName", aspectQNameEntity) + .setLong("aspectQNameId", aspectQNamePair.getFirst()) .setLong("minNodeId", minNodeId) .setMaxResults(count); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); @@ -2433,12 +2406,12 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final QName assocTypeQName, final QName assocQName) { - final QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); - final NamespaceEntity assocQNameNamespaceEntity = qnameDAO.getNamespaceEntity(assocQName.getNamespaceURI()); + final Pair assocTypeQNamePair = qnameDAO.getQName(assocTypeQName); + final Pair assocQNameNamespacePair = qnameDAO.getNamespace(assocQName.getNamespaceURI()); final String assocQNameLocalName = assocQName.getLocalName(); // Shortcut - if (assocTypeQNameEntity == null || assocQNameNamespaceEntity == null) + if (assocTypeQNamePair == null || assocQNameNamespacePair == null) { return false; } @@ -2451,8 +2424,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CHILD_ASSOCS_BY_ALL) .setLong("parentId", parentNodeId) .setLong("childId", childNodeId) - .setParameter("typeQName", assocTypeQNameEntity) - .setParameter("qnameNamespace", assocQNameNamespaceEntity) + .setLong("typeQNameId", assocTypeQNamePair.getFirst()) + .setParameter("qnameNamespaceId", assocQNameNamespacePair.getFirst()) .setParameter("qnameLocalName", assocQNameLocalName); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.list(); @@ -2587,13 +2560,17 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements return query.list(); } }; - parentAssocs = (List) getHibernateTemplate().execute(callback); - // Populate the cache + List rows = (List) getHibernateTemplate().execute(callback); + parentAssocs = new ArrayList(rows.size()); parentAssocIds = new HashSet(parentAssocs.size()); - for (ChildAssoc parentAssoc : parentAssocs) + for (Object[] row : rows) { + ChildAssoc parentAssoc = (ChildAssoc) row[0]; + // Populate the results + parentAssocs.add(parentAssoc); parentAssocIds.add(parentAssoc.getId()); } + // Populate the cache parentAssocsCache.put(childNodeId, parentAssocIds); if (isDebugParentAssocCacheEnabled) { @@ -2620,7 +2597,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements for (ChildAssoc childAssoc : parentAssocs) { Long childAssocId = childAssoc.getId(); - ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef(); + ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef(qnameDAO); Pair childAssocPair = new Pair(childAssocId, childAssocRef); ret.add(childAssocPair); } @@ -2675,7 +2652,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } else { - return new Pair(primaryAssoc.getId(), primaryAssoc.getChildAssocRef()); + return new Pair(primaryAssoc.getId(), primaryAssoc.getChildAssocRef(qnameDAO)); } } @@ -2684,8 +2661,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final Node sourceNode = getNodeNotNull(sourceNodeId); final Node targetNode = getNodeNotNull(targetNodeId); - final QNameEntity assocTypeQNameEntity = qnameDAO.getOrCreateQNameEntity(assocTypeQName); - HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) @@ -2694,7 +2669,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements DirtySessionMethodInterceptor.flushSession(session, true); NodeAssoc assoc = new NodeAssocImpl(); - assoc.setTypeQName(assocTypeQNameEntity); + assoc.setTypeQName(qnameDAO, assocTypeQName); assoc.buildAssociation(sourceNode, targetNode); session.save(assoc); @@ -2710,7 +2685,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { NodeAssoc assoc = (NodeAssoc) getHibernateTemplate().execute(callback); // done - return new Pair(assoc.getId(), assoc.getNodeAssocRef()); + return new Pair(assoc.getId(), assoc.getNodeAssocRef(qnameDAO)); } catch (DataIntegrityViolationException e) { @@ -2746,9 +2721,9 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final Long targetNodeId, final QName assocTypeQName) { - final QNameEntity assocTypeQNameEntity = qnameDAO.getQNameEntity(assocTypeQName); + final Pair assocTypeQNamePair = qnameDAO.getQName(assocTypeQName); // Shortcut - if (assocTypeQNameEntity == null) + if (assocTypeQNamePair == null) { return null; } @@ -2761,13 +2736,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODE_ASSOC) .setLong("sourceId", sourceNodeId) .setLong("targetId", targetNodeId) - .setParameter("assocTypeQName", assocTypeQNameEntity); + .setLong("assocTypeQNameId", assocTypeQNamePair.getFirst()); DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.uniqueResult(); } }; NodeAssoc result = (NodeAssoc) getHibernateTemplate().execute(callback); - Pair ret = new Pair(result.getId(), result.getNodeAssocRef()); + Pair ret = new Pair(result.getId(), result.getNodeAssocRef(qnameDAO)); return ret; } @@ -2824,13 +2799,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements final String value, final NodePropertyHandler handler) { - QNameEntity propQNameEntity = qnameDAO.getQNameEntity(propertyQName); + Pair propQNamePair = qnameDAO.getQName(propertyQName); // Shortcut - if (propQNameEntity == null) + if (propQNamePair == null) { return; } - final Long propQNameEntityId = propQNameEntity.getId(); + final Long propQNameEntityId = propQNamePair.getFirst(); // Run the query HibernateCallback callback = new HibernateCallback() { @@ -2857,8 +2832,8 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { Node node = (Node) results.get(0); NodeRef nodeRef = node.getNodeRef(); - QNameEntity nodeTypeQNameEntity = node.getTypeQName(); - QName nodeTypeQName = nodeTypeQNameEntity.getQName(); + Long nodeTypeQNameId = node.getTypeQNameId(); + QName nodeTypeQName = qnameDAO.getQName(nodeTypeQNameId).getSecond(); handler.handle(nodeRef, nodeTypeQName, propertyQName, value); // Flush if required DirtySessionMethodInterceptor.flushSession(session); @@ -2873,36 +2848,154 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } } - public void getNodesWithCreatorAndStore( + public void getContentUrlsForStore( final StoreRef storeRef, - final String userName, - final NodeRefQueryCallback resultsCallback) + final ObjectArrayQueryCallback resultsCallback) { + final Long contentTypeQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.TYPE_CONTENT).getFirst(); + final Long ownerPropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_OWNER).getFirst(); + final Long contentPropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_CONTENT).getFirst(); + HibernateCallback callback = new HibernateCallback() { public Object doInHibernate(Session session) { Query query = session - .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_NODES_WITH_CREATOR_AND_STORE) + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_CONTENT_URLS_FOR_STORE) .setString("storeProtocol", storeRef.getProtocol()) .setString("storeIdentifier", storeRef.getIdentifier()) - .setString("userName", userName) + .setParameter("ownerPropQNameID", ownerPropQNameEntityId) // cm:owner + .setParameter("contentPropQNameID", contentPropQNameEntityId) // cm:content + .setParameter("contentTypeQNameID", contentTypeQNameEntityId) // cm:content ; DirtySessionMethodInterceptor.setQueryFlushMode(session, query); return query.scroll(ScrollMode.FORWARD_ONLY); } }; - ScrollableResults queryResults = null; + ScrollableResults results = null; try { - queryResults = (ScrollableResults) getHibernateTemplate().execute(callback); - processNodeResults(queryResults, resultsCallback); + results = (ScrollableResults) getHibernateTemplate().execute(callback); + // Callback with the results + Session session = getSession(); + while (results.next()) + { + Object[] arr = new Object[3]; + arr[0] = (String)results.get(0); // owner (can be null) + arr[1] = (String)results.get(1); // creator + arr[2] = (String)results.get(2); // contentUrl + resultsCallback.handle(arr); + // Flush if required + DirtySessionMethodInterceptor.flushSession(session); + } } finally { - if (queryResults != null) + if (results != null) { - queryResults.close(); + results.close(); + } + } + + // Done + } + + public void getUsersWithoutUsage( + final StoreRef storeRef, + final ObjectArrayQueryCallback resultsCallback) + { + final Long personTypeQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.TYPE_PERSON).getFirst(); + final Long usernamePropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_USERNAME).getFirst(); + final Long sizeCurrentPropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_SIZE_CURRENT).getFirst(); + + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_USERS_WITHOUT_USAGE) + .setString("storeProtocol", storeRef.getProtocol()) + .setString("storeIdentifier", storeRef.getIdentifier()) + .setParameter("usernamePropQNameID", usernamePropQNameEntityId) // cm:username + .setParameter("sizeCurrentPropQNameID", sizeCurrentPropQNameEntityId) // cm:sizeCurrent + .setParameter("personTypeQNameID", personTypeQNameEntityId) // cm:person + ; + DirtySessionMethodInterceptor.setQueryFlushMode(session, query); + return query.scroll(ScrollMode.FORWARD_ONLY); + } + }; + ScrollableResults results = null; + try + { + results = (ScrollableResults) getHibernateTemplate().execute(callback); + // Callback with the results + Session session = getSession(); + while (results.next()) + { + Object[] arr = new Object[2]; + arr[0] = (String)results.get(0); // username + arr[1] = (String)results.get(1); // node uuid + resultsCallback.handle(arr); + // Flush if required + DirtySessionMethodInterceptor.flushSession(session); + } + } + finally + { + if (results != null) + { + results.close(); + } + } + + // Done + } + + public void getUsersWithUsage( + final StoreRef storeRef, + final ObjectArrayQueryCallback resultsCallback) + { + final Long personTypeQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.TYPE_PERSON).getFirst(); + final Long usernamePropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_USERNAME).getFirst(); + final Long sizeCurrentPropQNameEntityId = qnameDAO.getOrCreateQName(ContentModel.PROP_SIZE_CURRENT).getFirst(); + + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_USERS_WITH_USAGE) + .setString("storeProtocol", storeRef.getProtocol()) + .setString("storeIdentifier", storeRef.getIdentifier()) + .setParameter("usernamePropQNameID", usernamePropQNameEntityId) // cm:username + .setParameter("sizeCurrentPropQNameID", sizeCurrentPropQNameEntityId) // cm:sizeCurrent + .setParameter("personTypeQNameID", personTypeQNameEntityId) // cm:person + ; + DirtySessionMethodInterceptor.setQueryFlushMode(session, query); + return query.scroll(ScrollMode.FORWARD_ONLY); + } + }; + ScrollableResults results = null; + try + { + results = (ScrollableResults) getHibernateTemplate().execute(callback); + // Callback with the results + Session session = getSession(); + while (results.next()) + { + Object[] arr = new Object[2]; + arr[0] = (String)results.get(0); // username + arr[1] = (String)results.get(1); // node uuid + resultsCallback.handle(arr); + // Flush if required + DirtySessionMethodInterceptor.flushSession(session); + } + } + finally + { + if (results != null) + { + results.close(); } } @@ -2936,18 +3029,21 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements while (results.next()) { Node node = (Node) results.get()[0]; + Long nodeTypeQNameId = node.getTypeQNameId(); + QName nodeTypeQName = qnameDAO.getQName(nodeTypeQNameId).getSecond(); // loop through all the node properties Map properties = node.getProperties(); for (Map.Entry entry : properties.entrySet()) { - PropertyMapKey propertyKey = entry.getKey(); + PropertyMapKey propertyKey = entry.getKey(); + Long propertyQNameId = propertyKey.getQnameId(); + QName propertyQName = qnameDAO.getQName(propertyQNameId).getSecond(); NodePropertyValue propertyValue = entry.getValue(); // ignore nulls if (propertyValue == null) { continue; } - Long propertyQNameId = propertyKey.getQnameId(); // Get the actual value(s) as a collection Collection values = propertyValue.getCollection(DataTypeDefinition.ANY); // attempt to convert instance in the collection @@ -2970,8 +3066,6 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements if (convertedValue != null) { NodeRef nodeRef = node.getNodeRef(); - QName nodeTypeQName = node.getTypeQName().getQName(); - QName propertyQName = qnameDAO.getQName(propertyQNameId); handler.handle(nodeRef, nodeTypeQName, propertyQName, convertedValue); } } @@ -3401,7 +3495,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements Serializable value = entry.getValue(); // Get the qname ID QName propertyQName = entry.getKey(); - Long propertyQNameId = qnameDAO.getOrCreateQNameEntity(propertyQName).getId(); + Long propertyQNameId = qnameDAO.getOrCreateQName(propertyQName).getFirst(); // Get the locale ID Long propertylocaleId = localeDAO.getOrCreateDefaultLocalePair().getFirst(); // Get the property definition, if available @@ -3659,13 +3753,13 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements DictionaryService dictionaryService) { // Get the qname ID - QNameEntity qnameEntity = qnameDAO.getQNameEntity(propertyQName); - if (qnameEntity == null) + Pair qnamePair = qnameDAO.getQName(propertyQName); + if (qnamePair == null) { // There is no persisted property with that QName, so we can't match anything return null; } - Long qnameId = qnameEntity.getId(); + Long qnameId = qnamePair.getFirst(); // Now loop over the properties and extract those with the given qname ID SortedMap scratch = new TreeMap(); for (Map.Entry entry : propertyValues.entrySet()) @@ -3727,7 +3821,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements // If the QName is going to change, and we have some entries to process, then process them. if (scratch.size() > 0 && (nextQNameId == null || !nextQNameId.equals(currentQNameId))) { - QName currentQName = qnameDAO.getQName(currentQNameId); + QName currentQName = qnameDAO.getQName(currentQNameId).getSecond(); PropertyDefinition currentPropertyDef = dictionaryService.getProperty(currentQName); // We have added something to the scratch properties but the qname has just changed Serializable collapsedValue = null; diff --git a/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java b/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java index 76022eeaed..ceb4795dbc 100644 --- a/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonDaoImpl.java @@ -37,7 +37,6 @@ import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.NodePropertyValue; import org.alfresco.repo.domain.PropertyMapKey; import org.alfresco.repo.domain.QNameDAO; -import org.alfresco.repo.domain.hibernate.NodeImpl; import org.alfresco.repo.node.db.hibernate.HibernateNodeDaoServiceImpl; import org.alfresco.repo.tenant.TenantService; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -134,7 +133,7 @@ public class PersonDaoImpl extends HibernateDaoSupport implements PersonDao public void init() { - qNameId = qnameDAO.getOrCreateQNameEntity(ContentModel.PROP_USERNAME).getId(); + qNameId = qnameDAO.getOrCreateQName(ContentModel.PROP_USERNAME).getFirst(); } @SuppressWarnings("unchecked") diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index 86b012ce75..221144197e 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * Copyright (C) 2005-2008 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 @@ -24,16 +24,17 @@ */ package org.alfresco.repo.usage; -import java.io.Serializable; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.alfresco.model.ContentModel; import org.alfresco.repo.node.db.NodeDaoService; -import org.alfresco.repo.node.db.NodeDaoService.NodePropertyHandler; -import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback; +import org.alfresco.repo.node.db.NodeDaoService.ObjectArrayQueryCallback; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.Tenant; @@ -44,12 +45,9 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; -import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; -import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.usage.UsageService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; -import org.apache.commons.lang.mutable.MutableLong; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -63,18 +61,21 @@ public class UserUsageTrackingComponent { private static Log logger = LogFactory.getLog(UserUsageTrackingComponent.class); - private static boolean busy = false; - private TransactionServiceImpl transactionService; private ContentUsageImpl contentUsageImpl; - private PersonService personService; private NodeService nodeService; private NodeDaoService nodeDaoService; private UsageService usageService; private TenantAdminService tenantAdminService; + private StoreRef personStoreRef; + + private int clearBatchSize = 50; + private int updateBatchSize = 50; + private boolean enabled = true; + private Lock writeLock = new ReentrantLock(); public void setTransactionService(TransactionServiceImpl transactionService) { @@ -86,9 +87,9 @@ public class UserUsageTrackingComponent this.contentUsageImpl = contentUsageImpl; } - public void setPersonService(PersonService personService) + public void setPersonStoreUrl(String storeUrl) { - this.personService = personService; + this.personStoreRef = new StoreRef(storeUrl); } public void setNodeService(NodeService nodeService) @@ -111,6 +112,16 @@ public class UserUsageTrackingComponent this.tenantAdminService = tenantAdminService; } + public void setClearBatchSize(int clearBatchSize) + { + this.clearBatchSize = clearBatchSize; + } + + public void setUpdateBatchSize(int updateBatchSize) + { + this.updateBatchSize = updateBatchSize; + } + public void setEnabled(boolean enabled) { this.enabled = enabled; @@ -118,23 +129,24 @@ public class UserUsageTrackingComponent public void execute() { - if (enabled == true) + if (enabled == false || transactionService.isReadOnly()) { - if (! busy) - { - try - { - busy = true; - - // collapse usages - note: for MT environment, will collapse for all tenants - collapseUsages(); - } - finally - { - busy = false; - } - } + return; } + + boolean locked = writeLock.tryLock(); + if (locked) + { + // collapse usages - note: for MT environment, will collapse for all tenants + try + { + collapseUsages(); + } + finally + { + writeLock.unlock(); + } + } } // called once on startup @@ -162,104 +174,138 @@ public class UserUsageTrackingComponent public void bootstrapInternal() { - if (! busy) - { - try - { - busy = true; - - if (enabled) - { - // enabled - calculate missing usages - calculateMissingUsages(); - } - else - { - // disabled - remove all usages - clearAllUsages(); - } + if (transactionService.isReadOnly()) + { + return; + } + + boolean locked = writeLock.tryLock(); + if (locked) + { + try + { + if (enabled) + { + // enabled - calculate missing usages + calculateMissingUsages(); + } + else + { + if (clearBatchSize != 0) + { + // disabled - remove all usages + clearAllUsages(); + } + } } finally { - busy = false; + writeLock.unlock(); } - } + } } + /** + * Clear content usage for all users that have a usage. + */ private void clearAllUsages() { if (logger.isInfoEnabled()) { logger.info("Disabled - clear non-missing user usages ..."); } - - RetryingTransactionCallback> getPersonRefs = new RetryingTransactionCallback>() + + final Map users = new HashMap(); + + RetryingTransactionCallback getUsersWithUsage = new RetryingTransactionCallback() { - public List execute() throws Throwable + public Object execute() throws Throwable { - Set allPeople = personService.getAllPeople(); - - if (logger.isDebugEnabled()) + // get people (users) with calculated usage + ObjectArrayQueryCallback userHandler = new ObjectArrayQueryCallback() { - logger.debug("Found " + allPeople.size() + " people"); - } - - List personRefsToClear = new ArrayList(); - - for (NodeRef personNodeRef : allPeople) - { - Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); - if (currentUsage != null) + public boolean handle(Object[] arr) { - personRefsToClear.add(personNodeRef); + String username = (String)arr[0]; + String uuid = (String)arr[1]; + + users.put(username, new NodeRef(personStoreRef, uuid)); + + return true; // continue to next node (more required) } - } - - if (logger.isDebugEnabled()) - { - logger.debug("Found " + personRefsToClear.size() + " users to clear"); - } - - return personRefsToClear; + }; + nodeDaoService.getUsersWithUsage(personStoreRef, userHandler); + + return null; } }; // execute in READ-ONLY txn - List personRefsToClear = transactionService.getRetryingTransactionHelper().doInTransaction(getPersonRefs, true); + transactionService.getRetryingTransactionHelper().doInTransaction(getUsersWithUsage, true); - for (NodeRef personNodeRef : personRefsToClear) + if (logger.isInfoEnabled()) { - clearUsage(personNodeRef); + logger.info("Found " + users.size() + " users to clear"); + } + + int clearCount = 0; + int batchCount = 0; + int totalCount = 0; + + List batchPersonRefs = new ArrayList(clearBatchSize); + for (NodeRef personNodeRef : users.values()) + { + batchPersonRefs.add(personNodeRef); + batchCount++; + totalCount++; + + if ((batchCount == clearBatchSize) || (totalCount == users.size())) + { + int cleared = clearUsages(batchPersonRefs); + clearCount = clearCount + cleared; + + batchPersonRefs.clear(); + batchCount = 0; + } } if (logger.isInfoEnabled()) { - logger.info("... cleared non-missing usages for " + personRefsToClear.size() + " users"); - } + logger.info("... cleared non-missing usages for " + clearCount + " users"); + } } - private void clearUsage(final NodeRef personNodeRef) + private int clearUsages(final List personNodeRefs) { RetryingTransactionCallback clearPersonUsage = new RetryingTransactionCallback() { public Integer execute() throws Throwable { - nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null); - usageService.deleteDeltas(personNodeRef); - - if (logger.isTraceEnabled()) + int clearCount = 0; + for (NodeRef personNodeRef : personNodeRefs) { - logger.trace("Cleared usage for person ("+ personNodeRef+")"); + nodeService.setProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT, null); + usageService.deleteDeltas(personNodeRef); + + if (logger.isTraceEnabled()) + { + logger.trace("Cleared usage for person ("+ personNodeRef+")"); + } + + clearCount++; } - - return null; + return clearCount; } }; // execute in READ-WRITE txn - transactionService.getRetryingTransactionHelper().doInTransaction(clearPersonUsage, false); + return transactionService.getRetryingTransactionHelper().doInTransaction(clearPersonUsage, false); } + /** + * Recalculate content usage for all users that have no usage. + * Required if upgrading an existing Alfresco, for users that have not had their initial usage calculated. + */ private void calculateMissingUsages() { if (logger.isInfoEnabled()) @@ -267,157 +313,177 @@ public class UserUsageTrackingComponent logger.info("Enabled - calculate missing user usages ..."); } - RetryingTransactionCallback> getAllPeople = new RetryingTransactionCallback>() + final Map users = new HashMap(); + + RetryingTransactionCallback getUsersWithoutUsage = new RetryingTransactionCallback() { - public Set execute() throws Throwable + public Object execute() throws Throwable { - Set allPeople = personService.getAllPeople(); - - if (logger.isDebugEnabled()) + // get people (users) without calculated usage + ObjectArrayQueryCallback userHandler = new ObjectArrayQueryCallback() { - logger.debug("Found " + allPeople.size() + " people"); - } - - Set userNames = new HashSet(); - - for (NodeRef personNodeRef : allPeople) - { - Long currentUsage = (Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); - if (currentUsage == null) + public boolean handle(Object[] arr) { - String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); - userNames.add(userName); + String username = (String)arr[0]; + String uuid = (String)arr[1]; + + users.put(username, new NodeRef(personStoreRef, uuid)); + + return true; // continue to next node (more required) } - } - - if (logger.isDebugEnabled()) - { - logger.debug("Found " + userNames.size() + " users to recalculate"); - } - - return userNames; + }; + nodeDaoService.getUsersWithoutUsage(personStoreRef, userHandler); + + return null; } }; // execute in READ-ONLY txn - Set userNames = transactionService.getRetryingTransactionHelper().doInTransaction(getAllPeople, true); + transactionService.getRetryingTransactionHelper().doInTransaction(getUsersWithoutUsage, true); - for (final String userName : userNames) + if (logger.isInfoEnabled()) { - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - recalculateUsage(userName); - return null; - } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantAdminService.getUserDomain(userName))); + logger.info("Found " + users.size() + " users to recalculate"); + } + + int updateCount = 0; + if (users.size() > 0) + { + updateCount = recalculateUsages(users); } if (logger.isInfoEnabled()) { - logger.info("... calculated missing usages for " + userNames.size() + " users"); + logger.info("... calculated missing usages for " + updateCount + " users"); } } - /** - * Recalculate content usage for given user. Required if upgrading an existing Alfresco, for users that + /* + * Recalculate content usage for given users. Required if upgrading an existing Alfresco, for users that * have not had their initial usage calculated. In a future release, could also be called explicitly by * a SysAdmin, eg. via a JMX operation. - * - * @param username the username to for which calcualte usages */ - private void recalculateUsage(final String username) + private int recalculateUsages(final Map users) { - RetryingTransactionCallback calculatePersonCurrentUsage = new RetryingTransactionCallback() + final Map currentUserUsages = new HashMap(users.size()); + + RetryingTransactionCallback calculateCurrentUsages = new RetryingTransactionCallback() { public Long execute() throws Throwable { List stores = contentUsageImpl.getStores(); - final MutableLong totalUsage = new MutableLong(0L); - + for (String store : stores) { final StoreRef storeRef = new StoreRef(store); if (logger.isTraceEnabled()) { - logger.trace("Recalc usage (" + username + ") store=" + storeRef); + logger.trace("Recalc usages for store=" + storeRef); } - NodePropertyHandler propOwnerHandler = new NodePropertyHandler() + // get content urls + ObjectArrayQueryCallback nodeContentUrlHandler = new ObjectArrayQueryCallback() { - public void handle(NodeRef nodeRef, QName nodeTypeQName, QName propertyQName, Serializable value) + public boolean handle(Object[] arr) { - if (nodeTypeQName.equals(ContentModel.TYPE_CONTENT)) - { - // It is not content - } - ContentData contentData = DefaultTypeConverter.INSTANCE.convert( - ContentData.class, - nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT)); - if (contentData != null) - { - long currentTotalUsage = totalUsage.longValue(); - totalUsage.setValue(currentTotalUsage + contentData.getSize()); - } - } - }; - nodeDaoService.getPropertyValuesByPropertyAndValue(storeRef, ContentModel.PROP_OWNER, username, propOwnerHandler); - - // get nodes for which user is creator (ignore those with owner) - NodeRefQueryCallback nodeCreatorHandler = new NodeRefQueryCallback() - { - public boolean handle(Pair nodePair) - { - NodeRef nodeRef = nodePair.getSecond(); + String owner = (String)arr[0]; + String creator = (String)arr[1]; + String contentUrlStr = (String)arr[2]; - if (nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER) != null) + if (owner == null) { - // There is an owner property so we will have processed this already - return true; // continue to next node (more required) + owner = creator; } - ContentData contentData = DefaultTypeConverter.INSTANCE.convert( - ContentData.class, - nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT)); + ContentData contentData = ContentData.createContentProperty(contentUrlStr); if (contentData != null) { - long currentTotalUsage = totalUsage.longValue(); - totalUsage.setValue(currentTotalUsage + contentData.getSize()); + Long currentUsage = currentUserUsages.get(owner); + + if (currentUsage == null) + { + currentUsage = 0L; + } + + currentUserUsages.put(owner, currentUsage + contentData.getSize()); } return true; // continue to next node (more required) } }; - nodeDaoService.getNodesWithCreatorAndStore(storeRef, username, nodeCreatorHandler); - } - - if (logger.isTraceEnabled()) - { - long quotaSize = contentUsageImpl.getUserQuota(username); - logger.trace("Recalc usage ("+ username+") totalUsage="+totalUsage+", quota="+quotaSize); + nodeDaoService.getContentUrlsForStore(storeRef, nodeContentUrlHandler); } - return totalUsage.longValue(); - } - }; - - // execute in READ-ONLY txn - final Long currentUsage = transactionService.getRetryingTransactionHelper().doInTransaction(calculatePersonCurrentUsage, true); - - RetryingTransactionCallback updatePersonCurrentUsage = new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - NodeRef personNodeRef = personService.getPerson(username); - contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); - usageService.deleteDeltas(personNodeRef); return null; } }; + // execute in READ-ONLY txn + transactionService.getRetryingTransactionHelper().doInTransaction(calculateCurrentUsages, true); + + if (logger.isDebugEnabled()) + { + logger.debug("Usages calculated - start update"); + } + + int updateCount = 0; + int batchCount = 0; + int totalCount = 0; + + List> batchUserUsages = new ArrayList>(updateBatchSize); + + for (Map.Entry user : users.entrySet()) + { + String userName = user.getKey(); + NodeRef personNodeRef = user.getValue(); + + Long currentUsage = currentUserUsages.get(userName); + if (currentUsage == null) + { + currentUsage = 0L; + } + + batchUserUsages.add(new Pair(personNodeRef, currentUsage)); + batchCount++; + totalCount++; + + if ((batchCount == updateBatchSize) || (totalCount == users.size())) + { + int updated = updateUsages(batchUserUsages); + updateCount = updateCount + updated; + + batchUserUsages.clear(); + batchCount = 0; + } + } + + return totalCount; + } + + private int updateUsages(final List> userUsages) + { + RetryingTransactionCallback updateCurrentUsages = new RetryingTransactionCallback() + { + public Integer execute() throws Throwable + { + int updateCount = 0; + + for (Pair userUsage : userUsages) + { + NodeRef personNodeRef = userUsage.getFirst(); + Long currentUsage = userUsage.getSecond(); + + contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); + usageService.deleteDeltas(personNodeRef); + + updateCount++; + } + return updateCount; + } + }; + // execute in READ-WRITE txn - transactionService.getRetryingTransactionHelper().doInTransaction(updatePersonCurrentUsage, false); + return transactionService.getRetryingTransactionHelper().doInTransaction(updateCurrentUsages, false); } /** @@ -425,57 +491,91 @@ public class UserUsageTrackingComponent */ private void collapseUsages() { - // Collapse usage deltas (if a person has initial usage set) - RetryingTransactionCallback collapseUsages = new RetryingTransactionCallback() + if (logger.isDebugEnabled()) { - public Object execute() throws Throwable + logger.debug("Collapse usages ..."); + } + + // Collapse usage deltas (if a person has initial usage set) + RetryingTransactionCallback> getUsageNodeRefs = new RetryingTransactionCallback>() + { + public Set execute() throws Throwable { // Get distinct candidates - Set usageNodeRefs = usageService.getUsageDeltaNodes(); + return usageService.getUsageDeltaNodes(); + } + }; + + // execute in READ-ONLY txn + Set usageNodeRefs = transactionService.getRetryingTransactionHelper().doInTransaction(getUsageNodeRefs, true); + + int collapseCount = 0; + for (final NodeRef usageNodeRef : usageNodeRefs) + { + Boolean collapsed = AuthenticationUtil.runAs(new RunAsWork() + { + public Boolean doWork() throws Exception + { + return collapseUsage(usageNodeRef); + } + }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantAdminService.getDomain(usageNodeRef.getStoreRef().getIdentifier()))); + + if (collapsed) + { + collapseCount++; + } + } + + if (logger.isDebugEnabled()) + { + logger.debug("... collapsed usages for " + collapseCount + " users"); + } + } + + private boolean collapseUsage(final NodeRef usageNodeRef) + { + RetryingTransactionCallback collapseUsages = new RetryingTransactionCallback() + { + public Boolean execute() throws Throwable + { + if (!nodeService.exists(usageNodeRef)) + { + // Ignore + return false; + } + QName nodeType = nodeService.getType(usageNodeRef); - for(final NodeRef usageNodeRef : usageNodeRefs) - { - AuthenticationUtil.runAs(new RunAsWork() + if (nodeType.equals(ContentModel.TYPE_PERSON)) + { + NodeRef personNodeRef = usageNodeRef; + String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); + + long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef); + if (currentUsage != -1) { - public Object doWork() throws Exception + // collapse the usage deltas + currentUsage = contentUsageImpl.getUserUsage(userName); + usageService.deleteDeltas(personNodeRef); + contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); + + if (logger.isTraceEnabled()) { - QName nodeType = nodeService.getType(usageNodeRef); - - if (nodeType.equals(ContentModel.TYPE_PERSON)) - { - NodeRef personNodeRef = usageNodeRef; - String userName = (String)nodeService.getProperty(personNodeRef, ContentModel.PROP_USERNAME); - - long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef); - if (currentUsage != -1) - { - // collapse the usage deltas - currentUsage = contentUsageImpl.getUserUsage(userName); - usageService.deleteDeltas(personNodeRef); - contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage); - - if (logger.isTraceEnabled()) - { - logger.trace("Collapsed usage: username=" + userName + ", usage=" + currentUsage); - } - } - else - { - if (logger.isWarnEnabled()) - { - logger.warn("Initial usage for user has not yet been calculated: " + userName); - } - } - } - return null; + logger.trace("Collapsed usage: username=" + userName + ", usage=" + currentUsage); } - }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantAdminService.getDomain(usageNodeRef.getStoreRef().getIdentifier()))); - } - return null; + } + else + { + if (logger.isWarnEnabled()) + { + logger.warn("Initial usage for user has not yet been calculated: " + userName); + } + } + } + return true; } }; // execute in READ-WRITE txn - transactionService.getRetryingTransactionHelper().doInTransaction(collapseUsages, false); + return transactionService.getRetryingTransactionHelper().doInTransaction(collapseUsages, false); } } diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java new file mode 100644 index 0000000000..429b2bdfd9 --- /dev/null +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2005-2008 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.usage; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.usage.ContentUsageService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.springframework.context.ApplicationContext; + +/** + * Test enabling (recalculating) and disabling (clearing) and user usages, and also collapsing user usage deltas. + */ +public class UserUsageTrackingComponentTest extends TestCase +{ + private static ApplicationContext applicationContext = ApplicationContextHelper.getApplicationContext(); + + private boolean clean = true; + + private AuthenticationService authenticationService; + private ContentService contentService; + private TransactionService transactionService; + private PersonService personService; + private NodeService nodeService; + private UserUsageTrackingComponent userUsageTrackingComponent; + private ContentUsageService contentUsageService; + + private UserTransaction testTX; + + public static StoreRef SPACES_STORE = new StoreRef(StoreRef.PROTOCOL_WORKSPACE, "SpacesStore"); + + private static final String TEST_USER_PREFIX = "user-"; + + private static final int MAX_USERS = 100; + private static final int BATCH_SIZE = 5; + private static final int PROGRESS_SIZE = 100; + + protected void setUp() throws Exception + { + nodeService = (NodeService)applicationContext.getBean("NodeService"); + authenticationService = (AuthenticationService)applicationContext.getBean("authenticationService"); + transactionService = (TransactionService)applicationContext.getBean("transactionComponent"); + personService = (PersonService)applicationContext.getBean("personService"); + contentService = (ContentService)applicationContext.getBean("ContentService"); + contentUsageService = (ContentUsageService)applicationContext.getBean("ContentUsageService"); + + userUsageTrackingComponent = (UserUsageTrackingComponent)applicationContext.getBean("userUsageTrackingComponent"); + + AuthenticationUtil.setSystemUserAsCurrentUser(); + + createUsersAndContent(); + } + + protected void tearDown() throws Exception + { + if (clean) + { + deleteUsersAndContent(); + } + + super.tearDown(); + } + + private void createUsersAndContent() + { + long start = System.currentTimeMillis(); + long progressStart = System.currentTimeMillis(); + + try + { + int count = 0; + int batch = 0; + //long batchStart = 0; + + for (int i = 1; i <= MAX_USERS; i++) + { + if (count == 0) + { + batch++; + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + + //batchStart = System.currentTimeMillis(); + } + + count++; + String userName = TEST_USER_PREFIX+i; + + if (! authenticationService.authenticationExists(userName)) + { + // Note: this will auto-create the user home + HashMap props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, userName); + personService.createPerson(props); + + authenticationService.createAuthentication(userName, userName.toCharArray()); + + NodeRef homeFolder = getHomeSpaceFolderNode(userName); + + StringBuilder sb = new StringBuilder(); + for (int j = 1; j <= i; j++) + { + int k = j % 10; + sb.append(k); + } + + AuthenticationUtil.setCurrentUser(userName); + + addTextContent(homeFolder, "a-"+userName+".txt", sb.toString()); + addTextContent(homeFolder, "b-"+userName+".txt", sb.toString()); + } + + AuthenticationUtil.setSystemUserAsCurrentUser(); + + if ((count == BATCH_SIZE) || (i == MAX_USERS)) + { + testTX.commit(); + count = 0; + + //System.out.println("Batch "+batch+" took "+((System.currentTimeMillis()-batchStart)/1000)+" secs"); + } + + if (((i % PROGRESS_SIZE) == 0) && (i != MAX_USERS)) + { + System.out.println("Progress: "+PROGRESS_SIZE+" users created in "+((System.currentTimeMillis()-progressStart)/1000)+" secs"); + progressStart = System.currentTimeMillis(); + } + } + + System.out.println("Total: "+MAX_USERS+" users created in "+((System.currentTimeMillis()-start)/1000)+" secs"); + } + catch (Throwable t) + { + t.printStackTrace(); + + try { testTX.rollback(); } catch (Exception e) { e.printStackTrace(); } + } + } + + public void testEnableDisableCollapse() + { + userUsageTrackingComponent.setEnabled(false); + userUsageTrackingComponent.bootstrapInternal(); // false => clear + + System.out.println("Cleared usages"); + + checkCleared(); + + userUsageTrackingComponent.setEnabled(true); + userUsageTrackingComponent.bootstrapInternal(); // true => recalculate + + System.out.println("Recalculated usages"); + + checkCalculated(2L); + + checkUsage(2L); + + // add content + for (int i = 1; i <= MAX_USERS; i++) + { + String userName = TEST_USER_PREFIX+i; + + NodeRef homeFolder = getHomeSpaceFolderNode(userName); + + AuthenticationUtil.setCurrentUser(userName); + + StringBuilder sb = new StringBuilder(); + for (int j = 1; j <= i; j++) + { + int k = j % 10; + sb.append(k); + } + + addTextContent(homeFolder, "c-"+userName+".txt", sb.toString()); + addTextContent(homeFolder, "d-"+userName+".txt", sb.toString()); + + AuthenticationUtil.setSystemUserAsCurrentUser(); + } + + System.out.println("Added content"); + + checkUsage(4L); + + userUsageTrackingComponent.execute(); // collapse usages + + System.out.println("Collapsed usages"); + + checkUsage(4L); + + // delete content + for (int i = 1; i <= MAX_USERS; i++) + { + String userName = TEST_USER_PREFIX+i; + + NodeRef homeFolder = getHomeSpaceFolderNode(userName); + + AuthenticationUtil.setCurrentUser(userName); + + NodeRef childNodeRef = nodeService.getChildByName(homeFolder, ContentModel.ASSOC_CONTAINS, "a-"+userName+".txt"); + nodeService.deleteNode(childNodeRef); + + childNodeRef = nodeService.getChildByName(homeFolder, ContentModel.ASSOC_CONTAINS, "b-"+userName+".txt"); + nodeService.deleteNode(childNodeRef); + + AuthenticationUtil.setSystemUserAsCurrentUser(); + } + + System.out.println("Deleted content"); + + checkUsage(2L); + + userUsageTrackingComponent.execute(); // collapse usages + + System.out.println("Collapsed usages"); + + checkUsage(2L); + + userUsageTrackingComponent.setEnabled(false); + userUsageTrackingComponent.bootstrapInternal(); // false => clear + + System.out.println("Cleared usages"); + checkCleared(); + } + + private void checkCalculated(long factor) + { + for (int i = 1; i <= MAX_USERS; i++) + { + String userName = TEST_USER_PREFIX+i; + NodeRef personNodeRef = personService.getPerson(userName); + + // get user stored usage (not including deltas, if any) + assertEquals(userName, i*factor, ((Long)nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT)).longValue()); + } + } + + private void checkUsage(long factor) + { + for (int i = 1; i <= MAX_USERS; i++) + { + String userName = TEST_USER_PREFIX+i; + + // get user usage (including deltas, if any) + assertEquals(userName, i*factor, contentUsageService.getUserUsage(userName)); + } + } + + private void checkCleared() + { + for (int i = 1; i <= MAX_USERS; i++) + { + String userName = TEST_USER_PREFIX+i; + NodeRef personNodeRef = personService.getPerson(userName); + + assertNull(nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT)); + } + } + + private void deleteUsersAndContent() + { + long start = System.currentTimeMillis(); + + try + { + int count = 0; + int batch = 0; + //long batchStart = 0; + + int deleteCount = 0; + + for (int i = 1; i <= MAX_USERS; i++) + { + if (count == 0) + { + batch++; + + testTX = transactionService.getUserTransaction(); + testTX.begin(); + //batchStart = System.currentTimeMillis(); + } + + count++; + String userName = TEST_USER_PREFIX+i; + + + if (authenticationService.authenticationExists(userName)) + { + NodeRef homeFolder = getHomeSpaceFolderNode(userName); + nodeService.deleteNode(homeFolder); + + authenticationService.deleteAuthentication(userName); + personService.deletePerson(userName); + deleteCount++; + } + + if ((count == BATCH_SIZE) || (i == MAX_USERS)) + { + testTX.commit(); + count = 0; + + //System.out.println("Batch "+batch+" took "+((System.currentTimeMillis()-batchStart)/1000)+" secs"); + } + } + + System.out.println("Total: "+deleteCount+" users deleted in "+((System.currentTimeMillis()-start)/1000)+" secs"); + } + catch (Throwable t) + { + t.printStackTrace(); + + try { testTX.rollback(); } catch (Exception e) { e.printStackTrace(); } + } + } + + private NodeRef getHomeSpaceFolderNode(String userName) + { + return (NodeRef)this.nodeService.getProperty(personService.getPerson(userName), ContentModel.PROP_HOMEFOLDER); + } + + private NodeRef addTextContent(NodeRef spaceRef, String fileName, String textData) + { + Map contentProps = new HashMap(); + contentProps.put(ContentModel.PROP_NAME, fileName); + + ChildAssociationRef association = nodeService.createNode(spaceRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, fileName), + ContentModel.TYPE_CONTENT, + contentProps); + + NodeRef content = association.getChildRef(); + + // add titled aspect (for Web Client display) + Map titledProps = new HashMap(); + titledProps.put(ContentModel.PROP_TITLE, fileName); + titledProps.put(ContentModel.PROP_DESCRIPTION, fileName); + this.nodeService.addAspect(content, ContentModel.ASPECT_TITLED, titledProps); + + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); + + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.setEncoding("UTF-8"); + + writer.putContent(textData); + + return content; + } +}