Пример #1
0
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.sakaiproject.nakamura.api.solr.IndexingHandler#getDocuments(org.sakaiproject.nakamura.api.solr.RepositorySession,
   *     org.osgi.service.event.Event)
   */
  @Override
  public Collection<SolrInputDocument> getDocuments(RepositorySession repoSession, Event event) {
    LOGGER.debug("getDocuments for {}", event);
    String path = (String) event.getProperty(IndexingHandler.FIELD_PATH);

    List<SolrInputDocument> documents = Lists.newArrayList();
    if (!StringUtils.isBlank(path)) {
      Session session = repoSession.adaptTo(Session.class);
      try {
        ContentManager cm = session.getContentManager();
        Content content = cm.get(path);
        SolrInputDocument doc = new SolrInputDocument();
        for (Entry<String, String> prop : PROPERTIES.entrySet()) {
          String key = prop.getKey();
          Object value = content.getProperty(key);
          if (value != null) {
            doc.addField(PROPERTIES.get(key), value);
          }
        }
        doc.setField(_DOC_SOURCE_OBJECT, content);
        documents.add(doc);
      } catch (StorageClientException e) {
        LOGGER.error(e.getMessage(), e);
      } catch (AccessDeniedException e) {
        LOGGER.error(e.getMessage(), e);
      }
    }
    return documents;
  }
  public void writeResults(
      SlingHttpServletRequest request, JSONWriter write, Iterator<Result> results)
      throws JSONException {
    ExtendedJSONWriter exWriter = (ExtendedJSONWriter) write;
    Session session =
        StorageClientUtils.adaptToSession(
            request.getResourceResolver().adaptTo(javax.jcr.Session.class));

    String currUser = request.getRemoteUser();
    try {
      // write out the profile information for each result
      while (results.hasNext()) {
        Result result = results.next();
        // start the object here so we can decorate with contact details
        write.object();
        super.writeResult(request, write, result, true);

        // add contact information if appropriate
        String otherUser = String.valueOf(result.getFirstValue("path"));
        connMgr.writeConnectionInfo(exWriter, session, currUser, otherUser);

        write.endObject();
      }
    } catch (StorageClientException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (AccessDeniedException e) {
      LOGGER.error(e.getMessage(), e);
    }
  }
 /**
  * {@inheritDoc}
  *
  * @see
  *     org.sakaiproject.nakamura.api.personal.PersonalTrackingStore#recordActivity(java.lang.String,
  *     java.lang.String, java.lang.String, java.lang.String, java.util.Date)
  */
 public void recordActivity(
     String resourceId,
     String resourceType,
     String activityType,
     String userId,
     Calendar timestamp) {
   Session session = null;
   try {
     session = repository.loginAdministrative();
     final ContentManager cm = session.getContentManager();
     final String trackingNodePath = "/activity/" + resourceType + "/" + resourceId;
     Content trackingNode = null;
     if (cm.exists(trackingNodePath)) {
       trackingNode = cm.get(trackingNodePath);
     } else {
       trackingNode = new Content(trackingNodePath, new HashMap<String, Object>());
     }
     if (!trackingNode.hasProperty("count")) {
       trackingNode.setProperty("count", BigDecimal.ZERO);
     }
     if (!trackingNode.hasProperty("sling:resourceType")) {
       trackingNode.setProperty("sling:resourceType", "sakai/resource-activity");
     }
     final String generatedNodeName =
         Base64.encodeBase64URLSafeString(asShorterByteArray(UUID.randomUUID()));
     final String activityNodePath = trackingNodePath + "/" + generatedNodeName;
     Content activityNode = null;
     if (cm.exists(activityNodePath)) {
       activityNode = cm.get(activityNodePath);
     } else {
       activityNode = new Content(activityNodePath, new HashMap<String, Object>());
     }
     BigDecimal activityCount = (BigDecimal) trackingNode.getProperty("count");
     activityNode.setProperty("sling:resourceType", "sakai/resource-update");
     trackingNode.setProperty("count", activityCount.add(BigDecimal.ONE));
     activityNode.setProperty("resourceId", resourceId);
     activityNode.setProperty("resourcetype", resourceType);
     activityNode.setProperty("activitytype", activityType);
     activityNode.setProperty("timestamp", timestamp);
     activityNode.setProperty("userid", userId);
     cm.update(activityNode);
     cm.update(trackingNode);
   } catch (AccessDeniedException e) {
     LOG.error(e.getLocalizedMessage(), e);
   } catch (StorageClientException e) {
     LOG.error(e.getLocalizedMessage(), e);
   } finally {
     if (session != null) {
       try {
         session.logout();
       } catch (ClientPoolException e) {
         LOG.error(e.getLocalizedMessage(), e);
         throw new IllegalStateException(e);
       }
     }
   }
 }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.sakaiproject.nakamura.api.solr.IndexingHandler#getDocuments(org.sakaiproject.nakamura.api.solr.RepositorySession,
   *     org.osgi.service.event.Event)
   */
  public Collection<SolrInputDocument> getDocuments(
      RepositorySession repositorySession, Event event) {
    String path = (String) event.getProperty(FIELD_PATH);

    logger.info("Indexing connections at path {}", path);
    List<SolrInputDocument> documents = Lists.newArrayList();
    if (!StringUtils.isBlank(path)) {
      try {
        Session session = repositorySession.adaptTo(Session.class);
        ContentManager cm = session.getContentManager();
        Content content = cm.get(path);

        int lastSlash = path.lastIndexOf('/');
        String contactName = path.substring(lastSlash + 1);
        AuthorizableManager am = session.getAuthorizableManager();
        Authorizable contactAuth = am.findAuthorizable(contactName);

        if (content != null && contactAuth != null) {
          SolrInputDocument doc = new SolrInputDocument();
          for (Entry<String, String> prop : WHITELISTED_PROPS.entrySet()) {
            String key = prop.getKey();
            Object value = content.getProperty(key);
            if (value != null) {
              doc.addField(WHITELISTED_PROPS.get(key), value);
            }
          }

          // flatten out the contact so we can search it
          Map<String, Object> contactProps = contactAuth.getSafeProperties();
          if (contactAuth != null) {
            for (String prop : FLATTENED_PROPS) {
              Object value = contactProps.get(prop);
              if (value != null) {
                doc.addField(prop, value);
              }
            }
          }

          doc.addField(_DOC_SOURCE_OBJECT, content);
          documents.add(doc);
        } else {
          logger.warn(
              "Did not index {}: Content == {}; Contact Auth == {}",
              new Object[] {path, content, contactAuth});
        }
      } catch (StorageClientException e) {
        logger.error(e.getMessage(), e);
      } catch (AccessDeniedException e) {
        logger.error(e.getMessage(), e);
      }
    }
    logger.debug("Got documents {} ", documents);
    return documents;
  }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.sakaiproject.nakamura.api.solr.IndexingHandler#getDocuments(org.sakaiproject.nakamura.api.solr.RepositorySession,
   *     org.osgi.service.event.Event)
   */
  public Collection<SolrInputDocument> getDocuments(
      RepositorySession repositorySession, Event event) {
    LOGGER.debug("GetDocuments for {} ", event);
    String path = (String) event.getProperty("path");
    if (ignorePath(path)) {
      return Collections.emptyList();
    }
    List<SolrInputDocument> documents = Lists.newArrayList();
    if (path != null) {
      try {
        Session session = repositorySession.adaptTo(Session.class);
        ContentManager contentManager = session.getContentManager();
        Content content = contentManager.get(path);
        if (content != null) {
          SolrInputDocument doc = new SolrInputDocument();

          Map<String, Object> properties = content.getProperties();

          for (Entry<String, Object> p : properties.entrySet()) {
            String indexName = index(p);
            if (indexName != null) {
              for (Object o : convertToIndex(p)) {
                doc.addField(indexName, o);
              }
            }
          }

          InputStream contentStream = contentManager.getInputStream(path);
          if (contentStream != null) {
            try {
              String extracted = tika.parseToString(contentStream);
              doc.addField("content", extracted);
            } catch (TikaException e) {
              LOGGER.warn(e.getMessage(), e);
            }
          }

          doc.addField(_DOC_SOURCE_OBJECT, content);
          documents.add(doc);
        }
      } catch (ClientPoolException e) {
        LOGGER.warn(e.getMessage(), e);
      } catch (StorageClientException e) {
        LOGGER.warn(e.getMessage(), e);
      } catch (AccessDeniedException e) {
        LOGGER.warn(e.getMessage(), e);
      } catch (IOException e) {
        LOGGER.warn(e.getMessage(), e);
      }
    }
    LOGGER.debug("Got documents {} ", documents);
    return documents;
  }
