First-cut Activity Service (SLNG-20)

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@9128 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Jan Vonka
2008-05-15 16:34:11 +00:00
parent 9e67621ace
commit 067013afad
58 changed files with 5420 additions and 2 deletions

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<!-- NOTE: although we use iBatis here rather than Hibernate, for consistency we make use of single SQL dialect property (hibernate.dialect) for consistency -->
<properties resource="alfresco/domain/hibernate-cfg.properties" />
<!-- SQL Map XML files loaded from the classpath -->
<!-- note: dialect property is set in properties resource above -->
<sqlMap resource="alfresco/activities/${hibernate.dialect}/ActivityPost.xml"/>
<sqlMap resource="alfresco/activities/${hibernate.dialect}/ActivityFeed.xml"/>
<sqlMap resource="alfresco/activities/${hibernate.dialect}/ActivityFeedControl.xml"/>
</sqlMapConfig>

View File

@@ -0,0 +1,86 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="activityService" class="org.alfresco.repo.activities.ActivityServiceImpl">
<property name="postDaoService" ref="postDaoService"/>
<property name="feedDaoService" ref="feedDaoService"/>
<property name="feedControlDaoService" ref="feedControlDaoService"/>
<property name="authorityService" ref="AuthorityService"/>
<property name="userNamesAreCaseSensitive" value="${user.name.caseSensitive}"/>
<property name="feedGenerator" ref="feedGenerator"/>
<property name="maxFeedItems" value="100"/>
</bean>
<bean id="iBatisDataSource" parent="dataSource">
<property name="defaultAutoCommit" >
<value>true</value>
</property>
</bean>
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean" singleton="true">
<property name="configLocation"><value>classpath:alfresco/activities/activities-SqlMapConfig.xml</value></property>
<property name="dataSource" ref="iBatisDataSource"/>
</bean>
<bean id="postDaoService" class="org.alfresco.repo.activities.ibatis.IBatisActivityPostDaoServiceImpl">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="feedDaoService" class="org.alfresco.repo.activities.ibatis.IBatisActivityFeedDaoServiceImpl">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<bean id="feedControlDaoService" class="org.alfresco.repo.activities.ibatis.IBatisFeedControlDaoServiceImpl">
<property name="sqlMapClient" ref="sqlMapClient"/>
</bean>
<!-- cleans out-of-date feed entries -->
<bean id="feedCleaner" class="org.alfresco.repo.activities.feed.cleanup.FeedCleaner">
<property name="feedDaoService" ref="feedDaoService"/>
<property name="maxAgeMins">
<value>20160</value> <!-- 20160 mins = 2 weeks -->
</property>
</bean>
<!-- cleans processed posts - max age can be small, unless required to be kept longer (for debugging) -->
<bean id="postCleaner" class="org.alfresco.repo.activities.post.cleanup.PostCleaner">
<property name="postDaoService" ref="postDaoService"/>
<property name="maxAgeMins">
<value>30</value> <!-- 30 minutes -->
</property>
</bean>
<!-- secondary lookup for pending posts -->
<bean id="postLookup" class="org.alfresco.repo.activities.post.lookup.PostLookup">
<property name="postDaoService" ref="postDaoService"/>
<property name="nodeService" ref="NodeService"/>
<property name="permissionService" ref="PermissionService"/>
<property name="transactionService" ref="transactionService"/>
<property name="personService" ref="personService"/>
</bean>
<bean id="baseFeedGenerator" class="org.alfresco.repo.activities.feed.AbstractFeedGenerator" abstract="true" init-method="init">
<property name="postDaoService" ref="postDaoService"/>
<property name="authenticationService" ref="AuthenticationService"/>
<property name="repoEndPoint" value="${repo.remote.endpoint.url}"/>
<property name="maxItemsPerCycle" value="100"/>
</bean>
<!-- Local (non-grid-based) Feed Generator -->
<bean id="feedGenerator" class="org.alfresco.repo.activities.feed.local.LocalFeedGenerator" parent="baseFeedGenerator">
<property name="feedTaskProcessor" ref="feedTaskProcessor"/>
</bean>
<bean id="feedTaskProcessor" class="org.alfresco.repo.activities.feed.local.LocalFeedTaskProcessor">
<!-- NOTE: assumes same sqlMapClient is also configured for postDaoService and feedDaoService -->
<property name="sqlMapClient" ref="sqlMapClient"/>
<property name="postDaoService" ref="postDaoService"/>
<property name="feedDaoService" ref="feedDaoService"/>
<property name="feedControlDaoService" ref="feedControlDaoService"/>
</bean>
</beans>

View File

