Merged V3.2 to HEAD

17294: Fix for ETHREEOH-3194 - It's impossible to find Blogs and Discussions by title.
   17301: Activity Service fixes (ETHREEOH-1362 & ETHREEOH-1741)
   17302: Fix for ETHREEOH-2849 JPG to GIF transformation fails when using imagemagick cmd line options listed in our wiki
   17305: AVM - fix AVMStoreDescriptor ( creator/createDate) returned by getStores
   17306: Fix for ETHREEOH-1578 - Incorrect behavior of Calendar in Month view ...
   17318: Merged V3.1 to V3.2
      17317: Fix for ETHREEOH-3236 It is impossible to change start location, everything is reverted to My Alfresco
   17320: Merged V3.1 to V3.2
      17287 - Fix for ETHREEOH-110- It is impossible to browse events by tags
   17326: iBatis mapping fixes for AVM on Oracle (including ETHREEOH-3205)
   17327: Merged V3.1 to V3.2
      17324: Fix for ETHREEOH-2723 Script error appears when trying to edit Home Space Name for user
   17329: Merged V3.1 to V3.2
      17180: Merged V2.2 to V3.1
         17164: Fixes for deletion of large hierarchies: (ETHREEOH-2161 and ETHREEOH-2650)
         17179: (RECORD ONLY) Merged V3.1 to V2.2 ...
   17330: Fix for SiteActivityTest failure (caused by earlier -ve test data)
   17331: Merged V3.1 to V3.2
      17190: Further fixes for ETHREEOH-2161: Delete process hangs when deleting large directory structure (with rules applied)
      17207: Fix fallout from work on ETHREEOH-2161: Delete process hangs when deleting large directory structure (with rules applied)
      17215: Added back firing of policies for archive stores
   17351: Build fix check in to DBNodeService.    Will be followed by full fix when available.
   17353: Applied TransactionListenerAdapter
___________________________________________________________________
Modified: svn:mergeinfo
   Reverse-merged /alfresco/BRANCHES/V3.1:r13091
   Merged /alfresco/BRANCHES/V2.2:r13089,13091,14190-14191,14199,14210,14216,14229,14655,14825,14869,17164,17179
   Merged /alfresco/BRANCHES/V3.1:r17180,17190,17207,17215,17287,17317,17324
   Merged /alfresco/BRANCHES/V3.2:r17294,17301-17302,17305-17306,17318,17320,17326-17327,17329-17331,17351,17353


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18056 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2010-01-15 11:09:09 +00:00
parent 354faccbc3
commit e2fbd4a8de
24 changed files with 1671 additions and 1446 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap <!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd"> "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="alfresco.activities"> <sqlMap namespace="alfresco.activities">
@@ -9,7 +9,7 @@
<typeAlias alias="ActivityFeed" type="org.alfresco.repo.domain.activities.ActivityFeedEntity"/> <typeAlias alias="ActivityFeed" type="org.alfresco.repo.domain.activities.ActivityFeedEntity"/>
<typeAlias alias="FeedControl" type="org.alfresco.repo.domain.activities.FeedControlEntity"/> <typeAlias alias="FeedControl" type="org.alfresco.repo.domain.activities.FeedControlEntity"/>
<typeAlias alias="ActivityPost" type="org.alfresco.repo.domain.activities.ActivityPostEntity"/> <typeAlias alias="ActivityPost" type="org.alfresco.repo.domain.activities.ActivityPostEntity"/>
<resultMap id="ActivityFeedResult" class="ActivityFeed"> <resultMap id="ActivityFeedResult" class="ActivityFeed">
<result property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="feedUserId" column="feed_user_id"/> <result property="feedUserId" column="feed_user_id"/>
@@ -22,7 +22,7 @@
<result property="activitySummaryFormat" column="activity_format"/> <result property="activitySummaryFormat" column="activity_format"/>
<result property="feedDate" column="feed_date"/> <result property="feedDate" column="feed_date"/>
</resultMap> </resultMap>
<resultMap id="FeedControlResult" class="FeedControl"> <resultMap id="FeedControlResult" class="FeedControl">
<result property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="id" column="id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="feedUserId" column="feed_user_id"/> <result property="feedUserId" column="feed_user_id"/>
@@ -30,7 +30,7 @@
<result property="appTool" column="app_tool"/> <result property="appTool" column="app_tool"/>
<result property="lastModified" column="last_modified"/> <result property="lastModified" column="last_modified"/>
</resultMap> </resultMap>
<resultMap id="ActivityPostResult" class="ActivityPost"> <resultMap id="ActivityPostResult" class="ActivityPost">
<result property="id" column="sequence_id" jdbcType="BIGINT" javaType="java.lang.Long"/> <result property="id" column="sequence_id" jdbcType="BIGINT" javaType="java.lang.Long"/>
<result property="activityData" column="activity_data"/> <result property="activityData" column="activity_data"/>
@@ -170,14 +170,14 @@
where feed_user_id = #feedUserId# where feed_user_id = #feedUserId#
]]> ]]>
</select> </select>
<select id="select_activity_feedcontrol" parameterClass="FeedControl" resultClass="long"> <select id="select_activity_feedcontrol" parameterClass="FeedControl" resultClass="long">
<![CDATA[ <![CDATA[
select id as id select id as id
from alf_activity_feed_control from alf_activity_feed_control
where feed_user_id = #feedUserId# where feed_user_id = #feedUserId#
and site_network = #siteNetwork# and ((site_network = #siteNetwork#) or ((#siteNetwork# is null) and (site_network is null)))
and app_tool = #appTool# and ((app_tool = #appTool#) or ((#appTool# is null) and (app_tool is null)))
]]> ]]>
</select> </select>
@@ -185,11 +185,11 @@
<![CDATA[ <![CDATA[
delete from alf_activity_feed_control delete from alf_activity_feed_control
where feed_user_id = #feedUserId# where feed_user_id = #feedUserId#
and site_network = #siteNetwork# and ((site_network = #siteNetwork#) or ((#siteNetwork# is null) and (site_network is null)))
and app_tool = #appTool# and ((app_tool = #appTool#) or ((#appTool# is null) and (app_tool is null)))
]]> ]]>
</delete> </delete>
<select id="select_activity_posts" parameterClass="ActivityPost" resultClass="ActivityPost"> <select id="select_activity_posts" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[ <![CDATA[
select select
@@ -211,7 +211,7 @@
status = #status# status = #status#
]]> ]]>
</select> </select>
<select id="select_activity_posts_by_status_only" parameterClass="ActivityPost" resultClass="ActivityPost"> <select id="select_activity_posts_by_status_only" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[ <![CDATA[
select select
@@ -230,25 +230,25 @@
status = #status# status = #status#
]]> ]]>
</select> </select>
<select id="select_activity_post_max_seq" resultClass="long"> <select id="select_activity_post_max_seq" resultClass="long">
select max(sequence_id) as maxId select max(sequence_id) as maxId
from alf_activity_post from alf_activity_post
where status = 'POSTED' where status = 'POSTED'
</select> </select>
<select id="select_activity_post_min_seq" resultClass="long"> <select id="select_activity_post_min_seq" resultClass="long">
select min(sequence_id) as minId select min(sequence_id) as minId
from alf_activity_post from alf_activity_post
where status = 'POSTED' where status = 'POSTED'
</select> </select>
<select id="select_activity_post_max_jobtasknode" resultClass="int"> <select id="select_activity_post_max_jobtasknode" resultClass="int">
select max(job_task_node) as maxJobTaskNode select max(job_task_node) as maxJobTaskNode
from alf_activity_post from alf_activity_post
where status = 'POSTED' where status = 'POSTED'
</select> </select>
<delete id="delete_activity_posts_older_than_date" parameterClass="ActivityPost"> <delete id="delete_activity_posts_older_than_date" parameterClass="ActivityPost">
<![CDATA[ <![CDATA[
delete from alf_activity_post delete from alf_activity_post
@@ -256,17 +256,17 @@
and status = #status# and status = #status#
]]> ]]>
</delete> </delete>
<update id="update_activity_post_data" parameterClass="ActivityPost"> <update id="update_activity_post_data" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified# update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
where sequence_id = #id# where sequence_id = #id#
and status != #status# and status != #status#
</update> </update>
<update id="update_activity_post_status" parameterClass="ActivityPost"> <update id="update_activity_post_status" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, last_modified=#lastModified# update alf_activity_post set status = #status#, last_modified=#lastModified#
where sequence_id = #id# where sequence_id = #id#
and status != #status# and status != #status#
</update> </update>
</sqlMap> </sqlMap>

