/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.rss.cache;
import com.concursive.commons.text.StringUtils;
import com.concursive.commons.xml.XMLUtils;
import com.concursive.connect.cache.CacheContext;
import com.concursive.connect.cache.utils.CacheUtils;
import com.concursive.connect.web.modules.activity.dao.ProjectHistory;
import com.concursive.connect.web.modules.activity.dao.ProjectHistoryList;
import com.concursive.connect.web.modules.blog.feed.BlogPostFeedEntry;
import com.concursive.connect.web.modules.discussion.feed.DiscussionTopicFeedEntry;
import com.concursive.connect.web.modules.login.dao.User;
import com.concursive.connect.web.modules.login.utils.InstanceUtils;
import com.concursive.connect.web.modules.login.utils.UserUtils;
import com.concursive.connect.web.modules.profile.dao.Project;
import com.concursive.connect.web.modules.profile.dao.ProjectCategoryList;
import com.concursive.connect.web.modules.profile.feed.ProjectFeedEntry;
import com.concursive.connect.web.modules.profile.utils.ProjectUtils;
import com.concursive.connect.web.modules.reviews.feed.ProjectRatingFeedEntry;
import com.concursive.connect.web.rss.servlets.FeedServlet;
import com.concursive.connect.web.utils.PagedListInfo;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Element;
import java.net.URL;
import java.sql.Connection;
import java.util.ArrayList;
/**
* Provides public feeds
*
* @author Kailash Bhoopalam
* @created October 3, 2008
*/
public class FeedCacheEntryFactory implements CacheEntryFactory {
private static final Log LOG = LogFactory.getLog(FeedCacheEntryFactory.class);
private CacheContext context = null;
public FeedCacheEntryFactory(CacheContext context) {
this.context = context;
}
public Object createEntry(Object key) throws Exception {
if (key == null) {
throw new Exception("FeedCacheEntryFactory-> Invalid key specified: null");
}
// Determine the feed, expect an optional category and a site purpose for lookup
String[] items = key.toString().split("[|]");
Connection db = null;
String requestedFeedUrl = items[0];
String serverUrl = items[1];
String[] requestedFeedUrlPart = requestedFeedUrl.split("/rss.xml");
String purpose = "";
if (requestedFeedUrlPart.length > 1) {
purpose = requestedFeedUrlPart[1].substring(1);
}
String category = requestedFeedUrlPart[0];
// Load the RSS config file to determine the objects for display
String fileName = "rss_en_US.xml";
URL resource = FeedServlet.class.getResource("/" + fileName);
LOG.debug("RSS config file: " + resource.toString());
XMLUtils library = new XMLUtils(resource);
try {
// Determine if a site feed or a specific category feed
Element feedElement;
if (StringUtils.hasText(category)) {
// Find the site specific category configuration
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "site," + purpose + ",category," + category);
if (feedElement == null) {
// Find the specific category configuration
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "category," + category);
if (feedElement == null) {
// Find the site specific generic category
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "site," + purpose + ",category");
// If not found, use the generic category configuration
if (feedElement == null) {
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "category");
}
}
}
} else {
// Use the site specific configuration
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "site," + purpose);
if (feedElement == null) {
feedElement = XMLUtils.getElement(library.getDocumentElement(), "feed", "url", "site");
}
}
// Process the feedElement
if (feedElement != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Feed found: " + feedElement.getAttribute("url"));
}
// Determine the maximum number of feed entries
String limit = feedElement.getAttribute("limit");
String useDefaultProfile = feedElement.getAttribute("useDefaultProfile");
// Determine the types of entries to display in the feed based on the config file
ArrayList<Element> entryElements = new ArrayList<Element>();
XMLUtils.getAllChildren(feedElement, "entry", entryElements);
// Create the Feed object to be returned
SyndFeed feed = new SyndFeedImpl();
ArrayList<SyndEntry> entries = new ArrayList<SyndEntry>();
// Find feed items by querying the ProjectHistoryList
ProjectHistoryList historyList = new ProjectHistoryList();
historyList.setInstanceId(InstanceUtils.getInstance(serverUrl).getId());
PagedListInfo info = new PagedListInfo();
info.setItemsPerPage(limit);
historyList.setPagedListInfo(info);
// Retrieve the database connection to use
db = CacheUtils.getConnection(context);
// Check to see if the main profile is to be used
if (StringUtils.hasText(useDefaultProfile) && "true".equals(useDefaultProfile)) {
Project project = ProjectUtils.loadProject(context.getApplicationPrefs().get("MAIN_PROFILE"));
historyList.setProjectId(project.getId());
}
// set all the requested types based on the types we allow for the query..
ArrayList<String> types = new ArrayList<String>();
for (Element entryElement : entryElements) {
String type = XMLUtils.getNodeText(entryElement);
types.add(type);
}
historyList.setObjectPreferences(types);
// If a specific category is requested, search for it's id
if (StringUtils.hasText(category)) {
int projectCategoryId = -1;
ProjectCategoryList projectCategoryList = new ProjectCategoryList();
projectCategoryList.setTopLevelOnly(true);
projectCategoryList.setEnabled(true);
projectCategoryList.setCategoryDescriptionLowerCase(category.toLowerCase());
projectCategoryList.buildList(db);
if (projectCategoryList.size() >= 1) {
projectCategoryId = projectCategoryList.get(0).getId();
}
historyList.setProjectCategoryId(projectCategoryId);
}
// Since this is a public feed, base the feed off of a guest user
User guestUser = UserUtils.createGuestUser();
historyList.setForUser(guestUser.getId());
historyList.buildList(db);
// For each history item, determine the feed content
for (ProjectHistory item : historyList) {
SyndEntry syndEntry = null;
if (ProjectHistoryList.BLOG_OBJECT.equals(item.getLinkObject())) {
syndEntry = BlogPostFeedEntry.getSyndEntry(db, item.getLinkItemId(), serverUrl);
if (syndEntry != null) {
entries.add(syndEntry);
}
} else if (ProjectHistoryList.RATING_OBJECT.equals(item.getLinkObject())) {
syndEntry = ProjectRatingFeedEntry.getSyndEntry(db, item.getLinkItemId(), serverUrl);
if (syndEntry != null) {
entries.add(syndEntry);
}
} else if (ProjectHistoryList.TOPIC_OBJECT.equals(item.getLinkObject())) {
syndEntry = DiscussionTopicFeedEntry.getSyndEntry(db, item.getLinkItemId(), serverUrl);
if (syndEntry != null) {
entries.add(syndEntry);
}
} else if (ProjectHistoryList.PROFILE_OBJECT.equals(item.getLinkObject())) {
syndEntry = ProjectFeedEntry.getSyndEntry(db, item.getLinkItemId(), serverUrl);
if (syndEntry != null) {
entries.add(syndEntry);
}
}
}
feed.setEntries(entries);
return feed;
}
} catch (Exception e) {
throw new Exception(e);
} finally {
CacheUtils.freeConnection(context, db);
}
return null;
}
}