blob: feaf910a2d7ef9ce0a9937971a1f89d0de1fabdf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.sling.samples.espblog.internal;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.observation.Event;
import javax.jcr.observation.EventIterator;
import javax.jcr.observation.EventListener;
import javax.jcr.observation.ObservationManager;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.jcr.api.SlingRepository;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Observe the espblog content for changes, and generate
* thumbnails when images are added.
*
*/
@Component(immediate=true)
@Property(name="service.description", value="Sling ESP blog sample thumbnails generator")
public class ThumbnailGenerator implements EventListener {
private Session session;
private ObservationManager observationManager;
@Reference
private SlingRepository repository;
@Property(value="/content/espblog")
private static final String CONTENT_PATH_PROPERTY = "content.path";
private static final Logger log = LoggerFactory
.getLogger(ThumbnailGenerator.class);
private Map<String, String> supportedMimeTypes = new HashMap<String, String>();
protected void activate(ComponentContext context) throws Exception {
supportedMimeTypes.put("image/jpeg", ".jpg");
supportedMimeTypes.put("image/png", ".png");
String contentPath = (String)context.getProperties().get(CONTENT_PATH_PROPERTY);
session = repository.loginAdministrative(null);
if (repository.getDescriptor(Repository.OPTION_OBSERVATION_SUPPORTED).equals("true")) {
observationManager = session.getWorkspace().getObservationManager();
String[] types = { "nt:file" };
observationManager.addEventListener(this, Event.NODE_ADDED, contentPath, true, null, types, false);
}
}
protected void deactivate(ComponentContext componentContext) throws RepositoryException {
if(observationManager != null) {
observationManager.removeEventListener(this);
}
if (session != null) {
session.logout();
session = null;
}
}
public void onEvent(EventIterator it) {
while (it.hasNext()) {
Event event = it.nextEvent();
try {
if (event.getType() == Event.NODE_ADDED && !(event.getPath().contains("thumbnails"))) {
log.info("new upload: {}", event.getPath());
Node addedNode = session.getRootNode().getNode(event.getPath().substring(1));
processNewNode(addedNode);
log.info("finished processing of {}", event.getPath());
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
private String getMimeType(Node n) throws RepositoryException {
String result = null;
final String mimeType = n.getProperty("jcr:mimeType").getString();
for(String key : supportedMimeTypes.keySet()) {
if(mimeType!=null && mimeType.startsWith(key)) {
result = key;
break;
}
}
if(result == null) {
log.info("Node {} rejected, unsupported mime-type {}", n.getPath(), mimeType);
}
if(n.getName().startsWith(".")) {
log.info("Node {} rejected, name starts with '.'", n.getPath(), mimeType);
result = null;
}
return result;
}
private void processNewNode(Node addedNode) throws Exception {
final String mimeType = getMimeType(addedNode);
if (mimeType == null) {
return;
}
final String suffix = supportedMimeTypes.get(mimeType);
// Scale to a temp file for simplicity
log.info("Creating thumbnails for node {}", addedNode.getPath());
final int [] widths = { 50, 100, 250 };
for(int width : widths) {
createThumbnail(addedNode, width, mimeType, suffix);
}
}
private void createThumbnail(Node image, int scalePercent, String mimeType, String suffix) throws Exception {
final File tmp = File.createTempFile(getClass().getSimpleName(), suffix);
try {
scale(image.getProperty("jcr:data").getStream(), scalePercent, new FileOutputStream(tmp), suffix);
// Create thumbnail node and set the mandatory properties
Node thumbnailFolder = getThumbnailFolder(image);
Node thumbnail = thumbnailFolder.addNode(image.getParent().getName() + "_" + scalePercent + suffix, "nt:file");
Node contentNode = thumbnail.addNode("jcr:content", "nt:resource");
contentNode.setProperty("jcr:data", new FileInputStream(tmp));
contentNode.setProperty("jcr:lastModified", Calendar.getInstance());
contentNode.setProperty("jcr:mimeType", mimeType);
session.save();
log.info("Created thumbnail " + contentNode.getPath());
} finally {
if(tmp != null) {
tmp.delete();
}
}
}
private Node getThumbnailFolder(Node addedNode) throws Exception {
Node post = addedNode.getParent().getParent().getParent();
if (post.hasNode("thumbnails")) {
log.info("thumbnails node exists already at " + post.getPath());
return post.getNode("thumbnails");
} else {
Node t = post.addNode("thumbnails", "nt:folder");
session.save();
return t;
}
}
public void scale(InputStream inputStream, int width, OutputStream outputStream, String suffix) throws IOException {
if(inputStream == null) {
throw new IOException("InputStream is null");
}
final BufferedImage src = ImageIO.read(inputStream);
if(src == null) {
final StringBuffer sb = new StringBuffer();
for(String fmt : ImageIO.getReaderFormatNames()) {
sb.append(fmt);
sb.append(' ');
}
throw new IOException("Unable to read image, registered formats: " + sb);
}
final double scale = (double)width / src.getWidth();
int destWidth = width;
int destHeight = new Double(src.getHeight() * scale).intValue();
log.debug("Generating thumbnail, w={}, h={}", destWidth, destHeight);
BufferedImage dest = new BufferedImage(destWidth, destHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = dest.createGraphics();
AffineTransform at = AffineTransform.getScaleInstance((double) destWidth / src.getWidth(), (double) destHeight / src.getHeight());
g.drawRenderedImage(src, at);
ImageIO.write(dest, suffix.substring(1), outputStream);
}
}