View File

@@ -181,7 +181,7 @@
<parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/> <parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/>
<parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/> <parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/>
<parameter property="stringValue" jdbcType="TEXT" javaType="java.lang.String"/> <parameter property="stringValue" jdbcType="VARCHAR" javaType="java.lang.String"/>
<parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/> <parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/>
<parameter property="nodeId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="nodeId" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/>
@@ -195,7 +195,7 @@
<parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/> <parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/>
<parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/> <parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/>
<parameter property="stringValue" jdbcType="TEXT" javaType="java.lang.String"/> <parameter property="stringValue" jdbcType="VARCHAR" javaType="java.lang.String"/>
<parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/> <parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/>
<parameter property="avmStoreId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="avmStoreId" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/>
@@ -697,6 +697,7 @@
avm_version_roots avm_version_roots
where where
avm_store_id = ? avm_store_id = ?
order by version_id
</select> </select>
<select id="select_AVMVersionRootsByTo" parameterMap="parameter_IdToMap" resultMap="result_AVMVersionRoot"> <select id="select_AVMVersionRootsByTo" parameterMap="parameter_IdToMap" resultMap="result_AVMVersionRoot">
@@ -708,7 +709,7 @@
where where
avm_store_id = ? and avm_store_id = ? and
create_date <= ? create_date <= ?
order by version_id; order by version_id
]]> ]]>
</select> </select>
@@ -720,7 +721,7 @@
where where
avm_store_id = ? and avm_store_id = ? and
create_date >= ? create_date >= ?
order by version_id; order by version_id
</select> </select>
<select id="select_AVMVersionRootsBetween" parameterMap="parameter_IdFromToMap" resultMap="result_AVMVersionRoot"> <select id="select_AVMVersionRootsBetween" parameterMap="parameter_IdFromToMap" resultMap="result_AVMVersionRoot">
@@ -731,7 +732,7 @@
where where
avm_store_id = ? and avm_store_id = ? and
create_date between ? and ? create_date between ? and ?
order by version_id; order by version_id
</select> </select>
<!-- Optimistic update of the version root --> <!-- Optimistic update of the version root -->

View File

@@ -15,7 +15,7 @@
<parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="longValue" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/> <parameter property="floatValue" jdbcType="FLOAT" javaType="java.lang.Float"/>
<parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/> <parameter property="doubleValue" jdbcType="DOUBLE" javaType="java.lang.Double"/>
<parameter property="stringValue" jdbcType="TEXT" javaType="java.lang.String"/> <parameter property="stringValue" jdbcType="VARCHAR" javaType="java.lang.String"/>
<parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/> <parameter property="serializableValue" jdbcType="BLOB" javaType="java.io.Serializable"/>
<parameter property="avmStoreId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="avmStoreId" jdbcType="BIGINT" javaType="java.lang.Long"/>
<parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/> <parameter property="qnameId" jdbcType="BIGINT" javaType="java.lang.Long"/>

View File

@@ -196,9 +196,6 @@
<ref bean="nodeDaoService" /> <ref bean="nodeDaoService" />
</property> </property>
</bean> </bean>
<bean id="nodeCleanup.moveChildrenToCorrectStore"
class="org.alfresco.repo.node.db.DbNodeServiceImpl$MoveChildrenToCorrectStore"
parent="nodeCleanupBase"/>
<bean id="nodeCleanup.indexChildrenWhereRequired" <bean id="nodeCleanup.indexChildrenWhereRequired"
class="org.alfresco.repo.node.db.IndexChildrenWhereRequiredWorker" class="org.alfresco.repo.node.db.IndexChildrenWhereRequiredWorker"
parent="nodeCleanupBase"/> parent="nodeCleanupBase"/>

View File

@@ -5,7 +5,7 @@
<!-- Rules Service --> <!-- Rules Service -->
<bean id="ruleService" class="org.alfresco.repo.rule.RuleServiceImpl"> <bean id="ruleService" class="org.alfresco.repo.rule.RuleServiceImpl" init-method="init">
<property name="nodeService"> <property name="nodeService">
<ref bean="NodeService"/> <ref bean="NodeService"/>
</property> </property>
@@ -21,9 +21,15 @@
<property name="dictionaryService"> <property name="dictionaryService">
<ref bean="dictionaryService"/> <ref bean="dictionaryService"/>
</property> </property>
<property name="policyComponent">
<ref bean="policyComponent"/>
</property>
<property name="permissionService"> <property name="permissionService">
<ref bean="permissionService"/> <ref bean="permissionService"/>
</property> </property>
<property name="nodeRulesCache">
<ref bean="nodeRulesCache"/>
</property>
<property name="rulesDisabled"> <property name="rulesDisabled">
<value>false</value> <value>false</value>
</property> </property>

