Beispiel #1
0
 public static boolean createLink(
     Content content, String link, org.sakaiproject.nakamura.api.lite.Session session)
     throws org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException,
         StorageClientException {
   String userId = session.getUserId();
   if (User.ANON_USER.equals(userId)) {
     throw new org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException(
         Security.ZONE_CONTENT, link, "Cant create a link", userId);
   }
   ContentManager contentManager = session.getContentManager();
   Content linkNode = contentManager.get(link);
   if (linkNode == null) {
     linkNode =
         new Content(
             link,
             ImmutableMap.of(
                 JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY,
                 (Object) RT_SAKAI_LINK,
                 SAKAI_LINK,
                 content.getPath()));
   } else {
     linkNode.setProperty(JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY, RT_SAKAI_LINK);
     linkNode.setProperty(SAKAI_LINK, content.getPath());
   }
   contentManager.update(linkNode);
   return true;
 }
Beispiel #2
0
  public static void writeLinkNode(
      Content content,
      org.sakaiproject.nakamura.api.lite.Session session,
      JSONWriter writer,
      boolean objectInProgress)
      throws StorageClientException, JSONException {

    if (!objectInProgress) {
      writer.object();
    }
    ContentManager contentManager = session.getContentManager();

    // Write all the properties.
    ExtendedJSONWriter.writeNodeContentsToWriter(writer, content);

    // permissions
    writePermissions(content, session, writer);

    // Write the actual file.
    if (content.hasProperty(SAKAI_LINK)) {
      String linkPath = (String) content.getProperty(SAKAI_LINK);
      writer.key("file");
      try {
        Content fileNode = contentManager.get(linkPath);
        writeFileNode(fileNode, session, writer);
      } catch (org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException e) {
        writer.value(false);
      }
    }
    if (!objectInProgress) {
      writer.endObject();
    }
  }
  /**
   * {@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;
  }
Beispiel #4
0
  public static void writeFileNode(
      Content content,
      org.sakaiproject.nakamura.api.lite.Session session,
      JSONWriter write,
      int maxDepth,
      boolean objectInProgress)
      throws JSONException, StorageClientException {
    if (content == null) {
      log.warn("Can't output null content.");
      return;
    }

    if (!objectInProgress) {
      write.object();
    }
    // dump all the properties.
    ExtendedJSONWriter.writeContentTreeToWriter(write, content, true, maxDepth);
    // The permissions for this session.
    writePermissions(content, session, write);

    write.key(JcrConstants.JCR_LASTMODIFIED);
    Calendar cal = new GregorianCalendar();
    cal.setTimeInMillis(StorageClientUtils.toLong(content.getProperty(Content.LASTMODIFIED_FIELD)));
    write.value(DateUtils.iso8601(cal));
    write.key(JcrConstants.JCR_MIMETYPE);
    write.value(content.getProperty(Content.MIMETYPE_FIELD));
    write.key(JcrConstants.JCR_DATA);
    write.value(StorageClientUtils.toLong(content.getProperty(Content.LENGTH_FIELD)));
    if (!objectInProgress) {
      write.endObject();
    }
  }
 private DependencySequence getMigratorSequence(
     SessionImpl session, PropertyMigrator[] propertyMigrators)
     throws StorageClientException, AccessDeniedException {
   Content runMigrators = session.getContentManager().get(SYSTEM_MIGRATION_CONTENT_ITEM);
   Map<String, Object> runMigratorRecord = ImmutableMap.of();
   if (runMigrators != null) {
     runMigratorRecord = runMigrators.getProperties();
   }
   return new DependencySequence(propertyMigrators, runMigratorRecord);
 }
  /**
   * {@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;
  }
 private void writeCommentCountProperty(JSONWriter write, Session session, Content contentResult)
     throws StorageClientException, JSONException, AccessDeniedException {
   ContentManager contentManager = session.getContentManager();
   Content comments = contentManager.get(contentResult.getPath() + "/" + "comments");
   long commentCount = 0;
   if (comments != null) {
     for (@SuppressWarnings("unused") Content comment : comments.listChildren()) {
       commentCount++;
     }
   }
   write.key("commentCount");
   write.value(commentCount);
 }
  private void addDefaultFields(SolrInputDocument doc, RepositorySession repositorySession)
      throws StorageClientException {
    Object o = doc.getFieldValue(_DOC_SOURCE_OBJECT);
    if (o instanceof Content) {
      Content content = (Content) o;
      boolean writeReaders = true;
      Object suppressReadersValue = doc.getFieldValue(FIELD_SUPPRESS_READERS);
      if (suppressReadersValue instanceof String) {
        if (FIELD_SUPPRESS_READERS.equals(suppressReadersValue)) {
          writeReaders = false;
          doc.removeField(FIELD_SUPPRESS_READERS);
        }
      }
      if (writeReaders) {
        String[] principals =
            getReadingPrincipals(repositorySession, Security.ZONE_CONTENT, content.getPath());
        for (String principal : principals) {
          doc.addField(FIELD_READERS, principal);
        }
      } else {
        doc.removeField(FIELD_READERS);
      }

      if (content.hasProperty(SLING_RESOURCE_TYPE)) {
        doc.setField(FIELD_RESOURCE_TYPE, content.getProperty(SLING_RESOURCE_TYPE));
      }
      String path = content.getPath();
      // we don't overwrite the id field if it has been provided
      if (!doc.getFieldNames().contains(FIELD_ID)) {
        doc.setField(FIELD_ID, path);
      }
      while (path != null) {
        doc.addField(FIELD_PATH, path);
        String newPath = Utils.getParentPath(path);
        if (path.equals(newPath)) {
          break;
        }
        path = newPath;
      }
      doc.removeField(_DOC_SOURCE_OBJECT);
    } else {
      TelemetryCounter.incrementValue("solr", "SparseIndexingServiceImpl", "docMissingSource");
      LOGGER.error(
          "Note to Developer: Indexer must add the _source fields so that the default fields can be set, please correct, SolrDoc was {} ",
          doc);
      throw new StorageClientException(
          _DOC_SOURCE_OBJECT
              + " fields was missing from Solr Document, please correct the handler implementation");
    }
  }
  @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;
  }
  /**
   * {@inheritDoc}
   *
   * @see
   *     org.sakaiproject.nakamura.api.message.LiteMessageTransport#send(org.sakaiproject.nakamura.api.message.MessageRoutes,
   *     org.osgi.service.event.Event, org.sakaiproject.nakamura.api.lite.content.Content)
   */
  public void send(MessageRoutes routes, Event event, Content message) {
    LOGGER.debug("Started handling an email message");

    // delay list instantiation to save object creation when not needed.
    List<String> recipients = null;
    for (MessageRoute route : routes) {
      if (TYPE.equals(route.getTransport())) {
        if (recipients == null) {
          recipients = new ArrayList<String>();
        }
        recipients.add(route.getRcpt());
      }
    }

    if (recipients != null) {
      Properties props = new Properties();
      if (event != null) {
        for (String propName : event.getPropertyNames()) {
          Object propValue = event.getProperty(propName);
          props.put(propName, propValue);
        }
      }
      // make the message deliver to one listener, that means the desination must be a queue.
      props.put(EventDeliveryConstants.DELIVERY_MODE, EventDeliveryMode.P2P);
      // make the message persistent to survive restarts.
      props.put(EventDeliveryConstants.MESSAGE_MODE, EventMessageMode.PERSISTENT);
      props.put(LiteOutgoingEmailMessageListener.RECIPIENTS, recipients);
      props.put(LiteOutgoingEmailMessageListener.CONTENT_PATH_PROPERTY, message.getPath());
      Event emailEvent = new Event(LiteOutgoingEmailMessageListener.QUEUE_NAME, (Map) props);

      LOGGER.debug("Sending event [" + emailEvent + "]");
      eventAdmin.postEvent(emailEvent);
    }
  }
