diff --git a/changes.xml b/changes.xml index 7546ec62..a7505f03 100644 --- a/changes.xml +++ b/changes.xml @@ -23,6 +23,12 @@ xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd"> + + + Improve trace logging: Make log messages involving value maps and resource/page objects more compact and better readable. + + + Dynamic Media Support: Append fmt=png-alpha for PNG assets to ensure that the alpha channel is preserved in the Dynamic Media rendition. diff --git a/src/main/java/io/wcm/handler/media/MediaArgs.java b/src/main/java/io/wcm/handler/media/MediaArgs.java index af283690..5957ea0d 100644 --- a/src/main/java/io/wcm/handler/media/MediaArgs.java +++ b/src/main/java/io/wcm/handler/media/MediaArgs.java @@ -37,6 +37,7 @@ import org.osgi.annotation.versioning.ProviderType; import io.wcm.handler.media.format.MediaFormat; +import io.wcm.handler.media.impl.AemObjectsReflectionToStringBuilder; import io.wcm.handler.media.markup.DragDropSupport; import io.wcm.handler.media.markup.IPERatioCustomize; import io.wcm.handler.mediasource.dam.AemRenditionType; @@ -843,7 +844,7 @@ public String toString() { sb.append("webOptimizedImageDeliveryDisabled", webOptimizedImageDeliveryDisabled); } if (properties != null && !properties.isEmpty()) { - sb.append("properties", properties); + sb.append("properties", AemObjectsReflectionToStringBuilder.filteredValueMap(properties)); } return sb.build(); } diff --git a/src/main/java/io/wcm/handler/media/MediaRequest.java b/src/main/java/io/wcm/handler/media/MediaRequest.java index 64a71267..1f0e1da1 100644 --- a/src/main/java/io/wcm/handler/media/MediaRequest.java +++ b/src/main/java/io/wcm/handler/media/MediaRequest.java @@ -21,9 +21,7 @@ import java.util.HashMap; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; -import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ValueMap; import org.apache.sling.api.wrappers.ValueMapDecorator; @@ -31,6 +29,8 @@ import org.jetbrains.annotations.Nullable; import org.osgi.annotation.versioning.ProviderType; +import io.wcm.handler.media.impl.AemObjectsReflectionToStringBuilder; + /** * Holds all properties that are part of a media handling request. */ @@ -119,10 +119,11 @@ public MediaRequest(@Nullable Resource resource, @Nullable String mediaRef, @Nul @Override public String toString() { - ToStringBuilder sb = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + ToStringBuilder sb = new ToStringBuilder(this, + io.wcm.wcm.commons.util.ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE); if (resource != null) { sb.append("resource", resource.getPath()); - sb.append("resourceProperties", "[" + StringUtils.join(resource.getValueMap().entrySet(), ",") + "]"); + sb.append("resourceProperties", AemObjectsReflectionToStringBuilder.filteredValueMap(resource.getValueMap())); } if (mediaRef != null) { sb.append("mediaRef", mediaRef); diff --git a/src/main/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilder.java b/src/main/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilder.java new file mode 100644 index 00000000..16dda12e --- /dev/null +++ b/src/main/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilder.java @@ -0,0 +1,89 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package io.wcm.handler.media.impl; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; + +import com.day.cq.wcm.api.Page; + +/** + * Extends ReflectionToStringBuilder to provide custom handling for AEM-related objects + * (Resource, ValueMap, Page) for a more compact log output. + */ +public class AemObjectsReflectionToStringBuilder extends ReflectionToStringBuilder { + + /** + * @param object Object to output + */ + public AemObjectsReflectionToStringBuilder(Object object) { + super(object); + } + + /** + * @param object Object to output + * @param style Style + */ + public AemObjectsReflectionToStringBuilder(Object object, ToStringStyle style) { + super(object, style); + } + + @Override + protected Object getValue(Field field) throws IllegalAccessException { + if (Resource.class.isAssignableFrom(field.getType())) { + Resource resource = (Resource)field.get(this.getObject()); + if (resource != null) { + return resource.getPath(); + } + } + else if (ValueMap.class.isAssignableFrom(field.getType())) { + ValueMap valueMap = (ValueMap)field.get(this.getObject()); + if (valueMap != null) { + return filteredValueMap(valueMap); + } + } + else if (Page.class.isAssignableFrom(field.getType())) { + Page page = (Page)field.get(this.getObject()); + if (page != null) { + return page.getPath(); + } + } + return super.getValue(field); + } + + /** + * Filter value map to exclude jcr:* properties and null values. + * @param props Value map + * @return Filtered value map, sorted by key + */ + public static Map filteredValueMap(ValueMap props) { + return props.entrySet().stream() + .filter(entry -> !entry.getKey().startsWith("jcr:") && entry.getValue() != null) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (o1, o2) -> o1, TreeMap::new)); + } + +} diff --git a/src/main/java/io/wcm/handler/mediasource/dam/impl/DamAsset.java b/src/main/java/io/wcm/handler/mediasource/dam/impl/DamAsset.java index 430eb5b8..ac5e134e 100644 --- a/src/main/java/io/wcm/handler/mediasource/dam/impl/DamAsset.java +++ b/src/main/java/io/wcm/handler/mediasource/dam/impl/DamAsset.java @@ -21,7 +21,6 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.sling.api.adapter.Adaptable; import org.apache.sling.api.adapter.SlingAdaptable; import org.apache.sling.api.resource.Resource; @@ -41,11 +40,11 @@ import io.wcm.handler.media.Rendition; import io.wcm.handler.media.UriTemplate; import io.wcm.handler.media.UriTemplateType; +import io.wcm.handler.media.impl.AemObjectsReflectionToStringBuilder; import io.wcm.handler.media.spi.MediaHandlerConfig; import io.wcm.handler.mediasource.dam.AssetRendition; import io.wcm.handler.mediasource.dam.impl.dynamicmedia.DynamicMediaSupportService; import io.wcm.handler.mediasource.dam.impl.weboptimized.WebOptimizedImageDeliveryService; -import io.wcm.wcm.commons.util.ToStringStyle; /** * {@link Asset} implementation for DAM assets. @@ -225,7 +224,8 @@ public AdapterType adaptTo(Class type) { @Override public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE); + return new AemObjectsReflectionToStringBuilder(this, + io.wcm.wcm.commons.util.ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE).build(); } } diff --git a/src/main/java/io/wcm/handler/mediasource/inline/InlineAsset.java b/src/main/java/io/wcm/handler/mediasource/inline/InlineAsset.java index 3a8ffa28..96febe41 100644 --- a/src/main/java/io/wcm/handler/mediasource/inline/InlineAsset.java +++ b/src/main/java/io/wcm/handler/mediasource/inline/InlineAsset.java @@ -21,7 +21,6 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.sling.api.adapter.Adaptable; import org.apache.sling.api.adapter.SlingAdaptable; import org.apache.sling.api.resource.Resource; @@ -36,8 +35,8 @@ import io.wcm.handler.media.Rendition; import io.wcm.handler.media.UriTemplate; import io.wcm.handler.media.UriTemplateType; +import io.wcm.handler.media.impl.AemObjectsReflectionToStringBuilder; import io.wcm.handler.media.spi.MediaHandlerConfig; -import io.wcm.wcm.commons.util.ToStringStyle; /** * {@link Asset} implementation for inline media objects stored in a node in a content page. @@ -169,7 +168,8 @@ public AdapterType adaptTo(Class type) { @Override public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE); + return new AemObjectsReflectionToStringBuilder(this, + io.wcm.wcm.commons.util.ToStringStyle.SHORT_PREFIX_OMIT_NULL_STYLE).build(); } } diff --git a/src/main/java/io/wcm/handler/mediasource/ngdm/NextGenDynamicMediaAsset.java b/src/main/java/io/wcm/handler/mediasource/ngdm/NextGenDynamicMediaAsset.java index 4330065b..7283c0f5 100644 --- a/src/main/java/io/wcm/handler/mediasource/ngdm/NextGenDynamicMediaAsset.java +++ b/src/main/java/io/wcm/handler/mediasource/ngdm/NextGenDynamicMediaAsset.java @@ -19,6 +19,7 @@ */ package io.wcm.handler.mediasource.ngdm; +import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.resource.ValueMap; import org.jetbrains.annotations.NotNull; @@ -119,4 +120,12 @@ final class NextGenDynamicMediaAsset implements Asset { return null; } + @Override + public String toString() { + return new ToStringBuilder(this) + .append("reference", context.getReference()) + .append("metadata", context.getMetadata()) + .toString(); + } + } diff --git a/src/test/java/io/wcm/handler/media/MediaRequestTest.java b/src/test/java/io/wcm/handler/media/MediaRequestTest.java index 4a9e387b..68d43b82 100644 --- a/src/test/java/io/wcm/handler/media/MediaRequestTest.java +++ b/src/test/java/io/wcm/handler/media/MediaRequestTest.java @@ -23,13 +23,27 @@ import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import io.wcm.handler.media.testcontext.AppAemContext; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; + +@ExtendWith(AemContextExtension.class) class MediaRequestTest { + private final AemContext context = AppAemContext.newAemContext(); + @Test void testToString() { MediaRequest request = new MediaRequest("/path", null); assertTrue(StringUtils.contains(request.toString(), "/path")); } + @Test + void testToString_Resource() { + MediaRequest request = new MediaRequest(context.create().resource("/content/test"), null); + assertTrue(StringUtils.contains(request.toString(), "/content/test")); + } + } diff --git a/src/test/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilderTest.java b/src/test/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilderTest.java new file mode 100644 index 00000000..e902f4a1 --- /dev/null +++ b/src/test/java/io/wcm/handler/media/impl/AemObjectsReflectionToStringBuilderTest.java @@ -0,0 +1,94 @@ +/* + * #%L + * wcm.io + * %% + * Copyright (C) 2024 wcm.io + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package io.wcm.handler.media.impl; + +import static com.day.cq.commons.jcr.JcrConstants.JCR_CREATED; +import static com.day.cq.commons.jcr.JcrConstants.JCR_PRIMARYTYPE; +import static com.day.cq.commons.jcr.JcrConstants.NT_UNSTRUCTURED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.util.Date; +import java.util.Map; + +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import com.day.cq.wcm.api.Page; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.wcm.handler.media.testcontext.AppAemContext; +import io.wcm.sling.commons.resource.ImmutableValueMap; +import io.wcm.testing.mock.aem.junit5.AemContext; +import io.wcm.testing.mock.aem.junit5.AemContextExtension; + +@ExtendWith(AemContextExtension.class) +class AemObjectsReflectionToStringBuilderTest { + + private final AemContext context = AppAemContext.newAemContext(); + + @SuppressWarnings("null") + private static final ValueMap VALUEMAP_SAMPLE = ImmutableValueMap.of( + "prop1", "value1", + JCR_CREATED, new Date(), + JCR_PRIMARYTYPE, NT_UNSTRUCTURED, + "prop2", 5, + "prop3", null); + + @Test + void testBuild() { + ClassWithFields obj = new ClassWithFields(); + obj.prop1 = "value1"; + obj.resource = context.create().resource("/content/resource1", + "prop2", "value2"); + obj.page = context.create().page("/content/page1"); + obj.props = VALUEMAP_SAMPLE; + + assertEquals("[page=/content/page1,prop1=value1,props={prop1=value1, prop2=5},resource=/content/resource1]", + new AemObjectsReflectionToStringBuilder(obj, ToStringStyle.NO_CLASS_NAME_STYLE).build()); + } + + @Test + void testBuild_NullObjects() { + ClassWithFields obj = new ClassWithFields(); + + assertNotNull(new AemObjectsReflectionToStringBuilder(obj).build()); + } + + @Test + void testFilteredValueMap() { + Map filtered = AemObjectsReflectionToStringBuilder.filteredValueMap(VALUEMAP_SAMPLE); + + assertEquals(Map.of("prop1", "value1", "prop2", 5), filtered); + } + + @SuppressWarnings("unused") + @SuppressFBWarnings("URF_UNREAD_FIELD") + private static final class ClassWithFields { + String prop1; + Resource resource; + Page page; + ValueMap props; + } + +}