removed wicket and tomee, use dropwizard instead. remove wro4j, use gulp instead

This commit is contained in:
Athou
2014-08-08 16:49:02 +02:00
parent bbcd79e49f
commit 986fd25942
357 changed files with 2178 additions and 19556 deletions

View File

@@ -15,6 +15,7 @@ import javax.net.ssl.X509TrustManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.Header;
@@ -42,7 +43,6 @@ import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.apache.wicket.util.io.IOUtils;
/**
* Smart HTTP getter: handles gzip, ssl, last modified and etag headers

View File

@@ -1,48 +0,0 @@
package com.commafeed.backend;
import java.util.Date;
import javax.ejb.Schedule;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.inject.Inject;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.commafeed.backend.services.DatabaseCleaningService;
/**
* Contains all scheduled tasks
*
*/
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class ScheduledTasks {
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
DatabaseCleaningService cleaner;
/**
* clean old read statuses
*/
@Schedule(hour = "*", persistent = false)
private void cleanupOldStatuses() {
Date threshold = applicationSettingsService.getUnreadThreshold();
if (threshold != null) {
cleaner.cleanStatusesOlderThan(threshold);
}
}
/**
* clean feeds without subscriptions, then clean contents without entries
*/
@Schedule(hour = "*", persistent = false)
private void cleanFeedsAndContents() {
cleaner.cleanEntriesWithoutSubscriptions();
cleaner.cleanFeedsWithoutSubscriptions();
cleaner.cleanContentsWithoutEntries();
}
}

View File