View File

@@ -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_URI = "http://www.alfresco.org/model/action/1.0";
static final String ACTION_MODEL_PREFIX = "act"; 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 TYPE_ACTION = QName.createQName(ACTION_MODEL_URI, "action");
static final QName PROP_DEFINITION_NAME = QName.createQName(ACTION_MODEL_URI, "definitionName"); static final QName PROP_DEFINITION_NAME = QName.createQName(ACTION_MODEL_URI, "definitionName");
static final QName PROP_ACTION_TITLE = QName.createQName(ACTION_MODEL_URI, "actionTitle"); static final QName PROP_ACTION_TITLE = QName.createQName(ACTION_MODEL_URI, "actionTitle");

View File

@@ -48,12 +48,6 @@ public class ActivityPostServiceImpl implements ActivityPostService
{ {
private static final Log logger = LogFactory.getLog(ActivityServiceImpl.class); 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 ActivityPostDAO postDAO;
private TenantService tenantService; private TenantService tenantService;
private int estGridSize = 1; private int estGridSize = 1;
@@ -148,9 +142,9 @@ public class ActivityPostServiceImpl implements ActivityPostService
{ {
siteId = ""; 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 // optional - default to empty string
@@ -158,17 +152,17 @@ public class ActivityPostServiceImpl implements ActivityPostService
{ {
appTool = ""; 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 // required
ParameterCheck.mandatoryString("activityType", activityType); 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 // optional - default to empty string
@@ -176,24 +170,24 @@ public class ActivityPostServiceImpl implements ActivityPostService
{ {
activityData = ""; 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 // required
ParameterCheck.mandatoryString("currentUser", currentUser); 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 // log error and throw exception
logger.error(e); logger.error(e);
throw e; throw new IllegalArgumentException("Failed to post activity: " + e, e);
} }
try try

View File

@@ -45,6 +45,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import org.alfresco.repo.domain.activities.ActivityFeedDAO;
import org.alfresco.repo.domain.activities.ActivityFeedEntity; import org.alfresco.repo.domain.activities.ActivityFeedEntity;
import org.alfresco.repo.domain.activities.ActivityPostEntity; import org.alfresco.repo.domain.activities.ActivityPostEntity;
import org.alfresco.repo.domain.activities.FeedControlEntity; import org.alfresco.repo.domain.activities.FeedControlEntity;
@@ -113,7 +114,7 @@ public abstract class FeedTaskProcessor
Map<String, Set<String>> siteConnectedUsers = new TreeMap<String, Set<String>>(); Map<String, Set<String>> siteConnectedUsers = new TreeMap<String, Set<String>>();
Map<String, Template> templateCache = new TreeMap<String, Template>(); Map<String, Template> templateCache = new TreeMap<String, Template>();
// for each activity post ... // for each activity post ...
for (ActivityPostEntity activityPost : activityPosts) for (ActivityPostEntity activityPost : activityPosts)
{ {
@@ -179,7 +180,7 @@ public abstract class FeedTaskProcessor
} }
} }
} }
if (fmTemplates.size() == 0) if (fmTemplates.size() == 0)
{ {
logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType ); logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType );
@@ -199,18 +200,17 @@ public abstract class FeedTaskProcessor
continue; continue;
} }
String thisSite = activityPost.getSiteNetwork();
model.put("activityType", activityPost.getActivityType()); model.put("activityType", activityPost.getActivityType());
model.put("siteNetwork", activityPost.getSiteNetwork()); model.put("siteNetwork", thisSite);
model.put("userId", activityPost.getUserId()); model.put("userId", activityPost.getUserId());
model.put("id", activityPost.getId()); model.put("id", activityPost.getId());
model.put("date", activityPost.getPostDate()); // post date rather than time that feed is generated model.put("date", activityPost.getPostDate()); // post date rather than time that feed is generated
model.put("xmldate", new ISO8601DateFormatMethod()); model.put("xmldate", new ISO8601DateFormatMethod());
model.put("repoEndPoint", ctx.getRepoEndPoint()); model.put("repoEndPoint", ctx.getRepoEndPoint());
// Get the members of this site // Get the members of this site - save hammering the repository by reusing cached site members
String thisSite = activityPost.getSiteNetwork();
// Save hammering the repository by reusing cached site members
Set<String> connectedUsers = siteConnectedUsers.get(thisSite); Set<String> connectedUsers = siteConnectedUsers.get(thisSite);
if (connectedUsers == null) if (connectedUsers == null)
{ {
@@ -237,7 +237,7 @@ public abstract class FeedTaskProcessor
} }
} }
} }
try try
{ {
startTransaction(); startTransaction();
@@ -258,15 +258,15 @@ public abstract class FeedTaskProcessor
} }
// filter based on opt-out feed controls (if any) // filter based on opt-out feed controls (if any)
if (! acceptActivity(activityPost, feedControls)) if (! acceptActivity(activityPost, feedControls))
{ {
excludedConnections++; excludedConnections++;
} }
else else
{ {
for (String fmTemplate : fmTemplates) for (String fmTemplate : fmTemplates)
{ {
// determine format - based on template naming convention // determine format - based on template naming convention
String formatFound = null; String formatFound = null;
for (String format : formats) for (String format : formats)
{ {
@@ -275,7 +275,7 @@ public abstract class FeedTaskProcessor
formatFound = format; formatFound = format;
break; break;
} }
} }
if (formatFound == null) if (formatFound == null)
{ {
@@ -283,48 +283,55 @@ public abstract class FeedTaskProcessor
logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'"); logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'");
} }
ActivityFeedEntity feed = new ActivityFeedEntity(); ActivityFeedEntity feed = new ActivityFeedEntity();
// Generate activity feed summary // Generate activity feed summary
feed.setFeedUserId(connectedUser); feed.setFeedUserId(connectedUser);
feed.setPostUserId(postingUserId); feed.setPostUserId(postingUserId);
feed.setActivityType(activityType); feed.setActivityType(activityType);
if (formatFound.equals("json")) if (formatFound.equals("json"))
{ {
// allows generic JSON template to simply pass straight through // allows generic JSON template to simply pass straight through
model.put("activityData", activityPost.getActivityData()); model.put("activityData", activityPost.getActivityData());
} }
String activitySummary = processFreemarker(templateCache, fmTemplate, cfg, model); String activitySummary = processFreemarker(templateCache, fmTemplate, cfg, model);
if (! activitySummary.equals("")) if (! activitySummary.equals(""))
{ {
feed.setActivitySummary(activitySummary); if (activitySummary.length() > ActivityFeedDAO.MAX_LEN_ACTIVITY_SUMMARY)
feed.setActivitySummaryFormat(formatFound); {
feed.setSiteNetwork(thisSite); logger.warn("Skip feed entry (activity post " + activityPost.getId() + ") since activity summary - exceeds " + ActivityFeedDAO.MAX_LEN_ACTIVITY_SUMMARY + " chars: " + activitySummary);
feed.setAppTool(activityPost.getAppTool()); }
feed.setPostDate(activityPost.getPostDate()); else
feed.setPostId(activityPost.getId()); {
feed.setFeedDate(new Date()); feed.setActivitySummary(activitySummary);
feed.setActivitySummaryFormat(formatFound);
// Insert activity feed feed.setSiteNetwork(thisSite);
insertFeedEntry(feed); // ignore returned feedId feed.setAppTool(activityPost.getAppTool());
feed.setPostDate(activityPost.getPostDate());
totalGenerated++; feed.setPostId(activityPost.getId());
} feed.setFeedDate(new Date());
else
{ // Insert activity feed
if (logger.isDebugEnabled()) 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() + ")"); 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); updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.PROCESSED);
commitTransaction(); commitTransaction();
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
@@ -366,21 +373,21 @@ public abstract class FeedTaskProcessor
protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException
{ {
URL url = new URL(urlString); URL url = new URL(urlString);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug(">>> Request URI: " + url.toURI()); logger.debug(">>> Request URI: " + url.toURI());
} }
HttpURLConnection conn = (HttpURLConnection)url.openConnection(); HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET"); conn.setRequestMethod("GET");
if (ticket != null) 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 // 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; String result = null;
@@ -389,34 +396,35 @@ public abstract class FeedTaskProcessor
try try
{ {
is = conn.getInputStream(); is = conn.getInputStream();
br = new BufferedReader(new InputStreamReader(is)); br = new BufferedReader(new InputStreamReader(is));
String line = null; String line = null;
StringBuffer sb = new StringBuffer(); StringBuffer sb = new StringBuffer();
while(((line = br.readLine()) !=null)) { while(((line = br.readLine()) !=null))
sb.append(line); {
} sb.append(line);
}
result = sb.toString();
result = sb.toString();
if (logger.isDebugEnabled())
{ if (logger.isDebugEnabled())
int responseCode = conn.getResponseCode(); {
logger.debug(">>> Response code: " + responseCode); int responseCode = conn.getResponseCode();
} logger.debug(">>> Response code: " + responseCode);
}
} }
finally finally
{ {
if (br != null) { br.close(); }; if (br != null) { br.close(); };
if (is != null) { is.close(); }; if (is != null) { is.close(); };
} }
return result; return result;
} }
protected Set<String> getSiteMembers(RepoCtx ctx, String siteId) throws Exception protected Set<String> getSiteMembers(RepoCtx ctx, String siteId) throws Exception
{ {
Set<String> members = new HashSet<String>(); Set<String> members = new HashSet<String>();
if ((siteId != null) && (siteId.length() != 0)) if ((siteId != null) && (siteId.length() != 0))
{ {
@@ -516,10 +524,10 @@ public abstract class FeedTaskProcessor
return activityTemplates; return activityTemplates;
} }
protected Configuration getFreemarkerConfiguration(RepoCtx ctx) protected Configuration getFreemarkerConfiguration(RepoCtx ctx)
{ {
Configuration cfg = new Configuration(); Configuration cfg = new Configuration();
cfg.setObjectWrapper(new DefaultObjectWrapper()); cfg.setObjectWrapper(new DefaultObjectWrapper());
// custom template loader // custom template loader
@@ -670,7 +678,7 @@ public abstract class FeedTaskProcessor
catch (Exception e) catch (Exception e)
{ {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }
} }

View File

@@ -128,20 +128,23 @@ public class PostLookup
for (final ActivityPostEntity activityPost : activityPosts) for (final ActivityPostEntity activityPost : activityPosts)
{ {
try final String postUserId = activityPost.getUserId();
// MT share
String tenantDomain = tenantService.getUserDomain(postUserId);
AuthenticationUtil.runAs(new RunAsWork<Object>()
{ {
postDAO.startTransaction(); public Object doWork() throws Exception
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<Object>()
{ {
public Object doWork() throws Exception try
{ {
postDAO.startTransaction();
JSONObject jo = new JSONObject(new JSONTokener(activityPost.getActivityData()));
String activityDataStr = null;
if (! jo.isNull("nodeRef")) if (! jo.isNull("nodeRef"))
{ {
String nodeRefStr = jo.getString("nodeRef"); String nodeRefStr = jo.getString("nodeRef");
@@ -149,8 +152,7 @@ public class PostLookup
// lookup additional node data // lookup additional node data
JSONObject activityData = lookupNode(nodeRef, postUserId, jo); JSONObject activityData = lookupNode(nodeRef, postUserId, jo);
activityDataStr = activityData.toString();
activityPost.setActivityData(activityData.toString());
} }
else else
{ {
@@ -161,42 +163,67 @@ public class PostLookup
jo.put("firstName", firstLastName.getFirst()); jo.put("firstName", firstLastName.getFirst());
jo.put("lastName", firstLastName.getSecond()); 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()); activityPost.setLastModified(new Date());
postDAO.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostEntity.STATUS.POSTED); postDAO.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostEntity.STATUS.POSTED);
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
activityPost.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); // for debug output activityPost.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); // for debug output
logger.debug("Updated: " + activityPost); logger.debug("Updated: " + activityPost);
} }
return null; postDAO.commitTransaction();
} }
}, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); catch (IllegalArgumentException e)
{
postDAO.commitTransaction(); // log error, but consume exception (skip this post)
} logger.error("Skipping activity post " + activityPost.getId() + ": " + e);
catch (JSONException e) postDAO.updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR);
{
// log error, but consume exception (skip this post) postDAO.commitTransaction();
logger.error("Skipping activity post " + activityPost.getId() + ": " + e); }
postDAO.updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR); catch (JSONException e)
{
postDAO.commitTransaction(); // log error, but consume exception (skip this post)
} logger.error("Skipping activity post " + activityPost.getId() + ": " + e);
catch (SQLException e) postDAO.updatePostStatus(activityPost.getId(), ActivityPostEntity.STATUS.ERROR);
{
logger.error("Exception during update of post", e); postDAO.commitTransaction();
throw new JobExecutionException(e); }
} catch (SQLException e)
finally {
{ logger.error("Exception during update of post", e);
postDAO.endTransaction(); throw new JobExecutionException(e);
} }
finally
{
postDAO.endTransaction();
}
return null;
}
}, tenantService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain));
} }
} }
catch (SQLException e) catch (SQLException e)

