/**
  * {@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) {
    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;
  }
 public void process(FilterChain chain, Request request, Response response) {
   try {
     chain.process(request, response);
     for (Session s : tracker.get()) {
       LOGGER.debug("Committing {} ", s);
       s.commit();
     }
   } finally {
     Set<Session> sessions = tracker.get();
     for (Session s : sessions) {
       try {
         LOGGER.debug("Logout {} ", s);
         s.logout();
       } catch (ClientPoolException e) {
         LOGGER.error(e.getMessage(), e);
       }
     }
     sessions.clear();
   }
 }
  @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);
    }
  }
  /**
   * Manipulate the member list for this file.
   *
   * <p>{@inheritDoc}
   *
   * @see
   *     org.apache.sling.api.servlets.SlingAllMethodsServlet#doPost(org.apache.sling.api.SlingHttpServletRequest,
   *     org.apache.sling.api.SlingHttpServletResponse)
   */
  @SuppressWarnings("unchecked")
  @Override
  protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response)
      throws ServletException, IOException {
    // fail if anonymous
    String remoteUser = request.getRemoteUser();
    if (User.ANON_USER.equals(remoteUser)) {
      response.sendError(SC_FORBIDDEN, "Anonymous users cannot update content members.");
      return;
    }
    Session session = null;
    boolean releaseSession = false;
    try {
      Resource resource = request.getResource();
      session = resource.adaptTo(Session.class);
      Content pooledContent = resource.adaptTo(Content.class);
      AccessControlManager accessControlManager = session.getAccessControlManager();
      AuthorizableManager authorizableManager = session.getAuthorizableManager();
      User thisUser = authorizableManager.getUser();
      if (!accessControlManager.can(
          thisUser, Security.ZONE_CONTENT, pooledContent.getPath(), Permissions.CAN_READ)) {
        response.sendError(SC_FORBIDDEN, "Insufficient permission to read this content.");
      }

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

      Set<String> managerSet = Sets.newHashSet(managers);
      Set<String> editorSet = Sets.newHashSet(editors);
      Set<String> viewerSet = Sets.newHashSet(viewers);

      List<String> removeViewers =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":viewer@Delete")));
      List<String> removeManagers =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":manager@Delete")));
      List<String> removeEditors =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":editor@Delete")));
      List<String> addViewers =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":viewer")));
      List<String> addManagers =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":manager")));
      List<String> addEditors =
          Arrays.asList(
              StorageClientUtils.nonNullStringArray(request.getParameterValues(":editor")));

      if (!accessControlManager.can(
          thisUser, Security.ZONE_CONTENT, pooledContent.getPath(), Permissions.CAN_WRITE)) {
        if (!addManagers.isEmpty()) {
          response.sendError(SC_FORBIDDEN, "Non-managers may not add managers to content.");
          return;
        }

        for (String name : removeManagers) {
          // asking to remove managers who don't exist is harmless
          if (managerSet.contains(name)) {
            response.sendError(SC_FORBIDDEN, "Non-managers may not remove managers from content.");
            return;
          }
        }

        if (addViewers.contains(User.ANON_USER) || addViewers.contains(Group.EVERYONE)) {
          response.sendError(
              SC_FORBIDDEN, "Non-managers may not add 'anonymous' or 'everyone' as viewers.");
          return;
        }

        if (addEditors.contains(User.ANON_USER) || addEditors.contains(Group.EVERYONE)) {
          response.sendError(
              SC_FORBIDDEN, "Non-managers may not add 'anonymous' or 'everyone' as editors.");
          return;
        }

        for (String name : removeViewers) {
          if (!thisUser.getId().equals(name)) {
            Authorizable viewer = authorizableManager.findAuthorizable(name);
            if (viewer != null
                && !accessControlManager.can(
                    thisUser, Security.ZONE_AUTHORIZABLES, name, Permissions.CAN_WRITE)) {
              response.sendError(
                  SC_FORBIDDEN,
                  "Non-managers may not remove any viewer other than themselves or a group which they manage.");
            }
          }
        }

        // the request has passed all the rules that govern non-manager users
        // so we'll grant an administrative session
        session = session.getRepository().loginAdministrative();
        releaseSession = true;
      }
      List<AclModification> aclModifications = Lists.newArrayList();

      for (String addManager : addManagers) {
        if ((addManager.length() > 0) && !managerSet.contains(addManager)) {
          managerSet.add(addManager);
          AclModification.addAcl(true, Permissions.CAN_MANAGE, addManager, aclModifications);
        }
      }

      for (String removeManager : removeManagers) {
        if ((removeManager.length() > 0) && managerSet.contains(removeManager)) {
          managerSet.remove(removeManager);
          AclModification.removeAcl(true, Permissions.CAN_MANAGE, removeManager, aclModifications);
        }
      }

      for (String addEditor : addEditors) {
        if ((addEditor.length() > 0) && !editorSet.contains(addEditor)) {
          editorSet.add(addEditor);
          AclModification.addAcl(true, PERMISSION_EDITOR, addEditor, aclModifications);
        }
      }

      for (String removeEditor : removeEditors) {
        if ((removeEditor.length() > 0) && editorSet.contains(removeEditor)) {
          editorSet.remove(removeEditor);
          AclModification.removeAcl(true, PERMISSION_EDITOR, removeEditor, aclModifications);
        }
      }

      for (String addViewer : addViewers) {
        if ((addViewer.length() > 0) && !viewerSet.contains(addViewer)) {
          viewerSet.add(addViewer);
          AclModification.addAcl(true, Permissions.CAN_READ, addViewer, aclModifications);
        }
      }

      for (String removeViewer : removeViewers) {
        removeViewer = removeViewer.trim();
        if ((removeViewer.length() > 0) && viewerSet.contains(removeViewer)) {
          viewerSet.remove(removeViewer);
          if (!managerSet.contains(removeViewer)) {
            AclModification.removeAcl(true, Permissions.CAN_READ, removeViewer, aclModifications);
          }
        }
      }

      updateContentMembers(session, pooledContent, viewerSet, managerSet, editorSet);
      updateContentAccess(session, pooledContent, aclModifications);

      this.authorizableCountChanger.notify(
          UserConstants.CONTENT_ITEMS_PROP,
          addViewers,
          addEditors,
          addManagers,
          removeViewers,
          removeEditors,
          removeManagers);

      response.setStatus(SC_OK);

    } catch (StorageClientException e) {
      LOGGER.error(e.getMessage());
      response.sendError(
          SC_INTERNAL_SERVER_ERROR, "StorageClientException: " + e.getLocalizedMessage());
    } catch (AccessDeniedException e) {
      response.sendError(
          SC_FORBIDDEN,
          "Insufficient permission to update content members at " + request.getRequestURI());
    } finally {
      if (session != null && releaseSession) {
        try {
          session.logout();
        } catch (ClientPoolException e) {
          LOGGER.error(e.getMessage());
        }
      }
    }
  }