Beispiel #12
0
  private static void writePermissions(
      Content content, org.sakaiproject.nakamura.api.lite.Session session, JSONWriter writer)
      throws StorageClientException, JSONException {
    if (content == null) {
      log.warn("Can't output permissions of null content.");
      return;
    }

    AccessControlManager acm = session.getAccessControlManager();
    String path = content.getPath();

    writer.key("permissions");
    writer.object();
    writer.key("set_property");
    // TODO does CAN_WRITE == set_property -CFH : yes, ieb
    // TODO: make this a bit more efficient, checking permissions one by one is going to rely on
    //       caching to make it efficient. It would be better to get the permissions bitmap and then
    //       check it to see what has been set. That might require a niew methods in the
    // AccessControl
    //       manager API.
    writer.value(hasPermission(acm, path, Permissions.CAN_WRITE));
    writer.key("read");
    writer.value(hasPermission(acm, path, Permissions.CAN_READ));
    writer.key("remove");
    writer.value(hasPermission(acm, path, Permissions.CAN_DELETE));
    writer.endObject();
  }
Beispiel #13
0
  /*
   * getPageTree: returns List of all pages under the passed path
   */
  private List<Content> getPageTree(Content pageContent) {
    List<Content> contentList = new ArrayList();

    // Add to list only if content is a page
    String resourceType = (String) pageContent.getProperty("sling:resourceType");
    if (resourceType != null && resourceType.equals("sakai/page")) {
      contentList.add(pageContent);
    }

    if (pageContent != null) {
      for (Content page : pageContent.listChildren()) {
        contentList.addAll(getPageTree(page));
      }
    }

    return contentList;
  }
  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;
  }
 private void saveMigratorSequence(
     SessionImpl session, DependencySequence migratorDependencySequence)
     throws AccessDeniedException, StorageClientException {
   Content runMigrators = session.getContentManager().get(SYSTEM_MIGRATION_CONTENT_ITEM);
   String ts = String.valueOf(System.currentTimeMillis());
   int i = 0;
   if (runMigrators == null) {
     Builder<String, Object> b = ImmutableMap.builder();
     for (PropertyMigrator pm : migratorDependencySequence) {
       b.put(pm.getName(), ts + ";" + i);
     }
     runMigrators = new Content(SYSTEM_MIGRATION_CONTENT_ITEM, b.build());
   } else {
     for (PropertyMigrator pm : migratorDependencySequence) {
       runMigrators.setProperty(pm.getName(), ts + ";" + i);
     }
   }
   session.getContentManager().update(runMigrators);
 }