View File

@@ -1284,7 +1284,8 @@ public class AVMRepository
List<AVMStoreDescriptor> result = new ArrayList<AVMStoreDescriptor>(storeEntities.size()); List<AVMStoreDescriptor> result = new ArrayList<AVMStoreDescriptor>(storeEntities.size());
for (AVMStoreEntity storeEntity : storeEntities) for (AVMStoreEntity storeEntity : storeEntities)
{ {
AVMStore store = new AVMStoreImpl(); AVMStoreImpl store = new AVMStoreImpl();
store.setId(storeEntity.getId());
store.setName(storeEntity.getName()); store.setName(storeEntity.getName());
result.add(store.getDescriptor()); result.add(store.getDescriptor());
} }

View File

@@ -43,6 +43,7 @@ import org.alfresco.repo.avm.util.RawServices;
import org.alfresco.repo.avm.util.SimplePath; import org.alfresco.repo.avm.util.SimplePath;
import org.alfresco.repo.domain.DbAccessControlList; import org.alfresco.repo.domain.DbAccessControlList;
import org.alfresco.repo.domain.PropertyValue; 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.ACLCopyMode;
import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.avm.AVMBadArgumentException; 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 // Get the creator ensuring that nulls are not hit
PropertyValue creatorValue = getProperty(ContentModel.PROP_CREATOR); PropertyValue creatorValue = getProperty(ContentModel.PROP_CREATOR);
String creator = creatorValue == null ? "system" : (String) creatorValue.getValue(DataTypeDefinition.TEXT); String creator = (creatorValue == null ? AuthenticationUtil.SYSTEM_USER_NAME : (String) creatorValue.getValue(DataTypeDefinition.TEXT));
creator = (creator == null) ? "system" : creator; creator = (creator == null ? AuthenticationUtil.SYSTEM_USER_NAME : creator);
// Get the created date ensuring that nulls are not hit // Get the created date ensuring that nulls are not hit
PropertyValue createdValue = getProperty(ContentModel.PROP_CREATED); PropertyValue createdValue = getProperty(ContentModel.PROP_CREATED);
Date created = createdValue == null ? (new Date()) : (Date) createdValue.getValue(DataTypeDefinition.DATE); Date created = createdValue == null ? (new Date()) : (Date) createdValue.getValue(DataTypeDefinition.DATE);

