diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 7e5bbf522f..af8ce63be2 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -1,984 +1,1009 @@ - - - - - - - - - - - - - - classpath:alfresco/ehcache-transactional.xml - - - - - - - - - - - - - - - - - - - - - - - - org.alfresco.cache.qnameEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.qnameEntityTransactionalCache - - - 500 - - - - - - - - - - - - - - - - - org.alfresco.cache.namespaceEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.namespaceEntityTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.localeIdCache - - - - - - - - - - - - - - - - org.alfresco.cache.localeEntityTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.mimetypeEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.mimetypeEntityTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.encodingEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.encodingEntityTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.immutableEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.immutableEntityTransactionalCache - - - 10000 - - - - - - - - - - - - - - - - - org.alfresco.cache.storeAndNodeIdCache - - - - - - - - - - - - - - - - org.alfresco.storeAndNodeIdTransactionalCache - - - 10000 - - - - - - - - - - - - - - - - - org.alfresco.cache.parentAssocsCache - - - - - - - - - - - - - - - - org.alfresco.parentAssocsTransactionalCache - - + + + + + + + + + + + + + + classpath:alfresco/ehcache-transactional.xml + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.cache.qnameEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.qnameEntityTransactionalCache + + + 500 + + + + + + + + + + + + + + + + + org.alfresco.cache.namespaceEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.namespaceEntityTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.localeIdCache + + + + + + + + + + + + + + + + org.alfresco.cache.localeEntityTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.mimetypeEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.mimetypeEntityTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.encodingEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.encodingEntityTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.immutableEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.immutableEntityTransactionalCache + + + 10000 + + + + + + + + + + + + + + + + + org.alfresco.cache.storeAndNodeIdCache + + + + + + + + + + + + + + + + org.alfresco.storeAndNodeIdTransactionalCache + + + 10000 + + + + + + + + + + + + + + + + + org.alfresco.cache.parentAssocsCache + + + + + + + + + + + + + + + + org.alfresco.parentAssocsTransactionalCache + + 80000 - - - - - - - - - - - - - - - - - org.alfresco.cache.userToAuthorityCache - - - - - - - - - - - - - - - - org.alfresco.userToAuthorityTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.permissionsAccessCache - - - - - - - - - - - - - - - - - org.alfresco.permissionsAccessTransactionalCache - - - 10000 - - - - - - - - - - - - - - - - - org.alfresco.cache.nodeOwnerCache - - - - - - - - - - - - - - - - org.alfresco.nodeOwnerTransactionalCache - - - 10000 - - - - - - - - - - - - - - - - - org.alfresco.cache.personCache - - - - - - - - - - - - - - - - - org.alfresco.personTransactionalCache - - - 1000 - - - - - - - - - - - - - - - - - org.alfresco.cache.ticketsCache - - - - - - - - - - - - - - - - org.alfresco.ticketsTransactionalCache - - - 10 - - - - - - - - - - - - - - - - - - - - - - - - - org.alfresco.cache.avmStoreTransactionalCache - - - 1000 - - - - - - - - - - - - - org.alfresco.cache.avm.avmEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.avmEntityTransactionalCache - - - 5000 - - - - - - - - - - - - - org.alfresco.cache.avm.avmVersionRootEntityCache - - - - - - - - - - - - - - - - org.alfresco.cache.avmVersionRootEntityTransactionalCache - - - 100 - - - - - - - - - - - - - org.alfresco.cache.avm.avmNodeCache - - - - - - - - - - - - - - - - org.alfresco.cache.avmNodeTransactionalCache - - - 5000 - - - - - - - - - - - - - - org.alfresco.cache.avm.avmNodeAspectsCache - - - - - - - - - - - - - - - - org.alfresco.cache.avmNodeAspectsTransactionalCache - - - 5000 - - - - - - - - - - - - - - - - - org.alfresco.repo.webservices.querySessionSharedCache - - - - - - - - - - - - - - - - org.alfresco.repo.webservices.querySessionTransactionalCache - - - 50 - - - - - - - - - - - - - - - - - - org.alfresco.cache.aclCache - - - - - - - - - - - - - - - - org.alfresco.aclTransactionalCache - - - 10000 - - - - - - - - - - - - - - - - - org.alfresco.cache.resourceBundleBaseNamesCache - - - - - - - - - - - - - - - - org.alfresco.resourceBundleBaseNamesTransactionalCache - - - 100 - - - - - - - - - - - - - org.alfresco.cache.loadedResourceBundlesCache - - - - - - - - - - - - - - - - org.alfresco.loadedResourceBundlesTransactionalCache - - - 100 - - - - - - - - - - - - - org.alfresco.cache.messagesCache - - - - - - - - - - - - - - - - org.alfresco.messagesTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - - org.alfresco.cache.compiledModelsCache - - - - - - - - - - - - - - - - org.alfresco.compiledModelsTransactionalCache - - - 100 - - - - - - - - - - - - - org.alfresco.cache.prefixesCache - - - - - - - - - - - - - - - - org.alfresco.prefixesTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.webScriptsRegistryCache - - - - - - - - - - - - - - - - org.alfresco.webScriptsRegistryTransactionalCache - - - 100 - - - - - - - - - - - - - - - - - org.alfresco.cache.routingContentStoreSharedCache - - - - - - - - - - - - - - - - org.alfresco.routingContentStoreTransactionalCache - - - 1000 - - - + + + + + + + + + + + + + + + + + + + + + + org.alfresco.nodeRulesTransactionalCache + + + 2000 + + + + + + + + + + + + + + + + + org.alfresco.cache.userToAuthorityCache + + + + + + + + + + + + + + + + org.alfresco.userToAuthorityTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.permissionsAccessCache + + + + + + + + + + + + + + + + + org.alfresco.permissionsAccessTransactionalCache + + + 10000 + + + + + + + + + + + + + + + + + org.alfresco.cache.nodeOwnerCache + + + + + + + + + + + + + + + + org.alfresco.nodeOwnerTransactionalCache + + + 10000 + + + + + + + + + + + + + + + + + org.alfresco.cache.personCache + + + + + + + + + + + + + + + + + org.alfresco.personTransactionalCache + + + 1000 + + + + + + + + + + + + + + + + + org.alfresco.cache.ticketsCache + + + + + + + + + + + + + + + + org.alfresco.ticketsTransactionalCache + + + 10 + + + + + + + + + + + + + + + + + + + + + + + + + org.alfresco.cache.avmStoreTransactionalCache + + + 1000 + + + + + + + + + + + + + org.alfresco.cache.avm.avmEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.avmEntityTransactionalCache + + + 5000 + + + + + + + + + + + + + org.alfresco.cache.avm.avmVersionRootEntityCache + + + + + + + + + + + + + + + + org.alfresco.cache.avmVersionRootEntityTransactionalCache + + + 100 + + + + + + + + + + + + + org.alfresco.cache.avm.avmNodeCache + + + + + + + + + + + + + + + + org.alfresco.cache.avmNodeTransactionalCache + + + 5000 + + + + + + + + + + + + + + org.alfresco.cache.avm.avmNodeAspectsCache + + + + + + + + + + + + + + + + org.alfresco.cache.avmNodeAspectsTransactionalCache + + + 5000 + + + + + + + + + + + + + + + + + org.alfresco.repo.webservices.querySessionSharedCache + + + + + + + + + + + + + + + + org.alfresco.repo.webservices.querySessionTransactionalCache + + + 50 + + + + + + + + + + + + + + + + + + org.alfresco.cache.aclCache + + + + + + + + + + + + + + + + org.alfresco.aclTransactionalCache + + + 10000 + + + + + + + + + + + + + + + + + org.alfresco.cache.resourceBundleBaseNamesCache + + + + + + + + + + + + + + + + org.alfresco.resourceBundleBaseNamesTransactionalCache + + + 100 + + + + + + + + + + + + + org.alfresco.cache.loadedResourceBundlesCache + + + + + + + + + + + + + + + + org.alfresco.loadedResourceBundlesTransactionalCache + + + 100 + + + + + + + + + + + + + org.alfresco.cache.messagesCache + + + + + + + + + + + + + + + + org.alfresco.messagesTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + + org.alfresco.cache.compiledModelsCache + + + + + + + + + + + + + + + + org.alfresco.compiledModelsTransactionalCache + + + 100 + + + + + + + + + + + + + org.alfresco.cache.prefixesCache + + + + + + + + + + + + + + + + org.alfresco.prefixesTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.webScriptsRegistryCache + + + + + + + + + + + + + + + + org.alfresco.webScriptsRegistryTransactionalCache + + + 100 + + + + + + + + + + + + + + + + + org.alfresco.cache.routingContentStoreSharedCache + + + + + + + + + + + + + + + + org.alfresco.routingContentStoreTransactionalCache + + + 1000 + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml index 1fee1bd1ef..c1bb816998 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml @@ -1,7 +1,7 @@ - @@ -9,7 +9,7 @@ - + @@ -22,7 +22,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -170,14 +170,14 @@ where feed_user_id = #feedUserId# ]]> - + @@ -185,11 +185,11 @@ - + - + - + - + - + - + - + update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified# where sequence_id = #id# and status != #status# - + update alf_activity_post set status = #status#, last_modified=#lastModified# where sequence_id = #id# and status != #status# - + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml index d1c2a68942..93f450d9b2 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/avm-common-SqlMap.xml @@ -181,7 +181,7 @@ - + @@ -195,7 +195,7 @@ - + @@ -697,6 +697,7 @@ avm_version_roots where avm_store_id = ? + order by version_id @@ -720,7 +721,7 @@ where avm_store_id = ? and create_date >= ? - order by version_id; + order by version_id diff --git a/config/alfresco/ibatis/org.hibernate.dialect.PostgreSQLDialect/avm-insert-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.PostgreSQLDialect/avm-insert-SqlMap.xml index a6919cc645..756666a00b 100755 --- a/config/alfresco/ibatis/org.hibernate.dialect.PostgreSQLDialect/avm-insert-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.PostgreSQLDialect/avm-insert-SqlMap.xml @@ -15,7 +15,7 @@ - + diff --git a/config/alfresco/node-services-context.xml b/config/alfresco/node-services-context.xml index 86cf177465..973f08b217 100644 --- a/config/alfresco/node-services-context.xml +++ b/config/alfresco/node-services-context.xml @@ -196,9 +196,6 @@ - diff --git a/config/alfresco/rule-services-context.xml b/config/alfresco/rule-services-context.xml index 6718f04ceb..abf2a404b3 100644 --- a/config/alfresco/rule-services-context.xml +++ b/config/alfresco/rule-services-context.xml @@ -5,7 +5,7 @@ - + @@ -21,9 +21,15 @@ + + + + + + false diff --git a/source/java/org/alfresco/repo/action/ActionModel.java b/source/java/org/alfresco/repo/action/ActionModel.java index 036e380e52..cb983294bc 100644 --- a/source/java/org/alfresco/repo/action/ActionModel.java +++ b/source/java/org/alfresco/repo/action/ActionModel.java @@ -6,6 +6,7 @@ public interface ActionModel { static final String ACTION_MODEL_URI = "http://www.alfresco.org/model/action/1.0"; static final String ACTION_MODEL_PREFIX = "act"; + static final QName TYPE_ACTION_BASE = QName.createQName(ACTION_MODEL_URI, "actionbase"); static final QName TYPE_ACTION = QName.createQName(ACTION_MODEL_URI, "action"); static final QName PROP_DEFINITION_NAME = QName.createQName(ACTION_MODEL_URI, "definitionName"); static final QName PROP_ACTION_TITLE = QName.createQName(ACTION_MODEL_URI, "actionTitle"); diff --git a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java index 1a8df8647d..c3093ef581 100644 --- a/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityPostServiceImpl.java @@ -48,12 +48,6 @@ public class ActivityPostServiceImpl implements ActivityPostService { 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 TenantService tenantService; private int estGridSize = 1; @@ -148,9 +142,9 @@ public class ActivityPostServiceImpl implements ActivityPostService { siteId = ""; } - else if (siteId.length() > MAX_LEN_SITE_ID) + else if (siteId.length() > ActivityPostDAO.MAX_LEN_SITE_ID) { - throw new AlfrescoRuntimeException("Invalid siteId - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteId); + throw new IllegalArgumentException("Invalid siteId - exceeds " + ActivityPostDAO.MAX_LEN_SITE_ID + " chars: " + siteId); } // optional - default to empty string @@ -158,17 +152,17 @@ public class ActivityPostServiceImpl implements ActivityPostService { appTool = ""; } - else if (appTool.length() > MAX_LEN_APP_TOOL_ID) + else if (appTool.length() > ActivityPostDAO.MAX_LEN_APP_TOOL_ID) { - throw new AlfrescoRuntimeException("Invalid app tool - exceeds " + MAX_LEN_APP_TOOL_ID + " chars: " + appTool); + throw new IllegalArgumentException("Invalid app tool - exceeds " + ActivityPostDAO.MAX_LEN_APP_TOOL_ID + " chars: " + appTool); } // required ParameterCheck.mandatoryString("activityType", activityType); - if (activityType.length() > MAX_LEN_ACTIVITY_TYPE) + if (activityType.length() > ActivityPostDAO.MAX_LEN_ACTIVITY_TYPE) { - throw new AlfrescoRuntimeException("Invalid activity type - exceeds " + MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType); + throw new IllegalArgumentException("Invalid activity type - exceeds " + ActivityPostDAO.MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType); } // optional - default to empty string @@ -176,24 +170,24 @@ public class ActivityPostServiceImpl implements ActivityPostService { activityData = ""; } - else if (activityType.length() > MAX_LEN_ACTIVITY_DATA) + else if (activityData.length() > ActivityPostDAO.MAX_LEN_ACTIVITY_DATA) { - throw new AlfrescoRuntimeException("Invalid activity data - exceeds " + MAX_LEN_ACTIVITY_DATA + " chars: " + activityData); + throw new IllegalArgumentException("Invalid activity data - exceeds " + ActivityPostDAO.MAX_LEN_ACTIVITY_DATA + " chars: " + activityData); } // required ParameterCheck.mandatoryString("currentUser", currentUser); - if (currentUser.length() > MAX_LEN_USER_ID) + if (currentUser.length() > ActivityPostDAO.MAX_LEN_USER_ID) { - throw new AlfrescoRuntimeException("Invalid user - exceeds " + MAX_LEN_USER_ID + " chars: " + currentUser); + throw new IllegalArgumentException("Invalid user - exceeds " + ActivityPostDAO.MAX_LEN_USER_ID + " chars: " + currentUser); } } - catch (AlfrescoRuntimeException e) + catch (IllegalArgumentException e) { // log error and throw exception logger.error(e); - throw e; + throw new IllegalArgumentException("Failed to post activity: " + e, e); } try diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java index da1d286ab8..7ae6d78c1a 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java @@ -45,6 +45,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import org.alfresco.repo.domain.activities.ActivityFeedDAO; import org.alfresco.repo.domain.activities.ActivityFeedEntity; import org.alfresco.repo.domain.activities.ActivityPostEntity; import org.alfresco.repo.domain.activities.FeedControlEntity; @@ -113,7 +114,7 @@ public abstract class FeedTaskProcessor Map> siteConnectedUsers = new TreeMap>(); Map templateCache = new TreeMap(); - + // for each activity post ... for (ActivityPostEntity activityPost : activityPosts) { @@ -179,7 +180,7 @@ public abstract class FeedTaskProcessor } } } - + if (fmTemplates.size() == 0) { logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType ); @@ -199,18 +200,17 @@ public abstract class FeedTaskProcessor continue; } + String thisSite = activityPost.getSiteNetwork(); + model.put("activityType", activityPost.getActivityType()); - model.put("siteNetwork", activityPost.getSiteNetwork()); + model.put("siteNetwork", thisSite); 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 + // Get the members of this site - save hammering the repository by reusing cached site members Set connectedUsers = siteConnectedUsers.get(thisSite); if (connectedUsers == null) { @@ -237,7 +237,7 @@ public abstract class FeedTaskProcessor } } } - + try { startTransaction(); @@ -258,15 +258,15 @@ public abstract class FeedTaskProcessor } // filter based on opt-out feed controls (if any) - if (! acceptActivity(activityPost, feedControls)) - { - excludedConnections++; - } - else - { + if (! acceptActivity(activityPost, feedControls)) + { + excludedConnections++; + } + else + { for (String fmTemplate : fmTemplates) { - // determine format - based on template naming convention + // determine format - based on template naming convention String formatFound = null; for (String format : formats) { @@ -275,7 +275,7 @@ public abstract class FeedTaskProcessor formatFound = format; break; } - } + } if (formatFound == null) { @@ -283,48 +283,55 @@ public abstract class FeedTaskProcessor 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()) + 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("")) + { + if (activitySummary.length() > ActivityFeedDAO.MAX_LEN_ACTIVITY_SUMMARY) + { + logger.warn("Skip feed entry (activity post " + activityPost.getId() + ") since activity summary - exceeds " + ActivityFeedDAO.MAX_LEN_ACTIVITY_SUMMARY + " chars: " + activitySummary); + } + else + { + 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()) @@ -366,21 +373,21 @@ public abstract class FeedTaskProcessor 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()); - } + 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 + // 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())); + conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes())); } String result = null; @@ -389,34 +396,35 @@ public abstract class FeedTaskProcessor 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); - } + 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(); }; + 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)) { @@ -516,10 +524,10 @@ public abstract class FeedTaskProcessor return activityTemplates; } - + protected Configuration getFreemarkerConfiguration(RepoCtx ctx) - { - Configuration cfg = new Configuration(); + { + Configuration cfg = new Configuration(); cfg.setObjectWrapper(new DefaultObjectWrapper()); // custom template loader @@ -670,7 +678,7 @@ public abstract class FeedTaskProcessor catch (Exception e) { throw new RuntimeException(e); - } + } } } } 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 9a2d8ed804..b732dfbc61 100644 --- a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java +++ b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java @@ -128,20 +128,23 @@ public class PostLookup for (final ActivityPostEntity activityPost : activityPosts) { - try + final String postUserId = activityPost.getUserId(); + + // MT share + String tenantDomain = tenantService.getUserDomain(postUserId); + + AuthenticationUtil.runAs(new RunAsWork() { - 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 { - public Object doWork() throws Exception + try { + postDAO.startTransaction(); + + JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData())); + + String activityDataStr = null; + if (! jo.isNull("nodeRef")) { String nodeRefStr = jo.getString("nodeRef"); @@ -149,8 +152,7 @@ public class PostLookup // lookup additional node data JSONObject activityData = lookupNode(nodeRef, postUserId, jo); - - activityPost.setActivityData(activityData.toString()); + activityDataStr = activityData.toString(); } else { @@ -161,42 +163,67 @@ public class PostLookup jo.put("firstName", firstLastName.getFirst()); jo.put("lastName", firstLastName.getSecond()); - activityPost.setActivityData(jo.toString()); + activityDataStr = jo.toString(); } } - + + if (activityDataStr != null) + { + activityPost.setActivityData(activityDataStr); + } + + if (activityPost.getActivityData().length() > ActivityPostDAO.MAX_LEN_ACTIVITY_DATA) + { + throw new IllegalArgumentException("Invalid activity data - exceeds " + ActivityPostDAO.MAX_LEN_ACTIVITY_DATA + " chars: " + activityPost.getActivityData()); + } + + if (activityPost.getSiteNetwork().length() > ActivityPostDAO.MAX_LEN_SITE_ID) + { + // belts-and-braces - should not get here since checked during post (and not modified) + throw new IllegalArgumentException("Invalid siteId - exceeds " + ActivityPostDAO.MAX_LEN_SITE_ID + " chars: " + activityPost.getSiteNetwork()); + } + 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; + postDAO.commitTransaction(); } - }, 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 (IllegalArgumentException 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 (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(); + } + + return null; + } + }, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); } } catch (SQLException e) diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index f6c72b31e8..d8d6aac891 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -1284,7 +1284,8 @@ public class AVMRepository List result = new ArrayList(storeEntities.size()); for (AVMStoreEntity storeEntity : storeEntities) { - AVMStore store = new AVMStoreImpl(); + AVMStoreImpl store = new AVMStoreImpl(); + store.setId(storeEntity.getId()); store.setName(storeEntity.getName()); result.add(store.getDescriptor()); } diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index f477bfdf6f..b0732c4927 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -43,6 +43,7 @@ import org.alfresco.repo.avm.util.RawServices; import org.alfresco.repo.avm.util.SimplePath; import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.PropertyValue; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.ACLCopyMode; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.avm.AVMBadArgumentException; @@ -1273,8 +1274,8 @@ public class AVMStoreImpl implements AVMStore { // Get the creator ensuring that nulls are not hit PropertyValue creatorValue = getProperty(ContentModel.PROP_CREATOR); - String creator = creatorValue == null ? "system" : (String) creatorValue.getValue(DataTypeDefinition.TEXT); - creator = (creator == null) ? "system" : creator; + String creator = (creatorValue == null ? AuthenticationUtil.SYSTEM_USER_NAME : (String) creatorValue.getValue(DataTypeDefinition.TEXT)); + creator = (creator == null ? AuthenticationUtil.SYSTEM_USER_NAME : creator); // Get the created date ensuring that nulls are not hit PropertyValue createdValue = getProperty(ContentModel.PROP_CREATED); Date created = createdValue == null ? (new Date()) : (Date) createdValue.getValue(DataTypeDefinition.DATE); diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java b/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java index abcd3756cd..fc33acf6e6 100644 --- a/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java +++ b/source/java/org/alfresco/repo/content/transform/magick/ImageTransformationOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2007 Alfresco Software Limited. + * 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 @@ -78,4 +78,16 @@ public class ImageTransformationOptions extends TransformationOptions { return resizeOptions; } + + @Override + public String toString() + { + StringBuilder msg = new StringBuilder(100); + msg.append(this.getClass().getSimpleName()) + .append("[ commandOptions=").append(commandOptions) + .append(", resizeOptions=").append(resizeOptions) + .append("]"); + + return msg.toString(); + } } diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java b/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java index b8ba5b1ab8..ec9ebbe771 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java @@ -33,6 +33,13 @@ import java.util.List; */ public interface ActivityFeedDAO extends ActivitiesDAO { + public static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id + public static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network + public static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type + public static final int MAX_LEN_ACTIVITY_SUMMARY = 4000; // needs to match schema: activity_summary + public static final int MAX_LEN_ACTIVITY_FORMAT = 255; // needs to match schema: activity_format + public static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool + public long insertFeedEntry(ActivityFeedEntity activityFeed) throws SQLException; public int deleteFeedEntries(Date keepDate) throws SQLException; diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java b/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java index 8771672a68..ae7e631d75 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityPostDAO.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * 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 @@ -33,6 +33,12 @@ import java.util.List; */ public interface ActivityPostDAO extends ActivitiesDAO { + public static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id + public static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network + public static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type + public static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data + public static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool + public List selectPosts(ActivityPostEntity activityPost) throws SQLException; public Long getMaxActivitySeq() throws SQLException; diff --git a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMVersionRootDAOImpl.java b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMVersionRootDAOImpl.java index 1adb299f57..ba020a7e1d 100644 --- a/source/java/org/alfresco/repo/domain/avm/ibatis/AVMVersionRootDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/avm/ibatis/AVMVersionRootDAOImpl.java @@ -141,20 +141,18 @@ public class AVMVersionRootDAOImpl extends AbstractAVMVersionRootDAOImpl @SuppressWarnings("unchecked") protected List getVersionRootEntitiesByTo(long storeId, long to) { - Map params = new HashMap(3); + Map params = new HashMap(2); params.put("id", storeId); params.put("to", to); - params.put("from", null); return (List) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_TO, params); } @SuppressWarnings("unchecked") protected List getVersionRootEntitiesByFrom(long storeId, long from) { - Map params = new HashMap(3); + Map params = new HashMap(2); params.put("id", storeId); params.put("from", from); - params.put("to", null); return (List) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_FROM, params); } diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index bb24c541f9..781996c383 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -581,6 +581,7 @@ assoc.id + select parent.id, diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java index 6b2f566fe8..681bbcf806 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -1008,54 +1008,92 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest { private NodeService nodeService; private List deletedNodeRefs; + private List beforeDeleteNodeRefs; - public BadOnDeleteNodePolicy(NodeService nodeService, List deletedNodeRefs) + private boolean onDeleteCreateChild = true; + + public BadOnDeleteNodePolicy(NodeService nodeService, + List beforeDeleteNodeRefs, + List deletedNodeRefs) { + + this.nodeService = nodeService; + this.beforeDeleteNodeRefs = beforeDeleteNodeRefs; this.deletedNodeRefs = deletedNodeRefs; } public void beforeDeleteNode(NodeRef nodeRef) { - // add a new child to the child, i.e. just before it is deleted - ChildAssociationRef assocRef = nodeService.createNode( + // add the child to the list + beforeDeleteNodeRefs.add(nodeRef); + + if(onDeleteCreateChild) + { + // add a new child to the child, i.e. just before it is deleted + ChildAssociationRef assocRef = nodeService.createNode( nodeRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("pre-delete new child"), ContentModel.TYPE_CONTAINER); - // set some child node properties - nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true"); - // add an aspect to the child - nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null); + // set some child node properties + nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true"); + // add an aspect to the child + nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null); + } + } public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode) { // add the child to the list deletedNodeRefs.add(childAssocRef.getChildRef()); - // now perform some nasties on the node's parent, i.e. add a new child - NodeRef parentRef = childAssocRef.getParentRef(); - NodeRef childRef = childAssocRef.getChildRef(); - ChildAssociationRef assocRef = nodeService.createNode( + + if(onDeleteCreateChild) + { + // now perform some nasties on the node's parent, i.e. add a new child + NodeRef parentRef = childAssocRef.getParentRef(); + NodeRef childRef = childAssocRef.getChildRef(); + ChildAssociationRef assocRef = nodeService.createNode( parentRef, ASSOC_TYPE_QNAME_TEST_CHILDREN, QName.createQName("post-delete new child"), ContentModel.TYPE_CONTAINER); + } + } + + private void setOnDeleteCreateChild(boolean onDeleteCreateChild) + { + this.onDeleteCreateChild = onDeleteCreateChild; + } + + private boolean isOnDeleteCreateChild() + { + return onDeleteCreateChild; } } public void testDelete() throws Exception { + final List beforeDeleteNodeRefs = new ArrayList(5); final List deletedNodeRefs = new ArrayList(5); - NodeServicePolicies.OnDeleteNodePolicy policy = new BadOnDeleteNodePolicy(nodeService, deletedNodeRefs); + BadOnDeleteNodePolicy nasty = new BadOnDeleteNodePolicy(nodeService, beforeDeleteNodeRefs, deletedNodeRefs); + nasty.setOnDeleteCreateChild(false); + NodeServicePolicies.OnDeleteNodePolicy policy = nasty; + // bind to listen to the deletion of a node policyComponent.bindClassBehaviour( QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), policy, new JavaBehaviour(policy, "onDeleteNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), + policy, + new JavaBehaviour(policy, "beforeDeleteNode")); + // build the node and commit the node graph Map assocRefs = buildNodeGraph(nodeService, rootNodeRef); NodeRef n1Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "root_p_n1")).getChildRef(); @@ -1072,11 +1110,86 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest assertEquals("Node not cascade deleted", 0, countNodesByReference(n6Ref)); assertEquals("Node not cascade deleted", 0, countNodesByReference(n8Ref)); + // check before delete delete policy has been called + assertTrue("n1Ref before delete policy not called", beforeDeleteNodeRefs.contains(n1Ref)); + assertTrue("n3Ref before delete policy not called", beforeDeleteNodeRefs.contains(n3Ref)); + assertTrue("n6Ref before delete policy not called", beforeDeleteNodeRefs.contains(n6Ref)); + assertTrue("n8Ref before delete policy not called", beforeDeleteNodeRefs.contains(n8Ref)); + + // check delete policy has been called + assertTrue("n1Ref delete policy not called", deletedNodeRefs.contains(n1Ref)); + assertTrue("n3Ref delete policy not called", deletedNodeRefs.contains(n3Ref)); + assertTrue("n6Ref delete policy not called", deletedNodeRefs.contains(n6Ref)); + assertTrue("n8Ref delete policy not called", deletedNodeRefs.contains(n8Ref)); + // commit to check setComplete(); endTransaction(); } +// /** +// * This test is similar to the test above but the delete policies do nasty stuff such as +// * creating children of the soon to be deleted children. +// * +// * In particular, it verifies that we don't get stuck in an infinite loop. +// * @throws Exception +// */ +// public void testDeleteWithBadlyBehavedPolicies() throws Exception +// { +// try +// { +// final List beforeDeleteNodeRefs = new ArrayList(5); +// final List deletedNodeRefs = new ArrayList(5); +// +// BadOnDeleteNodePolicy nasty = new BadOnDeleteNodePolicy(nodeService, beforeDeleteNodeRefs, deletedNodeRefs); +// nasty.setOnDeleteCreateChild(true); +// NodeServicePolicies.OnDeleteNodePolicy policy = nasty; +// +// // bind to listen to the deletion of a node +// policyComponent.bindClassBehaviour( +// QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), +// policy, +// new JavaBehaviour(policy, "onDeleteNode")); +// +// policyComponent.bindClassBehaviour( +// QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"), +// policy, +// new JavaBehaviour(policy, "beforeDeleteNode")); +// +// // build the node and commit the node graph +// Map assocRefs = buildNodeGraph(nodeService, rootNodeRef); +// NodeRef n1Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "root_p_n1")).getChildRef(); +// NodeRef n3Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n1_p_n3")).getChildRef(); +// NodeRef n4Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n2_p_n4")).getChildRef(); +// NodeRef n6Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n3_p_n6")).getChildRef(); +// NodeRef n8Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "n6_p_n8")).getChildRef(); +// +// // delete n1 +// nodeService.deleteNode(n1Ref); +// +// // turn off nasty policy - may upset other tests +// nasty.setOnDeleteCreateChild(false); +// +// // Just a cut down set of tests to validate that something has happened, the real point of the test is to see how +// // the end of the transaction fails. +// +// assertEquals("Node not directly deleted", 0, countNodesByReference(n1Ref)); +// assertTrue("n1Ref before delete policy not called", beforeDeleteNodeRefs.contains(n1Ref)); +// assertTrue("n1Ref delete policy not called", deletedNodeRefs.contains(n1Ref)); +// +// // commit to check +// setComplete(); +// endTransaction(); +// fail("test has not detected orphan children"); +// } +// catch (Exception e) +// { +// // We expect to get here with this test. +// e.printStackTrace(); +// } +// } + + @SuppressWarnings("unchecked") private int countChildrenOfNode(NodeRef nodeRef) { diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 495d1e3700..bc3689c24a 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -42,12 +42,12 @@ import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.Node; import org.alfresco.repo.node.AbstractNodeServiceImpl; import org.alfresco.repo.node.StoreArchiveMap; -import org.alfresco.repo.node.cleanup.AbstractNodeCleanupWorker; -import org.alfresco.repo.node.db.NodeDaoService.NodeRefQueryCallback; import org.alfresco.repo.node.index.NodeIndexer; import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.TransactionListener; +import org.alfresco.repo.transaction.TransactionListenerAdapter; +import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; @@ -94,13 +94,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private NodeDaoService nodeDaoService; private StoreArchiveMap storeArchiveMap; private NodeService avmNodeService; - private NodeIndexer nodeIndexer; - private boolean cascadeInTransaction; + private NodeIndexer nodeIndexer; + private final static String KEY_PRE_COMMIT_ADD_NODE = "DbNodeServiceImpl.PreCommitAddNode"; public DbNodeServiceImpl() { storeArchiveMap = new StoreArchiveMap(); // in case it is not set - cascadeInTransaction = true; } public void setNodeDaoService(NodeDaoService nodeDaoService) @@ -128,15 +127,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } /** - * Set whether store delete and archive operations must cascade to all children - * in the same transaction. - * - * @param cascadeInTransaction true (default) to cascade during - * delete and archive + * @deprecated the functionality did not see wide enough usage to warrant the maintenance */ public void setCascadeInTransaction(boolean cascadeInTransaction) { - this.cascadeInTransaction = cascadeInTransaction; + logger.warn("NodeService property 'cascadeInTransaction' is no longer available."); } /** @@ -339,12 +334,81 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl addMissingAspects(childNodePair, propertiesBefore, propertiesAfter); addMissingAspects(parentNodePair, assocTypeQName); + /** + * track new node ref so we can validate its path. + * + * it may be valid now, but who knows what will happen between + * now and commit! + */ + trackNewNodeRef(childAssocRef.getChildRef()); + // Index nodeIndexer.indexCreateNode(childAssocRef); // done return childAssocRef; } + + /** + * Track a new node ref so we can validate its path at commit time. + * + * It may have a valid path now, but who knows what will happen between + * now and commit! + * + * @param newNodeRef the node to track + */ + private void trackNewNodeRef(NodeRef newNodeRef) + { +// // bind a pre-commit listener to validate any new node associations +// Set newNodes = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE); +// if (newNodes.size() == 0) +// { +// PreCommitNewNodeListener listener = new PreCommitNewNodeListener(); +// AlfrescoTransactionSupport.bindListener(listener); +// } +// newNodes.add(newNodeRef); + } + + /** + * loose interest in tracking a node ref + * + * for example if its been deleted or moved + * @param nodeRef the node ref to untrack + */ + private void untrackNodeRef(NodeRef nodeRef) + { +// Set newNodes = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE); +// if (newNodes.size() > 0) +// { +// newNodes.remove(nodeRef); +// } + } + + private class PreCommitNewNodeListener extends TransactionListenerAdapter + { + @Override + public void beforeCommit(boolean readOnly) + { + if (readOnly) + { + return; + } + Set nodeRefs = TransactionalResourceHelper.getSet(KEY_PRE_COMMIT_ADD_NODE); +// for (NodeRef nodeRef : nodeRefs) +// { +// // Need to check for exists the node may be created +// // and deleted within the same transaction +// if(exists(nodeRef)) +// { +// System.out.println("Checking bideRef " + nodeRef); +// // Check that the primary path is valid for this node +// getPaths(nodeRef, false); +// } +// } + nodeRefs.clear(); + } + } + /** * Adds all the default aspects and properties required for the given type. @@ -798,10 +862,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl invokeBeforeDeleteNode(nodeRef); // Cascade delecte as required - if (cascadeInTransaction) - { - deletePrimaryChildrenNotArchived(nodePair, true); - } + deletePrimaryChildrenNotArchived(nodePair); // perform a normal deletion nodeDaoService.deleteNode(nodeId); // Invoke policy behaviours @@ -820,17 +881,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl */ archiveNode(nodeRef, archiveStoreRef); } + + // remove the deleted node from the list of new nodes + untrackNodeRef(nodeRef); + } /** * delete primary children - private method for deleteNode. * * recurses through children when deleting a node. Does not archive. - * - * @param nodePair - * @param cascade */ - private void deletePrimaryChildrenNotArchived(Pair nodePair, boolean cascade) + private void deletePrimaryChildrenNotArchived(Pair nodePair) { Long nodeId = nodePair.getFirst(); // Get the node's primary children @@ -859,10 +921,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl }; // Get all the QNames to remove - nodeDaoService.getPrimaryChildAssocs(nodeId, callback); - // Each child must be deleted - for (Pair childNodePair : childNodePairs) - { + nodeDaoService.getPrimaryChildAssocs(nodeId, callback); + // Each child must be deleted + for (Pair childNodePair : childNodePairs) + { // Fire node policies. This ensures that each node in the hierarchy gets a notification fired. Long childNodeId = childNodePair.getFirst(); NodeRef childNodeRef = childNodePair.getSecond(); @@ -872,16 +934,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl invokeBeforeDeleteNode(childNodeRef); - // Cascade first, if required. + // Cascade first // This ensures that the beforeDelete policy is fired for all nodes in the hierarchy before // the actual delete starts. - if (cascade) - { - deletePrimaryChildrenNotArchived(childNodePair, true); - } + deletePrimaryChildrenNotArchived(childNodePair); // Delete the child nodeDaoService.deleteNode(childNodeId); invokeOnDeleteNode(childParentAssocRef, childNodeType, childNodeQNames, false); + + // loose interest in tracking this node ref + untrackNodeRef(childNodeRef); } } @@ -2197,12 +2259,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl invokeOnMoveNode(oldParentAssocRef, newParentAssocRef); } - // If we have to cascade in the transaction, then pull the children over to the new store - if (cascadeInTransaction) - { - // Pull children to the new store - pullNodeChildrenToSameStore(newNodeToMovePair, true, true); - } + // Pull children to the new store + pullNodeChildrenToSameStore(newNodeToMovePair, true); // Done return newParentAssocRef; @@ -2231,7 +2289,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * do not need to be remade. If the children are in the same store, only the indexChildren * value is needed. */ - private void pullNodeChildrenToSameStore(Pair nodePair, boolean cascade, boolean indexChildren) + private void pullNodeChildrenToSameStore(Pair nodePair, boolean indexChildren) { Long nodeId = nodePair.getFirst(); NodeRef nodeRef = nodePair.getSecond(); @@ -2291,7 +2349,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index if (indexChildren) { - nodeIndexer.indexDeleteNode(oldParentAssocPair.getSecond()); nodeIndexer.indexCreateNode(newParentAssocPair.getSecond()); } else @@ -2302,11 +2359,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Fire node policies. This ensures that each node in the hierarchy gets a notification fired. invokeOnDeleteNode(oldParentAssocPair.getSecond(), childNodeTypeQName, childNodeAspectQNames, true); invokeOnCreateNode(newParentAssocPair.getSecond()); - // Cascade, if required - if (cascade) - { - pullNodeChildrenToSameStore(newChildNodePair, cascade, indexChildren); - } + // Cascade + pullNodeChildrenToSameStore(newChildNodePair, indexChildren); } } @@ -2418,107 +2472,4 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl nodeDaoService.setChildNameUnique(assocId, newName); } } - - public static class MoveChildrenToCorrectStore extends AbstractNodeCleanupWorker - { - @Override - protected List doCleanInternal() throws Throwable - { - return dbNodeService.moveChildrenToCorrectStore(); - } - }; - - private List moveChildrenToCorrectStore() - { - List results = new ArrayList(1000); - // Repeat the process for each store - List> storePairs = nodeDaoService.getStores(); - for (Pair storePair : storePairs) - { - List storeResults = moveChildrenToCorrectStore(storePair.getFirst()); - results.addAll(storeResults); - } - return results; - } - - private List moveChildrenToCorrectStore(final Long storeId) - { - final List> parentNodePairs = new ArrayList>(100); - final NodeRefQueryCallback callback = new NodeRefQueryCallback() - { - public boolean handle(Pair nodePair) - { - parentNodePairs.add(nodePair); - return true; - } - }; - RetryingTransactionCallback getNodesCallback = new RetryingTransactionCallback() - { - public Object execute() throws Throwable - { - nodeDaoService.getNodesWithChildrenInDifferentStore(storeId, Long.MIN_VALUE, 100, callback); - // Done - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(getNodesCallback, true, true); - // Process the nodes in random order - Collections.shuffle(parentNodePairs); - // Iterate and operate - List results = new ArrayList(100); - for (final Pair parentNodePair : parentNodePairs) - { - RetryingTransactionCallback fixNodesCallback = new RetryingTransactionCallback() - { - public String execute() throws Throwable - { - // Pull the children to the same store with full indexing - but don't cascade. - pullNodeChildrenToSameStore(parentNodePair, true, true); - // Done - return null; - } - }; - RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); - txnHelper.setMaxRetries(1); - try - { - txnHelper.doInTransaction(fixNodesCallback, false, true); - String msg = - "Moved child nodes to parent node's store: \n" + - " Parent node: " + parentNodePair.getFirst(); - results.add(msg); - } - catch (Throwable e) - { - String msg = - "Failed to move child nodes to parent node's store." + - " Set log level to WARN for this class to get exception log: \n" + - " Parent node: " + parentNodePair.getFirst() + "\n" + - " Error: " + e.getMessage(); - // It failed; do a full log in WARN mode - if (logger.isWarnEnabled()) - { - logger.warn(msg, e); - } - else - { - logger.error(msg); - } - results.add(msg); - } - } - // Done - if (logger.isDebugEnabled()) - { - StringBuilder sb = new StringBuilder(256); - sb.append("Moved children to correct stores: \n") - .append(" Results:\n"); - for (String msg : results) - { - sb.append(" ").append(msg).append("\n"); - } - logger.debug(sb.toString()); - } - return results; - } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java index 41b5fa409d..ba987c9f1f 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java @@ -38,8 +38,6 @@ import org.springframework.extensions.surf.util.I18NUtil; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.node.BaseNodeServiceTest; -import org.alfresco.repo.node.StoreArchiveMap; -import org.alfresco.repo.node.cleanup.NodeCleanupRegistry; import org.alfresco.repo.node.db.NodeDaoService.NodePropertyHandler; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -50,7 +48,6 @@ import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; @@ -73,7 +70,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest { // Force cascading DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl"); - dbNodeServiceImpl.setCascadeInTransaction(true); return (NodeService) applicationContext.getBean("dbNodeService"); } @@ -402,49 +398,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest nodeService.deleteNode(nodeRef); } - public void testCleanup() throws Exception - { - @SuppressWarnings("unchecked") - StoreArchiveMap storeArchiveMap = (StoreArchiveMap) applicationContext.getBean("storeArchiveMap"); - DbNodeServiceImpl ns = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl"); - ns.setCascadeInTransaction(false); - - NodeRef parentNodeRef = nodeService.createNode( - rootNodeRef, - ASSOC_TYPE_QNAME_TEST_CHILDREN, - QName.createQName(NAMESPACE, this.getName()), - ContentModel.TYPE_FOLDER).getChildRef(); - NodeRef childNodeRef = nodeService.createNode( - parentNodeRef, - ContentModel.ASSOC_CONTAINS, - QName.createQName(NAMESPACE, this.getName()), - ContentModel.TYPE_FOLDER).getChildRef(); - - // Ensure that the archive feature is enabled - StoreRef archiveStoreRef = ns.createStore("test", getName() + "-" + System.currentTimeMillis()); - storeArchiveMap.put(parentNodeRef.getStoreRef(), archiveStoreRef); - - // Delete parent. Cascade is OFF, so children should be left in their current store. - ns.deleteNode(parentNodeRef); - // Check that the node n1 is in the archive store - assertFalse("Parent should be deleted", ns.exists(parentNodeRef)); - NodeRef parentArchiveRef = new NodeRef(archiveStoreRef, parentNodeRef.getId()); - assertTrue("Parent should be in the archive store", ns.exists(parentArchiveRef)); - - // Force a commit here - setComplete(); - endTransaction(); - - NodeCleanupRegistry nodeCleanupRegistry = new NodeCleanupRegistry(); - DbNodeServiceImpl.MoveChildrenToCorrectStore worker = new DbNodeServiceImpl.MoveChildrenToCorrectStore(); - worker.setTransactionService(transactionService); - worker.setDbNodeService(ns); - worker.setNodeDaoService(nodeDaoService); - - // Run cleanup - worker.doClean(); - } - /** * Adds a property to a node and checks that it can be found using the low-level DB query */ diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index 25cbcd05c5..a158706155 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -357,6 +357,8 @@ public interface NodeDaoService * @param minNodeId the min node ID to return * @param count the maximum number of results * @param resultsCallback the node callback + * + * @deprecated Since 2.2SP6, 3.1SP2, 3.2: not performant and not relevant to any use-cases */ @DirtySessionAnnotation(markDirty=false) public void getNodesWithChildrenInDifferentStore( diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index bc4dc90c83..b50b7313ee 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -3232,6 +3232,9 @@ public class HibernateNodeDaoServiceImpl // Done } + /** + * @deprecated Not performant. Do not use. + */ public void getNodesWithChildrenInDifferentStore( final Long storeId, final Long minNodeId, diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index f3e6c08d0a..3afd90db0e 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -26,6 +26,7 @@ package org.alfresco.repo.rule; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -35,6 +36,11 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.ActionModel; import org.alfresco.repo.action.RuntimeActionService; +import org.alfresco.repo.cache.NullCache; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.service.cmr.action.Action; @@ -51,6 +57,7 @@ import org.alfresco.service.cmr.rule.RuleServiceException; import org.alfresco.service.cmr.rule.RuleType; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.GUID; @@ -67,7 +74,12 @@ import org.apache.commons.logging.LogFactory; * * @author Roy Wetherall */ -public class RuleServiceImpl implements RuleService, RuntimeRuleService +public class RuleServiceImpl + implements RuleService, RuntimeRuleService, + NodeServicePolicies.BeforeCreateChildAssociationPolicy, + NodeServicePolicies.OnCreateNodePolicy, + NodeServicePolicies.OnUpdateNodePolicy, + NodeServicePolicies.OnAddAspectPolicy { /** key against which to store rules pending on the current transaction */ private static final String KEY_RULES_PENDING = "RuleServiceImpl.PendingRules"; @@ -79,44 +91,30 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService private String ASSOC_NAME_RULES_PREFIX = "rules"; private RegexQNamePattern ASSOC_NAME_RULES_REGEX = new RegexQNamePattern(RuleModel.RULE_MODEL_URI, "^" + ASSOC_NAME_RULES_PREFIX + ".*"); - /** - * The logger - */ private static Log logger = LogFactory.getLog(RuleServiceImpl.class); - /** - * The permission-safe node service - */ private NodeService nodeService; - - /** - * The runtime node service (ignores permissions) - */ private NodeService runtimeNodeService; - - /** - * The action service - */ private ActionService actionService; - - /** - * The dictionary service - */ private DictionaryService dictionaryService; - - /** - * The permission service - */ + private PolicyComponent policyComponent; private PermissionService permissionService; /** * The action service implementation which we need for some things. */ - RuntimeActionService runtimeActionService; + private RuntimeActionService runtimeActionService; + + /** + * Cache of raw rules (not inherited or interpreted) for a given node + */ + private SimpleCache> nodeRulesCache; /** * List of disabled node refs. The rules associated with these nodes will node be added to the pending list, and * therefore not fired. This list is transient. + * + * TODO: (DH) Make this txn-local */ private Set disabledNodeRefs = new HashSet(5); @@ -148,8 +146,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * Set the permission-safe node service - * - * @param nodeService the permission-safe node service */ public void setNodeService(NodeService nodeService) { @@ -158,8 +154,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * Set the direct node service - * - * @param nodeService the node service */ public void setRuntimeNodeService(NodeService runtimeNodeService) { @@ -168,8 +162,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * Set the action service - * - * @param actionService the action service */ public void setActionService(ActionService actionService) { @@ -178,8 +170,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * Set the runtime action service - * - * @param actionRegistration the action service */ public void setRuntimeActionService(RuntimeActionService runtimeActionService) { @@ -188,24 +178,41 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * Set the dictionary service - * - * @param dictionaryService the dictionary service */ public void setDictionaryService(DictionaryService dictionaryService) { this.dictionaryService = dictionaryService; } + /** + * Set the policy component to listen for various events + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + /** * Set the permission service - * - * @param permissionService the permission service */ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } + /** + * Set the cache to hold node's individual rules. This cache must not be shared + * across transactions. + * + * @param nodeRulesCache a cache of raw rules contained on a node + * + * @see NullCache + */ + public void setNodeRulesCache(SimpleCache> nodeRulesCache) + { + this.nodeRulesCache = nodeRulesCache; + } + /** * Set the global rules disabled flag * @@ -216,6 +223,88 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService this.globalRulesDisabled = rulesDisabled; } + /** + * Registers to listen for events of interest. For instance, the creation or deletion of a rule folder + * will affect the caching of rules. + */ + public void init() + { + policyComponent.bindAssociationBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "beforeCreateChildAssociation"), + RuleModel.ASPECT_RULES, + RuleModel.ASSOC_RULE_FOLDER, + new JavaBehaviour(this, "beforeCreateChildAssociation")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"), + RuleModel.ASPECT_RULES, + new JavaBehaviour(this, "onAddAspect")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateNode"), + RuleModel.ASPECT_RULES, + new JavaBehaviour(this, "onUpdateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + RuleModel.TYPE_RULE, + new JavaBehaviour(this, "onCreateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateNode"), + RuleModel.TYPE_RULE, + new JavaBehaviour(this, "onUpdateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + ActionModel.TYPE_ACTION_BASE, + new JavaBehaviour(this, "onCreateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateNode"), + ActionModel.TYPE_ACTION_BASE, + new JavaBehaviour(this, "onUpdateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), + ActionModel.TYPE_ACTION_PARAMETER, + new JavaBehaviour(this, "onCreateNode")); + policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateNode"), + ActionModel.TYPE_ACTION_PARAMETER, + new JavaBehaviour(this, "onUpdateNode")); + } + + /** + * Cache invalidation + */ + public void beforeCreateChildAssociation( + NodeRef parentNodeRef, + NodeRef childNodeRef, + QName assocTypeQName, + QName assocQName, + boolean isNewNode) + { + nodeRulesCache.clear(); + } + + /** + * Cache invalidation + */ + public void onUpdateNode(NodeRef nodeRef) + { + nodeRulesCache.clear(); + } + + /** + * Cache invalidation + */ + public void onCreateNode(ChildAssociationRef childAssocRef) + { + nodeRulesCache.clear(); + } + + /** + * Cache invalidation + */ + public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName) + { + nodeRulesCache.clear(); + } + /** * Gets the saved rule folder reference * @@ -355,60 +444,78 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService { List rules = new ArrayList(); - if (this.runtimeNodeService.exists(nodeRef) == true && checkNodeType(nodeRef) == true) + if (!this.runtimeNodeService.exists(nodeRef) || !checkNodeType(nodeRef)) { - if (includeInherited == true && this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false) + // Node has gone or is not the correct type + return rules; + } + if (includeInherited == true && this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false) + { + // Get any inherited rules + for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null)) { - // Get any inherited rules - for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null)) + // Ensure rules are not duplicated in the list + if (rules.contains(rule) == false) { - // Ensure rules are not duplicated in the list - if (rules.contains(rule) == false) - { - rules.add(rule); - } + rules.add(rule); } } + } - // Extra check of CONSUMER permission was added to rule selection, - // to prevent Access Denied Exception due to the bug: - // https://issues.alfresco.com/browse/ETWOTWO-438 - - if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true && - permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED) + // Get the node's own rules and add them to the list + List nodeRules = getRulesForNode(nodeRef); + for (Rule rule : nodeRules) + { + if ((rules.contains(rule) == false) && + (ruleTypeName == null || rule.getRuleTypes().contains(ruleTypeName) == true)) { - NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); - if (ruleFolder != null) - { - List allRules = new ArrayList(); - - // Get the rules for this node - List ruleChildAssocRefs = - this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX); - for (ChildAssociationRef ruleChildAssocRef : ruleChildAssocRefs) - { - // Create the rule and add to the list - NodeRef ruleNodeRef = ruleChildAssocRef.getChildRef(); - Rule rule = getRule(ruleNodeRef); - allRules.add(rule); - } - - // Build the list of rules that is returned to the client - for (Rule rule : allRules) - { - if ((rules.contains(rule) == false) && - (ruleTypeName == null || rule.getRuleTypes().contains(ruleTypeName) == true)) - { - rules.add(rule); - } - } - } + rules.add(rule); } } return rules; } + private List getRulesForNode(NodeRef nodeRef) + { + // Extra check of CONSUMER permission was added to rule selection, + // to prevent Access Denied Exception due to the bug: + // https://issues.alfresco.com/browse/ETWOTWO-438 + + if (!runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) || + permissionService.hasPermission(nodeRef, PermissionService.READ) != AccessStatus.ALLOWED) + { + // Doesn't have the aspect or the user doesn't have access + return Collections.emptyList(); + } + List nodeRules = nodeRulesCache.get(nodeRef); + if (nodeRules != null) + { + // We have already processed this node + return nodeRules; + } + // Not in the cache, so go and get the rules + nodeRules = new ArrayList(); + NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); + if (ruleFolder != null) + { + // Get the rules for this node + List ruleChildAssocRefs = + this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX); + for (ChildAssociationRef ruleChildAssocRef : ruleChildAssocRefs) + { + // Create the rule and add to the list + NodeRef ruleNodeRef = ruleChildAssocRef.getChildRef(); + Rule rule = getRule(ruleNodeRef); + nodeRules.add(rule); + } + } + // Store this in the cache for later re-use + nodeRulesCache.put(nodeRef, nodeRules); + // Done + return nodeRules; + } + /** * @see org.alfresco.service.cmr.rule.RuleService#countRules(org.alfresco.service.cmr.repository.NodeRef) */ @@ -651,6 +758,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService finally { enableRules(); + // Drop the rules from the cache + nodeRulesCache.remove(nodeRef); } } else @@ -728,6 +837,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService enableRules(nodeRef); } } + // Drop the rules from the cache + nodeRulesCache.remove(nodeRef); } else { @@ -757,6 +868,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService } } } + // Drop the rules from the cache + nodeRulesCache.remove(nodeRef); } else { @@ -767,7 +880,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService /** * @see org.alfresco.repo.rule.RuntimeRuleService#addRulePendingExecution(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule) */ - @SuppressWarnings("unchecked") public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule) { addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false); diff --git a/source/java/org/alfresco/service/cmr/activities/FeedControl.java b/source/java/org/alfresco/service/cmr/activities/FeedControl.java index 5dd34102ec..2b7b0ca154 100644 --- a/source/java/org/alfresco/service/cmr/activities/FeedControl.java +++ b/source/java/org/alfresco/service/cmr/activities/FeedControl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Alfresco Software Limited. + * 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 @@ -36,10 +36,16 @@ public class FeedControl implements Serializable public FeedControl(String siteId, String appToolId) { - if (siteId == null) siteId = ""; + if (siteId == null) + { + siteId = ""; + } this.siteId = siteId; - if (appToolId == null) appToolId = ""; + if (appToolId == null) + { + appToolId = ""; + } this.appToolId = appToolId; }