Beispiel #16
0
  @Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
      throws ServletException, IOException {

    Resource resource = request.getResource();
    Node node = resource.adaptTo(Node.class);
    Content content = resource.adaptTo(Content.class);

    System.err.println("Node is " + node + " content is " + content);
    String link = null;
    try {
      if (node != null && node.hasProperty(FilesConstants.SAKAI_LINK)) {
        link = node.getProperty(FilesConstants.SAKAI_LINK).getString();
      } else if (content != null && content.hasProperty(FilesConstants.SAKAI_LINK)) {
        link = (String) content.getProperty(FilesConstants.SAKAI_LINK);
      }
      System.err.println("Link is " + link);

      if (link != null) {
        String[] linkProps = StringUtils.split(link, ':');
        LinkHandler handler = null;
        String path = null;
        if (linkProps.length == 2) {
          handler = fileHandlerTracker.getProcessorByName(linkProps[0]);
          path = linkProps[1];
        } else {
          if (node != null) {
            handler = new JcrInternalFileHandler();
          } else {
            handler = new SparseContentInternalFileHandler();
          }
          path = link;
        }
        if (handler != null) {
          handler.handleFile(request, response, path);
        }
      }
    } catch (RepositoryException e) {
      LOGGER.warn(e.getMessage(), e);
      response.sendError(500, "Unable to handle linked file.");
    }
  }
  private void scheduleRetry(int errorCode, Content contentNode) {
    // All retry-able SMTP errors should have codes starting with 4
    if ((errorCode / 100) == 4) {
      long retryCount = 0;
      if (contentNode.hasProperty(MessageConstants.PROP_SAKAI_RETRY_COUNT)) {
        retryCount =
            StorageClientUtils.toLong(
                contentNode.getProperty(MessageConstants.PROP_SAKAI_RETRY_COUNT));
      }

      if (retryCount < maxRetries) {
        Job job =
            new Job() {

              public void execute(JobContext jc) {
                Map<String, Serializable> config = jc.getConfiguration();
                Properties eventProps = new Properties();
                eventProps.put(NODE_PATH_PROPERTY, config.get(NODE_PATH_PROPERTY));

                Event retryEvent = new Event(QUEUE_NAME, eventProps);
                eventAdmin.postEvent(retryEvent);
              }
            };

        HashMap<String, Serializable> jobConfig = new HashMap<String, Serializable>();
        jobConfig.put(NODE_PATH_PROPERTY, contentNode.getPath());

        int retryIntervalMillis = retryInterval * 60000;
        Date nextTry = new Date(System.currentTimeMillis() + (retryIntervalMillis));

        try {
          scheduler.fireJobAt(null, job, jobConfig, nextTry);
        } catch (Exception e) {
          LOGGER.error(e.getMessage(), e);
        }
      } else {
        setError(contentNode, "Unable to send message, exhausted SMTP retries.");
      }
    } else {
      LOGGER.warn("Not scheduling a retry for error code not of the form 4xx.");
    }
  }
 private void updateContentMembers(
     Session session,
     Content content,
     Set<String> viewerSet,
     Set<String> managerSet,
     Set<String> editorSet)
     throws StorageClientException, AccessDeniedException {
   content.setProperty(
       POOLED_CONTENT_USER_VIEWER, viewerSet.toArray(new String[viewerSet.size()]));
   content.setProperty(
       POOLED_CONTENT_USER_MANAGER, managerSet.toArray(new String[managerSet.size()]));
   content.setProperty(
       POOLED_CONTENT_USER_EDITOR, editorSet.toArray(new String[editorSet.size()]));
   LOGGER.debug(
       "Set Managers to {}", Arrays.toString(managerSet.toArray(new String[managerSet.size()])));
   LOGGER.debug(
       "Set Editors to {}", Arrays.toString(editorSet.toArray(new String[editorSet.size()])));
   LOGGER.debug(
       "Set Viewers to {}", Arrays.toString(viewerSet.toArray(new String[managerSet.size()])));
   session.getContentManager().update(content);
 }
 private void updateContentAccess(
     Session session, Content content, List<AclModification> aclModifications)
     throws StorageClientException, AccessDeniedException {
   LOGGER.debug(
       "ACL Modifications {}",
       Arrays.toString(aclModifications.toArray(new AclModification[aclModifications.size()])));
   session
       .getAccessControlManager()
       .setAcl(
           Security.ZONE_CONTENT,
           content.getPath(),
           aclModifications.toArray(new AclModification[aclModifications.size()]));
 }
 private void addDefaultFields(SolrInputDocument doc, RepositorySession repositorySession)
     throws StorageClientException {
   Object o = doc.getFieldValue(_DOC_SOURCE_OBJECT);
   if (o instanceof Content) {
     Content content = (Content) o;
     String[] principals =
         getReadingPrincipals(repositorySession, Security.ZONE_CONTENT, content.getPath());
     for (String principal : principals) {
       doc.addField(FIELD_READERS, principal);
     }
     if (content.hasProperty("sling:resourceType")) {
       doc.setField(FIELD_RESOURCE_TYPE, content.getProperty("sling:resourceType"));
     }
     String path = content.getPath();
     // we don't overwrite the id field if it has been provided
     if (!doc.getFieldNames().contains(FIELD_ID)) {
       doc.setField(FIELD_ID, path);
     }
     while (path != null) {
       doc.addField(FIELD_PATH, path);
       String newPath = Utils.getParentPath(path);
       if (path.equals(newPath)) {
         break;
       }
       path = newPath;
     }
     doc.removeField(_DOC_SOURCE_OBJECT);
   } else {
     LOGGER.error(
         "Note to Developer: Indexer must add the _source fields so that the default fields can be set, please correct, SolrDoc was {} ",
         doc);
     throw new StorageClientException(
         _DOC_SOURCE_OBJECT
             + " fields was missing from Solr Document, please correct the handler implementation");
   }
 }
  /**
   * Give a JSON representation of the content.
   *
   * @param content
   * @param session
   * @param write The {@link JSONWriter} to output to.
   * @param depth
   * @throws JSONException
   * @throws StorageClientException
   */
  protected void handleContent(
      final Content content, final Session session, final JSONWriter write, final int depth)
      throws JSONException, StorageClientException {

    write.object();
    final String type = (String) content.getProperty(SLING_RESOURCE_TYPE_PROPERTY);
    if (FilesConstants.RT_SAKAI_LINK.equals(type)) {
      FileUtils.writeLinkNode(content, session, write, true);
    } else {
      FileUtils.writeFileNode(content, session, write, depth, true);
    }
    FileUtils.writeComments(content, session, write);
    FileUtils.writeCommentCountProperty(content, session, write, repository);
    write.endObject();
  }