@@ -3,17 +3,12 @@ package com.commafeed.backend.cache;
import java.util.Collections;
import java.util.List;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User;
import com.commafeed.frontend.model.Category;
import com.commafeed.frontend.model.UnreadCount;
@Alternative
@ApplicationScoped
public class NoopCacheService extends CacheService {
@Override

View File

@@ -4,13 +4,10 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool.impl.GenericObjectPool;
import lombok.extern.slf4j.Slf4j;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@@ -26,8 +23,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
@Alternative
@ApplicationScoped
@Slf4j
public class RedisCacheService extends CacheService {
@@ -35,8 +30,7 @@ public class RedisCacheService extends CacheService {
private JedisPool pool;
@PostConstruct
private void init() {
public RedisCacheService() {
JedisPoolConfig config = new JedisPoolConfig();
config.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
pool = new JedisPool(config, "localhost");

View File

@@ -1,10 +0,0 @@
package com.commafeed.backend.dao;
import javax.ejb.Stateless;
import com.commafeed.backend.model.ApplicationSettings;
@Stateless
public class ApplicationSettingsDAO extends GenericDAO<ApplicationSettings> {
}

View File

@@ -2,91 +2,50 @@ package com.commafeed.backend.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang.ObjectUtils;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedCategory_;
import com.commafeed.backend.model.QFeedCategory;
import com.commafeed.backend.model.QUser;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.User_;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.mysema.query.types.Predicate;
@Stateless
public class FeedCategoryDAO extends GenericDAO<FeedCategory> {
@SuppressWarnings("unchecked")
private QFeedCategory category = QFeedCategory.feedCategory;
public FeedCategoryDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public List<FeedCategory> findAll(User user) {
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
Root<FeedCategory> root = query.from(getType());
Join<FeedCategory, User> userJoin = (Join<FeedCategory, User>) root.fetch(FeedCategory_.user);
query.where(builder.equal(userJoin.get(User_.id), user.getId()));
return cache(em.createQuery(query)).getResultList();
return newQuery().from(category).where(category.user.eq(user)).join(category.user, QUser.user).fetch().list(category);
}
public FeedCategory findById(User user, Long id) {
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
Root<FeedCategory> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedCategory_.user).get(User_.id), user.getId());
Predicate p2 = builder.equal(root.get(FeedCategory_.id), id);
query.where(p1, p2);
return Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
return newQuery().from(category).where(category.user.eq(user), category.id.eq(id)).uniqueResult(category);
}
public FeedCategory findByName(User user, String name, FeedCategory parent) {
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
Root<FeedCategory> root = query.from(getType());
List<Predicate> predicates = Lists.newArrayList();
predicates.add(builder.equal(root.get(FeedCategory_.user), user));
predicates.add(builder.equal(root.get(FeedCategory_.name), name));
Predicate parentPredicate = null;
if (parent == null) {
predicates.add(builder.isNull(root.get(FeedCategory_.parent)));
parentPredicate = category.parent.isNull();
} else {
predicates.add(builder.equal(root.get(FeedCategory_.parent), parent));
parentPredicate = category.parent.eq(parent);
}
query.where(predicates.toArray(new Predicate[0]));
FeedCategory category = null;
try {
category = em.createQuery(query).getSingleResult();
} catch (NoResultException e) {
category = null;
}
return category;
return newQuery().from(category).where(category.user.eq(user), category.name.eq(name), parentPredicate).uniqueResult(category);
}
public List<FeedCategory> findByParent(User user, FeedCategory parent) {
CriteriaQuery<FeedCategory> query = builder.createQuery(getType());
Root<FeedCategory> root = query.from(getType());
List<Predicate> predicates = Lists.newArrayList();
predicates.add(builder.equal(root.get(FeedCategory_.user), user));
Predicate parentPredicate = null;
if (parent == null) {
predicates.add(builder.isNull(root.get(FeedCategory_.parent)));
parentPredicate = category.parent.isNull();
} else {
predicates.add(builder.equal(root.get(FeedCategory_.parent), parent));
parentPredicate = category.parent.eq(parent);
}
query.where(predicates.toArray(new Predicate[0]));
return em.createQuery(query).getResultList();
return newQuery().from(category).where(category.user.eq(user), parentPredicate).list(category);
}
public List<FeedCategory> findAllChildrenCategories(User user, FeedCategory parent) {

View File

@@ -3,104 +3,75 @@ package com.commafeed.backend.dao;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.persistence.criteria.Subquery;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.SessionFactory;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.FeedSubscription_;
import com.commafeed.backend.model.Feed_;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.User_;
import com.commafeed.backend.model.QFeed;
import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.QUser;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.mysema.query.BooleanBuilder;
import com.mysema.query.jpa.hibernate.HibernateQuery;
import com.mysema.query.jpa.hibernate.HibernateSubQuery;
@Stateless
public class FeedDAO extends GenericDAO<Feed> {
private List<Predicate> getUpdatablePredicates(CriteriaQuery<?> query, Root<Feed> root, Date lastLoginThreshold) {
private QFeed feed = QFeed.feed;
List<Predicate> preds = Lists.newArrayList();
Predicate isNull = builder.isNull(root.get(Feed_.disabledUntil));
Predicate lessThan = builder.lessThan(root.get(Feed_.disabledUntil), new Date());
preds.add(builder.or(isNull, lessThan));
if (lastLoginThreshold != null) {
Subquery<Long> subquery = query.subquery(Long.class);
Root<FeedSubscription> subroot = subquery.from(FeedSubscription.class);
subquery.select(builder.count(subroot.get(FeedSubscription_.id)));
Join<FeedSubscription, User> userJoin = subroot.join(FeedSubscription_.user);
Predicate p1 = builder.equal(subroot.get(FeedSubscription_.feed), root);
Predicate p2 = builder.greaterThanOrEqualTo(userJoin.get(User_.lastLogin), lastLoginThreshold);
subquery.where(p1, p2);
preds.add(builder.exists(subquery));
}
return preds;
public FeedDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public Long getUpdatableCount(Date lastLoginThreshold) {
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<Feed> root = query.from(getType());
BooleanBuilder disabledDatePredicate = new BooleanBuilder();
disabledDatePredicate.or(feed.disabledUntil.isNull());
disabledDatePredicate.or(feed.disabledUntil.lt(new Date()));
query.select(builder.count(root));
query.where(getUpdatablePredicates(query, root, lastLoginThreshold).toArray(new Predicate[0]));
TypedQuery<Long> q = em.createQuery(query);
return q.getSingleResult();
HibernateQuery query = newQuery().from(feed).where(disabledDatePredicate);
if (lastLoginThreshold != null) {
QFeedSubscription sub = QFeedSubscription.feedSubscription;
QUser user = QUser.user;
HibernateSubQuery subquery = new HibernateSubQuery();
subquery.from(sub).join(sub.user, user).where(sub.feed.eq(feed), user.lastLogin.gt(lastLoginThreshold));
query.where(subquery.exists());
}
return query.orderBy(feed.disabledUntil.asc()).count();
}
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
CriteriaQuery<Feed> query = builder.createQuery(getType());
Root<Feed> root = query.from(getType());
BooleanBuilder disabledDatePredicate = new BooleanBuilder();
disabledDatePredicate.or(feed.disabledUntil.isNull());
disabledDatePredicate.or(feed.disabledUntil.lt(new Date()));
query.where(getUpdatablePredicates(query, root, lastLoginThreshold).toArray(new Predicate[0]));
query.orderBy(builder.asc(root.get(Feed_.disabledUntil)));
TypedQuery<Feed> q = em.createQuery(query);
q.setMaxResults(count);
return q.getResultList();
}
public Feed findByUrl(String url) {
String normalized = FeedUtils.normalizeURL(url);
List<Feed> feeds = findByField(Feed_.normalizedUrlHash, DigestUtils.sha1Hex(normalized));
Feed feed = Iterables.getFirst(feeds, null);
if (feed != null && StringUtils.equals(normalized, feed.getNormalizedUrl())) {
return feed;
HibernateQuery query = newQuery().from(feed).where(disabledDatePredicate);
if (lastLoginThreshold != null) {
QFeedSubscription sub = QFeedSubscription.feedSubscription;
QUser user = QUser.user;
HibernateSubQuery subquery = new HibernateSubQuery();
subquery.from(sub).join(sub.user, user).where(sub.feed.eq(feed), user.lastLogin.gt(lastLoginThreshold));
query.where(subquery.exists());
}
return query.orderBy(feed.disabledUntil.asc()).limit(count).list(feed);
}
public Feed findByUrl(String normalizedUrl) {
List<Feed> feeds = newQuery().from(feed).where(feed.normalizedUrlHash.eq(DigestUtils.sha1Hex(normalizedUrl))).list(feed);
Feed feed = Iterables.getFirst(feeds, null);
if (feed != null && StringUtils.equals(normalizedUrl, feed.getNormalizedUrl())) {
return feed;
}
return null;
}
public List<Feed> findByTopic(String topic) {
return findByField(Feed_.pushTopicHash, DigestUtils.sha1Hex(topic));
return newQuery().from(feed).where(feed.pushTopicHash.eq(DigestUtils.sha1Hex(topic))).list(feed);
}
public List<Feed> findWithoutSubscriptions(int max) {
CriteriaQuery<Feed> query = builder.createQuery(getType());
Root<Feed> root = query.from(getType());
SetJoin<Feed, FeedSubscription> join = root.join(Feed_.subscriptions, JoinType.LEFT);
query.where(builder.isNull(join.get(FeedSubscription_.id)));
TypedQuery<Feed> q = em.createQuery(query);
q.setMaxResults(max);
return q.getResultList();
QFeedSubscription sub = QFeedSubscription.feedSubscription;
return newQuery().from(feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max).list(feed);
}
}

View File

@@ -2,52 +2,34 @@ package com.commafeed.backend.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.FeedEntryContent_;
import com.commafeed.backend.model.FeedEntry_;
import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryContent;
import com.google.common.collect.Iterables;
import com.mysema.query.types.ConstructorExpression;
@Stateless
public class FeedEntryContentDAO extends GenericDAO<FeedEntryContent> {
private QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
public FeedEntryContentDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public Long findExisting(String contentHash, String titleHash) {
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<FeedEntryContent> root = query.from(getType());
query.select(root.get(FeedEntryContent_.id));
Predicate p1 = builder.equal(root.get(FeedEntryContent_.contentHash), contentHash);
Predicate p2 = builder.equal(root.get(FeedEntryContent_.titleHash), titleHash);
query.where(p1, p2);
TypedQuery<Long> q = em.createQuery(query);
limit(q, 0, 1);
return Iterables.getFirst(q.getResultList(), null);
List<Long> list = newQuery().from(content).where(content.contentHash.eq(contentHash), content.titleHash.eq(titleHash)).limit(1)
.list(ConstructorExpression.create(Long.class, content.id));
return Iterables.getFirst(list, null);
}
public int deleteWithoutEntries(int max) {
CriteriaQuery<FeedEntryContent> query = builder.createQuery(getType());
Root<FeedEntryContent> root = query.from(getType());
Join<FeedEntryContent, FeedEntry> join = root.join(FeedEntryContent_.entries, JoinType.LEFT);
query.where(builder.isNull(join.get(FeedEntry_.id)));
TypedQuery<FeedEntryContent> q = em.createQuery(query);
q.setMaxResults(max);
List<FeedEntryContent> list = q.getResultList();
QFeedEntry entry = QFeedEntry.feedEntry;
List<FeedEntryContent> list = newQuery().from(content).leftJoin(content.entries, entry).where(entry.id.isNull()).limit(max)
.list(content);
int deleted = list.size();
delete(list);
return deleted;
}
}

View File

@@ -3,82 +3,47 @@ package com.commafeed.backend.dao;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import org.apache.commons.codec.digest.DigestUtils;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntry_;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.FeedSubscription_;
import com.commafeed.backend.model.Feed_;
import com.commafeed.backend.model.QFeed;
import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedSubscription;
import com.google.common.collect.Iterables;
import com.mysema.query.types.ConstructorExpression;
@Stateless
public class FeedEntryDAO extends GenericDAO<FeedEntry> {
public Long findExisting(String guid, Long feedId) {
private QFeedEntry entry = QFeedEntry.feedEntry;
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<FeedEntry> root = query.from(getType());
query.select(root.get(FeedEntry_.id));
public FeedEntryDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
Predicate p1 = builder.equal(root.get(FeedEntry_.guidHash), DigestUtils.sha1Hex(guid));
Predicate p2 = builder.equal(root.get(FeedEntry_.feed).get(Feed_.id), feedId);
query.where(p1, p2);
TypedQuery<Long> q = em.createQuery(query);
limit(q, 0, 1);
List<Long> list = q.getResultList();
public Long findExisting(String guid, Feed feed) {
List<Long> list = newQuery().from(entry).where(entry.guidHash.eq(DigestUtils.sha1Hex(guid)), entry.feed.eq(feed)).limit(1)
.list(ConstructorExpression.create(Long.class, entry.id));
return Iterables.getFirst(list, null);
}
public List<FeedEntry> findWithoutSubscriptions(int max) {
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
Root<FeedEntry> root = query.from(getType());
Join<FeedEntry, Feed> feedJoin = root.join(FeedEntry_.feed);
SetJoin<Feed, FeedSubscription> subJoin = feedJoin.join(Feed_.subscriptions, JoinType.LEFT);
query.where(builder.isNull(subJoin.get(FeedSubscription_.id)));
TypedQuery<FeedEntry> q = em.createQuery(query);
q.setMaxResults(max);
return q.getResultList();
QFeed feed = QFeed.feed;
QFeedSubscription sub = QFeedSubscription.feedSubscription;
return newQuery().from(entry).join(entry.feed, feed).leftJoin(feed.subscriptions, sub).where(sub.id.isNull()).limit(max)
.list(entry);
}
public int delete(Feed feed, int max) {
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
Root<FeedEntry> root = query.from(getType());
query.where(builder.equal(root.get(FeedEntry_.feed), feed));
TypedQuery<FeedEntry> q = em.createQuery(query);
q.setMaxResults(max);
List<FeedEntry> list = q.getResultList();
List<FeedEntry> list = newQuery().from(entry).where(entry.feed.eq(feed)).limit(max).list(entry);
int deleted = list.size();
delete(list);
return deleted;
}
public int delete(Date olderThan, int max) {
CriteriaQuery<FeedEntry> query = builder.createQuery(getType());
Root<FeedEntry> root = query.from(getType());
query.where(builder.lessThan(root.get(FeedEntry_.inserted), olderThan));
TypedQuery<FeedEntry> q = em.createQuery(query);
q.setMaxResults(max);
List<FeedEntry> list = q.getResultList();
List<FeedEntry> list = newQuery().from(entry).where(entry.inserted.lt(olderThan)).limit(max).list(entry);
int deleted = list.size();
delete(list);
return deleted;

View File

@@ -5,18 +5,10 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang.builder.CompareToBuilder;
import org.hibernate.Criteria;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
@@ -27,33 +19,44 @@ import org.hibernate.criterion.Restrictions;
import org.hibernate.sql.JoinType;
import org.hibernate.transform.Transformers;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.FixedSizeSortedSet;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent_;
import com.commafeed.backend.model.FeedEntryStatus;
import com.commafeed.backend.model.FeedEntryStatus_;
import com.commafeed.backend.model.FeedEntryTag;
import com.commafeed.backend.model.FeedEntryTag_;
import com.commafeed.backend.model.FeedEntry_;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedEntry;
import com.commafeed.backend.model.QFeedEntryContent;
import com.commafeed.backend.model.QFeedEntryStatus;
import com.commafeed.backend.model.QFeedEntryTag;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings.ReadingOrder;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.commafeed.frontend.model.UnreadCount;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.mysema.query.jpa.hibernate.HibernateQuery;
@Stateless
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
private static final String ALIAS_STATUS = "status";
private static final String ALIAS_ENTRY = "entry";
private static final String ALIAS_TAG = "tag";
@Inject
FeedEntryTagDAO feedEntryTagDAO;
private FeedEntryDAO feedEntryDAO;
private FeedEntryTagDAO feedEntryTagDAO;
private CommaFeedConfiguration config;
private QFeedEntryStatus status = QFeedEntryStatus.feedEntryStatus;
public FeedEntryStatusDAO(SessionFactory sessionFactory, FeedEntryDAO feedEntryDAO, FeedEntryTagDAO feedEntryTagDAO,
CommaFeedConfiguration config) {
super(sessionFactory);
this.feedEntryDAO = feedEntryDAO;
this.feedEntryTagDAO = feedEntryTagDAO;
this.config = config;
}
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_DESC = new Comparator<FeedEntryStatus>() {
@Override
@@ -61,34 +64,21 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
CompareToBuilder builder = new CompareToBuilder();
builder.append(o2.getEntryUpdated(), o1.getEntryUpdated());
builder.append(o2.getId(), o1.getId());
return builder.build();
return builder.toComparison();
};
};
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = Ordering.from(STATUS_COMPARATOR_DESC).reverse();
@Inject
ApplicationSettingsService applicationSettingsService;
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
Root<FeedEntryStatus> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedEntryStatus_.entry), entry);
Predicate p2 = builder.equal(root.get(FeedEntryStatus_.subscription), sub);
query.where(p1, p2);
List<FeedEntryStatus> statuses = em.createQuery(query).getResultList();
List<FeedEntryStatus> statuses = newQuery().from(status).where(status.entry.eq(entry), status.subscription.eq(sub)).list(status);
FeedEntryStatus status = Iterables.getFirst(statuses, null);
return handleStatus(user, status, sub, entry);
}
private FeedEntryStatus handleStatus(User user, FeedEntryStatus status, FeedSubscription sub, FeedEntry entry) {
if (status == null) {
Date unreadThreshold = applicationSettingsService.getUnreadThreshold();
Date unreadThreshold = config.getApplicationSettings().getUnreadThreshold();
boolean read = unreadThreshold == null ? false : entry.getUpdated().before(unreadThreshold);
status = new FeedEntryStatus(user, sub, entry);
status.setRead(read);
@@ -106,27 +96,20 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
}
public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) {
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
Root<FeedEntryStatus> root = query.from(getType());
List<Predicate> predicates = Lists.newArrayList();
predicates.add(builder.equal(root.get(FeedEntryStatus_.user), user));
predicates.add(builder.equal(root.get(FeedEntryStatus_.starred), true));
HibernateQuery query = newQuery().from(status).where(status.user.eq(user), status.starred.isTrue());
if (newerThan != null) {
predicates.add(builder.greaterThanOrEqualTo(root.get(FeedEntryStatus_.entryInserted), newerThan));
query.where(status.entryInserted.gt(newerThan));
}
query.where(predicates.toArray(new Predicate[0]));
orderStatusesBy(query, root, order);
if (order == ReadingOrder.asc) {
query.orderBy(status.entryUpdated.asc(), status.id.asc());
} else {
query.orderBy(status.entryUpdated.desc(), status.id.desc());
}
TypedQuery<FeedEntryStatus> q = em.createQuery(query);
limit(q, offset, limit);
setTimeout(q);
List<FeedEntryStatus> statuses = q.getResultList();
query.offset(offset).limit(limit).setTimeout(config.getApplicationSettings().getQueryTimeout());
List<FeedEntryStatus> statuses = query.list(status);
for (FeedEntryStatus status : statuses) {
status = handleStatus(user, status, status.getSubscription(), status.getEntry());
status = fetchTags(user, status);
@@ -134,62 +117,66 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, statuses);
}
private Criteria buildSearchCriteria(User user, FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset, int limit,
ReadingOrder order, Date last, String tag) {
Criteria criteria = getSession().createCriteria(FeedEntry.class, ALIAS_ENTRY);
private Criteria buildSearchCriteria(User user, FeedSubscription sub, boolean unreadOnly, String keywords, Date newerThan, int offset,
int limit, ReadingOrder order, Date last, String tag) {
QFeedEntry entry = QFeedEntry.feedEntry;
QFeedEntryContent content = QFeedEntryContent.feedEntryContent;
QFeedEntryStatus status = QFeedEntryStatus.feedEntryStatus;
QFeedEntryTag entryTag = QFeedEntryTag.feedEntryTag;
criteria.add(Restrictions.eq(FeedEntry_.feed.getName(), sub.getFeed()));
Criteria criteria = currentSession().createCriteria(FeedEntry.class, ALIAS_ENTRY);
criteria.add(Restrictions.eq(entry.feed.getMetadata().getName(), sub.getFeed()));
if (keywords != null) {
Criteria contentJoin = criteria.createCriteria(FeedEntry_.content.getName(), "content", JoinType.INNER_JOIN);
Criteria contentJoin = criteria.createCriteria(entry.content.getMetadata().getName(), "content", JoinType.INNER_JOIN);
for (String keyword : StringUtils.split(keywords)) {
Disjunction or = Restrictions.disjunction();
or.add(Restrictions.ilike(FeedEntryContent_.content.getName(), keyword, MatchMode.ANYWHERE));
or.add(Restrictions.ilike(FeedEntryContent_.title.getName(), keyword, MatchMode.ANYWHERE));
or.add(Restrictions.ilike(content.content.getMetadata().getName(), keyword, MatchMode.ANYWHERE));
or.add(Restrictions.ilike(content.title.getMetadata().getName(), keyword, MatchMode.ANYWHERE));
contentJoin.add(or);
}
}
Criteria statusJoin = criteria.createCriteria(FeedEntry_.statuses.getName(), ALIAS_STATUS, JoinType.LEFT_OUTER_JOIN,
Restrictions.eq(FeedEntryStatus_.subscription.getName(), sub));
Criteria statusJoin = criteria.createCriteria(entry.statuses.getMetadata().getName(), ALIAS_STATUS, JoinType.LEFT_OUTER_JOIN,
Restrictions.eq(status.subscription.getMetadata().getName(), sub));
if (unreadOnly && tag == null) {
Disjunction or = Restrictions.disjunction();
or.add(Restrictions.isNull(FeedEntryStatus_.read.getName()));
or.add(Restrictions.eq(FeedEntryStatus_.read.getName(), false));
or.add(Restrictions.isNull(status.read.getMetadata().getName()));
or.add(Restrictions.eq(status.read.getMetadata().getName(), false));
statusJoin.add(or);
Date unreadThreshold = applicationSettingsService.getUnreadThreshold();
Date unreadThreshold = config.getApplicationSettings().getUnreadThreshold();
if (unreadThreshold != null) {
criteria.add(Restrictions.ge(FeedEntry_.updated.getName(), unreadThreshold));
criteria.add(Restrictions.ge(entry.updated.getMetadata().getName(), unreadThreshold));
}
}
if (tag != null) {
Conjunction and = Restrictions.conjunction();
and.add(Restrictions.eq(FeedEntryTag_.user.getName(), user));
and.add(Restrictions.eq(FeedEntryTag_.name.getName(), tag));
criteria.createCriteria(FeedEntry_.tags.getName(), ALIAS_TAG, JoinType.INNER_JOIN, and);
and.add(Restrictions.eq(entryTag.user.getMetadata().getName(), user));
and.add(Restrictions.eq(entryTag.name.getMetadata().getName(), tag));
criteria.createCriteria(entry.tags.getMetadata().getName(), ALIAS_TAG, JoinType.INNER_JOIN, and);
}
if (newerThan != null) {
criteria.add(Restrictions.ge(FeedEntry_.inserted.getName(), newerThan));
criteria.add(Restrictions.ge(entry.inserted.getMetadata().getName(), newerThan));
}
if (last != null) {
if (order == ReadingOrder.desc) {
criteria.add(Restrictions.gt(FeedEntry_.updated.getName(), last));
criteria.add(Restrictions.gt(entry.updated.getMetadata().getName(), last));
} else {
criteria.add(Restrictions.lt(FeedEntry_.updated.getName(), last));
criteria.add(Restrictions.lt(entry.updated.getMetadata().getName(), last));
}
}
if (order != null) {
if (order == ReadingOrder.asc) {
criteria.addOrder(Order.asc(FeedEntry_.updated.getName())).addOrder(Order.asc(FeedEntry_.id.getName()));
criteria.addOrder(Order.asc(entry.updated.getMetadata().getName())).addOrder(Order.asc(entry.id.getMetadata().getName()));
} else {
criteria.addOrder(Order.desc(FeedEntry_.updated.getName())).addOrder(Order.desc(FeedEntry_.id.getName()));
criteria.addOrder(Order.desc(entry.updated.getMetadata().getName())).addOrder(Order.desc(entry.id.getMetadata().getName()));
}
}
if (offset > -1) {
@@ -198,7 +185,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
if (limit > -1) {
criteria.setMaxResults(limit);
}
int timeout = applicationSettingsService.get().getQueryTimeout();
int timeout = config.getApplicationSettings().getQueryTimeout();
if (timeout > 0) {
// hibernate timeout is in seconds, jpa timeout is in millis
criteria.setTimeout(timeout / 1000);
@@ -255,7 +242,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
statuses = Lists.newArrayList();
for (FeedEntryStatus placeholder : placeholders) {
Long statusId = placeholder.getId();
FeedEntry entry = em.find(FeedEntry.class, placeholder.getEntry().getId());
FeedEntry entry = feedEntryDAO.findById(placeholder.getEntry().getId());
FeedEntryStatus status = handleStatus(user, statusId == null ? null : findById(statusId), placeholder.getSubscription(),
entry);
status = fetchTags(user, status);
@@ -272,7 +259,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
Criteria criteria = buildSearchCriteria(user, subscription, true, null, null, -1, -1, null, null, null);
ProjectionList projection = Projections.projectionList();
projection.add(Projections.rowCount(), "count");
projection.add(Projections.max(FeedEntry_.updated.getName()), "updated");
projection.add(Projections.max(QFeedEntry.feedEntry.updated.getMetadata().getName()), "updated");
criteria.setProjection(projection);
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String, Object>> list = criteria.list();
@@ -294,35 +281,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return results;
}
private void orderStatusesBy(CriteriaQuery<?> query, Path<FeedEntryStatus> statusJoin, ReadingOrder order) {
orderBy(query, statusJoin.get(FeedEntryStatus_.entryUpdated), statusJoin.get(FeedEntryStatus_.id), order);
}
private void orderBy(CriteriaQuery<?> query, Path<Date> date, Path<Long> id, ReadingOrder order) {
if (order != null) {
if (order == ReadingOrder.asc) {
query.orderBy(builder.asc(date), builder.asc(id));
} else {
query.orderBy(builder.desc(date), builder.desc(id));
}
}
}
protected void setTimeout(Query query) {
setTimeout(query, applicationSettingsService.get().getQueryTimeout());
}
public List<FeedEntryStatus> getOldStatuses(Date olderThan, int limit) {
CriteriaQuery<FeedEntryStatus> query = builder.createQuery(getType());
Root<FeedEntryStatus> root = query.from(getType());
Predicate p1 = builder.lessThan(root.get(FeedEntryStatus_.entryInserted), olderThan);
Predicate p2 = builder.isFalse(root.get(FeedEntryStatus_.starred));
query.where(p1, p2);
TypedQuery<FeedEntryStatus> q = em.createQuery(query);
q.setMaxResults(limit);
return q.getResultList();
return newQuery().from(status).where(status.entryInserted.lt(olderThan), status.starred.isFalse()).limit(limit).list(status);
}
}

View File

@@ -2,42 +2,27 @@ package com.commafeed.backend.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryTag;
import com.commafeed.backend.model.FeedEntryTag_;
import com.commafeed.backend.model.FeedEntry_;
import com.commafeed.backend.model.QFeedEntryTag;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.User_;
import com.mysema.query.types.ConstructorExpression;
@Stateless
public class FeedEntryTagDAO extends GenericDAO<FeedEntryTag> {
private QFeedEntryTag tag = QFeedEntryTag.feedEntryTag;
public FeedEntryTagDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public List<String> findByUser(User user) {
CriteriaQuery<String> query = builder.createQuery(String.class);
Root<FeedEntryTag> root = query.from(getType());
query.select(root.get(FeedEntryTag_.name));
query.distinct(true);
Predicate p1 = builder.equal(root.get(FeedEntryTag_.user).get(User_.id), user.getId());
query.where(p1);
return cache(em.createQuery(query)).getResultList();
return newQuery().from(tag).where(tag.user.eq(user)).distinct().list(ConstructorExpression.create(String.class, tag.name));
}
public List<FeedEntryTag> findByEntry(User user, FeedEntry entry) {
CriteriaQuery<FeedEntryTag> query = builder.createQuery(getType());
Root<FeedEntryTag> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedEntryTag_.user).get(User_.id), user.getId());
Predicate p2 = builder.equal(root.get(FeedEntryTag_.entry).get(FeedEntry_.id), entry.getId());
query.where(p1, p2);
return cache(em.createQuery(query)).getResultList();
return newQuery().from(tag).where(tag.user.eq(user), tag.entry.eq(entry)).list(tag);
}
}

View File

@@ -2,110 +2,59 @@ package com.commafeed.backend.dao;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedCategory_;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.FeedSubscription_;
import com.commafeed.backend.model.Feed_;
import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.User_;
import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.mysema.query.jpa.hibernate.HibernateQuery;
@Stateless
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
private QFeedSubscription sub = QFeedSubscription.feedSubscription;
public FeedSubscriptionDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public FeedSubscription findById(User user, Long id) {
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
Root<FeedSubscription> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
Predicate p2 = builder.equal(root.get(FeedSubscription_.id), id);
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
root.fetch(FeedSubscription_.category, JoinType.LEFT);
query.where(p1, p2);
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
initRelations(sub);
return sub;
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user), sub.id.eq(id)).leftJoin(sub.feed).fetch()
.leftJoin(sub.category).fetch().list(sub);
return initRelations(Iterables.getFirst(subs, null));
}
public List<FeedSubscription> findByFeed(Feed feed) {
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
Root<FeedSubscription> root = query.from(getType());
query.where(builder.equal(root.get(FeedSubscription_.feed), feed));
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
return list;
return newQuery().from(sub).where(sub.feed.eq(feed)).list(sub);
}
public FeedSubscription findByFeed(User user, Feed feed) {
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
Root<FeedSubscription> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
Predicate p2 = builder.equal(root.get(FeedSubscription_.feed).get(Feed_.id), feed.getId());
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
root.fetch(FeedSubscription_.category, JoinType.LEFT);
query.where(p1, p2);
FeedSubscription sub = Iterables.getFirst(cache(em.createQuery(query)).getResultList(), null);
initRelations(sub);
return sub;
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user), sub.feed.eq(feed)).list(sub);
return initRelations(Iterables.getFirst(subs, null));
}
public List<FeedSubscription> findAll(User user) {
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
Root<FeedSubscription> root = query.from(getType());
root.fetch(FeedSubscription_.feed, JoinType.LEFT);
root.fetch(FeedSubscription_.category, JoinType.LEFT);
query.where(builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId()));
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
initRelations(list);
return list;
List<FeedSubscription> subs = newQuery().from(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetch().leftJoin(sub.category)
.fetch().list(sub);
return initRelations(subs);
}
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
CriteriaQuery<FeedSubscription> query = builder.createQuery(getType());
Root<FeedSubscription> root = query.from(getType());
Predicate p1 = builder.equal(root.get(FeedSubscription_.user).get(User_.id), user.getId());
Predicate p2 = null;
HibernateQuery query = newQuery().from(sub).where(sub.user.eq(user));
if (category == null) {
p2 = builder.isNull(root.get(FeedSubscription_.category));
query.where(sub.category.isNull());
} else {
p2 = builder.equal(root.get(FeedSubscription_.category).get(FeedCategory_.id), category.getId());
query.where(sub.category.eq(category));
}
query.where(p1, p2);
List<FeedSubscription> list = cache(em.createQuery(query)).getResultList();
initRelations(list);
return list;
return initRelations(query.list(sub));
}
public List<FeedSubscription> findByCategories(User user, List<FeedCategory> categories) {
List<Long> categoryIds = Lists.transform(categories, new Function<FeedCategory, Long>() {
@Override
public Long apply(FeedCategory input) {
@@ -122,16 +71,18 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
return subscriptions;
}
private void initRelations(List<FeedSubscription> list) {
private List<FeedSubscription> initRelations(List<FeedSubscription> list) {
for (FeedSubscription sub : list) {
initRelations(sub);
}
return list;
}
private void initRelations(FeedSubscription sub) {
private FeedSubscription initRelations(FeedSubscription sub) {
if (sub != null) {
Models.initialize(sub.getFeed());
Models.initialize(sub.getCategory());
}
return sub;
}
}

View File

@@ -1,160 +1,59 @@
package com.commafeed.backend.dao;
import java.util.Arrays;
import io.dropwizard.hibernate.AbstractDAO;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.AbstractModel;
import com.google.common.reflect.TypeToken;
import com.mysema.query.jpa.hibernate.HibernateQuery;
@SuppressWarnings("serial")
public abstract class GenericDAO<T extends AbstractModel> {
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
private TypeToken<T> type = new TypeToken<T>(getClass()) {
};
@PersistenceContext
protected EntityManager em;
protected CriteriaBuilder builder;
@PostConstruct
public void init() {
builder = em.getCriteriaBuilder();
public GenericDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public Session getSession() {
Session session = em.unwrap(Session.class);
return session;
protected HibernateQuery newQuery() {
return new HibernateQuery(currentSession());
}
public void saveOrUpdate(Collection<? extends AbstractModel> models) {
Session session = getSession();
int i = 1;
for (AbstractModel model : models) {
session.saveOrUpdate(model);
if (i % 50 == 0) {
session.flush();
session.clear();
}
public void saveOrUpdate(T model) {
persist(model);
}
public void saveOrUpdate(Collection<T> models) {
for (T model : models) {
persist(model);
}
}
public void saveOrUpdate(AbstractModel... models) {
saveOrUpdate(Arrays.asList(models));
public void merge(T model) {
currentSession().merge(model);
}
public void delete(AbstractModel object) {
if (object != null) {
object = em.merge(object);
em.remove(object);
}
}
public int delete(Collection<? extends AbstractModel> objects) {
for (AbstractModel object : objects) {
delete(object);
}
return objects.size();
}
public void deleteById(Long id) {
Object ref = em.getReference(getType(), id);
if (ref != null) {
em.remove(ref);
public void merge(Collection<T> models) {
for (T model : models) {
merge(model);
}
}
public T findById(Long id) {
T t = em.find(getType(), id);
return t;
return get(id);
}
public List<T> findAll() {
CriteriaQuery<T> query = builder.createQuery(getType());
query.from(getType());
return em.createQuery(query).getResultList();
}
public List<T> findAll(int startIndex, int count) {
CriteriaQuery<T> query = builder.createQuery(getType());
query.from(getType());
TypedQuery<T> q = em.createQuery(query);
q.setMaxResults(count);
q.setFirstResult(startIndex);
return q.getResultList();
}
public List<T> findAll(int startIndex, int count, String orderBy, boolean asc) {
CriteriaQuery<T> query = builder.createQuery(getType());
Root<T> root = query.from(getType());
if (asc) {
query.orderBy(builder.asc(root.get(orderBy)));
} else {
query.orderBy(builder.desc(root.get(orderBy)));
}
TypedQuery<T> q = em.createQuery(query);
q.setMaxResults(count);
q.setFirstResult(startIndex);
return q.getResultList();
}
public long getCount() {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Long> query = builder.createQuery(Long.class);
Root<T> root = query.from(getType());
query.select(builder.count(root));
return em.createQuery(query).getSingleResult();
}
protected <V> List<T> findByField(Attribute<T, V> field, V value) {
return findByField(field, value, false);
}
protected <V> List<T> findByField(Attribute<T, V> field, V value, boolean cache) {
CriteriaQuery<T> query = builder.createQuery(getType());
Root<T> root = query.from(getType());
query.where(builder.equal(root.get(field.getName()), value));
TypedQuery<T> q = em.createQuery(query);
if (cache) {
cache(q);
}
return em.createQuery(query).getResultList();
}
protected <Q> void limit(TypedQuery<Q> query, int offset, int limit) {
if (offset > -1) {
query.setFirstResult(offset);
}
if (limit > -1) {
query.setMaxResults(limit);
public void delete(T object) {
if (object != null) {
currentSession().delete(object);
}
}
protected <Q> TypedQuery<Q> readOnly(TypedQuery<Q> query) {
query.unwrap(Query.class).setReadOnly(true);
return query;
}
protected <Q> TypedQuery<Q> cache(TypedQuery<Q> query) {
query.unwrap(Query.class).setCacheable(true);
return query;
public int delete(Collection<T> objects) {
for (T object : objects) {
delete(object);
}
return objects.size();
}
protected void setTimeout(javax.persistence.Query query, int queryTimeout) {
@@ -162,9 +61,4 @@ public abstract class GenericDAO<T extends AbstractModel> {
query.setHint("javax.persistence.query.timeout", queryTimeout);
}
}
@SuppressWarnings("unchecked")
protected Class<T> getType() {
return (Class<T>) type.getRawType();
}
}

View File

@@ -0,0 +1,60 @@
package com.commafeed.backend.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.context.internal.ManagedSessionContext;
public abstract class UnitOfWork<T> {
private SessionFactory sessionFactory;
public UnitOfWork(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected abstract T runInSession() throws Exception;
public T run() {
final Session session = sessionFactory.openSession();
if (ManagedSessionContext.hasBind(sessionFactory)) {
throw new IllegalStateException("Already in a unit of work!");
}
T t = null;
try {
ManagedSessionContext.bind(session);
session.beginTransaction();
try {
t = runInSession();
commitTransaction(session);
} catch (Exception e) {
rollbackTransaction(session);
this.<RuntimeException> rethrow(e);
}
} finally {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
return t;
}
private void rollbackTransaction(Session session) {
final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) {
txn.rollback();
}
}
private void commitTransaction(Session session) {
final Transaction txn = session.getTransaction();
if (txn != null && txn.isActive()) {
txn.commit();
}
}
@SuppressWarnings("unchecked")
private <E extends Exception> void rethrow(Exception e) throws E {
throw (E) e;
}
}

View File

@@ -1,68 +1,27 @@
package com.commafeed.backend.dao;
import javax.ejb.Stateless;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.QUser;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.User_;
@Stateless
public class UserDAO extends GenericDAO<User> {
private QUser user = QUser.user;
public UserDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public User findByName(String name) {
CriteriaQuery<User> query = builder.createQuery(getType());
Root<User> root = query.from(getType());
query.where(builder.equal(builder.lower(root.get(User_.name)), name.toLowerCase()));
TypedQuery<User> q = em.createQuery(query);
cache(q);
User user = null;
try {
user = q.getSingleResult();
} catch (NoResultException e) {
user = null;
}
return user;
return newQuery().from(user).where(user.name.equalsIgnoreCase(name)).uniqueResult(user);
}
public User findByApiKey(String key) {
CriteriaQuery<User> query = builder.createQuery(getType());
Root<User> root = query.from(getType());
query.where(builder.equal(root.get(User_.apiKey), key));
TypedQuery<User> q = em.createQuery(query);
cache(q);
User user = null;
try {
user = q.getSingleResult();
} catch (NoResultException e) {
user = null;
}
return user;
return newQuery().from(user).where(user.apiKey.equalsIgnoreCase(key)).uniqueResult(user);
}
public User findByEmail(String email) {
if (StringUtils.isBlank(email)) {
return null;
}
CriteriaQuery<User> query = builder.createQuery(getType());
Root<User> root = query.from(getType());
query.where(builder.equal(root.get(User_.email), email));
TypedQuery<User> q = em.createQuery(query);
User user = null;
try {
user = q.getSingleResult();
} catch (NoResultException e) {
user = null;
}
return user;
return newQuery().from(user).where(user.email.equalsIgnoreCase(email)).uniqueResult(user);
}
}

View File

@@ -3,38 +3,28 @@ package com.commafeed.backend.dao;
import java.util.List;
import java.util.Set;
import javax.ejb.Stateless;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;
import org.hibernate.SessionFactory;
import com.commafeed.backend.model.QUserRole;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserRole;
import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.model.UserRole_;
import com.commafeed.backend.model.User_;
import com.google.common.collect.Sets;
@Stateless
public class UserRoleDAO extends GenericDAO<UserRole> {
@Override
private QUserRole role = QUserRole.userRole;
public UserRoleDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public List<UserRole> findAll() {
CriteriaQuery<UserRole> query = builder.createQuery(getType());
Root<UserRole> root = query.from(getType());
query.distinct(true);
root.fetch(UserRole_.user, JoinType.LEFT);
return em.createQuery(query).getResultList();
return newQuery().from(role).leftJoin(role.user).fetch().distinct().list(role);
}
public List<UserRole> findAll(User user) {
CriteriaQuery<UserRole> query = builder.createQuery(getType());
Root<UserRole> root = query.from(getType());
query.where(builder.equal(root.get(UserRole_.user).get(User_.id), user.getId()));
return cache(em.createQuery(query)).getResultList();
return newQuery().from(role).where(role.user.eq(user)).distinct().list(role);
}
public Set<Role> findRoles(User user) {

View File

@@ -1,31 +1,18 @@
package com.commafeed.backend.dao;
import javax.ejb.Stateless;
import javax.persistence.NoResultException;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import com.commafeed.backend.model.User;
import com.commafeed.backend.model.UserSettings;
import com.commafeed.backend.model.UserSettings_;
import com.commafeed.backend.model.User_;
@Stateless
public class UserSettingsDAO extends GenericDAO<UserSettings> {
public UserSettingsDAO(SessionFactory sessionFactory) {
super(sessionFactory);
}
public UserSettings findByUser(User user) {
CriteriaQuery<UserSettings> query = builder.createQuery(getType());
Root<UserSettings> root = query.from(getType());
query.where(builder.equal(root.get(UserSettings_.user).get(User_.id), user.getId()));
UserSettings settings = null;
try {
settings = cache(em.createQuery(query)).getSingleResult();
} catch (NoResultException e) {
settings = null;
}
return settings;
return uniqueResult(criteria().add(Restrictions.eq("user", user)));
}
}

View File

@@ -1,13 +1,12 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
@@ -20,6 +19,7 @@ import com.commafeed.backend.HttpGetter.HttpResult;
*
*/
@Slf4j
@AllArgsConstructor
public class FaviconFetcher {
private static long MIN_ICON_LENGTH = 100;
@@ -30,8 +30,7 @@ public class FaviconFetcher {
"text/ico", "application/ico", "image/x-ms-bmp", "image/x-bmp", "image/gif", "image/png", "image/jpeg");
private static List<String> ICON_MIMETYPE_BLACKLIST = Arrays.asList("application/xml", "text/html");
@Inject
HttpGetter getter;
private final HttpGetter getter;
public byte[] fetch(String url) {

View File

@@ -1,10 +1,9 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.io.IOException;
import java.util.Date;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.StringUtils;
@@ -21,13 +20,11 @@ import com.commafeed.backend.model.Feed;
import com.sun.syndication.io.FeedException;
@Slf4j
@AllArgsConstructor
public class FeedFetcher {
@Inject
FeedParser parser;
@Inject
HttpGetter getter;
private final FeedParser parser;
private final HttpGetter getter;
public FetchedFeed fetch(String feedUrl, boolean extractFeedUrlFromHtml, String lastModified, String eTag, Date lastPublishedDate,
String lastContentHash) throws FeedException, ClientProtocolException, IOException, NotModifiedException {

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.io.StringReader;
import java.text.DateFormat;

View File

@@ -1,71 +1,41 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.time.DateUtils;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
/**
* Infinite loop fetching feeds from the database and queuing them to the {@link FeedRefreshWorker} pool. Also handles feed database updates
* at the end of the cycle through {@link #giveBack(Feed)}.
*
*/
@ApplicationScoped
@Slf4j
public class FeedRefreshTaskGiver {
public class FeedQueues {
@Inject
FeedDAO feedDAO;
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
MetricRegistry metrics;
@Inject
FeedRefreshWorker worker;
private int backgroundThreads;
private final FeedDAO feedDAO;
private final CommaFeedConfiguration config;
private Queue<FeedRefreshContext> addQueue = Queues.newConcurrentLinkedQueue();
private Queue<FeedRefreshContext> takeQueue = Queues.newConcurrentLinkedQueue();
private Queue<Feed> giveBackQueue = Queues.newConcurrentLinkedQueue();
private ExecutorService executor;
private Meter feedRefreshed;
private Meter threadWaited;
private Meter refill;
@PostConstruct
public void init() {
backgroundThreads = applicationSettingsService.get().getBackgroundThreads();
executor = Executors.newFixedThreadPool(1);
feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed"));
threadWaited = metrics.meter(MetricRegistry.name(getClass(), "threadWaited"));
@Inject
public FeedQueues(FeedDAO feedDAO, CommaFeedConfiguration config, MetricRegistry metrics) {
this.config = config;
this.feedDAO = feedDAO;
refill = metrics.meter(MetricRegistry.name(getClass(), "refill"));
metrics.register(MetricRegistry.name(getClass(), "addQueue"), new Gauge<Integer>() {
@Override
@@ -87,56 +57,10 @@ public class FeedRefreshTaskGiver {
});
}
@PreDestroy
public void shutdown() {
executor.shutdownNow();
while (!executor.isTerminated()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
log.error("interrupted while waiting for threads to finish.");
}
}
}
public void start() {
log.info("starting feed refresh task giver");
executor.execute(new Runnable() {
@Override
public void run() {
try {
// sleeping for a little while, let everything settle
Thread.sleep(60000);
} catch (InterruptedException e) {
log.error("interrupted while sleeping");
}
while (!executor.isShutdown()) {
try {
FeedRefreshContext context = take();
if (context != null) {
feedRefreshed.mark();
worker.updateFeed(context);
} else {
log.debug("nothing to do, sleeping for 15s");
threadWaited.mark();
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
log.error("interrupted while sleeping");
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
});
}
/**
* take a feed from the refresh queue
*/
private FeedRefreshContext take() {
public synchronized FeedRefreshContext take() {
FeedRefreshContext context = takeQueue.poll();
if (context == null) {
@@ -146,15 +70,11 @@ public class FeedRefreshTaskGiver {
return context;
}
public Long getUpdatableCount() {
return feedDAO.getUpdatableCount(getLastLoginThreshold());
}
/**
* add a feed to the refresh queue
*/
public void add(Feed feed, boolean urgent) {
int refreshInterval = applicationSettingsService.get().getRefreshIntervalMinutes();
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
if (feed.getLastUpdated() == null || feed.getLastUpdated().before(DateUtils.addMinutes(new Date(), -1 * refreshInterval))) {
addQueue.add(new FeedRefreshContext(feed, urgent));
}
@@ -167,7 +87,7 @@ public class FeedRefreshTaskGiver {
refill.mark();
List<FeedRefreshContext> contexts = Lists.newArrayList();
int batchSize = Math.min(100, 3 * backgroundThreads);
int batchSize = Math.min(100, 3 * config.getApplicationSettings().getBackgroundThreads());
// add feeds we got from the add() method
int addQueueSize = addQueue.size();
@@ -176,7 +96,7 @@ public class FeedRefreshTaskGiver {
}
// add feeds that are up to refresh from the database
if (!applicationSettingsService.get().isCrawlingPaused()) {
if (!config.getApplicationSettings().isCrawlingPaused()) {
int count = batchSize - contexts.size();
if (count > 0) {
List<Feed> feeds = feedDAO.findNextUpdatable(count, getLastLoginThreshold());
@@ -186,12 +106,12 @@ public class FeedRefreshTaskGiver {
}
}
// set the disabledDate to now as we use the disabledDate in feedDAO to decide what to refresh next. We also use a map to remove
// set the disabledDate as we use it in feedDAO to decide what to refresh next. We also use a map to remove
// duplicates.
Map<Long, FeedRefreshContext> map = Maps.newLinkedHashMap();
for (FeedRefreshContext context : contexts) {
Feed feed = context.getFeed();
feed.setDisabledUntil(new Date());
feed.setDisabledUntil(DateUtils.addMinutes(new Date(), config.getApplicationSettings().getRefreshIntervalMinutes()));
map.put(feed.getId(), context);
}
@@ -210,7 +130,7 @@ public class FeedRefreshTaskGiver {
for (FeedRefreshContext context : map.values()) {
feeds.add(context.getFeed());
}
feedDAO.saveOrUpdate(feeds);
feedDAO.merge(feeds);
}
/**
@@ -225,7 +145,7 @@ public class FeedRefreshTaskGiver {
}
private Date getLastLoginThreshold() {
if (applicationSettingsService.get().isHeavyLoad()) {
if (config.getApplicationSettings().isHeavyLoad()) {
return DateUtils.addDays(new Date(), -30);
} else {
return null;

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.RejectedExecutionHandler;

View File

@@ -0,0 +1,95 @@
package com.commafeed.backend.feed;
import io.dropwizard.lifecycle.Managed;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.dao.UnitOfWork;
/**
* Infinite loop fetching feeds from @FeedQueues and queuing them to the {@link FeedRefreshWorker} pool.
*
*/
@Slf4j
public class FeedRefreshTaskGiver implements Managed {
private final SessionFactory sessionFactory;
private final FeedQueues queues;
private final FeedRefreshWorker worker;
private ExecutorService executor;
private Meter feedRefreshed;
private Meter threadWaited;
@Inject
public FeedRefreshTaskGiver(SessionFactory sessionFactory, FeedQueues queues, FeedDAO feedDAO, FeedRefreshWorker worker,
CommaFeedConfiguration config, MetricRegistry metrics) {
this.sessionFactory = sessionFactory;
this.queues = queues;
this.worker = worker;
executor = Executors.newFixedThreadPool(1);
feedRefreshed = metrics.meter(MetricRegistry.name(getClass(), "feedRefreshed"));
threadWaited = metrics.meter(MetricRegistry.name(getClass(), "threadWaited"));
}
@Override
public void stop() {
executor.shutdownNow();
while (!executor.isTerminated()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
log.error("interrupted while waiting for threads to finish.");
}
}
}
@Override
public void start() {
log.info("starting feed refresh task giver");
executor.execute(new Runnable() {
@Override
public void run() {
while (!executor.isShutdown()) {
try {
FeedRefreshContext context = new UnitOfWork<FeedRefreshContext>(sessionFactory) {
@Override
protected FeedRefreshContext runInSession() throws Exception {
FeedRefreshContext context = queues.take();
if (context != null) {
feedRefreshed.mark();
worker.updateFeed(context);
}
return context;
}
}.run();
if (context == null) {
log.debug("nothing to do, sleeping for 15s");
threadWaited.mark();
try {
Thread.sleep(15000);
} catch (InterruptedException e) {
log.error("interrupted while sleeping");
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
});
}
}

View File

@@ -1,4 +1,6 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import io.dropwizard.lifecycle.Managed;
import java.util.Arrays;
import java.util.Date;
@@ -7,67 +9,42 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.SessionFactory;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConfiguration.ApplicationSettings;
import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feeds.FeedRefreshExecutor.Task;
import com.commafeed.backend.model.ApplicationSettings;
import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.feed.FeedRefreshExecutor.Task;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User;
import com.commafeed.backend.pubsubhubbub.SubscriptionHandler;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.commafeed.backend.services.FeedUpdateService;
import com.commafeed.backend.service.FeedUpdateService;
import com.commafeed.backend.service.PubSubService;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Striped;
@ApplicationScoped
@Slf4j
public class FeedRefreshUpdater {
public class FeedRefreshUpdater implements Managed {
@Inject
FeedUpdateService feedUpdateService;
@Inject
SubscriptionHandler handler;
@Inject
FeedRefreshTaskGiver taskGiver;
@Inject
FeedDAO feedDAO;
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
MetricRegistry metrics;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
CacheService cache;
private final SessionFactory sessionFactory;
private final FeedUpdateService feedUpdateService;
private final PubSubService pubSubService;
private final FeedQueues queues;
private final CommaFeedConfiguration config;
private final FeedSubscriptionDAO feedSubscriptionDAO;
private final CacheService cache;
private FeedRefreshExecutor pool;
private Striped<Lock> locks;
@@ -77,9 +54,18 @@ public class FeedRefreshUpdater {
private Meter feedUpdated;
private Meter entryInserted;
@PostConstruct
public void init() {
ApplicationSettings settings = applicationSettingsService.get();
public FeedRefreshUpdater(SessionFactory sessionFactory, FeedUpdateService feedUpdateService, PubSubService pubSubService,
FeedQueues queues, CommaFeedConfiguration config, MetricRegistry metrics, FeedSubscriptionDAO feedSubscriptionDAO,
CacheService cache) {
this.sessionFactory = sessionFactory;
this.feedUpdateService = feedUpdateService;
this.pubSubService = pubSubService;
this.queues = queues;
this.config = config;
this.feedSubscriptionDAO = feedSubscriptionDAO;
this.cache = cache;
ApplicationSettings settings = config.getApplicationSettings();
int threads = Math.max(settings.getDatabaseUpdateThreads(), 1);
pool = new FeedRefreshExecutor("feed-refresh-updater", threads, Math.min(50 * threads, 1000), metrics);
locks = Striped.lazyWeakLock(threads * 100000);
@@ -90,8 +76,12 @@ public class FeedRefreshUpdater {
entryInserted = metrics.meter(MetricRegistry.name(getClass(), "entryInserted"));
}
@PreDestroy
public void shutdown() {
@Override
public void start() throws Exception {
}
@Override
public void stop() throws Exception {
pool.shutdown();
}
@@ -109,6 +99,16 @@ public class FeedRefreshUpdater {
@Override
public void run() {
new UnitOfWork<Void>(sessionFactory) {
@Override
protected Void runInSession() throws Exception {
internalRun();
return null;
}
}.run();
}
public void internalRun() {
boolean ok = true;
Feed feed = context.getFeed();
List<FeedEntry> entries = context.getEntries();
@@ -151,7 +151,7 @@ public class FeedRefreshUpdater {
}
}
if (applicationSettingsService.get().isPubsubhubbub()) {
if (config.getApplicationSettings().isPubsubhubbub()) {
handlePubSub(feed);
}
if (!ok) {
@@ -159,7 +159,7 @@ public class FeedRefreshUpdater {
feed.setDisabledUntil(new Date(0));
}
feedUpdated.mark();
taskGiver.giveBack(feed);
queues.giveBack(feed);
}
@Override
@@ -218,10 +218,11 @@ public class FeedRefreshUpdater {
new Thread() {
@Override
public void run() {
handler.subscribe(feed);
pubSubService.subscribe(feed);
}
}.start();
}
}
}
}

View File

@@ -1,62 +1,53 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import io.dropwizard.lifecycle.Managed;
import java.util.Date;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.codahale.metrics.MetricRegistry;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.HttpGetter.NotModifiedException;
import com.commafeed.backend.feeds.FeedRefreshExecutor.Task;
import com.commafeed.backend.model.ApplicationSettings;
import com.commafeed.backend.feed.FeedRefreshExecutor.Task;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.google.common.base.Optional;
/**
* Calls {@link FeedFetcher} and handles its outcome
*
*/
@ApplicationScoped
@Slf4j
public class FeedRefreshWorker {
public class FeedRefreshWorker implements Managed {
@Inject
FeedRefreshUpdater feedRefreshUpdater;
private final FeedRefreshUpdater feedRefreshUpdater;
private final FeedFetcher fetcher;
private final FeedQueues queues;
private final CommaFeedConfiguration config;
private final FeedRefreshExecutor pool;
@Inject
FeedFetcher fetcher;
@Inject
FeedRefreshTaskGiver taskGiver;
@Inject
MetricRegistry metrics;
@Inject
ApplicationSettingsService applicationSettingsService;
private FeedRefreshExecutor pool;
@PostConstruct
private void init() {
ApplicationSettings settings = applicationSettingsService.get();
int threads = settings.getBackgroundThreads();
public FeedRefreshWorker(FeedRefreshUpdater feedRefreshUpdater, FeedFetcher fetcher, FeedQueues queues, CommaFeedConfiguration config,
MetricRegistry metrics) {
this.feedRefreshUpdater = feedRefreshUpdater;
this.fetcher = fetcher;
this.config = config;
this.queues = queues;
int threads = config.getApplicationSettings().getBackgroundThreads();
pool = new FeedRefreshExecutor("feed-refresh-worker", threads, Math.min(20 * threads, 1000), metrics);
}
@PreDestroy
public void shutdown() {
@Override
public void start() throws Exception {
}
@Override
public void stop() throws Exception {
pool.shutdown();
}
@@ -85,16 +76,16 @@ public class FeedRefreshWorker {
private void update(FeedRefreshContext context) {
Feed feed = context.getFeed();
int refreshInterval = applicationSettingsService.get().getRefreshIntervalMinutes();
int refreshInterval = config.getApplicationSettings().getRefreshIntervalMinutes();
Date disabledUntil = DateUtils.addMinutes(new Date(), refreshInterval);
try {
String url = ObjectUtils.firstNonNull(feed.getUrlAfterRedirect(), feed.getUrl());
String url = Optional.fromNullable(feed.getUrlAfterRedirect()).or(feed.getUrl());
FetchedFeed fetchedFeed = fetcher.fetch(url, false, feed.getLastModifiedHeader(), feed.getEtagHeader(),
feed.getLastPublishedDate(), feed.getLastContentHash());
// stops here if NotModifiedException or any other exception is thrown
List<FeedEntry> entries = fetchedFeed.getEntries();
if (applicationSettingsService.get().isHeavyLoad()) {
if (config.getApplicationSettings().isHeavyLoad()) {
disabledUntil = FeedUtils.buildDisabledUntil(fetchedFeed.getFeed().getLastEntryDate(), fetchedFeed.getFeed()
.getAverageEntryInterval(), disabledUntil);
}
@@ -122,14 +113,14 @@ public class FeedRefreshWorker {
} catch (NotModifiedException e) {
log.debug("Feed not modified : {} - {}", feed.getUrl(), e.getMessage());
if (applicationSettingsService.get().isHeavyLoad()) {
if (config.getApplicationSettings().isHeavyLoad()) {
disabledUntil = FeedUtils.buildDisabledUntil(feed.getLastEntryDate(), feed.getAverageEntryInterval(), disabledUntil);
}
feed.setErrorCount(0);
feed.setMessage(e.getMessage());
feed.setDisabledUntil(disabledUntil);
taskGiver.giveBack(feed);
queues.giveBack(feed);
} catch (Exception e) {
String message = "Unable to refresh feed " + feed.getUrl() + " : " + e.getMessage();
log.debug(e.getClass().getName() + " " + message, e);
@@ -138,7 +129,7 @@ public class FeedRefreshWorker {
feed.setMessage(message);
feed.setDisabledUntil(FeedUtils.buildDisabledUntil(feed.getErrorCount()));
taskGiver.giveBack(feed);
queues.giveBack(feed);
}
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.io.StringReader;
import java.net.MalformedURLException;
@@ -15,9 +15,8 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.math.stat.descriptive.SummaryStatistics;
import org.apache.wicket.request.UrlUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Document.OutputSettings;
@@ -129,10 +128,10 @@ public class FeedUtils {
}
return encoding;
}
public static String replaceHtmlEntitiesWithNumericEntities(String source){
public static String replaceHtmlEntitiesWithNumericEntities(String source) {
String result = source;
for(String entity : HtmlEntities.NUMERIC_MAPPING.keySet()){
for (String entity : HtmlEntities.NUMERIC_MAPPING.keySet()) {
result = result.replace(entity, HtmlEntities.NUMERIC_MAPPING.get(entity));
}
return result;
@@ -422,7 +421,7 @@ public class FeedUtils {
return url;
}
String baseUrl = (feedLink == null || UrlUtils.isRelative(feedLink)) ? feedUrl : feedLink;
String baseUrl = (feedLink == null || isRelative(feedLink)) ? feedUrl : feedLink;
if (baseUrl == null) {
return url;
@@ -439,6 +438,15 @@ public class FeedUtils {
return result;
}
public static boolean isRelative(final String url) {
// the regex means "doesn't start with 'scheme://'"
if ((url != null) && (url.startsWith("/") == false) && (!url.matches("^\\w+\\:\\/\\/.*")) && !(url.startsWith("#"))) {
return true;
} else {
return false;
}
}
public static String getFaviconUrl(FeedSubscription subscription, String publicUrl) {
return removeTrailingSlash(publicUrl) + "/rest/feed/favicon/" + subscription.getId();
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.List;

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.feeds;
package com.commafeed.backend.feed;
import java.util.Collections;
import java.util.Map;

View File

@@ -1,30 +0,0 @@
package com.commafeed.backend.metrics;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
import lombok.extern.slf4j.Slf4j;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
@ApplicationScoped
@Slf4j
public class MetricRegistryProducer {
private MetricRegistry registry;
@PostConstruct
private void init() {
log.info("initializing metrics registry");
registry = new MetricRegistry();
JmxReporter.forRegistry(registry).build().start();
log.info("metrics registry initialized");
}
@Produces
public MetricRegistry produceMetricsRegistry() {
return registry;
}
}

View File

@@ -1,43 +0,0 @@
package com.commafeed.backend.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.apache.log4j.Level;
@Entity
@Table(name = "APPLICATIONSETTINGS")
@SuppressWarnings("serial")
@Getter
@Setter
public class ApplicationSettings extends AbstractModel {
private String publicUrl;
private boolean allowRegistrations = false;
private String googleAnalyticsTrackingCode;
private String googleClientId;
private String googleClientSecret;
private int backgroundThreads = 3;
private int databaseUpdateThreads = 1;
private String smtpHost;
private int smtpPort;
private boolean smtpTls;
private String smtpUserName;
private String smtpPassword;
private boolean heavyLoad;
private boolean pubsubhubbub;
private boolean feedbackButton = true;
private String logLevel = Level.INFO.toString();
private boolean imageProxyEnabled;
private int queryTimeout;
private boolean crawlingPaused;
private int keepStatusDays = 0;
private int refreshIntervalMinutes = 5;
@Column(length = 255)
private String announcement;
}

View File

@@ -3,7 +3,6 @@ package com.commafeed.backend.model;
import java.util.Date;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -15,14 +14,9 @@ import javax.persistence.TemporalType;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class Feed extends AbstractModel {

View File

@@ -2,7 +2,6 @@ package com.commafeed.backend.model;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -14,14 +13,9 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDCATEGORIES")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class FeedCategory extends AbstractModel {
@@ -31,11 +25,9 @@ public class FeedCategory extends AbstractModel {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private FeedCategory parent;
@OneToMany(mappedBy = "parent")

View File

@@ -3,7 +3,6 @@ package com.commafeed.backend.model;
import java.util.Date;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -19,14 +18,9 @@ import javax.persistence.TemporalType;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDENTRIES")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class FeedEntry extends AbstractModel {
@@ -55,7 +49,7 @@ public class FeedEntry extends AbstractModel {
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
private Set<FeedEntryStatus> statuses;
@OneToMany(mappedBy = "entry", cascade = CascadeType.REMOVE)
private Set<FeedEntryTag> tags;

View File

@@ -2,7 +2,6 @@ package com.commafeed.backend.model;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
@@ -12,14 +11,9 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDENTRYCONTENTS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class FeedEntryContent extends AbstractModel {

View File

@@ -3,7 +3,6 @@ package com.commafeed.backend.model;
import java.util.Date;
import java.util.List;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -19,19 +18,16 @@ import javax.persistence.Transient;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import com.google.common.collect.Lists;
@Entity
@Table(name = "FEEDENTRYSTATUSES")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
@NamedQueries(@NamedQuery(name="Statuses.deleteOld", query="delete from FeedEntryStatus s where s.entryInserted < :date and s.starred = false"))
@NamedQueries(@NamedQuery(
name = "Statuses.deleteOld",
query = "delete from FeedEntryStatus s where s.entryInserted < :date and s.starred = false"))
public class FeedEntryStatus extends AbstractModel {
@ManyToOne(fetch = FetchType.LAZY)
@@ -48,7 +44,7 @@ public class FeedEntryStatus extends AbstractModel {
@Transient
private boolean markable;
@Transient
private List<FeedEntryTag> tags = Lists.newArrayList();

View File

@@ -1,6 +1,5 @@
package com.commafeed.backend.model;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@@ -11,14 +10,9 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDENTRYTAGS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class FeedEntryTag extends AbstractModel {

View File

@@ -2,7 +2,6 @@ package com.commafeed.backend.model;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -15,33 +14,25 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "FEEDSUBSCRIPTIONS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class FeedSubscription extends AbstractModel {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private Feed feed;
@Column(length = 128, nullable = false)
private String title;
@ManyToOne(fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
private FeedCategory category;
@OneToMany(mappedBy = "subscription", cascade = CascadeType.REMOVE)

View File

@@ -3,7 +3,6 @@ package com.commafeed.backend.model;
import java.util.Date;
import java.util.Set;
import javax.persistence.Cacheable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
@@ -16,23 +15,20 @@ import javax.persistence.TemporalType;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import com.google.common.collect.Sets;
@Entity
@Table(name = "USERS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class User extends AbstractModel {
@Column(length = 32, nullable = false, unique = true)
private String name;
@Column(length = 255, unique = true)
private String email;
@@ -61,6 +57,8 @@ public class User extends AbstractModel {
private Date recoverPasswordTokenDate;
@OneToMany(mappedBy = "user", cascade = { CascadeType.PERSIST, CascadeType.REMOVE })
@Cascade({ org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.REMOVE })
private Set<UserRole> roles = Sets.newHashSet();
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)

View File

@@ -1,6 +1,5 @@
package com.commafeed.backend.model;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
@@ -13,14 +12,9 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "USERROLES")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class UserRole extends AbstractModel {

View File

@@ -1,6 +1,5 @@
package com.commafeed.backend.model;
import javax.persistence.Cacheable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
@@ -14,14 +13,9 @@ import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
@Entity
@Table(name = "USERSETTINGS")
@SuppressWarnings("serial")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
@Getter
@Setter
public class UserSettings extends AbstractModel {
@@ -69,7 +63,7 @@ public class UserSettings extends AbstractModel {
@Column(name = "scroll_speed")
private int scrollSpeed;
private boolean email;
private boolean gmail;
private boolean facebook;

View File

@@ -3,8 +3,7 @@ package com.commafeed.backend.opml;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
@@ -15,14 +14,11 @@ import com.sun.syndication.feed.opml.Attribute;
import com.sun.syndication.feed.opml.Opml;
import com.sun.syndication.feed.opml.Outline;
@Stateless
@AllArgsConstructor
public class OPMLExporter {
@Inject
FeedCategoryDAO feedCategoryDAO;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
private final FeedCategoryDAO feedCategoryDAO;
private final FeedSubscriptionDAO feedSubscriptionDAO;
@SuppressWarnings("unchecked")
public Opml export(User user) {

View File

@@ -3,12 +3,6 @@ package com.commafeed.backend.opml;
import java.io.StringReader;
import java.util.List;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
@@ -16,37 +10,36 @@ import org.apache.commons.lang.StringUtils;
import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.User;
import com.commafeed.backend.services.FeedSubscriptionService;
import com.commafeed.backend.services.FeedSubscriptionService.FeedSubscriptionException;
import com.commafeed.backend.service.FeedSubscriptionService;
import com.commafeed.backend.service.FeedSubscriptionService.FeedSubscriptionException;
import com.sun.syndication.feed.opml.Opml;
import com.sun.syndication.feed.opml.Outline;
import com.sun.syndication.io.WireFeedInput;
@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
@Slf4j
public class OPMLImporter {
@Inject
FeedSubscriptionService feedSubscriptionService;
private FeedCategoryDAO feedCategoryDAO;
private FeedSubscriptionService feedSubscriptionService;
private CacheService cache;
@Inject
FeedCategoryDAO feedCategoryDAO;
@Inject
CacheService cache;
public OPMLImporter(FeedCategoryDAO feedCategoryDAO, FeedSubscriptionService feedSubscriptionService, CacheService cache) {
super();
this.feedCategoryDAO = feedCategoryDAO;
this.feedSubscriptionService = feedSubscriptionService;
this.cache = cache;
}
@SuppressWarnings("unchecked")
@Asynchronous
public void importOpml(User user, String xml) {
xml = xml.substring(xml.indexOf('<'));
WireFeedInput input = new WireFeedInput();
try {
Opml feed = (Opml) input.build(new StringReader(xml));
List<Outline> outlines = (List<Outline>) feed.getOutlines();
List<Outline> outlines = feed.getOutlines();
for (Outline outline : outlines) {
handleOutline(user, outline, null);
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.ResourceBundle;
@@ -6,13 +6,7 @@ public class ApplicationPropertiesService {
private ResourceBundle bundle;
private static ApplicationPropertiesService INSTANCE = new ApplicationPropertiesService();
public static ApplicationPropertiesService get() {
return INSTANCE;
}
private ApplicationPropertiesService() {
public ApplicationPropertiesService() {
bundle = ResourceBundle.getBundle("application");
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.Calendar;
import java.util.Collections;
@@ -6,8 +6,7 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.commafeed.backend.dao.FeedDAO;
@@ -25,28 +24,17 @@ import com.commafeed.backend.model.FeedSubscription;
*
*/
@Slf4j
@RequiredArgsConstructor
public class DatabaseCleaningService {
private static final int BATCH_SIZE = 100;
@Inject
FeedDAO feedDAO;
private final FeedDAO feedDAO;
private final FeedEntryDAO feedEntryDAO;
private final FeedEntryContentDAO feedEntryContentDAO;
private final FeedEntryStatusDAO feedEntryStatusDAO;
private final FeedSubscriptionDAO feedSubscriptionDAO;
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
@Inject
FeedEntryContentDAO feedEntryContentDAO;
@Inject
FeedEntryStatusDAO feedEntryStatusDAO;
@Inject
ApplicationSettingsService applicationSettingsService;
public long cleanEntriesWithoutSubscriptions() {
log.info("cleaning entries without subscriptions");
long total = 0;

View File

@@ -1,18 +1,18 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import com.commafeed.backend.dao.FeedEntryContentDAO;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.FeedEntryContent;
@RequiredArgsConstructor
public class FeedEntryContentService {
@Inject
FeedEntryContentDAO feedEntryContentDAO;
private final FeedEntryContentDAO feedEntryContentDAO;
/**
* this is NOT thread-safe

View File

@@ -1,10 +1,9 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.Date;
import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedEntryDAO;
@@ -16,20 +15,13 @@ import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User;
import com.google.common.collect.Lists;
@Stateless
@RequiredArgsConstructor
public class FeedEntryService {
@Inject
FeedEntryStatusDAO feedEntryStatusDAO;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
CacheService cache;
private final FeedSubscriptionDAO feedSubscriptionDAO;
private final FeedEntryDAO feedEntryDAO;
private final FeedEntryStatusDAO feedEntryStatusDAO;
private final CacheService cache;
public void markEntry(User user, Long entryId, boolean read) {

View File

@@ -1,10 +1,9 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.List;
import java.util.Map;
import javax.ejb.Stateless;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryTagDAO;
@@ -15,14 +14,11 @@ import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@Stateless
@RequiredArgsConstructor
public class FeedEntryTagService {
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
FeedEntryTagDAO feedEntryTagDAO;
private final FeedEntryDAO feedEntryDAO;
private final FeedEntryTagDAO feedEntryTagDAO;
public void updateTags(User user, Long entryId, List<String> tagNames) {
FeedEntry entry = feedEntryDAO.findById(entryId);

View File

@@ -0,0 +1,32 @@
package com.commafeed.backend.service;
import java.util.Date;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.Feed;
@RequiredArgsConstructor
public class FeedService {
private final FeedDAO feedDAO;
public synchronized Feed findOrCreate(String url) {
String normalized = FeedUtils.normalizeURL(url);
Feed feed = feedDAO.findByUrl(normalized);
if (feed == null) {
feed = new Feed();
feed.setUrl(url);
feed.setNormalizedUrl(normalized);
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
feed.setDisabledUntil(new Date(0));
feedDAO.saveOrUpdate(feed);
}
return feed;
}
}

View File

@@ -1,21 +1,19 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.List;
import java.util.Map;
import javax.ejb.ApplicationException;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.cache.CacheService;
import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.feed.FeedQueues;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedSubscription;
@@ -25,40 +23,26 @@ import com.commafeed.frontend.model.UnreadCount;
import com.google.common.collect.Maps;
@Slf4j
@RequiredArgsConstructor
public class FeedSubscriptionService {
@SuppressWarnings("serial")
@ApplicationException
public static class FeedSubscriptionException extends RuntimeException {
public FeedSubscriptionException(String msg) {
super(msg);
}
}
@Inject
FeedService feedService;
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
FeedEntryStatusDAO feedEntryStatusDAO;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
FeedRefreshTaskGiver taskGiver;
@Inject
CacheService cache;
private final FeedEntryStatusDAO feedEntryStatusDAO;
private final FeedSubscriptionDAO feedSubscriptionDAO;
private final FeedService feedService;
private final FeedQueues queues;
private final CacheService cache;
private final CommaFeedConfiguration config;
public Feed subscribe(User user, String url, String title, FeedCategory category) {
final String pubUrl = applicationSettingsService.get().getPublicUrl();
final String pubUrl = config.getApplicationSettings().getPublicUrl();
if (StringUtils.isBlank(pubUrl)) {
throw new FeedSubscriptionException("Public URL of this CommaFeed instance is not set");
}
@@ -79,7 +63,7 @@ public class FeedSubscriptionService {
sub.setTitle(FeedUtils.truncate(title, 128));
feedSubscriptionDAO.saveOrUpdate(sub);
taskGiver.add(feed, false);
queues.add(feed, false);
cache.invalidateUserRootCategory(user);
return feed;
}
@@ -99,7 +83,7 @@ public class FeedSubscriptionService {
List<FeedSubscription> subs = feedSubscriptionDAO.findAll(user);
for (FeedSubscription sub : subs) {
Feed feed = sub.getFeed();
taskGiver.add(feed, true);
queues.add(feed, true);
}
}

View File

@@ -1,9 +1,8 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.Date;
import javax.ejb.Stateless;
import javax.inject.Inject;
import lombok.AllArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
@@ -12,21 +11,18 @@ import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent;
@Stateless
@AllArgsConstructor
public class FeedUpdateService {
@Inject
FeedEntryDAO feedEntryDAO;
@Inject
FeedEntryContentService feedEntryContentService;
private final FeedEntryDAO feedEntryDAO;
private final FeedEntryContentService feedEntryContentService;
/**
* this is NOT thread-safe
*/
public boolean addEntry(Feed feed, FeedEntry entry) {
Long existing = feedEntryDAO.findExisting(entry.getGuid(), feed.getId());
Long existing = feedEntryDAO.findExisting(entry.getGuid(), feed);
if (existing != null) {
return false;
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.io.Serializable;
import java.util.Properties;
@@ -12,7 +12,8 @@ import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import com.commafeed.backend.model.ApplicationSettings;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.CommaFeedConfiguration.ApplicationSettings;
import com.commafeed.backend.model.User;
/**
@@ -23,11 +24,11 @@ import com.commafeed.backend.model.User;
public class MailService implements Serializable {
@Inject
ApplicationSettingsService applicationSettingsService;
CommaFeedConfiguration config;
public void sendMail(User user, String subject, String content) throws Exception {
ApplicationSettings settings = applicationSettingsService.get();
ApplicationSettings settings = config.getApplicationSettings();
final String username = settings.getSmtpUserName();
final String password = settings.getSmtpPassword();
@@ -41,6 +42,7 @@ public class MailService implements Serializable {
props.put("mail.smtp.port", "" + settings.getSmtpPort());
Session session = Session.getInstance(props, new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}

View File

@@ -1,4 +1,4 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.io.Serializable;
import java.security.NoSuchAlgorithmException;

View File

@@ -1,12 +1,13 @@
package com.commafeed.backend.pubsubhubbub;
package com.commafeed.backend.service;
import java.util.List;
import javax.inject.Inject;
import javax.ws.rs.core.MediaType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpHeaders;
import org.apache.http.NameValuePair;
@@ -16,14 +17,13 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.wicket.util.io.IOUtils;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.HttpGetter;
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.feed.FeedQueues;
import com.commafeed.backend.feed.FeedUtils;
import com.commafeed.backend.model.Feed;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.commafeed.frontend.rest.resources.PubSubHubbubCallbackREST;
import com.commafeed.frontend.resource.PubSubHubbubCallbackREST;
import com.google.common.collect.Lists;
/**
@@ -31,13 +31,11 @@ import com.google.common.collect.Lists;
*
*/
@Slf4j
public class SubscriptionHandler {
@RequiredArgsConstructor
public class PubSubService {
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
FeedRefreshTaskGiver taskGiver;
private final CommaFeedConfiguration config;
private final FeedQueues queues;
public void subscribe(Feed feed) {
@@ -51,7 +49,7 @@ public class SubscriptionHandler {
String hub = feed.getPushHub();
String topic = feed.getPushTopic();
String publicUrl = FeedUtils.removeTrailingSlash(applicationSettingsService.get().getPublicUrl());
String publicUrl = FeedUtils.removeTrailingSlash(config.getApplicationSettings().getPublicUrl());
log.debug("sending new pubsub subscription to {} for {}", hub, topic);
@@ -81,7 +79,7 @@ public class SubscriptionHandler {
if (code == 400 && StringUtils.contains(message, pushpressError)) {
String[] tokens = message.split(" ");
feed.setPushTopic(tokens[tokens.length - 1]);
taskGiver.giveBack(feed);
queues.giveBack(feed);
log.debug("handled pushpress subfeed {} : {}", topic, feed.getPushTopic());
} else {
throw new Exception("Unexpected response code: " + code + " " + response.getStatusLine().getReasonPhrase() + " - "

View File

@@ -0,0 +1,127 @@
package com.commafeed.backend.service;
import io.dropwizard.lifecycle.Managed;
import java.sql.Connection;
import java.util.Arrays;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.core.PostgresDatabase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.SessionFactory;
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.internal.SessionFactoryImpl;
import com.commafeed.CommaFeedApplication;
import com.commafeed.backend.dao.UnitOfWork;
import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.model.UserRole.Role;
@Slf4j
public class StartupService implements Managed {
private SessionFactory sessionFactory;
private UserDAO userDAO;
private UserService userService;
public StartupService(SessionFactory sessionFactory, UserDAO userDAO, UserService userService) {
this.sessionFactory = sessionFactory;
this.userDAO = userDAO;
this.userService = userService;
}
@Override
public void start() throws Exception {
updateSchema();
new UnitOfWork<Void>(sessionFactory) {
@Override
protected Void runInSession() throws Exception {
if (userDAO.findByName(CommaFeedApplication.USERNAME_ADMIN) == null) {
initialData();
}
return null;
}
}.run();
}
private void updateSchema() {
try {
Context context = null;
Connection connection = null;
try {
Thread currentThread = Thread.currentThread();
ClassLoader classLoader = currentThread.getContextClassLoader();
ResourceAccessor accessor = new ClassLoaderResourceAccessor(classLoader);
context = new InitialContext();
DataSource dataSource = getDataSource(sessionFactory);
connection = dataSource.getConnection();
JdbcConnection jdbcConnection = new JdbcConnection(connection);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);
if (database instanceof PostgresDatabase) {
database = new PostgresDatabase() {
@Override
public String escapeObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
return objectName;
}
};
database.setConnection(jdbcConnection);
}
Liquibase liq = new Liquibase("migrations.xml", accessor, database);
liq.update("prod");
} finally {
if (context != null) {
context.close();
}
if (connection != null) {
connection.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void initialData() {
log.info("Populating database with default values");
try {
userService.register(CommaFeedApplication.USERNAME_ADMIN, "admin", "admin@commafeed.com", Arrays.asList(Role.ADMIN, Role.USER),
true);
userService.register(CommaFeedApplication.USERNAME_DEMO, "demo", "demo@commafeed.com", Arrays.asList(Role.USER), true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
@Override
public void stop() throws Exception {
}
private static DataSource getDataSource(SessionFactory sessionFactory) {
if (sessionFactory instanceof SessionFactoryImpl) {
ConnectionProvider cp = ((SessionFactoryImpl) sessionFactory).getConnectionProvider();
if (cp instanceof DatasourceConnectionProviderImpl) {
return ((DatasourceConnectionProviderImpl) cp).getDataSource();
}
}
return null;
}
}

View File

@@ -1,18 +1,17 @@
package com.commafeed.backend.services;
package com.commafeed.backend.service;
import java.util.Collection;
import java.util.Date;
import java.util.UUID;
import javax.ejb.Stateless;
import javax.inject.Inject;
import lombok.RequiredArgsConstructor;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import com.commafeed.CommaFeedConfiguration;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.UserDAO;
import com.commafeed.backend.dao.UserSettingsDAO;
import com.commafeed.backend.model.User;
@@ -20,29 +19,16 @@ import com.commafeed.backend.model.UserRole;
import com.commafeed.backend.model.UserRole.Role;
import com.google.common.base.Preconditions;
@Stateless
@RequiredArgsConstructor
public class UserService {
@Inject
UserDAO userDAO;
private final FeedCategoryDAO feedCategoryDAO;
private final UserDAO userDAO;
private final UserSettingsDAO userSettingsDAO;
@Inject
FeedEntryStatusDAO feedEntryStatusDAO;
@Inject
FeedCategoryDAO feedCategoryDAO;
@Inject
UserSettingsDAO userSettingsDAO;
@Inject
PasswordEncryptionService encryptionService;
@Inject
ApplicationSettingsService applicationSettingsService;
@Inject
FeedSubscriptionService feedSubscriptionService;
private final FeedSubscriptionService feedSubscriptionService;
private final PasswordEncryptionService encryptionService;
private final CommaFeedConfiguration config;
public User login(String name, String password) {
if (name == null || password == null) {
@@ -63,7 +49,7 @@ public class UserService {
user.setLastLogin(now);
saveUser = true;
}
if (applicationSettingsService.get().isHeavyLoad()
if (config.getApplicationSettings().isHeavyLoad()
&& (user.getLastFullRefresh() == null || user.getLastFullRefresh().before(DateUtils.addMinutes(now, -30)))) {
user.setLastFullRefresh(now);
saveUser = true;
@@ -89,7 +75,7 @@ public class UserService {
Preconditions.checkNotNull(password);
if (!forceRegistration) {
Preconditions.checkState(applicationSettingsService.get().isAllowRegistrations(),
Preconditions.checkState(config.getApplicationSettings().isAllowRegistrations(),
"Registrations are closed on this CommaFeed instance");
Preconditions.checkNotNull(email);

View File

@@ -1,60 +0,0 @@
package com.commafeed.backend.services;
import java.util.Date;
import java.util.Enumeration;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import org.apache.commons.lang.time.DateUtils;
import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import com.commafeed.backend.dao.ApplicationSettingsDAO;
import com.commafeed.backend.model.ApplicationSettings;
import com.google.common.collect.Iterables;
@ApplicationScoped
public class ApplicationSettingsService {
@Inject
ApplicationSettingsDAO applicationSettingsDAO;
private ApplicationSettings settings;
@PostConstruct
private void init() {
settings = Iterables.getFirst(applicationSettingsDAO.findAll(), null);
}
public ApplicationSettings get() {
return settings;
}
public void save(ApplicationSettings settings) {
applicationSettingsDAO.saveOrUpdate(settings);
this.settings = settings;
applyLogLevel();
}
public Date getUnreadThreshold() {
int keepStatusDays = get().getKeepStatusDays();
return keepStatusDays > 0 ? DateUtils.addDays(new Date(), -1 * keepStatusDays) : null;
}
@SuppressWarnings("unchecked")
public void applyLogLevel() {
String logLevel = get().getLogLevel();
Level level = Level.toLevel(logLevel);
Enumeration<Logger> loggers = LogManager.getCurrentLoggers();
while (loggers.hasMoreElements()) {
Logger logger = loggers.nextElement();
if (logger.getName().startsWith("com.commafeed")) {
logger.setLevel(level);
}
}
}
}

View File

@@ -1,41 +0,0 @@
package com.commafeed.backend.services;
import java.util.Date;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;
import javax.inject.Inject;
import org.apache.commons.codec.digest.DigestUtils;
import com.commafeed.backend.dao.FeedDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feeds.FeedUtils;
import com.commafeed.backend.model.Feed;
@Singleton
public class FeedService {
@Inject
FeedDAO feedDAO;
@Inject
FeedSubscriptionDAO feedSubscriptionDAO;
@Lock(LockType.WRITE)
public Feed findOrCreate(String url) {
Feed feed = feedDAO.findByUrl(url);
if (feed == null) {
String normalized = FeedUtils.normalizeURL(url);
feed = new Feed();
feed.setUrl(url);
feed.setNormalizedUrl(normalized);
feed.setNormalizedUrlHash(DigestUtils.sha1Hex(normalized));
feed.setDisabledUntil(new Date(0));
feedDAO.saveOrUpdate(feed);
}
return feed;
}
}

View File

@@ -1,76 +0,0 @@
package com.commafeed.backend.startup;
import java.sql.Connection;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.core.PostgresDatabase;
import liquibase.database.jvm.JdbcConnection;
import liquibase.resource.ClassLoaderResourceAccessor;
import liquibase.resource.ResourceAccessor;
import liquibase.structure.DatabaseObject;
import com.commafeed.backend.services.ApplicationPropertiesService;
/**
* Executes needed liquibase database schema upgrades
*
*/
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class DatabaseUpdater {
public void update() {
ApplicationPropertiesService properties = ApplicationPropertiesService.get();
String datasourceName = properties.getDatasource();
try {
Context context = null;
Connection connection = null;
try {
Thread currentThread = Thread.currentThread();
ClassLoader classLoader = currentThread.getContextClassLoader();
ResourceAccessor accessor = new ClassLoaderResourceAccessor(classLoader);
context = new InitialContext();
DataSource dataSource = (DataSource) context.lookup(datasourceName);
connection = dataSource.getConnection();
JdbcConnection jdbcConnection = new JdbcConnection(connection);
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(jdbcConnection);
if (database instanceof PostgresDatabase) {
database = new PostgresDatabase() {
@Override
public String escapeObjectName(String objectName, Class<? extends DatabaseObject> objectType) {
return objectName;
}
};
database.setConnection(jdbcConnection);
}
Liquibase liq = new Liquibase("changelogs/db.changelog-master.xml", accessor, database);
liq.update("prod");
} finally {
if (context != null) {
context.close();
}
if (connection != null) {
connection.close();
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,120 +0,0 @@
package com.commafeed.backend.startup;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import com.commafeed.backend.dao.ApplicationSettingsDAO;
import com.commafeed.backend.feeds.FeedRefreshTaskGiver;
import com.commafeed.backend.model.ApplicationSettings;
import com.commafeed.backend.model.UserRole.Role;
import com.commafeed.backend.services.ApplicationSettingsService;
import com.commafeed.backend.services.UserService;
import com.google.common.collect.Maps;
/**
* Starting point of the application
*
*/
@Startup
@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Slf4j
public class StartupBean {
public static final String USERNAME_ADMIN = "admin";
public static final String USERNAME_DEMO = "demo";
@Inject
DatabaseUpdater databaseUpdater;
@Inject
ApplicationSettingsDAO applicationSettingsDAO;
@Inject
UserService userService;
@Inject
FeedRefreshTaskGiver taskGiver;
@Inject
ApplicationSettingsService applicationSettingsService;
private long startupTime;
private Map<String, String> supportedLanguages = Maps.newHashMap();
@PostConstruct
private void init() {
startupTime = System.currentTimeMillis();
// update database schema
databaseUpdater.update();
if (applicationSettingsDAO.getCount() == 0) {
// import initial data
initialData();
}
applicationSettingsService.applyLogLevel();
initSupportedLanguages();
// start fetching feeds
taskGiver.start();
}
private void initSupportedLanguages() {
Properties props = new Properties();
InputStream is = null;
try {
is = getClass().getResourceAsStream("/i18n/languages.properties");
props.load(new InputStreamReader(is, "UTF-8"));
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
IOUtils.closeQuietly(is);
}
for (Object key : props.keySet()) {
supportedLanguages.put(key.toString(), props.getProperty(key.toString()));
}
}
/**
* create default users
*/
private void initialData() {
log.info("Populating database with default values");
ApplicationSettings settings = new ApplicationSettings();
settings.setAnnouncement("Set the Public URL in the admin section!");
applicationSettingsService.save(settings);
try {
userService.register(USERNAME_ADMIN, "admin", "admin@commafeed.com", Arrays.asList(Role.ADMIN, Role.USER), true);
userService.register(USERNAME_DEMO, "demo", "demo@commafeed.com", Arrays.asList(Role.USER), true);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
public long getStartupTime() {
return startupTime;
}
public Map<String, String> getSupportedLanguages() {
return supportedLanguages;
}
}