-
Notifications
You must be signed in to change notification settings - Fork 2
Allow custom RSS processing of non-standard feeds #26
base: master
Are you sure you want to change the base?
Changes from all commits
df53f19
fac2e2b
537f46f
6ea8bff
cd734e4
6fa06ee
c472580
4f4e8ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,11 @@ | ||
| package edu.wisc.my.rssToJson.dao; | ||
|
|
||
| import com.rometools.rome.feed.synd.SyndFeed; | ||
| import org.json.JSONObject; | ||
|
|
||
| public interface RssToJsonDao{ | ||
|
|
||
| public JSONObject getXMLFeed(String feedEndpoint); | ||
| public SyndFeed getRssFeed(String feedEndpoint); | ||
| public String getEndpointURL(String feedEndpoint); | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,23 @@ | ||
| package edu.wisc.my.rssToJson.dao; | ||
|
|
||
| import java.io.InputStream; | ||
| import java.io.InputStreamReader; | ||
|
|
||
| import org.apache.http.HttpHeaders; | ||
| import java.io.ByteArrayInputStream; | ||
| import java.io.IOException; | ||
| import org.apache.http.HttpResponse; | ||
| import org.apache.http.client.HttpClient; | ||
| import org.apache.http.client.methods.HttpGet; | ||
| import org.apache.http.impl.client.HttpClientBuilder; | ||
| import org.json.JSONObject; | ||
| import org.json.XML; | ||
| import org.apache.http.HttpEntity; | ||
| import org.apache.http.HttpResponse; | ||
| import org.apache.http.client.ClientProtocolException; | ||
| import org.apache.http.client.ResponseHandler; | ||
| import org.apache.http.client.methods.HttpGet; | ||
| import org.apache.http.impl.client.CloseableHttpClient; | ||
| import org.apache.http.impl.client.HttpClients; | ||
| import org.apache.http.util.EntityUtils; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
|
|
@@ -31,36 +42,81 @@ public class RssToJsonDaoImpl implements RssToJsonDao{ | |
| @Autowired | ||
| void setEnv(Environment env) { | ||
| this.env = env; | ||
| } | ||
| private String httpResponseAsString(String url) throws IOException { | ||
| logger.error("HTTP Response method " + url); | ||
| CloseableHttpClient httpclient = HttpClients.createDefault(); | ||
| try { | ||
| HttpGet httpget = new HttpGet(url); | ||
| logger.error(httpget.toString()); | ||
|
|
||
| // Create a custom response handler | ||
| ResponseHandler<String> responseHandler = new ResponseHandler<String>() { | ||
|
|
||
| @Override | ||
| public String handleResponse( | ||
| final HttpResponse response) throws ClientProtocolException, IOException { | ||
| logger.error("TWO " + response.toString()); | ||
| int status = response.getStatusLine().getStatusCode(); | ||
| logger.debug(status + " response code "); | ||
| if (status >= 200 && status < 300) { | ||
| HttpEntity entity = response.getEntity(); | ||
| return entity != null ? EntityUtils.toString(entity) : null; | ||
| } else { | ||
| throw new ClientProtocolException("Unexpected response status: " + status); | ||
| } | ||
| } | ||
| }; | ||
| String responseBody = httpclient.execute(httpget, responseHandler); | ||
| logger.error("ONE POINT SIX " + responseBody); | ||
| return responseBody; | ||
| } finally { | ||
| httpclient.close(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| @Override | ||
| public JSONObject getXMLFeed(String feedEndpoint) { | ||
| String endpointURL = getEndpointURL(feedEndpoint); | ||
| JSONObject jsonObject = null; | ||
| try { | ||
| String xmlString = httpResponseAsString(endpointURL); | ||
| jsonObject = XML.toJSONObject(xmlString); | ||
| } catch (Exception e) { | ||
| logger.warn(e.getMessage()); | ||
| return null; | ||
| } | ||
| return jsonObject; | ||
| } | ||
|
|
||
| public String getEndpointURL(String feed) { | ||
| logger.error("GETTING THE ENDPOINT FOR " + feed); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of these logging levels are too high, it looks like these should be debug logs, not error |
||
| String endpointURL = env.getProperty(feed); | ||
| logger.error(endpointURL); | ||
| if (endpointURL == null) { | ||
| logger.warn("No corresponding feed url for requested endpoint {}", feed); | ||
| return null; | ||
| } | ||
| return endpointURL; | ||
| } | ||
|
|
||
| @Override | ||
| @Cacheable(cacheNames="feeds", sync=true) | ||
| public SyndFeed getRssFeed(String feedEndpoint) { | ||
| logger.info("Fetching feed for {} ", feedEndpoint); | ||
| //see if property file has corresponding url for requested endpoint | ||
| String endpointURL = env.getProperty(feedEndpoint); | ||
| if (endpointURL == null){ | ||
| logger.warn("No corresponding feed url for requested endpoint {}", | ||
| feedEndpoint); | ||
| return null; | ||
| } | ||
| SyndFeed feed = null; | ||
| try{ | ||
| HttpClient client = HttpClientBuilder.create().build(); | ||
| HttpGet request = new HttpGet(endpointURL); | ||
| request.setHeader(HttpHeaders.USER_AGENT, "rss-to-json service"); | ||
| request.setHeader(HttpHeaders.CONTENT_ENCODING, "UTF-8"); | ||
| HttpResponse response = client.execute(request); | ||
| SyndFeedInput input = new SyndFeedInput(); | ||
| feed = input.build(new InputStreamReader(response.getEntity().getContent(), "UTF-8")); | ||
| feed.setFeedType("UTF-8"); | ||
| logger.debug("CONTENT OF FEED " + endpointURL); | ||
| logger.debug(feed.toString()); | ||
|
|
||
| }catch(Exception ex){ | ||
| logger.error("Error while fetching xml from {}", endpointURL, ex); | ||
| } | ||
| try{ | ||
| String result = httpResponseAsString(feedEndpoint); | ||
| SyndFeedInput input = new SyndFeedInput(); | ||
| InputStream stream = new ByteArrayInputStream(result.getBytes("UTF-8")); | ||
| SyndFeed feed = input.build(new InputStreamReader(stream, "UTF-8")); | ||
| logger.debug("CONTENT OF FEED " + feedEndpoint); | ||
| logger.debug(feed.toString()); | ||
| return feed; | ||
| } catch (Exception e) { | ||
| logger.warn("Could not get feed " + feedEndpoint + " " + e.getMessage()); | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package edu.wisc.my.rssToJson.filter; | ||
| import java.util.Iterator; | ||
| import org.json.JSONArray; | ||
| import org.json.JSONObject; | ||
| import java.util.ArrayList; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import com.fasterxml.jackson.core.JsonParser; | ||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| public class WudFilter implements iFilter{ | ||
| protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
| public WudFilter(){ | ||
| } | ||
| public JSONObject getFilteredJSON(JSONObject rawJSON){ | ||
| ObjectMapper om = new ObjectMapper(); | ||
| JSONObject feedInfo = new JSONObject(); | ||
| JSONObject feed = new JSONObject(); | ||
| JSONArray items = new JSONArray(); | ||
| try{ | ||
| JsonNode rootNode = om.readTree(rawJSON.toString()); | ||
| feedInfo.put("title", rootNode.findValue("title").asText()); | ||
| feedInfo.put("link", "https://union.wisc.edu/events-and-activities/event-calendar/"); | ||
| feedInfo.put("description", rootNode.findValue("description").asText()); | ||
| feedInfo.put("pubDate", rootNode.findValue("lastBuildDate").asText()); | ||
|
|
||
|
|
||
| JsonNode events = rootNode.findValue("event"); | ||
|
|
||
| Iterator<JsonNode> iter = events.elements(); | ||
|
|
||
| while(iter.hasNext()){ | ||
| JSONObject item = new JSONObject(); | ||
| JsonNode anEvent = iter.next(); | ||
| item.put("title", anEvent.findValue("event_title").asText()); | ||
| item.put("link", anEvent.findValue("url").asText()); | ||
| item.put("description",anEvent.findValue("short_description").asText()); | ||
| items.put(item); | ||
| } | ||
| feed.put("feed", feedInfo); | ||
| feed.put("items",items); | ||
|
|
||
|
|
||
| }catch(Exception e){ | ||
| logger.error(e.getMessage()); | ||
| }; | ||
| feed.put("status", "ok"); | ||
| return feed; | ||
| } | ||
| public String healthCheck(){ | ||
| return "WudFilter health check"; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package edu.wisc.my.rssToJson.filter; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| public class XmlFilter { | ||
| protected final Logger logger = LoggerFactory.getLogger(getClass()); | ||
| public static iFilter getXmlFilter(String filterName){ | ||
| try{ | ||
| String filterClass = XmlFilter.toTitleCase(filterName) + "Filter"; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting loader here. I'm hesitant about passing user input (a piece of the path from a incoming request) directly into a function that loads arbitrary classes... But as the code is structured right now, the string wouldn't get to this line without already being a key in A way I'd feel more comfortable with would be to somehow get Spring to handle the class loading, or if we absolutely had to do it manually, get all the possible Filter class handles at startup, and load into a map with approved keys.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might be feasible to ask the Spring ApplicationContext for a named bean, if we want to register and dereference filter singletons that way. |
||
| String pkg = new CurrentClassGetter().getPackageName(); | ||
| iFilter filter = (iFilter) Class.forName(pkg + "." +filterClass).newInstance(); | ||
| return filter; | ||
| } catch (Exception e){ | ||
| return null; | ||
| } | ||
| } | ||
| public static class CurrentClassGetter extends SecurityManager { | ||
| public String getPackageName() { | ||
| return getClassContext()[1].getPackage().getName(); | ||
| } | ||
| } | ||
| private static String toTitleCase(String filterNameIn){ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neat, but it only handles a narrow case of one word keys? (like I don't think it would handle Maybe want to pull in commons-text for this? https://commons.apache.org/proper/commons-text/apidocs/org/apache/commons/text/CaseUtils.html#toCamelCase(java.lang.String,boolean,char...) |
||
| StringBuilder titleCase = new StringBuilder(); | ||
| boolean nextTitleCase = true; | ||
| for (char c : filterNameIn.toCharArray()) { | ||
| if (nextTitleCase) { | ||
| c = Character.toTitleCase(c); | ||
| nextTitleCase = false; | ||
| } | ||
|
|
||
| titleCase.append(c); | ||
| } | ||
| String filterNameOut = titleCase.toString().trim(); | ||
| return filterNameOut; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package edu.wisc.my.rssToJson.filter; | ||
| import org.json.JSONObject; | ||
| public interface iFilter{ | ||
| public JSONObject getFilteredJSON(JSONObject rawJSON); | ||
| public String healthCheck(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe don't need these numbered logs after development