Beispiel #22
0
 /**
  * Writes comments of content
  *
  * @param node
  * @param session
  * @param write
  * @throws RepositoryException
  * @throws JSONException
  */
 public static void writeComments(
     Content content, org.sakaiproject.nakamura.api.lite.Session session, JSONWriter writer)
     throws StorageClientException, JSONException {
   if (content == null) {
     log.warn("Can't output comments of null content.");
     return;
   }
   writer.key("comments");
   writer.object();
   Content commentContent = null;
   try {
     commentContent = session.getContentManager().get(content.getPath() + "/comments");
     ExtendedJSONWriter.writeContentTreeToWriter(writer, commentContent, true, 2);
   } catch (org.sakaiproject.nakamura.api.lite.accesscontrol.AccessDeniedException e) {
     writer.value(false);
   } finally {
     writer.endObject();
   }
 }
 private void writeCanManageProperty(
     SlingHttpServletRequest request, JSONWriter write, Session session, Content contentResult)
     throws StorageClientException, JSONException, AccessDeniedException {
   write.key("sakai:canmanage");
   Authorizable thisUser =
       session.getAuthorizableManager().findAuthorizable(request.getRemoteUser());
   Collection<String> principals = new ArrayList<String>();
   principals.addAll(Arrays.asList(thisUser.getPrincipals()));
   principals.add(request.getRemoteUser());
   boolean canManage = false;
   for (String principal : principals) {
     if (Arrays.asList(
             StorageClientUtils.nonNullStringArray(
                 (String[]) contentResult.getProperty("sakai:pooled-content-manager")))
         .contains(principal)) {
       canManage = true;
     }
   }
   write.value(canManage);
 }
