Java tutorial
/** * Licensed to the Sakai Foundation (SF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The SF licenses this file * to you 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.sakaiproject.nakamura.files.pool; import static org.sakaiproject.nakamura.api.files.FilesConstants.POOLED_CONTENT_PUBLIC_RELATED_SELECTOR; import static org.sakaiproject.nakamura.api.files.FilesConstants.POOLED_CONTENT_RELATED_SELECTOR; import static org.sakaiproject.nakamura.api.files.FilesConstants.POOLED_CONTENT_RT; import static org.sakaiproject.nakamura.api.files.FilesConstants.SAKAI_TAGS; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.sling.SlingServlet; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.servlets.SlingSafeMethodsServlet; import org.apache.sling.commons.json.JSONException; import org.apache.solr.client.solrj.util.ClientUtils; import org.sakaiproject.nakamura.api.doc.BindingType; import org.sakaiproject.nakamura.api.doc.ServiceBinding; import org.sakaiproject.nakamura.api.doc.ServiceDocumentation; import org.sakaiproject.nakamura.api.doc.ServiceExtension; import org.sakaiproject.nakamura.api.doc.ServiceMethod; import org.sakaiproject.nakamura.api.doc.ServiceResponse; import org.sakaiproject.nakamura.api.doc.ServiceSelector; import org.sakaiproject.nakamura.api.lite.StorageClientException; import org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException; import org.sakaiproject.nakamura.api.lite.content.Content; import org.sakaiproject.nakamura.api.lite.content.ContentManager; import org.sakaiproject.nakamura.api.search.solr.Query; import org.sakaiproject.nakamura.api.search.solr.Result; import org.sakaiproject.nakamura.api.search.solr.SolrSearchException; import org.sakaiproject.nakamura.api.search.solr.SolrSearchResultSet; import org.sakaiproject.nakamura.api.search.solr.SolrSearchServiceFactory; import org.sakaiproject.nakamura.util.ExtendedJSONWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; /** * Returns up to 10 items related to the current content node. There are two versions of * the feed: one which contains items available to to any logged-in user (with a * "sakai:permissions" property of "everyone"), and one which contains only publicly * accessible items (with a "sakai:permissions" property of "public"). */ @ServiceDocumentation(name = "GetRelatedContentServlet", okForVersion = "1.1", shortDescription = "Get up to ten related nodes", description = { "This servlet returns an array of content related to the targeted node.", "Currently, relatedness is determined by the number of shared tags.", "<pre>curl http://localhost:8080/p/hHnm6yipCo.related.tidy.json</pre>", "<pre>[{\n \"_lastModifiedBy\": \"suzy\",\n \"_previousBlockId\": \"UbGXYKGfEeCAXdkUrBABAw+\",\n \"_previousVersion\": \"UbCF8KGfEeCAXdkUrBABAw+\",\n \"sakai:fileextension\": \".png\",\n \"_path\": \"hESoXumAT\",\n \"_blockId\": \"UbGXYKGfEeCAXdkUrBABAw+\",\n \"sakai:allowcomments\": \"true\",\n \"sakai:pooled-content-viewer\": [\"anonymous\", \"everyone\"],\n \"sakai:pool-content-created-for\": \"suzy\",\n \"_bodyCreatedBy\": \"admin\",\n \"_id\": \"UchTsaGfEeCAXdkUrBABAw+\",\n \"sakai:pooled-content-file-name\": \"hero-zach-unmasked.png\",\n \"_bodyCreated\": 1309276646363,\n \"sakai:copyright\": \"creativecommons\",\n \"_length\": 25606,\n \"sakai:needsprocessing\": \"true\",\n \"sakai:permissions\": \"public\",\n \"_mimeType\": \"image/png\",\n \"_bodyLastModifiedBy\": \"admin\",\n \"_createdBy\": \"admin\",\n \"sakai:tag\": [\"great tag\"],\n \"_versionHistoryId\": \"UchTsKGfEeCAXdkUrBABAw+\",\n \"sakai:tags\": [\"great\"],\n \"sakai:showcomments\": \"true\",\n \"sling:resourceType\": \"sakai/pooled-content\",\n \"sakai:pooled-content-manager\": [\"suzy\"],\n \"_created\": 1309276646351,\n \"_bodyLastModified\": 1309276646363,\n \"_lastModified\": 1309286723834,\n \"_bodyLocation\": \"2011/5/-V/7P/mM/-V7PmMdM-QDHyHslMftAMF21H4s\"\n}]</pre>" }, bindings = { @ServiceBinding(type = BindingType.TYPE, bindings = { POOLED_CONTENT_RT }, extensions = @ServiceExtension(name = "json", description = "This servlet outputs JSON data."), selectors = { @ServiceSelector(name = POOLED_CONTENT_RELATED_SELECTOR, description = "Will retrieve related content with an access scheme of 'everyone'."), @ServiceSelector(name = POOLED_CONTENT_PUBLIC_RELATED_SELECTOR, description = "Will retrieve related content with an access scheme of 'public'."), @ServiceSelector(name = "tidy", description = "Optional sub-selector. Will send back 'tidy' output.") }) }, methods = { @ServiceMethod(name = "GET", parameters = {}, description = { "This servlet only responds to GET requests." }, response = { @ServiceResponse(code = 200, description = "Successful request, json can be found in the body"), @ServiceResponse(code = 500, description = "Failure to retrieve tags or files, an explanation can be found in the HTMl.") }) }) @SlingServlet(methods = { "GET" }, extensions = { "json" }, resourceTypes = { POOLED_CONTENT_RT }, selectors = { POOLED_CONTENT_RELATED_SELECTOR, POOLED_CONTENT_PUBLIC_RELATED_SELECTOR }) public class GetRelatedContentServlet extends SlingSafeMethodsServlet { private static final long serialVersionUID = -1262781431579462713L; private static final Logger LOGGER = LoggerFactory.getLogger(GetRelatedContentServlet.class); public static final int MAX_RESULTS = 10; @Reference protected SolrSearchServiceFactory solrSearchServiceFactory; @Override protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { LOGGER.info("GETTING RELATED CONTENT ================================================ "); // Query should look like: // //element(*, // sakai:pooled-content)[(@sakai:tag-uuid='6c589c99-4a08-4a51-8f09-2960b36bec6f' // or @sakai:tag-uuid='506edc80-ad50-4bb3-abe8-aa5c72e65888') and // (@sakai:permissions='public' // or @sakai:permissions='everyone')] order by @jcr:score descending final String sakaiPooledContent = ClientUtils.escapeQueryChars("sakai/pooled-content"); StringBuilder sb = new StringBuilder("resourceType:"); sb.append(sakaiPooledContent); sb.append(" AND "); Set<String> selectors = ImmutableSet.copyOf(request.getRequestPathInfo().getSelectors()); boolean publicSearch = selectors.contains(POOLED_CONTENT_PUBLIC_RELATED_SELECTOR); // Collect tags to search against. Resource resource = request.getResource(); Content content = resource.adaptTo(Content.class); ContentManager contentManager = resource.adaptTo(ContentManager.class); try { ExtendedJSONWriter writer = new ExtendedJSONWriter(response.getWriter()); writer.setTidy(selectors.contains("tidy")); writer.array(); if (content.hasProperty(SAKAI_TAGS)) { String nodePath = content.getPath(); Map<String, Object> properties = content.getProperties(); Set<String> tags = Sets.newHashSet(); if (properties.containsKey(SAKAI_TAGS)) { String[] contentTags = (String[]) properties.get(SAKAI_TAGS); for (String contentTag : contentTags) { tags.add(ClientUtils.escapeQueryChars(contentTag)); } } if (tags.size() > 0) { sb.append("tag:(").append(StringUtils.join(tags, " OR ")).append(")"); } Query query = new Query(sb.toString()); LOGGER.info("Submitting Query {} ", query); SolrSearchResultSet resultSet = solrSearchServiceFactory.getSearchResultSet(request, query, publicSearch); Iterator<Result> iterator = resultSet.getResultSetIterator(); int count = 0; while ((count < MAX_RESULTS) && iterator.hasNext()) { Result result = iterator.next(); String path = result.getPath(); if (!nodePath.equals(path)) { try { Content contentResult = contentManager.get(path); writer.object(); ExtendedJSONWriter.writeNodeContentsToWriter(writer, contentResult); writer.endObject(); count++; } catch (StorageClientException e) { LOGGER.error("Error getting related content for " + request.getPathTranslated(), e); } catch (AccessDeniedException e) { LOGGER.info("Denied access for related content to " + request.getPathTranslated(), e); } } } } else { LOGGER.info("No Tags in {} ", content.getProperties()); } writer.endArray(); } catch (JSONException e) { LOGGER.error("Error writing related content for " + request.getPathTranslated(), e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } catch (SolrSearchException e) { LOGGER.error("Error writing related content for " + request.getPathTranslated(), e); response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage()); } } }