/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.core.rest.resources; import java.io.IOException; import java.io.Writer; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.eclipse.skalli.commons.CollectionUtils; import org.eclipse.skalli.commons.HtmlUtils; import org.eclipse.skalli.model.Project; import org.eclipse.skalli.services.Services; import org.eclipse.skalli.services.entity.EntityServices; import org.eclipse.skalli.services.extension.rest.ResourceBase; import org.eclipse.skalli.services.extension.rest.RestUtils; import org.eclipse.skalli.services.feed.Entry; import org.eclipse.skalli.services.feed.FeedService; import org.eclipse.skalli.services.permit.Permits; import org.eclipse.skalli.services.project.ProjectService; import org.jsoup.safety.Whitelist; import org.restlet.data.MediaType; import org.restlet.data.Status; import org.restlet.representation.Representation; import org.restlet.representation.WriterRepresentation; import org.restlet.resource.Get; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; public class TimelineResource extends ResourceBase { private static final Logger LOG = LoggerFactory.getLogger(TimelineResource.class); private static final String RSS_FORMAT = "rss"; //$NON-NLS-1$ private static final String ATOM_FORMAT = "atom"; //$NON-NLS-1$ private static final String DEFAULT_RSS_FORMAT = "rss_2.0"; //$NON-NLS-1$ private static final String DEFAULT_ATOM_FORMAT = "atom_1.0"; //$NON-NLS-1$ private static final int DEFAULT_COUNT = 10; public static final String PARAM_SOURCES = "sources"; //$NON-NLS-1$ public static final String PARAM_FORMAT = "format"; //$NON-NLS-1$ public static final String PARAM_COUNT = "count"; //$NON-NLS-1$ @SuppressWarnings("nls") private static final Set<String> SUPPORTED_FORMATS = CollectionUtils.asSet( "atom_0.3", DEFAULT_ATOM_FORMAT, "rss_0.9", "rss_0.91N", "rss_0.91U", "rss_0.92", "rss_0.93", "rss_0.94", DEFAULT_RSS_FORMAT, "rss_2.0", "atom", "rss" ); @Get public Representation retrieve() { if (!Permits.isAllowed(getAction(), getPath())) { return createUnauthorizedRepresentation(); } String id = (String) getRequestAttributes().get(RestUtils.PARAM_ID); String format = getQueryAttribute(PARAM_FORMAT); if (StringUtils.isBlank(format)) { format = DEFAULT_RSS_FORMAT; } format = format.toLowerCase(Locale.ENGLISH); if (!SUPPORTED_FORMATS.contains(format)) { format = DEFAULT_RSS_FORMAT; } if (RSS_FORMAT.equals(format)) { format = DEFAULT_RSS_FORMAT; } else if (ATOM_FORMAT.equals(format)) { format = DEFAULT_ATOM_FORMAT; } MediaType mediaType = format.startsWith(ATOM_FORMAT)? MediaType.APPLICATION_ATOM : MediaType.APPLICATION_RSS; int count = NumberUtils.toInt(getQueryAttribute(PARAM_COUNT), DEFAULT_COUNT); String[] sources = StringUtils.split(getQueryAttribute(PARAM_SOURCES), ','); ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class)); Project project = projectService.getProject(id); if (project == null) { setStatus(Status.CLIENT_ERROR_NOT_FOUND); return null; } FeedService feedService = Services.getRequiredService(FeedService.class); List<Entry> entries = null; try { entries = feedService.findEntries(project.getUuid(), sources != null? CollectionUtils.asSet(sources) : null, count); } catch (IOException e) { LOG.error(MessageFormat.format("Failed to retrieve timeline entries for {0}", project.getUuid()), e); setStatus(Status.SERVER_ERROR_INTERNAL); return null; } final SyndFeedOutput output = new SyndFeedOutput(); final SyndFeed feed = getFeed(project, getHost(), entries); feed.setFeedType(format); return new WriterRepresentation(mediaType) { @Override public void write(Writer writer) throws IOException { try { output.output(feed, writer); } catch (FeedException e) { LOG.error(MessageFormat.format("Failed to render ''{0}''", feed.getTitle()), e); } } }; } private SyndFeed getFeed(Project project, String host, List<Entry> entries) { SyndFeed feed = new SyndFeedImpl(); String projectName = HtmlUtils.clean(project.getName(), Whitelist.none()); feed.setTitle(MessageFormat.format("{0} | Timeline", projectName)); feed.setDescription(MessageFormat.format( "Latest changes to project ''{0}''.", projectName)); feed.setLink(host + RestUtils.URL_PROJECTS + project.getProjectId() + "/timeline"); //$NON-NLS-1$ List<SyndEntry> feedEntries = new ArrayList<SyndEntry>(); for (Entry entry : entries) { SyndEntry feedEntry = new SyndEntryImpl(); feedEntry.setTitle(MessageFormat.format("{0} | {1}", entry.getTitle(), entry.getSource())); feedEntry.setLink(entry.getLink().getHref()); feedEntry.setPublishedDate(entry.getPublished()); SyndContent entryDescription = new SyndContentImpl(); entryDescription.setType(entry.getContent().getType()); entryDescription.setValue(entry.getContent().getValue()); feedEntry.setDescription(entryDescription); feedEntries.add(feedEntry); } feed.setEntries(feedEntries); return feed; } }