com.adobe.cq.social.srp.internal.SocialProviderUtils Maven / Gradle / Ivy
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
* Copyright 2014 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.adobe.cq.social.srp.internal;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Various utilities useful to provider impls.
*/
public final class SocialProviderUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(SocialProviderUtils.class);
/* dot (.) sling selector resources that we don't need AS to resolve since they won't exist */
private static final String IGNORE_DOT_SUFFIX =
"html|json|form|feed|deletecomment|editcomment|createcomment|composer|social|socialuserstatetoggle";
/* known resource suffix that won't resolve */
private static final String IGNORE_SLASH_SUFFIX = "editcomment|editforum";
private static final String PAGING_PATTERN = "(\\.social(\\.[0-9]+){1,2})" // .social.number.number
+ "|" // OR
+ "(\\.social.\\$\\{startIndex}.*)"; // .social.${startIndex}
// SLING dot selectors
// ?i - ignore case in match
// \\. - follow by a dot '.'
private static final String DOT_PATTERN = "(\\.(?i)(" + IGNORE_DOT_SUFFIX + "))";
// known path suffix such as /editforum
private static final String SLASH_PATTERN = "(\\/(?i)(" + IGNORE_SLASH_SUFFIX + "))";
// [^\\s] - must start with one or more characters except white space
// list of SLING selectors OR known suffix to ignore
// $ - end of the string
private static final String SUFFIX_PATTERN = "([^\\s]+(" + DOT_PATTERN + "|" + SLASH_PATTERN + "|"
+ PAGING_PATTERN + ")$)";
private static final Pattern PATTERN = Pattern.compile(SUFFIX_PATTERN);
private SocialProviderUtils() {
}
/**
* Figure out if the path is one that we will never store in the cloud.
* @param path the path to check
* @return true iff the path will never be stored in the cloud.
*/
public static boolean isExtraneousSlingPath(final String path) {
return isExtraneousSlingPath(null, null, path);
}
/**
* Figure out if the path is one that we will never store in the cloud. Special case for paths ending with
* ".social.json" since those are POST requests which go directly to Sling and Sling does a resolve on the
* resource path.
* @param resolver resolver to use for pre-fetch resources
* @param srp SocialResourceProvider to batch reading
* @param path the path to check
* @return true iff the path will never be stored in the cloud.
*/
public static boolean isExtraneousSlingPath(final ResourceResolver resolver, final CachingResourceProvider srp,
final String path) {
// There are paths that sling asks us to resolve that will never resolve. Don't
// take the hit of asking Adobe Cloud Storage.
// Anything where jcr:content has been replaced with _jcr_content isn't going to be found.
if (path.contains("/_jcr_content/")) {
return true;
}
// Any directory requests isn't going to be found.
if (path.endsWith("/")) {
return true;
}
if (resolver != null && srp != null && StringUtils.endsWithIgnoreCase(path, ".social.json")) {
batchResolve(resolver, srp, StringUtils.removeEndIgnoreCase(path, ".social.json"));
return true;
}
// skip selectors and known suffix.
final Matcher matcher = PATTERN.matcher(path);
if (matcher.matches()) {
LOGGER.debug("Ignoring extraneous path: {}", path);
return true;
}
return false;
}
/**
* Convenient method to "convert" a resolve() to a batch read. Not as efficient as a real resolve() since it will
* read all the "nodes" in the path up to the ASI Root.
* @param resolver resolver for the batch read
* @param srp SocialResourceProvider for getResources()
* @param path The full path will ".social.json" already stripped from the end
*/
private static void batchResolve(final ResourceResolver resolver, final CachingResourceProvider srp,
final String path) {
final List resourcePaths = new ArrayList();
// the component doing the post, voting, following, etc.
final String component = StringUtils.substringAfterLast(path, "/");
final String prefix = StringUtils.substringBeforeLast(path, "/");
final StringBuilder sb = new StringBuilder(srp.getASIPath());
final StringTokenizer tokener =
new StringTokenizer(StringUtils.removeStartIgnoreCase(prefix, srp.getASIPath()), "/");
while (tokener.hasMoreTokens()) {
sb.append('/');
sb.append(tokener.nextToken());
resourcePaths.add(sb.toString());
}
// add the voting node and voting/userid node if it's a voting operation
if ("voting".equalsIgnoreCase(component)) {
sb.append("/voting");
resourcePaths.add(sb.toString());
sb.append("/");
sb.append(resolver.getUserID());
resourcePaths.add(sb.toString());
}
// add socialgraph path if it's a follow operation
if ("socialgraph".equalsIgnoreCase(component)) {
sb.append("/socialgraph");
resourcePaths.add(sb.toString());
} else {
// we may as well grab what we're originally be asked for (minus selectors) while we are here. it's the
// next thing we're asked for
final String resourceName = StringUtils.substringBefore(component, ".");
sb.append("/");
sb.append(resourceName);
resourcePaths.add(sb.toString());
}
if (!resourcePaths.isEmpty()) {
srp.getResources(resolver, resourcePaths);
}
}
}