diff --git a/bundles/core/pom.xml b/bundles/core/pom.xml index 8198158676..a6eb3fd450 100644 --- a/bundles/core/pom.xml +++ b/bundles/core/pom.xml @@ -371,6 +371,10 @@ commons-collections commons-collections + + org.apache.commons + commons-collections4 + org.slf4j slf4j-api diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/Utils.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/Utils.java index 4a3a04723a..408ffc5357 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/Utils.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/Utils.java @@ -15,6 +15,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package com.adobe.cq.wcm.core.components.internal; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -54,7 +55,6 @@ import com.day.cq.wcm.api.designer.Designer; import com.day.cq.wcm.api.designer.Style; import com.day.cq.wcm.foundation.AllowedComponentList; -import com.google.common.collect.ImmutableSet; import static com.adobe.cq.wcm.core.components.models.Image.PN_ALT_VALUE_FROM_DAM; import static com.adobe.cq.wcm.core.components.models.Image.PN_ALT_VALUE_FROM_PAGE_IMAGE; @@ -65,12 +65,12 @@ public class Utils { private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); - private static final Set INTERNAL_PARAMETER = ImmutableSet.of( - ":formstart", - "_charset_", - ":redirect", - ":cq_csrf_token" - ); + private static final Set INTERNAL_PARAMETER = Collections.unmodifiableSet(new HashSet<>(Arrays.asList( + ":formstart", + "_charset_", + ":redirect", + ":cq_csrf_token" + ))); private Utils() { } diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/helper/image/AdaptiveImageHelper.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/helper/image/AdaptiveImageHelper.java index a5930e5086..cdd3e60c97 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/helper/image/AdaptiveImageHelper.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/helper/image/AdaptiveImageHelper.java @@ -32,7 +32,6 @@ import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.PageManager; -import com.google.common.base.Joiner; public class AdaptiveImageHelper { @@ -124,12 +123,12 @@ public static String getRedirectLocation(SlingHttpServletRequest request, long l } if (lastModifiedSuffix > 0) { suffix = StringUtils.replace(suffix, String.valueOf(lastModifiedSuffix), String.valueOf(lastModifiedEpoch)); - redirectLocation = Joiner.on('.').join(Text.escapePath(request.getContextPath() + requestPathInfo.getResourcePath()), - requestPathInfo.getSelectorString(), requestPathInfo.getExtension() + Text.escapePath(suffix)); + redirectLocation = Text.escapePath(request.getContextPath() + requestPathInfo.getResourcePath()) + "." + + requestPathInfo.getSelectorString() + "." + requestPathInfo.getExtension() + Text.escapePath(suffix); } else if (request.getResource().isResourceType(IMAGE_RESOURCE_TYPE)) { - redirectLocation = Joiner.on('.').join(Text.escapePath(request.getContextPath() + requestPathInfo.getResourcePath()), - requestPathInfo.getSelectorString(), requestPathInfo.getExtension() + "/" + lastModifiedEpoch, - requestPathInfo.getExtension()); + redirectLocation = Text.escapePath(request.getContextPath() + requestPathInfo.getResourcePath()) + "." + + requestPathInfo.getSelectorString() + "." + requestPathInfo.getExtension() + "/" + + lastModifiedEpoch + "." + requestPathInfo.getExtension(); } else { String resourcePath = request.getPathInfo(); String extension = FilenameUtils.getExtension(resourcePath); diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/jackson/LinkHtmlAttributesSerializer.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/jackson/LinkHtmlAttributesSerializer.java index 0c8255d881..4ed9afb6b5 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/jackson/LinkHtmlAttributesSerializer.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/jackson/LinkHtmlAttributesSerializer.java @@ -17,6 +17,7 @@ package com.adobe.cq.wcm.core.components.internal.jackson; import java.io.IOException; +import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -26,14 +27,13 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.ser.std.StdSerializer; -import com.google.common.collect.ImmutableSet; public class LinkHtmlAttributesSerializer extends StdSerializer> { /** * List of the link's ignored html attributes from the Json export. */ - private static final Set IGNORED_HTML_ATTRIBUTES = ImmutableSet.of("href"); + private static final Set IGNORED_HTML_ATTRIBUTES = Collections.singleton("href"); public LinkHtmlAttributesSerializer() { this(null); } diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkImpl.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkImpl.java index 55c0a824b1..aa01ad790b 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkImpl.java @@ -15,6 +15,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ package com.adobe.cq.wcm.core.components.internal.link; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; @@ -32,7 +33,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.google.common.collect.ImmutableMap; /** * Wraps link information to be used in models. @@ -148,7 +148,7 @@ private static Map buildHtmlAttributes(String linkURL, Map_self is used in the edit dialog but not listed as allowed here as we do not * want to render a target attribute at all when _self is selected. */ - public static final Set VALID_LINK_TARGETS = ImmutableSet.of("_blank", "_parent", "_top"); + public static final Set VALID_LINK_TARGETS = SetUtils.unmodifiableSet("_blank", "_parent", "_top"); /** * Name of the resource property that for redirecting pages will indicate if original page or redirect target page should be returned. diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkUtil.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkUtil.java index 4743c05fa9..82804cc31a 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkUtil.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/link/LinkUtil.java @@ -23,6 +23,7 @@ import java.security.SecureRandom; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -30,6 +31,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; + +import org.apache.commons.codec.net.URLCodec; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.commons.lang3.StringUtils; @@ -58,6 +61,27 @@ public class LinkUtil { .collect(Collectors.toList())); } + //SITES-18137: imitate the exact behavior of com.google.common.net.URL_FRAGMENT_ESCAPER + private static final BitSet URL_FRAGMENT_SAFE_CHARS = new BitSet(256); + + static { + //alphanumeric characters + for(int i = 97; i <= 122; i++) { + URL_FRAGMENT_SAFE_CHARS.set(i); + } + for(int i = 65; i <= 90; i++) { + URL_FRAGMENT_SAFE_CHARS.set(i); + } + for(int i = 48; i <= 57; i++) { + URL_FRAGMENT_SAFE_CHARS.set(i); + } + // safe characters from Guava's URL_FRAGMENT_ESCAPER: -._~!$'()*,;&=@:+/? + byte[] nonAlphaNumeric = new byte[] { 45, 46, 95, 126, 33, 36, 39, 40, 41, 42, 44, 59, 38, 61, 64, 58, 43, 47, 63 }; + for(int i = 0; i < nonAlphaNumeric.length; i++) { + URL_FRAGMENT_SAFE_CHARS.set(nonAlphaNumeric[i]); + } + } + /** * Decodes and encoded or escaped URL taking care to not break Adobe Campaign expressions @@ -138,6 +162,20 @@ public static String escape(final String path, final String queryString, final S return unmasked; } + /** + * Escapes an URI fragment, imitating the exact behavior of com.google.common.net.URL_FRAGMENT_ESCAPER. + * (SITES-18137) + * + * @param fragment The URI fragment + * @return The escaped fragment + */ + public static String escapeFragment(final String fragment) { + if (fragment == null) { + return null; + } + return new String(URLCodec.encodeUrl(URL_FRAGMENT_SAFE_CHARS, fragment.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8); + } + /** * Masks a given {@link String} by replacing all occurrences of {@link LinkUtil#PATTERNS} with a placeholder. * The generated placeholders are put into the given {@link Map} and can be used to unmask a {@link String} later on. diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v1/embeddable/YouTubeImpl.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v1/embeddable/YouTubeImpl.java index 5792751d94..d413e3b579 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v1/embeddable/YouTubeImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v1/embeddable/YouTubeImpl.java @@ -17,6 +17,7 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.Collections; import java.util.Optional; import org.apache.commons.lang3.StringUtils; @@ -45,7 +46,6 @@ import com.day.cq.wcm.api.policies.ContentPolicy; import com.day.cq.wcm.api.policies.ContentPolicyManager; import com.day.cq.wcm.commons.policy.ContentPolicyStyle; -import com.google.common.collect.ImmutableMap; @Model( adaptables = SlingHttpServletRequest.class, @@ -202,7 +202,7 @@ Style getStyleForWrappedResource(Resource resource) { @NotNull protected EmbeddableData getComponentData() { return DataLayerBuilder.extending(super.getComponentData()).asEmbeddable() - .withEmbeddableDetails(() -> ImmutableMap.of(PN_VIDEO_ID, videoId)) + .withEmbeddableDetails(() -> Collections.singletonMap(PN_VIDEO_ID, videoId)) .build(); } diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v2/ImageImpl.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v2/ImageImpl.java old mode 100644 new mode 100755 index dd2534d7a0..83d970e7ef --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v2/ImageImpl.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/models/v2/ImageImpl.java @@ -40,6 +40,7 @@ import com.adobe.cq.wcm.core.components.internal.helper.image.AssetDeliveryHelper; import com.adobe.cq.wcm.core.components.internal.models.v1.ImageAreaImpl; import com.adobe.cq.wcm.core.components.internal.servlets.AdaptiveImageServlet; +import com.adobe.cq.wcm.core.components.internal.link.LinkUtil; import com.adobe.cq.wcm.core.components.models.Image; import com.adobe.cq.wcm.core.components.models.ImageArea; import com.day.cq.dam.api.Asset; @@ -47,7 +48,6 @@ import com.day.cq.dam.scene7.api.constants.Scene7AssetType; import com.day.cq.dam.scene7.api.constants.Scene7Constants; import com.fasterxml.jackson.annotation.JsonInclude; -import com.google.common.net.UrlEscapers; /** * V2 Image model implementation. @@ -187,7 +187,7 @@ protected void initModel() { //check DM asset - check for "dam:scene7File" metadata value String dmAssetName = asset.getMetadataValue(Scene7Constants.PN_S7_FILE); if(isDmFeaturesEnabled && (!StringUtils.isEmpty(dmAssetName))){ - dmAssetName = UrlEscapers.urlFragmentEscaper().escape(dmAssetName); + dmAssetName = LinkUtil.escapeFragment(dmAssetName); //image is DM dmImage = true; useAssetDelivery = false; diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/services/seo/LanguageNavigationSiteRootSelectionStrategy.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/services/seo/LanguageNavigationSiteRootSelectionStrategy.java index b5cf9a8aad..c9ced624a6 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/services/seo/LanguageNavigationSiteRootSelectionStrategy.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/services/seo/LanguageNavigationSiteRootSelectionStrategy.java @@ -16,7 +16,7 @@ package com.adobe.cq.wcm.core.components.internal.services.seo; import java.util.Optional; -import java.util.concurrent.ExecutionException; +import java.util.WeakHashMap; import java.util.function.Predicate; import org.apache.sling.api.resource.Resource; @@ -39,8 +39,6 @@ import com.day.cq.wcm.api.Page; import com.day.cq.wcm.api.TemplatedResource; import com.day.cq.wcm.msm.api.LiveRelationshipManager; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; /** * An implementation of {@link SiteRootSelectionStrategy} that looks for a language navigation component on the given page and uses it's @@ -79,7 +77,7 @@ public class LanguageNavigationSiteRootSelectionStrategy implements SiteRootSele * meaning a consumer will call the public methods of this interface close after each other passing the same {@link Page} object to each * method call. In this case, we don't need to traverse multiple times. */ - private final Cache> languageNavigationCache = CacheBuilder.newBuilder().weakKeys().build(); + private final WeakHashMap> languageNavigationCache = new WeakHashMap<>(); @Override @Nullable @@ -98,12 +96,15 @@ public int getStructuralDepth(@NotNull Page page) { } private Optional findLanguageNavigation(Page page) { - try { - return languageNavigationCache.get(page, () -> findLanguageNavigation(page.getContentResource(), page)); - } catch (ExecutionException ex) { - LOG.warn("Failed to find language navigation", ex); - return Optional.empty(); + Optional resource = languageNavigationCache.get(page); + if (resource == null) { + resource = findLanguageNavigation(page.getContentResource(), page); + if (resource == null || !resource.isPresent()) { + resource = Optional.empty(); + } + languageNavigationCache.put(page, resource); } + return resource; } private Optional findLanguageNavigation(Resource contentResource, Page containingPage) { diff --git a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/servlets/AdaptiveImageServlet.java b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/servlets/AdaptiveImageServlet.java index 6f2ad54ac8..eafb52c16e 100644 --- a/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/servlets/AdaptiveImageServlet.java +++ b/bundles/core/src/main/java/com/adobe/cq/wcm/core/components/internal/servlets/AdaptiveImageServlet.java @@ -75,9 +75,6 @@ import com.day.cq.wcm.commons.WCMUtils; import com.day.cq.wcm.foundation.WCMRenditionPicker; import com.day.image.Layer; -import com.google.common.base.Splitter; -import com.google.common.collect.Lists; -import com.google.common.net.HttpHeaders; import static com.adobe.cq.wcm.core.components.internal.Utils.getWrappedImageResourceWithInheritance; import static com.adobe.cq.wcm.core.components.internal.helper.image.AdaptiveImageHelper.IMAGE_RESOURCE_TYPE; @@ -656,7 +653,7 @@ private void stream(@NotNull SlingHttpServletResponse response, @NotNull InputSt response.setContentType(contentType); String extension = mimeTypeService.getExtension(contentType); String disposition = "svg".equalsIgnoreCase(extension) ? "attachment" : "inline"; - response.setHeader(HttpHeaders.CONTENT_DISPOSITION, disposition + "; filename=" + URLEncoder.encode(imageName, CharEncoding.UTF_8)); + response.setHeader("Content-Disposition", disposition + "; filename=" + URLEncoder.encode(imageName, CharEncoding.UTF_8)); IOUtils.copy(inputStream, response.getOutputStream()); } @@ -852,7 +849,13 @@ private List selectorToList(String selector) throws IllegalArgumentExcep if (StringUtils.isEmpty(selector)) { throw new IllegalArgumentException("Expected 1, 2 or 3 selectors instead got empty selector"); } - ArrayList selectorList = Lists.newArrayList(Splitter.on('.').omitEmptyStrings().trimResults().split(selector)); + ArrayList selectorList = new ArrayList<>(); + for (String s : selector.split("\\.")) { + String trimmed = s.trim(); + if (!trimmed.isEmpty()) { + selectorList.add(trimmed); + } + } if (selectorList.size() > 3) { throw new IllegalArgumentException("Expected 1, 2 or 3 selectors, instead got: " + selectorList.size()); } diff --git a/parent/pom.xml b/parent/pom.xml index 0ff175b49f..dcbf1672a8 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -726,6 +726,13 @@ provided + + org.apache.commons + commons-collections4 + 4.4 + provided + + org.jetbrains annotations