Beispiel #24
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);
  }
  /**
   * {@inheritDoc} This post processor is only interested in posts to messages, so it should iterate
   * rapidly through all messages.
   *
   * @see
   *     org.apache.sling.servlets.post.SlingPostProcessor#process(org.apache.sling.api.SlingHttpServletRequest,
   *     java.util.List)
   */
  public void process(SlingHttpServletRequest request, List<Modification> changes)
      throws Exception {

    Resource resource = request.getResource();
    ResourceResolver resourceResolver = request.getResourceResolver();
    if (SparseContentResource.SPARSE_CONTENT_RT.equals(resource.getResourceSuperType())) {
      Session session = resource.adaptTo(Session.class);
      ContentManager contentManager = session.getContentManager();
      Map<Content, String> messageMap = new HashMap<Content, String>();
      for (Modification m : changes) {
        try {
          switch (m.getType()) {
            case CREATE:
            case MODIFY:
              String path = m.getSource();
              if (path.lastIndexOf("@") > 0) {
                path = path.substring(0, path.lastIndexOf("@"));
              }
              if (path.endsWith("/" + MessageConstants.PROP_SAKAI_MESSAGEBOX)) {
                path =
                    path.substring(
                        0, path.length() - MessageConstants.PROP_SAKAI_MESSAGEBOX.length() - 1);
              }

              // The Modification Source is the Resource path, and so we
              // need to translate that into a Content path.
              // TODO This is not a cheap operation. We might be better off
              // if we start including the Content path in our Modification objects.
              Resource modifiedResource = resourceResolver.getResource(path);
              if (modifiedResource == null) {
                return;
              }
              Content content = modifiedResource.adaptTo(Content.class);
              String contentPath = content.getPath();

              if (contentManager.exists(contentPath)) {
                content = contentManager.get(contentPath);
                if (content.hasProperty(SLING_RESOURCE_TYPE_PROPERTY)
                    && content.hasProperty(PROP_SAKAI_MESSAGEBOX)) {
                  if (SAKAI_MESSAGE_RT.equals(content.getProperty(SLING_RESOURCE_TYPE_PROPERTY))
                      && BOX_OUTBOX.equals(content.getProperty(PROP_SAKAI_MESSAGEBOX))) {
                    String sendstate;
                    if (content.hasProperty(PROP_SAKAI_SENDSTATE)) {
                      sendstate = (String) content.getProperty(PROP_SAKAI_SENDSTATE);
                    } else {
                      sendstate = STATE_NONE;
                    }
                    messageMap.put(content, sendstate);
                  }
                }
              }
              break;
          }
        } catch (StorageClientException ex) {
          LOGGER.warn("Failed to process on create for {} ", m.getSource(), ex);
        } catch (AccessDeniedException ex) {
          LOGGER.warn("Failed to process on create for {} ", m.getSource(), ex);
        }
      }

      List<String> handledNodes = new ArrayList<String>();
      // Check if we have any nodes that have a pending state and launch an OSGi
      // event
      for (Entry<Content, String> mm : messageMap.entrySet()) {
        Content content = mm.getKey();
        String path = content.getPath();
        String state = mm.getValue();
        if (!handledNodes.contains(path)) {
          if (STATE_NONE.equals(state) || STATE_PENDING.equals(state)) {

            content.setProperty(PROP_SAKAI_SENDSTATE, STATE_NOTIFIED);
            contentManager.update(content);

            Dictionary<String, Object> messageDict = new Hashtable<String, Object>();
            // WARNING
            // We can't pass in the node, because the session might expire before the event gets
            // handled
            // This does mean that the listener will have to get the node each time, and probably
            // create a new session for each message
            // This might be heavy on performance.
            messageDict.put(EVENT_LOCATION, path);
            messageDict.put(UserConstants.EVENT_PROP_USERID, request.getRemoteUser());
            LOGGER.debug("Launched event for message: {} ", path);
            Event pendingMessageEvent = new Event(PENDINGMESSAGE_EVENT, messageDict);
            // KERN-790: Initiate a synchronous event.
            try {
              eventAdmin.postEvent(pendingMessageEvent);
              handledNodes.add(path);
            } catch (Exception e) {
              LOGGER.warn("Failed to post message dispatch event, cause {} ", e.getMessage(), e);
            }
          }
        }
      }
    }
  }
  public void createActivity(
      Session session, Content targetLocation, String userId, ActivityServiceCallback callback)
      throws AccessDeniedException, StorageClientException, ServletException, IOException {
    if (userId == null) {
      userId = session.getUserId();
    }
    if (!userId.equals(session.getUserId()) && !User.ADMIN_USER.equals(session.getUserId())) {
      throw new IllegalStateException(
          "Only Administrative sessions may act on behalf of another user for activities");
    }
    ContentManager contentManager = session.getContentManager();
    // create activityStore if it does not exist
    String path = StorageClientUtils.newPath(targetLocation.getPath(), ACTIVITY_STORE_NAME);
    if (!contentManager.exists(path)) {
      contentManager.update(
          new Content(
              path,
              ImmutableMap.<String, Object>of(
                  SLING_RESOURCE_TYPE_PROPERTY, ACTIVITY_STORE_RESOURCE_TYPE)));
      // inherit ACL from the target node, but let logged-in users write activities
      session
          .getAccessControlManager()
          .setAcl(
              Security.ZONE_CONTENT,
              path,
              new AclModification[] {
                new AclModification(
                    AclModification.grantKey(Group.EVERYONE),
                    Permissions.CAN_WRITE.getPermission(),
                    Operation.OP_AND)
              });
    }
    // create activity within activityStore
    String activityPath = StorageClientUtils.newPath(path, ActivityUtils.createId());
    String activityFeedPath = StorageClientUtils.newPath(targetLocation.getPath(), "activityFeed");

    if (!contentManager.exists(activityFeedPath)) {
      contentManager.update(new Content(activityFeedPath, null));
    }
    if (!contentManager.exists(activityPath)) {
      contentManager.update(
          new Content(
              activityPath,
              ImmutableMap.of(
                  JcrResourceConstants.SLING_RESOURCE_TYPE_PROPERTY,
                  (Object) ActivityConstants.ACTIVITY_ITEM_RESOURCE_TYPE)));
    }

    Content activtyNode = contentManager.get(activityPath);
    callback.processRequest(activtyNode);

    activtyNode = contentManager.get(activityPath);
    activtyNode.setProperty(PARAM_ACTOR_ID, userId);
    activtyNode.setProperty(ActivityConstants.PARAM_SOURCE, targetLocation.getPath());

    Session adminSession = repository.loginAdministrative();
    List<String> routesStr = new LinkedList<String>();
    List<String> readers = new LinkedList<String>();
    try {
      List<ActivityRoute> routes =
          activityRouterManager.getActivityRoutes(activtyNode, adminSession);
      if (routes != null) {
        for (ActivityRoute route : routes) {
          routesStr.add(route.getDestination());
          if (route.getReaders() != null && route.getReaders().length > 0) {
            readers.addAll(Arrays.asList(route.getReaders()));
          }
        }
      }

      // store the routes as child content of the activity so we may lock it down to admin. It's
      // common for
      // the activity to be stored within the context of the content to which it pertains (e.g.,
      // within the
      // pooled content item on which the user performed the activity), therefore we could expose
      // user
      // activity routes there -- that is an exposure of potentially sensitive content such as who
      // the user's
      // connections are.
      String routesPath =
          StorageClientUtils.newPath(activtyNode.getPath(), ActivityConstants.PARAM_ROUTES);
      contentManager.update(
          new Content(
              routesPath,
              ImmutableMap.<String, Object>of(
                  ActivityConstants.PARAM_ROUTES,
                  routesStr.toArray(new String[routesStr.size()]))));
      adminSession
          .getAccessControlManager()
          .setAcl(
              Security.ZONE_CONTENT,
              routesPath,
              new AclModification[] {
                new AclModification(
                    AclModification.denyKey(User.ANON_USER),
                    Permissions.ALL.getPermission(),
                    Operation.OP_REPLACE),
                new AclModification(
                    AclModification.denyKey(Group.EVERYONE),
                    Permissions.ALL.getPermission(),
                    Operation.OP_REPLACE),
                new AclModification(
                    AclModification.denyKey(userId),
                    Permissions.ALL.getPermission(),
                    Operation.OP_REPLACE)
              });

      if (!readers.isEmpty()) {
        AclModification[] readerAcls = new AclModification[readers.size()];
        int i = 0;
        for (String reader : readers) {
          // ensure all the necessary readers/routes can read the activity
          readerAcls[i] =
              new AclModification(
                  AclModification.grantKey(reader),
                  Permissions.CAN_READ.getPermission(),
                  Operation.OP_OR);
          i++;
        }

        adminSession
            .getAccessControlManager()
            .setAcl(Security.ZONE_CONTENT, activtyNode.getPath(), readerAcls);
      }
    } finally {
      SparseUtils.logoutQuietly(adminSession);
    }

    // store the activity node
    contentManager.update(activtyNode);

    // post the asynchronous OSGi event
    final Dictionary<String, String> properties = new Hashtable<String, String>();
    properties.put(UserConstants.EVENT_PROP_USERID, userId);
    properties.put(ActivityConstants.EVENT_PROP_PATH, activityPath);
    properties.put("path", activityPath);
    properties.put("resourceType", ActivityConstants.ACTIVITY_ITEM_RESOURCE_TYPE);
    EventUtils.sendOsgiEvent(properties, LITE_EVENT_TOPIC, eventAdmin);
  }
 private void setError(Content node, String error) {
   node.setProperty(MessageConstants.PROP_SAKAI_MESSAGEERROR, error);
 }
