001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: Resource.java,v 1.2 2006-06-15 13:40:47 sinisa Exp $
022: */
023:
024: package com.lutris.classloader;
025:
026: // lutris packages
027: // v. strahinja, 24 sep 2002
028: import java.io.ByteArrayOutputStream;
029: import java.io.FileNotFoundException;
030: import java.io.IOException;
031: import java.io.InputStream;
032:
033: import com.lutris.logging.LogChannel;
034:
035: /**
036: * <P>A resource that is a file existing on the local machine or a remote
037: * machine. The properties of a resource include file name, location, size,
038: * and last-modified time. The location of the resource is represented by a
039: * <CODE>ClassPathEntry</CODE> and is either a directory or a zip file.
040: * The file name, described by a String, is relative to the specified location.
041: * The resource size is the file size in bytes and the resource time is the
042: * last-modified time of the file in milliseconds.
043: *
044: * <P>This is an abstract class. All subclasses must determine the size
045: * and last-modified time for the resource, and must implement the
046: * <CODE>getInputStream</CODE> method.
047: *
048: * <P>
049: * Resources can be ASCII or binary files, such as text files, HTML files,
050: * Java source-code files, Java byte-code files, Java archive (JAR) files, and
051: * gif files. Some examples resource file names are:
052: *
053: * <PRE>
054: * /users/kristen/text/schedule.txt
055: * /webserver/htdocs/index.html
056: * /jdk1.1.5/src/java/util/Enumeration.java
057: * /jdk1.1.5/classes/java/util/Enumeration.class
058: * /users/kristen/lutris/lutris.jar
059: * /webserver/htdocs/banner.gif
060: * </PRE>
061: *
062: * Since the file names are relative to the location, the beginning slash ("/")
063: * does NOT mean that the file name is given as the absolute path on its
064: * host machine.
065: *
066: * @author Kristen Pol, Lutris Technologies
067: * @version $Revision : 1.1 $
068: * @see com.lutris.classloader.ClassPathEntry
069: */
070: public abstract class Resource {
071:
072: // protected data members
073:
074: /** The file name of the resource relative to the location. */
075: protected String name = null;
076:
077: /** The location of the resource, which can be local or remote. */
078: protected ClassPathEntry location = null;
079:
080: /** The size of the resource in bytes. */
081: protected long size = -1;
082:
083: /** The last-modified time of the resource when this object was created. */
084: protected long lastModifiedTime = -1;
085:
086: /** Is logging enabled? */
087: protected boolean loggingEnabled = false;
088:
089: /** Log channel to write messages to */
090: // v. strahinja, 24 sep 2002
091: protected LogChannel logChannel;
092: // protected static Logger logger;
093:
094: /** Numeric log level number for LOGLEVEL string */
095: // v. strahinja, 24 sep 2002
096: protected int logLevel;
097:
098: // protected Level logLevel;
099:
100: // constructors
101:
102: /**
103: * Constructs resource with specified name, location and log channel.
104: * The file name must be described relative to the location. The location,
105: * described by the <CODE>ClassPathEntry</CODE>, can be a directory
106: * or a zip file on the local or a remote machine.
107: *
108: * <P>Assumes that all slashes in the <CODE>name</CODE> parameter are
109: * forward slashes ("/");
110: *
111: * @param name The file name of the resource.
112: * @param location The location of the resource.
113: * @param loadLogChannel The log channel for logging.
114: * @exception NullPointerException if either the <CODE>name</CODE> or
115: * <CODE>location</CODE> parameters are null.
116: * @see ClassPathEntry
117: */
118: protected Resource(String name, ClassPathEntry location,
119: // v. strahinja, 24 sep 2002
120: LogChannel loadLogChannel)
121: // Logger loadLogger)
122: throws NullPointerException {
123: // v. strahinja, 24 sep 2002
124: this .logChannel = loadLogChannel;
125: // v. strahinja, 24 sep 2002
126: if (logChannel != null) {
127: // v. strahinja, 24 sep 2002
128: this .logLevel = logChannel
129: .getLevel(MultiClassLoader.LOG_LEVEL);
130: // v. strahinja, 24 sep 2002
131: loggingEnabled = logChannel.isEnabled(logLevel);
132: // this.logger = loadLogger;
133: // if (logger != null) {
134: // this.logLevel = MultiClassLoader.LOG_LEVEL;
135: // loggingEnabled = logger.isEnabledFor(logLevel);
136: }
137: if (name == null || location == null) {
138: throw new NullPointerException();
139: }
140: this .name = name;
141: this .location = location;
142: }
143:
144: // public methods
145:
146: /**
147: * Gets file name of resource set previously by constructor.
148: *
149: * @return the file name of the resource represented by a
150: * <CODE>String</CODE>.
151: */
152: public String getName() {
153: return name;
154: }
155:
156: /**
157: * Gets location of resource set previously by constructor.
158: *
159: * @return the location of the resource represented by a
160: * <CODE>ClassPathEntry</CODE>.
161: */
162: public ClassPathEntry getLocation() {
163: return location;
164: }
165:
166: /**
167: * Stringifies resource described previously by constructor. The
168: * the file name and location are concatenated together to represent
169: * the resource.
170: *
171: * @return the resource represented by a <CODE>String</CODE> composed of
172: * the file name and location.
173: */
174: public String toString() {
175: return "[" + location + "][" + name + "]";
176: }
177:
178: /**
179: * Gets size of resource in bytes.
180: *
181: * <P>All subclasses are expected to determine this size prior to
182: * this method being called.
183: *
184: * @return the size of the resource in bytes.
185: */
186: public long getSize() {
187: return size;
188: }
189:
190: /**
191: * Gets last-modification time of resource at the time this
192: * Resource object was created.
193: *
194: * <P>All subclasses are expected to determine this time prior to
195: * this method being called.
196: *
197: * @return the last-modified time of the resource in milliseconds.
198: */
199: public long getLastModifiedTime() {
200: return lastModifiedTime;
201: }
202:
203: /**
204: * Gets last-modified time of resource in milliseconds.
205: *
206: * @deprectated
207: * @see getlastModifiedTime
208: */
209: public long getTime() {
210: return lastModifiedTime;
211: }
212:
213: /**
214: * Get current last-modification time of resource. This is the
215: * time on the disk file the resource is associated with.
216: *
217: * @return the last-modified time of the permanent copy of the resource
218: * in milliseconds.
219: */
220: public abstract long getCurrentLastModifiedTime()
221: throws FileNotFoundException;
222:
223: /**
224: * Determine if the resource has been modified since it was loaded.
225: *
226: * @return <CODE>true</CODE> if the resource has been modified,
227: * <CODE>false</CODE> if not.
228: */
229: public boolean hasBeenModified() throws FileNotFoundException {
230: long currentTime = getCurrentLastModifiedTime();
231: if (loggingEnabled) {
232: // v. strahinja, 24 sep 2002
233: logChannel.write(logLevel, "hasBeenModified: " + getName()
234: // logger.log(logLevel, "hasBeenModified: " + getName()
235: + ": current time=" + currentTime + ", last time="
236: + lastModifiedTime);
237: }
238: return (currentTime > lastModifiedTime);
239: }
240:
241: /**
242: * Get the resouce as a byte array when the size is known.
243: *
244: * @param inputStream Resource input stream
245: * @return an array of bytes representing the resource.
246: * @exception IOException if the byte array can not be constructed.
247: */
248: private byte[] getBytesKnowSize(InputStream inputStream)
249: throws IOException {
250: byte[] bytes = new byte[(int) size];
251: int bytesRead;
252:
253: // Even though the total size to read is known, there is
254: // no guarantee that it's returned all in one read.
255: int idx = 0;
256: while (idx < size) {
257: if (inputStream.available() == 0) {
258: break;
259: }
260: if ((bytesRead = inputStream.read(bytes, idx, ((int) size)
261: - idx)) == -1) {
262: break;
263: }
264: idx += bytesRead;
265: }
266:
267: if (idx != size) {
268: throw new IOException("Read of resource expected " + size
269: + " bytes, got " + idx + " bytes");
270: }
271: return bytes;
272: }
273:
274: /**
275: * Get the resouce as a byte array when the size is not known.
276: *
277: * @param inputStream Resource input stream
278: * @return an array of bytes representing the resource.
279: * @exception IOException if the byte array can not be constructed.
280: */
281: private byte[] getBytesUnknowSize(InputStream inputStream)
282: throws IOException {
283: int bytesRead;
284: byte[] byteBuffer = new byte[10240];
285: ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
286: while (true) {
287: if (inputStream.available() == 0) {
288: break;
289: }
290: if ((bytesRead = inputStream.read(byteBuffer)) == -1) {
291: break;
292: }
293: byteStream.write(byteBuffer, 0, bytesRead);
294: }
295: return byteStream.toByteArray();
296: }
297:
298: /**
299: * Gets byte array representing resource.
300: *
301: * <P>This method relies on the implementation of
302: * <CODE>getInputStream</CODE>.
303: *
304: * @return an array of bytes representing the resource.
305: * @exception IOException if the byte array can not be constructed.
306: */
307: public byte[] getBytes() throws IOException {
308: InputStream inputStream = getInputStream();
309: if (inputStream == null) {
310: throw new IOException("Null InputStream for resource, "
311: + this );
312: }
313: try {
314: if (size < 0) {
315: return getBytesUnknowSize(inputStream);
316: } else {
317: return getBytesKnowSize(inputStream);
318: }
319: } finally {
320: inputStream.close();
321: }
322: }
323:
324: /**
325: * Gets input stream representing resource.
326: *
327: * <P>This method is abstract and must be implemented by all subclasses.
328: * The <CODE>getBytes</CODE> method also depends upon this implementation.
329: *
330: * @return the input stream that represents the resource.
331: * @exception IOException if the input stream can not be constructed.
332: */
333: public abstract InputStream getInputStream() throws IOException;
334:
335: /**
336: * Determines if the specified resource is equal to this resource.
337: * Resources are considered equal if the file names, locations, sizes,
338: * and last-modified times are all the same.
339: *
340: * @return true if the resources are equal, false if not.
341: */
342: public boolean equals(Resource resource) {
343: if (name.equals(resource.getName())
344: && location.equals(resource.getLocation())
345: && (size == resource.getSize())
346: && (lastModifiedTime == resource.getLastModifiedTime())) {
347: return true;
348: }
349: return false;
350: }
351: }
|