support for Java9+ (#906)

* initial java9+ support

* restore session management, updated for jetty 9.4

* Session actually implements EntityManager

* reusable method for setting the timeout
This commit is contained in:
Jérémie Panzer
2019-04-22 20:30:06 +02:00
committed by GitHub
parent 5370db7c5e
commit 71ac2bfc45
13 changed files with 159 additions and 147 deletions

View File

@@ -13,7 +13,6 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.jetty.server.session.SessionHandler;
import org.hibernate.cfg.AvailableSettings;
import com.commafeed.backend.feed.FeedRefreshTaskGiver;
@@ -111,7 +110,7 @@ public class CommaFeedApplication extends Application<CommaFeedConfiguration> {
Injector injector = Guice.createInjector(new CommaFeedModule(hibernateBundle.getSessionFactory(), config, environment.metrics()));
// session management
environment.servlets().setSessionHandler(new SessionHandler(config.getSessionManagerFactory().build()));
environment.servlets().setSessionHandler(config.getSessionHandlerFactory().build());
// support for "@SecurityCheck User user" injection
environment.jersey().register(new SecurityCheckFactoryProvider.Binder(injector.getInstance(UserService.class)));

View File

@@ -1,8 +1,5 @@
package com.commafeed;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import java.util.Date;
import java.util.ResourceBundle;
@@ -10,15 +7,17 @@ import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import lombok.Getter;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.validator.constraints.NotBlank;
import com.commafeed.backend.cache.RedisPoolFactory;
import com.commafeed.frontend.session.SessionManagerFactory;
import com.commafeed.frontend.session.SessionHandlerFactory;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.dropwizard.Configuration;
import io.dropwizard.db.DataSourceFactory;
import lombok.Getter;
@Getter
public class CommaFeedConfiguration extends Configuration {
@@ -45,7 +44,7 @@ public class CommaFeedConfiguration extends Configuration {
@Valid
@NotNull
@JsonProperty("session")
private SessionManagerFactory sessionManagerFactory = new SessionManagerFactory();
private SessionHandlerFactory SessionHandlerFactory = new SessionHandlerFactory();
@Valid
@NotNull
@@ -138,7 +137,6 @@ public class CommaFeedConfiguration extends Configuration {
@Valid
private CacheType cache;
@NotNull
@Valid
private String announcement;

View File

@@ -17,7 +17,6 @@ import com.commafeed.backend.model.QUser;
import com.google.common.collect.Iterables;
import com.querydsl.jpa.JPAExpressions;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.hibernate.HibernateQuery;
@Singleton
public class FeedDAO extends GenericDAO<Feed> {
@@ -30,7 +29,7 @@ public class FeedDAO extends GenericDAO<Feed> {
}
public List<Feed> findNextUpdatable(int count, Date lastLoginThreshold) {
HibernateQuery<Feed> query = query().selectFrom(feed);
JPQLQuery<Feed> query = query().selectFrom(feed);
query.where(feed.disabledUntil.isNull().or(feed.disabledUntil.lt(new Date())));
if (lastLoginThreshold != null) {
@@ -60,7 +59,6 @@ public class FeedDAO extends GenericDAO<Feed> {
public List<Feed> findWithoutSubscriptions(int max) {
QFeedSubscription sub = QFeedSubscription.feedSubscription;
return query().selectFrom(feed).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max)
.fetch();
return query().selectFrom(feed).where(JPAExpressions.selectOne().from(sub).where(sub.feed.eq(feed)).notExists()).limit(max).fetch();
}
}

View File

@@ -33,7 +33,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Tuple;
import com.querydsl.jpa.hibernate.HibernateQuery;
import com.querydsl.jpa.impl.JPAQuery;
@Singleton
public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
@@ -67,7 +67,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
};
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ASC = Ordering.from(STATUS_COMPARATOR_DESC).reverse();
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ABC = new Comparator<FeedEntryStatus>() {
@Override
public int compare(FeedEntryStatus o1, FeedEntryStatus o2) {
@@ -77,9 +77,8 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return builder.toComparison();
}
};
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ZYX = Ordering.from(STATUS_COMPARATOR_ABC).reverse();
private static final Comparator<FeedEntryStatus> STATUS_COMPARATOR_ZYX = Ordering.from(STATUS_COMPARATOR_ABC).reverse();
public FeedEntryStatus getStatus(User user, FeedSubscription sub, FeedEntry entry) {
List<FeedEntryStatus> statuses = query().selectFrom(status).where(status.entry.eq(entry), status.subscription.eq(sub)).fetch();
@@ -107,26 +106,23 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
}
public List<FeedEntryStatus> findStarred(User user, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent) {
HibernateQuery<FeedEntryStatus> query = query().selectFrom(status).where(status.user.eq(user), status.starred.isTrue());
JPAQuery<FeedEntryStatus> query = query().selectFrom(status).where(status.user.eq(user), status.starred.isTrue());
if (newerThan != null) {
query.where(status.entryInserted.gt(newerThan));
}
if (order == ReadingOrder.asc) {
query.orderBy(status.entryUpdated.asc(), status.id.asc());
} else if (order == ReadingOrder.desc){
} else if (order == ReadingOrder.desc) {
query.orderBy(status.entryUpdated.desc(), status.id.desc());
} else if (order == ReadingOrder.abc) {
query.orderBy(status.entry.content.title.asc(), status.id.desc());
} else { //order == ReadingOrder.xyz
} else { // order == ReadingOrder.xyz
query.orderBy(status.entry.content.title.desc(), status.id.desc());
}
query.offset(offset).limit(limit);
int timeout = config.getApplicationSettings().getQueryTimeout();
if (timeout > 0) {
query.setTimeout(timeout / 1000);
}
setTimeout(query, config.getApplicationSettings().getQueryTimeout());
List<FeedEntryStatus> statuses = query.fetch();
for (FeedEntryStatus status : statuses) {
@@ -136,10 +132,10 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
return lazyLoadContent(includeContent, statuses);
}
private HibernateQuery<FeedEntry> buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords,
private JPAQuery<FeedEntry> buildQuery(User user, FeedSubscription sub, boolean unreadOnly, List<FeedEntryKeyword> keywords,
Date newerThan, int offset, int limit, ReadingOrder order, FeedEntryStatus last, String tag) {
HibernateQuery<FeedEntry> query = query().selectFrom(entry).where(entry.feed.eq(sub.getFeed()));
JPAQuery<FeedEntry> query = query().selectFrom(entry).where(entry.feed.eq(sub.getFeed()));
if (CollectionUtils.isNotEmpty(keywords)) {
query.join(entry.content, content);
@@ -187,14 +183,14 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
} else if (order == ReadingOrder.abc) {
query.join(entry.content, content);
query.where(content.title.lt(last.getEntry().getContent().getTitle()));
} else { //order == ReadingOrder.zyx
} else { // order == ReadingOrder.zyx
query.join(entry.content, content);
query.where(content.title.gt(last.getEntry().getContent().getTitle()));
}
} else if (order != null && (order == ReadingOrder.abc || order == ReadingOrder.zyx)) {
query.join(entry.content, content);
}
if (order != null) {
if (order == ReadingOrder.asc) {
query.orderBy(entry.updated.asc(), entry.id.asc());
@@ -202,7 +198,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
query.orderBy(entry.updated.desc(), entry.id.desc());
} else if (order == ReadingOrder.abc) {
query.orderBy(content.title.asc(), entry.id.asc());
} else { //order == ReadingOrder.zyx
} else { // order == ReadingOrder.zyx
query.orderBy(content.title.desc(), entry.id.desc());
}
}
@@ -212,10 +208,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
if (limit > -1) {
query.limit(limit);
}
int timeout = config.getApplicationSettings().getQueryTimeout();
if (timeout > 0) {
query.setTimeout(timeout / 1000);
}
setTimeout(query, config.getApplicationSettings().getQueryTimeout());
return query;
}
@@ -223,7 +216,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
List<FeedEntryKeyword> keywords, Date newerThan, int offset, int limit, ReadingOrder order, boolean includeContent,
boolean onlyIds, String tag) {
int capacity = offset + limit;
Comparator<FeedEntryStatus> comparator;
if (order == ReadingOrder.desc) {
comparator = STATUS_COMPARATOR_DESC;
@@ -238,7 +231,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
FixedSizeSortedSet<FeedEntryStatus> set = new FixedSizeSortedSet<FeedEntryStatus>(capacity, comparator);
for (FeedSubscription sub : subs) {
FeedEntryStatus last = (order != null && set.isFull()) ? set.last() : null;
HibernateQuery<FeedEntry> query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
JPAQuery<FeedEntry> query = buildQuery(user, sub, unreadOnly, keywords, newerThan, -1, capacity, order, last, tag);
List<Tuple> tuples = query.select(entry.id, entry.updated, status.id, entry.content.title).fetch();
for (Tuple tuple : tuples) {
@@ -291,7 +284,7 @@ public class FeedEntryStatusDAO extends GenericDAO<FeedEntryStatus> {
public UnreadCount getUnreadCount(User user, FeedSubscription subscription) {
UnreadCount uc = null;
HibernateQuery<FeedEntry> query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null);
JPAQuery<FeedEntry> query = buildQuery(user, subscription, true, null, null, -1, -1, null, null, null);
List<Tuple> tuples = query.select(entry.count(), entry.updated.max()).fetch();
for (Tuple tuple : tuples) {
Long count = tuple.get(entry.count());

View File

@@ -16,7 +16,7 @@ import com.commafeed.backend.model.Models;
import com.commafeed.backend.model.QFeedSubscription;
import com.commafeed.backend.model.User;
import com.google.common.collect.Iterables;
import com.querydsl.jpa.hibernate.HibernateQuery;
import com.querydsl.jpa.JPQLQuery;
@Singleton
public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
@@ -44,13 +44,13 @@ public class FeedSubscriptionDAO extends GenericDAO<FeedSubscription> {
}
public List<FeedSubscription> findAll(User user) {
List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetchJoin()
.leftJoin(sub.category).fetchJoin().fetch();
List<FeedSubscription> subs = query().selectFrom(sub).where(sub.user.eq(user)).leftJoin(sub.feed).fetchJoin().leftJoin(sub.category)
.fetchJoin().fetch();
return initRelations(subs);
}
public List<FeedSubscription> findByCategory(User user, FeedCategory category) {
HibernateQuery<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user));
JPQLQuery<FeedSubscription> query = query().selectFrom(sub).where(sub.user.eq(user));
if (category == null) {
query.where(sub.category.isNull());
} else {

View File

@@ -3,22 +3,24 @@ package com.commafeed.backend.dao;
import java.util.Collection;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.QueryHints;
import com.commafeed.backend.model.AbstractModel;
import com.querydsl.jpa.hibernate.HibernateQueryFactory;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import io.dropwizard.hibernate.AbstractDAO;
public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T> {
private HibernateQueryFactory factory;
private JPAQueryFactory factory;
protected GenericDAO(SessionFactory sessionFactory) {
super(sessionFactory);
this.factory = new HibernateQueryFactory(() -> currentSession());
this.factory = new JPAQueryFactory(() -> currentSession());
}
protected HibernateQueryFactory query() {
protected JPAQueryFactory query() {
return factory;
}
@@ -49,4 +51,10 @@ public abstract class GenericDAO<T extends AbstractModel> extends AbstractDAO<T>
return objects.size();
}
protected void setTimeout(JPAQuery<?> query, int timeoutMs) {
if (timeoutMs > 0) {
query.setHint(QueryHints.TIMEOUT_JPA, timeoutMs);
}
}
}

View File

@@ -8,11 +8,11 @@ import javax.persistence.Lob;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
@Entity
@Table(name = "FEEDENTRYCONTENTS")
@SuppressWarnings("serial")
@@ -28,7 +28,7 @@ public class FeedEntryContent extends AbstractModel {
@Lob
@Column(length = Integer.MAX_VALUE)
@Type(type = "org.hibernate.type.StringClobType")
@Type(type = "org.hibernate.type.TextType")
private String content;
@Column(length = 40)

View File

@@ -10,11 +10,11 @@ import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
@Entity
@Table(name = "USERSETTINGS")
@SuppressWarnings("serial")
@@ -61,7 +61,7 @@ public class UserSettings extends AbstractModel {
@Lob
@Column(length = Integer.MAX_VALUE)
@Type(type = "org.hibernate.type.StringClobType")
@Type(type = "org.hibernate.type.TextType")
private String customCss;
@Column(name = "scroll_speed")

View File

@@ -8,18 +8,19 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.model.FeedCategory;
import com.commafeed.backend.model.FeedSubscription;
import com.commafeed.backend.model.User;
import com.google.common.base.MoreObjects;
import com.rometools.opml.feed.opml.Attribute;
import com.rometools.opml.feed.opml.Opml;
import com.rometools.opml.feed.opml.Outline;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(onConstructor = @__({ @Inject }))
@Singleton
public class OPMLExporter {
@@ -35,11 +36,11 @@ public class OPMLExporter {
List<FeedCategory> categories = feedCategoryDAO.findAll(user);
Collections.sort(categories,
(e1, e2) -> MoreObjects.firstNonNull(e1.getPosition(), 0) - MoreObjects.firstNonNull(e2.getPosition(), 0));
(e1, e2) -> ObjectUtils.firstNonNull(e1.getPosition(), 0) - ObjectUtils.firstNonNull(e2.getPosition(), 0));
List<FeedSubscription> subscriptions = feedSubscriptionDAO.findAll(user);
Collections.sort(subscriptions,
(e1, e2) -> MoreObjects.firstNonNull(e1.getPosition(), 0) - MoreObjects.firstNonNull(e2.getPosition(), 0));
(e1, e2) -> ObjectUtils.firstNonNull(e1.getPosition(), 0) - ObjectUtils.firstNonNull(e2.getPosition(), 0));
// export root categories
for (FeedCategory cat : categories.stream().filter(c -> c.getParent() == null).collect(Collectors.toList())) {

View File

@@ -0,0 +1,42 @@
package com.commafeed.frontend.session;
import java.io.File;
import javax.servlet.SessionTrackingMode;
import org.eclipse.jetty.server.session.DefaultSessionCache;
import org.eclipse.jetty.server.session.FileSessionDataStore;
import org.eclipse.jetty.server.session.SessionCache;
import org.eclipse.jetty.server.session.SessionHandler;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.util.Duration;
public class SessionHandlerFactory {
private String path = "sessions";
private Duration cookieRefreshAge = Duration.days(1);
private Duration maxInactiveInterval = Duration.days(30);
private Duration savePeriod = Duration.minutes(5);
public SessionHandler build() {
SessionHandler sessionHandler = new SessionHandler();
SessionCache sessionCache = new DefaultSessionCache(sessionHandler);
sessionHandler.setSessionCache(sessionCache);
FileSessionDataStore dataStore = new FileSessionDataStore();
sessionCache.setSessionDataStore(dataStore);
sessionHandler.setHttpOnly(true);
sessionHandler.setSessionTrackingModes(ImmutableSet.of(SessionTrackingMode.COOKIE));
sessionHandler.setMaxInactiveInterval((int) maxInactiveInterval.toSeconds());
sessionHandler.setRefreshCookieAge((int) cookieRefreshAge.toSeconds());
dataStore.setDeleteUnrestorableFiles(true);
dataStore.setStoreDir(new File(path));
dataStore.setSavePeriodSec((int) savePeriod.toSeconds());
return sessionHandler;
}
}

View File

@@ -1,45 +0,0 @@
package com.commafeed.frontend.session;
import io.dropwizard.util.Duration;
import java.io.File;
import java.io.IOException;
import javax.servlet.SessionTrackingMode;
import lombok.Getter;
import org.eclipse.jetty.server.SessionManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import com.google.common.collect.ImmutableSet;
@Getter
public class SessionManagerFactory {
private String path = "sessions";
private Duration cookieMaxAge = Duration.days(30);
private Duration cookieRefreshAge = Duration.days(1);
private Duration maxInactiveInterval = Duration.days(30);
private Duration idleSavePeriod = Duration.hours(2);
private Duration savePeriod = Duration.minutes(5);
private Duration scavengePeriod = Duration.minutes(5);
public SessionManager build() throws IOException {
HashSessionManager manager = new HashSessionManager();
manager.setSessionTrackingModes(ImmutableSet.of(SessionTrackingMode.COOKIE));
manager.setHttpOnly(true);
manager.getSessionCookieConfig().setHttpOnly(true);
manager.setDeleteUnrestorableSessions(true);
manager.setStoreDirectory(new File(getPath()));
manager.getSessionCookieConfig().setMaxAge((int) cookieMaxAge.toSeconds());
manager.setRefreshCookieAge((int) cookieRefreshAge.toSeconds());
manager.setMaxInactiveInterval((int) maxInactiveInterval.toSeconds());
manager.setIdleSavePeriod((int) idleSavePeriod.toSeconds());
manager.setSavePeriod((int) savePeriod.toSeconds());
manager.setScavengePeriod((int) scavengePeriod.toSeconds());
return manager;
}
}