mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Merged THOR1_SPRINTS to HEAD
Performance improvement: prevent unnecessary 304 revalidation requests for thumbnails in detailed view of My-Documents and Recently Modified Documents dashlets Fixed bean config problem (caused by r34662) Fix build break Refactored revalidation code to remove previously added WebScripts that are now surplus to requirements Performance improvement: prevent unnecessary 304 revalidation for avatars on site colleagues dashlet Performance improvement: prevent unnecessary 304 revalidation for avatars on following/follwers pages Performance improvement: prevent unnecessary 304 revalidation for avatars in activity feeds Performance improvement: prevent unecessary 304 revalidation for user avatar thumbnails in header WebScript Prevent 304 revalidations for unchanged thumbnails in document library, web preview and search git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@34698 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1355,6 +1355,17 @@
|
||||
</associations>
|
||||
</aspect>
|
||||
|
||||
<aspect name="cm:thumbnailModification">
|
||||
<title>Thumbnail Modification Data</title>
|
||||
<properties>
|
||||
<property name="cm:lastThumbnailModification">
|
||||
<title>Last thumbnail modifcation data</title>
|
||||
<type>d:text</type>
|
||||
<multiple>true</multiple>
|
||||
</property>
|
||||
</properties>
|
||||
</aspect>
|
||||
|
||||
<!-- -->
|
||||
<!-- EXIF -->
|
||||
<!-- -->
|
||||
|
@@ -19,6 +19,8 @@
|
||||
<property name="tenantService" ref="tenantService"/>
|
||||
<property name="siteService" ref="siteService"/>
|
||||
<property name="activityPostService" ref="activityPostService"/>
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="personService" ref="PersonService"/>
|
||||
<property name="userNamesAreCaseSensitive" value="${user.name.caseSensitive}"/>
|
||||
<property name="maxFeedItems" value="${activities.feed.max.size}"/>
|
||||
</bean>
|
||||
|
@@ -20,10 +20,14 @@ package org.alfresco.repo.activities;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.activities.feed.cleanup.FeedCleaner;
|
||||
import org.alfresco.repo.domain.activities.ActivityFeedDAO;
|
||||
import org.alfresco.repo.domain.activities.ActivityFeedEntity;
|
||||
@@ -35,11 +39,16 @@ import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.service.cmr.activities.ActivityPostService;
|
||||
import org.alfresco.service.cmr.activities.ActivityService;
|
||||
import org.alfresco.service.cmr.activities.FeedControl;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AuthorityService;
|
||||
import org.alfresco.service.cmr.security.PersonService;
|
||||
import org.alfresco.service.cmr.site.SiteInfo;
|
||||
import org.alfresco.service.cmr.site.SiteService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.json.JSONException;
|
||||
@@ -62,6 +71,8 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean
|
||||
private TenantService tenantService;
|
||||
private SiteService siteService;
|
||||
private ActivityPostService activityPostService;
|
||||
private PersonService personService;
|
||||
private NodeService nodeService;
|
||||
|
||||
private int maxFeedItems = 100;
|
||||
|
||||
@@ -112,6 +123,16 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean
|
||||
this.activityPostService = activityPostService;
|
||||
}
|
||||
|
||||
public void setPersonService(PersonService personService)
|
||||
{
|
||||
this.personService = personService;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
|
||||
/*(non-Javadoc)
|
||||
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
|
||||
@@ -254,6 +275,12 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean
|
||||
|
||||
List<ActivityFeedEntity> activityFeeds = feedDAO.selectUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers, minFeedId, maxFeedItems);
|
||||
|
||||
// Create a local cache just for this method to map IDs of users to their avatar NodeRef. This
|
||||
// is local to the method because we only want to cache per request - there is not point in keeping
|
||||
// an instance cache because the data will become stale if a user changes their avatar.
|
||||
Map<String, NodeRef> userIdToAvatarNodeRefCache = new HashMap<String, NodeRef>();
|
||||
|
||||
|
||||
for (ActivityFeedEntity activityFeed : activityFeeds)
|
||||
{
|
||||
if (actvityFilter != null && !actvityFilter.contains(activityFeed.getActivityType())) {
|
||||
@@ -264,6 +291,36 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean
|
||||
continue;
|
||||
}
|
||||
|
||||
// In order to prevent unnecessary 304 revalidations on user avatars in the activity stream the
|
||||
// activity posting user avatars will be retrieved and added to the activity feed. This will enable
|
||||
// avatars to be requested using the unique nodeRef which can be safely cached by the browser and
|
||||
// improve performance...
|
||||
if (userIdToAvatarNodeRefCache.containsKey(activityFeed.getPostUserId()))
|
||||
{
|
||||
// If we've previously cached the users avatar, or if we've determine that the user doesn't
|
||||
// have an avatar then use the cached data.
|
||||
activityFeed.setPostUserAvatarNodeRef(userIdToAvatarNodeRefCache.get(activityFeed.getPostUserId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the avatar for the user id, set it in the activity feed and update the cache
|
||||
NodeRef avatarNodeRef = null;
|
||||
NodeRef postPerson = this.personService.getPerson(activityFeed.getPostUserId());
|
||||
List<AssociationRef> assocRefs = this.nodeService.getTargetAssocs(postPerson, ContentModel.ASSOC_AVATAR);
|
||||
if (!assocRefs.isEmpty())
|
||||
{
|
||||
avatarNodeRef = assocRefs.get(0).getTargetRef();
|
||||
activityFeed.setPostUserAvatarNodeRef(avatarNodeRef);
|
||||
}
|
||||
else
|
||||
{
|
||||
activityFeed.setPostUserAvatarNodeRef(null);
|
||||
}
|
||||
|
||||
// Update the cache (setting null if there is no avatar for the user)...
|
||||
userIdToAvatarNodeRefCache.put(activityFeed.getPostUserId(), avatarNodeRef);
|
||||
}
|
||||
|
||||
activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork()));
|
||||
result.add(activityFeed);
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.util.JSONtoFmModel;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
@@ -37,6 +38,7 @@ public class ActivityFeedEntity
|
||||
public static final String KEY_ACTIVITY_FEED_ID = "id";
|
||||
public static final String KEY_ACTIVITY_FEED_POST_DATE = "postDate";
|
||||
public static final String KEY_ACTIVITY_FEED_POST_USERID = "postUserId";
|
||||
public static final String KEY_ACTIVITY_FEED_POST_USER_AVATAR_NODE = "postUserAvatar";
|
||||
public static final String KEY_ACTIVITY_FEED_USERID = "feedUserId";
|
||||
public static final String KEY_ACTIVITY_FEED_SITE = "siteNetwork";
|
||||
public static final String KEY_ACTIVITY_FEED_TYPE = "activityType";
|
||||
@@ -49,6 +51,7 @@ public class ActivityFeedEntity
|
||||
private String activitySummaryFormat;
|
||||
private String feedUserId;
|
||||
private String postUserId;
|
||||
private NodeRef postUserAvatarNodeRef;
|
||||
private String siteNetwork;
|
||||
private String appTool;
|
||||
private Date postDate;
|
||||
@@ -155,6 +158,16 @@ public class ActivityFeedEntity
|
||||
this.feedDate = feedDate;
|
||||
}
|
||||
|
||||
public NodeRef getPostUserAvatarNodeRef()
|
||||
{
|
||||
return postUserAvatarNodeRef;
|
||||
}
|
||||
|
||||
public void setPostUserAvatarNodeRef(NodeRef postUserAvatarNodeRef)
|
||||
{
|
||||
this.postUserAvatarNodeRef = postUserAvatarNodeRef;
|
||||
}
|
||||
|
||||
public String getAppTool()
|
||||
{
|
||||
return appTool;
|
||||
@@ -172,6 +185,10 @@ public class ActivityFeedEntity
|
||||
jo.put(KEY_ACTIVITY_FEED_ID, id);
|
||||
|
||||
jo.put(KEY_ACTIVITY_FEED_POST_USERID, postUserId);
|
||||
if (postUserAvatarNodeRef != null)
|
||||
{
|
||||
jo.put(KEY_ACTIVITY_FEED_POST_USER_AVATAR_NODE, postUserAvatarNodeRef.toString());
|
||||
}
|
||||
jo.put(KEY_ACTIVITY_FEED_POST_DATE, ISO8601DateFormat.format(postDate));
|
||||
|
||||
if (getFeedUserId() != null) { jo.put(KEY_ACTIVITY_FEED_USERID, getFeedUserId()); } // eg. site feed
|
||||
|
@@ -7,6 +7,7 @@ import java.io.Serializable;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -301,6 +302,10 @@ public class JSONConversionComponent
|
||||
dateObj.put("iso8601", JSONObject.escape(ISO8601DateFormat.format((Date)value)));
|
||||
propertiesJSON.put(key, dateObj);
|
||||
}
|
||||
else if (value instanceof List)
|
||||
{
|
||||
propertiesJSON.put(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
propertiesJSON.put(key, value.toString());
|
||||
|
@@ -189,6 +189,9 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
||||
// When a thumbnail succeeds, we must delete any existing thumbnail failure nodes.
|
||||
String thumbnailName = (String) nodeService.getProperty(childAssoc.getChildRef(), ContentModel.PROP_NAME);
|
||||
|
||||
// Update the parent node with the thumbnail update...
|
||||
addThumbnailModificationData(childAssoc.getChildRef(), thumbnailName);
|
||||
|
||||
// In fact there should only be zero or one such failedThumbnails
|
||||
Map<String, FailedThumbnailInfo> failures = getFailedThumbnails(childAssoc.getParentRef());
|
||||
FailedThumbnailInfo existingFailedThumbnail = failures.get(thumbnailName);
|
||||
@@ -610,4 +613,78 @@ public class ThumbnailServiceImpl implements ThumbnailService,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Updates the parent of the supplied {@link NodeRef} to ensure that it has the "cm:thumbnailModification" aspect
|
||||
* and sets the last modification data for it.</p>
|
||||
* @param nodeRef A {@link NodeRef} representing a thumbnail to provide last modification data for.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addThumbnailModificationData(NodeRef nodeRef, String thumbnailName)
|
||||
{
|
||||
if (nodeService.exists(nodeRef))
|
||||
{
|
||||
if (thumbnailName != null && !nodeRef.toString().endsWith(thumbnailName))
|
||||
{
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
if (modified != null)
|
||||
{
|
||||
// Get the last modified value as a timestamp...
|
||||
Long timestamp = modified.getTime();
|
||||
|
||||
// Create the value we want to set...
|
||||
String lastModifiedValue = thumbnailName + ":" + timestamp;
|
||||
|
||||
// Get the parent node (there should be only one) and apply the aspect and
|
||||
// set the property to indicate which thumbnail the checksum refers to...
|
||||
for (ChildAssociationRef parent: nodeService.getParentAssocs(nodeRef))
|
||||
{
|
||||
List<String> thumbnailMods = null;
|
||||
|
||||
NodeRef parentNode = parent.getParentRef();
|
||||
if (nodeService.hasAspect(parentNode, ContentModel.ASPECT_THUMBNAIL_MODIFICATION))
|
||||
{
|
||||
// The node already has the aspect, check to see if the current thumbnail modification exists...
|
||||
thumbnailMods = (List<String>) nodeService.getProperty(parentNode, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA);
|
||||
|
||||
// If we have previously set last modified thumbnail data then it will exist as part of the multi-value
|
||||
// property. The value will consist of the "cm:thumbnailName" value delimited with a ":" and then the
|
||||
// timestamp. We need to find the appropriate entry in the multivalue property and then update it
|
||||
String target = null;
|
||||
for (String currThumbnailMod: thumbnailMods)
|
||||
{
|
||||
if (currThumbnailMod.startsWith(thumbnailName))
|
||||
{
|
||||
target = currThumbnailMod;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the previous value
|
||||
if (target != null)
|
||||
{
|
||||
thumbnailMods.remove(target);
|
||||
}
|
||||
|
||||
// Add the timestamp...
|
||||
thumbnailMods.add(lastModifiedValue);
|
||||
|
||||
// Set the property...
|
||||
nodeService.setProperty(parentNode, ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA, (Serializable) thumbnailMods);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the aspect has not previously been added then we'll need to set it now...
|
||||
thumbnailMods = new ArrayList<String>();
|
||||
thumbnailMods.add(lastModifiedValue);
|
||||
|
||||
// Add the aspect with the new property...
|
||||
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
|
||||
properties.put(ContentModel.PROP_LAST_THUMBNAIL_MODIFICATION_DATA, (Serializable) thumbnailMods);
|
||||
nodeService.addAspect(parentNode, ContentModel.ASPECT_THUMBNAIL_MODIFICATION, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user