AssetService.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2015 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.dam.assetservice.impl;

  21. import java.util.Dictionary;
  22. import java.util.Hashtable;

  23. import javax.servlet.Servlet;

  24. import org.apache.commons.lang3.StringUtils;
  25. import org.apache.sling.api.resource.ResourceResolverFactory;
  26. import org.osgi.framework.BundleContext;
  27. import org.osgi.framework.ServiceRegistration;
  28. import org.osgi.service.component.annotations.Activate;
  29. import org.osgi.service.component.annotations.Component;
  30. import org.osgi.service.component.annotations.Deactivate;
  31. import org.osgi.service.component.annotations.Reference;
  32. import org.osgi.service.event.Event;
  33. import org.osgi.service.event.EventConstants;
  34. import org.osgi.service.event.EventHandler;
  35. import org.osgi.service.metatype.annotations.AttributeDefinition;
  36. import org.osgi.service.metatype.annotations.Designate;
  37. import org.osgi.service.metatype.annotations.ObjectClassDefinition;
  38. import org.osgi.service.metatype.annotations.Option;
  39. import org.slf4j.Logger;
  40. import org.slf4j.LoggerFactory;

  41. import com.day.cq.dam.api.DamConstants;
  42. import com.day.cq.dam.api.DamEvent;

  43. import io.wcm.dam.assetservice.impl.dataversion.ChecksumDataVersionStrategy;
  44. import io.wcm.dam.assetservice.impl.dataversion.TimestampDataVersionStrategy;
  45. import io.wcm.wcm.commons.contenttype.FileExtension;

  46. /**
  47.  * Implements a simple REST interface that allows resolving DAM asset paths to URLs.
  48.  * For image assets resolving to specific dimensions is supported.
  49.  */
  50. @Component(immediate = true, service = { AssetService.class, EventHandler.class },
  51.     property = EventConstants.EVENT_TOPIC + "=" + DamEvent.EVENT_TOPIC)
  52. @Designate(ocd = AssetService.Config.class)
  53. public class AssetService implements EventHandler {

  54.   @ObjectClassDefinition(name = "wcm.io DAM Asset Service",
  55.       description = "A RESTful service for resolving URLs to DAM assets and renditions.")
  56.   @interface Config {

  57.     @AttributeDefinition(name = "Asset Selector", description = "Selector for attaching REST service to DAM asset paths.")
  58.     String assetServletSelector() default "wcm-io-asset-service";

  59.     @AttributeDefinition(name = "Data Version Selector", description = "Selector for attaching REST service to DAM folder for getting data version.")
  60.     String dataVersionServletSelector() default "wcm-io-asset-service-dataversion";

  61.     @AttributeDefinition(name = "Data Version Strategy", description = "Strategy for building the data versions. See documentation for details.",
  62.         options = {
  63.             @Option(label = TimestampDataVersionStrategy.STRATEGY + ": Timestamp of last DAM event", value = TimestampDataVersionStrategy.STRATEGY),
  64.             @Option(label = ChecksumDataVersionStrategy.STRATEGY + ": Aggregated checksum of DAM assets", value = ChecksumDataVersionStrategy.STRATEGY)
  65.         })
  66.     String dataVersionStrategy() default TimestampDataVersionStrategy.STRATEGY;

  67.     @AttributeDefinition(name = "Update Interval (sec)", description = "Updating interval for calculating data versions in seconds. "
  68.         + "If multiple changes to the DAM folders contents are detected within this interval they are collected. "
  69.         + "This is only used by the 'aggregated checksum' strategy.")
  70.     int dataVersionUpdateIntervalSec() default 60;

  71.     @AttributeDefinition(name = "DAM paths", description = "List of DAM paths for which the asset service should be active. "
  72.         + "If not set, the service is active for all paths.")
  73.     String[] damPaths();

  74.   }

  75.   @Reference
  76.   private ResourceResolverFactory resourceResolverFactory;

  77.   private DamPathHandler damPathHandler;
  78.   private BundleContext bundleContext;
  79.   private ServiceRegistration<Servlet> assetRequestServletReg;
  80.   private ServiceRegistration<Servlet> dataVersionServletReg;

  81.   private static final Logger log = LoggerFactory.getLogger(AssetService.class);

  82.   @Activate
  83.   protected void activate(BundleContext context, Config config) {
  84.     log.info("Start wcm.io DAM Asset Service.");

  85.     this.bundleContext = context;

  86.     String assetServletSelector = config.assetServletSelector();
  87.     String dataVersionServletSelector = config.dataVersionServletSelector();
  88.     int dataVersionUpdateIntervalSec = config.dataVersionUpdateIntervalSec();

  89.     String[] damPaths = config.damPaths();
  90.     String dataVersionStrategyId = config.dataVersionStrategy();
  91.     damPathHandler = new DamPathHandler(damPaths, dataVersionStrategyId, dataVersionUpdateIntervalSec, resourceResolverFactory);

  92.     // register servlets to resource types to handle the JSON requests
  93.     // they are registered dynamically because the selectors are configurable
  94.     assetRequestServletReg = registerServlet(context, new AssetRequestServlet(damPathHandler),
  95.         DamConstants.NT_DAM_ASSET, assetServletSelector);
  96.     dataVersionServletReg = registerServlet(context, new DataVersionServlet(damPathHandler),
  97.         "sling:OrderedFolder", dataVersionServletSelector);
  98.   }

  99.   @Deactivate
  100.   protected void deactivate() {
  101.     log.info("Shutdown wcm.io DAM Asset Service.");

  102.     assetRequestServletReg.unregister();
  103.     dataVersionServletReg.unregister();
  104.     damPathHandler.shutdown();
  105.   }

  106.   @Override
  107.   public void handleEvent(Event event) {
  108.     if (!StringUtils.equals(event.getTopic(), DamEvent.EVENT_TOPIC)) {
  109.       return;
  110.     }
  111.     DamEvent damEvent = DamEvent.fromEvent(event);
  112.     damPathHandler.handleDamEvent(damEvent);
  113.   }

  114.   AssetRequestServlet getAssetRequestServlet() {
  115.     return (AssetRequestServlet)bundleContext.getService(assetRequestServletReg.getReference());
  116.   }

  117.   DataVersionServlet getDataVersionServlet() {
  118.     return (DataVersionServlet)bundleContext.getService(dataVersionServletReg.getReference());
  119.   }

  120.   @SuppressWarnings("null")
  121.   private static <T extends Servlet> ServiceRegistration<Servlet> registerServlet(BundleContext bundleContext, T servletInstance,
  122.       String resourceType, String selector) {
  123.     if (StringUtils.isEmpty(selector)) {
  124.       throw new IllegalArgumentException("No selector defined for " + servletInstance.getClass().getName() + " - skipping servlet registration.");
  125.     }
  126.     Dictionary<String, Object> config = new Hashtable<>();
  127.     config.put("sling.servlet.resourceTypes", resourceType);
  128.     config.put("sling.servlet.selectors", selector);
  129.     config.put("sling.servlet.extensions", FileExtension.JSON);
  130.     return bundleContext.registerService(Servlet.class, servletInstance, config);
  131.   }

  132. }