Beispiel #28
0
  /**
   * {@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 {
      List<Content> contentList = null;
      RequestParameter rp = request.getRequestParameter("path");
      ResourceResolver resourceResolver = request.getResourceResolver();
      if (rp != null) {
        String contentPath = rp.getString("UTF-8");
        if (contentPath.startsWith("/_groupa:")) {
          contentPath = contentPath.replaceFirst("/_groupa:", "/~");
        }
        if (contentPath.endsWith("/")) {
          contentPath = contentPath.substring(0, contentPath.length() - 1);
        }

        Resource pagesResource = resourceResolver.getResource(contentPath);
        if (pagesResource != null) {
          contentList = getPageTree(pagesResource.adaptTo(Content.class));
          ;
        }
      }

      response.setContentType("application/json");
      response.setCharacterEncoding("UTF-8");
      PrintWriter w = response.getWriter();
      ExtendedJSONWriter writer = new ExtendedJSONWriter(w);
      writer.object();
      // pages info
      int messageCount = 0;
      writer.key("items");
      writer.value(255);

      writer.key("results");
      writer.array();
      if (contentList != null) {
        for (int i = 0; i < contentList.size(); i++) {
          Content page = contentList.get(i);

          writer.object();
          writer.key("jcr:path");
          writer.value(
              page.getPath()
                  .replaceFirst(
                      LitePersonalUtils.PATH_AUTHORIZABLE,
                      LitePersonalUtils.PATH_RESOURCE_AUTHORIZABLE));
          for (String messagePropKey : page.getProperties().keySet()) {
            writer.key(messagePropKey);
            writer.value(massageValue(messagePropKey, page.getProperty(messagePropKey)));
          }
          writer.endObject();
          messageCount++;
        }
      }
      writer.endArray();
      writer.key("total");
      writer.value(messageCount);

      writer.endObject();
    } catch (JSONException e) {
      LOG.error("Failed to create proper JSON response in /var/search/page", e);
      response.sendError(
          HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to create proper JSON response.");
    }
  }
  private MultiPartEmail constructMessage(
      Content contentNode,
      List<String> recipients,
      javax.jcr.Session session,
      org.sakaiproject.nakamura.api.lite.Session sparseSession)
      throws EmailDeliveryException, StorageClientException, AccessDeniedException,
          PathNotFoundException, RepositoryException {
    MultiPartEmail email = new MultiPartEmail();
    // TODO: the SAKAI_TO may make no sense in an email context
    // and there does not appear to be any distinction between Bcc and To in java mail.

    Set<String> toRecipients = new HashSet<String>();
    Set<String> bccRecipients = new HashSet<String>();
    for (String r : recipients) {
      bccRecipients.add(convertToEmail(r.trim(), sparseSession));
    }

    if (contentNode.hasProperty(MessageConstants.PROP_SAKAI_TO)) {
      String[] tor =
          StringUtils.split((String) contentNode.getProperty(MessageConstants.PROP_SAKAI_TO), ',');
      for (String r : tor) {
        r = convertToEmail(r.trim(), sparseSession);
        if (bccRecipients.contains(r)) {
          toRecipients.add(r);
          bccRecipients.remove(r);
        }
      }
    }
    for (String r : toRecipients) {
      try {
        email.addTo(convertToEmail(r, sparseSession));
      } catch (EmailException e) {
        throw new EmailDeliveryException(
            "Invalid To Address [" + r + "], message is being dropped :" + e.getMessage(), e);
      }
    }
    for (String r : bccRecipients) {
      try {
        email.addBcc(convertToEmail(r, sparseSession));
      } catch (EmailException e) {
        throw new EmailDeliveryException(
            "Invalid Bcc Address [" + r + "], message is being dropped :" + e.getMessage(), e);
      }
    }

    if (contentNode.hasProperty(MessageConstants.PROP_SAKAI_FROM)) {
      String from = (String) contentNode.getProperty(MessageConstants.PROP_SAKAI_FROM);
      try {
        email.setFrom(convertToEmail(from, sparseSession));
      } catch (EmailException e) {
        throw new EmailDeliveryException(
            "Invalid From Address [" + from + "], message is being dropped :" + e.getMessage(), e);
      }
    } else {
      throw new EmailDeliveryException("Must provide a 'from' address.");
    }

    if (contentNode.hasProperty(MessageConstants.PROP_SAKAI_BODY)) {
      String messageBody = (String) contentNode.getProperty(MessageConstants.PROP_SAKAI_BODY);
      // if this message has a template, use it
      LOGGER.debug(
          "Checking for sakai:templatePath and sakai:templateParams properties on the outgoing message's node.");
      if (contentNode.hasProperty(MessageConstants.PROP_TEMPLATE_PATH)
          && contentNode.hasProperty(MessageConstants.PROP_TEMPLATE_PARAMS)) {
        Map<String, String> parameters =
            getTemplateProperties(
                (String) contentNode.getProperty(MessageConstants.PROP_TEMPLATE_PARAMS));
        String templatePath = (String) contentNode.getProperty(MessageConstants.PROP_TEMPLATE_PATH);
        LOGGER.debug("Got the path '{0}' to the template for this outgoing message.", templatePath);
        Node templateNode = session.getNode(templatePath);
        if (templateNode.hasProperty("sakai:template")) {
          String template = templateNode.getProperty("sakai:template").getString();
          LOGGER.debug("Pulled the template body from the template node: {0}", template);
          messageBody = templateService.evaluateTemplate(parameters, template);
          LOGGER.debug("Performed parameter substitution in the template: {0}", messageBody);
        }
      } else {
        LOGGER.debug(
            "Message node '{0}' does not have sakai:templatePath and sakai:templateParams properties",
            contentNode.getPath());
      }
      try {
        email.setMsg(messageBody);
      } catch (EmailException e) {
        throw new EmailDeliveryException(
            "Invalid Message Body, message is being dropped :" + e.getMessage(), e);
      }
    }

    if (contentNode.hasProperty(MessageConstants.PROP_SAKAI_SUBJECT)) {
      email.setSubject((String) contentNode.getProperty(MessageConstants.PROP_SAKAI_SUBJECT));
    }

    ContentManager contentManager = sparseSession.getContentManager();
    for (String streamId : contentNode.listStreams()) {
      String description = null;
      if (contentNode.hasProperty(
          StorageClientUtils.getAltField(
              MessageConstants.PROP_SAKAI_ATTACHMENT_DESCRIPTION, streamId))) {
        description =
            (String)
                contentNode.getProperty(
                    StorageClientUtils.getAltField(
                        MessageConstants.PROP_SAKAI_ATTACHMENT_DESCRIPTION, streamId));
      }
      LiteEmailDataSource ds = new LiteEmailDataSource(contentManager, contentNode, streamId);
      try {
        email.attach(ds, streamId, description);
      } catch (EmailException e) {
        throw new EmailDeliveryException(
            "Invalid Attachment [" + streamId + "] message is being dropped :" + e.getMessage(), e);
      }
    }
    return email;
  }
  @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);
    }
  }