cannot loop forever

This commit is contained in:
Athou
2014-11-07 08:46:09 +01:00
parent eea0c24d2b
commit a94ef980bb
4 changed files with 55 additions and 15 deletions

View File

@@ -1,12 +1,20 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext; import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine; import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.JexlException;
import org.apache.commons.jexl2.JexlInfo; import org.apache.commons.jexl2.JexlInfo;
import org.apache.commons.jexl2.MapContext; import org.apache.commons.jexl2.MapContext;
import org.apache.commons.jexl2.Script;
import org.apache.commons.jexl2.introspection.JexlMethod; import org.apache.commons.jexl2.introspection.JexlMethod;
import org.apache.commons.jexl2.introspection.JexlPropertyGet; import org.apache.commons.jexl2.introspection.JexlPropertyGet;
import org.apache.commons.jexl2.introspection.Uberspect; import org.apache.commons.jexl2.introspection.Uberspect;
@@ -63,12 +71,17 @@ public class FeedEntryFilter {
private final String filter; private final String filter;
public boolean matchesEntry(FeedEntry entry) { public boolean matchesEntry(FeedEntry entry) throws FeedEntryFilterException {
if (StringUtils.isBlank(filter)) { if (StringUtils.isBlank(filter)) {
return true; return true;
} }
Expression expression = ENGINE.createExpression(filter); Script script = null;
try {
script = ENGINE.createScript(filter);
} catch (JexlException e) {
throw new FeedEntryFilterException("Exception while parsing expression " + filter, e);
}
JexlContext context = new MapContext(); JexlContext context = new MapContext();
context.set("title", Jsoup.parse(entry.getContent().getTitle()).text().toLowerCase()); context.set("title", Jsoup.parse(entry.getContent().getTitle()).text().toLowerCase());
@@ -76,6 +89,25 @@ public class FeedEntryFilter {
context.set("content", Jsoup.parse(entry.getContent().getContent()).text().toLowerCase()); context.set("content", Jsoup.parse(entry.getContent().getContent()).text().toLowerCase());
context.set("url", entry.getUrl().toLowerCase()); context.set("url", entry.getUrl().toLowerCase());
return (boolean) expression.evaluate(context); Callable<Object> callable = script.callable(context);
Future<Object> future = Executors.newFixedThreadPool(1).submit(callable);
Object result = null;
try {
result = future.get(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new FeedEntryFilterException("interrupted while evaluating expression " + filter, e);
} catch (ExecutionException e) {
throw new FeedEntryFilterException("Exception while evaluating expression " + filter, e);
} catch (TimeoutException e) {
throw new FeedEntryFilterException("Took too long evaluating expression " + filter, e);
}
return (boolean) result;
}
@SuppressWarnings("serial")
public static class FeedEntryFilterException extends Exception {
public FeedEntryFilterException(String message, Throwable t) {
super(message, t);
}
} }
} }

View File

@@ -14,6 +14,7 @@ import org.apache.commons.codec.digest.DigestUtils;
import com.commafeed.backend.dao.FeedEntryDAO; import com.commafeed.backend.dao.FeedEntryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO; import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.feed.FeedEntryFilter; import com.commafeed.backend.feed.FeedEntryFilter;
import com.commafeed.backend.feed.FeedEntryFilter.FeedEntryFilterException;
import com.commafeed.backend.model.Feed; import com.commafeed.backend.model.Feed;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
@@ -52,7 +53,7 @@ public class FeedUpdateService {
boolean matches = true; boolean matches = true;
try { try {
matches = filter.matchesEntry(entry); matches = filter.matchesEntry(entry);
} catch (Exception e) { } catch (FeedEntryFilterException e) {
log.error("could not evaluate filter {}", sub.getFilter(), e); log.error("could not evaluate filter {}", sub.getFilter(), e);
} }
if (!matches) { if (!matches) {

View File

@@ -45,6 +45,7 @@ import com.commafeed.backend.dao.FeedCategoryDAO;
import com.commafeed.backend.dao.FeedEntryStatusDAO; import com.commafeed.backend.dao.FeedEntryStatusDAO;
import com.commafeed.backend.dao.FeedSubscriptionDAO; import com.commafeed.backend.dao.FeedSubscriptionDAO;
import com.commafeed.backend.feed.FeedEntryFilter; import com.commafeed.backend.feed.FeedEntryFilter;
import com.commafeed.backend.feed.FeedEntryFilter.FeedEntryFilterException;
import com.commafeed.backend.feed.FeedFetcher; import com.commafeed.backend.feed.FeedFetcher;
import com.commafeed.backend.feed.FeedQueues; import com.commafeed.backend.feed.FeedQueues;
import com.commafeed.backend.feed.FeedUtils; import com.commafeed.backend.feed.FeedUtils;
@@ -437,7 +438,7 @@ public class FeedREST {
try { try {
new FeedEntryFilter(req.getFilter()).matchesEntry(TEST_ENTRY); new FeedEntryFilter(req.getFilter()).matchesEntry(TEST_ENTRY);
} catch (Exception e) { } catch (FeedEntryFilterException e) {
Throwable root = Throwables.getRootCause(e); Throwable root = Throwables.getRootCause(e);
return Response.status(Status.BAD_REQUEST).entity(root.getMessage()).build(); return Response.status(Status.BAD_REQUEST).entity(root.getMessage()).build();
} }

View File

@@ -1,10 +1,10 @@
package com.commafeed.backend.feed; package com.commafeed.backend.feed;
import org.apache.commons.jexl2.JexlException;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import com.commafeed.backend.feed.FeedEntryFilter.FeedEntryFilterException;
import com.commafeed.backend.model.FeedEntry; import com.commafeed.backend.model.FeedEntry;
import com.commafeed.backend.model.FeedEntryContent; import com.commafeed.backend.model.FeedEntryContent;
@@ -27,39 +27,45 @@ public class FeedEntryFilterTest {
} }
@Test @Test
public void emptyFilterMatchesFilter() { public void emptyFilterMatchesFilter() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter(null); FeedEntryFilter filter = new FeedEntryFilter(null);
Assert.assertTrue(filter.matchesEntry(entry)); Assert.assertTrue(filter.matchesEntry(entry));
} }
@Test @Test
public void blankFilterMatchesFilter() { public void blankFilterMatchesFilter() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter(""); FeedEntryFilter filter = new FeedEntryFilter("");
Assert.assertTrue(filter.matchesEntry(entry)); Assert.assertTrue(filter.matchesEntry(entry));
} }
@Test @Test
public void simpleExpression() { public void simpleExpression() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter("author eq 'athou'"); FeedEntryFilter filter = new FeedEntryFilter("author eq 'athou'");
Assert.assertTrue(filter.matchesEntry(entry)); Assert.assertTrue(filter.matchesEntry(entry));
} }
@Test(expected = JexlException.class) @Test(expected = FeedEntryFilterException.class)
public void newIsDisabled() { public void newIsDisabled() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter("null eq new ('java.lang.String', 'athou')"); FeedEntryFilter filter = new FeedEntryFilter("null eq new ('java.lang.String', 'athou')");
filter.matchesEntry(entry); filter.matchesEntry(entry);
} }
@Test(expected = JexlException.class) @Test(expected = FeedEntryFilterException.class)
public void getClassMethodIsDisabled() { public void getClassMethodIsDisabled() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter("null eq ''.getClass()"); FeedEntryFilter filter = new FeedEntryFilter("null eq ''.getClass()");
filter.matchesEntry(entry); filter.matchesEntry(entry);
} }
@Test @Test
public void dotClassIsDisabled() { public void dotClassIsDisabled() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter("null eq ''.class"); FeedEntryFilter filter = new FeedEntryFilter("null eq ''.class");
Assert.assertTrue(filter.matchesEntry(entry)); Assert.assertTrue(filter.matchesEntry(entry));
} }
@Test(expected = FeedEntryFilterException.class)
public void cannotLoopForever() throws FeedEntryFilterException {
FeedEntryFilter filter = new FeedEntryFilter("while(true) {}");
filter.matchesEntry(entry);
}
} }