001: // FileResource.java
002: // $Id: FileResource.java,v 1.21 2004/07/20 15:06:21 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.tools.resources;
007:
008: import java.io.File;
009: import java.io.FileOutputStream;
010: import java.io.IOException;
011: import java.io.InputStream;
012:
013: /**
014: * A simple file resource.
015: */
016: public class FileResource extends FramedResource {
017:
018: /**
019: * Attributes index - The filename attribute.
020: */
021: protected static int ATTR_FILENAME = -1;
022:
023: /**
024: * Attribute index - The date at which we last checked the file content.
025: */
026: protected static int ATTR_FILESTAMP = -1;
027:
028: /**
029: * Attribute index - The index for the content length attribute.
030: */
031: protected static int ATTR_FILE_LENGTH = -1;
032:
033: /**
034: * Attribute index - The index for the backup flag
035: */
036: protected static int ATTR_FILE_BACKUP = -1;
037:
038: static {
039: Attribute a = null;
040: Class cls = null;
041: try {
042: cls = Class.forName("org.w3c.tools.resources.FileResource");
043: } catch (Exception ex) {
044: ex.printStackTrace();
045: System.exit(0);
046: }
047: // The filename attribute.
048: a = new FilenameAttribute("filename", null, Attribute.EDITABLE);
049: ATTR_FILENAME = AttributeRegistry.registerAttribute(cls, a);
050: // The file stamp attribute
051: a = new DateAttribute("file-stamp", new Long(-1),
052: Attribute.COMPUTED);
053: ATTR_FILESTAMP = AttributeRegistry.registerAttribute(cls, a);
054: // The file length attribute:
055: a = new IntegerAttribute("file-length", null,
056: Attribute.COMPUTED);
057: ATTR_FILE_LENGTH = AttributeRegistry.registerAttribute(cls, a);
058: // The backup attribute:
059: a = new BooleanAttribute("backup", Boolean.FALSE,
060: Attribute.EDITABLE);
061: ATTR_FILE_BACKUP = AttributeRegistry.registerAttribute(cls, a);
062: }
063:
064: /**
065: * The file we refer to.
066: * This is a cached version of some attributes, so we need to override
067: * the setValue method in order to be able to catch any changes to it.
068: */
069: protected File file = null;
070:
071: /**
072: * Get this resource filename attribute.
073: */
074: public String getFilename() {
075: return (String) getValue(ATTR_FILENAME, null);
076: }
077:
078: /**
079: * Get this file length
080: */
081: public int getFileLength() {
082: return ((Integer) getValue(ATTR_FILE_LENGTH, new Integer(0)))
083: .intValue();
084: }
085:
086: /**
087: * Get the date at which we last examined the file.
088: */
089:
090: public long getFileStamp() {
091: return getLong(ATTR_FILESTAMP, (long) -1);
092: }
093:
094: /**
095: * Get the backup flag, create a backup file when content change
096: * if true.
097: */
098:
099: public boolean getBackupFlag() {
100: return getBoolean(ATTR_FILE_BACKUP, false);
101: }
102:
103: /**
104: * Get the name of the backup file for this resource.
105: * @return A File object suitable to receive the backup version of this
106: * file.
107: */
108:
109: public File getBackupFile() {
110: File file = getFile();
111: String name = file.getName();
112: return new File(file.getParent(), name + "~");
113: }
114:
115: /**
116: * Save the given stream as the underlying file content.
117: * This method preserve the old file version in a <code>~</code> file.
118: * @param in The input stream to use as the resource entity.
119: * @return A boolean, <strong>true</strong> if the resource was just
120: * created, <strong>false</strong> otherwise.
121: * @exception IOException If dumping the content failed.
122: */
123:
124: public synchronized boolean newContent(InputStream in)
125: throws IOException {
126: File file = getFile();
127: boolean created = (!file.exists() || (file.length() == 0));
128: String name = file.getName();
129: File temp = new File(file.getParent(), "#" + name + "#");
130: String iomsg = null;
131: // We are not catching IO exceptions here, except to remove temp:
132: try {
133: FileOutputStream fout = new FileOutputStream(temp);
134: byte buf[] = new byte[4096];
135: for (int got = 0; (got = in.read(buf)) > 0;)
136: fout.write(buf, 0, got);
137: fout.close();
138: } catch (IOException ex) {
139: iomsg = ex.getMessage();
140: } finally {
141: if (iomsg != null) {
142: temp.delete();
143: throw new IOException(iomsg);
144: } else {
145: if (getBackupFlag()) {
146: File backup = getBackupFile();
147: if (backup.exists())
148: backup.delete();
149: file.renameTo(getBackupFile());
150: }
151: // with some OSes, rename doesn't overwrite so...
152: if (file.exists())
153: file.delete();
154: temp.renameTo(file);
155: // update our attributes for this new content:
156: updateFileAttributes();
157: }
158: }
159: return created;
160: }
161:
162: /**
163: * Check this file content, and update attributes if needed.
164: * This method is normally called before any perform request is done, so
165: * that we make sure that all meta-informations is up to date before
166: * handling a request.
167: * @return The time of the last update to the resource.
168: */
169:
170: public long checkContent() {
171: File file = getFile();
172: // Has this resource changed since last queried ?
173: long lmt = file.lastModified();
174: long cmt = getFileStamp();
175: if ((cmt < 0) || (cmt < lmt)) {
176: updateFileAttributes();
177: return getLastModified();
178: } else if (getFileLength() != (int) file.length()) {
179: updateFileAttributes();
180: return getLastModified();
181: } else {
182: return cmt;
183: }
184: }
185:
186: /**
187: * Set some of this resource attribute.
188: * We just catch here any write access to the filename's, to update
189: * our cache file object.
190: */
191:
192: public synchronized void setValue(int idx, Object value) {
193: if (idx == ATTR_IDENTIFIER) {
194: String oldid = getIdentifier();
195: if (getFilename() == null) {
196: File oldfile = getFile(oldid);
197: if ((oldfile != null) && (oldfile.exists()))
198: super .setValue(ATTR_FILENAME, oldid);
199: }
200: }
201: super .setValue(idx, value);
202: if ((idx == ATTR_FILENAME) || (idx == ATTR_IDENTIFIER))
203: file = null;
204: }
205:
206: private synchronized File getFile(String name) {
207: ResourceReference rr = getParent();
208: ResourceReference rrtemp = null;
209: Resource p = this ;
210: while (true) {
211: try {
212: if (rr == null)
213: return null;
214: p = rr.lock();
215: if (p instanceof DirectoryResource) {
216: DirectoryResource dr = (DirectoryResource) p;
217: return new File(dr.unsafeGetDirectory(), name);
218: }
219: rrtemp = p.getParent();
220: } catch (InvalidResourceException ex) {
221: ex.printStackTrace();
222: return null;
223: } finally {
224: if (rr != null)
225: rr.unlock();
226: }
227: rr = rrtemp;
228: }
229: }
230:
231: /**
232: * Get this file resource file name.
233: * @return a File instance.
234: * @exception InvalidParentException If no parent is available,
235: * and then the FileReource is unable to get its file.
236: */
237:
238: public synchronized File getFile() {
239: // Have we already computed this ?
240: if (file == null) {
241: // Get the file name:
242: String name = getFilename();
243: if (name == null)
244: name = getIdentifier();
245: // Get the file directory:
246: ResourceReference rr = getParent();
247: ResourceReference rrtemp = null;
248: Resource p = this ;
249: while (true) {
250: try {
251: if (rr == null)
252: throw new InvalidParentException(p
253: .getIdentifier()
254: + " can't find his parent, "
255: + "context : " + p.unsafeGetContext());
256: p = rr.lock();
257: if (p instanceof DirectoryResource) {
258: DirectoryResource dr = (DirectoryResource) p;
259: file = new File(dr.unsafeGetDirectory(), name);
260: return file;
261: }
262: rrtemp = p.getParent();
263: } catch (InvalidResourceException ex) {
264: ex.printStackTrace();
265: return null;
266: } finally {
267: if (rr != null)
268: rr.unlock();
269: }
270: rr = rrtemp;
271: }
272: }
273: return file;
274: }
275:
276: /**
277: * Is that resource still wrapping an existing file ?
278: * If the underlying file has disappeared <string> and if</strong> the
279: * container directory is extensible, remove the resource.
280: * @return true if that resource is wrapping an existing file
281: * @exception org.w3c.tools.resources.MultipleLockException When the
282: * resource try to delete itself (because there is no more file)
283: */
284:
285: public synchronized boolean verify() throws MultipleLockException {
286: File file = getFile();
287: if (!file.exists()) {
288: // Is the parent extensible:
289: ResourceReference rr = getParent();
290: ResourceReference rrtemp = null;
291: Resource p = null;
292:
293: while (true) {
294: try {
295: if (rr == null)
296: return false;
297: p = rr.lock();
298: if (p instanceof DirectoryResource) {
299: DirectoryResource d = (DirectoryResource) p;
300: if (!d.unsafeGetShrinkableFlag())
301: return false;
302: else {
303: // Emit an error message, and delete the resource:
304: String msg = file
305: + ": deleted, removing the FileResource.";
306: getServer().errlog(this , msg);
307: delete();
308: return false;
309: }
310: }
311: rrtemp = p.getParent();
312: } catch (InvalidResourceException ex) {
313: return false;
314: } finally {
315: if (rr != null)
316: rr.unlock();
317: }
318: rr = rrtemp;
319: }
320: } else {
321: return true;
322: }
323: }
324:
325: /**
326: * Update the file related attributes.
327: * The file we serve has changed since the last time we checked it, if
328: * any of the attribute values depend on the file content, this is the
329: * appropriate place to recompute them.
330: */
331:
332: public void updateFileAttributes() {
333: File file = getFile();
334: setValue(ATTR_FILESTAMP, new Long(file.lastModified()));
335: setValue(ATTR_FILE_LENGTH, new Integer((int) file.length()));
336: return;
337: }
338:
339: /**
340: * Update our computed attributes.
341: */
342:
343: public void updateAttributes() {
344: long fstamp = getFile().lastModified();
345: long stamp = getLong(ATTR_FILESTAMP, -1);
346: // should be stamp < fstamp, but it avoid clocks weirdness
347: if ((stamp < 0) || (stamp != fstamp))
348: updateFileAttributes();
349: }
350:
351: /**
352: * Initialize the FileResource instance.
353: */
354:
355: public void initialize(Object values[]) {
356: super .initialize(values);
357: disableEvent();
358: // If we have a filename attribute, update url:
359: String filename = getFilename();
360: if (filename != null) {
361: ResourceReference rr = getParent();
362: if (rr != null) {
363: try {
364: Resource parent = rr.unsafeLock();
365: setValue(ATTR_URL, parent.unsafeGetURLPath()
366: + java.net.URLEncoder
367: .encode(getIdentifier()));
368: } catch (InvalidResourceException ex) {
369: //FIXME
370: } finally {
371: rr.unlock();
372: }
373: }
374: }
375: enableEvent();
376: }
377: }
|