MediaHandlerImpl.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.media.impl;
- import java.util.ArrayList;
- import java.util.List;
- import org.apache.sling.api.SlingHttpServletRequest;
- import org.apache.sling.api.adapter.Adaptable;
- import org.apache.sling.api.resource.Resource;
- import org.apache.sling.models.annotations.Model;
- import org.apache.sling.models.annotations.injectorspecific.OSGiService;
- import org.apache.sling.models.annotations.injectorspecific.Self;
- 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.commons.dom.HtmlElement;
- import io.wcm.handler.media.Media;
- import io.wcm.handler.media.MediaArgs;
- import io.wcm.handler.media.MediaBuilder;
- import io.wcm.handler.media.MediaHandler;
- import io.wcm.handler.media.MediaInvalidReason;
- import io.wcm.handler.media.MediaRequest;
- import io.wcm.handler.media.format.MediaFormat;
- import io.wcm.handler.media.format.MediaFormatHandler;
- import io.wcm.handler.media.spi.MediaHandlerConfig;
- import io.wcm.handler.media.spi.MediaMarkupBuilder;
- import io.wcm.handler.media.spi.MediaProcessor;
- import io.wcm.handler.media.spi.MediaSource;
- import io.wcm.sling.commons.adapter.AdaptTo;
- import io.wcm.wcm.commons.component.ComponentPropertyResolverFactory;
- /**
- * Default Implementation of a {@link MediaHandler}.
- */
- @Model(adaptables = {
- SlingHttpServletRequest.class, Resource.class
- }, adapters = MediaHandler.class)
- public final class MediaHandlerImpl implements MediaHandler {
- @Self
- private Adaptable adaptable;
- @Self
- private MediaHandlerConfig mediaHandlerConfig;
- @Self
- private MediaFormatHandler mediaFormatHandler;
- @OSGiService
- private ComponentPropertyResolverFactory componentPropertyResolverFactory;
- private static final Logger log = LoggerFactory.getLogger(MediaHandlerImpl.class);
- @Override
- public @NotNull MediaBuilder get(@Nullable Resource resource) {
- return new MediaBuilderImpl(resource, this, componentPropertyResolverFactory);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable Resource resource, @NotNull MediaArgs mediaArgs) {
- return get(resource).args(mediaArgs);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable Resource resource, MediaFormat @NotNull... mediaFormats) {
- return get(resource).mediaFormats(mediaFormats);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable String mediaRef) {
- return new MediaBuilderImpl(mediaRef, null, this, componentPropertyResolverFactory);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable String mediaRef, @Nullable Resource contextResource) {
- return new MediaBuilderImpl(mediaRef, contextResource, this, componentPropertyResolverFactory);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable String mediaRef, @NotNull MediaArgs mediaArgs) {
- return get(mediaRef).args(mediaArgs);
- }
- @Override
- public @NotNull MediaBuilder get(@Nullable String mediaRef, MediaFormat @NotNull... mediaFormats) {
- return get(mediaRef).mediaFormats(mediaFormats);
- }
- @Override
- public @NotNull MediaBuilder get(@NotNull MediaRequest mediaRequest) {
- return new MediaBuilderImpl(mediaRequest, this);
- }
- /**
- * Resolves the media request
- * @param mediaRequest Media request
- * @return Media metadata (never null)
- */
- @NotNull
- @SuppressWarnings({
- "null", "unused", "java:S2589",
- "java:S3776", "java:S6541", // ignore complexity
- "java:S112", // allow runtime exception
- "java:S1192" // multiple strings
- })
- @SuppressFBWarnings({ "CORRECTNESS", "STYLE" })
- Media processRequest(@NotNull final MediaRequest mediaRequest) {
- // detect media source
- MediaSource mediaSource = null;
- List<Class<? extends MediaSource>> mediaSources = mediaHandlerConfig.getSources();
- if (mediaSources == null || mediaSources.isEmpty()) {
- throw new RuntimeException("No media sources defined.");
- }
- MediaSource firstMediaSource = null;
- for (Class<? extends MediaSource> candidateMediaSourceClass : mediaSources) {
- MediaSource candidateMediaSource = AdaptTo.notNull(adaptable, candidateMediaSourceClass);
- if (candidateMediaSource.accepts(mediaRequest)) {
- mediaSource = candidateMediaSource;
- break;
- }
- else if (firstMediaSource == null) {
- firstMediaSource = candidateMediaSource;
- }
- }
- // if no media source was detected use first media resource defined
- if (mediaSource == null) {
- mediaSource = firstMediaSource;
- }
- Media media = new Media(mediaSource, mediaRequest);
- // resolve media format names to media formats
- MediaFormatResolver mediaFormatResolver = new MediaFormatResolver(mediaFormatHandler);
- if (!mediaFormatResolver.resolve(mediaRequest.getMediaArgs())) {
- media.setMediaInvalidReason(MediaInvalidReason.INVALID_MEDIA_FORMAT);
- return media;
- }
- // if only downloads are accepted prepare media format filter set which only contains download media formats
- if (!resolveDownloadMediaFormats(mediaRequest.getMediaArgs())) {
- media.setMediaInvalidReason(MediaInvalidReason.INVALID_MEDIA_FORMAT);
- return media;
- }
- // apply defaults to media args
- if (mediaRequest.getMediaArgs().getIncludeAssetAemRenditions() == null) {
- mediaRequest.getMediaArgs().includeAssetAemRenditions(mediaHandlerConfig.getIncludeAssetAemRenditionsByDefault());
- }
- if (log.isTraceEnabled()) {
- log.trace("Start processing media request (mediaSource={}): {}", mediaSource.getId(), mediaRequest);
- }
- // preprocess media request before resolving
- List<Class<? extends MediaProcessor>> mediaPreProcessors = mediaHandlerConfig.getPreProcessors();
- if (mediaPreProcessors != null) {
- for (Class<? extends MediaProcessor> processorClass : mediaPreProcessors) {
- log.trace("Apply pre processor ({}): {}", processorClass, mediaRequest);
- MediaProcessor processor = AdaptTo.notNull(adaptable, processorClass);
- media = processor.process(media);
- if (media == null) {
- throw new RuntimeException("MediaPreProcessor '" + processor + "' returned null, request: " + mediaRequest);
- }
- }
- }
- if (media.getMediaInvalidReason() == null) {
- // resolve media request
- media = mediaSource.resolveMedia(media);
- if (media == null) {
- throw new RuntimeException("MediaType '" + mediaSource + "' returned null, request: " + mediaRequest);
- }
- // generate markup (if markup builder is available) - first accepting wins
- List<Class<? extends MediaMarkupBuilder>> mediaMarkupBuilders = mediaHandlerConfig.getMarkupBuilders();
- if (mediaMarkupBuilders != null) {
- media.setElementBuilder(m -> {
- for (Class<? extends MediaMarkupBuilder> mediaMarkupBuilderClass : mediaMarkupBuilders) {
- MediaMarkupBuilder mediaMarkupBuilder = AdaptTo.notNull(adaptable, mediaMarkupBuilderClass);
- if (mediaMarkupBuilder.accepts(m)) {
- log.trace("Apply media markup builder ({}): {}", mediaMarkupBuilderClass, mediaRequest);
- return mediaMarkupBuilder.build(m);
- }
- }
- return null;
- });
- }
- // postprocess media request after resolving
- List<Class<? extends MediaProcessor>> mediaPostProcessors = mediaHandlerConfig.getPostProcessors();
- if (mediaPostProcessors != null) {
- for (Class<? extends MediaProcessor> processorClass : mediaPostProcessors) {
- log.trace("Apply post processor ({}): {}", processorClass, mediaRequest);
- MediaProcessor processor = AdaptTo.notNull(adaptable, processorClass);
- media = processor.process(media);
- if (media == null) {
- throw new RuntimeException("MediaPostProcessor '" + processor + "' returned null, request: " + mediaRequest);
- }
- }
- }
- }
- else {
- log.trace("Skip media resolving because media was set to invalid by prepocessor. reason={}, message={}",
- media.getMediaInvalidReason(), media.getMediaInvalidReasonCustomMessage());
- }
- log.debug("Finished media processing: {}", media);
- return media;
- }
- @Override
- @SuppressWarnings({ "null", "java:S2589" })
- public boolean isValidElement(HtmlElement element) {
- // if it is null it is always invalid
- if (element == null) {
- return false;
- }
- // otherwise check if any media markup builder is available that rates this html element valid
- List<Class<? extends MediaMarkupBuilder>> mediaMarkupBuilders = mediaHandlerConfig.getMarkupBuilders();
- if (mediaMarkupBuilders != null) {
- for (Class<? extends MediaMarkupBuilder> mediaMarkupBuilderClass : mediaMarkupBuilders) {
- MediaMarkupBuilder mediaMarkupBuilder = AdaptTo.notNull(adaptable, mediaMarkupBuilderClass);
- if (mediaMarkupBuilder.isValidMedia(element)) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * If a set of media formats is given it is filtered to contain only download media formats.
- * If no is given a new set of allowed media formats is created by getting from all media formats those marked as
- * "download".
- * If the result is an empty set of media formats (but downloads are requested) resolution is not successful.
- * If the result is an empty set because no media format requests and no download format at all defined, it is
- * successful.
- * @param mediaArgs Media args
- * @return true if resolving was successful
- */
- private boolean resolveDownloadMediaFormats(MediaArgs mediaArgs) {
- if (!mediaArgs.isDownload()) {
- // not filtering for downloads
- return true;
- }
- List<MediaFormat> candidates = new ArrayList<>();
- boolean fallbackToAllMediaFormats = false;
- if (mediaArgs.getMediaFormats() != null) {
- candidates.addAll(List.of(mediaArgs.getMediaFormats()));
- }
- else {
- candidates.addAll(mediaFormatHandler.getMediaFormats());
- fallbackToAllMediaFormats = true;
- }
- MediaFormat[] result = candidates.stream()
- .filter(MediaFormat::isDownload)
- .toArray(size -> new MediaFormat[size]);
- if (result.length > 0) {
- mediaArgs.mediaFormats(result);
- return true;
- }
- else {
- // not successful when an explicit list of media formats was given, and this did not contain any download format
- // successful when no media format was given, and the global list of all formats does not contain any download format
- return fallbackToAllMediaFormats;
- }
- }
- @Override
- @SuppressWarnings("java:S112") // allow runtime exception
- public Media invalid() {
- // build invalid media with first media source
- Class<? extends MediaSource> mediaSourceClass = mediaHandlerConfig.getSources().stream().findFirst().orElse(null);
- if (mediaSourceClass == null) {
- throw new RuntimeException("No media sources defined.");
- }
- MediaSource mediaSource = AdaptTo.notNull(adaptable, mediaSourceClass);
- Media media = new Media(mediaSource, new MediaRequest((String)null, null));
- media.setMediaInvalidReason(MediaInvalidReason.MEDIA_REFERENCE_MISSING);
- return media;
- }
- }