001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings;
014:
015: import org.apache.commons.logging.Log;
016: import org.apache.commons.logging.LogFactory;
017: import org.wings.externalizer.ExternalizeManager;
018: import org.wings.io.Device;
019: import org.wings.resource.ResourceNotFoundException;
020: import org.wings.session.Session;
021: import org.wings.session.SessionManager;
022:
023: import java.io.ByteArrayOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026:
027: /**
028: * A {@link Resource} which is immutable and may therefore be cached.
029: *
030: * @author <a href="mailto:haaf@mercatis.de">Armin Haaf</a>
031: * @author <a href="mailto:H.Zeller@acm.org">Henner Zeller</a>
032: */
033: public abstract class StaticResource extends Resource {
034: private final transient static Log log = LogFactory
035: .getLog(StaticResource.class);
036: /**
037: * Flags that influence the behaviour of the externalize manager
038: */
039: protected int externalizerFlags = ExternalizeManager.FINAL;
040:
041: /**
042: * A buffer for temporal storage of the resource
043: */
044: protected transient LimitedBuffer buffer;
045:
046: /**
047: * The max size of the buffer
048: */
049: protected int maxBufferSize = -1;
050:
051: /**
052: * The size of this resource. Initially, this will be '-1', but
053: * the value is updated, once the Resource is delivered.
054: */
055: protected int size = -1;
056:
057: /**
058: * An ByteArrayOutputStream that buffers up to the limit
059: * MAX_SIZE_TO_BUFFER. Is able to write to an Device.
060: */
061: protected final static class LimitedBuffer extends
062: ByteArrayOutputStream {
063: public static final int MAX_SIZE_TO_BUFFER = 8 * 1024; // 8 KByte
064:
065: private boolean withinLimit;
066:
067: private int maxSizeToBuffer = MAX_SIZE_TO_BUFFER;
068:
069: /**
070: * creates a new buffer
071: */
072: LimitedBuffer() {
073: /*
074: * don't waste too much memory; most resources (like icons)
075: * are tiny, so we should start with a small initial size.
076: */
077: super (64);
078: withinLimit = true;
079:
080: initMaxSizeToBuffer();
081: }
082:
083: /**
084: * creates a new buffer with the specified max buffer size
085: * @param maxSizeToBuffer the max size in bytes
086: */
087: LimitedBuffer(int maxSizeToBuffer) {
088: this ();
089: this .maxSizeToBuffer = maxSizeToBuffer;
090: }
091:
092: private void initMaxSizeToBuffer() {
093: Session session = SessionManager.getSession();
094: if (session == null)
095: return;
096: Object prop = session
097: .getProperty("Resource.MaxSizeToBuffer");
098:
099: if (prop != null && prop instanceof Number) {
100:
101: maxSizeToBuffer = ((Number) prop).intValue();
102: }
103: }
104:
105: /**
106: * write to the stream. If the output size exceeds the limit,
107: * then set the stream to error state.
108: */
109: public void write(byte[] b, int off, int len) {
110: if (!withinLimit)
111: return;
112: withinLimit = (count + len < maxSizeToBuffer);
113: if (withinLimit)
114: super .write(b, off, len);
115: else
116: reset(); // discard all input so far: it would become too large
117: }
118:
119: // Don't use write(int b)! It does not check the size.
120:
121: /**
122: * returns, whether the filled buffer is within the limits,
123: * and thus, its content is valid and can be used.
124: */
125: public boolean isValid() {
126: return withinLimit;
127: }
128:
129: /**
130: * sets, whether this resource is valid.
131: */
132: public void setValid(boolean valid) {
133: withinLimit = valid;
134: }
135:
136: /**
137: * returns the _raw_ buffer; i.e. the buffer may be larger than
138: * the current size().
139: */
140: public byte[] getBytes() {
141: return buf;
142: }
143:
144: /**
145: * write to some output device.
146: */
147: public void writeTo(Device out) throws IOException {
148: out.write(buf, 0, size());
149: }
150: }
151:
152: /**
153: * A static resource that is obtained from the specified class loader
154: */
155: protected StaticResource(String extension, String mimeType) {
156: super (extension, mimeType);
157: }
158:
159: /**
160: * Get the id that identifies this resource as an externalized object.
161: * If the object has not been externalized yet, it will be externalized.
162: *
163: * @return the externalization id
164: */
165: public String getId() {
166: Session session = SessionManager.getSession();
167: if (id == null && session != null) {
168: ExternalizeManager ext = session.getExternalizeManager();
169: id = ext.getId(ext.externalize(this , externalizerFlags));
170: log.debug("new " + getClass().getName() + " with id " + id);
171: }
172: return id;
173: }
174:
175: public void setMimeType(String mimeType) {
176: this .mimeType = mimeType;
177: }
178:
179: /**
180: * Reads the resource into an LimitedBuffer and returns it. If the
181: * size of the resource is larger than
182: * {@link LimitedBuffer#MAX_SIZE_TO_BUFFER}, then the returned Buffer
183: * is empty and does not contain the Resource's content (and the
184: * isValid() flag is false).
185: *
186: * @return buffered resource as LimitedBuffer, that may be invalid,
187: * if the size of the resource is beyond MAX_SIZE_TO_BUFFER. It is
188: * null, if the Resource returned an invalid stream.
189: * @throws IOException
190: */
191: protected LimitedBuffer bufferResource()
192: throws ResourceNotFoundException, IOException {
193: try {
194: if (buffer == null) {
195: if (maxBufferSize < 0) {
196: buffer = new LimitedBuffer();
197: } else {
198: buffer = new LimitedBuffer(maxBufferSize);
199: }
200: InputStream resource = getResourceStream();
201: if (resource != null) {
202: byte[] copyBuffer = new byte[1024];
203: int read;
204: while (buffer.isValid()
205: && (read = resource.read(copyBuffer)) > 0) {
206: buffer.write(copyBuffer, 0, read);
207: }
208: resource.close();
209: if (buffer.isValid()) {
210: size = buffer.size();
211: }
212: } else {
213: log
214: .fatal("Resource returned empty stream: "
215: + this );
216: buffer.setValid(false);
217: }
218: }
219: } catch (ResourceNotFoundException e) {
220: buffer = null;
221: throw e;
222: }
223: return buffer;
224: }
225:
226: /**
227: * writes the Resource to the given Stream. If the resource
228: * is not larger than {@link LimitedBuffer#MAX_SIZE_TO_BUFFER}, then
229: * an internal buffer caches the content the first time, so that it
230: * is delivered as fast as possible at any subsequent calls.
231: *
232: * @param out the sink, the content of the resource should
233: * be written to.
234: */
235: public void write(Device out) throws IOException,
236: ResourceNotFoundException {
237: /*
238: * if the buffer is null, then we are called the first time.
239: */
240: if (buffer == null) {
241: bufferResource();
242: if (buffer == null) // no valid bufferable resource available
243: return;
244: }
245:
246: if (buffer.isValid()) { // buffered and small enough. buffer->out
247: buffer.writeTo(out);
248: } else { // too large to be buffered. res->out
249: InputStream resource = getResourceStream();
250: if (resource != null) {
251: int deliverSize = 0;
252: byte[] copyBuffer = new byte[1024];
253: int read;
254: while ((read = resource.read(copyBuffer)) > 0) {
255: out.write(copyBuffer, 0, read);
256: deliverSize += read;
257: }
258: resource.close();
259: size = deliverSize;
260: }
261: }
262:
263: out.flush();
264: }
265:
266: /**
267: * Return the size in bytes of the resource, if known
268: */
269: @Override
270: public final int getLength() {
271: return size;
272: }
273:
274: @Override
275: public SimpleURL getURL() {
276: String name = getId();
277: RequestURL requestURL = (RequestURL) SessionManager
278: .getSession().getProperty("request.url");
279: requestURL = (RequestURL) requestURL.clone();
280: requestURL.setResource(name);
281: return requestURL;
282: }
283:
284: public String toString() {
285: return getId();
286: }
287:
288: /**
289: * set the externalizer flags as defined in
290: * {@link org.wings.externalizer.AbstractExternalizeManager}.
291: */
292: public void setExternalizerFlags(int flags) {
293: externalizerFlags = flags;
294: }
295:
296: public int getExternalizerFlags() {
297: return externalizerFlags;
298: }
299:
300: protected static String resolveName(Class baseClass, String fileName) {
301: if (fileName == null) {
302: return fileName;
303: }
304: if (!fileName.startsWith("/")) {
305: while (baseClass.isArray()) {
306: baseClass = baseClass.getComponentType();
307: }
308: String baseName = baseClass.getName();
309: int index = baseName.lastIndexOf('.');
310: if (index != -1) {
311: fileName = baseName.substring(0, index).replace('.',
312: '/')
313: + "/" + fileName;
314: }
315: } else {
316: fileName = fileName.substring(1);
317: }
318: return fileName;
319: }
320:
321: protected abstract InputStream getResourceStream()
322: throws ResourceNotFoundException;
323:
324: public int getMaxBufferSize() {
325: return maxBufferSize;
326: }
327:
328: public void setMaxBufferSize(int maxBufferSize) {
329: this.maxBufferSize = maxBufferSize;
330: }
331:
332: }
|