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; }
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; }
public void writeResult(SlingHttpServletRequest request, JSONWriter write, Result result) throws JSONException { String contentPath = result.getPath(); Session session = StorageClientUtils.adaptToSession( request.getResourceResolver().adaptTo(javax.jcr.Session.class)); try { Content contentResult = session.getContentManager().get(contentPath); if (contentResult != null) { write.object(); writeCanManageProperty(request, write, session, contentResult); writeCommentCountProperty(write, session, contentResult); int depth = SolrSearchUtil.getTraversalDepth(request); ExtendedJSONWriter.writeContentTreeToWriter(write, contentResult, true, depth); write.endObject(); } } catch (AccessDeniedException ade) { // if access is denied we simply won't // write anything for this result // this implies content was private LOGGER.info("Denied {} access to {}", request.getRemoteUser(), contentPath); return; } catch (Exception e) { throw new JSONException(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; }
/** * 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} * * @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); }
@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; }
public void writeResult(SlingHttpServletRequest request, JSONWriter write, Result result) throws JSONException { String contentPath = (String) result.getFirstValue("path"); Session session = StorageClientUtils.adaptToSession( request.getResourceResolver().adaptTo(javax.jcr.Session.class)); try { Content contentResult = session.getContentManager().get(contentPath); if (contentResult != null) { ExtendedJSONWriter.writeContentTreeToWriter(write, contentResult, -1); } else { write.object().endObject(); } } catch (Exception e) { throw new JSONException(e); } }
/** * Same as writeResults logic, but counts number of results iterated over. * * @param request * @param write * @param iterator * @return Set containing all unique paths processed. * @throws JSONException */ public Set<String> writeResultsInternal( SlingHttpServletRequest request, JSONWriter write, Iterator<Result> iterator) throws JSONException { final Set<String> uniquePaths = new HashSet<String>(); final Integer iDepth = (Integer) request.getAttribute("depth"); int depth = 0; if (iDepth != null) { depth = iDepth.intValue(); } try { javax.jcr.Session jcrSession = request.getResourceResolver().adaptTo(javax.jcr.Session.class); final Session session = StorageClientUtils.adaptToSession(jcrSession); while (iterator.hasNext()) { final Result result = iterator.next(); uniquePaths.add(result.getPath()); try { if ("authorizable".equals(result.getFirstValue("resourceType"))) { AuthorizableManager authManager = session.getAuthorizableManager(); Authorizable auth = authManager.findAuthorizable((String) result.getFirstValue("id")); if (auth != null) { write.object(); ValueMap map = profileService.getProfileMap(auth, jcrSession); ExtendedJSONWriter.writeValueMapInternals(write, map); write.endObject(); } } else { String contentPath = result.getPath(); final Content content = session.getContentManager().get(contentPath); if (content != null) { handleContent(content, session, write, depth); } else { LOGGER.debug("Found null content item while writing results [{}]", contentPath); } } } catch (AccessDeniedException e) { // do nothing } catch (RepositoryException e) { throw new JSONException(e); } } } catch (StorageClientException e) { throw new JSONException(e); } return uniquePaths; }
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; }
/** * 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 String convertToEmail(String address, org.sakaiproject.nakamura.api.lite.Session session) throws StorageClientException, AccessDeniedException { if (address.indexOf('@') < 0) { String emailAddress = null; Authorizable user = session.getAuthorizableManager().findAuthorizable(address); if (user != null) { Content profile = session.getContentManager().get(LitePersonalUtils.getProfilePath(user.getId())); if (profile != null) { emailAddress = LitePersonalUtils.getProfilePath(user.getId()); } } if (emailAddress != null && emailAddress.trim().length() > 0) { address = emailAddress; } else { address = address + "@" + smtpServer; } } return address; }
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); }
/** * Process properties to query sparse content directly. * * @param request * @param query * @param asAnon * @return * @throws StorageClientException * @throws AccessDeniedException */ private SolrSearchResultSet processSparseQuery( SlingHttpServletRequest request, Query query, boolean asAnon) throws StorageClientException, AccessDeniedException, ParseException { // 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()); Set<Term> terms = Sets.newHashSet(); luceneQuery.extractTerms(terms); Map<String, Object> props = Maps.newHashMap(); for (Term term : terms) { props.put(term.field(), term.text()); } Session session = StorageClientUtils.adaptToSession( request.getResourceResolver().adaptTo(javax.jcr.Session.class)); ContentManager cm = session.getContentManager(); Iterable<Content> items = cm.find(props); SolrSearchResultSet rs = new SparseSearchResultSet(items); return rs; }
private SolrInputDocument handleMessageIndexing(Map<String, Object> props) throws Exception { String messagePath = "a:user1/messagePath"; Content content = new Content(messagePath, props); Authorizable sender = mock(Authorizable.class); when(sender.getId()).thenReturn("sender"); when(sender.isGroup()).thenReturn(Boolean.FALSE); when(sender.getProperty(eq("firstName"))).thenReturn("test"); when(sender.getProperty(eq("lastName"))).thenReturn("user"); Authorizable user1 = mock(Authorizable.class); when(user1.getId()).thenReturn("user1"); when(user1.isGroup()).thenReturn(Boolean.FALSE); when(user1.getProperty(eq("firstName"))).thenReturn("user"); when(user1.getProperty(eq("lastName"))).thenReturn("one"); when(authorizableManager.findAuthorizable(anyString())).thenReturn(sender); when(repositorySession.adaptTo(Session.class)).thenReturn(session); when(session.getAuthorizableManager()).thenReturn(authorizableManager); when(session.getContentManager()).thenReturn(contentManager); when(contentManager.get(messagePath)).thenReturn(content); MessageIndexingHandler handler = new MessageIndexingHandler(); handler.dateParser = this.dateParser; Event event = new Event("topic", buildEventProperties(messagePath)); Collection<SolrInputDocument> documents = handler.getDocuments(repositorySession, event); assertNotNull(documents); assertTrue(!documents.isEmpty()); Iterator<SolrInputDocument> docIt = documents.iterator(); SolrInputDocument doc = docIt.next(); // test basic message properties assertEquals("test-messagestore", doc.getField("messagestore").getValue()); assertEquals("test-messagebox", doc.getField("messagebox").getValue()); assertEquals("test-type", doc.getField("type").getValue()); assertEquals("test-category", doc.getField("category").getValue()); assertEquals("test-from", doc.getField("from").getValue()); assertEquals("test-to", doc.getField("to").getValue()); assertEquals("test-read", doc.getField("read").getValue()); assertEquals("test-marker", doc.getField("marker").getValue()); assertEquals("test-sendstate", doc.getField("sendstate").getValue()); assertEquals("test-initialpost", doc.getField("initialpost").getValue()); assertEquals("test-title", doc.getField("title").getValue()); assertEquals("test-content", doc.getField("content").getValue()); // ensure unexpected value is skipped assertNull(doc.getField("notindexed")); // test sender name is set assertEquals("test", doc.getField("firstName").getValue()); assertEquals("user", doc.getField("lastName").getValue()); // an additional doc should have been added for authorizable searching: assertTrue(docIt.hasNext()); SolrInputDocument authDoc = docIt.next(); // test values set for user/group searching assertEquals("test-title", authDoc.getField("title").getValue()); assertEquals("test-content", authDoc.getField("content").getValue()); assertEquals("u", authDoc.getField("type").getValue()); assertEquals(content, authDoc.getField(IndexingHandler._DOC_SOURCE_OBJECT).getValue()); assertEquals("user1", authDoc.getField(IndexingHandler.FIELD_PATH).getValue()); assertEquals(messagePath + "-auth", authDoc.getField(IndexingHandler.FIELD_ID).getValue()); assertEquals("user1", authDoc.getField("returnpath").getValue()); assertTrue(!docIt.hasNext()); return doc; }
/** * {@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); } } } } } }
/** * {@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); } }
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); } }
/** * 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()); } }
@Test public void testProperPost() throws ServletException, IOException, RepositoryException, JSONException, AccessDeniedException, StorageClientException { SlingHttpServletRequest request = createMock(SlingHttpServletRequest.class); SlingHttpServletResponse response = createMock(SlingHttpServletResponse.class); javax.jcr.Session jcrSession = Mockito.mock( javax.jcr.Session.class, Mockito.withSettings().extraInterfaces(SessionAdaptable.class)); Session mockSession = mock(Session.class); ContentManager contentManager = mock(ContentManager.class); when(mockSession.getContentManager()).thenReturn(contentManager); Mockito.when(((SessionAdaptable) jcrSession).getSession()).thenReturn(mockSession); ResourceResolver resourceResolver = mock(ResourceResolver.class); Mockito.when(resourceResolver.adaptTo(javax.jcr.Session.class)).thenReturn(jcrSession); expect(request.getResourceResolver()).andReturn(resourceResolver); // Provide parameters String[] dimensions = new String[] {"16x16", "32x32"}; addStringRequestParameter(request, "img", "/~johndoe/people.png"); addStringRequestParameter(request, "save", "/~johndoe/breadcrumbs"); addStringRequestParameter(request, "x", "10"); addStringRequestParameter(request, "y", "10"); addStringRequestParameter(request, "width", "70"); addStringRequestParameter(request, "height", "70"); addStringRequestParameter(request, "dimensions", StringUtils.join(dimensions, 0, ';')); expect(request.getRemoteUser()).andReturn("johndoe"); String imagePath = "a:johndoe/people.png"; when(contentManager.getInputStream(imagePath)) .thenReturn(getClass().getClassLoader().getResourceAsStream("people.png")); when(contentManager.get(anyString())).thenReturn(new Content("foo", null)); SparseContentResource someResource = mock(SparseContentResource.class); when(someResource.adaptTo(Content.class)) .thenReturn( new Content( imagePath, ImmutableMap.of( "mimeType", (Object) "image/png", "_bodyLocation", "2011/lt/zz/x8"))); JackrabbitSession jrSession = mock(JackrabbitSession.class); SparseMapUserManager userManager = mock(SparseMapUserManager.class); when(userManager.getSession()).thenReturn(mockSession); when(jrSession.getUserManager()).thenReturn(userManager); when(resourceResolver.adaptTo(javax.jcr.Session.class)).thenReturn(jrSession); when(resourceResolver.getResource(anyString())).thenReturn(someResource); // Capture output. ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintWriter write = new PrintWriter(baos); response.setContentType("application/json"); response.setCharacterEncoding("UTF-8"); expect(response.getWriter()).andReturn(write); replay(); servlet.doPost(request, response); write.flush(); String s = baos.toString("UTF-8"); JSONObject o = new JSONObject(s); JSONArray files = o.getJSONArray("files"); assertEquals(2, files.length()); for (int i = 0; i < files.length(); i++) { String url = files.getString(i); assertEquals("/~johndoe/breadcrumbs/" + dimensions[i] + "_people.png", url); } }
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); }