@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeed">
<typeAlias alias="ActivityFeed" type="org.alfresco.repo.activities.feed.ActivityFeedDAO"/>
<resultMap id="ActivityFeedResult" class="ActivityFeed">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="postUserId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="postId" column="POST_ID"/> <!-- not an explicit FK constraint, can dangle if and when activity post is deleted -->
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="activitySummary" column="ACTIVITY_SUMMARY"/>
<result property="activitySummaryFormat" column="ACTIVITY_FORMAT"/>
<result property="feedDate" column="FEED_DATE"/>
</resultMap>
<select id="select.activity.feed.for.feeduser" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feedcontrol" parameterClass="FeedControl" resultClass="long">
<![CDATA[
select id as id
from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</select>
<select id="select.activity.feed.for.feeduser.and.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feed.for.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = ''
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<insert id="insert.activity.feed" parameterClass="ActivityFeed">
insert into alf_activity_feed (activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
values (#activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
CALL IDENTITY()
</selectKey>
</insert>
<delete id="delete.activity.feed.entries.older.than.date" parameterClass="Date">
<![CDATA[
delete from alf_activity_feed where post_date < #keepdate#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeedControl">
<typeAlias alias="FeedControl" type="org.alfresco.repo.activities.feed.control.FeedControlDAO"/>
<resultMap id="FeedControlResult" class="FeedControl">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.feedcontrols.for.user" parameterClass="FeedControl" resultClass="FeedControl">
<![CDATA[
select id as id, feed_user_id as feedUserId, site_network as siteNetwork, app_tool as appTool
from alf_activity_feed_control
where feed_user_id = #feedUserId#
]]>
</select>
<insert id="insert.activity.feedcontrol" parameterClass="FeedControl">
insert into alf_activity_feed_control (feed_user_id, site_network, app_tool, last_modified)
values (#feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
CALL IDENTITY()
</selectKey>
</insert>
<delete id="delete.activity.feedcontrol" parameterClass="FeedControl">
<![CDATA[
delete from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityPost">
<typeAlias alias="ActivityPost" type="org.alfresco.repo.activities.post.ActivityPostDAO"/>
<resultMap id="ActivityPostResult" class="ActivityPost">
<result property="id" column="SEQUENCE_ID"/>
<result property="activityData" column="ACTIVITY_DATA"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="userId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="jobTaskNode" column="JOB_TASK_NODE"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="status" column="STATUS"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.posts" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
job_task_node = #jobTaskNode# and
sequence_id >= #minId# and
sequence_id <= #maxId# and
status = #status#
]]>
</select>
<select id="select.activity.posts.by.status.only" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
status = #status#
]]>
</select>
<select id="select.activity.post.max.seq" resultClass="long">
select max(sequence_id) as maxId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.min.seq" resultClass="long">
select min(sequence_id) as minId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.max.jobtasknode" resultClass="int">
select max(job_task_node) as maxJobTaskNode
from alf_activity_post
where status = 'POSTED'
</select>
<insert id="insert.activity.post" parameterClass="ActivityPost">
insert into alf_activity_post (status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
values (#status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
CALL IDENTITY()
</selectKey>
</insert>
<delete id="delete.activity.posts.older.than.date" parameterClass="ActivityPost">
<![CDATA[
delete from alf_activity_post
where post_date < #postDate#
and status = #status#
]]>
</delete>
<update id="update.activity.post.data" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
<update id="update.activity.post.status" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
</sqlMap>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeed">
<typeAlias alias="ActivityFeed" type="org.alfresco.repo.activities.feed.ActivityFeedDAO"/>
<resultMap id="ActivityFeedResult" class="ActivityFeed">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="postUserId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="postId" column="POST_ID"/> <!-- not an explicit FK constraint, can dangle if and when activity post is deleted -->
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="activitySummary" column="ACTIVITY_SUMMARY"/>
<result property="activitySummaryFormat" column="ACTIVITY_FORMAT"/>
<result property="feedDate" column="FEED_DATE"/>
</resultMap>
<select id="select.activity.feed.for.feeduser" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feed.for.feeduser.and.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feed.for.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = ''
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<insert id="insert.activity.feed" parameterClass="ActivityFeed">
insert into alf_activity_feed (activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
values (#activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
SELECT LAST_INSERT_ID() AS value
</selectKey>
</insert>
<delete id="delete.activity.feed.entries.older.than.date" parameterClass="Date">
<![CDATA[
delete from alf_activity_feed where post_date < #keepdate#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeedControl">
<typeAlias alias="FeedControl" type="org.alfresco.repo.activities.feed.control.FeedControlDAO"/>
<resultMap id="FeedControlResult" class="FeedControl">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.feedcontrols.for.user" parameterClass="FeedControl" resultClass="FeedControl">
<![CDATA[
select id as id, feed_user_id as feedUserId, site_network as siteNetwork, app_tool as appTool
from alf_activity_feed_control
where feed_user_id = #feedUserId#
]]>
</select>
<select id="select.activity.feedcontrol" parameterClass="FeedControl" resultClass="long">
<![CDATA[
select id as id
from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</select>
<insert id="insert.activity.feedcontrol" parameterClass="FeedControl">
insert into alf_activity_feed_control (feed_user_id, site_network, app_tool, last_modified)
values (#feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
SELECT LAST_INSERT_ID() AS value
</selectKey>
</insert>
<delete id="delete.activity.feedcontrol" parameterClass="FeedControl">
<![CDATA[
delete from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityPost">
<typeAlias alias="ActivityPost" type="org.alfresco.repo.activities.post.ActivityPostDAO"/>
<resultMap id="ActivityPostResult" class="ActivityPost">
<result property="id" column="SEQUENCE_ID"/>
<result property="activityData" column="ACTIVITY_DATA"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="userId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="jobTaskNode" column="JOB_TASK_NODE"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="status" column="STATUS"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.posts" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
job_task_node = #jobTaskNode# and
sequence_id >= #minId# and
sequence_id <= #maxId# and
status = #status#
]]>
</select>
<select id="select.activity.posts.by.status.only" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
status = #status#
]]>
</select>
<select id="select.activity.post.max.seq" resultClass="long">
select max(sequence_id) as maxId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.min.seq" resultClass="long">
select min(sequence_id) as minId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.max.jobtasknode" resultClass="int">
select max(job_task_node) as maxJobTaskNode
from alf_activity_post
where status = 'POSTED'
</select>
<insert id="insert.activity.post" parameterClass="ActivityPost">
insert into alf_activity_post (status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
values (#status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
<!-- optionally return auto-generated primary key - only required for debug (can be commented out) -->
<selectKey resultClass="long" keyProperty="id" type="post">
SELECT LAST_INSERT_ID() AS value
</selectKey>
</insert>
<delete id="delete.activity.posts.older.than.date" parameterClass="ActivityPost">
<![CDATA[
delete from alf_activity_post
where post_date < #postDate#
and status = #status#
]]>
</delete>
<update id="update.activity.post.data" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
<update id="update.activity.post.status" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
</sqlMap>

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeed">
<typeAlias alias="ActivityFeed" type="org.alfresco.repo.activities.feed.ActivityFeedDAO"/>
<resultMap id="ActivityFeedResult" class="ActivityFeed">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="postUserId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="postId" column="POST_ID"/> <!-- not an explicit FK constraint, can dangle if and when activity post is deleted -->
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="activitySummary" column="ACTIVITY_SUMMARY"/>
<result property="activitySummaryFormat" column="ACTIVITY_FORMAT"/>
<result property="feedDate" column="FEED_DATE"/>
</resultMap>
<select id="select.activity.feed.for.feeduser" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feed.for.feeduser.and.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = #feedUserId#
and post_user_id != #feedUserId#
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<select id="select.activity.feed.for.site" parameterClass="ActivityFeed" resultClass="ActivityFeed">
<![CDATA[
select id as id, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date postDate
from alf_activity_feed
where feed_user_id = ''
and site_network = #siteNetwork#
and activity_format = #activitySummaryFormat#
order by post_date desc
]]>
</select>
<insert id="insert.activity.feed" parameterClass="ActivityFeed">
<selectKey resultClass="long" keyProperty="id" type="pre">
select alf_activity_feed_seq.nextval as value from dual
</selectKey>
insert into alf_activity_feed (id, activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date)
values (#id#, #activityType#, #activitySummary#, #activitySummaryFormat#, #feedUserId#, #postUserId#, #postDate#, #postId#, #siteNetwork#, #appTool#, #feedDate#)
</insert>
<delete id="delete.activity.feed.entries.older.than.date" parameterClass="Date">
<![CDATA[
delete from alf_activity_feed where post_date < #keepdate#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityFeedControl">
<typeAlias alias="FeedControl" type="org.alfresco.repo.activities.feed.control.FeedControlDAO"/>
<resultMap id="FeedControlResult" class="FeedControl">
<result property="id" column="ID"/>
<result property="feedUserId" column="FEED_USER_ID"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.feedcontrols.for.user" parameterClass="FeedControl" resultClass="FeedControl">
<![CDATA[
select id as id, feed_user_id as feedUserId, site_network as siteNetwork, app_tool as appTool
from alf_activity_feed_control
where feed_user_id = #feedUserId#
]]>
</select>
<select id="select.activity.feedcontrol" parameterClass="FeedControl" resultClass="long">
<![CDATA[
select id as id
from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</select>
<insert id="insert.activity.feedcontrol" parameterClass="FeedControl">
<selectKey resultClass="long" keyProperty="id" type="pre">
select alf_activity_feed_control_seq.nextval as value from dual
</selectKey>
insert into alf_activity_feed_control (id, feed_user_id, site_network, app_tool, last_modified)
values (#id#, #feedUserId#, #siteNetwork#, #appTool#, #lastModified#)
</insert>
<delete id="delete.activity.feedcontrol" parameterClass="FeedControl">
<![CDATA[
delete from alf_activity_feed_control
where feed_user_id = #feedUserId#
and site_network = #siteNetwork#
and app_tool = #appTool#
]]>
</delete>
</sqlMap>

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="ActivityPost">
<typeAlias alias="ActivityPost" type="org.alfresco.repo.activities.post.ActivityPostDAO"/>
<resultMap id="ActivityPostResult" class="ActivityPost">
<result property="id" column="SEQUENCE_ID"/>
<result property="activityData" column="ACTIVITY_DATA"/>
<result property="activityType" column="ACTIVITY_TYPE"/>
<result property="userId" column="POST_USER_ID"/>
<result property="postDate" column="POST_DATE"/>
<result property="jobTaskNode" column="JOB_TASK_NODE"/>
<result property="siteNetwork" column="SITE_NETWORK"/>
<result property="appTool" column="APP_TOOL"/>
<result property="status" column="STATUS"/>
<result property="lastModified" column="LAST_MODIFIED"/>
</resultMap>
<select id="select.activity.posts" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
job_task_node = #jobTaskNode# and
sequence_id >= #minId# and
sequence_id <= #maxId# and
status = #status#
]]>
</select>
<select id="select.activity.posts.by.status.only" parameterClass="ActivityPost" resultClass="ActivityPost">
<![CDATA[
select
sequence_id as id,
activity_data as activityData,
activity_type as activityType,
post_user_id as userId,
post_date as postDate,
job_task_node as jobTaskNode,
site_network as siteNetwork,
app_tool as appTool,
status as status
from
alf_activity_post
where
status = #status#
]]>
</select>
<select id="select.activity.post.max.seq" resultClass="long">
select max(sequence_id) as maxId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.min.seq" resultClass="long">
select min(sequence_id) as minId
from alf_activity_post
where status = 'POSTED'
</select>
<select id="select.activity.post.max.jobtasknode" resultClass="int">
select max(job_task_node) as maxJobTaskNode
from alf_activity_post
where status = 'POSTED'
</select>
<insert id="insert.activity.post" parameterClass="ActivityPost">
<selectKey resultClass="long" keyProperty="id" type="pre">
select alf_activity_post_seq.nextval as value from dual
</selectKey>
insert into alf_activity_post (sequence_id, status, activity_data, post_user_id, post_date, activity_type, site_network, app_tool, job_task_node, last_modified)
values (#id#, #status#, #activityData#, #userId#, #postDate#, #activityType#, #siteNetwork#, #appTool#, #jobTaskNode#, #lastModified#)
</insert>
<delete id="delete.activity.posts.older.than.date" parameterClass="ActivityPost">
<![CDATA[
delete from alf_activity_post
where post_date < #postDate#
and status = #status#
]]>
</delete>
<update id="update.activity.post.data" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, activity_data=#activityData#, site_network=#siteNetwork#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
<update id="update.activity.post.status" parameterClass="ActivityPost">
update alf_activity_post set status = #status#, last_modified=#lastModified#
where sequence_id = #id#
and status != #status#
</update>
</sqlMap>

View File

@@ -13,6 +13,7 @@
<import resource="classpath:alfresco/action-services-context.xml" />
<import resource="classpath:alfresco/rule-services-context.xml" />
<import resource="classpath:alfresco/node-services-context.xml" />
<import resource="classpath:alfresco/activities/activities-feed-context.xml" />
<import resource="classpath:alfresco/scheduled-jobs-context.xml" />
<import resource="classpath:alfresco/network-protocol-context.xml" />
<import resource="classpath:alfresco/emailserver/email-service-context.xml" />

View File

@@ -43,6 +43,7 @@
<list>
<value>classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-MappedFKIndexes.sql</value>
<value>classpath:alfresco/dbscripts/create/2.2/${db.script.dialect}/AlfrescoPostCreate-2.2-Extra.sql</value>
<value>classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-tables.sql</value>
</list>
</property>
<property name="validateUpdateScriptPatches">
@@ -59,6 +60,7 @@
<ref bean="patch.db-V2.2-1-DropIndexesAndConstraints" />
<ref bean="patch.db-V2.2-2-MoveQNames" />
<ref bean="patch.db-V2.2-3-PropTypes" />
<ref bean="patch.db-V3.0-0-CreateActivitiesTables" />
</list>
</property>
<property name="postUpdateScriptPatches">

View File

@@ -0,0 +1,59 @@
--
-- Title: Activities Schema
-- Database: HSQL
-- Since: V3.0.0 Schema
--
-- Note: The Activities schema is NOT managed by Hibernate
--
CREATE TABLE alf_activity_post (
sequence_id bigint generated by default as identity (start with 1),
post_date timestamp NOT NULL,
status varchar(10) NOT NULL,
activity_data varchar(4000) NOT NULL,
post_user_id varchar(255) NOT NULL,
job_task_node integer NOT NULL,
site_network varchar(255) default NULL,
app_tool varchar(36) default NULL,
activity_type varchar(255) NOT NULL,
last_modified timestamp NOT NULL,
primary key (sequence_id)
);
CREATE INDEX jobtasknode_idx on alf_activity_post(job_task_node);
CREATE INDEX status_idx on alf_activity_post(status);
CREATE TABLE alf_activity_feed (
id bigint generated by default as identity (start with 1),
post_id bigint default NULL,
post_date timestamp NOT NULL,
activity_summary varchar(4000) default NULL,
feed_user_id varchar(255) NOT NULL,
activity_type varchar(255) NOT NULL,
activity_format varchar(10) default NULL,
site_network varchar(255) default NULL,
app_tool varchar(36) default NULL,
post_user_id varchar(255) NOT NULL,
feed_date timestamp NOT NULL,
primary key (id)
);
CREATE INDEX postdate_idx ON alf_activity_feed(post_date);
CREATE INDEX feeduserid_idx ON alf_activity_feed(feed_user_id);
CREATE INDEX postuserid_idx ON alf_activity_feed(post_user_id);
CREATE INDEX sitenetwork_idx ON alf_activity_feed(site_network);
CREATE INDEX activityformat_idx ON alf_activity_feed(activity_format);
CREATE TABLE alf_activity_feed_control (
id bigint generated by default as identity (start with 1),
feed_user_id varchar(255) NOT NULL,
site_network varchar(255) NOT NULL,
app_tool varchar(36) default NULL,
last_modified timestamp NOT NULL,
primary key (id)
);
CREATE INDEX feedcontroluserid_idx ON alf_activity_feed_control(feed_user_id);

View File

@@ -0,0 +1,56 @@
--
-- Title: Activities Schema
-- Database: MySQL
-- Since: V3.0.0 Schema
--
-- Note: The Activities schema is NOT managed by Hibernate
--
CREATE TABLE alf_activity_post (
sequence_id bigint NOT NULL auto_increment,
post_date timestamp NOT NULL,
status varchar(10) NOT NULL,
activity_data varchar(4000) NOT NULL,
post_user_id varchar(255) NOT NULL,
job_task_node int(11) NOT NULL,
site_network varchar(255) default NULL,
app_tool varchar(36) default NULL,
activity_type varchar(255) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (sequence_id),
KEY jobtasknode_idx (job_task_node),
KEY status_idx (status)
) type=InnoDB;
CREATE TABLE alf_activity_feed (
id bigint NOT NULL auto_increment,
post_id bigint default NULL,
post_date timestamp NOT NULL,
activity_summary varchar(4000) default NULL,
feed_user_id varchar(255) NOT NULL,
activity_type varchar(255) NOT NULL,
activity_format varchar(10) default NULL,
site_network varchar(255) default NULL,
app_tool varchar(36) default NULL,
post_user_id varchar(255) NOT NULL,
feed_date timestamp NOT NULL,
PRIMARY KEY (id),
KEY postdate_idx (post_date),
KEY feeduserid_idx (feed_user_id),
KEY postuserid_idx (post_user_id),
KEY sitenetwork_idx (site_network),
KEY activityformat_idx (activity_format)
) type=InnoDB;
CREATE TABLE alf_activity_feed_control (
id bigint NOT NULL auto_increment,
feed_user_id varchar(255) NOT NULL,
site_network varchar(255) NOT NULL,
app_tool varchar(36) NOT NULL,
last_modified timestamp NOT NULL,
PRIMARY KEY (id),
KEY feedcontroluserid_idx (feed_user_id)
) type=InnoDB;

View File

@@ -0,0 +1,65 @@
--
-- Title: Activities Schema
-- Database: Oracle
-- Since: V3.0.0 Schema
--
-- Note: The Activities schema is NOT managed by Hibernate
--
CREATE TABLE alf_activity_post (
sequence_id number(19,0) NOT NULL,
post_date timestamp NOT NULL,
status varchar2(10) NOT NULL,
activity_data varchar2(4000) NOT NULL,
post_user_id varchar2(255) NOT NULL,
job_task_node number(19,0) NOT NULL,
site_network varchar2(255) default NULL,
app_tool varchar2(36) default NULL,
activity_type varchar2(255) NOT NULL,
last_modified timestamp NOT NULL,
primary key (sequence_id)
);
CREATE SEQUENCE alf_activity_post_seq START WITH 1 INCREMENT BY 1;
CREATE INDEX jobtasknode_idx on alf_activity_post(job_task_node);
CREATE INDEX status_idx on alf_activity_post(status);
CREATE TABLE alf_activity_feed (
id number(19,0) NOT NULL,
post_id number(19,0) default NULL,
post_date timestamp NOT NULL,
activity_summary varchar2(4000) default NULL,
feed_user_id varchar2(255) default NULL,
activity_type varchar2(255) NOT NULL,
activity_format varchar2(10) default NULL,
site_network varchar2(255) default NULL,
app_tool varchar2(36) default NULL,
post_user_id varchar2(255) NOT NULL,
feed_date timestamp NOT NULL,
primary key (id)
);
CREATE SEQUENCE alf_activity_feed_seq START WITH 1 INCREMENT BY 1;
CREATE INDEX postdate_idx ON alf_activity_feed(post_date);
CREATE INDEX feeduserid_idx ON alf_activity_feed(feed_user_id);
CREATE INDEX postuserid_idx ON alf_activity_feed(post_user_id);
CREATE INDEX sitenetwork_idx ON alf_activity_feed(site_network);
CREATE INDEX activityformat_idx ON alf_activity_feed(activity_format);
CREATE TABLE alf_activity_feed_control (
id number(19,0) NOT NULL,
feed_user_id varchar2(255) NOT NULL,
site_network varchar2(255) NOT NULL,
app_tool varchar2(36) NOT NULL,
last_modified timestamp NOT NULL,
primary key (id)
);
CREATE SEQUENCE alf_activity_feed_control_seq START WITH 1 INCREMENT BY 1;
CREATE INDEX feedcontroluserid_idx ON alf_activity_feed_control(feed_user_id);

View File

@@ -1468,4 +1468,15 @@
</property>
</bean>
<bean id="patch.db-V3.0-0-CreateActivitiesTables" class="org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch" parent="basePatch">
<property name="id"><value>patch.db-V3.0-0-CreateActivitiesTables</value></property>
<property name="description"><value>patch.schemaUpgradeScript.description</value></property>
<property name="fixesFromSchema"><value>0</value></property>
<property name="fixesToSchema"><value>125</value></property>
<property name="targetSchema"><value>126</value></property>
<property name="scriptUrl">
<value>classpath:alfresco/dbscripts/create/3.0/${db.script.dialect}/create-activities-tables.sql</value>
</property>
</bean>
</beans>

View File

@@ -205,3 +205,6 @@ avm.remote.idlestream.timeout=30000
# ECM content usages/quotas
system.usages.enabled=true
# Repository endpoint - used by Activity Service
repo.remote.endpoint.url=http://localhost:8080/alfresco/service

View File

@@ -354,4 +354,120 @@
<!-- See the wiki (http://wiki.alfresco.com/wikiDeployment) for details -->
<!-- and the deployment-attempt-cleaner-context.xml.sample file. -->
<!-- Activities Feed Cleaner -->
<bean id="feedCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.repo.activities.feed.cleanup.FeedCleanupJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="feedCleaner">
<ref bean="feedCleaner" />
</entry>
</map>
</property>
</bean>
<bean id="feedCleanerTrigger" class="org.alfresco.util.TriggerBean">
<property name="jobDetail">
<ref bean="feedCleanerJobDetail" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="startDelayMinutes">
<value>5</value>
</property>
<property name="repeatIntervalMinutes">
<value>10</value>
</property>
</bean>
<bean id="feedGeneratorJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.repo.activities.feed.FeedGeneratorJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="feedGenerator">
<ref bean="feedGenerator" />
</entry>
</map>
</property>
</bean>
<bean id="feedGeneratorTrigger" class="org.alfresco.util.TriggerBean">
<property name="jobDetail">
<ref bean="feedGeneratorJobDetail" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="startDelayMinutes">
<value>0</value>
</property>
<property name="repeatInterval">
<value>30000</value> <!-- 30000 msecs = 30 seconds -->
</property>
</bean>
<!-- Activities Post Lookup (for secondary lookup) -->
<bean id="postLookupJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.repo.activities.post.lookup.PostLookupJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="postLookup">
<ref bean="postLookup" />
</entry>
</map>
</property>
</bean>
<bean id="postLookupTrigger" class="org.alfresco.util.TriggerBean">
<property name="jobDetail">
<ref bean="postLookupJobDetail" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="startDelayMinutes">
<value>1</value>
</property>
<property name="repeatInterval">
<value>15000</value> <!-- 15000 msecs = 15 seconds -->
</property>
</bean>
<!-- Activities Post Cleaner -->
<bean id="postCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass">
<value>org.alfresco.repo.activities.post.cleanup.PostCleanupJob</value>
</property>
<property name="jobDataAsMap">
<map>
<entry key="postCleaner">
<ref bean="postCleaner" />
</entry>
</map>
</property>
</bean>
<bean id="postCleanerTrigger" class="org.alfresco.util.TriggerBean">
<property name="jobDetail">
<ref bean="postCleanerJobDetail" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="startDelayMinutes">
<value>10</value>
</property>
<property name="repeatIntervalMinutes">
<value>10</value>
</property>
</bean>
</beans>

View File

@@ -167,4 +167,13 @@
</property>
</bean>
<bean id="activitiesScript" parent="baseJavaScriptExtension" class="org.alfresco.repo.activities.script.Activity">
<property name="extensionName">
<value>activities</value>
</property>
<property name="activityService">
<ref bean="activityService"/>
</property>
</bean>
</beans>

View File

@@ -19,4 +19,4 @@ version.build=@build-number@
# Schema number
version.schema=125
version.schema=126

View File

@@ -0,0 +1,511 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.activities.feed.ActivityFeedDAO;
import org.alfresco.repo.activities.feed.ActivityFeedDaoService;
import org.alfresco.repo.activities.feed.FeedGenerator;
import org.alfresco.repo.activities.feed.control.FeedControlDAO;
import org.alfresco.repo.activities.feed.control.FeedControlDaoService;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.activities.FeedControl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.JSONtoFmModel;
import org.alfresco.util.ParameterCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
/**
* Activity Service Implementation
*
* @author janv
*/
public class ActivityServiceImpl implements ActivityService
{
private static final Log logger = LogFactory.getLog(ActivityServiceImpl.class);
private static final int MAX_LEN_USER_ID = 255; // needs to match schema: feed_user_id, post_user_id
private static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network
private static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type
private static final int MAX_LEN_ACTIVITY_DATA = 4000; // needs to match schema: activity_data
private static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool
private ActivityPostDaoService postDaoService;
private ActivityFeedDaoService feedDaoService;
private FeedControlDaoService feedControlDaoService;
private AuthorityService authorityService;
private FeedGenerator feedGenerator;
private int maxFeedItems = 100;
private boolean userNamesAreCaseSensitive = false;
public void setMaxFeedItems(int maxFeedItems)
{
this.maxFeedItems = maxFeedItems;
}
public void setUserNamesAreCaseSensitive(boolean userNamesAreCaseSensitive)
{
this.userNamesAreCaseSensitive = userNamesAreCaseSensitive;
}
public void setPostDaoService(ActivityPostDaoService postDaoService)
{
this.postDaoService = postDaoService;
}
public void setFeedDaoService(ActivityFeedDaoService feedDaoService)
{
this.feedDaoService = feedDaoService;
}
public void setFeedControlDaoService(FeedControlDaoService feedControlDaoService)
{
this.feedControlDaoService = feedControlDaoService;
}
public void setAuthorityService(AuthorityService authorityService)
{
this.authorityService = authorityService;
}
public void setFeedGenerator(FeedGenerator feedGenerator)
{
this.feedGenerator = feedGenerator;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void postActivity(String activityType, String network, String appTool, String activityData)
{
postActivity(activityType, network, appTool, activityData, ActivityPostDAO.STATUS.POSTED);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef)
*/
public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
StringBuffer sb = new StringBuffer();
sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append("}");
postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
StringBuffer sb = new StringBuffer();
sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",")
.append("\"name\":\"").append(name).append("\"")
.append("}");
postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#postActivity(java.lang.String, java.lang.String, java.lang.String, org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef)
*/
public void postActivity(String activityType, String network, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef)
{
// primarily for delete node activities - eg. delete document, delete folder
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("typeQName", typeQName);
ParameterCheck.mandatory("parentNodeRef", parentNodeRef);
StringBuffer sb = new StringBuffer();
sb.append("{").append("\"nodeRef\":\"").append(nodeRef.toString()).append("\"").append(",")
.append("\"name\":\"").append(name).append("\"").append(",")
.append("\"typeQName\":\"").append(typeQName.toPrefixString()).append("\"").append(",") // TODO toPrefixString does not return prefix ??!!
.append("\"parentNodeRef\":\"").append(parentNodeRef.toString()).append("\"")
.append("}");
postActivity(activityType, network, appTool, sb.toString(), ActivityPostDAO.STATUS.PENDING);
}
private void postActivity(String activityType, String siteNetwork, String appTool, String activityData, ActivityPostDAO.STATUS status)
{
String currentUser = AuthenticationUtil.getCurrentUserName();
try
{
// optional - default to empty string
if (siteNetwork == null)
{
siteNetwork = "";
}
else if (siteNetwork.length() > MAX_LEN_SITE_ID)
{
throw new AlfrescoRuntimeException("Invalid site network - exceeds " + MAX_LEN_SITE_ID + " chars: " + siteNetwork);
}
// optional - default to empty string
if (appTool == null)
{
appTool = "";
}
else if (appTool.length() > MAX_LEN_APP_TOOL_ID)
{
throw new AlfrescoRuntimeException("Invalid app tool - exceeds " + MAX_LEN_APP_TOOL_ID + " chars: " + appTool);
}
// required
ParameterCheck.mandatoryString("activityType", activityType);
if (activityType.length() > MAX_LEN_ACTIVITY_TYPE)
{
throw new AlfrescoRuntimeException("Invalid activity type - exceeds " + MAX_LEN_ACTIVITY_TYPE + " chars: " + activityType);
}
// optional - default to empty string
if (activityData == null)
{
activityData = "";
}
else if (activityType.length() > MAX_LEN_ACTIVITY_DATA)
{
throw new AlfrescoRuntimeException("Invalid activity data - exceeds " + MAX_LEN_ACTIVITY_DATA + " chars: " + activityData);
}
// required
ParameterCheck.mandatoryString("currentUser", currentUser);
if (currentUser.length() > MAX_LEN_USER_ID)
{
throw new AlfrescoRuntimeException("Invalid user - exceeds " + MAX_LEN_USER_ID + " chars: " + currentUser);
}
else if ((! currentUser.equals(AuthenticationUtil.SYSTEM_USER_NAME)) && (! userNamesAreCaseSensitive))
{
// user names are case-insensitive
currentUser = currentUser.toLowerCase();
}
}
catch (AlfrescoRuntimeException e)
{
// log error and throw exception
logger.error(e);
throw e;
}
try
{
Date postDate = new Date();
ActivityPostDAO activityPost = new ActivityPostDAO();
activityPost.setUserId(currentUser);
activityPost.setSiteNetwork(siteNetwork);
activityPost.setAppTool(appTool);
activityPost.setActivityData(activityData);
activityPost.setActivityType(activityType);
activityPost.setPostDate(postDate);
activityPost.setStatus(status.toString());
activityPost.setLastModified(postDate);
// hash the userid to generate a job task node
int nodeCount = feedGenerator.getEstimatedGridSize();
int userHashCode = currentUser.hashCode();
int nodeHash = (userHashCode % nodeCount) + 1;
activityPost.setJobTaskNode(nodeHash);
try
{
long postId = postDaoService.insertPost(activityPost);
if (logger.isDebugEnabled())
{
activityPost.setId(postId);
logger.debug("Posted: " + activityPost);
}
}
catch (SQLException e)
{
throw new AlfrescoRuntimeException("Failed to post activity: " + e, e);
}
}
catch (AlfrescoRuntimeException e)
{
// log error, subsume exception
logger.error(e);
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String)
*/
public List<Map<String, Object>> getUserFeedEntries(String feedUserId, String format, String siteId)
{
// NOTE: siteId is optional
ParameterCheck.mandatoryString("feedUserId", feedUserId);
ParameterCheck.mandatoryString("format", format);
List<Map<String, Object>> activityFeedModels = new ArrayList<Map<String, Object>>();
if (! userNamesAreCaseSensitive)
{
feedUserId = feedUserId.toLowerCase();
}
String currentUser = AuthenticationUtil.getCurrentUserName();
if (currentUser != null)
{
if ((! authorityService.isAdminAuthority(currentUser)) && (! currentUser.equals(feedUserId)))
{
throw new AlfrescoRuntimeException("Unable to get feed entries for '" + feedUserId + "' - currently logged in as '" + currentUser +"'");
}
try
{
List<ActivityFeedDAO> activityFeeds = null;
if (siteId != null)
{
activityFeeds = feedDaoService.selectUserFeedEntries(feedUserId, format, siteId);
}
else
{
activityFeeds = feedDaoService.selectUserFeedEntries(feedUserId, format);
}
int count = 0;
for (ActivityFeedDAO activityFeed : activityFeeds)
{
count++;
if (count > maxFeedItems)
{
break;
}
activityFeedModels.add(JSONtoFmModel.convertJSONObjectToMap(activityFeed.getJSONString()));
}
}
catch (SQLException se)
{
throw new AlfrescoRuntimeException("Unable to get user feed entries: " + se.getMessage());
}
catch (JSONException je)
{
throw new AlfrescoRuntimeException("Unable to get user feed entries: " + je.getMessage());
}
}
else
{
throw new AlfrescoRuntimeException("Unable to get user feed entries - current user is null");
}
return activityFeedModels;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#getSiteFeedEntries(java.lang.String, java.lang.String)
*/
public List<Map<String, Object>> getSiteFeedEntries(String siteId, String format)
{
ParameterCheck.mandatoryString("siteId", siteId);
ParameterCheck.mandatoryString("format", format);
List<Map<String, Object>> activityFeedModels = new ArrayList<Map<String, Object>>();
String currentUser = AuthenticationUtil.getCurrentUserName();
if (currentUser != null)
{
// TODO - check whether site is public or private, if private, check whether user is member or admin - authorityService.isAdminAuthority(currentUser))
try
{
List<ActivityFeedDAO> activityFeeds = feedDaoService.selectSiteFeedEntries(siteId, format);
int count = 0;
for (ActivityFeedDAO activityFeed : activityFeeds)
{
count++;
if (count > maxFeedItems)
{
break;
}
activityFeedModels.add(JSONtoFmModel.convertJSONObjectToMap(activityFeed.getJSONString()));
}
}
catch (SQLException se)
{
throw new AlfrescoRuntimeException("Unable to get site feed entries: " + se.getMessage());
}
catch (JSONException je)
{
throw new AlfrescoRuntimeException("Unable to get site feed entries: " + je.getMessage());
}
}
else
{
throw new AlfrescoRuntimeException("Unable to get site feed entries - current user is null");
}
return activityFeedModels;
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#setFeedControl(org.alfresco.service.cmr.activities.FeedControl)
*/
public void setFeedControl(FeedControl feedControl)
{
ParameterCheck.mandatory("feedControl", feedControl);
String userId = AuthenticationUtil.getCurrentUserName();
if (! userNamesAreCaseSensitive)
{
userId = userId.toLowerCase();
}
try
{
if (! existsFeedControl(feedControl))
{
feedControlDaoService.insertFeedControl(new FeedControlDAO(userId, feedControl));
}
}
catch (SQLException e)
{
throw new AlfrescoRuntimeException("Failed to set feed control: " + e, e);
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls()
*/
public List<FeedControl> getFeedControls()
{
String userId = AuthenticationUtil.getCurrentUserName();
return getFeedControlsImpl(userId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#getFeedControls(java.lang.String)
*/
public List<FeedControl> getFeedControls(String userId)
{
ParameterCheck.mandatoryString("userId", userId);
String currentUser = AuthenticationUtil.getCurrentUserName();
if ((currentUser == null) || ((! currentUser.equals(AuthenticationUtil.getSystemUserName())) && (! currentUser.equals(userId)) && (! authorityService.isAdminAuthority(currentUser))))
{
throw new AlfrescoRuntimeException("Current user " + currentUser + " is not permitted to get feed controls for " + userId);
}
return getFeedControlsImpl(userId);
}
private List<FeedControl> getFeedControlsImpl(String userId)
{
ParameterCheck.mandatoryString("userId", userId);
if (! userNamesAreCaseSensitive)
{
userId = userId.toLowerCase();
}
try
{
List<FeedControlDAO> feedControlDaos = feedControlDaoService.selectFeedControls(userId);
List<FeedControl> feedControls = new ArrayList<FeedControl>(feedControlDaos.size());
for (FeedControlDAO feedControlDao : feedControlDaos)
{
feedControls.add(feedControlDao.getFeedControl());
}
return feedControls;
}
catch (SQLException e)
{
throw new AlfrescoRuntimeException("Failed to set feed control: " + e, e);
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#deleteFeedControl(org.alfresco.service.cmr.activities.FeedControl)
*/
public void unsetFeedControl(FeedControl feedControl)
{
ParameterCheck.mandatory("feedControl", feedControl);
String userId = AuthenticationUtil.getCurrentUserName();
if (! userNamesAreCaseSensitive)
{
userId = userId.toLowerCase();
}
try
{
feedControlDaoService.deleteFeedControl(new FeedControlDAO(userId, feedControl));
}
catch (SQLException e)
{
throw new AlfrescoRuntimeException("Failed to set feed control: " + e, e);
}
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.activities.ActivityService#existsFeedControl(java.lang.String, org.alfresco.service.cmr.activities.FeedControl)
*/
public boolean existsFeedControl(FeedControl feedControl)
{
ParameterCheck.mandatory("feedControl", feedControl);
String userId = AuthenticationUtil.getCurrentUserName();
if (! userNamesAreCaseSensitive)
{
userId = userId.toLowerCase();
}
try
{
long id = feedControlDaoService.selectFeedControl(new FeedControlDAO(userId, feedControl));
return (id != -1);
}
catch (SQLException e)
{
throw new AlfrescoRuntimeException("Failed to query feed control: " + e, e);
}
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.repo.jscript.ClasspathScriptLocation;
import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.activities.FeedControl;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.util.BaseSpringTest;
/**
* Activity Service Implementation unit test
*
* @author janv
*/
public class ActivityServiceImplTest extends BaseSpringTest
{
private ActivityService activityService;
private ScriptService scriptService;
private AuthenticationService authenticationService;
private static final String ADMIN_UN = "admin";
private static final String ADMIN_PW = "admin";
private static final String USER_UN = "bob";
private static final String USER_PW = "bob";
protected void onSetUpInTransaction() throws Exception
{
super.onSetUpInTransaction();
// Get the required services
this.activityService = (ActivityService)this.applicationContext.getBean("ActivityService");
this.scriptService = (ScriptService)this.applicationContext.getBean("ScriptService");
this.authenticationService = (AuthenticationService)applicationContext.getBean("authenticationService");
authenticationService.authenticate(ADMIN_UN, ADMIN_PW.toCharArray());
}
protected void onTearDownInTransaction() throws Exception
{
authenticationService.clearCurrentSecurityContext();
}
public void testPostValidActivities() throws Exception
{
this.activityService.postActivity("org.alfresco.testActivityType1", null, null, "");
this.activityService.postActivity("org.alfresco.testActivityType2", "", "", "");
this.activityService.postActivity("org.alfresco.testActivityType3", "site1", "appToolA", "{ \"var1\" : \"val1\" }");
}
public void testPostInvalidActivities() throws Exception
{
try
{
this.activityService.postActivity("", "", "", null, "");
fail("invalid post activity");
}
catch (IllegalArgumentException iae)
{
assertTrue(iae.getMessage().contains("nodeRef is a mandatory parameter"));
}
try
{
this.activityService.postActivity("", "", "", "");
fail("invalid post activity");
}
catch (IllegalArgumentException iae)
{
assertTrue(iae.getMessage().contains("activityType is a mandatory parameter"));
}
}
public void testGetEmptySiteFeed() throws Exception
{
authenticationService.clearCurrentSecurityContext();
if(! authenticationService.authenticationExists(USER_UN))
{
authenticationService.createAuthentication(USER_UN, USER_PW.toCharArray());
}
authenticationService.authenticate(USER_UN, USER_PW.toCharArray());
List<Map<String, Object>> siteFeedEntries = this.activityService.getSiteFeedEntries("unknown site", "some format");
assertNotNull(siteFeedEntries);
assertTrue(siteFeedEntries.isEmpty());
}
public void testGetEmptyUserFeed() throws Exception
{
List<Map<String, Object>> userFeedEntries = this.activityService.getUserFeedEntries("unknown user", "some format", null);
assertNotNull(userFeedEntries);
assertTrue(userFeedEntries.isEmpty());
}
public void testJSAPI() throws Exception
{
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/activities/script/test_activityService.js");
String result = (String)this.scriptService.executeScript(location, new HashMap<String, Object>(0));
// Check the result and fail if message returned
if (result != null && result.length() != 0)
{
fail("The activity service test JS script failed: " + result);
}
}
public void testFeedControls() throws Exception
{
List<FeedControl> feedControls = this.activityService.getFeedControls(USER_UN);
assertNotNull(feedControls);
assertTrue(feedControls.isEmpty());
authenticationService.clearCurrentSecurityContext();
if(! authenticationService.authenticationExists(USER_UN))
{
authenticationService.createAuthentication(USER_UN, USER_PW.toCharArray());
}
authenticationService.authenticate(USER_UN, USER_PW.toCharArray());
feedControls = this.activityService.getFeedControls();
assertNotNull(feedControls);
assertTrue(feedControls.isEmpty());
assertFalse(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1")));
this.activityService.setFeedControl(new FeedControl("mySite1", null));
this.activityService.setFeedControl(new FeedControl("mySite1", "appTool1"));
this.activityService.setFeedControl(new FeedControl(null, "appTool2"));
feedControls = this.activityService.getFeedControls();
assertEquals(3, feedControls.size());
feedControls = this.activityService.getFeedControls(USER_UN);
assertEquals(3, feedControls.size());
assertTrue(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1")));
this.activityService.unsetFeedControl(new FeedControl("mySite1", "appTool1"));
assertFalse(this.activityService.existsFeedControl(new FeedControl("mySite1", "appTool1")));
feedControls = this.activityService.getFeedControls();
assertEquals(2, feedControls.size());
this.activityService.unsetFeedControl(new FeedControl("mySite1", null));
this.activityService.unsetFeedControl(new FeedControl(null, "appTool2"));
feedControls = this.activityService.getFeedControls();
assertEquals(0, feedControls.size());
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities;
public interface ActivityType
{
// pre-defined alfresco activity types
// generic fallback (if specific template is missing)
public final String GENERIC_FALLBACK = "org.alfresco.generic";
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionException;
/**
* Implementations of the abstract feed generator component are responsible for generating activity feed entries
*/
public abstract class AbstractFeedGenerator implements FeedGenerator
{
private static Log logger = LogFactory.getLog(AbstractFeedGenerator.class);
private static VmShutdownListener vmShutdownListener = new VmShutdownListener(AbstractFeedGenerator.class.getName());
private int maxItemsPerCycle = 100;
private ActivityPostDaoService postDaoService;
private AuthenticationService authenticationService;
private String repoEndPoint; // http://hostname:port/webapp (eg. http://localhost:8080/alfresco)
private RepoCtx ctx = null;
private boolean busy;
public void setPostDaoService(ActivityPostDaoService postDaoService)
{
this.postDaoService = postDaoService;
}
public void setAuthenticationService(AuthenticationService authenticationService)
{
this.authenticationService = authenticationService;
}
public void setRepoEndPoint(String repoEndPoint)
{
this.repoEndPoint = repoEndPoint;
}
public void setMaxItemsPerCycle(int maxItemsPerCycle)
{
this.maxItemsPerCycle = maxItemsPerCycle;
}
public int getMaxItemsPerCycle()
{
return this.maxItemsPerCycle;
}
public ActivityPostDaoService getPostDaoService()
{
return this.postDaoService;
}
public AuthenticationService getAuthenticationService()
{
return this.authenticationService;
}
public RepoCtx getWebScriptsCtx()
{
return this.ctx;
}
public void init() throws Exception
{
ctx = new RepoCtx(repoEndPoint);
busy = false;
}
/**
* Perform basic checks to ensure that the necessary dependencies were injected.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "postDaoService", postDaoService);
}
public void execute() throws JobExecutionException
{
if (busy)
{
logger.warn("Still busy ...");
return;
}
checkProperties();
try
{
// run at least one job cycle
boolean moreWork = true;
while (moreWork)
{
moreWork = generate();
}
}
catch (Throwable e)
{
// If the VM is shutting down, then ignore
if (vmShutdownListener.isVmShuttingDown())
{
// Ignore
}
else
{
logger.error("Exception during generation of feeds", e);
}
}
finally
{
busy = false;
}
}
protected abstract boolean generate() throws Exception;
}

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.util.Date;
import org.json.JSONException;
import org.json.JSONStringer;
/**
* Activity Feed DAO
*/
public class ActivityFeedDAO
{
private long id; // internal DB-generated id
private String activityType;
private String activitySummary;
private String activitySummaryFormat;
private String feedUserId;
private String postUserId;
private String siteNetwork;
private String appTool;
private Date postDate;
private Date feedDate; // for debug
private long postId; // for debug - not an explicit FK constraint, could be used to implement re-generate
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getActivitySummary()
{
return activitySummary;
}
public void setActivitySummary(String summary)
{
this.activitySummary = summary;
}
public String getFeedUserId()
{
return feedUserId;
}
public void setFeedUserId(String userid)
{
this.feedUserId = userid;
}
public String getPostUserId()
{
return postUserId;
}
public void setPostUserId(String userid)
{
this.postUserId = userid;
}
public String getActivitySummaryFormat()
{
return activitySummaryFormat;
}
public void setActivitySummaryFormat(String format)
{
this.activitySummaryFormat = format;
}
public String getSiteNetwork()
{
return siteNetwork;
}
public void setSiteNetwork(String siteNetwork)
{
this.siteNetwork = siteNetwork;
}
public String getActivityType()
{
return activityType;
}
public void setActivityType(String activityType)
{
this.activityType = activityType;
}
public Date getPostDate()
{
return postDate;
}
public void setPostDate(Date postDate)
{
this.postDate = postDate;
}
public long getPostId()
{
return postId;
}
public void setPostId(long postId)
{
this.postId = postId;
}
public Date getFeedDate()
{
return feedDate;
}
public void setFeedDate(Date feedDate)
{
this.feedDate = feedDate;
}
public String getAppTool()
{
return appTool;
}
public void setAppTool(String appTool)
{
this.appTool = appTool;
}
public String getJSONString() throws JSONException
{
return new JSONStringer()
.object()
.key("postUserId")
.value(postUserId)
.key("postDate")
.value(postDate)
.key("feedUserId")
.value(feedUserId)
.key("siteNetwork")
.value(siteNetwork)
.key("activityType")
.value(activityType)
.key("activitySummary")
.value(activitySummary)
.key("activitySummaryFormat")
.value(activitySummaryFormat)
.endObject().toString();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.alfresco.repo.activities.ibatis.ActivityDaoService;
/**
* Interface for activity feed DAO service
*/
public interface ActivityFeedDaoService extends ActivityDaoService
{
public long insertFeedEntry(ActivityFeedDAO activityFeed) throws SQLException;
public int deleteFeedEntries(Date keepDate) throws SQLException;
public List<ActivityFeedDAO> selectUserFeedEntries(String feedUserId, String format) throws SQLException;
public List<ActivityFeedDAO> selectUserFeedEntries(String feedUserId, String format, String siteId) throws SQLException;
public List<ActivityFeedDAO> selectSiteFeedEntries(String siteUserId, String format) throws SQLException;
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import org.quartz.JobExecutionException;
/**
* Interface for feed generator
*/
public interface FeedGenerator
{
public void execute() throws JobExecutionException;
public int getEstimatedGridSize();
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import org.alfresco.error.AlfrescoRuntimeException;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Executes scheduled feed generator quartz-job - refer to scheduled-jobs-context.xml
*/
public class FeedGeneratorJob implements Job
{
public FeedGeneratorJob()
{
}
/**
* Calls the feed generator to do its work
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
// extract the feed cleaner to use
Object feedGeneratorObj = jobData.get("feedGenerator");
if (feedGeneratorObj == null || !(feedGeneratorObj instanceof FeedGenerator))
{
throw new AlfrescoRuntimeException(
"FeedGeneratorObj data must contain valid 'feedGenerator' reference");
}
FeedGenerator feedGenerator = (FeedGenerator)feedGeneratorObj;
feedGenerator.execute();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.io.Serializable;
/**
* Interface for feed grid job
*/
public interface FeedGridJob
{
public void setArgument(JobSettings arg);
public JobSettings getArgument();
public Serializable execute() throws Exception;
}

View File

@@ -0,0 +1,636 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.repo.activities.feed.control.FeedControlDAO;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.template.ISO8601DateFormatMethod;
import org.alfresco.util.Base64;
import org.alfresco.util.JSONtoFmModel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import freemarker.cache.URLTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* Responsible for processing the individual task
*/
public abstract class FeedTaskProcessor
{
private static final Log logger = LogFactory.getLog(FeedTaskProcessor.class);
private static final String defaultFormat = "text";
private static final String[] formats = {"atomentry", "rss", "json", "html", "xml", defaultFormat};
private static final String URL_SERVICE_SITES = "/api/sites";
private static final String URL_MEMBERSHIPS = "/memberships";
private static final String URL_SERVICE_TEMPLATES = "/api/activities/templates";
private static final String URL_SERVICE_TEMPLATE = "/api/activities/template";
public void process(int jobTaskNode, long minSeq, long maxSeq, RepoCtx ctx) throws Exception
{
if (logger.isDebugEnabled())
{
logger.debug(">>> Process: jobTaskNode '" + jobTaskNode + "' from seq '" + minSeq + "' to seq '" + maxSeq + "' on this node from grid job.");
}
ActivityPostDAO selector = new ActivityPostDAO();
selector.setJobTaskNode(jobTaskNode);
selector.setMinId(minSeq);
selector.setMaxId(maxSeq);
selector.setStatus(ActivityPostDAO.STATUS.POSTED.toString());
String ticket = ctx.getTicket();
List<ActivityPostDAO> activityPosts = null;
int totalGenerated = 0;
try
{
activityPosts = selectPosts(selector);
if (logger.isDebugEnabled()) { logger.debug(">>> Process: " + activityPosts.size() + " activity posts"); }
Configuration cfg = getFreemarkerConfiguration(ctx);
Map<String, List<String>> activityTemplates = new HashMap<String, List<String>>(10);
// for each activity post ...
for (ActivityPostDAO activityPost : activityPosts)
{
String postingUserId = activityPost.getUserId();
String activityType = activityPost.getActivityType();
// eg. org.alfresco.folder.added -> added
String baseActivityType = getBaseActivityType(activityType);
List<String> fmTemplates = activityTemplates.get(baseActivityType);
if (fmTemplates == null)
{
// eg. org.alfresco.folder.added -> /org/alfresco/folder/added (note: the leading slash)
String templateSubPath = getTemplateSubPath(activityType);
fmTemplates = new ArrayList<String>(0);
while (true)
{
int idx = templateSubPath.lastIndexOf("/");
if (idx != -1)
{
templateSubPath = templateSubPath.substring(0, idx);
Map<String, List<String>> templates = null;
try
{
// Repository callback to get list of FreeMarker templates for given activity type
templates = getActivityTypeTemplates(ctx.getRepoEndPoint(), ticket, templateSubPath+"/");
}
catch (FileNotFoundException fnfe)
{
// ignore - path does not exist
}
if (templates != null)
{
if (templates.get(baseActivityType) != null)
{
// add templates, if format not already included
addMissingFormats(activityType, fmTemplates, templates.get(baseActivityType));
}
// special fallback case
if (templates.get("generic") != null)
{
// add templates, if format not already included
addMissingFormats(activityType, fmTemplates, templates.get("generic"));
}
}
}
else
{
break;
}
}
activityTemplates.put(baseActivityType, fmTemplates);
}
if (fmTemplates.size() == 0)
{
logger.error(">>> Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType );
updatePostStatus(activityPost.getId(), ActivityPostDAO.STATUS.ERROR);
continue;
}
Map<String, Object> model = null;
try
{
model = JSONtoFmModel.convertJSONObjectToMap(activityPost.getActivityData());
}
catch(JSONException je)
{
logger.error(">>> Skipping activity post " + activityPost.getId() + " due to invalid activity data: " + je);
updatePostStatus(activityPost.getId(), ActivityPostDAO.STATUS.ERROR);
continue;
}
model.put("activityType", activityPost.getActivityType());
model.put("siteNetwork", activityPost.getSiteNetwork());
model.put("userId", activityPost.getUserId());
model.put("id", activityPost.getId());
model.put("date", activityPost.getPostDate()); // post date rather than time that feed is generated
model.put("xmldate", new ISO8601DateFormatMethod());
model.put("repoEndPoint", ctx.getRepoEndPoint());
Set<String> connectedUsers = null;
if ((activityPost.getSiteNetwork() == null) || (activityPost.getSiteNetwork().length() == 0))
{
connectedUsers = new HashSet<String>(1);
}
else
{
try
{
// Repository callback to get site members
connectedUsers = getSiteMembers(ctx, activityPost.getSiteNetwork());
}
catch(Exception e)
{
logger.error(">>> Skipping activity post " + activityPost.getId() + " since failed to get site members: " + e);
updatePostStatus(activityPost.getId(), ActivityPostDAO.STATUS.ERROR);
continue;
}
}
connectedUsers.add(""); // add empty posting userid - to represent site feed !
try
{
startTransaction();
if (logger.isDebugEnabled())
{
logger.debug(">>> Process: " + connectedUsers.size() + " candidate connections for activity post " + activityPost.getId());
}
int excludedConnections = 0;
for (String connectedUser : connectedUsers)
{
List<FeedControlDAO> feedControls = null;
if (! connectedUser.equals(""))
{
feedControls = getFeedControls(connectedUser);
}
// filter based on opt-out feed controls (if any)
if (! acceptActivity(activityPost, feedControls))
{
excludedConnections++;
}
else
{
for (String fmTemplate : fmTemplates)
{
// determine format - based on template naming convention
String formatFound = null;
for (String format : formats)
{
if (fmTemplate.contains("."+format+"."))
{
formatFound = format;
break;
}
}
if (formatFound == null)
{
formatFound = defaultFormat;
logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'");
}
ActivityFeedDAO feed = new ActivityFeedDAO();
// 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(fmTemplate, cfg, model);
if (! activitySummary.equals(""))
{
feed.setActivitySummary(activitySummary);
feed.setActivitySummaryFormat(formatFound);
feed.setSiteNetwork(activityPost.getSiteNetwork());
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(), ActivityPostDAO.STATUS.PROCESSED);
commitTransaction();
if (logger.isDebugEnabled())
{
logger.debug(">>> Processed: " + (connectedUsers.size() - excludedConnections) + " connections for activity post " + activityPost.getId() + " (excluded " + excludedConnections + ")");
}
}
finally
{
endTransaction();
}
}
}
catch(SQLException se)
{
logger.error(se);
throw se;
}
finally
{
logger.info(">>> Generated " + totalGenerated + " activity feed entries for " + (activityPosts == null ? 0 : activityPosts.size()) + " activity posts");
}
}
public abstract void startTransaction() throws SQLException;
public abstract void commitTransaction() throws SQLException;
public abstract void endTransaction() throws SQLException;
public abstract List<ActivityPostDAO> selectPosts(ActivityPostDAO selector) throws SQLException;
public abstract List<FeedControlDAO> selectUserFeedControls(String userId) throws SQLException;
public abstract long insertFeedEntry(ActivityFeedDAO feed) throws SQLException;
public abstract int updatePostStatus(long id, ActivityPostDAO.STATUS status) throws SQLException;
protected String callWebScript(String urlString, String ticket) throws MalformedURLException, URISyntaxException, IOException
{
URL url = new URL(urlString);
if (logger.isDebugEnabled())
{
logger.debug(">>> Request URI: " + url.toURI());
}
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
conn.setRequestMethod("GET");
if (ticket != null)
{
// add Base64 encoded authorization header
// refer to: http://wiki.alfresco.com/wiki/Web_Scripts_Framework#HTTP_Basic_Authentication
conn.addRequestProperty("Authorization", "Basic " + Base64.encodeBytes(ticket.getBytes()));
}
String result = null;
InputStream is = null;
BufferedReader br = null;
try
{
is = conn.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String line = null;
StringBuffer sb = new StringBuffer();
while(((line = br.readLine()) !=null)) {
sb.append(line);
}
result = sb.toString();
if (logger.isDebugEnabled())
{
int responseCode = conn.getResponseCode();
logger.debug(">>> Response code: " + responseCode);
}
}
finally
{
if (br != null) { br.close(); };
if (is != null) { is.close(); };
}
return result;
}
protected Set<String> getSiteMembers(RepoCtx ctx, String siteId) throws Exception
{
Set<String> members = new HashSet<String>();
if ((siteId != null) && (siteId.length() != 0))
{
StringBuffer sbUrl = new StringBuffer();
sbUrl.append(ctx.getRepoEndPoint()).
append(URL_SERVICE_SITES).append("/").append(siteId).append(URL_MEMBERSHIPS);
String jsonArrayResult = callWebScript(sbUrl.toString(), ctx.getTicket());
if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0))
{
JSONArray ja = new JSONArray(jsonArrayResult);
for (int i = 0; i < ja.length(); i++)
{
JSONObject member = (JSONObject)ja.get(i);
JSONObject person = (JSONObject)member.getJSONObject("person");
members.add(person.getString("userName"));
}
}
}
return members;
}
protected Map<String, List<String>> getActivityTypeTemplates(String repoEndPoint, String ticket, String subPath) throws Exception
{
StringBuffer sbUrl = new StringBuffer();
sbUrl.append(repoEndPoint).append(URL_SERVICE_TEMPLATES).append("?p=").append(subPath).append("*").append("&format=json");
String jsonArrayResult = null;
try
{
jsonArrayResult = callWebScript(sbUrl.toString(), ticket);
}
catch (FileNotFoundException e)
{
return null;
}
List<String> allTemplateNames = new ArrayList<String>(10);
if ((jsonArrayResult != null) && (jsonArrayResult.length() != 0))
{
JSONArray ja = new JSONArray(jsonArrayResult);
for (int i = 0; i < ja.length(); i++)
{
String name = ja.getString(i);
if (! name.contains(" (Working Copy)."))
{
allTemplateNames.add(name);
}
}
}
Map<String, List<String>> activityTemplates = new HashMap<String, List<String>>(10);
for (String template : allTemplateNames)
{
// assume template path = <path>/<base-activityType>.<format>.ftl
// and base-activityType can contain "."
String baseActivityType = template;
int idx1 = baseActivityType.lastIndexOf("/");
if (idx1 != -1)
{
baseActivityType = baseActivityType.substring(idx1+1);
}
int idx2 = baseActivityType.lastIndexOf(".");
if (idx2 != -1)
{
int idx3 = baseActivityType.substring(0, idx2).lastIndexOf(".");
if (idx3 != -1)
{
baseActivityType = baseActivityType.substring(0, idx3);
List<String> activityTypeTemplateList = activityTemplates.get(baseActivityType);
if (activityTypeTemplateList == null)
{
activityTypeTemplateList = new ArrayList<String>(1);
activityTemplates.put(baseActivityType, activityTypeTemplateList);
}
activityTypeTemplateList.add(template);
}
}
}
return activityTemplates;
}
protected Configuration getFreemarkerConfiguration(RepoCtx ctx)
{
Configuration cfg = new Configuration();
cfg.setObjectWrapper(new DefaultObjectWrapper());
// custom template loader
cfg.setTemplateLoader(new TemplateWebScriptLoader(ctx.getRepoEndPoint(), ctx.getTicket()));
// TODO review i18n
cfg.setLocalizedLookup(false);
return cfg;
}
protected String processFreemarker(String fmTemplate, Configuration cfg, Map<String, Object> model) throws IOException, TemplateException, Exception
{
Template myTemplate = cfg.getTemplate(fmTemplate);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Writer out = new OutputStreamWriter(bos);
myTemplate.process(model, out);
out.flush();
return new String(bos.toByteArray());
}
protected List<FeedControlDAO> getFeedControls(String connectedUser) throws SQLException
{
// TODO cache for this run
return selectUserFeedControls(connectedUser);
}
protected boolean acceptActivity(ActivityPostDAO activityPost, List<FeedControlDAO> feedControls)
{
if (feedControls == null)
{
return true;
}
for (FeedControlDAO feedControl : feedControls)
{
if ((feedControl.getSiteNetwork() == null) && (feedControl.getAppTool() != null))
{
if (feedControl.getAppTool().equals(activityPost.getAppTool()))
{
// exclude this appTool (across sites)
return false;
}
}
else if ((feedControl.getAppTool() == null) && (feedControl.getSiteNetwork() != null))
{
if (feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork()))
{
// exclude this site (across appTools)
return false;
}
}
else if ((feedControl.getSiteNetwork() != null) && (feedControl.getAppTool() != null))
{
if ((feedControl.getSiteNetwork().equals(activityPost.getSiteNetwork())) &&
(feedControl.getAppTool().equals(activityPost.getAppTool())))
{
// exclude this appTool for this site
return false;
}
}
}
return true;
}
protected void addMissingFormats(String activityType, List<String> fmTemplates, List<String> templatesToAdd)
{
for (String templateToAdd : templatesToAdd)
{
int idx1 = templateToAdd.lastIndexOf(".");
if (idx1 != -1)
{
int idx2 = templateToAdd.substring(0, idx1).lastIndexOf(".");
if (idx2 != -1)
{
String templateFormat = templateToAdd.substring(idx2+1, idx1);
boolean found = false;
for (String fmTemplate : fmTemplates)
{
if (fmTemplate.contains("."+templateFormat+"."))
{
found = true;
}
}
if (! found)
{
if (logger.isDebugEnabled())
{
logger.debug(">>> Add template '" + templateToAdd + "' for type '" + activityType + "'");
}
fmTemplates.add(templateToAdd);
}
}
}
}
}
protected String getTemplateSubPath(String activityType)
{
return (! activityType.startsWith("/") ? "/" : "") + activityType.replace(".", "/");
}
protected String getBaseActivityType(String activityType)
{
String[] parts = activityType.split("\\.");
return (parts.length != 0 ? parts[parts.length-1] : "");
}
protected class TemplateWebScriptLoader extends URLTemplateLoader
{
private String repoEndPoint;
private String ticketId;
public TemplateWebScriptLoader(String repoEndPoint, String ticketId)
{
this.repoEndPoint = repoEndPoint;
this.ticketId = ticketId;
}
public URL getURL(String templatePath)
{
try
{
StringBuffer sb = new StringBuffer();
sb.append(this.repoEndPoint).
append(URL_SERVICE_TEMPLATE).append("?p=").append(templatePath).
append("&format=text").
append("&alf_ticket=").append(ticketId);
if (logger.isDebugEnabled())
{
logger.debug(">>> getURL: " + sb.toString());
}
return new URL(sb.toString());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Responsible for splitting the feed task into feed jobs (to be executed locally or on a grid)
*/
public class FeedTaskSplit
{
private static Log logger = LogFactory.getLog(FeedTaskSplit.class);
public Collection<JobSettings> split(int gridSize, JobSettings splitSettings)
{
long maxSequence = splitSettings.getMaxSeq();
if (logger.isDebugEnabled())
{
logger.debug("split: start - gridSize = " + gridSize + ", maxSequence = " + maxSequence);
}
long minSequence = maxSequence - splitSettings.getMaxItemsPerCycle() + 1;
splitSettings.setMinSeq((minSequence >= 0L ? minSequence : 0L));
List<JobSettings> jobs = new ArrayList<JobSettings>(gridSize);
int maxNodeHash = splitSettings.getJobTaskNode();
// note: gridSize may change between runs, hence use maximum node hash/bucket for this cycle
for (int n = 1; n <= maxNodeHash; n++)
{
// every job gets its own copy of the jobSettings (with different nodeHash) as an argument.
JobSettings jobSettings = splitSettings.clone();
jobSettings.setJobTaskNode(n);
jobs.add(jobSettings);
}
return jobs;
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.io.Serializable;
/**
* Job settings passed from grid task to grid job
*/
public class JobSettings implements Serializable
{
public static final long serialVersionUID = -3896042917378679686L;
private int jobTaskNode;
private long maxSeq;
private long minSeq;
private RepoCtx ctx;
private int maxItemsPerCycle;
public int getJobTaskNode()
{
return jobTaskNode;
}
public void setJobTaskNode(int jobTaskNode)
{
this.jobTaskNode = jobTaskNode;
}
public long getMaxSeq()
{
return maxSeq;
}
public void setMaxSeq(long maxSeq)
{
this.maxSeq = maxSeq;
}
public long getMinSeq()
{
return minSeq;
}
public void setMinSeq(long minSeq)
{
this.minSeq = minSeq;
}
public RepoCtx getWebScriptsCtx() {
return ctx;
}
public void setWebScriptsCtx(RepoCtx ctx) {
this.ctx = ctx;
}
public int getMaxItemsPerCycle()
{
return maxItemsPerCycle;
}
public void setMaxItemsPerCycle(int maxItemsPerCycle)
{
this.maxItemsPerCycle = maxItemsPerCycle;
}
public JobSettings clone()
{
JobSettings js = new JobSettings();
js.setMaxItemsPerCycle(this.maxItemsPerCycle);
js.setMaxSeq(this.maxSeq);
js.setMinSeq(this.minSeq);
js.setJobTaskNode(this.jobTaskNode);
js.setWebScriptsCtx(this.ctx); // note: shallow copy
return js;
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed;
import java.io.Serializable;
/**
* Repository context passed from grid task to grid job
*/
public class RepoCtx implements Serializable
{
private String repoEndPoint; // http://hostname:port/webapp (eg. http://localhost:8080/alfresco)
private String ticket;
public static final long serialVersionUID = -3896042917378679686L;
public RepoCtx(String repoEndPoint)
{
this.repoEndPoint = repoEndPoint.endsWith("/") ? repoEndPoint.substring(0, repoEndPoint.length()-1) : repoEndPoint;
}
public String getRepoEndPoint() {
return repoEndPoint;
}
public String getTicket()
{
return ticket;
}
public void setTicket(String ticket)
{
this.ticket = ticket;
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.cleanup;
import java.sql.SQLException;
import java.util.Date;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.activities.feed.ActivityFeedDaoService;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionException;
/**
* The feed cleaner component is responsible for purging 'obsolete' activity feed entries
*/
public class FeedCleaner
{
private static Log logger = LogFactory.getLog(FeedCleaner.class);
private static VmShutdownListener vmShutdownListener = new VmShutdownListener(FeedCleaner.class.getName());
private int maxAgeMins = 0;
private ActivityFeedDaoService feedDaoService;
public void setFeedDaoService(ActivityFeedDaoService feedDaoService)
{
this.feedDaoService = feedDaoService;
}
public void setMaxAgeMins(int mins)
{
this.maxAgeMins = mins;
}
/**
* Perform basic checks to ensure that the necessary dependencies were injected.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "feedDaoService", feedDaoService);
// check the max age
if (maxAgeMins <= 0)
{
throw new AlfrescoRuntimeException("Property 'maxAgeMins' must be greater than 0");
}
}
public void execute() throws JobExecutionException
{
checkProperties();
try
{
long nowTimeOffset = new Date().getTime();
long keepTimeOffset = nowTimeOffset - (maxAgeMins*60*1000); // millsecs = mins * 60 secs * 1000 msecs
Date keepDate = new Date(keepTimeOffset);
// clean old entries
int deletedCount = feedDaoService.deleteFeedEntries(keepDate);
if (logger.isDebugEnabled())
{
logger.debug("Cleaned " + deletedCount + " entries (upto " + keepDate + ", max age " + maxAgeMins + " mins)");
}
}
catch (SQLException e)
{
logger.error("Exception during cleanup of feeds", e);
throw new JobExecutionException(e);
}
catch (Throwable e)
{
// If the VM is shutting down, then ignore
if (vmShutdownListener.isVmShuttingDown())
{
// Ignore
}
else
{
logger.error("Exception during cleanup of feeds", e);
}
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.cleanup;
import org.alfresco.error.AlfrescoRuntimeException;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Executes scheduled feed cleaner quartz-job - refer to scheduled-jobs-context.xml
*/
public class FeedCleanupJob implements Job
{
public FeedCleanupJob()
{
}
/**
* Calls the feed cleaner to do its work
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
// extract the feed cleaner to use
Object feedCleanerObj = jobData.get("feedCleaner");
if (feedCleanerObj == null || !(feedCleanerObj instanceof FeedCleaner))
{
throw new AlfrescoRuntimeException(
"FeedCleanupJob data must contain valid 'feedCleaner' reference");
}
FeedCleaner feedCleaner = (FeedCleaner)feedCleanerObj;
feedCleaner.execute();
}
}

View File

@@ -0,0 +1,117 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.control;
import java.util.Date;
import org.alfresco.service.cmr.activities.FeedControl;
/**
* Activity Feed Control DAO
*/
public class FeedControlDAO
{
private long id; // internal DB-generated id
private String feedUserId;
private String siteNetwork;
private String appTool;
private Date lastModified; // when inserted
// TODO - review - deleted feed controls are not kept and available feed controls are currently retrieved during generation, hence
// it is possible for a feed control to be applied even if lastModified is greater than postDate - could check the date !
// it is also possible for a feed control to not be applied if it is deleted just after the post - would need to keep, at least until next generation
public FeedControlDAO()
{
}
public FeedControlDAO(String feedUserId)
{
this.feedUserId = feedUserId;
}
public FeedControlDAO(String feedUserId, FeedControl feedControl)
{
this.feedUserId = feedUserId;
this.siteNetwork = feedControl.getSiteId();
this.appTool = feedControl.getAppToolId();
}
public FeedControl getFeedControl()
{
return new FeedControl(this.siteNetwork, this.appTool);
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getSiteNetwork()
{
return siteNetwork;
}
public void setSiteNetwork(String siteNetwork)
{
this.siteNetwork = siteNetwork;
}
public String getAppTool()
{
return appTool;
}
public void setAppTool(String appTool)
{
this.appTool = appTool;
}
public String getFeedUserId()
{
return feedUserId;
}
public void setFeedUserId(String feedUserId)
{
this.feedUserId = feedUserId;
}
public Date getLastModified()
{
return lastModified;
}
public void setLastModified(Date lastModified)
{
this.lastModified = lastModified;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.control;
import java.sql.SQLException;
import java.util.List;
/**
* Interface for user activity feed controls DAO service
*/
public interface FeedControlDaoService
{
public long insertFeedControl(FeedControlDAO activityFeedControl) throws SQLException;
public int deleteFeedControl(FeedControlDAO activityFeedControl) throws SQLException;
public List<FeedControlDAO> selectFeedControls(String userId) throws SQLException;
public long selectFeedControl(FeedControlDAO activityFeedControl) throws SQLException;
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.local;
import java.util.Collection;
import java.util.Date;
import org.alfresco.repo.activities.feed.AbstractFeedGenerator;
import org.alfresco.repo.activities.feed.FeedGridJob;
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
import org.alfresco.repo.activities.feed.JobSettings;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The local (ie. not grid) feed generator component is responsible for generating feed entries
*/
public class LocalFeedGenerator extends AbstractFeedGenerator
{
private static Log logger = LogFactory.getLog(LocalFeedGenerator.class);
private FeedTaskProcessor feedTaskProcessor;
public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor)
{
this.feedTaskProcessor = feedTaskProcessor;
}
public int getEstimatedGridSize()
{
return 1;
}
public void init() throws Exception
{
super.init();
}
protected boolean generate() throws Exception
{
Long maxSequence = getPostDaoService().getMaxActivitySeq();
Integer maxNodeHash = getPostDaoService().getMaxNodeHash();
String gridName = "local";
if (maxSequence != null)
{
if (logger.isDebugEnabled())
{
logger.debug(">>> Execute job cycle: " + gridName + " (maxSeq: " + maxSequence + ")");
}
long startTime = new Date().getTime();
// TODO ... or push this upto to job scheduler ... ?
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork()
{
getWebScriptsCtx().setTicket(getAuthenticationService().getCurrentTicket());
return null;
}
}, AuthenticationUtil.getSystemUserName()); // need web scripts to support System-level authentication ... see RepositoryContainer !
JobSettings js = new JobSettings();
js.setMaxSeq(maxSequence);
js.setJobTaskNode(maxNodeHash);
js.setWebScriptsCtx(getWebScriptsCtx());
js.setMaxItemsPerCycle(getMaxItemsPerCycle());
LocalFeedTaskSplitter splitter = new LocalFeedTaskSplitter();
splitter.setFeedTaskProcessor(feedTaskProcessor);
Collection<FeedGridJob> jobs = splitter.split(getEstimatedGridSize(), js);
for (FeedGridJob job : jobs)
{
job.execute();
}
long endTime = new Date().getTime();
if (logger.isDebugEnabled())
{
logger.debug(">>> Finish job cycle: " + gridName + " (time taken (secs) = " + ((endTime - startTime) / 1000) + ")");
}
return true;
}
else
{
if (logger.isDebugEnabled())
{
logger.debug(">>> No work to be done for this job cycle: " + gridName);
}
return false;
}
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.local;
import java.io.Serializable;
import org.alfresco.repo.activities.feed.FeedGridJob;
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
import org.alfresco.repo.activities.feed.JobSettings;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Implementation to execute local (ie. not grid) feed job
*/
public class LocalFeedGridJob implements FeedGridJob
{
private static final Log logger = LogFactory.getLog(LocalFeedGridJob.class);
private JobSettings arg;
private FeedTaskProcessor feedTaskProcessor;
public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor)
{
this.feedTaskProcessor = feedTaskProcessor;
}
public Serializable execute() throws Exception
{
JobSettings js = getArgument();
if (logger.isDebugEnabled()) { logger.debug(">>> Execute: nodehash '" + js.getJobTaskNode() + "' from seq '" + js.getMinSeq() + "' to seq '" + js.getMaxSeq() + "' on this node"); }
try
{
feedTaskProcessor.process(js.getJobTaskNode(), js.getMinSeq(), js.getMaxSeq(), js.getWebScriptsCtx());
}
catch (Exception e)
{
logger.error(e);
throw new Exception(e.getMessage(), e.getCause());
}
// This job does not return any result.
return null;
}
public void setArgument(JobSettings arg)
{
this.arg = arg;
}
public JobSettings getArgument()
{
return this.arg;
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.local;
import java.sql.SQLException;
import java.util.List;
import org.alfresco.repo.activities.feed.ActivityFeedDAO;
import org.alfresco.repo.activities.feed.ActivityFeedDaoService;
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
import org.alfresco.repo.activities.feed.control.FeedControlDAO;
import org.alfresco.repo.activities.feed.control.FeedControlDaoService;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
import com.ibatis.sqlmap.client.SqlMapClient;
/**
* The local (ie. not grid) feed task processor is responsible for processing the individual feed job
*/
public class LocalFeedTaskProcessor extends FeedTaskProcessor
{
private ActivityPostDaoService postDaoService;
private ActivityFeedDaoService feedDaoService;
private FeedControlDaoService feedControlDaoService;
// used to start/end/commit transaction
// note: currently assumes that all dao services are configured with this mapper / data source
private SqlMapClient sqlMapper;
public void setPostDaoService(ActivityPostDaoService postDaoService)
{
this.postDaoService = postDaoService;
}
public void setFeedDaoService(ActivityFeedDaoService feedDaoService)
{
this.feedDaoService = feedDaoService;
}
public void setFeedControlDaoService(FeedControlDaoService feedControlDaoService)
{
this.feedControlDaoService = feedControlDaoService;
}
public void setSqlMapClient(SqlMapClient sqlMapper)
{
this.sqlMapper = sqlMapper;
}
public void startTransaction() throws SQLException
{
sqlMapper.startTransaction();
}
public void commitTransaction() throws SQLException
{
sqlMapper.commitTransaction();
}
public void endTransaction() throws SQLException
{
sqlMapper.endTransaction();
}
public List<ActivityPostDAO> selectPosts(ActivityPostDAO selector) throws SQLException
{
return postDaoService.selectPosts(selector);
}
public long insertFeedEntry(ActivityFeedDAO feed) throws SQLException
{
return feedDaoService.insertFeedEntry(feed);
}
public int updatePostStatus(long id, ActivityPostDAO.STATUS status) throws SQLException
{
return postDaoService.updatePostStatus(id, status);
}
public List<FeedControlDAO> selectUserFeedControls(String userId) throws SQLException
{
return feedControlDaoService.selectFeedControls(userId);
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.feed.local;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.alfresco.repo.activities.feed.FeedGridJob;
import org.alfresco.repo.activities.feed.FeedTaskProcessor;
import org.alfresco.repo.activities.feed.FeedTaskSplit;
import org.alfresco.repo.activities.feed.JobSettings;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The local feed task splitter is responsible for splitting the feed task into feed jobs
*/
public class LocalFeedTaskSplitter
{
private static final Log logger = LogFactory.getLog(LocalFeedTaskSplitter.class);
private FeedTaskProcessor feedTaskProcessor;
public void setFeedTaskProcessor(FeedTaskProcessor feedTaskProcessor)
{
this.feedTaskProcessor = feedTaskProcessor;
}
public Collection<FeedGridJob> split(int gridSize, Object o) throws Exception
{
try
{
FeedTaskSplit feedSplitter = new FeedTaskSplit();
Collection<JobSettings> jobs = feedSplitter.split(gridSize, (JobSettings)o);
List<FeedGridJob> gridJobs = new ArrayList<FeedGridJob>(jobs.size());
for (JobSettings job : jobs)
{
LocalFeedGridJob gridJob = new LocalFeedGridJob();
gridJob.setFeedTaskProcessor(feedTaskProcessor);
gridJob.setArgument(job);
gridJobs.add(gridJob);
}
return gridJobs;
//return (Collection<FeedGridJob>)feedSplitter.split(gridSize, (JobSettings)o, new LocalFeedGridJob());
}
catch (Exception e)
{
logger.equals(e);
throw new Exception(e.getMessage());
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.ibatis;
import java.sql.SQLException;
/**
* Common interface for activity DAO service
*/
public interface ActivityDaoService
{
public void startTransaction() throws SQLException;
public void commitTransaction() throws SQLException;
public void endTransaction() throws SQLException;
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.ibatis;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.alfresco.repo.activities.feed.ActivityFeedDAO;
import org.alfresco.repo.activities.feed.ActivityFeedDaoService;
public class IBatisActivityFeedDaoServiceImpl extends IBatisSqlMapper implements ActivityFeedDaoService
{
public long insertFeedEntry(ActivityFeedDAO activityFeed) throws SQLException
{
Long id = (Long)getSqlMapClient().insert("insert.activity.feed", activityFeed);
return (id != null ? id : -1);
}
public int deleteFeedEntries(Date keepDate) throws SQLException
{
return getSqlMapClient().delete("delete.activity.feed.entries.older.than.date", keepDate);
}
@SuppressWarnings("unchecked")
public List<ActivityFeedDAO> selectUserFeedEntries(String feedUserId, String format) throws SQLException
{
ActivityFeedDAO params = new ActivityFeedDAO();
params.setFeedUserId(feedUserId);
params.setActivitySummaryFormat(format);
// where feed user is me and post user is not me
return (List<ActivityFeedDAO>)getSqlMapClient().queryForList("select.activity.feed.for.feeduser", params);
}
@SuppressWarnings("unchecked")
public List<ActivityFeedDAO> selectUserFeedEntries(String feedUserId, String format, String siteId) throws SQLException
{
ActivityFeedDAO params = new ActivityFeedDAO();
params.setFeedUserId(feedUserId);
params.setPostUserId(feedUserId);
params.setActivitySummaryFormat(format);
params.setSiteNetwork(siteId);
// where feed user is me and post user is not me
return (List<ActivityFeedDAO>)getSqlMapClient().queryForList("select.activity.feed.for.feeduser.and.site", params);
}
@SuppressWarnings("unchecked")
public List<ActivityFeedDAO> selectSiteFeedEntries(String siteId, String format) throws SQLException
{
ActivityFeedDAO params = new ActivityFeedDAO();
params.setSiteNetwork(siteId);
params.setActivitySummaryFormat(format);
// where feed user is me and post user is not me
return (List<ActivityFeedDAO>)getSqlMapClient().queryForList("select.activity.feed.for.site", params);
}
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.ibatis;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
public class IBatisActivityPostDaoServiceImpl extends IBatisSqlMapper implements ActivityPostDaoService
{
@SuppressWarnings("unchecked")
public List<ActivityPostDAO> selectPosts(ActivityPostDAO activityPost) throws SQLException
{
if ((activityPost.getJobTaskNode() != -1) &&
(activityPost.getMinId() != -1) &&
(activityPost.getMaxId() != -1) &&
(activityPost.getStatus() != null))
{
return (List<ActivityPostDAO>)getSqlMapClient().queryForList("select.activity.posts", activityPost);
}
else if (activityPost.getStatus() != null)
{
return (List<ActivityPostDAO>)getSqlMapClient().queryForList("select.activity.posts.by.status.only", activityPost);
}
else
{
return new ArrayList<ActivityPostDAO>(0);
}
}
public Long getMaxActivitySeq() throws SQLException
{
return (Long)getSqlMapClient().queryForObject("select.activity.post.max.seq");
}
public Long getMinActivitySeq() throws SQLException
{
return (Long)getSqlMapClient().queryForObject("select.activity.post.min.seq");
}
public Integer getMaxNodeHash() throws SQLException
{
return (Integer)getSqlMapClient().queryForObject("select.activity.post.max.jobtasknode");
}
public int updatePost(long id, String siteNetwork, String activityData, ActivityPostDAO.STATUS status) throws SQLException
{
ActivityPostDAO post = new ActivityPostDAO();
post.setId(id);
post.setSiteNetwork(siteNetwork);
post.setActivityData(activityData);
post.setStatus(status.toString());
post.setLastModified(new Date());
return getSqlMapClient().update("update.activity.post.data", post);
}
public int updatePostStatus(long id, ActivityPostDAO.STATUS status) throws SQLException
{
ActivityPostDAO post = new ActivityPostDAO();
post.setId(id);
post.setStatus(status.toString());
post.setLastModified(new Date());
return getSqlMapClient().update("update.activity.post.status", post);
}
public int deletePosts(Date keepDate, ActivityPostDAO.STATUS status) throws SQLException
{
ActivityPostDAO params = new ActivityPostDAO();
params.setPostDate(keepDate);
params.setStatus(status.toString());
return getSqlMapClient().delete("delete.activity.posts.older.than.date", params);
}
public long insertPost(ActivityPostDAO activityPost) throws SQLException
{
Long id = (Long)getSqlMapClient().insert("insert.activity.post", activityPost);
return (id != null ? id : -1);
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.ibatis;
import java.sql.SQLException;
import java.util.List;
import org.alfresco.repo.activities.feed.control.FeedControlDAO;
import org.alfresco.repo.activities.feed.control.FeedControlDaoService;
public class IBatisFeedControlDaoServiceImpl extends IBatisSqlMapper implements FeedControlDaoService
{
public long insertFeedControl(FeedControlDAO activityFeedControl) throws SQLException
{
Long id = (Long)getSqlMapClient().insert("insert.activity.feedcontrol", activityFeedControl);
return (id != null ? id : -1);
}
public int deleteFeedControl(FeedControlDAO activityFeedControl) throws SQLException
{
return getSqlMapClient().delete("delete.activity.feedcontrol", activityFeedControl);
}
@SuppressWarnings("unchecked")
public List<FeedControlDAO> selectFeedControls(String feedUserId) throws SQLException
{
FeedControlDAO params = new FeedControlDAO(feedUserId);
return (List<FeedControlDAO>)getSqlMapClient().queryForList("select.activity.feedcontrols.for.user", params);
}
public long selectFeedControl(FeedControlDAO activityFeedControl) throws SQLException
{
Long id = (Long)getSqlMapClient().queryForObject("select.activity.feedcontrol", activityFeedControl);
return (id != null ? id : -1);
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.ibatis;
import java.sql.SQLException;
import com.ibatis.sqlmap.client.SqlMapClient;
public class IBatisSqlMapper implements ActivityDaoService
{
private SqlMapClient sqlMapper;
public void setSqlMapClient(SqlMapClient sqlMapper)
{
this.sqlMapper = sqlMapper;
}
public SqlMapClient getSqlMapClient()
{
return this.sqlMapper;
}
public void startTransaction() throws SQLException
{
sqlMapper.startTransaction();
}
public void commitTransaction() throws SQLException
{
sqlMapper.commitTransaction();
}
public void endTransaction() throws SQLException
{
sqlMapper.endTransaction();
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post;
import java.util.Date;
/**
* Activity Post DAO
*/
public class ActivityPostDAO
{
public enum STATUS { POSTED, PENDING, PROCESSED, ERROR };
private long id; // internal DB-generated sequence id
private String activityData;
private String activityType;
private String userId;
private int jobTaskNode = -1;
private String siteNetwork;
private String appTool;
private String status;
private Date postDate;
private Date lastModified; // for debug
// for selector
private long minId = -1;
private long maxId = -1;
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getUserId()
{
return userId;
}
public void setUserId(String userId)
{
this.userId = userId;
}
public int getJobTaskNode()
{
return jobTaskNode;
}
public void setJobTaskNode(int jobTaskNode)
{
this.jobTaskNode = jobTaskNode;
}
public long getMinId()
{
return minId;
}
public void setMinId(long minId)
{
this.minId = minId;
}
public long getMaxId()
{
return maxId;
}
public void setMaxId(long maxId)
{
this.maxId = maxId;
}
public String getSiteNetwork()
{
return siteNetwork;
}
public void setSiteNetwork(String siteNetwork)
{
this.siteNetwork = siteNetwork;
}
public String getActivityData()
{
return activityData;
}
public void setActivityData(String activityData)
{
this.activityData = activityData;
}
public String getActivityType()
{
return activityType;
}
public void setActivityType(String activityType)
{
this.activityType = activityType;
}
public Date getPostDate()
{
return postDate;
}
public void setPostDate(Date postDate)
{
this.postDate = postDate;
}
public String getStatus()
{
return status;
}
public void setStatus(String status)
{
this.status = status;
}
public Date getLastModified()
{
return lastModified;
}
public void setLastModified(Date lastModified)
{
this.lastModified = lastModified;
}
public String getAppTool()
{
return appTool;
}
public void setAppTool(String appTool)
{
this.appTool = appTool;
}
// for debug only
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("ActivityPost\n[");
sb.append("id=").append(id).append(",");
sb.append("status=").append(status).append(",");
sb.append("postDate=").append(postDate).append(",");
sb.append("userId=").append(userId).append(",");
sb.append("siteNetwork=").append(siteNetwork).append(",");
sb.append("appTool=").append(appTool).append(",");
sb.append("type=").append(activityType).append(",");
sb.append("jobTaskNode=").append(jobTaskNode).append(",");
sb.append("data=\n").append(activityData).append("\n]");
return sb.toString();
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import org.alfresco.repo.activities.ibatis.ActivityDaoService;
/**
* Interface for activity post DAO service
*/
public interface ActivityPostDaoService extends ActivityDaoService
{
public List<ActivityPostDAO> selectPosts(ActivityPostDAO activityPost) throws SQLException;
public Long getMaxActivitySeq() throws SQLException;
public Long getMinActivitySeq() throws SQLException;
public Integer getMaxNodeHash() throws SQLException;
public int deletePosts(Date keepDate, ActivityPostDAO.STATUS status) throws SQLException;
public long insertPost(ActivityPostDAO activityPost) throws SQLException;
public int updatePost(long id, String network, String activityData, ActivityPostDAO.STATUS status) throws SQLException;
public int updatePostStatus(long id, ActivityPostDAO.STATUS status) throws SQLException;
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post.cleanup;
import java.sql.SQLException;
import java.util.Date;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionException;
/**
* Thr post cleaner component is responsible for purging 'obsolete' activity posts
*/
public class PostCleaner
{
private static Log logger = LogFactory.getLog(PostCleaner.class);
private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostCleaner.class.getName());
private int maxAgeMins = 0;
private ActivityPostDaoService postDaoService;
public void setPostDaoService(ActivityPostDaoService postDaoService)
{
this.postDaoService = postDaoService;
}
public void setMaxAgeMins(int mins)
{
this.maxAgeMins = mins;
}
/**
* Perform basic checks to ensure that the necessary dependencies were injected.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "postDaoService", postDaoService);
// check the max age
if (maxAgeMins <= 0)
{
throw new AlfrescoRuntimeException("Property 'maxAgeMins' must be greater than 0");
}
}
public void execute() throws JobExecutionException
{
checkProperties();
try
{
long nowTimeOffset = new Date().getTime();
long keepTimeOffset = nowTimeOffset - (maxAgeMins*60*1000); // millsecs = mins * 60 secs * 1000 msecs
Date keepDate = new Date(keepTimeOffset);
// clean old entries - PROCESSED - does not clean POSTED or PENDING, which will need to be done manually, if stuck
int deletedCount = postDaoService.deletePosts(keepDate, ActivityPostDAO.STATUS.PROCESSED);
if (logger.isDebugEnabled())
{
logger.debug("Cleaned " + deletedCount + " entries (upto " + keepDate + ", max age " + maxAgeMins + " mins)");
}
}
catch (SQLException e)
{
logger.error("Exception during cleanup of posts", e);
throw new JobExecutionException(e);
}
catch (Throwable e)
{
// If the VM is shutting down, then ignore
if (vmShutdownListener.isVmShuttingDown())
{
// Ignore
}
else
{
logger.error("Exception during cleanup of posts", e);
}
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post.cleanup;
import org.alfresco.error.AlfrescoRuntimeException;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Executes scheduled post cleaner quartz-job - refer to scheduled-jobs-context.xml
*/
public class PostCleanupJob implements Job
{
public PostCleanupJob()
{
}
/**
* Calls the post cleaner to do its work
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
// extract the post cleaner to use
Object postCleanerObj = jobData.get("postCleaner");
if (postCleanerObj == null || !(postCleanerObj instanceof PostCleaner))
{
throw new AlfrescoRuntimeException(
"FeedCleanupJob data must contain valid 'postCleaner' reference");
}
PostCleaner postCleaner = (PostCleaner)postCleanerObj;
postCleaner.execute();
}
}

View File

@@ -0,0 +1,292 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post.lookup;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.activities.post.ActivityPostDAO;
import org.alfresco.repo.activities.post.ActivityPostDaoService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.JSONtoFmModel;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.VmShutdownListener;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONStringer;
import org.quartz.JobExecutionException;
/**
* The post lookup component is responsible for updating posts that require a secondary lookup (to get additional activity data)
*/
public class PostLookup
{
private static Log logger = LogFactory.getLog(PostLookup.class);
private static VmShutdownListener vmShutdownListener = new VmShutdownListener(PostLookup.class.getName());
private ActivityPostDaoService postDaoService;
private NodeService nodeService;
private PermissionService permissionService;
private TransactionService transactionService;
private PersonService personService;
public void setPostDaoService(ActivityPostDaoService postDaoService)
{
this.postDaoService = postDaoService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setPersonService(PersonService personService)
{
this.personService = personService;
}
/**
* Perform basic checks to ensure that the necessary dependencies were injected.
*/
private void checkProperties()
{
PropertyCheck.mandatory(this, "postDaoService", postDaoService);
PropertyCheck.mandatory(this, "nodeService", nodeService);
PropertyCheck.mandatory(this, "permissionService", permissionService);
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "personService", personService);
}
public void execute() throws JobExecutionException
{
checkProperties();
try
{
ActivityPostDAO params = new ActivityPostDAO();
params.setStatus(ActivityPostDAO.STATUS.PENDING.toString());
List<ActivityPostDAO> activityPosts = postDaoService.selectPosts(params);
if (activityPosts.size() > 0)
{
logger.info("Update: " + activityPosts.size() + " activity posts");
}
for (ActivityPostDAO activityPost : activityPosts)
{
Map<String, Object> model = JSONtoFmModel.convertJSONObjectToMap(activityPost.getActivityData());
String postUserId = activityPost.getUserId();
String name = (String)model.get("name"); // can be null
String nodeRefStr = (String)model.get("nodeRef"); // required
NodeRef nodeRef = new NodeRef(nodeRefStr);
String parentNodeRefStr = (String)model.get("parentNodeRef"); // optional
NodeRef parentNodeRef = null;
if (parentNodeRefStr != null)
{
parentNodeRef = new NodeRef(parentNodeRefStr);
}
String typeQName = (String)model.get("typeQName");
try
{
postDaoService.startTransaction();
Pair<String, String> siteNetworkActivityData = lookup(activityPost.getSiteNetwork(), nodeRef, name, typeQName, parentNodeRef, postUserId);
activityPost.setSiteNetwork(siteNetworkActivityData.getFirst());
activityPost.setActivityData(siteNetworkActivityData.getSecond());
activityPost.setLastModified(new Date());
postDaoService.updatePost(activityPost.getId(), activityPost.getSiteNetwork(), activityPost.getActivityData(), ActivityPostDAO.STATUS.POSTED);
if (logger.isDebugEnabled())
{
activityPost.setStatus(ActivityPostDAO.STATUS.POSTED.toString()); // for debug output
logger.debug("Updated: " + activityPost);
}
postDaoService.commitTransaction();
}
catch (JSONException e)
{
// log error, but consume exception (skip this post)
logger.error(e);
}
catch (SQLException e)
{
logger.error("Exception during update of post", e);
throw new JobExecutionException(e);
}
finally
{
postDaoService.endTransaction();
}
}
}
catch (SQLException e)
{
logger.error("Exception during select of posts", e);
throw new JobExecutionException(e);
}
catch (Throwable e)
{
// If the VM is shutting down, then ignore
if (vmShutdownListener.isVmShuttingDown())
{
// Ignore
}
else
{
logger.error("Exception during update of posts", e);
}
}
}
private Pair<String, String> lookup(final String networkIn, final NodeRef nodeRef, final String nameIn, final String typeQNameIn, final NodeRef parentNodeRef, final String postUserId) throws JSONException
{
return AuthenticationUtil.runAs(new RunAsWork<Pair<String, String>>()
{
public Pair<String, String> doWork() throws Exception
{
RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
// wrap to make the request in a transaction
RetryingTransactionCallback<Pair<String, String>> lookup = new RetryingTransactionCallback<Pair<String, String>>()
{
public Pair<String, String> execute() throws Throwable
{
String jsonString = null;
String displayPath = "";
String name = nameIn;
String network = networkIn;
String typeQName = typeQNameIn;
Path path = null;
String firstName = "";
String lastName = "";
if (personService.personExists(postUserId))
{
NodeRef personRef = personService.getPerson(postUserId);
firstName = (String)nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
lastName = (String)nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
}
if (((name == null) || (name.length() == 0)) && (nodeRef != null) && (nodeService.exists(nodeRef)))
{
// node exists, lookup node name
if ((name == null) || (name.length() == 0))
{
name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
}
path = nodeService.getPath(nodeRef);
// TODO: missing the prefix ?
typeQName = nodeService.getType(nodeRef).toPrefixString();
}
if (((path == null) || (path.size() == 0)) && (parentNodeRef != null) && (nodeService.exists(parentNodeRef)))
{
// parent node exists, lookup parent node path
path = nodeService.getPath(parentNodeRef);
}
if (path != null)
{
// lookup display path
displayPath = path.toDisplayPath(nodeService, permissionService);
// note: for now, also tack on the node name
displayPath += "/" + name;
}
if (name == null)
{
name = "";
}
if (typeQName == null)
{
typeQName = "";
}
// activity data
jsonString = new JSONStringer()
.object()
.key("name")
.value(name)
.key("nodeRef")
.value(nodeRef)
.key("typeQName")
.value(typeQName)
.key("displayPath")
.value(displayPath)
.key("firstName")
.value(firstName)
.key("lastName")
.value(lastName)
.endObject().toString();
return new Pair<String, String>(network, jsonString);
}
};
// execute in txn
return txnHelper.doInTransaction(lookup, true);
}
}, AuthenticationUtil.getSystemUserName());
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.post.lookup;
import org.alfresco.error.AlfrescoRuntimeException;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Executes scheduled post lookup quartz-job - refer to scheduled-jobs-context.xml
*/
public class PostLookupJob implements Job
{
public PostLookupJob()
{
}
/**
* Calls the post lookup to do its work
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap jobData = context.getJobDetail().getJobDataMap();
// extract the post cleaner to use
Object postLookupObj = jobData.get("postLookup");
if (postLookupObj == null || !(postLookupObj instanceof PostLookup))
{
throw new AlfrescoRuntimeException(
"FeedCleanupJob data must contain valid 'postLookup' reference");
}
PostLookup postLookup = (PostLookup)postLookupObj;
postLookup.execute();
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.repo.activities.script;
import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
/**
* Scripted Activity Service for posting activities.
*/
public final class Activity extends BaseScopableProcessorExtension
{
private ActivityService activityService;
/**
* Set the activity service
*
* @param activityService the activity service
*/
public void setActivityService(ActivityService activityService)
{
this.activityService = activityService;
}
/**
* Post a custom activity type
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param jsonActivityData - required
*/
public void postActivity(String activityType, String siteId, String appTool, String jsonActivityData)
{
activityService.postActivity(activityType, siteId, appTool, jsonActivityData);
}
/**
* Post a pre-defined activity type - activity data will be looked-up asynchronously, including:
*
* name
* displayPath
* typeQName
* firstName (of posting user)
* lastName (of posting user)
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - do not use for deleted (or about to be deleted) nodeRef
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef)
{
activityService.postActivity(activityType, siteId, appTool, nodeRef);
}
/**
* Post a pre-defined activity type - eg. for checked-out nodeRef or renamed nodeRef
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - do not use deleted (or about to be deleted) nodeRef
* @param beforeName - optional - name of node (eg. prior to name change)
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String beforeName)
{
activityService.postActivity(activityType, siteId, appTool, nodeRef, beforeName);
}
/**
* Post a pre-defined activity type - eg. for deleted nodeRef
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - can be a deleted (or about to be deleted) nodeRef
* @param name - optional - name of name
* @param typeQName - optional - type of node
* @param parentNodeRef - required - used to lookup path/displayPath
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef)
{
activityService.postActivity(activityType, siteId, appTool, nodeRef, name, typeQName, parentNodeRef);
}
}

View File

@@ -0,0 +1,19 @@
// called by ActivityServiceImplTest.java (test_JSAPI)
var failure = "did not complete script";
// invalid
// activities.postActivity("my activity type", null, null, null);
// activities.postActivity(null, "my site", "my app tool", '{ 000 }');
// valid
activities.postActivity("test activity type 4", null, null, '{ "item1" : 123 }');
activities.postActivity("test activity type 5", "my site", null, '{ "item2" : 456 }');
activities.postActivity("test activity type 6", "my site", "my app tool", '{ "item3" : 789 }');
activities.postActivity("test activity type 7", "my site", "my app tool", '{ invalidJSON }');
failure = "";
// Return the failure message
failure;

View File

@@ -0,0 +1,153 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.activities;
import java.util.List;
import java.util.Map;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
public interface ActivityService
{
/*
* Post Activity
*/
/**
* Post a custom activity type
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param jsonActivityData - required
*/
public void postActivity(String activityType, String siteId, String appTool, String jsonActivityData);
/**
* Post a pre-defined activity type - certain activity data will be looked-up asynchronously, including:
*
* name (of nodeRef)
* displayPath
* typeQName
* firstName (of posting user)
* lastName (of posting user)
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - do not use for deleted (or about to be deleted) nodeRef
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef);
/**
* Post a pre-defined activity type - eg. for checked-out nodeRef or renamed nodeRef
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - do not use deleted (or about to be deleted) nodeRef
* @param beforeName - optional - name of node (eg. prior to name change)
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String beforeName);
/**
* Post a pre-defined activity type - eg. for deleted nodeRef
*
* @param activityType - required
* @param siteId - optional, if null will be stored as empty string
* @param appTool - optional, if null will be stored as empty string
* @param nodeRef - required - can be a deleted (or about to be deleted) nodeRef
* @param name - optional - name of name
* @param typeQName - optional - type of node
* @param parentNodeRef - required - used to lookup path/displayPath
*/
public void postActivity(String activityType, String siteId, String appTool, NodeRef nodeRef, String name, QName typeQName, NodeRef parentNodeRef);
/*
* Retrieve Feed Entries
*/
/**
* Retrieve user feed
*
* @param userId - required
* @param format - required
* @param siteId - optional, if set then will filter by given siteId else return all sites
*/
public List<Map<String, Object>> getUserFeedEntries(String userId, String format, String siteId);
/**
* Retrieve site feed
*
* @param activityType - required
* @param format - required
*/
public List<Map<String, Object>> getSiteFeedEntries(String siteId, String format);
/*
* Manage User Feed Controls
*/
/**
* For current user, set feed control (opt-out) for a site or an appTool or a site/appTool
*
* @param feedControl - required
*/
public void setFeedControl(FeedControl feedControl);
/**
* For given user, get feed controls
*
* @param userId - required (must match
* @return list of user feed controls
*/
public List<FeedControl> getFeedControls(String userId);
/**
* For current user, get feed controls
*
* @return list of user feed controls
*/
public List<FeedControl> getFeedControls();
/**
* For current user, unset feed control
*
* @param feedControl - required
*/
public void unsetFeedControl(FeedControl feedControl);
/**
* For current user, does the feed control exist ?
*
* @param feedControl - required
* @return true, if user feed control exists
*/
public boolean existsFeedControl(FeedControl feedControl);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (C) 2005-2008 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.activities;
public class FeedControl
{
private String siteId;
private String appToolId;
public FeedControl(String siteId, String appToolId)
{
if (siteId == null) siteId = "";
this.siteId = siteId;
if (appToolId == null) appToolId = "";
this.appToolId = appToolId;
}
public String getSiteId()
{
return this.siteId;
}
public String getAppToolId()
{
return this.appToolId;
}
}

View File

@@ -95,6 +95,10 @@ public abstract class BaseSpringTest extends AbstractTransactionalDataSourceSpri
{
// The derived class is using the default context
defaultContext = true;
this.setAutowireMode(AUTOWIRE_BY_NAME);
//this.setDependencyCheck(false);
if (logger.isDebugEnabled())
{
logger.debug("Getting config locations");