Пример #6
0
  @SuppressWarnings("unchecked")
  private <T> T getHandler(
      RepositorySession repositorySession,
      String path,
      Map<String, T> indexers,
      Map<String, String> ignoreCache) {
    org.sakaiproject.nakamura.api.lite.Session sparseSession =
        repositorySession.adaptTo(org.sakaiproject.nakamura.api.lite.Session.class);

    while (path != null) {
      if (!ignoreCache.containsKey(path)) {
        try {
          if (sparseSession != null) {
            ContentManager contentManager = sparseSession.getContentManager();
            Content c = contentManager.get(path);
            LOGGER.debug("Checking Content at {} got {} ", path, c);
            if (c != null) {
              if (c.hasProperty("sling:resourceType")) {
                String resourceType = (String) c.getProperty("sling:resourceType");
                T handler = indexers.get(resourceType);
                if (handler != null) {
                  LOGGER.debug(
                      "Handler of type {} found {} for {} from {} ",
                      new Object[] {resourceType, handler, path, indexers});
                  return handler;
                } else {
                  LOGGER.debug("Ignoring {}; no handler", path);
                  synchronized (this) {
                    ignoreCache.put(path, path);
                  }
                }
              } else {
                LOGGER.debug("Ignored {} no resource type ", path);
              }
            }
          }
        } catch (StorageClientException e) {
          LOGGER.debug(e.getMessage(), e);
        } catch (AccessDeniedException e) {
          LOGGER.debug(e.getMessage(), e);
        }
      }
      if (StorageClientUtils.isRoot(path)) {
        break;
      }
      path = Utils.getParentPath(path);
    }
    return (T) defaultHandler;
  }
  @POST
  @Path("{type:user|group}/{userid}")
  public Response doUpdateAuthorizable(
      @Context HttpServletRequest request,
      @Context HttpServletResponse response,
      @PathParam(value = "type") String authorizableType,
      @PathParam(value = "userid") String authorizableId) {
    try {
      AuthorizableManager authorizableManager = getAuthorizableManager(request, response);
      Authorizable authorizable = authorizableManager.findAuthorizable(authorizableId);
      Response checkType = checkType(authorizable, authorizableType);
      if (checkType != null) {
        return checkType;
      }

      // process the post request.
      AuthorizableHelper authorizableHelper = new AuthorizableHelper(authorizableManager);
      ModificationRequest modificationRequest = new ModificationRequest();
      modificationRequest.processRequest(request);
      authorizableHelper.applyProperties(authorizable, modificationRequest);
      authorizableHelper.save();
      final List<String> feedback = modificationRequest.getFeedback();

      return Response.ok(
              new StreamingOutput() {
                @Override
                public void write(OutputStream output) throws IOException, WebApplicationException {
                  ResponseUtils.writeFeedback(feedback, output);
                }
              })
          .type(MediaType.APPLICATION_JSON_TYPE.toString() + "; charset=utf-8")
          .lastModified(new Date())
          .build();

    } catch (StorageClientException e) {
      return ResponseUtils.getResponse(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    } catch (AccessDeniedException e) {
      return ResponseUtils.getResponse(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
    } catch (IOException e) {
      return ResponseUtils.getResponse(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    } catch (FileUploadException e) {
      return ResponseUtils.getResponse(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    }
  }
  private IndexingHandler getHandler(RepositorySession repositorySession, String path) {
    org.sakaiproject.nakamura.api.lite.Session sparseSession =
        repositorySession.adaptTo(org.sakaiproject.nakamura.api.lite.Session.class);

    while (path != null) {
      if (!ignoreCache.containsKey(path)) {
        try {
          if (sparseSession != null) {
            ContentManager contentManager = sparseSession.getContentManager();
            Content c = contentManager.get(path);
            LOGGER.debug("Checking Content at {} got {} ", path, c);
            if (c != null) {
              if (c.hasProperty(SLING_RESOURCE_TYPE)) {
                String resourceType = (String) c.getProperty(SLING_RESOURCE_TYPE);
                IndexingHandler handler = indexers.get(resourceType);
                if (handler != null) {
                  LOGGER.debug(
                      "Handler of type {} found {} for {} from {} ",
                      new Object[] {resourceType, handler, path, indexers});
                  return handler;
                } else {
                  TelemetryCounter.incrementValue(
                      "solr", "SparseIndexingServiceImpl-ignoredPath", path);
                  LOGGER.debug("Ignored {} no handler for {} ", path, resourceType);
                  ignoreCache.put(path, path);
                }
              } else {
                LOGGER.debug("Ignored {} no resource type ", path);
              }
            }
          }
        } catch (StorageClientException e) {
          LOGGER.debug(e.getMessage(), e);
        } catch (AccessDeniedException e) {
          LOGGER.debug(e.getMessage(), e);
        }
      }
      if (StorageClientUtils.isRoot(path)) {
        break;
      }
      path = Utils.getParentPath(path);
    }
    TelemetryCounter.incrementValue("solr", "SparseIndexingServiceImpl", "useDefaultHandler");
    return defaultHandler;
  }
  @GET
  @Path("{type:user|group}/{userid}.{format}")
  public Response getUser(
      @Context HttpServletRequest request,
      @Context HttpServletResponse response,
      @PathParam(value = "type") String authorizableType,
      @PathParam(value = "userid") String authorizableId,
      @PathParam(value = "format") final String outputFormat) {
    try {

      AuthorizableManager authorizableManager = getAuthorizableManager(request, response);
      final Authorizable authorizable = authorizableManager.findAuthorizable(authorizableId);
      Response checkType = checkType(authorizable, authorizableType);
      if (checkType != null) {
        return checkType;
      }
      Date lastModified = new Date();
      Long lm = (Long) authorizable.getProperty(Authorizable.LASTMODIFIED_FIELD);
      if (lm == null) {
        lm = (Long) authorizable.getProperty(Authorizable.CREATED_FIELD);
      }
      if (lm != null) {
        lastModified = new Date(lm);
      }
      return Response.ok(
              new StreamingOutput() {
                @Override
                public void write(OutputStream output) throws IOException, WebApplicationException {
                  ResponseUtils.writeTree(authorizable, outputFormat, output);
                }
              })
          .type(MediaType.APPLICATION_JSON_TYPE.toString() + "; charset=utf-8")
          .lastModified(lastModified)
          .build();

    } catch (StorageClientException e) {
      return ResponseUtils.getResponse(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
    } catch (AccessDeniedException e) {
      return ResponseUtils.getResponse(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
    }
  }
Пример #10
0
  /**
   * Writes commentCount of content
   *
   * @param node
   * @param session
   * @param write
   * @throws RepositoryException
   * @throws JSONException
   */
  public static void writeCommentCountProperty(
      Content content,
      org.sakaiproject.nakamura.api.lite.Session session,
      JSONWriter writer,
      Repository repository)
      throws StorageClientException, JSONException {

    int commentCount = 0;
    String COMMENTCOUNT = "commentCount";

    if (content.hasProperty(COMMENTCOUNT)) {
      commentCount = (Integer) content.getProperty(COMMENTCOUNT);
    } else {
      // no commentCount property on Content, then evaluate count and add property
      Content comments = null;
      org.sakaiproject.nakamura.api.lite.Session adminSession = null;
      try {
        comments = session.getContentManager().get(content.getPath() + "/comments");
        if (comments != null) {
          commentCount = Iterables.size(comments.listChildPaths());
        }
        content.setProperty(COMMENTCOUNT, commentCount);
        // save property
        adminSession = repository.loginAdministrative();
        ContentManager adminContentManager = adminSession.getContentManager();
        adminContentManager.update(content);
      } catch (org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException e) {
        log.error(e.getMessage(), e);
      } finally {
        if (adminSession != null) {
          try {
            adminSession.logout();
          } catch (Exception e) {
            log.error("Could not logout administrative session.");
          }
        }
      }
    }
    writer.key(COMMENTCOUNT);
    writer.value(commentCount);
  }
 public SolrSearchResultSet getSearchResultSet(
     SlingHttpServletRequest request, Query query, boolean asAnon) throws SolrSearchException {
   try {
     SolrSearchResultSet rs = null;
     if (query.getType() == Type.SOLR) {
       rs = processSolrQuery(request, query, asAnon);
     } else if (query.getType() == Type.SPARSE) {
       rs = processSparseQuery(request, query, asAnon);
     }
     return rs;
   } catch (SolrServerException e) {
     LOGGER.warn(e.getMessage(), e);
     throw new SolrSearchException(500, e.getMessage());
   } catch (ParseException e) {
     LOGGER.warn(e.getMessage(), e);
     throw new SolrSearchException(500, e.getMessage());
   } catch (StorageClientException e) {
     LOGGER.warn(e.getMessage(), e);
     throw new SolrSearchException(500, e.getMessage());
   } catch (AccessDeniedException e) {
     LOGGER.warn(e.getMessage(), e);
     throw new SolrSearchException(403, e.getMessage());
   }
 }
  @SuppressWarnings("unchecked")
  public void onMessage(Message message) {
    try {
      LOGGER.debug("Started handling email jms message.");

      String nodePath = message.getStringProperty(NODE_PATH_PROPERTY);
      String contentPath = message.getStringProperty(CONTENT_PATH_PROPERTY);
      Object objRcpt = message.getObjectProperty(RECIPIENTS);
      List<String> recipients = null;

      if (objRcpt instanceof List<?>) {
        recipients = (List<String>) objRcpt;
      } else if (objRcpt instanceof String) {
        recipients = new LinkedList<String>();
        String[] rcpts = StringUtils.split((String) objRcpt, ',');
        for (String rcpt : rcpts) {
          recipients.add(rcpt);
        }
      }

      if (contentPath != null && contentPath.length() > 0) {
        javax.jcr.Session adminSession = repository.loginAdministrative(null);
        org.sakaiproject.nakamura.api.lite.Session sparseSession =
            StorageClientUtils.adaptToSession(adminSession);

        try {
          ContentManager contentManager = sparseSession.getContentManager();
          Content messageContent = contentManager.get(contentPath);

          if (objRcpt != null) {
            // validate the message
            if (messageContent != null) {
              if (messageContent.hasProperty(MessageConstants.PROP_SAKAI_MESSAGEBOX)
                  && (MessageConstants.BOX_OUTBOX.equals(
                          messageContent.getProperty(MessageConstants.PROP_SAKAI_MESSAGEBOX))
                      || MessageConstants.BOX_PENDING.equals(
                          messageContent.getProperty(MessageConstants.PROP_SAKAI_MESSAGEBOX)))) {
                if (messageContent.hasProperty(MessageConstants.PROP_SAKAI_MESSAGEERROR)) {
                  // We're retrying this message, so clear the errors
                  messageContent.setProperty(
                      MessageConstants.PROP_SAKAI_MESSAGEERROR, (String) null);
                }
                if (messageContent.hasProperty(MessageConstants.PROP_SAKAI_TO)
                    && messageContent.hasProperty(MessageConstants.PROP_SAKAI_FROM)) {
                  // make a commons-email message from the message
                  MultiPartEmail email = null;
                  try {
                    email =
                        constructMessage(messageContent, recipients, adminSession, sparseSession);

                    email.setSmtpPort(smtpPort);
                    email.setHostName(smtpServer);

                    email.send();
                  } catch (EmailException e) {
                    String exMessage = e.getMessage();
                    Throwable cause = e.getCause();

                    setError(messageContent, exMessage);
                    LOGGER.warn("Unable to send email: " + exMessage);

                    // Get the SMTP error code
                    // There has to be a better way to do this
                    boolean rescheduled = false;
                    if (cause != null && cause.getMessage() != null) {
                      String smtpError = cause.getMessage().trim();
                      try {
                        int errorCode = Integer.parseInt(smtpError.substring(0, 3));
                        // All retry-able SMTP errors should have codes starting
                        // with 4
                        scheduleRetry(errorCode, messageContent);
                        rescheduled = true;
                      } catch (NumberFormatException nfe) {
                        // smtpError didn't start with an error code, let's dig for
                        // it
                        String searchFor = "response:";
                        int rindex = smtpError.indexOf(searchFor);
                        if (rindex > -1 && (rindex + searchFor.length()) < smtpError.length()) {
                          int errorCode =
                              Integer.parseInt(
                                  smtpError.substring(searchFor.length(), searchFor.length() + 3));
                          scheduleRetry(errorCode, messageContent);
                          rescheduled = true;
                        }
                      }
                    }
                    if (rescheduled) {
                      LOGGER.info("Email {} rescheduled for redelivery. ", nodePath);
                    } else {
                      LOGGER.error("Unable to reschedule email for delivery: " + e.getMessage(), e);
                    }
                  }
                } else {
                  setError(messageContent, "Message must have a to and from set");
                }
              } else {
                setError(messageContent, "Not an outbox");
              }
              if (!messageContent.hasProperty(MessageConstants.PROP_SAKAI_MESSAGEERROR)) {
                messageContent.setProperty(
                    MessageConstants.PROP_SAKAI_MESSAGEBOX, MessageConstants.BOX_SENT);
              }
            }
          } else {
            String retval = "null";
            setError(
                messageContent,
                "Expected recipients to be String or List<String>.  Found " + retval);
          }
        } finally {
          if (adminSession != null) {
            adminSession.logout();
          }
        }
      }
    } catch (PathNotFoundException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (RepositoryException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (JMSException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (EmailDeliveryException e) {
      LOGGER.error(e.getMessage());
    } catch (ClientPoolException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (StorageClientException e) {
      LOGGER.error(e.getMessage(), e);
    } catch (AccessDeniedException e) {
      LOGGER.error(e.getMessage(), e);
    }
  }
Пример #13
0
  /**
   * Process properties to query sparse content directly.
   *
   * @param request
   * @param query
   * @param asAnon
   * @return
   * @throws StorageClientException
   * @throws AccessDeniedException
   */
  public SolrSearchResultSet processQuery(
      SlingHttpServletRequest request, Query query, boolean asAnon) throws SolrSearchException {
    try {
      // use solr parsing to get the terms from the query string
      QueryParser parser =
          new QueryParser(Version.LUCENE_40, "id", new TextField().getQueryAnalyzer());
      org.apache.lucene.search.Query luceneQuery = parser.parse(query.getQueryString());

      Map<String, Object> props = Maps.newHashMap();
      if (luceneQuery instanceof BooleanQuery) {
        BooleanQuery boolLucQuery = (BooleanQuery) luceneQuery;

        int orCount = 0;
        List<BooleanClause> clauses = boolLucQuery.clauses();
        for (BooleanClause clause : clauses) {
          org.apache.lucene.search.Query clauseQuery = clause.getQuery();
          Map<String, Object> subOrs = Maps.newHashMap();
          // we support 1 level of nesting for OR clauses
          if (clauseQuery instanceof BooleanQuery) {
            for (BooleanClause subclause : ((BooleanQuery) clauseQuery).clauses()) {
              org.apache.lucene.search.Query subclauseQuery = subclause.getQuery();
              extractTerms(subclause, subclauseQuery, props, subOrs);
            }
            props.put("orset" + orCount, subOrs);
            orCount++;
          } else {
            extractTerms(clause, clauseQuery, props, subOrs);
            if (!subOrs.isEmpty()) {
              props.put("orset" + orCount, subOrs);
              orCount++;
            }
          }
        }
      } else {
        extractTerms(null, luceneQuery, props, null);
      }

      // add the options to the parameters but prepend _ to avoid collision
      for (Entry<String, String> option : query.getOptions().entrySet()) {
        props.put("_" + option.getKey(), option.getValue());
      }

      Session session =
          StorageClientUtils.adaptToSession(
              request.getResourceResolver().adaptTo(javax.jcr.Session.class));
      ContentManager cm = session.getContentManager();
      long tquery = System.currentTimeMillis();
      Iterable<Content> items = cm.find(props);
      tquery = System.currentTimeMillis() - tquery;
      try {
        if (tquery > verySlowQueryThreshold) {
          SLOW_QUERY_LOGGER.error(
              "Very slow sparse query {} ms {} ",
              tquery,
              URLDecoder.decode(query.toString(), "UTF-8"));
        } else if (tquery > slowQueryThreshold) {
          SLOW_QUERY_LOGGER.warn(
              "Slow sparse query {} ms {} ", tquery, URLDecoder.decode(query.toString(), "UTF-8"));
        }
      } catch (UnsupportedEncodingException e) {
        // quietly swallow this exception
        LOGGER.debug(e.getLocalizedMessage(), e);
      }
      SolrSearchResultSet rs = new SparseSearchResultSet(items, defaultMaxResults);
      return rs;
    } catch (AccessDeniedException e) {
      throw new SolrSearchException(500, e.getMessage());
    } catch (StorageClientException e) {
      throw new SolrSearchException(500, e.getMessage());
    } catch (ParseException e) {
      throw new SolrSearchException(500, e.getMessage());
    }
  }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.sakaiproject.nakamura.api.search.solr.SolrSearchBatchResultProcessor#writeResults(org.apache.sling.api.SlingHttpServletRequest,
   *     org.apache.sling.commons.json.io.JSONWriter, java.util.Iterator)
   */
  public void writeResults(
      SlingHttpServletRequest request, JSONWriter writer, Iterator<Result> iterator)
      throws JSONException {

    try {
      Session session =
          StorageClientUtils.adaptToSession(
              request.getResourceResolver().adaptTo(javax.jcr.Session.class));
      ContentManager cm = session.getContentManager();
      List<String> basePosts = new ArrayList<String>();
      Map<String, List<Post>> postChildren = new HashMap<String, List<Post>>();
      Map<String, Post> allPosts = new HashMap<String, Post>();
      while (iterator.hasNext()) {
        Result result = iterator.next();
        Content content = cm.get(result.getPath());
        if (content == null) {
          continue;
        }
        Post p = new Post(content, session);
        allPosts.put((String) content.getProperty(MessageConstants.PROP_SAKAI_ID), p);

        if (content.hasProperty(DiscussionConstants.PROP_REPLY_ON)) {
          // This post is a reply on another post.
          String replyon = (String) content.getProperty(DiscussionConstants.PROP_REPLY_ON);
          if (!postChildren.containsKey(replyon)) {
            postChildren.put(replyon, new ArrayList<Post>());
          }

          postChildren.get(replyon).add(p);

        } else {
          // This post is not a reply to another post, thus it is a basepost.
          basePosts.add(p.getPostId());
        }
      }

      // Now that we have all the base posts, we can sort the replies properly
      for (Entry<String, List<Post>> postChild : postChildren.entrySet()) {
        Post parentPost = allPosts.get(postChild.getKey());
        if (parentPost != null) {
          List<Post> childrenList = parentPost.getChildren();
          List<Post> childrenActual = postChild.getValue();
          childrenList.addAll(childrenActual);
        }
      }

      // The posts are sorted, now return them as json.
      for (String basePostId : basePosts) {
        allPosts
            .get(basePostId)
            .outputPostAsJSON(
                (ExtendedJSONWriter) writer, presenceService, basicUserInfoService, session);
      }
    } catch (StorageClientException e) {
      throw new RuntimeException(e.getMessage(), e);
    } catch (AccessDeniedException e) {
      throw new RuntimeException(e.getMessage(), e);
    } catch (RepositoryException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }
  /**
   * Retrieves the list of members.
   *
   * <p>{@inheritDoc}
   *
   * @see
   *     org.apache.sling.api.servlets.SlingSafeMethodsServlet#doGet(org.apache.sling.api.SlingHttpServletRequest,
   *     org.apache.sling.api.SlingHttpServletResponse)
   */
  @Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
      throws ServletException, IOException {
    try {
      // Get hold of the actual file.
      Resource resource = request.getResource();
      javax.jcr.Session jcrSession = request.getResourceResolver().adaptTo(javax.jcr.Session.class);
      Session session = resource.adaptTo(Session.class);

      AuthorizableManager am = session.getAuthorizableManager();
      AccessControlManager acm = session.getAccessControlManager();
      Content node = resource.adaptTo(Content.class);
      Authorizable thisUser = am.findAuthorizable(session.getUserId());

      if (!acm.can(thisUser, Security.ZONE_CONTENT, resource.getPath(), Permissions.CAN_READ)) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
      }

      Map<String, Object> properties = node.getProperties();
      String[] managers = (String[]) properties.get(POOLED_CONTENT_USER_MANAGER);
      String[] editors = (String[]) properties.get(POOLED_CONTENT_USER_EDITOR);
      String[] viewers = (String[]) properties.get(POOLED_CONTENT_USER_VIEWER);

      boolean detailed = false;
      boolean tidy = false;
      for (String selector : request.getRequestPathInfo().getSelectors()) {
        if ("detailed".equals(selector)) {
          detailed = true;
        } else if ("tidy".equals(selector)) {
          tidy = true;
        }
      }

      // Loop over the sets and output it.
      ExtendedJSONWriter writer = new ExtendedJSONWriter(response.getWriter());
      writer.setTidy(tidy);
      writer.object();
      writer.key("managers");
      writer.array();
      for (String manager : StorageClientUtils.nonNullStringArray(managers)) {
        try {
          writeProfileMap(jcrSession, am, writer, manager, detailed);
        } catch (AccessDeniedException e) {
          LOGGER.debug("Skipping private manager [{}]", manager);
        }
      }
      writer.endArray();
      writer.key("editors");
      writer.array();
      for (String editor : StorageClientUtils.nonNullStringArray(editors)) {
        try {
          writeProfileMap(jcrSession, am, writer, editor, detailed);
        } catch (AccessDeniedException e) {
          LOGGER.debug("Skipping private editor [{}]", editor);
        }
      }
      writer.endArray();
      writer.key("viewers");
      writer.array();
      for (String viewer : StorageClientUtils.nonNullStringArray(viewers)) {
        try {
          writeProfileMap(jcrSession, am, writer, viewer, detailed);
        } catch (AccessDeniedException e) {
          LOGGER.debug("Skipping private viewer [{}]", viewer);
        }
      }
      writer.endArray();
      writer.endObject();
    } catch (JSONException e) {
      response.sendError(SC_INTERNAL_SERVER_ERROR, "Failed to generate proper JSON.");
      LOGGER.error(e.getMessage(), e);
    } catch (StorageClientException e) {
      response.sendError(SC_INTERNAL_SERVER_ERROR, "Failed to generate proper JSON.");
      LOGGER.error(e.getMessage(), e);
    } catch (AccessDeniedException e) {
      response.sendError(SC_INTERNAL_SERVER_ERROR, "Failed to generate proper JSON.");
      LOGGER.error(e.getMessage(), e);
    } catch (RepositoryException e) {
      response.sendError(SC_INTERNAL_SERVER_ERROR, "Failed to generate proper JSON.");
      LOGGER.error(e.getMessage(), e);
    }
  }