io.wcm.handler.mediasource.dam.impl.DamRenditionMetadataService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of io.wcm.handler.media Show documentation
Show all versions of io.wcm.handler.media Show documentation
Media resolving, processing and markup generation.
/*
* #%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 io.wcm.wcm.commons.contenttype.FileExtension;
import io.wcm.wcm.commons.util.RunMode;
import java.util.EnumSet;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.util.Text;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.dam.api.Asset;
import com.day.cq.dam.api.DamEvent;
import com.day.image.Layer;
/**
* Background service that extracts additional metadata like width and height for DAM renditions.
*/
@Component(immediate = true, metatype = true,
label = "wcm.io DAM Rendition Metadata Service",
description = "Extracts additional metadata like width and height for DAM renditions.")
@Property(name = EventConstants.EVENT_TOPIC, value = DamEvent.EVENT_TOPIC, propertyPrivate = true)
@Service(EventHandler.class)
public final class DamRenditionMetadataService implements EventHandler {
/**
* Name for Renditions Metadata node
*/
public static final String NN_RENDITIONS_METADATA = "renditionsMetadata";
/**
* Property for image with in pixels
*/
public static final String PN_IMAGE_WIDTH = "imageWidth";
/**
* Property for image height in pixels
*/
public static final String PN_IMAGE_HEIGHT = "imageHeight";
private static final EnumSet SUPPORTED_EVENT_TYPES = EnumSet.of(DamEvent.Type.RENDITION_UPDATED, DamEvent.Type.RENDITION_REMOVED);
private static final boolean DEFAULT_ENABLED = true;
@Property(boolValue = DEFAULT_ENABLED, label = "Enabled", description = "Switch to enable or disable this service.")
static final String PROPERTY_ENABLED = "enabled";
private final Logger log = LoggerFactory.getLogger(this.getClass());
private boolean enabled;
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private SlingSettingsService slingSettings;
@Activate
private void activate(ComponentContext componentContext) {
// Activate only in author mode, and check enabled status in service configuration as well
enabled = !RunMode.disableIfNotAuthor(slingSettings.getRunModes(), componentContext, log)
&& PropertiesUtil.toBoolean(componentContext.getProperties().get(PROPERTY_ENABLED), DEFAULT_ENABLED);
}
@Override
public void handleEvent(Event event) {
if (!enabled || !StringUtils.equals(event.getTopic(), DamEvent.EVENT_TOPIC)) {
return;
}
DamEvent damEvent = DamEvent.fromEvent(event);
if (SUPPORTED_EVENT_TYPES.contains(damEvent.getType())) {
handleDamEvent(damEvent);
}
}
/**
* Handle dam event if certain conditions are fulfilled.
* @param event DAM event
*/
private void handleDamEvent(DamEvent event) {
// make sure rendition file extension is an image extensions
String renditionPath = event.getAdditionalInfo();
String renditionNodeName = Text.getName(renditionPath);
String fileExtension = StringUtils.substringAfterLast(renditionNodeName, ".");
if (!FileExtension.isImage(fileExtension)) {
return;
}
// open admin session for reading/writing rendition metadata
ResourceResolver adminResourceResolver = null;
try {
adminResourceResolver = resourceResolverFactory.getServiceResourceResolver(null);
// make sure asset exists
Asset asset = getAsset(event.getAssetPath(), adminResourceResolver);
if (asset == null) {
return;
}
if (event.getType() == DamEvent.Type.RENDITION_UPDATED) {
renditionAddedOrUpdated(asset, renditionPath, event.getUserId(), adminResourceResolver);
}
else if (event.getType() == DamEvent.Type.RENDITION_REMOVED) {
renditionRemoved(asset, renditionPath, event.getUserId(), adminResourceResolver);
}
}
catch (LoginException ex) {
log.warn("Getting service resource resolver failed. "
+ "Please make sure a service user is defined for bundle 'io.wcm.handler.media'.", ex);
}
finally {
if (adminResourceResolver != null) {
adminResourceResolver.close();
}
}
}
/**
* Create or update rendition metadata if rendition is created or updated.
* @param asset Asset
* @param renditionPath Rendition path
*/
private void renditionAddedOrUpdated(Asset asset, String renditionPath, String userId, ResourceResolver resolver) {
String renditionNodeName = Text.getName(renditionPath);
// check for resource existence and try to get layer from image
Resource renditionResource = resolver.getResource(renditionPath);
if (renditionResource == null) {
return;
}
Layer renditionLayer = renditionResource.adaptTo(Layer.class);
if (renditionLayer == null) {
return;
}
// update metadata
Node renditionsMetadata = getRenditionsMetadataNode(asset, true);
if (renditionsMetadata != null) {
try {
Node metadataNode;
if (renditionsMetadata.hasNode(renditionNodeName)) {
metadataNode = renditionsMetadata.getNode(renditionNodeName);
}
else {
metadataNode = renditionsMetadata.addNode(renditionNodeName, JcrConstants.NT_UNSTRUCTURED);
}
metadataNode.setProperty(PN_IMAGE_WIDTH, renditionLayer.getWidth());
metadataNode.setProperty(PN_IMAGE_HEIGHT, renditionLayer.getHeight());
updateLastModifiedAndSave(asset, userId, resolver);
log.debug("Updated rendition metadata at " + metadataNode.getPath() + " "
+ "(width=" + renditionLayer.getWidth() + ", height=" + renditionLayer.getHeight() + ").");
}
catch (RepositoryException ex) {
log.error("Unable to create or update rendition metadata node for " + renditionPath, ex);
}
}
}
/**
* Remove rendition metadata node if rendition is removed.
* @param asset Asset
* @param renditionPath Rendition path
*/
private void renditionRemoved(Asset asset, String renditionPath, String userId, ResourceResolver resolver) {
Node renditionsMetadata = getRenditionsMetadataNode(asset, false);
if (renditionsMetadata == null) {
return;
}
try {
String renditionNodeName = Text.getName(renditionPath);
if (renditionsMetadata.hasNode(renditionNodeName)) {
Node metadataNode = renditionsMetadata.getNode(renditionNodeName);
String pathToRemove = metadataNode.getPath();
metadataNode.remove();
updateLastModifiedAndSave(asset, userId, resolver);
log.debug("Removed rendition metadata at " + pathToRemove + ".");
}
}
catch (RepositoryException ex) {
log.error("Unable to delete rendition metadata node for " + renditionPath, ex);
}
}
/**
* Updates last modified information and saves the session.
* @param asset Asset
* @param userId User id
* @throws RepositoryException
*/
private void updateLastModifiedAndSave(Asset asset, String userId, ResourceResolver resolver) throws RepositoryException {
// -- this is currently DISABLED due to WCMIO-28, concurrency issues with DAM workflows
/*
Node node = asset.adaptTo(Node.class);
Node contentNode = node.getNode(JcrConstants.JCR_CONTENT);
// this is a workaround to make sure asset is marked as modified
contentNode.setProperty(JcrConstants.JCR_LASTMODIFIED, Calendar.getInstance());
contentNode.setProperty(JcrConstants.JCR_LAST_MODIFIED_BY, userId);
*/
resolver.adaptTo(Session.class).save();
}
/**
* Get asset instance for given asset path.
* @param assetPath Asset path
* @return Asset or null if path is invalid
*/
private Asset getAsset(String assetPath, ResourceResolver resolver) {
Resource assetResource = resolver.getResource(assetPath);
if (assetResource != null) {
return assetResource.adaptTo(Asset.class);
}
else {
return null;
}
}
/**
* Get node for storing the renditions additional metadata.
* @param asset Asset
* @param createIfNotExists if true the node is (tried to be) created automatically if it does not exist
* @return Node or null if it does not exist or could not be created
*/
private Node getRenditionsMetadataNode(Asset asset, boolean createIfNotExists) {
try {
Node assetNode = asset.adaptTo(Node.class);
if (assetNode != null) {
Node assetContentNode = assetNode.getNode(JcrConstants.JCR_CONTENT);
if (assetContentNode.hasNode(NN_RENDITIONS_METADATA)) {
return assetContentNode.getNode(NN_RENDITIONS_METADATA);
}
else if (createIfNotExists) {
return assetContentNode.addNode(NN_RENDITIONS_METADATA, JcrConstants.NT_UNSTRUCTURED);
}
}
}
catch (RepositoryException ex) {
log.error("Unable to get/create renditions metadata node at " + asset.getPath(), ex);
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy