/*******************************************************************************
* Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
*
* This file is part of the OpenWGA server platform.
*
* OpenWGA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, a special exception is granted by the copyright holders
* of OpenWGA called "OpenWGA plugin exception". You should have received
* a copy of this exception along with OpenWGA in file COPYING.
* If not, see <http://www.openwga.com/gpl-plugin-exception>.
*
* OpenWGA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenWGA in file COPYING.
* If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package de.innovationgate.wgpublisher;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import javax.activation.DataSource;
import de.innovationgate.utils.ImageScaler;
import de.innovationgate.utils.MimeTypeSpecificImageScaler;
import de.innovationgate.utils.io.IOBackendException;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGBackendException;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGFileContainer;
import de.innovationgate.webgate.api.WGNotSupportedException;
import de.innovationgate.wga.config.WGAConfiguration;
import de.innovationgate.wga.modules.ModuleInstantiationException;
import de.innovationgate.wgpublisher.webtml.utils.ImageScalerFactory;
public class PublishingFile implements DataSource {
private final WGPDispatcher _dispatcher;
private boolean _zipped = false;
private WGDocument _container;
private String _fileName;
private String _zippedFileName;
private ZipEntry _zipEntry;
private ZipInputStream _zipStream;
private boolean _publishable = true;
private boolean _streamRetrieved = false;
private int _scaleMaxHeight = -1;
private int _scaleMaxWidth = -1;
private byte[] _scaledVersion = null;
private MimeTypeSpecificImageScaler _scaler = null;
private String _mimeType;
public PublishingFile(WGPDispatcher wgpDispatcher, WGDocument container, String fileName) throws WGAPIException {
_dispatcher = wgpDispatcher;
_container = container;
_fileName = fileName;
// If the filename contains slashes we must address a file inside a
// zip file
int firstSlashIndex = fileName.indexOf("/");
if (firstSlashIndex != -1) {
String zipFileCandidate = fileName.substring(0, firstSlashIndex);
if (hasZipFileSuffix(zipFileCandidate) && _container.hasFile(zipFileCandidate)) {
_zippedFileName = fileName.substring(firstSlashIndex + 1);
_fileName = zipFileCandidate;
_zipped = true;
}
}
// We do not want to publish files from the system file container.
// They are the WGA pendant of J2EE's "WEB-INF" directory which is
// also unpublished.
if (container instanceof WGFileContainer) {
WGFileContainer fc = (WGFileContainer) container;
if (fc.getName().equals("system")) {
_publishable = false;
}
}
}
public long getLastModifiedTime() throws WGAPIException {
return _container.getFileLastModified(_fileName).getTime();
}
private void retrieveZipInputStream() throws WGAPIException {
if (_streamRetrieved) {
return;
}
InputStream in = _container.getFileData(_fileName);
_zipStream = new ZipInputStream(in);
_zipEntry = moveStreamToFile(_zipStream);
if (_zipEntry == null) {
try {
_zipStream.close();
}
catch (IOException e) {
_dispatcher.getCore().getLog().error("Error closing zip stream", e);
}
_zipStream = null;
}
_streamRetrieved = true;
}
private boolean hasZipFileSuffix(String zipFileCandidate) {
zipFileCandidate = zipFileCandidate.toLowerCase();
return (zipFileCandidate.endsWith(".zip") || zipFileCandidate.endsWith(".jar"));
}
/**
* @return Returns the container.
*/
public WGDocument getContainer() {
return _container;
}
/**
* @return Returns the fileName.
*/
public String getFileName() {
return _fileName;
}
/**
* @return Returns the zipped.
*/
public boolean isZipped() {
return _zipped;
}
/**
* @return Returns the zippedFileName.
*/
public String getZippedFileName() {
return _zippedFileName;
}
public long getFileSize() throws WGAPIException {
if (isScaled()) {
byte[] bytes;
try {
bytes = getScaledVersion();
return bytes.length;
}
catch (Exception e) {
throw new WGBackendException("Exception determining scaled file size on " + getCachingKey(), e);
}
}
if (isZipped()) {
retrieveZipInputStream();
if (_zipEntry != null) {
return _zipEntry.getSize();
}
else {
return -1;
}
}
else {
return _container.getFileSize(_fileName);
}
}
public InputStream getInputStream() throws IOException {
try {
if (isScaled()) {
return new ByteArrayInputStream(getScaledVersion());
}
else {
return innerGetInputStream();
}
}
catch (WGAPIException e) {
throw new IOBackendException("Exception serving file input stream", e);
}
catch (ModuleInstantiationException e) {
throw new IOBackendException("Exception scaling file", e);
}
}
private InputStream innerGetInputStream() throws WGAPIException {
InputStream stream;
if (isZipped()) {
retrieveZipInputStream();
stream = _zipStream;
}
else {
stream = _container.getFileData(_fileName);
}
return stream;
}
private ZipEntry moveStreamToFile(ZipInputStream in) {
try {
ZipEntry entry;
do {
entry = in.getNextEntry();
} while (entry != null && !entry.getName().equals(getZippedFileName()));
return entry;
}
catch (IOException e) {
_dispatcher.getCore().getLog().error("Error reading zip file entry", e);
return null;
}
}
public boolean isPublishable() {
return _publishable;
}
public String getName() {
if (isZipped()) {
return getZippedFileName() + " in archive '" + getFileName() + "' on document " + _container.getDocumentKey();
} else {
return getFileName() + " on document " + _container.getDocumentKey();
}
}
public String getContentType() {
return _dispatcher.getServletContext().getMimeType(_fileName);
}
public OutputStream getOutputStream() throws IOException {
throw new IOException("Not supported");
}
public boolean isScaled() {
return _scaler != null;
}
public String getCachingKey() {
StringBuffer key = new StringBuffer();
key.append(_container.getDocumentKey());
key.append("/");
key.append(_fileName);
if (_zippedFileName != null) {
key.append("/").append(_zippedFileName);
}
if (_scaler != null) {
key.append("//");
key.append(_scaleMaxWidth + "x" + _scaleMaxHeight);
}
return key.toString();
}
public int getScaleMaxHeight() {
return _scaleMaxHeight;
}
public void setScaleMaxHeight(int scaleMaxHeight) throws WGAPIException {
_scaleMaxHeight = scaleMaxHeight;
prepareScaler();
}
private void prepareScaler() throws WGAPIException {
// Scaler already prepared?
if (_scaler != null) {
return;
}
// File size below threshold?
int scalingThreshold = (Integer) _dispatcher._variousServerOptionReader.readOptionValueOrDefault(WGACore.SERVEROPTION_SERVER_SCALINGTHRESHOLD);
long thresholdBytes = scalingThreshold * 1024 * 1024;
long fileSize = getFileSize(); // Works here normally since the scaler is not yet prepared
if (thresholdBytes < fileSize) {
return;
}
// Find out the mime type, must be an image
if (_mimeType == null) {
_mimeType = _dispatcher.getServletContext().getMimeType(_fileName);
}
if (!_mimeType.startsWith("image/")) {
return;
}
// Find out about the max size threshold
WGAConfiguration wgaConfig = _dispatcher.getCore().getWgaConfiguration();
//wgaConfig.getOptionReader(wgaConfig.getServerOptions(), new Var)
// Try to allocate a scaler
try {
_scaler = ImageScalerFactory.createMimeTypeSpecificImageScaler(_dispatcher.getCore(), _mimeType);
}
catch (WGNotSupportedException e) {
// Fail silently here. This is the case when no scaler for this type is installed
}
catch (Exception e) {
throw new WGBackendException("Exception creating scaler", e);
}
}
public int getScaleMaxWidth() {
return _scaleMaxWidth;
}
public void setScaleMaxWidth(int scaleMaxWidth) throws WGAPIException {
_scaleMaxWidth = scaleMaxWidth;
prepareScaler();
}
private byte[] getScaledVersion() throws WGAPIException, IOException, ModuleInstantiationException {
if (_scaledVersion == null && _scaler != null) {
_scaler.load(innerGetInputStream(), _mimeType);
int maxHeight = (_scaleMaxHeight != -1 ? _scaleMaxHeight : Integer.MAX_VALUE);
int maxWidth = (_scaleMaxWidth != -1 ? _scaleMaxWidth : Integer.MAX_VALUE);
_scaler.shrinkToSize(maxWidth, maxHeight);
ByteArrayOutputStream out = new ByteArrayOutputStream();
_scaler.writeImage(out);
_scaledVersion = out.toByteArray();
}
return _scaledVersion;
}
}