org.wings.StaticResource Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package org.wings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.externalizer.ExternalizeManager;
import org.wings.io.Device;
import org.wings.resource.ResourceNotFoundException;
import org.wings.session.Session;
import org.wings.session.SessionManager;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* A {@link Resource} which is immutable and may therefore be cached.
*
* @author Armin Haaf
* @author Henner Zeller
*/
public abstract class StaticResource extends Resource {
private final transient static Logger log = LoggerFactory.getLogger(StaticResource.class);
/**
* Flags that influence the behaviour of the externalize manager
*/
protected int externalizerFlags = ExternalizeManager.FINAL;
/**
* A buffer for temporal storage of the resource
*/
protected transient LimitedBuffer buffer;
/**
* The max size of the buffer
*/
protected int maxBufferSize = -1;
/**
* The size of this resource. Initially, this will be '-1', but
* the value is updated, once the Resource is delivered.
*/
protected int size = -1;
/**
* An ByteArrayOutputStream that buffers up to the limit
* MAX_SIZE_TO_BUFFER. Is able to write to an Device.
*/
protected final static class LimitedBuffer extends ByteArrayOutputStream {
public static final int MAX_SIZE_TO_BUFFER = 8 * 1024; // 8 KByte
private boolean withinLimit;
private int maxSizeToBuffer = MAX_SIZE_TO_BUFFER;
/**
* creates a new buffer
*/
LimitedBuffer() {
/*
* don't waste too much memory; most resources (like icons)
* are tiny, so we should start with a small initial size.
*/
super(64);
withinLimit = true;
initMaxSizeToBuffer();
}
/**
* creates a new buffer with the specified max buffer size
* @param maxSizeToBuffer the max size in bytes
*/
LimitedBuffer(int maxSizeToBuffer) {
this();
this.maxSizeToBuffer = maxSizeToBuffer;
}
private void initMaxSizeToBuffer() {
Session session = SessionManager.getSession();
if (session == null)
return;
Object prop =
session.getProperty("Resource.MaxSizeToBuffer");
if (prop instanceof Number) {
maxSizeToBuffer = ((Number) prop).intValue();
}
}
/**
* write to the stream. If the output size exceeds the limit,
* then set the stream to error state.
*/
@Override
public void write(byte[] b, int off, int len) {
if (!withinLimit) return;
withinLimit = (count + len < maxSizeToBuffer);
if (withinLimit)
super.write(b, off, len);
else
reset(); // discard all input so far: it would become too large
}
// Don't use write(int b)! It does not check the size.
/**
* returns, whether the filled buffer is within the limits,
* and thus, its content is valid and can be used.
*/
public boolean isValid() {
return withinLimit;
}
/**
* sets, whether this resource is valid.
*/
public void setValid(boolean valid) {
withinLimit = valid;
}
/**
* returns the _raw_ buffer; i.e. the buffer may be larger than
* the current size().
*/
public byte[] getBytes() {
return buf;
}
/**
* write to some output device.
*/
public void writeTo(Device out) throws IOException {
out.write(buf, 0, size());
}
}
/**
* A static resource that is obtained from the specified class loader
*/
protected StaticResource(String extension, String mimeType) {
super(extension, mimeType);
}
/**
* Get the id that identifies this resource as an externalized object.
* If the object has not been externalized yet, it will be externalized.
*
* @return the externalization id
*/
@Override
public String getId() {
Session session = SessionManager.getSession();
if (id == null && session != null) {
ExternalizeManager ext = session.getExternalizeManager();
id = ext.getId(ext.externalize(this, externalizerFlags));
log.debug("new " + getClass().getName() + " with id " + id);
}
return id;
}
public void setMimeType(String mimeType) {
this.mimeType = mimeType;
}
/**
* Reads the resource into an LimitedBuffer and returns it. If the
* size of the resource is larger than
* {@link LimitedBuffer#MAX_SIZE_TO_BUFFER}, then the returned Buffer
* is empty and does not contain the Resource's content (and the
* isValid() flag is false).
*
* @return buffered resource as LimitedBuffer, that may be invalid,
* if the size of the resource is beyond MAX_SIZE_TO_BUFFER. It is
* null, if the Resource returned an invalid stream.
* @throws IOException
*/
protected LimitedBuffer bufferResource() throws ResourceNotFoundException, IOException {
try {
if (buffer == null) {
if (maxBufferSize < 0) {
buffer = new LimitedBuffer();
} else {
buffer = new LimitedBuffer(maxBufferSize);
}
InputStream resource = getResourceStream();
if (resource != null) {
byte[] copyBuffer = new byte[1024];
int read;
while (buffer.isValid() && (read = resource.read(copyBuffer)) > 0) {
buffer.write(copyBuffer, 0, read);
}
resource.close();
if (buffer.isValid()) {
size = buffer.size();
}
} else {
log.error("Resource returned empty stream: " + this);
buffer.setValid(false);
}
}
} catch (ResourceNotFoundException e) {
buffer = null;
throw e;
}
return buffer;
}
/**
* writes the Resource to the given Stream. If the resource
* is not larger than {@link LimitedBuffer#MAX_SIZE_TO_BUFFER}, then
* an internal buffer caches the content the first time, so that it
* is delivered as fast as possible at any subsequent calls.
*
* @param out the sink, the content of the resource should
* be written to.
*/
@Override
public void write(Device out) throws IOException, ResourceNotFoundException {
/*
* if the buffer is null, then we are called the first time.
*/
if (buffer == null) {
bufferResource();
if (buffer == null) // no valid bufferable resource available
return;
}
if (buffer.isValid()) { // buffered and small enough. buffer->out
buffer.writeTo(out);
} else { // too large to be buffered. res->out
InputStream resource = getResourceStream();
if (resource != null) {
int deliverSize = 0;
byte[] copyBuffer = new byte[1024];
int read;
while ((read = resource.read(copyBuffer)) > 0) {
out.write(copyBuffer, 0, read);
deliverSize += read;
}
resource.close();
size = deliverSize;
}
}
out.flush();
}
/**
* Return the size in bytes of the resource, if known
*/
@Override
public final int getLength() {
return size;
}
@Override
public SimpleURL getURL() {
String name = getId();
RequestURL requestURL = (RequestURL)SessionManager.getSession().getProperty("request.url");
requestURL = (RequestURL) requestURL.clone();
requestURL.setResource(name);
return requestURL;
}
public String toString() {
return getId();
}
/**
* set the externalizer flags as defined in
* {@link org.wings.externalizer.AbstractExternalizeManager}.
*/
public void setExternalizerFlags(int flags) {
externalizerFlags = flags;
}
public int getExternalizerFlags() {
return externalizerFlags;
}
protected static String resolveName(Class baseClass, String fileName) {
if (fileName == null) {
return fileName;
}
if (!fileName.startsWith("/")) {
while (baseClass.isArray()) {
baseClass = baseClass.getComponentType();
}
String baseName = baseClass.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
fileName = baseName.substring(0, index).replace('.', '/')
+ '/' + fileName;
}
} else {
fileName = fileName.substring(1);
}
return fileName;
}
protected abstract InputStream getResourceStream() throws ResourceNotFoundException;
public int getMaxBufferSize() {
return maxBufferSize;
}
public void setMaxBufferSize(int maxBufferSize) {
this.maxBufferSize = maxBufferSize;
}
}