diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java index d96a4a96bc..fe7d97832e 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java @@ -1,541 +1,541 @@ -/* - * Copyright (C) 2005-2009 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.activities; - -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.activities.feed.FeedGenerator; -import org.alfresco.repo.domain.activities.ActivityFeedDAO; -import org.alfresco.repo.domain.activities.ActivityFeedEntity; -import org.alfresco.repo.domain.activities.ActivityPostDAO; -import org.alfresco.repo.domain.activities.ActivityPostEntity; -import org.alfresco.repo.domain.activities.FeedControlDAO; -import org.alfresco.repo.domain.activities.FeedControlEntity; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.activities.ActivityService; -import org.alfresco.service.cmr.activities.FeedControl; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.security.AuthorityService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.ParameterCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; - -/** - * Activity Service Implementation - * - * @author janv - */ -public class ActivityServiceImpl implements ActivityService -{ - private static final Log logger = LogFactory.getLog(ActivityServiceImpl.class); - - private static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id - private static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network - private static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type - private static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data - private static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool - - private ActivityPostDAO postDAO; - private ActivityFeedDAO feedDAO; - private FeedControlDAO feedControlDAO; - private AuthorityService authorityService; - private FeedGenerator feedGenerator; - - private TenantService tenantService; - - private int maxFeedItems = 100; - - private boolean userNamesAreCaseSensitive = false; - - public void setMaxFeedItems(int maxFeedItems) - { - this.maxFeedItems = maxFeedItems; - } - - public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) - { - this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; - } - - public void setPostDAO(ActivityPostDAO postDAO) - { - this.postDAO = postDAO; - } - - public void setFeedDAO(ActivityFeedDAO feedDAO) - { - this.feedDAO = feedDAO; - } - - public void setFeedControlDAO(FeedControlDAO feedControlDAO) - { - this.feedControlDAO = feedControlDAO; - } - - public void setAuthorityService(AuthorityService authorityService) - { - this.authorityService = authorityService; - } - - public void setFeedGenerator(FeedGenerator feedGenerator) - { - this.feedGenerator = feedGenerator; - } - - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, java.lang.String) - */ - public void postActivity(String activityType, String siteId, String appTool, String activityData) - { - postActivity(activityType, siteId, appTool, activityData, ActivityPostEntity.STATUS.PENDING); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef) - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef) - { - ParameterCheck.mandatory("nodeRef", nodeRef); - - StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}"); - - postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name) - { - ParameterCheck.mandatory("nodeRef", nodeRef); - - StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") - .append("\"name\":\"").append(name).append("\"") - .append("}"); - - postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef) - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) - { - // primarily for delete node activities - eg. delete document, delete folder - - ParameterCheck.mandatory("nodeRef", nodeRef); - ParameterCheck.mandatory("typeQName", typeQName); - ParameterCheck.mandatory("parentNodeRef", parentNodeRef); - - StringBuffer sb = new StringBuffer(); - sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") - .append("\"name\":\"").append(name).append("\"").append(",") - .append("\"typeQName\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!! - .append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"") - .append("}"); - - postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); - } - - private void postActivity(String activityType, String siteId, String appTool, String activityData, ActivityPostEntity.STATUS status) - { - String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); - - try - { - // optional - default to empty string - if (siteId == null) - { - siteId = ""; - } - else if (siteId.length() > MAX_LEN_SITE_ID) - { - throw new AlfrescoRuntimeException("Invalid siteId - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteId); - } - - // optional - default to empty string - if (appTool == null) - { - appTool = ""; - } - else if (appTool.length() > MAX_LEN_APP_TOOL_ID) - { - throw new AlfrescoRuntimeException("Invalid app tool - exceeds " + MAX_LEN_APP_TOOL_ID + " chars: " + appTool); - } - - // required - ParameterCheck.mandatoryString("activityType", activityType); - - if (activityType.length() > MAX_LEN_ACTIVITY_TYPE) - { - throw new AlfrescoRuntimeException("Invalid activity type - exceeds " + MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType); - } - - // optional - default to empty string - if (activityData == null) - { - activityData = ""; - } - else if (activityType.length() > MAX_LEN_ACTIVITY_DATA) - { - throw new AlfrescoRuntimeException("Invalid activity data - exceeds " + MAX_LEN_ACTIVITY_DATA + " chars: " + activityData); - } - - // required - ParameterCheck.mandatoryString("currentUser", currentUser); - - if (currentUser.length() > MAX_LEN_USER_ID) - { - throw new AlfrescoRuntimeException("Invalid user - exceeds " + MAX_LEN_USER_ID + " chars: " + currentUser); - } - else if ((! currentUser.equals(AuthenticationUtil.SYSTEM_USER_NAME)) && (! userNamesAreCaseSensitive)) - { - // user names are not case-sensitive - currentUser = currentUser.toLowerCase(); - } - } - catch (AlfrescoRuntimeException e) - { - // log error and throw exception - logger.error(e); - throw e; - } - - try - { - Date postDate = new Date(); - ActivityPostEntity activityPost = new ActivityPostEntity(); - activityPost.setUserId(currentUser); - - activityPost.setSiteNetwork(tenantService.getName(siteId)); - - activityPost.setAppTool(appTool); - activityPost.setActivityData(activityData); - activityPost.setActivityType(activityType); - activityPost.setPostDate(postDate); - activityPost.setStatus(status.toString()); - activityPost.setLastModified(postDate); - - // hash the userid to generate a job task node - int nodeCount = feedGenerator.getEstimatedGridSize(); - int userHashCode = currentUser.hashCode(); - int nodeHash = (userHashCode % nodeCount) + 1; - - activityPost.setJobTaskNode(nodeHash); - - try - { - long postId = postDAO.insertPost(activityPost); - - if (logger.isDebugEnabled()) - { - activityPost.setId(postId); - logger.debug("Posted: " + activityPost); - } - } - catch (SQLException e) - { - throw new AlfrescoRuntimeException("Failed to post activity: " + e, e); - } - catch (Throwable t) - { - throw new AlfrescoRuntimeException("Failed to post activity: " + t, t); - } - } - catch (AlfrescoRuntimeException e) - { - // log error, subsume exception (for post activity) - logger.error(e); - } - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String) - */ - public List getUserFeedEntries(String feedUserId, String format, String siteId) - { - return getUserFeedEntries(feedUserId, format, siteId, false, false); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String, boolean, boolean) - */ - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers) - { - // NOTE: siteId is optional - ParameterCheck.mandatoryString("feedUserId", feedUserId); - ParameterCheck.mandatoryString("format", format); - - List activityFeedEntries = new ArrayList(); - - if (! userNamesAreCaseSensitive) - { - feedUserId = feedUserId.toLowerCase(); - } - - try - { - List activityFeeds = null; - if (siteId != null) - { - siteId = tenantService.getName(siteId); - } - - activityFeeds = feedDAO.selectUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers); - - int count = 0; - for (ActivityFeedEntity activityFeed : activityFeeds) - { - count++; - if (count > maxFeedItems) - { - break; - } - - activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); - activityFeedEntries.add(activityFeed.getJSONString()); - } - } - catch (SQLException se) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get user feed entries: " + se.getMessage()); - logger.error(are); - throw are; - } - catch (JSONException je) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get user feed entries: " + je.getMessage()); - logger.error(are); - throw are; - } - - return activityFeedEntries; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#getSiteFeedEntries(java.lang.String, java.lang.String) - */ - public List getSiteFeedEntries(String siteId, String format) - { - ParameterCheck.mandatoryString("siteId", siteId); - ParameterCheck.mandatoryString("format", format); - - List activityFeedEntries = new ArrayList(); - - try - { - siteId = tenantService.getName(siteId); - - List activityFeeds = feedDAO.selectSiteFeedEntries(siteId, format); - - int count = 0; - for (ActivityFeedEntity activityFeed : activityFeeds) - { - count++; - if (count > maxFeedItems) - { - break; - } - - activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); - activityFeedEntries.add(activityFeed.getJSONString()); - } - } - catch (SQLException se) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get site feed entries: " + se.getMessage()); - logger.error(are); - throw are; - } - catch (JSONException je) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get site feed entries: " + je.getMessage()); - logger.error(are); - throw are; - } - - return activityFeedEntries; - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#setFeedControl(org.alfresco.service.cmr.activities.FeedControl) - */ - public void setFeedControl(FeedControl feedControl) - { - ParameterCheck.mandatory("feedControl", feedControl); - - String userId = AuthenticationUtil.getFullyAuthenticatedUser(); - if (! userNamesAreCaseSensitive) - { - userId = userId.toLowerCase(); - } - - try - { - if (! existsFeedControl(feedControl)) - { - feedControlDAO.insertFeedControl(new FeedControlEntity(userId, feedControl)); - } - } - catch (SQLException e) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to set feed control: " + e, e); - logger.error(are); - throw are; - } - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls() - */ - public List getFeedControls() - { - String userId = AuthenticationUtil.getFullyAuthenticatedUser(); - return getFeedControlsImpl(userId); - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls(java.lang.String) - */ - public List getFeedControls(String userId) - { - ParameterCheck.mandatoryString("userId", userId); - String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); - - if ((currentUser == null) || ((! currentUser.equals(AuthenticationUtil.getSystemUserName())) && (! currentUser.equals(userId)) && (! authorityService.isAdminAuthority(currentUser)))) - { - throw new AlfrescoRuntimeException("Current user " + currentUser + " is not permitted to get feed controls for " + userId); - } - - return getFeedControlsImpl(userId); - } - - private List getFeedControlsImpl(String userId) - { - ParameterCheck.mandatoryString("userId", userId); - - if (! userNamesAreCaseSensitive) - { - userId = userId.toLowerCase(); - } - - try - { - List feedControlDaos = feedControlDAO.selectFeedControls(userId); - List feedControls = new ArrayList(feedControlDaos.size()); - for (FeedControlEntity feedControlDao : feedControlDaos) - { - feedControls.add(feedControlDao.getFeedControl()); - } - - return feedControls; - } - catch (SQLException e) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to get feed controls: " + e, e); - logger.error(are); - throw are; - } - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#deleteFeedControl(org.alfresco.service.cmr.activities.FeedControl) - */ - public void unsetFeedControl(FeedControl feedControl) - { - ParameterCheck.mandatory("feedControl", feedControl); - - String userId = AuthenticationUtil.getFullyAuthenticatedUser(); - if (! userNamesAreCaseSensitive) - { - userId = userId.toLowerCase(); - } - - try - { - feedControlDAO.deleteFeedControl(new FeedControlEntity(userId, feedControl)); - } - catch (SQLException e) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to unset feed control: " + e, e); - logger.error(are); - throw are; - } - } - - /* (non-Javadoc) - * @see org.alfresco.service.cmr.activities.ActivityService#existsFeedControl(java.lang.String, org.alfresco.service.cmr.activities.FeedControl) - */ - public boolean existsFeedControl(FeedControl feedControl) - { - ParameterCheck.mandatory("feedControl", feedControl); - - String userId = AuthenticationUtil.getFullyAuthenticatedUser(); - - if (! userNamesAreCaseSensitive) - { - userId = userId.toLowerCase(); - } - - try - { - long id = feedControlDAO.selectFeedControl(new FeedControlEntity(userId, feedControl)); - return (id != -1); - } - catch (SQLException e) - { - AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to query feed control: " + e, e); - logger.error(are); - throw are; - } - } - - private FeedControl getTenantFeedControl(FeedControl feedControl) - { - // TODO - return null; - } - - private FeedControl getBaseFeedControl(FeedControl feedControl) - { - // TODO - return null; - } -} +/* + * Copyright (C) 2005-2009 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.activities; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.activities.feed.FeedGenerator; +import org.alfresco.repo.domain.activities.ActivityFeedDAO; +import org.alfresco.repo.domain.activities.ActivityFeedEntity; +import org.alfresco.repo.domain.activities.ActivityPostDAO; +import org.alfresco.repo.domain.activities.ActivityPostEntity; +import org.alfresco.repo.domain.activities.FeedControlDAO; +import org.alfresco.repo.domain.activities.FeedControlEntity; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.activities.FeedControl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; + +/** + * Activity Service Implementation + * + * @author janv + */ +public class ActivityServiceImpl implements ActivityService +{ + private static final Log logger = LogFactory.getLog(ActivityServiceImpl.class); + + private static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id + private static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network + private static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type + private static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data + private static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool + + private ActivityPostDAO postDAO; + private ActivityFeedDAO feedDAO; + private FeedControlDAO feedControlDAO; + private AuthorityService authorityService; + private FeedGenerator feedGenerator; + + private TenantService tenantService; + + private int maxFeedItems = 100; + + private boolean userNamesAreCaseSensitive = false; + + public void setMaxFeedItems(int maxFeedItems) + { + this.maxFeedItems = maxFeedItems; + } + + public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + { + this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + } + + public void setPostDAO(ActivityPostDAO postDAO) + { + this.postDAO = postDAO; + } + + public void setFeedDAO(ActivityFeedDAO feedDAO) + { + this.feedDAO = feedDAO; + } + + public void setFeedControlDAO(FeedControlDAO feedControlDAO) + { + this.feedControlDAO = feedControlDAO; + } + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setFeedGenerator(FeedGenerator feedGenerator) + { + this.feedGenerator = feedGenerator; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, java.lang.String) + */ + public void postActivity(String activityType, String siteId, String appTool, String activityData) + { + postActivity(activityType, siteId, appTool, activityData, ActivityPostEntity.STATUS.PENDING); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef) + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + StringBuffer sb = new StringBuffer(); + sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}"); + + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String) + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name) + { + ParameterCheck.mandatory("nodeRef", nodeRef); + + StringBuffer sb = new StringBuffer(); + sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") + .append("\"name\":\"").append(name).append("\"") + .append("}"); + + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef) + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) + { + // primarily for delete node activities - eg. delete document, delete folder + + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("typeQName", typeQName); + ParameterCheck.mandatory("parentNodeRef", parentNodeRef); + + StringBuffer sb = new StringBuffer(); + sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",") + .append("\"name\":\"").append(name).append("\"").append(",") + .append("\"typeQName\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!! + .append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"") + .append("}"); + + postActivity(activityType, siteId, appTool, sb.toString(), ActivityPostEntity.STATUS.PENDING); + } + + private void postActivity(String activityType, String siteId, String appTool, String activityData, ActivityPostEntity.STATUS status) + { + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + + try + { + // optional - default to empty string + if (siteId == null) + { + siteId = ""; + } + else if (siteId.length() > MAX_LEN_SITE_ID) + { + throw new AlfrescoRuntimeException("Invalid siteId - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteId); + } + + // optional - default to empty string + if (appTool == null) + { + appTool = ""; + } + else if (appTool.length() > MAX_LEN_APP_TOOL_ID) + { + throw new AlfrescoRuntimeException("Invalid app tool - exceeds " + MAX_LEN_APP_TOOL_ID + " chars: " + appTool); + } + + // required + ParameterCheck.mandatoryString("activityType", activityType); + + if (activityType.length() > MAX_LEN_ACTIVITY_TYPE) + { + throw new AlfrescoRuntimeException("Invalid activity type - exceeds " + MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType); + } + + // optional - default to empty string + if (activityData == null) + { + activityData = ""; + } + else if (activityType.length() > MAX_LEN_ACTIVITY_DATA) + { + throw new AlfrescoRuntimeException("Invalid activity data - exceeds " + MAX_LEN_ACTIVITY_DATA + " chars: " + activityData); + } + + // required + ParameterCheck.mandatoryString("currentUser", currentUser); + + if (currentUser.length() > MAX_LEN_USER_ID) + { + throw new AlfrescoRuntimeException("Invalid user - exceeds " + MAX_LEN_USER_ID + " chars: " + currentUser); + } + else if ((! currentUser.equals(AuthenticationUtil.SYSTEM_USER_NAME)) && (! userNamesAreCaseSensitive)) + { + // user names are not case-sensitive + currentUser = currentUser.toLowerCase(); + } + } + catch (AlfrescoRuntimeException e) + { + // log error and throw exception + logger.error(e); + throw e; + } + + try + { + Date postDate = new Date(); + ActivityPostEntity activityPost = new ActivityPostEntity(); + activityPost.setUserId(currentUser); + + activityPost.setSiteNetwork(tenantService.getName(siteId)); + + activityPost.setAppTool(appTool); + activityPost.setActivityData(activityData); + activityPost.setActivityType(activityType); + activityPost.setPostDate(postDate); + activityPost.setStatus(status.toString()); + activityPost.setLastModified(postDate); + + // hash the userid to generate a job task node + int nodeCount = feedGenerator.getEstimatedGridSize(); + int userHashCode = currentUser.hashCode(); + int nodeHash = (userHashCode % nodeCount) + 1; + + activityPost.setJobTaskNode(nodeHash); + + try + { + long postId = postDAO.insertPost(activityPost); + + if (logger.isDebugEnabled()) + { + activityPost.setId(postId); + logger.debug("Posted: " + activityPost); + } + } + catch (SQLException e) + { + throw new AlfrescoRuntimeException("Failed to post activity: " + e, e); + } + catch (Throwable t) + { + throw new AlfrescoRuntimeException("Failed to post activity: " + t, t); + } + } + catch (AlfrescoRuntimeException e) + { + // log error, subsume exception (for post activity) + logger.error(e); + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String) + */ + public List getUserFeedEntries(String feedUserId, String format, String siteId) + { + return getUserFeedEntries(feedUserId, format, siteId, false, false); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String, boolean, boolean) + */ + public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers) + { + // NOTE: siteId is optional + ParameterCheck.mandatoryString("feedUserId", feedUserId); + ParameterCheck.mandatoryString("format", format); + + List activityFeedEntries = new ArrayList(); + + if (! userNamesAreCaseSensitive) + { + feedUserId = feedUserId.toLowerCase(); + } + + try + { + List activityFeeds = null; + if (siteId != null) + { + siteId = tenantService.getName(siteId); + } + + activityFeeds = feedDAO.selectUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers); + + int count = 0; + for (ActivityFeedEntity activityFeed : activityFeeds) + { + count++; + if (count > maxFeedItems) + { + break; + } + + activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); + activityFeedEntries.add(activityFeed.getJSONString()); + } + } + catch (SQLException se) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get user feed entries: " + se.getMessage()); + logger.error(are); + throw are; + } + catch (JSONException je) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get user feed entries: " + je.getMessage()); + logger.error(are); + throw are; + } + + return activityFeedEntries; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#getSiteFeedEntries(java.lang.String, java.lang.String) + */ + public List getSiteFeedEntries(String siteId, String format) + { + ParameterCheck.mandatoryString("siteId", siteId); + ParameterCheck.mandatoryString("format", format); + + List activityFeedEntries = new ArrayList(); + + try + { + siteId = tenantService.getName(siteId); + + List activityFeeds = feedDAO.selectSiteFeedEntries(siteId, format); + + int count = 0; + for (ActivityFeedEntity activityFeed : activityFeeds) + { + count++; + if (count > maxFeedItems) + { + break; + } + + activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); + activityFeedEntries.add(activityFeed.getJSONString()); + } + } + catch (SQLException se) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get site feed entries: " + se.getMessage()); + logger.error(are); + throw are; + } + catch (JSONException je) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Unable to get site feed entries: " + je.getMessage()); + logger.error(are); + throw are; + } + + return activityFeedEntries; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#setFeedControl(org.alfresco.service.cmr.activities.FeedControl) + */ + public void setFeedControl(FeedControl feedControl) + { + ParameterCheck.mandatory("feedControl", feedControl); + + String userId = AuthenticationUtil.getFullyAuthenticatedUser(); + if (! userNamesAreCaseSensitive) + { + userId = userId.toLowerCase(); + } + + try + { + if (! existsFeedControl(feedControl)) + { + feedControlDAO.insertFeedControl(new FeedControlEntity(userId, feedControl)); + } + } + catch (SQLException e) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to set feed control: " + e, e); + logger.error(are); + throw are; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls() + */ + public List getFeedControls() + { + String userId = AuthenticationUtil.getFullyAuthenticatedUser(); + return getFeedControlsImpl(userId); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls(java.lang.String) + */ + public List getFeedControls(String userId) + { + ParameterCheck.mandatoryString("userId", userId); + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + + if ((currentUser == null) || ((! currentUser.equals(AuthenticationUtil.getSystemUserName())) && (! currentUser.equals(userId)) && (! authorityService.isAdminAuthority(currentUser)))) + { + throw new AlfrescoRuntimeException("Current user " + currentUser + " is not permitted to get feed controls for " + userId); + } + + return getFeedControlsImpl(userId); + } + + private List getFeedControlsImpl(String userId) + { + ParameterCheck.mandatoryString("userId", userId); + + if (! userNamesAreCaseSensitive) + { + userId = userId.toLowerCase(); + } + + try + { + List feedControlDaos = feedControlDAO.selectFeedControls(userId); + List feedControls = new ArrayList(feedControlDaos.size()); + for (FeedControlEntity feedControlDao : feedControlDaos) + { + feedControls.add(feedControlDao.getFeedControl()); + } + + return feedControls; + } + catch (SQLException e) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to get feed controls: " + e, e); + logger.error(are); + throw are; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#deleteFeedControl(org.alfresco.service.cmr.activities.FeedControl) + */ + public void unsetFeedControl(FeedControl feedControl) + { + ParameterCheck.mandatory("feedControl", feedControl); + + String userId = AuthenticationUtil.getFullyAuthenticatedUser(); + if (! userNamesAreCaseSensitive) + { + userId = userId.toLowerCase(); + } + + try + { + feedControlDAO.deleteFeedControl(new FeedControlEntity(userId, feedControl)); + } + catch (SQLException e) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to unset feed control: " + e, e); + logger.error(are); + throw are; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.activities.ActivityService#existsFeedControl(java.lang.String, org.alfresco.service.cmr.activities.FeedControl) + */ + public boolean existsFeedControl(FeedControl feedControl) + { + ParameterCheck.mandatory("feedControl", feedControl); + + String userId = AuthenticationUtil.getFullyAuthenticatedUser(); + + if (! userNamesAreCaseSensitive) + { + userId = userId.toLowerCase(); + } + + try + { + long id = feedControlDAO.selectFeedControl(new FeedControlEntity(userId, feedControl)); + return (id != -1); + } + catch (SQLException e) + { + AlfrescoRuntimeException are = new AlfrescoRuntimeException("Failed to query feed control: " + e, e); + logger.error(are); + throw are; + } + } + + private FeedControl getTenantFeedControl(FeedControl feedControl) + { + // TODO + return null; + } + + private FeedControl getBaseFeedControl(FeedControl feedControl) + { + // TODO + return null; + } +} diff --git a/source/java/org/alfresco/repo/activities/ActivityType.java b/source/java/org/alfresco/repo/activities/ActivityType.java index 1ec54fd4b6..1e35afb3c2 100644 --- a/source/java/org/alfresco/repo/activities/ActivityType.java +++ b/source/java/org/alfresco/repo/activities/ActivityType.java @@ -1,38 +1,38 @@ -/* - * 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.activities; - -public interface ActivityType -{ - // pre-defined alfresco activity types - - // generic fallback (if specific template is missing) - public final String GENERIC_FALLBACK = "org.alfresco.generic"; - - // site membership - public final String SITE_USER_JOINED = "org.alfresco.site.user-joined"; - public final String SITE_USER_REMOVED = "org.alfresco.site.user-left"; - public final String SITE_USER_ROLE_UPDATE = "org.alfresco.site.user-role-changed"; -} +/* + * Copyright (C) 2005-2009 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.activities; + +public interface ActivityType +{ + // pre-defined alfresco activity types + + // generic fallback (if specific template is missing) + public final String GENERIC_FALLBACK = "org.alfresco.generic"; + + // site membership + public final String SITE_USER_JOINED = "org.alfresco.site.user-joined"; + public final String SITE_USER_REMOVED = "org.alfresco.site.user-left"; + public final String SITE_USER_ROLE_UPDATE = "org.alfresco.site.user-role-changed"; +} diff --git a/source/java/org/alfresco/repo/activities/feed/FeedGridJob.java b/source/java/org/alfresco/repo/activities/feed/FeedGridJob.java index ac6c31d99e..c7a0f9ac5f 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedGridJob.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedGridJob.java @@ -1,39 +1,39 @@ -/* - * 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.activities.feed; - -import java.io.Serializable; - -/** - * Interface for feed grid job - */ -public interface FeedGridJob -{ - public void setArgument(JobSettings arg); - - public JobSettings getArgument(); - - public Serializable execute() throws Exception; -} +/* + * Copyright (C) 2005-2009 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.activities.feed; + +import java.io.Serializable; + +/** + * Interface for feed grid job + */ +public interface FeedGridJob +{ + public void setArgument(JobSettings arg); + + public JobSettings getArgument(); + + public Serializable execute() throws Exception; +} diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java index 5beb021590..09de6af8ce 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java @@ -1,661 +1,661 @@ -/* - * 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.activities.feed; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import org.alfresco.repo.domain.activities.ActivityFeedEntity; -import org.alfresco.repo.domain.activities.ActivityPostEntity; -import org.alfresco.repo.domain.activities.FeedControlEntity; -import org.alfresco.repo.template.ISO8601DateFormatMethod; -import org.alfresco.util.Base64; -import org.alfresco.util.JSONtoFmModel; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import freemarker.cache.URLTemplateLoader; -import freemarker.template.Configuration; -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.Template; -import freemarker.template.TemplateException; - -/** - * Responsible for processing the individual task - */ -public abstract class FeedTaskProcessor -{ - private static final Log logger = LogFactory.getLog(FeedTaskProcessor.class); - - private static final String defaultFormat = "text"; - private static final String[] formats = {"atomentry", "rss", "json", "html", "xml", defaultFormat}; - - private static final String URL_SERVICE_SITES = "/api/sites"; - private static final String URL_MEMBERSHIPS = "/memberships"; - - private static final String URL_SERVICE_TEMPLATES = "/api/activities/templates"; - private static final String URL_SERVICE_TEMPLATE = "/api/activities/template"; - - - public void process(int jobTaskNode, long minSeq, long maxSeq, RepoCtx ctx) throws Exception - { - if (logger.isDebugEnabled()) - { - logger.debug(">>> Process: jobTaskNode '" + jobTaskNode + "' from seq '" + minSeq + "' to seq '" + maxSeq + "' on this node from grid job."); - } - - ActivityPostEntity selector = new ActivityPostEntity(); - selector.setJobTaskNode(jobTaskNode); - selector.setMinId(minSeq); - selector.setMaxId(maxSeq); - selector.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); - - String ticket = ctx.getTicket(); - - List activityPosts = null; - int totalGenerated = 0; - - try - { - activityPosts = selectPosts(selector); - - if (logger.isDebugEnabled()) { logger.debug(">>> Process: " + activityPosts.size() + " activity posts"); } - - Configuration cfg = getFreemarkerConfiguration(ctx); - - Map> activityTemplates = new HashMap>(10); - - Map> siteConnectedUsers = new TreeMap>(); - - Map templateCache = new TreeMap(); - - // for each activity post ... - for (ActivityPostEntity activityPost : activityPosts) - { - String postingUserId = activityPost.getUserId(); - String activityType = activityPost.getActivityType(); - - // eg. org.alfresco.folder.added -> added - String baseActivityType = getBaseActivityType(activityType); - - List fmTemplates = activityTemplates.get(baseActivityType); - - if (fmTemplates == null) - { - // eg. org.alfresco.folder.added -> /org/alfresco/folder/added (note: the leading slash) - String templateSubPath = getTemplateSubPath(activityType); - - fmTemplates = new ArrayList(0); - while (true) - { - int idx = templateSubPath.lastIndexOf("/"); - if (idx != -1) - { - templateSubPath = templateSubPath.substring(0, idx); - Map> templates = null; - try - { - // Repository callback to get list of FreeMarker templates for given activity type - templates = getActivityTypeTemplates(ctx.getRepoEndPoint(), ticket, templateSubPath+"/"); - } - catch (FileNotFoundException fnfe) - { - // ignore - path does not exist - } - if (templates != null) - { - if (templates.get(baseActivityType) != null) - { - // add templates, if format not already included - addMissingFormats(activityType, fmTemplates, templates.get(baseActivityType)); - } - - // special fallback case - if (templates.get("generic") != null) - { - // add templates, if format not already included - addMissingFormats(activityType, fmTemplates, templates.get("generic")); - } - } - } - else - { - break; - } - } - - activityTemplates.put(baseActivityType, fmTemplates); - } - - if (fmTemplates.size() == 0) - { - logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType ); - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - continue; - } - - Map model = null; - try - { - model = JSONtoFmModel.convertJSONObjectToMap(activityPost.getActivityData()); - } - catch(JSONException je) - { - logger.error(">>> Skipping activity post " + activityPost.getId() + " due to invalid activity data: " + je); - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - continue; - } - - model.put("activityType", activityPost.getActivityType()); - model.put("siteNetwork", activityPost.getSiteNetwork()); - model.put("userId", activityPost.getUserId()); - model.put("id", activityPost.getId()); - model.put("date", activityPost.getPostDate()); // post date rather than time that feed is generated - model.put("xmldate", new ISO8601DateFormatMethod()); - model.put("repoEndPoint", ctx.getRepoEndPoint()); - - // Get the members of this site - String thisSite = activityPost.getSiteNetwork(); - - // Save hammering the repository by reusing cached site members - Set connectedUsers = siteConnectedUsers.get(thisSite); - if (connectedUsers == null) - { - if ((thisSite == null) || (thisSite.length() == 0)) - { - connectedUsers = Collections.singleton(""); // add empty posting userid - to represent site feed ! - } - else - { - try - { - // Repository callback to get site members - connectedUsers = getSiteMembers(ctx, thisSite); - connectedUsers.add(""); // add empty posting userid - to represent site feed ! - - // Cache them for future use in this same invocation - siteConnectedUsers.put(thisSite, connectedUsers); - } - catch(Exception e) - { - logger.error(">>> Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e); - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - continue; - } - } - } - - try - { - startTransaction(); - - if (logger.isDebugEnabled()) - { - logger.debug(">>> Process: " + connectedUsers.size() + " candidate connections for activity post " + activityPost.getId()); - } - - int excludedConnections = 0; - - for (String connectedUser : connectedUsers) - { - List feedControls = null; - if (! connectedUser.equals("")) - { - feedControls = getFeedControls(connectedUser); - } - - // filter based on opt-out feed controls (if any) - if (! acceptActivity(activityPost, feedControls)) - { - excludedConnections++; - } - else - { - for (String fmTemplate : fmTemplates) - { - // determine format - based on template naming convention - String formatFound = null; - for (String format : formats) - { - if (fmTemplate.contains("."+format+".")) - { - formatFound = format; - break; - } - } - - if (formatFound == null) - { - formatFound = defaultFormat; - logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'"); - } - - ActivityFeedEntity feed = new ActivityFeedEntity(); - - // Generate activity feed summary - feed.setFeedUserId(connectedUser); - feed.setPostUserId(postingUserId); - feed.setActivityType(activityType); - - if (formatFound.equals("json")) - { - // allows generic JSON template to simply pass straight through - model.put("activityData", activityPost.getActivityData()); - } - - String activitySummary = processFreemarker(templateCache, fmTemplate, cfg, model); - if (! activitySummary.equals("")) - { - feed.setActivitySummary(activitySummary); - feed.setActivitySummaryFormat(formatFound); - feed.setSiteNetwork(thisSite); - feed.setAppTool(activityPost.getAppTool()); - feed.setPostDate(activityPost.getPostDate()); - feed.setPostId(activityPost.getId()); - feed.setFeedDate(new Date()); - - // Insert activity feed - insertFeedEntry(feed); // ignore returned feedId - - totalGenerated++; - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Empty template result for activityType '" + activityType + "' using format '" + formatFound + "' hence skip feed entry (activity post " + activityPost.getId() + ")"); - } - } - } - } - } - - updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.PROCESSED); - - commitTransaction(); - - if (logger.isDebugEnabled()) - { - logger.debug(">>> Processed: " + (connectedUsers.size() - excludedConnections) + " connections for activity post " + activityPost.getId() + " (excluded " + excludedConnections + ")"); - } - } - finally - { - endTransaction(); - } - } - } - catch(SQLException se) - { - logger.error(se); - throw se; - } - finally - { - logger.info(">>> Generated " + totalGenerated + " activity feed entries for " + (activityPosts == null ? 0 : activityPosts.size()) + " activity posts"); - } - } - - public abstract void startTransaction() throws SQLException; - - public abstract void commitTransaction() throws SQLException; - - public abstract void endTransaction() throws SQLException; - - public abstract List selectPosts(ActivityPostEntity selector) throws SQLException; - - public abstract List selectUserFeedControls(String userId) throws SQLException; - - public abstract long insertFeedEntry(ActivityFeedEntity feed) throws SQLException; - - public abstract int updatePostStatus(long id, ActivityPostEntity.STATUS status) throws SQLException; - - - protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException - { - URL url = new URL(urlString); - - if (logger.isDebugEnabled()) - { - logger.debug(">>> Request URI: " + url.toURI()); - } - - HttpURLConnection conn = (HttpURLConnection)url.openConnection(); - conn.setRequestMethod("GET"); - - if (ticket != null) - { - // add Base64 encoded authorization header - // refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication - conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes())); - } - - String result = null; - InputStream is = null; - BufferedReader br = null; - - try - { - is = conn.getInputStream(); - br = new BufferedReader(new InputStreamReader(is)); - - String line = null; - StringBuffer sb = new StringBuffer(); - while(((line = br.readLine()) !=null)) { - sb.append(line); - } - - result = sb.toString(); - - if (logger.isDebugEnabled()) - { - int responseCode = conn.getResponseCode(); - logger.debug(">>> Response code: " + responseCode); - } - } - finally - { - if (br != null) { br.close(); }; - if (is != null) { is.close(); }; - } - - return result; - } - - protected Set getSiteMembers(RepoCtx ctx, String siteId) throws Exception - { - Set members = new HashSet(); - if ((siteId != null) && (siteId.length() != 0)) - { - StringBuffer sbUrl = new StringBuffer(); - sbUrl.append(ctx.getRepoEndPoint()). - append(URL_SERVICE_SITES).append("/").append(siteId).append(URL_MEMBERSHIPS); - - String jsonArrayResult = callWebScript(sbUrl.toString(), ctx.getTicket()); - if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0)) - { - JSONArray ja = new JSONArray(jsonArrayResult); - for (int i = 0; i < ja.length(); i++) - { - JSONObject member = (JSONObject)ja.get(i); - JSONObject person = (JSONObject)member.getJSONObject("person"); - - String userName = person.getString("userName"); - if (! ctx.isUserNamesAreCaseSensitive()) - { - userName = userName.toLowerCase(); - } - members.add(person.getString("userName")); - } - } - } - - return members; - } - - protected Map> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception - { - StringBuffer sbUrl = new StringBuffer(); - sbUrl.append(repoEndPoint).append(URL_SERVICE_TEMPLATES).append(subPath).append("*").append("?format=json"); - - String jsonArrayResult = null; - try - { - jsonArrayResult = callWebScript(sbUrl.toString(), ticket); - } - catch (FileNotFoundException e) - { - return null; - } - - List allTemplateNames = new ArrayList(10); - - if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0)) - { - JSONArray ja = new JSONArray(jsonArrayResult); - for (int i = 0; i < ja.length(); i++) - { - String name = ja.getString(i); - if (! name.contains(" (Working Copy).")) - { - allTemplateNames.add(name); - } - } - } - - Map> activityTemplates = new HashMap>(10); - - for (String template : allTemplateNames) - { - // assume template path = /..ftl - // and base-activityType can contain "." - - String baseActivityType = template; - int idx1 = baseActivityType.lastIndexOf("/"); - if (idx1 != -1) - { - baseActivityType = baseActivityType.substring(idx1+1); - } - - int idx2 = baseActivityType.lastIndexOf("."); - if (idx2 != -1) - { - int idx3 = baseActivityType.substring(0, idx2).lastIndexOf("."); - if (idx3 != -1) - { - baseActivityType = baseActivityType.substring(0, idx3); - - List activityTypeTemplateList = activityTemplates.get(baseActivityType); - if (activityTypeTemplateList == null) - { - activityTypeTemplateList = new ArrayList(1); - activityTemplates.put(baseActivityType, activityTypeTemplateList); - } - activityTypeTemplateList.add(template); - } - } - } - - return activityTemplates; - } - - protected Configuration getFreemarkerConfiguration(RepoCtx ctx) - { - Configuration cfg = new Configuration(); - cfg.setObjectWrapper(new DefaultObjectWrapper()); - - // custom template loader - cfg.setTemplateLoader(new TemplateWebScriptLoader(ctx.getRepoEndPoint(), ctx.getTicket())); - - // TODO review i18n - cfg.setLocalizedLookup(false); - - return cfg; - } - - protected String processFreemarker(Map templateCache, String fmTemplate, Configuration cfg, Map model) throws IOException, TemplateException, Exception - { - // Save on lots of modification date checking by caching templates locally - Template myTemplate = templateCache.get(fmTemplate); - if (myTemplate == null) - { - myTemplate = cfg.getTemplate(fmTemplate); - templateCache.put(fmTemplate, myTemplate); - } - - StringWriter textWriter = new StringWriter(); - myTemplate.process(model, textWriter); - - return textWriter.toString(); - } - - protected List getFeedControls(String connectedUser) throws SQLException - { - // TODO cache for this run - return selectUserFeedControls(connectedUser); - } - - protected boolean acceptActivity(ActivityPostEntity activityPost, List feedControls) - { - if (feedControls == null) - { - return true; - } - - for (FeedControlEntity feedControl : feedControls) - { - if (((feedControl.getSiteNetwork() == null) || (feedControl.getSiteNetwork().length() == 0)) && (feedControl.getAppTool() != null)) - { - if (feedControl.getAppTool().equals(activityPost.getAppTool())) - { - // exclude this appTool (across sites) - return false; - } - } - else if (((feedControl.getAppTool() == null) || (feedControl.getAppTool().length() == 0)) && (feedControl.getSiteNetwork() != null)) - { - if (feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork())) - { - // exclude this site (across appTools) - return false; - } - } - else if (((feedControl.getSiteNetwork() != null) && (feedControl.getSiteNetwork().length() > 0)) && - ((feedControl.getAppTool() != null) && (feedControl.getAppTool().length() > 0))) - { - if ((feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork())) && - (feedControl.getAppTool().equals(activityPost.getAppTool()))) - { - // exclude this appTool for this site - return false; - } - } - } - - return true; - } - - protected void addMissingFormats(String activityType, List fmTemplates, List templatesToAdd) - { - for (String templateToAdd : templatesToAdd) - { - int idx1 = templateToAdd.lastIndexOf("."); - if (idx1 != -1) - { - int idx2 = templateToAdd.substring(0, idx1).lastIndexOf("."); - if (idx2 != -1) - { - String templateFormat = templateToAdd.substring(idx2+1, idx1); - - boolean found = false; - for (String fmTemplate : fmTemplates) - { - if (fmTemplate.contains("."+templateFormat+".")) - { - found = true; - } - } - - if (! found) - { - if (logger.isDebugEnabled()) - { - logger.debug(">>> Add template '" + templateToAdd + "' for type '" + activityType + "'"); - } - fmTemplates.add(templateToAdd); - } - } - } - } - } - - protected String getTemplateSubPath(String activityType) - { - return (! activityType.startsWith("/") ? "/" : "") + activityType.replace(".", "/"); - } - - protected String getBaseActivityType(String activityType) - { - String[] parts = activityType.split("\\."); - - return (parts.length != 0 ? parts[parts.length-1] : ""); - } - - protected class TemplateWebScriptLoader extends URLTemplateLoader - { - private String repoEndPoint; - private String ticketId; - - public TemplateWebScriptLoader(String repoEndPoint, String ticketId) - { - this.repoEndPoint = repoEndPoint; - this.ticketId = ticketId; - } - - public URL getURL(String templatePath) - { - try - { - StringBuffer sb = new StringBuffer(); - sb.append(this.repoEndPoint). - append(URL_SERVICE_TEMPLATE).append("/").append(templatePath). - append("?format=text"). - append("&alf_ticket=").append(ticketId); - - if (logger.isDebugEnabled()) - { - logger.debug(">>> getURL: " + sb.toString()); - } - - return new URL(sb.toString()); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.alfresco.repo.domain.activities.ActivityFeedEntity; +import org.alfresco.repo.domain.activities.ActivityPostEntity; +import org.alfresco.repo.domain.activities.FeedControlEntity; +import org.alfresco.repo.template.ISO8601DateFormatMethod; +import org.alfresco.util.Base64; +import org.alfresco.util.JSONtoFmModel; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import freemarker.cache.URLTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.DefaultObjectWrapper; +import freemarker.template.Template; +import freemarker.template.TemplateException; + +/** + * Responsible for processing the individual task + */ +public abstract class FeedTaskProcessor +{ + private static final Log logger = LogFactory.getLog(FeedTaskProcessor.class); + + private static final String defaultFormat = "text"; + private static final String[] formats = {"atomentry", "rss", "json", "html", "xml", defaultFormat}; + + private static final String URL_SERVICE_SITES = "/api/sites"; + private static final String URL_MEMBERSHIPS = "/memberships"; + + private static final String URL_SERVICE_TEMPLATES = "/api/activities/templates"; + private static final String URL_SERVICE_TEMPLATE = "/api/activities/template"; + + + public void process(int jobTaskNode, long minSeq, long maxSeq, RepoCtx ctx) throws Exception + { + if (logger.isDebugEnabled()) + { + logger.debug(">>> Process: jobTaskNode '" + jobTaskNode + "' from seq '" + minSeq + "' to seq '" + maxSeq + "' on this node from grid job."); + } + + ActivityPostEntity selector = new ActivityPostEntity(); + selector.setJobTaskNode(jobTaskNode); + selector.setMinId(minSeq); + selector.setMaxId(maxSeq); + selector.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); + + String ticket = ctx.getTicket(); + + List activityPosts = null; + int totalGenerated = 0; + + try + { + activityPosts = selectPosts(selector); + + if (logger.isDebugEnabled()) { logger.debug(">>> Process: " + activityPosts.size() + " activity posts"); } + + Configuration cfg = getFreemarkerConfiguration(ctx); + + Map> activityTemplates = new HashMap>(10); + + Map> siteConnectedUsers = new TreeMap>(); + + Map templateCache = new TreeMap(); + + // for each activity post ... + for (ActivityPostEntity activityPost : activityPosts) + { + String postingUserId = activityPost.getUserId(); + String activityType = activityPost.getActivityType(); + + // eg. org.alfresco.folder.added -> added + String baseActivityType = getBaseActivityType(activityType); + + List fmTemplates = activityTemplates.get(baseActivityType); + + if (fmTemplates == null) + { + // eg. org.alfresco.folder.added -> /org/alfresco/folder/added (note: the leading slash) + String templateSubPath = getTemplateSubPath(activityType); + + fmTemplates = new ArrayList(0); + while (true) + { + int idx = templateSubPath.lastIndexOf("/"); + if (idx != -1) + { + templateSubPath = templateSubPath.substring(0, idx); + Map> templates = null; + try + { + // Repository callback to get list of FreeMarker templates for given activity type + templates = getActivityTypeTemplates(ctx.getRepoEndPoint(), ticket, templateSubPath+"/"); + } + catch (FileNotFoundException fnfe) + { + // ignore - path does not exist + } + if (templates != null) + { + if (templates.get(baseActivityType) != null) + { + // add templates, if format not already included + addMissingFormats(activityType, fmTemplates, templates.get(baseActivityType)); + } + + // special fallback case + if (templates.get("generic") != null) + { + // add templates, if format not already included + addMissingFormats(activityType, fmTemplates, templates.get("generic")); + } + } + } + else + { + break; + } + } + + activityTemplates.put(baseActivityType, fmTemplates); + } + + if (fmTemplates.size() == 0) + { + logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType ); + updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); + continue; + } + + Map model = null; + try + { + model = JSONtoFmModel.convertJSONObjectToMap(activityPost.getActivityData()); + } + catch(JSONException je) + { + logger.error(">>> Skipping activity post " + activityPost.getId() + " due to invalid activity data: " + je); + updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); + continue; + } + + model.put("activityType", activityPost.getActivityType()); + model.put("siteNetwork", activityPost.getSiteNetwork()); + model.put("userId", activityPost.getUserId()); + model.put("id", activityPost.getId()); + model.put("date", activityPost.getPostDate()); // post date rather than time that feed is generated + model.put("xmldate", new ISO8601DateFormatMethod()); + model.put("repoEndPoint", ctx.getRepoEndPoint()); + + // Get the members of this site + String thisSite = activityPost.getSiteNetwork(); + + // Save hammering the repository by reusing cached site members + Set connectedUsers = siteConnectedUsers.get(thisSite); + if (connectedUsers == null) + { + if ((thisSite == null) || (thisSite.length() == 0)) + { + connectedUsers = Collections.singleton(""); // add empty posting userid - to represent site feed ! + } + else + { + try + { + // Repository callback to get site members + connectedUsers = getSiteMembers(ctx, thisSite); + connectedUsers.add(""); // add empty posting userid - to represent site feed ! + + // Cache them for future use in this same invocation + siteConnectedUsers.put(thisSite, connectedUsers); + } + catch(Exception e) + { + logger.error(">>> Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e); + updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); + continue; + } + } + } + + try + { + startTransaction(); + + if (logger.isDebugEnabled()) + { + logger.debug(">>> Process: " + connectedUsers.size() + " candidate connections for activity post " + activityPost.getId()); + } + + int excludedConnections = 0; + + for (String connectedUser : connectedUsers) + { + List feedControls = null; + if (! connectedUser.equals("")) + { + feedControls = getFeedControls(connectedUser); + } + + // filter based on opt-out feed controls (if any) + if (! acceptActivity(activityPost, feedControls)) + { + excludedConnections++; + } + else + { + for (String fmTemplate : fmTemplates) + { + // determine format - based on template naming convention + String formatFound = null; + for (String format : formats) + { + if (fmTemplate.contains("."+format+".")) + { + formatFound = format; + break; + } + } + + if (formatFound == null) + { + formatFound = defaultFormat; + logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'"); + } + + ActivityFeedEntity feed = new ActivityFeedEntity(); + + // Generate activity feed summary + feed.setFeedUserId(connectedUser); + feed.setPostUserId(postingUserId); + feed.setActivityType(activityType); + + if (formatFound.equals("json")) + { + // allows generic JSON template to simply pass straight through + model.put("activityData", activityPost.getActivityData()); + } + + String activitySummary = processFreemarker(templateCache, fmTemplate, cfg, model); + if (! activitySummary.equals("")) + { + feed.setActivitySummary(activitySummary); + feed.setActivitySummaryFormat(formatFound); + feed.setSiteNetwork(thisSite); + feed.setAppTool(activityPost.getAppTool()); + feed.setPostDate(activityPost.getPostDate()); + feed.setPostId(activityPost.getId()); + feed.setFeedDate(new Date()); + + // Insert activity feed + insertFeedEntry(feed); // ignore returned feedId + + totalGenerated++; + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Empty template result for activityType '" + activityType + "' using format '" + formatFound + "' hence skip feed entry (activity post " + activityPost.getId() + ")"); + } + } + } + } + } + + updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.PROCESSED); + + commitTransaction(); + + if (logger.isDebugEnabled()) + { + logger.debug(">>> Processed: " + (connectedUsers.size() - excludedConnections) + " connections for activity post " + activityPost.getId() + " (excluded " + excludedConnections + ")"); + } + } + finally + { + endTransaction(); + } + } + } + catch(SQLException se) + { + logger.error(se); + throw se; + } + finally + { + logger.info(">>> Generated " + totalGenerated + " activity feed entries for " + (activityPosts == null ? 0 : activityPosts.size()) + " activity posts"); + } + } + + public abstract void startTransaction() throws SQLException; + + public abstract void commitTransaction() throws SQLException; + + public abstract void endTransaction() throws SQLException; + + public abstract List selectPosts(ActivityPostEntity selector) throws SQLException; + + public abstract List selectUserFeedControls(String userId) throws SQLException; + + public abstract long insertFeedEntry(ActivityFeedEntity feed) throws SQLException; + + public abstract int updatePostStatus(long id, ActivityPostEntity.STATUS status) throws SQLException; + + + protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException + { + URL url = new URL(urlString); + + if (logger.isDebugEnabled()) + { + logger.debug(">>> Request URI: " + url.toURI()); + } + + HttpURLConnection conn = (HttpURLConnection)url.openConnection(); + conn.setRequestMethod("GET"); + + if (ticket != null) + { + // add Base64 encoded authorization header + // refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication + conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes())); + } + + String result = null; + InputStream is = null; + BufferedReader br = null; + + try + { + is = conn.getInputStream(); + br = new BufferedReader(new InputStreamReader(is)); + + String line = null; + StringBuffer sb = new StringBuffer(); + while(((line = br.readLine()) !=null)) { + sb.append(line); + } + + result = sb.toString(); + + if (logger.isDebugEnabled()) + { + int responseCode = conn.getResponseCode(); + logger.debug(">>> Response code: " + responseCode); + } + } + finally + { + if (br != null) { br.close(); }; + if (is != null) { is.close(); }; + } + + return result; + } + + protected Set getSiteMembers(RepoCtx ctx, String siteId) throws Exception + { + Set members = new HashSet(); + if ((siteId != null) && (siteId.length() != 0)) + { + StringBuffer sbUrl = new StringBuffer(); + sbUrl.append(ctx.getRepoEndPoint()). + append(URL_SERVICE_SITES).append("/").append(siteId).append(URL_MEMBERSHIPS); + + String jsonArrayResult = callWebScript(sbUrl.toString(), ctx.getTicket()); + if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0)) + { + JSONArray ja = new JSONArray(jsonArrayResult); + for (int i = 0; i < ja.length(); i++) + { + JSONObject member = (JSONObject)ja.get(i); + JSONObject person = (JSONObject)member.getJSONObject("person"); + + String userName = person.getString("userName"); + if (! ctx.isUserNamesAreCaseSensitive()) + { + userName = userName.toLowerCase(); + } + members.add(person.getString("userName")); + } + } + } + + return members; + } + + protected Map> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception + { + StringBuffer sbUrl = new StringBuffer(); + sbUrl.append(repoEndPoint).append(URL_SERVICE_TEMPLATES).append(subPath).append("*").append("?format=json"); + + String jsonArrayResult = null; + try + { + jsonArrayResult = callWebScript(sbUrl.toString(), ticket); + } + catch (FileNotFoundException e) + { + return null; + } + + List allTemplateNames = new ArrayList(10); + + if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0)) + { + JSONArray ja = new JSONArray(jsonArrayResult); + for (int i = 0; i < ja.length(); i++) + { + String name = ja.getString(i); + if (! name.contains(" (Working Copy).")) + { + allTemplateNames.add(name); + } + } + } + + Map> activityTemplates = new HashMap>(10); + + for (String template : allTemplateNames) + { + // assume template path = /..ftl + // and base-activityType can contain "." + + String baseActivityType = template; + int idx1 = baseActivityType.lastIndexOf("/"); + if (idx1 != -1) + { + baseActivityType = baseActivityType.substring(idx1+1); + } + + int idx2 = baseActivityType.lastIndexOf("."); + if (idx2 != -1) + { + int idx3 = baseActivityType.substring(0, idx2).lastIndexOf("."); + if (idx3 != -1) + { + baseActivityType = baseActivityType.substring(0, idx3); + + List activityTypeTemplateList = activityTemplates.get(baseActivityType); + if (activityTypeTemplateList == null) + { + activityTypeTemplateList = new ArrayList(1); + activityTemplates.put(baseActivityType, activityTypeTemplateList); + } + activityTypeTemplateList.add(template); + } + } + } + + return activityTemplates; + } + + protected Configuration getFreemarkerConfiguration(RepoCtx ctx) + { + Configuration cfg = new Configuration(); + cfg.setObjectWrapper(new DefaultObjectWrapper()); + + // custom template loader + cfg.setTemplateLoader(new TemplateWebScriptLoader(ctx.getRepoEndPoint(), ctx.getTicket())); + + // TODO review i18n + cfg.setLocalizedLookup(false); + + return cfg; + } + + protected String processFreemarker(Map templateCache, String fmTemplate, Configuration cfg, Map model) throws IOException, TemplateException, Exception + { + // Save on lots of modification date checking by caching templates locally + Template myTemplate = templateCache.get(fmTemplate); + if (myTemplate == null) + { + myTemplate = cfg.getTemplate(fmTemplate); + templateCache.put(fmTemplate, myTemplate); + } + + StringWriter textWriter = new StringWriter(); + myTemplate.process(model, textWriter); + + return textWriter.toString(); + } + + protected List getFeedControls(String connectedUser) throws SQLException + { + // TODO cache for this run + return selectUserFeedControls(connectedUser); + } + + protected boolean acceptActivity(ActivityPostEntity activityPost, List feedControls) + { + if (feedControls == null) + { + return true; + } + + for (FeedControlEntity feedControl : feedControls) + { + if (((feedControl.getSiteNetwork() == null) || (feedControl.getSiteNetwork().length() == 0)) && (feedControl.getAppTool() != null)) + { + if (feedControl.getAppTool().equals(activityPost.getAppTool())) + { + // exclude this appTool (across sites) + return false; + } + } + else if (((feedControl.getAppTool() == null) || (feedControl.getAppTool().length() == 0)) && (feedControl.getSiteNetwork() != null)) + { + if (feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork())) + { + // exclude this site (across appTools) + return false; + } + } + else if (((feedControl.getSiteNetwork() != null) && (feedControl.getSiteNetwork().length() > 0)) && + ((feedControl.getAppTool() != null) && (feedControl.getAppTool().length() > 0))) + { + if ((feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork())) && + (feedControl.getAppTool().equals(activityPost.getAppTool()))) + { + // exclude this appTool for this site + return false; + } + } + } + + return true; + } + + protected void addMissingFormats(String activityType, List fmTemplates, List templatesToAdd) + { + for (String templateToAdd : templatesToAdd) + { + int idx1 = templateToAdd.lastIndexOf("."); + if (idx1 != -1) + { + int idx2 = templateToAdd.substring(0, idx1).lastIndexOf("."); + if (idx2 != -1) + { + String templateFormat = templateToAdd.substring(idx2+1, idx1); + + boolean found = false; + for (String fmTemplate : fmTemplates) + { + if (fmTemplate.contains("."+templateFormat+".")) + { + found = true; + } + } + + if (! found) + { + if (logger.isDebugEnabled()) + { + logger.debug(">>> Add template '" + templateToAdd + "' for type '" + activityType + "'"); + } + fmTemplates.add(templateToAdd); + } + } + } + } + } + + protected String getTemplateSubPath(String activityType) + { + return (! activityType.startsWith("/") ? "/" : "") + activityType.replace(".", "/"); + } + + protected String getBaseActivityType(String activityType) + { + String[] parts = activityType.split("\\."); + + return (parts.length != 0 ? parts[parts.length-1] : ""); + } + + protected class TemplateWebScriptLoader extends URLTemplateLoader + { + private String repoEndPoint; + private String ticketId; + + public TemplateWebScriptLoader(String repoEndPoint, String ticketId) + { + this.repoEndPoint = repoEndPoint; + this.ticketId = ticketId; + } + + public URL getURL(String templatePath) + { + try + { + StringBuffer sb = new StringBuffer(); + sb.append(this.repoEndPoint). + append(URL_SERVICE_TEMPLATE).append("/").append(templatePath). + append("?format=text"). + append("&alf_ticket=").append(ticketId); + + if (logger.isDebugEnabled()) + { + logger.debug(">>> getURL: " + sb.toString()); + } + + return new URL(sb.toString()); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskSplit.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskSplit.java index 87061c6ad3..4f4a98b604 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskSplit.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskSplit.java @@ -1,70 +1,70 @@ -/* - * 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.activities.feed; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Responsible for splitting the feed task into feed jobs (to be executed locally or on a grid) - */ -public class FeedTaskSplit -{ - private static Log logger = LogFactory.getLog(FeedTaskSplit.class); - - public Collection split(int gridSize, JobSettings splitSettings) - { - long maxSequence = splitSettings.getMaxSeq(); - - if (logger.isDebugEnabled()) - { - logger.debug("split: start - gridSize = " + gridSize + ", maxSequence = " + maxSequence); - } - - long minSequence = maxSequence - splitSettings.getMaxItemsPerCycle() + 1; - - splitSettings.setMinSeq((minSequence >= 0L ? minSequence : 0L)); - - List jobs = new ArrayList(gridSize); - - int maxNodeHash = splitSettings.getJobTaskNode(); - - // note: gridSize may change between runs, hence use maximum node hash/bucket for this cycle - for (int n = 1; n <= maxNodeHash; n++) - { - // every job gets its own copy of the jobSettings (with different nodeHash) as an argument. - JobSettings jobSettings = splitSettings.clone(); - jobSettings.setJobTaskNode(n); - - jobs.add(jobSettings); - } - - return jobs; - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Responsible for splitting the feed task into feed jobs (to be executed locally or on a grid) + */ +public class FeedTaskSplit +{ + private static Log logger = LogFactory.getLog(FeedTaskSplit.class); + + public Collection split(int gridSize, JobSettings splitSettings) + { + long maxSequence = splitSettings.getMaxSeq(); + + if (logger.isDebugEnabled()) + { + logger.debug("split: start - gridSize = " + gridSize + ", maxSequence = " + maxSequence); + } + + long minSequence = maxSequence - splitSettings.getMaxItemsPerCycle() + 1; + + splitSettings.setMinSeq((minSequence >= 0L ? minSequence : 0L)); + + List jobs = new ArrayList(gridSize); + + int maxNodeHash = splitSettings.getJobTaskNode(); + + // note: gridSize may change between runs, hence use maximum node hash/bucket for this cycle + for (int n = 1; n <= maxNodeHash; n++) + { + // every job gets its own copy of the jobSettings (with different nodeHash) as an argument. + JobSettings jobSettings = splitSettings.clone(); + jobSettings.setJobTaskNode(n); + + jobs.add(jobSettings); + } + + return jobs; + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/JobSettings.java b/source/java/org/alfresco/repo/activities/feed/JobSettings.java index d87cc9fbff..8addbb7481 100644 --- a/source/java/org/alfresco/repo/activities/feed/JobSettings.java +++ b/source/java/org/alfresco/repo/activities/feed/JobSettings.java @@ -1,100 +1,100 @@ -/* - * 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.activities.feed; - -import java.io.Serializable; - -/** - * Job settings passed from grid task to grid job - */ -public class JobSettings implements Serializable -{ - public static final long serialVersionUID = -3896042917378679686L; - - private int jobTaskNode; - private long maxSeq; - private long minSeq; - private RepoCtx ctx; - private int maxItemsPerCycle; - - public int getJobTaskNode() - { - return jobTaskNode; - } - - public void setJobTaskNode(int jobTaskNode) - { - this.jobTaskNode = jobTaskNode; - } - - public long getMaxSeq() - { - return maxSeq; - } - - public void setMaxSeq(long maxSeq) - { - this.maxSeq = maxSeq; - } - - public long getMinSeq() - { - return minSeq; - } - - public void setMinSeq(long minSeq) - { - this.minSeq = minSeq; - } - - public RepoCtx getWebScriptsCtx() { - return ctx; - } - - public void setWebScriptsCtx(RepoCtx ctx) { - this.ctx = ctx; - } - - public int getMaxItemsPerCycle() - { - return maxItemsPerCycle; - } - - public void setMaxItemsPerCycle(int maxItemsPerCycle) - { - this.maxItemsPerCycle = maxItemsPerCycle; - } - - public JobSettings clone() - { - JobSettings js = new JobSettings(); - js.setMaxItemsPerCycle(this.maxItemsPerCycle); - js.setMaxSeq(this.maxSeq); - js.setMinSeq(this.minSeq); - js.setJobTaskNode(this.jobTaskNode); - js.setWebScriptsCtx(this.ctx); // note: shallow copy - return js; - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed; + +import java.io.Serializable; + +/** + * Job settings passed from grid task to grid job + */ +public class JobSettings implements Serializable +{ + public static final long serialVersionUID = -3896042917378679686L; + + private int jobTaskNode; + private long maxSeq; + private long minSeq; + private RepoCtx ctx; + private int maxItemsPerCycle; + + public int getJobTaskNode() + { + return jobTaskNode; + } + + public void setJobTaskNode(int jobTaskNode) + { + this.jobTaskNode = jobTaskNode; + } + + public long getMaxSeq() + { + return maxSeq; + } + + public void setMaxSeq(long maxSeq) + { + this.maxSeq = maxSeq; + } + + public long getMinSeq() + { + return minSeq; + } + + public void setMinSeq(long minSeq) + { + this.minSeq = minSeq; + } + + public RepoCtx getWebScriptsCtx() { + return ctx; + } + + public void setWebScriptsCtx(RepoCtx ctx) { + this.ctx = ctx; + } + + public int getMaxItemsPerCycle() + { + return maxItemsPerCycle; + } + + public void setMaxItemsPerCycle(int maxItemsPerCycle) + { + this.maxItemsPerCycle = maxItemsPerCycle; + } + + public JobSettings clone() + { + JobSettings js = new JobSettings(); + js.setMaxItemsPerCycle(this.maxItemsPerCycle); + js.setMaxSeq(this.maxSeq); + js.setMinSeq(this.minSeq); + js.setJobTaskNode(this.jobTaskNode); + js.setWebScriptsCtx(this.ctx); // note: shallow copy + return js; + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/RepoCtx.java b/source/java/org/alfresco/repo/activities/feed/RepoCtx.java index 7f9cf6059d..2767cdbaf5 100644 --- a/source/java/org/alfresco/repo/activities/feed/RepoCtx.java +++ b/source/java/org/alfresco/repo/activities/feed/RepoCtx.java @@ -1,69 +1,69 @@ -/* - * 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.activities.feed; - -import java.io.Serializable; - -/** - * Repository context passed from grid task to grid job - */ -public class RepoCtx implements Serializable -{ - private String repoEndPoint; // http://hostname:port/webapp (eg. http://localhost:8080/alfresco) - private boolean userNamesAreCaseSensitive = false; - - private String ticket; - - public static final long serialVersionUID = -3896042917378679686L; - - public RepoCtx(String repoEndPoint) - { - this.repoEndPoint = repoEndPoint.endsWith("/") ? repoEndPoint.substring(0, repoEndPoint.length()-1) : repoEndPoint; - } - - public String getRepoEndPoint() { - return repoEndPoint; - } - - public String getTicket() - { - return ticket; - } - - public void setTicket(String ticket) - { - this.ticket = ticket; - } - - public boolean isUserNamesAreCaseSensitive() - { - return userNamesAreCaseSensitive; - } - - public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) - { - this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed; + +import java.io.Serializable; + +/** + * Repository context passed from grid task to grid job + */ +public class RepoCtx implements Serializable +{ + private String repoEndPoint; // http://hostname:port/webapp (eg. http://localhost:8080/alfresco) + private boolean userNamesAreCaseSensitive = false; + + private String ticket; + + public static final long serialVersionUID = -3896042917378679686L; + + public RepoCtx(String repoEndPoint) + { + this.repoEndPoint = repoEndPoint.endsWith("/") ? repoEndPoint.substring(0, repoEndPoint.length()-1) : repoEndPoint; + } + + public String getRepoEndPoint() { + return repoEndPoint; + } + + public String getTicket() + { + return ticket; + } + + public void setTicket(String ticket) + { + this.ticket = ticket; + } + + public boolean isUserNamesAreCaseSensitive() + { + return userNamesAreCaseSensitive; + } + + public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive) + { + this.userNamesAreCaseSensitive = userNamesAreCaseSensitive; + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedGridJob.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedGridJob.java index fec390d459..16fed97580 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedGridJob.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedGridJob.java @@ -1,80 +1,80 @@ -/* - * 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.activities.feed.local; - -import java.io.Serializable; - -import org.alfresco.repo.activities.feed.FeedGridJob; -import org.alfresco.repo.activities.feed.FeedTaskProcessor; -import org.alfresco.repo.activities.feed.JobSettings; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Implementation to execute local (ie. not grid) feed job - */ -public class LocalFeedGridJob implements FeedGridJob -{ - private static final Log logger = LogFactory.getLog(LocalFeedGridJob.class); - - private JobSettings arg; - - private FeedTaskProcessor feedTaskProcessor; - - public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor) - { - this.feedTaskProcessor = feedTaskProcessor; - } - - public Serializable execute() throws Exception - { - JobSettings js = getArgument(); - - if (logger.isDebugEnabled()) { logger.debug(">>> Execute: nodehash '" + js.getJobTaskNode() + "' from seq '" + js.getMinSeq() + "' to seq '" + js.getMaxSeq() + "' on this node"); } - - try - { - feedTaskProcessor.process(js.getJobTaskNode(), js.getMinSeq(), js.getMaxSeq(), js.getWebScriptsCtx()); - } - catch (Exception e) - { - logger.error(e); - throw new Exception(e.getMessage(), e.getCause()); - } - - // This job does not return any result. - return null; - } - - public void setArgument(JobSettings arg) - { - this.arg = arg; - } - - public JobSettings getArgument() - { - return this.arg; - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed.local; + +import java.io.Serializable; + +import org.alfresco.repo.activities.feed.FeedGridJob; +import org.alfresco.repo.activities.feed.FeedTaskProcessor; +import org.alfresco.repo.activities.feed.JobSettings; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Implementation to execute local (ie. not grid) feed job + */ +public class LocalFeedGridJob implements FeedGridJob +{ + private static final Log logger = LogFactory.getLog(LocalFeedGridJob.class); + + private JobSettings arg; + + private FeedTaskProcessor feedTaskProcessor; + + public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor) + { + this.feedTaskProcessor = feedTaskProcessor; + } + + public Serializable execute() throws Exception + { + JobSettings js = getArgument(); + + if (logger.isDebugEnabled()) { logger.debug(">>> Execute: nodehash '" + js.getJobTaskNode() + "' from seq '" + js.getMinSeq() + "' to seq '" + js.getMaxSeq() + "' on this node"); } + + try + { + feedTaskProcessor.process(js.getJobTaskNode(), js.getMinSeq(), js.getMaxSeq(), js.getWebScriptsCtx()); + } + catch (Exception e) + { + logger.error(e); + throw new Exception(e.getMessage(), e.getCause()); + } + + // This job does not return any result. + return null; + } + + public void setArgument(JobSettings arg) + { + this.arg = arg; + } + + public JobSettings getArgument() + { + return this.arg; + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java index 7333b8fcb0..dd6564c919 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskProcessor.java @@ -1,107 +1,107 @@ -/* - * 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.activities.feed.local; - -import java.sql.SQLException; -import java.util.List; - -import org.alfresco.repo.activities.feed.FeedTaskProcessor; -import org.alfresco.repo.domain.activities.ActivityFeedDAO; -import org.alfresco.repo.domain.activities.ActivityFeedEntity; -import org.alfresco.repo.domain.activities.ActivityPostDAO; -import org.alfresco.repo.domain.activities.ActivityPostEntity; -import org.alfresco.repo.domain.activities.FeedControlDAO; -import org.alfresco.repo.domain.activities.FeedControlEntity; - -import com.ibatis.sqlmap.client.SqlMapClient; - -/** - * The local (ie. not grid) feed task processor is responsible for processing the individual feed job - */ -public class LocalFeedTaskProcessor extends FeedTaskProcessor -{ - private ActivityPostDAO postDAO; - private ActivityFeedDAO feedDAO; - private FeedControlDAO feedControlDAO; - - // used to start/end/commit transaction - // note: currently assumes that all dao services are configured with this mapper / data source - private SqlMapClient sqlMapper; - - public void setPostDAO(ActivityPostDAO postDAO) - { - this.postDAO = postDAO; - } - - public void setFeedDAO(ActivityFeedDAO feedDAO) - { - this.feedDAO = feedDAO; - } - - public void setFeedControlDAO(FeedControlDAO feedControlDAO) - { - this.feedControlDAO = feedControlDAO; - } - - public void setSqlMapClient(SqlMapClient sqlMapper) - { - this.sqlMapper = sqlMapper; - } - - public void startTransaction() throws SQLException - { - sqlMapper.startTransaction(); - } - - public void commitTransaction() throws SQLException - { - sqlMapper.commitTransaction(); - } - - public void endTransaction() throws SQLException - { - sqlMapper.endTransaction(); - } - - public List selectPosts(ActivityPostEntity selector) throws SQLException - { - return postDAO.selectPosts(selector); - } - - public long insertFeedEntry(ActivityFeedEntity feed) throws SQLException - { - return feedDAO.insertFeedEntry(feed); - } - - public int updatePostStatus(long id, ActivityPostEntity.STATUS status) throws SQLException - { - return postDAO.updatePostStatus(id, status); - } - - public List selectUserFeedControls(String userId) throws SQLException - { - return feedControlDAO.selectFeedControls(userId); - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed.local; + +import java.sql.SQLException; +import java.util.List; + +import org.alfresco.repo.activities.feed.FeedTaskProcessor; +import org.alfresco.repo.domain.activities.ActivityFeedDAO; +import org.alfresco.repo.domain.activities.ActivityFeedEntity; +import org.alfresco.repo.domain.activities.ActivityPostDAO; +import org.alfresco.repo.domain.activities.ActivityPostEntity; +import org.alfresco.repo.domain.activities.FeedControlDAO; +import org.alfresco.repo.domain.activities.FeedControlEntity; + +import com.ibatis.sqlmap.client.SqlMapClient; + +/** + * The local (ie. not grid) feed task processor is responsible for processing the individual feed job + */ +public class LocalFeedTaskProcessor extends FeedTaskProcessor +{ + private ActivityPostDAO postDAO; + private ActivityFeedDAO feedDAO; + private FeedControlDAO feedControlDAO; + + // used to start/end/commit transaction + // note: currently assumes that all dao services are configured with this mapper / data source + private SqlMapClient sqlMapper; + + public void setPostDAO(ActivityPostDAO postDAO) + { + this.postDAO = postDAO; + } + + public void setFeedDAO(ActivityFeedDAO feedDAO) + { + this.feedDAO = feedDAO; + } + + public void setFeedControlDAO(FeedControlDAO feedControlDAO) + { + this.feedControlDAO = feedControlDAO; + } + + public void setSqlMapClient(SqlMapClient sqlMapper) + { + this.sqlMapper = sqlMapper; + } + + public void startTransaction() throws SQLException + { + sqlMapper.startTransaction(); + } + + public void commitTransaction() throws SQLException + { + sqlMapper.commitTransaction(); + } + + public void endTransaction() throws SQLException + { + sqlMapper.endTransaction(); + } + + public List selectPosts(ActivityPostEntity selector) throws SQLException + { + return postDAO.selectPosts(selector); + } + + public long insertFeedEntry(ActivityFeedEntity feed) throws SQLException + { + return feedDAO.insertFeedEntry(feed); + } + + public int updatePostStatus(long id, ActivityPostEntity.STATUS status) throws SQLException + { + return postDAO.updatePostStatus(id, status); + } + + public List selectUserFeedControls(String userId) throws SQLException + { + return feedControlDAO.selectFeedControls(userId); + } +} diff --git a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskSplitter.java b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskSplitter.java index 1318c67c7f..b32c44f378 100644 --- a/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskSplitter.java +++ b/source/java/org/alfresco/repo/activities/feed/local/LocalFeedTaskSplitter.java @@ -1,76 +1,76 @@ -/* - * 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.activities.feed.local; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.alfresco.repo.activities.feed.FeedGridJob; -import org.alfresco.repo.activities.feed.FeedTaskProcessor; -import org.alfresco.repo.activities.feed.FeedTaskSplit; -import org.alfresco.repo.activities.feed.JobSettings; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * The local feed task splitter is responsible for splitting the feed task into feed jobs - */ -public class LocalFeedTaskSplitter -{ - private static final Log logger = LogFactory.getLog(LocalFeedTaskSplitter.class); - - private FeedTaskProcessor feedTaskProcessor; - - public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor) - { - this.feedTaskProcessor = feedTaskProcessor; - } - - public Collection split(int gridSize, Object o) throws Exception - { - try - { - FeedTaskSplit feedSplitter = new FeedTaskSplit(); - Collection jobs = feedSplitter.split(gridSize, (JobSettings)o); - - List gridJobs = new ArrayList(jobs.size()); - for (JobSettings job : jobs) - { - LocalFeedGridJob gridJob = new LocalFeedGridJob(); - gridJob.setFeedTaskProcessor(feedTaskProcessor); - gridJob.setArgument(job); - gridJobs.add(gridJob); - } - return gridJobs; - //return (Collection)feedSplitter.split(gridSize, (JobSettings)o, new LocalFeedGridJob()); - } - catch (Exception e) - { - logger.equals(e); - throw new Exception(e.getMessage()); - } - } -} +/* + * Copyright (C) 2005-2009 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.activities.feed.local; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.alfresco.repo.activities.feed.FeedGridJob; +import org.alfresco.repo.activities.feed.FeedTaskProcessor; +import org.alfresco.repo.activities.feed.FeedTaskSplit; +import org.alfresco.repo.activities.feed.JobSettings; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * The local feed task splitter is responsible for splitting the feed task into feed jobs + */ +public class LocalFeedTaskSplitter +{ + private static final Log logger = LogFactory.getLog(LocalFeedTaskSplitter.class); + + private FeedTaskProcessor feedTaskProcessor; + + public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor) + { + this.feedTaskProcessor = feedTaskProcessor; + } + + public Collection split(int gridSize, Object o) throws Exception + { + try + { + FeedTaskSplit feedSplitter = new FeedTaskSplit(); + Collection jobs = feedSplitter.split(gridSize, (JobSettings)o); + + List gridJobs = new ArrayList(jobs.size()); + for (JobSettings job : jobs) + { + LocalFeedGridJob gridJob = new LocalFeedGridJob(); + gridJob.setFeedTaskProcessor(feedTaskProcessor); + gridJob.setArgument(job); + gridJobs.add(gridJob); + } + return gridJobs; + //return (Collection)feedSplitter.split(gridSize, (JobSettings)o, new LocalFeedGridJob()); + } + catch (Exception e) + { + logger.equals(e); + throw new Exception(e.getMessage()); + } + } +} diff --git a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java index 3300703e3d..03ad8dfc55 100644 --- a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java +++ b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java @@ -1,351 +1,351 @@ -/* - * Copyright (C) 2005-2009 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.activities.post.lookup; - -import java.sql.SQLException; -import java.util.Date; -import java.util.List; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.domain.activities.ActivityPostDAO; -import org.alfresco.repo.domain.activities.ActivityPostEntity; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.tenant.TenantService; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.Path; -import org.alfresco.service.cmr.security.PermissionService; -import org.alfresco.service.cmr.security.PersonService; -import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.Pair; -import org.alfresco.util.PropertyCheck; -import org.alfresco.util.VmShutdownListener; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.quartz.JobExecutionException; - -/** - * The post lookup component is responsible for updating posts that require a secondary lookup (to get additional activity data) - */ -public class PostLookup -{ - private static Log logger = LogFactory.getLog(PostLookup.class); - - private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostLookup.class.getName()); - - private ActivityPostDAO postDAO; - private NodeService nodeService; - private PermissionService permissionService; - private TransactionService transactionService; - private PersonService personService; - private TenantService tenantService; - - public void setPostDAO(ActivityPostDAO postDAO) - { - this.postDAO = postDAO; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setPermissionService(PermissionService permissionService) - { - this.permissionService = permissionService; - } - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setPersonService(PersonService personService) - { - this.personService = personService; - } - - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - /** - * Perform basic checks to ensure that the necessary dependencies were injected. - */ - private void checkProperties() - { - PropertyCheck.mandatory(this, "postDAO", postDAO); - PropertyCheck.mandatory(this, "nodeService", nodeService); - PropertyCheck.mandatory(this, "permissionService", permissionService); - PropertyCheck.mandatory(this, "transactionService", transactionService); - PropertyCheck.mandatory(this, "personService", personService); - PropertyCheck.mandatory(this, "tenantService", tenantService); - } - - public void execute() throws JobExecutionException - { - checkProperties(); - try - { - ActivityPostEntity params = new ActivityPostEntity(); - params.setStatus(ActivityPostEntity.STATUS.PENDING.toString()); - - List activityPosts = postDAO.selectPosts(params); - - if (activityPosts.size() > 0) - { - logger.info("Update: " + activityPosts.size() + " activity posts"); - } - - for (final ActivityPostEntity activityPost : activityPosts) - { - try - { - postDAO.startTransaction(); - - final JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData())); - final String postUserId = activityPost.getUserId(); - - // MT share - String tenantDomain = tenantService.getUserDomain(postUserId); - - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() throws Exception - { - if (! jo.isNull("nodeRef")) - { - String nodeRefStr = jo.getString("nodeRef"); - NodeRef nodeRef = new NodeRef(nodeRefStr); - - // lookup additional node data - JSONObject activityData = lookupNode(nodeRef, postUserId, jo); - - activityPost.setActivityData(activityData.toString()); - } - else - { - // lookup additional person data - Pair firstLastName = lookupPerson(postUserId); - if (firstLastName != null) - { - jo.put("firstName", firstLastName.getFirst()); - jo.put("lastName", firstLastName.getSecond()); - - activityPost.setActivityData(jo.toString()); - } - } - - activityPost.setLastModified(new Date()); - - postDAO.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostEntity.STATUS.POSTED); - if (logger.isDebugEnabled()) - { - activityPost.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); // for debug output - logger.debug("Updated: " + activityPost); - } - - return null; - } - }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); - - postDAO.commitTransaction(); - } - catch (JSONException e) - { - // log error, but consume exception (skip this post) - logger.error("Skipping activity post " + activityPost.getId() + ": " + e); - postDAO.updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); - - postDAO.commitTransaction(); - } - catch (SQLException e) - { - logger.error("Exception during update of post", e); - throw new JobExecutionException(e); - } - finally - { - postDAO.endTransaction(); - } - } - } - catch (SQLException e) - { - logger.error("Exception during select of posts", e); - throw new JobExecutionException(e); - } - catch (Throwable e) - { - // If the VM is shutting down, then ignore - if (vmShutdownListener.isVmShuttingDown()) - { - // Ignore - } - else - { - logger.error("Exception during update of posts", e); - } - } - } - - private Pair lookupPerson(final String postUserId) throws JSONException - { - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback> lookup = new RetryingTransactionCallback>() - { - public Pair execute() throws Throwable - { - String firstName = ""; - String lastName = ""; - - if (personService.personExists(postUserId)) - { - NodeRef personRef = personService.getPerson(postUserId); - - firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); - lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); - - return new Pair(firstName, lastName); - } - - return null; - } - }; - - // execute in txn - return txnHelper.doInTransaction(lookup, true); - } - - private JSONObject lookupNode(final NodeRef nodeRef, final String postUserId, final JSONObject jo) throws JSONException - { - return AuthenticationUtil.runAs(new RunAsWork() - { - public JSONObject doWork() throws Exception - { - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - - // wrap to make the request in a transaction - RetryingTransactionCallback lookup = new RetryingTransactionCallback() - { - public JSONObject execute() throws Throwable - { - String name = ""; - if (! jo.isNull("name")) - { - name = jo.getString("name"); - } - - NodeRef parentNodeRef = null; - if (! jo.isNull("parentNodeRef")) - { - parentNodeRef = new NodeRef(jo.getString("parentNodeRef")); - } - - - String typeQName = ""; - if (! jo.isNull("typeQName")) - { - typeQName = jo.getString("typeQName"); - } - - String displayPath = ""; - Path path = null; - String firstName = ""; - String lastName = ""; - - if (personService.personExists(postUserId)) - { - // lookup firstname, lastname - NodeRef personRef = personService.getPerson(postUserId); - - firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); - lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); - } - - if ((nodeRef != null) && (nodeService.exists(nodeRef))) - { - if (name.length() == 0) - { - // lookup node name - name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - } - - if (typeQName.length() == 0) - { - // lookup type - typeQName = nodeService.getType(nodeRef).toPrefixString(); // TODO: missing the prefix ? - } - - if (parentNodeRef == null) - { - // lookup parent node - parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); - } - } - - if ((parentNodeRef != null) && (nodeService.exists(parentNodeRef))) - { - // parent node exists, lookup parent node path - path = nodeService.getPath(parentNodeRef); - } - - if (path != null) - { - // lookup display path - displayPath = path.toDisplayPath(nodeService, permissionService); - - // note: for now, also tack on the node name - displayPath += "/" + name; - } - - // merge with existing activity data - jo.put("name", name); - jo.put("nodeRef", nodeRef.toString()); - jo.put("typeQName", typeQName); - jo.put("parentNodeRef", (parentNodeRef != null ? parentNodeRef.toString() : null)); - jo.put("displayPath", displayPath); - jo.put("firstName", firstName); - jo.put("lastName", lastName); - - return jo; - } - }; - - // execute in txn - return txnHelper.doInTransaction(lookup, true); - } - }, AuthenticationUtil.getSystemUserName()); - } +/* + * Copyright (C) 2005-2009 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.activities.post.lookup; + +import java.sql.SQLException; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.activities.ActivityPostDAO; +import org.alfresco.repo.domain.activities.ActivityPostEntity; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Path; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.alfresco.util.VmShutdownListener; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.quartz.JobExecutionException; + +/** + * The post lookup component is responsible for updating posts that require a secondary lookup (to get additional activity data) + */ +public class PostLookup +{ + private static Log logger = LogFactory.getLog(PostLookup.class); + + private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostLookup.class.getName()); + + private ActivityPostDAO postDAO; + private NodeService nodeService; + private PermissionService permissionService; + private TransactionService transactionService; + private PersonService personService; + private TenantService tenantService; + + public void setPostDAO(ActivityPostDAO postDAO) + { + this.postDAO = postDAO; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setPersonService(PersonService personService) + { + this.personService = personService; + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Perform basic checks to ensure that the necessary dependencies were injected. + */ + private void checkProperties() + { + PropertyCheck.mandatory(this, "postDAO", postDAO); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "permissionService", permissionService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "personService", personService); + PropertyCheck.mandatory(this, "tenantService", tenantService); + } + + public void execute() throws JobExecutionException + { + checkProperties(); + try + { + ActivityPostEntity params = new ActivityPostEntity(); + params.setStatus(ActivityPostEntity.STATUS.PENDING.toString()); + + List activityPosts = postDAO.selectPosts(params); + + if (activityPosts.size() > 0) + { + logger.info("Update: " + activityPosts.size() + " activity posts"); + } + + for (final ActivityPostEntity activityPost : activityPosts) + { + try + { + postDAO.startTransaction(); + + final JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData())); + final String postUserId = activityPost.getUserId(); + + // MT share + String tenantDomain = tenantService.getUserDomain(postUserId); + + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() throws Exception + { + if (! jo.isNull("nodeRef")) + { + String nodeRefStr = jo.getString("nodeRef"); + NodeRef nodeRef = new NodeRef(nodeRefStr); + + // lookup additional node data + JSONObject activityData = lookupNode(nodeRef, postUserId, jo); + + activityPost.setActivityData(activityData.toString()); + } + else + { + // lookup additional person data + Pair firstLastName = lookupPerson(postUserId); + if (firstLastName != null) + { + jo.put("firstName", firstLastName.getFirst()); + jo.put("lastName", firstLastName.getSecond()); + + activityPost.setActivityData(jo.toString()); + } + } + + activityPost.setLastModified(new Date()); + + postDAO.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostEntity.STATUS.POSTED); + if (logger.isDebugEnabled()) + { + activityPost.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); // for debug output + logger.debug("Updated: " + activityPost); + } + + return null; + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + + postDAO.commitTransaction(); + } + catch (JSONException e) + { + // log error, but consume exception (skip this post) + logger.error("Skipping activity post " + activityPost.getId() + ": " + e); + postDAO.updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); + + postDAO.commitTransaction(); + } + catch (SQLException e) + { + logger.error("Exception during update of post", e); + throw new JobExecutionException(e); + } + finally + { + postDAO.endTransaction(); + } + } + } + catch (SQLException e) + { + logger.error("Exception during select of posts", e); + throw new JobExecutionException(e); + } + catch (Throwable e) + { + // If the VM is shutting down, then ignore + if (vmShutdownListener.isVmShuttingDown()) + { + // Ignore + } + else + { + logger.error("Exception during update of posts", e); + } + } + } + + private Pair lookupPerson(final String postUserId) throws JSONException + { + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback> lookup = new RetryingTransactionCallback>() + { + public Pair execute() throws Throwable + { + String firstName = ""; + String lastName = ""; + + if (personService.personExists(postUserId)) + { + NodeRef personRef = personService.getPerson(postUserId); + + firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); + lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); + + return new Pair(firstName, lastName); + } + + return null; + } + }; + + // execute in txn + return txnHelper.doInTransaction(lookup, true); + } + + private JSONObject lookupNode(final NodeRef nodeRef, final String postUserId, final JSONObject jo) throws JSONException + { + return AuthenticationUtil.runAs(new RunAsWork() + { + public JSONObject doWork() throws Exception + { + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + + // wrap to make the request in a transaction + RetryingTransactionCallback lookup = new RetryingTransactionCallback() + { + public JSONObject execute() throws Throwable + { + String name = ""; + if (! jo.isNull("name")) + { + name = jo.getString("name"); + } + + NodeRef parentNodeRef = null; + if (! jo.isNull("parentNodeRef")) + { + parentNodeRef = new NodeRef(jo.getString("parentNodeRef")); + } + + + String typeQName = ""; + if (! jo.isNull("typeQName")) + { + typeQName = jo.getString("typeQName"); + } + + String displayPath = ""; + Path path = null; + String firstName = ""; + String lastName = ""; + + if (personService.personExists(postUserId)) + { + // lookup firstname, lastname + NodeRef personRef = personService.getPerson(postUserId); + + firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); + lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); + } + + if ((nodeRef != null) && (nodeService.exists(nodeRef))) + { + if (name.length() == 0) + { + // lookup node name + name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + } + + if (typeQName.length() == 0) + { + // lookup type + typeQName = nodeService.getType(nodeRef).toPrefixString(); // TODO: missing the prefix ? + } + + if (parentNodeRef == null) + { + // lookup parent node + parentNodeRef = nodeService.getPrimaryParent(nodeRef).getParentRef(); + } + } + + if ((parentNodeRef != null) && (nodeService.exists(parentNodeRef))) + { + // parent node exists, lookup parent node path + path = nodeService.getPath(parentNodeRef); + } + + if (path != null) + { + // lookup display path + displayPath = path.toDisplayPath(nodeService, permissionService); + + // note: for now, also tack on the node name + displayPath += "/" + name; + } + + // merge with existing activity data + jo.put("name", name); + jo.put("nodeRef", nodeRef.toString()); + jo.put("typeQName", typeQName); + jo.put("parentNodeRef", (parentNodeRef != null ? parentNodeRef.toString() : null)); + jo.put("displayPath", displayPath); + jo.put("firstName", firstName); + jo.put("lastName", lastName); + + return jo; + } + }; + + // execute in txn + return txnHelper.doInTransaction(lookup, true); + } + }, AuthenticationUtil.getSystemUserName()); + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/activities/script/Activity.java b/source/java/org/alfresco/repo/activities/script/Activity.java index a4741c3cc0..b8fd529137 100644 --- a/source/java/org/alfresco/repo/activities/script/Activity.java +++ b/source/java/org/alfresco/repo/activities/script/Activity.java @@ -1,157 +1,157 @@ -/* - * Copyright (C) 2005-2007 Alfresco Software Limited. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - - * As a special exception to the terms and conditions of version 2.0 of - * the GPL, you may redistribute this Program in connection with Free/Libre - * and Open Source Software ("FLOSS") applications as described in Alfresco's - * FLOSS exception. You should have recieved a copy of the text describing - * the FLOSS exception, and it is also available here: - * http://www.alfresco.com/legal/licensing" - */ -package org.alfresco.repo.activities.script; - -import java.util.List; - -import org.alfresco.repo.jscript.BaseScopableProcessorExtension; -import org.alfresco.service.cmr.activities.ActivityService; -import org.alfresco.service.cmr.activities.FeedControl; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; - -/** - * Scripted Activity Service for posting activities. - */ - -public final class Activity extends BaseScopableProcessorExtension -{ - private ActivityService activityService; - - /** - * Set the activity service - * - * @param activityService the activity service - */ - public void setActivityService(ActivityService activityService) - { - this.activityService = activityService; - } - - - /* - * Post Activity - */ - - /** - * Post a custom activity type - * - * @param activityType - required - * @param siteId - optional, if null will be stored as empty string - * @param appTool - optional, if null will be stored as empty string - * @param jsonActivityData - required - */ - public void postActivity(String activityType, String siteId, String appTool, String jsonActivityData) - { - activityService.postActivity(activityType, siteId, appTool, jsonActivityData); - } - - /** - * Post a pre-defined activity type - activity data will be looked-up asynchronously, including: - * - * name - * displayPath - * typeQName - * firstName (of posting user) - * lastName (of posting user) - * - * @param activityType - required - * @param siteId - optional, if null will be stored as empty string - * @param appTool - optional, if null will be stored as empty string - * @param nodeRef - required - do not use for deleted (or about to be deleted) nodeRef - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef) - { - activityService.postActivity(activityType, siteId, appTool, nodeRef); - } - - /** - * Post a pre-defined activity type - eg. for checked-out nodeRef or renamed nodeRef - * - * @param activityType - required - * @param siteId - optional, if null will be stored as empty string - * @param appTool - optional, if null will be stored as empty string - * @param nodeRef - required - do not use deleted (or about to be deleted) nodeRef - * @param beforeName - optional - name of node (eg. prior to name change) - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String beforeName) - { - activityService.postActivity(activityType, siteId, appTool, nodeRef, beforeName); - } - - /** - * Post a pre-defined activity type - eg. for deleted nodeRef - * - * @param activityType - required - * @param siteId - optional, if null will be stored as empty string - * @param appTool - optional, if null will be stored as empty string - * @param nodeRef - required - can be a deleted (or about to be deleted) nodeRef - * @param name - optional - name of name - * @param typeQName - optional - type of node - * @param parentNodeRef - required - used to lookup path/displayPath - */ - public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) - { - activityService.postActivity(activityType, siteId, appTool, nodeRef, name, typeQName, parentNodeRef); - } - - - /* - * Manage User Feed Controls - */ - - /** - * For current user, get feed controls - * - * @return array of user feed controls - */ - public FeedControl[] getFeedControls() - { - List feedControls = activityService.getFeedControls(); - return (FeedControl[])feedControls.toArray(new FeedControl[feedControls.size()]); - } - - /** - * For current user, set feed control (opt-out) for a site or an appTool or a site/appTool combination - * - * @param siteId - required (optional, if appToolId is supplied) - * @param appToolId - required (optional, if siteId is supplied) - */ - public void setFeedControl(String siteId, String appToolId) - { - activityService.setFeedControl(new FeedControl(siteId, appToolId)); - } - - /** - * For current user, unset feed control - * - * @param siteId - required (optional, if appToolId is supplied) - * @param appToolId - required (optional, if siteId is supplied) - */ - public void unsetFeedControl(String siteId, String appToolId) - { - activityService.unsetFeedControl(new FeedControl(siteId, appToolId)); - } -} +/* + * Copyright (C) 2005-2009 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.activities.script; + +import java.util.List; + +import org.alfresco.repo.jscript.BaseScopableProcessorExtension; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.activities.FeedControl; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * Scripted Activity Service for posting activities. + */ + +public final class Activity extends BaseScopableProcessorExtension +{ + private ActivityService activityService; + + /** + * Set the activity service + * + * @param activityService the activity service + */ + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } + + + /* + * Post Activity + */ + + /** + * Post a custom activity type + * + * @param activityType - required + * @param siteId - optional, if null will be stored as empty string + * @param appTool - optional, if null will be stored as empty string + * @param jsonActivityData - required + */ + public void postActivity(String activityType, String siteId, String appTool, String jsonActivityData) + { + activityService.postActivity(activityType, siteId, appTool, jsonActivityData); + } + + /** + * Post a pre-defined activity type - activity data will be looked-up asynchronously, including: + * + * name + * displayPath + * typeQName + * firstName (of posting user) + * lastName (of posting user) + * + * @param activityType - required + * @param siteId - optional, if null will be stored as empty string + * @param appTool - optional, if null will be stored as empty string + * @param nodeRef - required - do not use for deleted (or about to be deleted) nodeRef + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef) + { + activityService.postActivity(activityType, siteId, appTool, nodeRef); + } + + /** + * Post a pre-defined activity type - eg. for checked-out nodeRef or renamed nodeRef + * + * @param activityType - required + * @param siteId - optional, if null will be stored as empty string + * @param appTool - optional, if null will be stored as empty string + * @param nodeRef - required - do not use deleted (or about to be deleted) nodeRef + * @param beforeName - optional - name of node (eg. prior to name change) + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String beforeName) + { + activityService.postActivity(activityType, siteId, appTool, nodeRef, beforeName); + } + + /** + * Post a pre-defined activity type - eg. for deleted nodeRef + * + * @param activityType - required + * @param siteId - optional, if null will be stored as empty string + * @param appTool - optional, if null will be stored as empty string + * @param nodeRef - required - can be a deleted (or about to be deleted) nodeRef + * @param name - optional - name of name + * @param typeQName - optional - type of node + * @param parentNodeRef - required - used to lookup path/displayPath + */ + public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef) + { + activityService.postActivity(activityType, siteId, appTool, nodeRef, name, typeQName, parentNodeRef); + } + + + /* + * Manage User Feed Controls + */ + + /** + * For current user, get feed controls + * + * @return array of user feed controls + */ + public FeedControl[] getFeedControls() + { + List feedControls = activityService.getFeedControls(); + return (FeedControl[])feedControls.toArray(new FeedControl[feedControls.size()]); + } + + /** + * For current user, set feed control (opt-out) for a site or an appTool or a site/appTool combination + * + * @param siteId - required (optional, if appToolId is supplied) + * @param appToolId - required (optional, if siteId is supplied) + */ + public void setFeedControl(String siteId, String appToolId) + { + activityService.setFeedControl(new FeedControl(siteId, appToolId)); + } + + /** + * For current user, unset feed control + * + * @param siteId - required (optional, if appToolId is supplied) + * @param appToolId - required (optional, if siteId is supplied) + */ + public void unsetFeedControl(String siteId, String appToolId) + { + activityService.unsetFeedControl(new FeedControl(siteId, appToolId)); + } +} diff --git a/source/java/org/alfresco/repo/activities/script/test_activityService.js b/source/java/org/alfresco/repo/activities/script/test_activityService.js index 0b7e562d2f..05cce5b1ed 100644 --- a/source/java/org/alfresco/repo/activities/script/test_activityService.js +++ b/source/java/org/alfresco/repo/activities/script/test_activityService.js @@ -1,46 +1,46 @@ -// called by ActivityServiceImplTest.java (test_JSAPI) - -var failure = "did not complete script"; - -// invalid -// activities.postActivity("my activity type", null, null, null); -// activities.postActivity(null, "my site", "my app tool", '{ 000 }'); - -// valid -activities.postActivity("test activity type 4", null, null, '{ "item1" : 123 }'); -activities.postActivity("test activity type 5", "my site", null, '{ "item2" : 456 }'); -activities.postActivity("test activity type 6", "my site", "my app tool", '{ "item3" : 789 }'); -activities.postActivity("test activity type 7", "my site", "my app tool", '{ invalidJSON }'); - -// user feed controls -var feedControls = activities.getFeedControls(); -test.assertEquals(0, feedControls.length); - -activities.setFeedControl("my site", "my app tool"); - -feedControls = activities.getFeedControls(); -test.assertEquals(1, feedControls.length); - -activities.setFeedControl("my site", null); - -feedControls = activities.getFeedControls(); -test.assertEquals(2, feedControls.length); - -activities.setFeedControl("", "my app tool"); - -feedControls = activities.getFeedControls(); -test.assertEquals(3, feedControls.length); - - -activities.unsetFeedControl("my site", "my app tool"); -activities.unsetFeedControl("my site", ""); -activities.unsetFeedControl(null, "my app tool"); - -feedControls = activities.getFeedControls(); -test.assertEquals(0, feedControls.length); - - -failure = ""; - -// Return the failure message -failure; +// called by ActivityServiceImplTest.java (test_JSAPI) + +var failure = "did not complete script"; + +// invalid +// activities.postActivity("my activity type", null, null, null); +// activities.postActivity(null, "my site", "my app tool", '{ 000 }'); + +// valid +activities.postActivity("test activity type 4", null, null, '{ "item1" : 123 }'); +activities.postActivity("test activity type 5", "my site", null, '{ "item2" : 456 }'); +activities.postActivity("test activity type 6", "my site", "my app tool", '{ "item3" : 789 }'); +activities.postActivity("test activity type 7", "my site", "my app tool", '{ invalidJSON }'); + +// user feed controls +var feedControls = activities.getFeedControls(); +test.assertEquals(0, feedControls.length); + +activities.setFeedControl("my site", "my app tool"); + +feedControls = activities.getFeedControls(); +test.assertEquals(1, feedControls.length); + +activities.setFeedControl("my site", null); + +feedControls = activities.getFeedControls(); +test.assertEquals(2, feedControls.length); + +activities.setFeedControl("", "my app tool"); + +feedControls = activities.getFeedControls(); +test.assertEquals(3, feedControls.length); + + +activities.unsetFeedControl("my site", "my app tool"); +activities.unsetFeedControl("my site", ""); +activities.unsetFeedControl(null, "my app tool"); + +feedControls = activities.getFeedControls(); +test.assertEquals(0, feedControls.length); + + +failure = ""; + +// Return the failure message +failure;