All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.day.cq.wcm.commons.AbstractImageServlet Maven / Gradle / Ivy

/*
 * Copyright 1997-2008 Day Management AG
 * Barfuesserplatz 6, 4001 Basel, Switzerland
 * All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * Day Management AG, ("Confidential Information"). You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Day.
 */
package com.day.cq.wcm.commons;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Calendar;

import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;

import com.day.cq.commons.DiffInfo;
import com.day.cq.commons.DiffService;
import com.day.cq.commons.ImageResource;
import com.day.cq.commons.SlingRepositoryException;
import com.day.cq.commons.jcr.JcrConstants;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
import com.day.cq.wcm.api.components.Component;
import com.day.cq.wcm.api.designer.Style;
import com.day.image.Layer;

/**
 * Servers as base for image servlets
 */
public abstract class AbstractImageServlet extends SlingSafeMethodsServlet {

    /**
     * Handles a GET request and created the desired image.
     * calls the following methods:
     *
     * 
    *
  • {@link #checkModifiedSince(SlingHttpServletRequest, SlingHttpServletResponse)} *
  • {@link #createLayer(ImageContext)} *
  • {@link #writeLayer(SlingHttpServletRequest, SlingHttpServletResponse, ImageContext, Layer)} *
* * If the requested extension does not map to a known image type via * {@link #getImageType(String)}, a 404 is responded. */ protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { try { if (checkModifiedSince(request, response)) { return; } String type = getImageType(request.getRequestPathInfo().getExtension()); if (type == null) { response.sendError(HttpServletResponse.SC_NOT_FOUND, "Image type not supported"); return; } ImageContext context = new ImageContext(request, type); Layer layer = createLayer(context); if (layer != null) { applyDiff(layer, context); } writeLayer(request, response, context, layer); } catch (RepositoryException e) { throw new SlingRepositoryException(e); } } /** * Writes the layer to the response. * * @param request servlet request * @param response servlet response * @param context the context * @param layer layer * @throws IOException if an I/O error occurs. * @throws RepositoryException if an repository error occurs. */ protected void writeLayer(SlingHttpServletRequest request, SlingHttpServletResponse response, ImageContext context, Layer layer) throws IOException, RepositoryException { writeLayer(request, response, context, layer, getImageQuality()); } /** * Writes the layer to the response at the specified quality. * * @param request servlet request * @param response servlet response * @param context the context * @param layer layer * @param quality image quality * @throws IOException if an I/O error occurs. * @throws RepositoryException if an repository error occurs. */ protected void writeLayer(SlingHttpServletRequest request, SlingHttpServletResponse response, ImageContext context, Layer layer, double quality) throws IOException, RepositoryException { String imageType = getImageType(); if (context != null && context.requestImageType != null && context.requestImageType.startsWith("image/")) { imageType = context.requestImageType; } response.setContentType(imageType); if (layer == null) { return; } // calculate size if not too big int size = layer.getHeight() * layer.getWidth(); if (size < 1024*1024) { // cache and spool ByteArrayOutputStream out = new ByteArrayOutputStream(size); layer.write(imageType, quality, out); byte[] bytes = out.toByteArray(); response.setContentLength(bytes.length); response.getOutputStream().write(bytes); } else { // write directly layer.write(imageType, quality, response.getOutputStream()); } } /** * Returns the image type. default "image/png" * @return the image type. */ protected String getImageType() { return "image/png"; } /** * Returns the image type for the given extension. currently there are only * "png", "gif" and "jpg" supported, but an subclass can provide other * mappings. * * @param ext the extension * @return the image type or null. */ protected String getImageType(String ext) { if (ext != null) { ext = ext.toLowerCase(); } if ("png".equals(ext)) { return "image/png"; } else if ("gif".equals(ext)) { return "image/gif"; } else if ("jpg".equals(ext) || "jpeg".equals(ext)) { return "image/jpeg"; } else { return null; } } /** * Returns the image quality. default 1.0 * @return the image quality. */ protected double getImageQuality() { return 1.0; } /** * Checks if the request contains a if-last-modified-since header and if the * node has a jcr:lastModified property. if the properties were modified * before the header a 304 is sent otherwise the response last modified header * is set. If the give doesn't have the property, the parent node is searched. * * @param req the request * @param resp the response * @return true if the response was sent */ protected boolean checkModifiedSince(SlingHttpServletRequest req, SlingHttpServletResponse resp) { Resource resource = req.getResource(); Node node = resource.adaptTo(Node.class); if (node != null) { return RequestHelper.handleIfModifiedSince(req, resp, node); } ValueMap properties = resource.adaptTo(ValueMap.class); if (properties != null) { return RequestHelper.handleIfModifiedSince(req, resp, properties); } return false; } /** * Creates the image layer. * * @param c the convenience context * @return the layer * @throws RepositoryException if an error occurs. * @throws IOException if an I/O error occurs */ protected abstract Layer createLayer(ImageContext c) throws RepositoryException, IOException; /** * Default behavior that applies diff information to the layer * @param layer the layer * @param c the context * @return true if the layer was modified. */ protected boolean applyDiff(Layer layer, ImageContext c) { if (layer == null || c.diffInfo == null) { return false; } if (isRemovedDiff(c)) { layer.setPaint(Color.RED); layer.setStroke(new BasicStroke(2)); layer.drawLine(0,0, layer.getWidth(), layer.getHeight()); layer.drawLine(layer.getWidth(), 0, 0 ,layer.getHeight()); return true; } else if (isAddedDiff(c)) { layer.setPaint(Color.GREEN); layer.setStroke(new BasicStroke(3)); layer.drawRect(new Rectangle(layer.getWidth(), layer.getHeight())); return true; } else if (isModifiedDiff(c)) { layer.setPaint(Color.RED); layer.setStroke(new BasicStroke(3)); layer.drawRect(new Rectangle(layer.getWidth(), layer.getHeight())); return true; } return false; } /** * Creates an image resource based on the resource in the image context. * Subclasses can override this to create extended variants of image * resources. * * @param resource the resource * @return the image resource */ protected ImageResource createImageResource(Resource resource) { return new ImageResource(resource); } /** * Calculates if the underlying image was modified in respect to the versioned * diff. * * @param c the image context * @return true if modified */ protected boolean isModifiedDiff(ImageContext c) { if (c.diffInfo == null || c.diffInfo.getContent() == null) { return false; } ImageResource img0 = createImageResource(c.resource); if (!img0.hasContent()) { return false; } ImageResource img1 = createImageResource(c.diffInfo.getContent()); if (!img1.hasContent()) { return false; } try { Calendar c0 = img0.getLastModified(); Calendar c1 = img1.getLastModified(); if (!(c0 == c1 || c0 != null && c0.equals(c1))) { return true; } } catch (RepositoryException e) { // ignore } Rectangle r0 = img0.getCropRect(); Rectangle r1 = img1.getCropRect(); if (!(r0 == r1 || r0 != null && r0.equals(r1))) { return true; } if (img0.getRotation() != img1.getRotation()) { return true; } Dimension d0 = new Dimension( img0.get(img0.getItemName(ImageResource.PN_WIDTH), 0), img0.get(img0.getItemName(ImageResource.PN_HEIGHT), 0)); Dimension d1 = new Dimension( img1.get(img1.getItemName(ImageResource.PN_WIDTH), 0), img1.get(img1.getItemName(ImageResource.PN_HEIGHT), 0)); return !d0.equals(d1); } /** * Calculates if the underlying image was added with respect to the versioned * diff. * * @param c the image context * @return true if added */ protected boolean isAddedDiff(ImageContext c) { if (c.diffInfo == null) { return false; } ImageResource img0 = createImageResource(c.resource); if (!img0.hasContent()) { return false; } if (c.diffInfo.getType() == DiffInfo.TYPE.ADDED) { return true; } if (c.resource.getPath().contains(JcrConstants.JCR_FROZENNODE)) { return false; } Resource diffContent = c.diffInfo.getContent(); if (diffContent == null) { return true; } ImageResource img1 = createImageResource(diffContent); if (!img1.hasContent()) { return true; } return false; } /** * Calculates if the underlying image was removed with respect to the versioned * diff. * * @param c the image context * @return true if removed */ protected boolean isRemovedDiff(ImageContext c) { if (c.diffInfo == null) { return false; } Resource diffContent = c.diffInfo.getContent(); if (diffContent == null) { return false; } ImageResource img1 = createImageResource(diffContent); if (!img1.hasContent()) { return false; } if (c.diffInfo.getType() == DiffInfo.TYPE.REMOVED) { return true; } ImageResource img0 = createImageResource(c.resource); if (!img0.hasContent()) { return true; } return false; } /** * Convenience class that holds useful stuff needed for image generation */ public static class ImageContext { public final SlingHttpServletRequest request; public final Resource resource; public final Resource defaultResource; public final ResourceResolver resolver; public final Node node; public final ValueMap properties; public final Style style; public final Page currentPage; public final ValueMap pageProperties; public final Component component; public final String requestImageType; public final DiffInfo diffInfo; public ImageContext(SlingHttpServletRequest request, String type) { this.request = request; resource = request.getResource(); resolver = request.getResourceResolver(); node = resource.adaptTo(Node.class); ValueMap props = resource.adaptTo(ValueMap.class); properties = props == null ? ValueMap.EMPTY : props; style = WCMUtils.getStyle(request); PageManager pageManager = resolver.adaptTo(PageManager.class); currentPage = pageManager.getContainingPage(resource); pageProperties = currentPage == null ? ValueMap.EMPTY : currentPage.getProperties(); component = WCMUtils.getComponent(resource); requestImageType = type; // get diff information Resource vRes = null; DiffInfo.TYPE diffType = null; String vType = request.getParameter(DiffService.REQUEST_PARAM_DIFF_TYPE); if (vType != null) { try { diffType = DiffInfo.TYPE.valueOf(vType); } catch (IllegalArgumentException e) { // ignore } } String vLabel = request.getParameter(DiffService.REQUEST_PARAM_DIFF_TO); if (vLabel != null) { vRes = DiffInfo.getVersionedResource(resource, vLabel); if (vRes == null && diffType == null) { // calculate missing vType if (!resource.getPath().contains(JcrConstants.JCR_FROZENNODE)) { diffType = DiffInfo.TYPE.ADDED; } } } String defaultResourcePath = request.getParameter(ImageResource.PN_DEFAULT_IMAGE_PATH); Resource defRes = null; if (defaultResourcePath != null) { defRes = resolver.resolve(defaultResourcePath); } defaultResource = defRes; // if (vRes != null || diffType != null) { diffInfo = new DiffInfo(vRes, diffType == null ? DiffInfo.TYPE.SAME : diffType); } else { diffInfo = null; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy