/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.streams.rss.serializer; import org.apache.streams.data.ActivitySerializer; import org.apache.streams.data.util.RFC3339Utils; import org.apache.streams.jackson.StreamsJacksonMapper; import org.apache.streams.pojo.json.Activity; import org.apache.streams.pojo.json.ActivityObject; import org.apache.streams.pojo.json.Author; import org.apache.streams.pojo.json.Provider; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.LinkedList; import java.util.List; import java.util.Objects; public class SyndEntryActivitySerializer implements ActivitySerializer<ObjectNode> { private static final Logger LOGGER = LoggerFactory.getLogger(SyndEntryActivitySerializer.class); private boolean includeRomeExtension; public SyndEntryActivitySerializer() { this(true); } public SyndEntryActivitySerializer(boolean includeRomeExtension) { this.includeRomeExtension = includeRomeExtension; } @Override public List<Activity> deserializeAll(List<ObjectNode> objectNodes) { List<Activity> result = new LinkedList<>(); for (ObjectNode node : objectNodes) { result.add(deserialize(node)); } return result; } @Override public String serializationFormat() { return "application/streams-provider-rss"; } @Override public ObjectNode serialize(Activity deserialized) { throw new UnsupportedOperationException("Cannot currently serialize to Rome"); } @Override public Activity deserialize(ObjectNode syndEntry) { return deserializeWithRomeExtension(syndEntry, this.includeRomeExtension); } /** * deserializeWithRomeExtension ObjectNode entry withExtension. * @param entry ObjectNode * @param withExtension whether to add Rome Extension * @return Activity */ public Activity deserializeWithRomeExtension(ObjectNode entry, boolean withExtension) { Objects.requireNonNull(entry); Activity activity = new Activity(); Provider provider = buildProvider(entry); ActivityObject actor = buildActor(entry); ActivityObject activityObject = buildActivityObject(entry); activityObject.setUrl(provider.getUrl()); activityObject.setAuthor(actor.getAuthor()); activity.setUrl(provider.getUrl()); activity.setProvider(provider); activity.setActor(actor); activity.setVerb("post"); activity.setId("id:rss:post:" + activity.getUrl()); JsonNode published = entry.get("publishedDate"); if (published != null) { try { activity.setPublished(RFC3339Utils.parseToUTC(published.textValue())); } catch (Exception ex) { LOGGER.warn("Failed to parse date : {}", published.textValue()); DateTime now = DateTime.now().withZone(DateTimeZone.UTC); activity.setPublished(now); } } activity.setUpdated(activityObject.getUpdated()); activity.setObject(activityObject); if (withExtension) { activity = addRomeExtension(activity, entry); } return activity; } /** * Given an RSS entry, extra out the author and actor information and return it * in an actor object * * @param entry entry * @return $.actor */ private ActivityObject buildActor(ObjectNode entry) { ActivityObject actor = new ActivityObject(); Author author = new Author(); if (entry.get("author") != null) { author.setId(entry.get("author").textValue()); author.setDisplayName(entry.get("author").textValue()); actor.setAuthor(author); String uriToSet = entry.get("rssFeed") != null ? entry.get("rssFeed").asText() : null; actor.setId("id:rss:" + uriToSet + ":" + author.getId()); actor.setDisplayName(author.getDisplayName()); } return actor; } /** * Given an RSS object, build the ActivityObject. * * @param entry ObjectNode * @return $.object */ private ActivityObject buildActivityObject(ObjectNode entry) { ActivityObject activityObject = new ActivityObject(); JsonNode summary = entry.get("description"); if (summary != null) { activityObject.setSummary(summary.textValue()); } else if ((summary = entry.get("title")) != null) { activityObject.setSummary(summary.textValue()); } return activityObject; } /** * Given an RSS object, build and return the Provider object. * * @param entry ObjectNode * @return $.provider */ private Provider buildProvider(ObjectNode entry) { Provider provider = new Provider(); String link = null; String uri = null; String resourceLocation = null; if (entry.get("link") != null) { link = entry.get("link").textValue(); } if (entry.get("uri") != null) { uri = entry.get("uri").textValue(); } /* * Order of precedence for resourceLocation selection * * 1. Valid URI * 2. Valid Link * 3. Non-null URI * 4. Non-null Link */ if (isValidResource(uri)) { resourceLocation = uri; } else if (isValidResource(link)) { resourceLocation = link; } else if (uri != null || link != null) { resourceLocation = (uri != null) ? uri : link; } provider.setId("id:providers:rss"); provider.setUrl(resourceLocation); provider.setDisplayName("RSS"); return provider; } /** * Tests whether or not the passed in resource is a valid URI. * @param resource resource * @return boolean of whether or not the resource is valid */ private boolean isValidResource(String resource) { return resource != null && (resource.startsWith("http") || resource.startsWith("www")); } /** * Given an RSS object and an existing activity, * add the Rome extension to that activity and return it. * * @param activity Activity * @param entry ObjectNode * @return Activity */ private Activity addRomeExtension(Activity activity, ObjectNode entry) { ObjectMapper mapper = StreamsJacksonMapper.getInstance(); ObjectNode activityRoot = mapper.convertValue(activity, ObjectNode.class); ObjectNode extensions = JsonNodeFactory.instance.objectNode(); extensions.put("rome", entry); activityRoot.put("extensions", extensions); activity = mapper.convertValue(activityRoot, Activity.class); return activity; } }