View File

@@ -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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@@ -78,4 +78,16 @@ public class ImageTransformationOptions extends TransformationOptions
{ {
return resizeOptions; 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();
}
} }

View File

@@ -33,6 +33,13 @@ import java.util.List;
*/ */
public interface ActivityFeedDAO extends ActivitiesDAO 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 long insertFeedEntry(ActivityFeedEntity activityFeed) throws SQLException;
public int deleteFeedEntries(Date keepDate) throws SQLException; public int deleteFeedEntries(Date keepDate) throws SQLException;

View File

@@ -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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 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<ActivityPostEntity> selectPosts(ActivityPostEntity activityPost) throws SQLException; public List<ActivityPostEntity> selectPosts(ActivityPostEntity activityPost) throws SQLException;
public Long getMaxActivitySeq() throws SQLException; public Long getMaxActivitySeq() throws SQLException;

View File

@@ -141,20 +141,18 @@ public class AVMVersionRootDAOImpl extends AbstractAVMVersionRootDAOImpl
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected List<AVMVersionRootEntity> getVersionRootEntitiesByTo(long storeId, long to) protected List<AVMVersionRootEntity> getVersionRootEntitiesByTo(long storeId, long to)
{ {
Map<String, Object> params = new HashMap<String, Object>(3); Map<String, Object> params = new HashMap<String, Object>(2);
params.put("id", storeId); params.put("id", storeId);
params.put("to", to); params.put("to", to);
params.put("from", null);
return (List<AVMVersionRootEntity>) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_TO, params); return (List<AVMVersionRootEntity>) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_TO, params);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected List<AVMVersionRootEntity> getVersionRootEntitiesByFrom(long storeId, long from) protected List<AVMVersionRootEntity> getVersionRootEntitiesByFrom(long storeId, long from)
{ {
Map<String, Object> params = new HashMap<String, Object>(3); Map<String, Object> params = new HashMap<String, Object>(2);
params.put("id", storeId); params.put("id", storeId);
params.put("from", from); params.put("from", from);
params.put("to", null);
return (List<AVMVersionRootEntity>) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_FROM, params); return (List<AVMVersionRootEntity>) template.queryForList(SELECT_AVM_VERSION_ROOTS_BY_FROM, params);
} }

View File

@@ -581,6 +581,7 @@
assoc.id assoc.id
</query> </query>
<!-- WARNING: This query is not performant and should not be used. -->
<query name="node.GetNodesWithChildrenInDifferentStore"> <query name="node.GetNodesWithChildrenInDifferentStore">
select select
parent.id, parent.id,

View File

