/*
* Copyright (c) 2010-2012 Lockheed Martin Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* 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.eurekastreams.server.service.restlets;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import net.sf.json.JSONObject;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eurekastreams.commons.actions.context.PrincipalActionContext;
import org.eurekastreams.commons.actions.context.PrincipalPopulator;
import org.eurekastreams.commons.actions.context.service.ServiceActionContext;
import org.eurekastreams.commons.actions.service.ServiceAction;
import org.eurekastreams.commons.server.service.ActionController;
import org.eurekastreams.server.domain.EntityType;
import org.eurekastreams.server.domain.PagedSet;
import org.eurekastreams.server.domain.stream.ActivityDTO;
import org.eurekastreams.server.domain.stream.Stream;
import org.eurekastreams.server.persistence.mappers.FindByIdMapper;
import org.eurekastreams.server.persistence.mappers.requests.FindByIdRequest;
import org.eurekastreams.server.service.restlets.support.RestletQueryRequestParser;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.resource.Representation;
import org.restlet.resource.ResourceException;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndContentImpl;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.feed.synd.SyndFeedImpl;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedOutput;
/**
* REST end point for stream filters.
*
*/
public class StreamXMLResource extends SmpResource
{
/**
* Logger.
*/
private final Log log = LogFactory.getLog(StreamResource.class);
/**
* Action.
*/
private final ServiceAction action;
/**
* Service Action Controller.
*/
private final ActionController serviceActionController;
/**
* Principal populator.
*/
private final PrincipalPopulator principalPopulator;
/**
* Used for testing.
*/
private String pathOverride;
/**
* Good status.
*/
private static final String GOOD_STATUS = "OK";
/**
* Stream find by ID mapper.
*/
private FindByIdMapper<Stream> streamMapper = null;
/**
* The mode of the resource, can be ad-hoc query, or saved stream. "query" is for ad-hoc. "saved" is for saved
* stream.
*/
private String mode;
/**
* The stream Id.
*/
private long streamId;
/** Extracts the query out of the request path. */
private final RestletQueryRequestParser requestParser;
/**
* The base url for eurkea.
*/
private final String baseUrl;
/**
* Default constructor.
*
* @param inAction
* the action.
* @param inServiceActionController
* {@link ActionController} used to execute action.
* @param inPrincipalPopulator
* {@link PrincipalPopulator} used to create principal via open social id.
* @param inStreamMapper
* the stream mapper.
* @param inRequestParser
* Extracts the query out of the request path.
* @param inBaseUrl
* the base url for eureka.
*/
public StreamXMLResource(final ServiceAction inAction, final ActionController inServiceActionController,
final PrincipalPopulator inPrincipalPopulator, final FindByIdMapper<Stream> inStreamMapper,
final RestletQueryRequestParser inRequestParser, final String inBaseUrl)
{
action = inAction;
serviceActionController = inServiceActionController;
principalPopulator = inPrincipalPopulator;
streamMapper = inStreamMapper;
requestParser = inRequestParser;
baseUrl = inBaseUrl;
}
/**
* init the params.
*
* @param request
* the request object.
*/
@Override
protected void initParams(final Request request)
{
mode = (String) request.getAttributes().get("mode");
String streamIdStr = ((String) request.getAttributes().get("streamId"));
if (null != streamIdStr && mode.equals("saved"))
{
streamId = Long.parseLong(streamIdStr);
}
}
/**
* GET the activites.
*
* @param variant
* the variant.
* @return the ATOM.
* @throws ResourceException
* the exception.
*/
@SuppressWarnings("unchecked")
@Override
public Representation represent(final Variant variant) throws ResourceException
{
if (log.isDebugEnabled())
{
log.debug("Path: " + getPath());
}
SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("atom_1.0");
String status = GOOD_STATUS;
try
{
JSONObject queryJson = null;
if (mode.equals("query"))
{
queryJson = requestParser.parseRequest(getPath(), 5);
feed.setTitle("Eureka Activity Stream Feed");
feed.setLink(baseUrl);
feed.setDescription("");
}
else if (mode.equals("saved"))
{
Stream stream = streamMapper.execute(new FindByIdRequest("Stream", streamId));
String feedTitle = ("Eureka Streams: " + stream.getName());
feed.setTitle(feedTitle);
feed.setLink(baseUrl);
feed.setDescription(feedTitle);
if (stream == null)
{
throw new Exception("Unknown saved stream.");
}
queryJson = JSONObject.fromObject(stream.getRequest());
}
else
{
throw new Exception("Unknown request mode.");
}
// add default date sort if sort not specified.
JSONObject query = queryJson.getJSONObject("query");
if (!query.has("sortBy"))
{
query.put("sortBy", "date");
}
log.debug("Making request using: " + queryJson);
PrincipalActionContext ac = new ServiceActionContext(queryJson.toString(),
principalPopulator.getPrincipal(null, null));
PagedSet<ActivityDTO> activities = (PagedSet<ActivityDTO>) serviceActionController.execute(ac, action);
feed.setUri(baseUrl + getRequest().getResourceRef().getPath());
if (activities.getPagedSet().size() > 0)
{
feed.setPublishedDate(activities.getPagedSet().get(0).getPostedTime());
}
else
{
feed.setPublishedDate(new Date());
}
List entries = new ArrayList();
for (ActivityDTO activity : activities.getPagedSet())
{
SyndEntry entry;
SyndContent description;
String title = null;
switch (activity.getBaseObjectType())
{
case BOOKMARK:
title = activity.getActor().getDisplayName() + ": "
+ activity.getBaseObjectProperties().get("content") + " "
+ activity.getBaseObjectProperties().get("targetUrl");
break;
case NOTE:
title = activity.getActor().getDisplayName() + ": "
+ activity.getBaseObjectProperties().get("content");
break;
case PHOTO:
title = activity.getActor().getDisplayName() + ": "
+ activity.getBaseObjectProperties().get("content");
break;
case VIDEO:
title = activity.getActor().getDisplayName() + ": "
+ activity.getBaseObjectProperties().get("content");
break;
default:
break;
}
if (title != null)
{
title = title.replace("%EUREKA:ACTORNAME%", activity.getActor().getDisplayName());
entry = new SyndEntryImpl();
entry.setTitle(title);
entry.setAuthor(activity.getActor().getDisplayName());
String linkPrefix = activity.getDestinationStream().getType() == EntityType.PERSON ? "/#people/"
: "/#groups/";
String link = baseUrl + linkPrefix + activity.getDestinationStream().getUniqueIdentifier()
+ "?activityId=" + activity.getId();
entry.setLink(link);
entry.setUri(link);
entry.setPublishedDate(activity.getPostedTime());
description = new SyndContentImpl();
description.setType("text/plain");
description.setValue(title);
entry.setDescription(description);
entries.add(entry);
}
}
feed.setEntries(entries);
}
catch (Exception ex)
{
// Log the error, but return an empty feed, so don't throw an error.
log.error("Error:" + ex.toString());
}
log.debug(status);
SyndFeedOutput output = new SyndFeedOutput();
StringWriter writer = new StringWriter();
try
{
output.output(feed, writer);
}
catch (IOException e)
{
log.error("error in writing feed");
}
catch (FeedException e)
{
log.error("error in feed");
}
Representation rep = new StringRepresentation(writer.toString(), MediaType.TEXT_XML);
rep.setExpirationDate(new Date(0L));
return rep;
}
/**
* Overrides the request path.
*
* @param inPathOverride
* the string to override the path with.
*/
public void setPathOverride(final String inPathOverride)
{
pathOverride = inPathOverride;
}
/**
* Get the request path.
*
* @return the path.
*/
public String getPath()
{
if (pathOverride == null)
{
return getRequest().getResourceRef().getPath();
}
else
{
return pathOverride;
}
}
}