DamRendition.java
/*
* #%L
* wcm.io
* %%
* Copyright (C) 2014 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.mediasource.dam.impl;
import java.util.Date;
import java.util.List;
import org.apache.commons.io.FilenameUtils;
import org.apache.sling.api.adapter.SlingAdaptable;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.wcm.handler.media.CropDimension;
import io.wcm.handler.media.MediaArgs;
import io.wcm.handler.media.MediaFileType;
import io.wcm.handler.media.Rendition;
import io.wcm.handler.media.UriTemplate;
import io.wcm.handler.media.UriTemplateType;
import io.wcm.handler.media.format.MediaFormat;
import io.wcm.handler.url.UrlHandler;
import io.wcm.sling.commons.adapter.AdaptTo;
import io.wcm.wcm.commons.caching.ModificationDate;
/**
* {@link Rendition} implementation for DAM asset renditions.
*/
class DamRendition extends SlingAdaptable implements Rendition {
private final DamContext damContext;
private final MediaArgs mediaArgs;
private final RenditionMetadata rendition;
private boolean fallback;
private static final Logger log = LoggerFactory.getLogger(DamRendition.class);
/**
* @param cropDimension Crop dimension
* @param mediaArgs Media args
* @param damContext DAM context objects
*/
@SuppressWarnings("java:S3776") // ignore complexity
DamRendition(CropDimension cropDimension, Integer rotation, MediaArgs mediaArgs, DamContext damContext) {
this.damContext = damContext;
this.mediaArgs = mediaArgs;
RenditionMetadata resolvedRendition = null;
// if no transformation parameters are given find non-transformed matching rendition
if (cropDimension == null && rotation == null) {
RenditionHandler renditionHandler = new DefaultRenditionHandler(damContext);
resolvedRendition = renditionHandler.getRendition(mediaArgs);
}
else {
// try to match with all transformations that are configured
RenditionHandler renditionHandler = new TransformedRenditionHandler(cropDimension, rotation, damContext);
resolvedRendition = renditionHandler.getRendition(mediaArgs);
// if no match was found check against renditions without applying the explicit cropping
if (resolvedRendition == null && cropDimension != null) {
if (rotation != null) {
renditionHandler = new TransformedRenditionHandler(null, rotation, damContext);
resolvedRendition = renditionHandler.getRendition(mediaArgs);
}
else {
renditionHandler = new DefaultRenditionHandler(damContext);
resolvedRendition = renditionHandler.getRendition(mediaArgs);
}
if (resolvedRendition != null) {
fallback = true;
}
}
}
// if no match was found and auto-cropping is enabled, try to build a transformed rendition
// with automatically devised cropping parameters
if (resolvedRendition == null && mediaArgs.isAutoCrop()) {
DamAutoCropping autoCropping = new DamAutoCropping(damContext, mediaArgs);
List<CropDimension> autoCropDimensions = autoCropping.calculateAutoCropDimensions();
for (CropDimension autoCropDimension : autoCropDimensions) {
RenditionHandler renditionHandler = new TransformedRenditionHandler(autoCropDimension, rotation, damContext);
resolvedRendition = renditionHandler.getRendition(mediaArgs);
if (resolvedRendition != null) {
break;
}
}
}
if (log.isTraceEnabled()) {
log.trace("DamRendition: resolvedRendition={}, mediaArgs={}, cropDimension={}, rotation={}",
resolvedRendition, mediaArgs, cropDimension, rotation);
}
this.rendition = resolvedRendition;
}
@Override
public String getUrl() {
if (rendition == null) {
return null;
}
String url = null;
// check for dynamic media support
if (damContext.isDynamicMediaEnabled()) {
if (damContext.isDynamicMediaAsset()) {
url = buildDynamicMediaUrl();
if (url == null) {
// asset is valid DM asset, but no valid rendition could be generated
// reason might be that the smart-cropped rendition was too small for the requested size
return null;
}
}
else {
// DM is enabled, but given asset is not a DM asset
if (damContext.isDynamicMediaAemFallbackDisabled()) {
log.warn("Asset is not a valid DM asset, fallback disabled, rendition invalid: {}", rendition.getRendition().getPath());
return null;
}
else {
log.trace("Asset is not a valid DM asset, fallback to AEM-rendered rendition: {}", rendition.getRendition().getPath());
}
}
}
// check for web-optimized image delivery
if (url == null) {
url = buildWebOptimizedImageDeliveryUrl();
}
// Fallback: Render renditions in AEM - build externalized URL
if (url == null) {
UrlHandler urlHandler = AdaptTo.notNull(damContext, UrlHandler.class);
String mediaPath = rendition.getMediaPath(mediaArgs.isContentDispositionAttachment());
url = urlHandler.get(mediaPath).urlMode(mediaArgs.getUrlMode())
.buildExternalResourceUrl(rendition.adaptTo(Resource.class));
}
return url;
}
/**
* Build DM URL for this rendition based on the calculated DM path and the configured DM hostname.
* @return DM URL or null if either DM path or configured DM hostname is null
*/
private @Nullable String buildDynamicMediaUrl() {
String dynamicMediaPath = rendition.getDynamicMediaPath(mediaArgs.isContentDispositionAttachment(), damContext);
String productionAssetUrl = damContext.getDynamicMediaServerUrl();
if (dynamicMediaPath != null && productionAssetUrl != null) {
return productionAssetUrl + dynamicMediaPath;
}
else {
return null;
}
}
/**
* Build web-optimized image delivery URL if this is a raster image.
* @return URL or null
*/
private @Nullable String buildWebOptimizedImageDeliveryUrl() {
if (MediaFileType.isImage(getFileExtension())
&& !MediaFileType.isVectorImage(getFileExtension())
&& !mediaArgs.isContentDispositionAttachment()
&& !mediaArgs.isWebOptimizedImageDeliveryDisabled()) {
return rendition.getWebOptimizedImageDeliveryPath(damContext);
}
else {
return null;
}
}
@Override
public String getPath() {
if (this.rendition != null) {
return this.rendition.getRendition().getPath();
}
else {
return null;
}
}
@Override
public String getFileName() {
if (this.rendition != null) {
return this.rendition.getFileName(this.mediaArgs.isContentDispositionAttachment());
}
else {
return null;
}
}
@Override
public String getFileExtension() {
return FilenameUtils.getExtension(getFileName());
}
@Override
public long getFileSize() {
if (this.rendition != null) {
return this.rendition.getFileSize();
}
else {
return 0L;
}
}
@Override
public String getMimeType() {
if (this.rendition != null) {
return this.rendition.getMimeType();
}
else {
return null;
}
}
@Override
public Date getModificationDate() {
if (this.rendition != null) {
return ModificationDate.get(this.rendition.getRendition().adaptTo(Resource.class));
}
else {
return null;
}
}
@Override
public MediaFormat getMediaFormat() {
if (this.rendition != null) {
return this.rendition.getMediaFormat();
}
else {
return null;
}
}
@Override
@SuppressWarnings("null")
@SuppressFBWarnings("NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE")
public @NotNull ValueMap getProperties() {
if (this.rendition != null) {
return this.rendition.getRendition().adaptTo(Resource.class).getValueMap();
}
else {
return ValueMap.EMPTY;
}
}
@Override
public boolean isImage() {
return MediaFileType.isImage(getFileExtension());
}
@Override
public boolean isBrowserImage() {
return MediaFileType.isBrowserImage(getFileExtension());
}
@Override
public boolean isVectorImage() {
return MediaFileType.isVectorImage(getFileExtension());
}
@Override
public boolean isDownload() {
return !isImage();
}
@Override
public long getWidth() {
if (this.rendition != null) {
return this.rendition.getWidth();
}
else {
return 0;
}
}
@Override
public long getHeight() {
if (this.rendition != null) {
return this.rendition.getHeight();
}
else {
return 0;
}
}
@Override
public boolean isFallback() {
return fallback;
}
@Override
public @NotNull UriTemplate getUriTemplate(@NotNull UriTemplateType type) {
if (this.rendition == null) {
throw new IllegalStateException("Rendition is not valid.");
}
if (type == UriTemplateType.CROP_CENTER) {
throw new IllegalArgumentException("CROP_CENTER not supported for rendition URI templates.");
}
return this.rendition.getUriTemplate(type, damContext);
}
@Override
@SuppressWarnings("null")
public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
if (this.rendition != null) {
AdapterType result = this.rendition.adaptTo(type);
if (result != null) {
return result;
}
}
return super.adaptTo(type);
}
@Override
public String toString() {
if (rendition != null) {
return rendition.toString();
}
return super.toString();
}
}