@@ -1008,54 +1008,92 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest
{ {
private NodeService nodeService; private NodeService nodeService;
private List<NodeRef> deletedNodeRefs; private List<NodeRef> deletedNodeRefs;
private List<NodeRef> beforeDeleteNodeRefs;
public BadOnDeleteNodePolicy(NodeService nodeService, List<NodeRef> deletedNodeRefs) private boolean onDeleteCreateChild = true;
public BadOnDeleteNodePolicy(NodeService nodeService,
List<NodeRef> beforeDeleteNodeRefs,
List<NodeRef> deletedNodeRefs)
{ {
this.nodeService = nodeService; this.nodeService = nodeService;
this.beforeDeleteNodeRefs = beforeDeleteNodeRefs;
this.deletedNodeRefs = deletedNodeRefs; this.deletedNodeRefs = deletedNodeRefs;
} }
public void beforeDeleteNode(NodeRef nodeRef) public void beforeDeleteNode(NodeRef nodeRef)
{ {
// add a new child to the child, i.e. just before it is deleted // add the child to the list
ChildAssociationRef assocRef = nodeService.createNode( beforeDeleteNodeRefs.add(nodeRef);
if(onDeleteCreateChild)
{
// add a new child to the child, i.e. just before it is deleted
ChildAssociationRef assocRef = nodeService.createNode(
nodeRef, nodeRef,
ASSOC_TYPE_QNAME_TEST_CHILDREN, ASSOC_TYPE_QNAME_TEST_CHILDREN,
QName.createQName("pre-delete new child"), QName.createQName("pre-delete new child"),
ContentModel.TYPE_CONTAINER); ContentModel.TYPE_CONTAINER);
// set some child node properties // set some child node properties
nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true"); nodeService.setProperty(nodeRef, PROP_QNAME_BOOLEAN_VALUE, "true");
// add an aspect to the child // add an aspect to the child
nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null); nodeService.addAspect(nodeRef, ASPECT_QNAME_TEST_TITLED, null);
}
} }
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode) public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isArchivedNode)
{ {
// add the child to the list // add the child to the list
deletedNodeRefs.add(childAssocRef.getChildRef()); deletedNodeRefs.add(childAssocRef.getChildRef());
// now perform some nasties on the node's parent, i.e. add a new child
NodeRef parentRef = childAssocRef.getParentRef(); if(onDeleteCreateChild)
NodeRef childRef = childAssocRef.getChildRef(); {
ChildAssociationRef assocRef = nodeService.createNode( // 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, parentRef,
ASSOC_TYPE_QNAME_TEST_CHILDREN, ASSOC_TYPE_QNAME_TEST_CHILDREN,
QName.createQName("post-delete new child"), QName.createQName("post-delete new child"),
ContentModel.TYPE_CONTAINER); ContentModel.TYPE_CONTAINER);
}
}
private void setOnDeleteCreateChild(boolean onDeleteCreateChild)
{
this.onDeleteCreateChild = onDeleteCreateChild;
}
private boolean isOnDeleteCreateChild()
{
return onDeleteCreateChild;
} }
} }
public void testDelete() throws Exception public void testDelete() throws Exception
{ {
final List<NodeRef> beforeDeleteNodeRefs = new ArrayList<NodeRef>(5);
final List<NodeRef> deletedNodeRefs = new ArrayList<NodeRef>(5); final List<NodeRef> deletedNodeRefs = new ArrayList<NodeRef>(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 // bind to listen to the deletion of a node
policyComponent.bindClassBehaviour( policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"), QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
policy, policy,
new JavaBehaviour(policy, "onDeleteNode")); 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 // build the node and commit the node graph
Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph(nodeService, rootNodeRef); Map<QName, ChildAssociationRef> assocRefs = buildNodeGraph(nodeService, rootNodeRef);
NodeRef n1Ref = assocRefs.get(QName.createQName(BaseNodeServiceTest.NAMESPACE, "root_p_n1")).getChildRef(); 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(n6Ref));
assertEquals("Node not cascade deleted", 0, countNodesByReference(n8Ref)); 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 // commit to check
setComplete(); setComplete();
endTransaction(); 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<NodeRef> beforeDeleteNodeRefs = new ArrayList<NodeRef>(5);
// final List<NodeRef> deletedNodeRefs = new ArrayList<NodeRef>(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<QName, ChildAssociationRef> 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") @SuppressWarnings("unchecked")
private int countChildrenOfNode(NodeRef nodeRef) private int countChildrenOfNode(NodeRef nodeRef)
{ {

View File

@@ -42,12 +42,12 @@ import org.alfresco.model.ContentModel;
import org.alfresco.repo.domain.Node; import org.alfresco.repo.domain.Node;
import org.alfresco.repo.node.AbstractNodeServiceImpl; import org.alfresco.repo.node.AbstractNodeServiceImpl;
import org.alfresco.repo.node.StoreArchiveMap; 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.node.index.NodeIndexer;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
@@ -94,13 +94,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
private NodeDaoService nodeDaoService; private NodeDaoService nodeDaoService;
private StoreArchiveMap storeArchiveMap; private StoreArchiveMap storeArchiveMap;
private NodeService avmNodeService; private NodeService avmNodeService;
private NodeIndexer nodeIndexer; private NodeIndexer nodeIndexer;
private boolean cascadeInTransaction; private final static String KEY_PRE_COMMIT_ADD_NODE = "DbNodeServiceImpl.PreCommitAddNode";
public DbNodeServiceImpl() public DbNodeServiceImpl()
{ {
storeArchiveMap = new StoreArchiveMap(); // in case it is not set storeArchiveMap = new StoreArchiveMap(); // in case it is not set
cascadeInTransaction = true;
} }
public void setNodeDaoService(NodeDaoService nodeDaoService) 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 * @deprecated the functionality did not see wide enough usage to warrant the maintenance
* in the same transaction.
*
* @param cascadeInTransaction <tt>true</tt> (default) to cascade during
* delete and archive
*/ */
public void setCascadeInTransaction(boolean cascadeInTransaction) 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(childNodePair, propertiesBefore, propertiesAfter);
addMissingAspects(parentNodePair, assocTypeQName); 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 // Index
nodeIndexer.indexCreateNode(childAssocRef); nodeIndexer.indexCreateNode(childAssocRef);
// done // done
return childAssocRef; 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<NodeRef> 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<NodeRef> 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<NodeRef> 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. * Adds all the default aspects and properties required for the given type.
@@ -798,10 +862,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
invokeBeforeDeleteNode(nodeRef); invokeBeforeDeleteNode(nodeRef);
// Cascade delecte as required // Cascade delecte as required
if (cascadeInTransaction) deletePrimaryChildrenNotArchived(nodePair);
{
deletePrimaryChildrenNotArchived(nodePair, true);
}
// perform a normal deletion // perform a normal deletion
nodeDaoService.deleteNode(nodeId); nodeDaoService.deleteNode(nodeId);
// Invoke policy behaviours // Invoke policy behaviours
@@ -820,17 +881,18 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
*/ */
archiveNode(nodeRef, archiveStoreRef); archiveNode(nodeRef, archiveStoreRef);
} }
// remove the deleted node from the list of new nodes
untrackNodeRef(nodeRef);
} }
/** /**
* delete primary children - private method for deleteNode. * delete primary children - private method for deleteNode.
* *
* recurses through children when deleting a node. Does not archive. * recurses through children when deleting a node. Does not archive.
*
* @param nodePair
* @param cascade
*/ */
private void deletePrimaryChildrenNotArchived(Pair<Long, NodeRef> nodePair, boolean cascade) private void deletePrimaryChildrenNotArchived(Pair<Long, NodeRef> nodePair)
{ {
Long nodeId = nodePair.getFirst(); Long nodeId = nodePair.getFirst();
// Get the node's primary children // Get the node's primary children
@@ -859,10 +921,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
}; };
// Get all the QNames to remove // Get all the QNames to remove
nodeDaoService.getPrimaryChildAssocs(nodeId, callback); nodeDaoService.getPrimaryChildAssocs(nodeId, callback);
// Each child must be deleted // Each child must be deleted
for (Pair<Long, NodeRef> childNodePair : childNodePairs) for (Pair<Long, NodeRef> childNodePair : childNodePairs)
{ {
// Fire node policies. This ensures that each node in the hierarchy gets a notification fired. // Fire node policies. This ensures that each node in the hierarchy gets a notification fired.
Long childNodeId = childNodePair.getFirst(); Long childNodeId = childNodePair.getFirst();
NodeRef childNodeRef = childNodePair.getSecond(); NodeRef childNodeRef = childNodePair.getSecond();
@@ -872,16 +934,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
invokeBeforeDeleteNode(childNodeRef); invokeBeforeDeleteNode(childNodeRef);
// Cascade first, if required. // Cascade first
// This ensures that the beforeDelete policy is fired for all nodes in the hierarchy before // This ensures that the beforeDelete policy is fired for all nodes in the hierarchy before
// the actual delete starts. // the actual delete starts.
if (cascade) deletePrimaryChildrenNotArchived(childNodePair);
{
deletePrimaryChildrenNotArchived(childNodePair, true);
}
// Delete the child // Delete the child
nodeDaoService.deleteNode(childNodeId); nodeDaoService.deleteNode(childNodeId);
invokeOnDeleteNode(childParentAssocRef, childNodeType, childNodeQNames, false); 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); invokeOnMoveNode(oldParentAssocRef, newParentAssocRef);
} }
// If we have to cascade in the transaction, then pull the children over to the new store // Pull children to the new store
if (cascadeInTransaction) pullNodeChildrenToSameStore(newNodeToMovePair, true);
{
// Pull children to the new store
pullNodeChildrenToSameStore(newNodeToMovePair, true, true);
}
// Done // Done
return newParentAssocRef; 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 <code>indexChildren</code> * do not need to be remade. If the children are in the same store, only the <code>indexChildren</code>
* value is needed. * value is needed.
*/ */
private void pullNodeChildrenToSameStore(Pair<Long, NodeRef> nodePair, boolean cascade, boolean indexChildren) private void pullNodeChildrenToSameStore(Pair<Long, NodeRef> nodePair, boolean indexChildren)
{ {
Long nodeId = nodePair.getFirst(); Long nodeId = nodePair.getFirst();
NodeRef nodeRef = nodePair.getSecond(); NodeRef nodeRef = nodePair.getSecond();
@@ -2291,7 +2349,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
// Index // Index
if (indexChildren) if (indexChildren)
{ {
nodeIndexer.indexDeleteNode(oldParentAssocPair.getSecond());
nodeIndexer.indexCreateNode(newParentAssocPair.getSecond()); nodeIndexer.indexCreateNode(newParentAssocPair.getSecond());
} }
else 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. // Fire node policies. This ensures that each node in the hierarchy gets a notification fired.
invokeOnDeleteNode(oldParentAssocPair.getSecond(), childNodeTypeQName, childNodeAspectQNames, true); invokeOnDeleteNode(oldParentAssocPair.getSecond(), childNodeTypeQName, childNodeAspectQNames, true);
invokeOnCreateNode(newParentAssocPair.getSecond()); invokeOnCreateNode(newParentAssocPair.getSecond());
// Cascade, if required // Cascade
if (cascade) pullNodeChildrenToSameStore(newChildNodePair, indexChildren);
{
pullNodeChildrenToSameStore(newChildNodePair, cascade, indexChildren);
}
} }
} }
@@ -2418,107 +2472,4 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
nodeDaoService.setChildNameUnique(assocId, newName); nodeDaoService.setChildNameUnique(assocId, newName);
} }
} }
public static class MoveChildrenToCorrectStore extends AbstractNodeCleanupWorker
{
@Override
protected List<String> doCleanInternal() throws Throwable
{
return dbNodeService.moveChildrenToCorrectStore();
}
};
private List<String> moveChildrenToCorrectStore()
{
List<String> results = new ArrayList<String>(1000);
// Repeat the process for each store
List<Pair<Long, StoreRef>> storePairs = nodeDaoService.getStores();
for (Pair<Long, StoreRef> storePair : storePairs)
{
List<String> storeResults = moveChildrenToCorrectStore(storePair.getFirst());
results.addAll(storeResults);
}
return results;
}
private List<String> moveChildrenToCorrectStore(final Long storeId)
{
final List<Pair<Long, NodeRef>> parentNodePairs = new ArrayList<Pair<Long, NodeRef>>(100);
final NodeRefQueryCallback callback = new NodeRefQueryCallback()
{
public boolean handle(Pair<Long, NodeRef> nodePair)
{
parentNodePairs.add(nodePair);
return true;
}
};
RetryingTransactionCallback<Object> getNodesCallback = new RetryingTransactionCallback<Object>()
{
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<String> results = new ArrayList<String>(100);
for (final Pair<Long, NodeRef> parentNodePair : parentNodePairs)
{
RetryingTransactionCallback<String> fixNodesCallback = new RetryingTransactionCallback<String>()
{
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;
}
} }

View File

@@ -38,8 +38,6 @@ import org.springframework.extensions.surf.util.I18NUtil;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.node.BaseNodeServiceTest; 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.node.db.NodeDaoService.NodePropertyHandler;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; 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.MLText;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; 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.QName;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
@@ -73,7 +70,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
{ {
// Force cascading // Force cascading
DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl"); DbNodeServiceImpl dbNodeServiceImpl = (DbNodeServiceImpl) applicationContext.getBean("dbNodeServiceImpl");
dbNodeServiceImpl.setCascadeInTransaction(true);
return (NodeService) applicationContext.getBean("dbNodeService"); return (NodeService) applicationContext.getBean("dbNodeService");
} }
@@ -402,49 +398,6 @@ public class DbNodeServiceImplTest extends BaseNodeServiceTest
nodeService.deleteNode(nodeRef); 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 * Adds a property to a node and checks that it can be found using the low-level DB query
*/ */

View File

@@ -357,6 +357,8 @@ public interface NodeDaoService
* @param minNodeId the min node ID to return * @param minNodeId the min node ID to return
* @param count the maximum number of results * @param count the maximum number of results
* @param resultsCallback the node callback * @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) @DirtySessionAnnotation(markDirty=false)
public void getNodesWithChildrenInDifferentStore( public void getNodesWithChildrenInDifferentStore(

View File

@@ -3232,6 +3232,9 @@ public class HibernateNodeDaoServiceImpl
// Done // Done
} }
/**
* @deprecated Not performant. Do not use.
*/
public void getNodesWithChildrenInDifferentStore( public void getNodesWithChildrenInDifferentStore(
final Long storeId, final Long storeId,
final Long minNodeId, final Long minNodeId,

View File

@@ -26,6 +26,7 @@ package org.alfresco.repo.rule;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -35,6 +36,11 @@ import java.util.Set;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionModel; import org.alfresco.repo.action.ActionModel;
import org.alfresco.repo.action.RuntimeActionService; 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.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.TransactionListener; import org.alfresco.repo.transaction.TransactionListener;
import org.alfresco.service.cmr.action.Action; 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.rule.RuleType;
import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.util.GUID; import org.alfresco.util.GUID;
@@ -67,7 +74,12 @@ import org.apache.commons.logging.LogFactory;
* *
* @author Roy Wetherall * @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 */ /** key against which to store rules pending on the current transaction */
private static final String KEY_RULES_PENDING = "RuleServiceImpl.PendingRules"; 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 String ASSOC_NAME_RULES_PREFIX = "rules";
private RegexQNamePattern ASSOC_NAME_RULES_REGEX = new RegexQNamePattern(RuleModel.RULE_MODEL_URI, "^" + ASSOC_NAME_RULES_PREFIX + ".*"); 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); private static Log logger = LogFactory.getLog(RuleServiceImpl.class);
/**
* The permission-safe node service
*/
private NodeService nodeService; private NodeService nodeService;
/**
* The runtime node service (ignores permissions)
*/
private NodeService runtimeNodeService; private NodeService runtimeNodeService;
/**
* The action service
*/
private ActionService actionService; private ActionService actionService;
/**
* The dictionary service
*/
private DictionaryService dictionaryService; private DictionaryService dictionaryService;
private PolicyComponent policyComponent;
/**
* The permission service
*/
private PermissionService permissionService; private PermissionService permissionService;
/** /**
* The action service implementation which we need for some things. * 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<NodeRef, List<Rule>> nodeRulesCache;
/** /**
* List of disabled node refs. The rules associated with these nodes will node be added to the pending list, and * 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. * therefore not fired. This list is transient.
*
* TODO: (DH) Make this txn-local
*/ */
private Set<NodeRef> disabledNodeRefs = new HashSet<NodeRef>(5); private Set<NodeRef> disabledNodeRefs = new HashSet<NodeRef>(5);
@@ -148,8 +146,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
/** /**
* Set the permission-safe node service * Set the permission-safe node service
*
* @param nodeService the permission-safe node service
*/ */
public void setNodeService(NodeService nodeService) public void setNodeService(NodeService nodeService)
{ {
@@ -158,8 +154,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
/** /**
* Set the direct node service * Set the direct node service
*
* @param nodeService the node service
*/ */
public void setRuntimeNodeService(NodeService runtimeNodeService) public void setRuntimeNodeService(NodeService runtimeNodeService)
{ {
@@ -168,8 +162,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
/** /**
* Set the action service * Set the action service
*
* @param actionService the action service
*/ */
public void setActionService(ActionService actionService) public void setActionService(ActionService actionService)
{ {
@@ -178,8 +170,6 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
/** /**
* Set the runtime action service * Set the runtime action service
*
* @param actionRegistration the action service
*/ */
public void setRuntimeActionService(RuntimeActionService runtimeActionService) public void setRuntimeActionService(RuntimeActionService runtimeActionService)
{ {
@@ -188,24 +178,41 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
/** /**
* Set the dictionary service * Set the dictionary service
*
* @param dictionaryService the dictionary service
*/ */
public void setDictionaryService(DictionaryService dictionaryService) public void setDictionaryService(DictionaryService dictionaryService)
{ {
this.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 * Set the permission service
*
* @param permissionService the permission service
*/ */
public void setPermissionService(PermissionService permissionService) public void setPermissionService(PermissionService permissionService)
{ {
this.permissionService = permissionService; this.permissionService = permissionService;
} }
/**
* Set the cache to hold node's individual rules. This cache <b>must not be shared</b>
* across transactions.
*
* @param nodeRulesCache a cache of raw rules contained on a node
*
* @see NullCache
*/
public void setNodeRulesCache(SimpleCache<NodeRef, List<Rule>> nodeRulesCache)
{
this.nodeRulesCache = nodeRulesCache;
}
/** /**
* Set the global rules disabled flag * Set the global rules disabled flag
* *
@@ -216,6 +223,88 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
this.globalRulesDisabled = rulesDisabled; 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 * Gets the saved rule folder reference
* *
@@ -355,60 +444,78 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
{ {
List<Rule> rules = new ArrayList<Rule>(); List<Rule> rules = new ArrayList<Rule>();
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 // Ensure rules are not duplicated in the list
for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null)) if (rules.contains(rule) == false)
{ {
// Ensure rules are not duplicated in the list rules.add(rule);
if (rules.contains(rule) == false)
{
rules.add(rule);
}
} }
} }
}
// Extra check of CONSUMER permission was added to rule selection, // Get the node's own rules and add them to the list
// to prevent Access Denied Exception due to the bug: List<Rule> nodeRules = getRulesForNode(nodeRef);
// https://issues.alfresco.com/browse/ETWOTWO-438 for (Rule rule : nodeRules)
{
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true && if ((rules.contains(rule) == false) &&
permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.ALLOWED) (ruleTypeName == null || rule.getRuleTypes().contains(ruleTypeName) == true))
{ {
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef); rules.add(rule);
if (ruleFolder != null)
{
List<Rule> allRules = new ArrayList<Rule>();
// Get the rules for this node
List<ChildAssociationRef> 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);
}
}
}
} }
} }
return rules; return rules;
} }
private List<Rule> 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<Rule> 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<Rule>();
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef);
if (ruleFolder != null)
{
// Get the rules for this node
List<ChildAssociationRef> 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) * @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 finally
{ {
enableRules(); enableRules();
// Drop the rules from the cache
nodeRulesCache.remove(nodeRef);
} }
} }
else else
@@ -728,6 +837,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
enableRules(nodeRef); enableRules(nodeRef);
} }
} }
// Drop the rules from the cache
nodeRulesCache.remove(nodeRef);
} }
else else
{ {
@@ -757,6 +868,8 @@ public class RuleServiceImpl implements RuleService, RuntimeRuleService
} }
} }
} }
// Drop the rules from the cache
nodeRulesCache.remove(nodeRef);
} }
else 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) * @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) public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule)
{ {
addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false); addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false);

View File

@@ -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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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) public FeedControl(String siteId, String appToolId)
{ {
if (siteId == null) siteId = ""; if (siteId == null)
{
siteId = "";
}
this.siteId = siteId; this.siteId = siteId;
if (appToolId == null) appToolId = ""; if (appToolId == null)
{
appToolId = "";
}
this.appToolId = appToolId; this.appToolId = appToolId;
} }