/* * JBoss, Home of Professional Open Source * Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @authors tag. All rights reserved. */ package org.searchisko.api.rest; import java.net.URI; import java.net.URISyntaxException; import java.util.Date; import java.util.List; import java.util.UUID; import javax.annotation.security.PermitAll; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.indices.IndexMissingException; import org.elasticsearch.search.SearchHit; import org.jboss.resteasy.plugins.providers.atom.Category; import org.jboss.resteasy.plugins.providers.atom.Content; import org.jboss.resteasy.plugins.providers.atom.Entry; import org.jboss.resteasy.plugins.providers.atom.Feed; import org.jboss.resteasy.plugins.providers.atom.Generator; import org.jboss.resteasy.plugins.providers.atom.Link; import org.jboss.resteasy.plugins.providers.atom.Person; import org.searchisko.api.ContentObjectFields; import org.searchisko.api.model.QuerySettings; import org.searchisko.api.model.QuerySettings.Filters; import org.searchisko.api.model.SortByValue; import org.searchisko.api.rest.exception.BadFieldException; import org.searchisko.api.service.ContributorService; import org.searchisko.api.service.SearchService; import org.searchisko.api.service.StatsRecordType; import org.searchisko.api.service.SystemInfoService; import org.searchisko.api.util.QuerySettingsParser; import org.searchisko.api.util.SearchUtils; /** * Feed REST API. * * @author Libor Krzyzanek * @author Vlastimil Elias (velias at redhat dot com) * */ @RequestScoped @Path("/feed") @Produces(MediaType.APPLICATION_ATOM_XML) public class FeedRestService extends RestServiceBase { @Inject protected SystemInfoService systemInfoService; protected static final String REQPARAM_FEED_TITLE = "feed_title"; private static URI TAG_SCHEME_URI = null; static { try { TAG_SCHEME_URI = new URI("searchisko:content:tags"); } catch (URISyntaxException e) { // OK } } @Inject protected SearchService searchService; @Inject protected QuerySettingsParser querySettingsParser; @GET @Path("/") @Produces(MediaType.APPLICATION_ATOM_XML) @PermitAll public Object feed(@Context UriInfo uriInfo) throws URISyntaxException { QuerySettings querySettings = null; try { if (uriInfo == null) { throw new BadFieldException("uriInfo"); } MultivaluedMap<String, String> params = uriInfo.getQueryParameters(); querySettings = querySettingsParser.parseUriParams(params); patchQuerySettings(querySettings); String responseUuid = UUID.randomUUID().toString(); SearchResponse searchResponse = searchService.performSearch(querySettings, responseUuid, StatsRecordType.FEED); return createAtomResponse(querySettings, searchResponse, uriInfo); } catch (IllegalArgumentException e) { throw new BadFieldException("unknown", e); } catch (IndexMissingException e) { return Response.status(Response.Status.NOT_FOUND).build(); } } /** * Patch query settings to search correct values necessary for feeds. * * @param querySettings */ protected void patchQuerySettings(QuerySettings querySettings) { querySettings.clearAggregations(); querySettings.setQueryHighlight(false); querySettings.clearFields(); querySettings.addField(ContentObjectFields.SYS_TITLE); querySettings.addField(ContentObjectFields.SYS_DESCRIPTION); querySettings.addField(ContentObjectFields.SYS_URL_VIEW); querySettings.addField(ContentObjectFields.SYS_CREATED); querySettings.addField(ContentObjectFields.SYS_LAST_ACTIVITY_DATE); querySettings.addField(ContentObjectFields.SYS_CONTENT); querySettings.addField(ContentObjectFields.SYS_CONTENT_CONTENT_TYPE); querySettings.addField(ContentObjectFields.SYS_TAGS); querySettings.addField(ContentObjectFields.SYS_CONTRIBUTORS); if (querySettings.getSortBy() != SortByValue.NEW_CREATION) { querySettings.setSortBy(SortByValue.NEW); } if (querySettings.getFrom() == null) querySettings.setFrom(0); if (querySettings.getSize() == null) querySettings.setSize(20); Filters filters = querySettings.getFiltersInit(); // TODO: no hardcodes, we could probably remove these filters.forgetUrlFilterCandidate("activity_date_from"); filters.forgetUrlFilterCandidate("activity_date_to"); filters.forgetUrlFilterCandidate("activity_date_interval"); } protected Feed createAtomResponse(final QuerySettings querySettings, final SearchResponse searchResponse, final UriInfo uriInfo) throws URISyntaxException { Feed feed = new Feed(); feed.setId(uriInfo.getRequestUri()); String title = SearchUtils.trimToNull(uriInfo.getQueryParameters().getFirst(REQPARAM_FEED_TITLE)); if (title == null) title = constructFeedTitle(querySettings); feed.setTitle(title); Generator generator = new Generator(); generator.setText("Searchisko"); generator.setVersion(systemInfoService.getVersion()); feed.setGenerator(generator); feed.setUpdated(new Date()); if (searchResponse.getHits().getHits() != null) { for (SearchHit hit : searchResponse.getHits().getHits()) { Entry entry = new Entry(); entry.setId(new URI("searchisko:content:id:" + hit.getId())); entry.setPublished(getHitDateFieldValue(hit, ContentObjectFields.SYS_CREATED)); entry.setUpdated(getHitDateFieldValue(hit, ContentObjectFields.SYS_LAST_ACTIVITY_DATE)); entry.setTitle(getHitStringFieldValue(hit, ContentObjectFields.SYS_TITLE)); String url = getHitStringFieldValue(hit, ContentObjectFields.SYS_URL_VIEW); if (url != null) entry.getLinks().add(new Link(null, url)); String contentValue = getHitStringFieldValue(hit, ContentObjectFields.SYS_CONTENT); String descriptionValue = getHitStringFieldValue(hit, ContentObjectFields.SYS_DESCRIPTION); if (contentValue != null) { Content content = new Content(); try { content.setType(MediaType .valueOf(getHitStringFieldValue(hit, ContentObjectFields.SYS_CONTENT_CONTENT_TYPE))); } catch (IllegalArgumentException e) { content.setType(MediaType.TEXT_PLAIN_TYPE); } content.setText(contentValue); entry.setContent(content); if (descriptionValue != null) entry.setSummary(descriptionValue); } else if (descriptionValue != null) { Content content = new Content(); content.setType(MediaType.TEXT_PLAIN_TYPE); content.setText(descriptionValue); entry.setContent(content); } List<Object> contributors = getHitListFieldValue(hit, ContentObjectFields.SYS_CONTRIBUTORS); if (contributors != null) { for (Object contributor : contributors) { Person p = new Person(ContributorService.extractContributorName(contributor.toString())); entry.getAuthors().add(p); } } // ATOM spec requires at least one author if (entry.getAuthors().isEmpty()) { entry.getAuthors().add(new Person("unknown")); } List<Object> tags = getHitListFieldValue(hit, ContentObjectFields.SYS_TAGS); if (tags != null) { for (Object tag : tags) { Category c = new Category(); c.setTerm(tag.toString()); c.setScheme(TAG_SCHEME_URI); entry.getCategories().add(c); } } feed.getEntries().add(entry); } } return feed; } protected String constructFeedTitle(QuerySettings querySettings) { StringBuilder sb = new StringBuilder(); Filters f = querySettings.getFiltersInit(); appendParamIfExists(sb, "project", f.getFilterCandidateValues("project")); appendParamIfExists(sb, "contributor", f.getFilterCandidateValues("contributor")); appendParamIfExists(sb, "tag", f.getFilterCandidateValues("tag")); appendParamIfExists(sb, "sys_type", f.getFilterCandidateValues("sys_type")); appendParamIfExists(sb, "type", f.getFilterCandidateValues("type")); appendParamIfExists(sb, "content_provider", f.getFilterCandidateValues("content_provider")); appendParamIfExists(sb, "query", querySettings.getQuery(), true); if (querySettings.getSortBy() != null && querySettings.getSortBy() != SortByValue.NEW) appendParamIfExists(sb, "sortBy", querySettings.getSortBy().toString()); if (sb.length() == 0) return "Whole feed content"; else return "Feed content for criteria " + sb.toString(); } private void appendParamIfExists(StringBuilder sb, String paramName, String value) { appendParamIfExists(sb, paramName, value, false); } private void appendParamIfExists(StringBuilder sb, String paramName, String value, boolean quote) { if (value != null) { if (sb.length() > 0) sb.append(" and "); sb.append(paramName).append("="); if (quote) sb.append("'"); sb.append(value); if (quote) sb.append("'"); } } private void appendParamIfExists(StringBuilder sb, String paramName, List<?> value) { if (value != null) { appendParamIfExists(sb, paramName, value.toString(), false); } } protected String getHitStringFieldValue(SearchHit hit, String fieldName) { if (hit.getFields().containsKey(fieldName)) return SearchUtils.trimToNull((String) hit.getFields().get(fieldName).getValue()); else return null; } protected List<Object> getHitListFieldValue(SearchHit hit, String fieldName) { if (hit.getFields().containsKey(fieldName)) return hit.getFields().get(fieldName).getValues(); else return null; } protected Date getHitDateFieldValue(SearchHit hit, String fieldName) { if (hit.getFields().containsKey(fieldName)) { Object o = SearchUtils.trimToNull((String) hit.getFields().get(fieldName).getValue()); if (o instanceof Date) { return (Date) o; } if (o != null) { return SearchUtils.dateFromISOString(o.toString(), true); } } return null; } }