001: /* ObjectPlusFilesOutputStream
002: *
003: * $Id: ObjectPlusFilesOutputStream.java 4646 2006-09-22 17:23:04Z paul_jack $
004: *
005: * Created on Apr 28, 2004
006: *
007: * Copyright (C) 2004 Internet Archive.
008: *
009: * This file is part of the Heritrix web crawler (crawler.archive.org).
010: *
011: * Heritrix is free software; you can redistribute it and/or modify
012: * it under the terms of the GNU Lesser Public License as published by
013: * the Free Software Foundation; either version 2.1 of the License, or
014: * any later version.
015: *
016: * Heritrix is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: * GNU Lesser Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser Public License
022: * along with Heritrix; if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: */
025: package org.archive.io;
026:
027: import java.io.File;
028: import java.io.IOException;
029: import java.io.ObjectOutputStream;
030: import java.io.OutputStream;
031: import java.util.LinkedList;
032:
033: import org.archive.util.FileUtils;
034:
035: /**
036: * Enhanced ObjectOutputStream which maintains (a stack of) auxiliary
037: * directories and offers convenience methods for serialized objects
038: * to save their related disk files alongside their serialized version.
039: *
040: * @author gojomo
041: */
042: public class ObjectPlusFilesOutputStream extends ObjectOutputStream {
043: LinkedList<File> auxiliaryDirectoryStack = new LinkedList<File>();
044:
045: /**
046: * Constructor
047: *
048: * @param out
049: * @param topDirectory
050: * @throws java.io.IOException
051: */
052: public ObjectPlusFilesOutputStream(OutputStream out,
053: File topDirectory) throws IOException {
054: super (out);
055: auxiliaryDirectoryStack.addFirst(topDirectory);
056: }
057:
058: /**
059: * Add another subdirectory for any file-capture needs during the
060: * current serialization.
061: *
062: * @param dir
063: */
064: public void pushAuxiliaryDirectory(String dir) {
065: auxiliaryDirectoryStack.addFirst(new File(
066: getAuxiliaryDirectory(), dir));
067: }
068:
069: /**
070: * Remove the top subdirectory.
071: *
072: */
073: public void popAuxiliaryDirectory() {
074: auxiliaryDirectoryStack.removeFirst();
075: }
076:
077: /**
078: * Return the current auxiliary directory for storing
079: * files associated with serialized objects.
080: *
081: * @return Auxillary directory.
082: */
083: public File getAuxiliaryDirectory() {
084: return (File) auxiliaryDirectoryStack.getFirst();
085: }
086:
087: /**
088: * Store a snapshot of an object's supporting file to the
089: * current auxiliary directory. Should only be used for
090: * files which are strictly appended-to, because it tries
091: * to use a "hard link" where possible (meaning that
092: * future edits to the original file's contents will
093: * also affect the snapshot).
094: *
095: * Remembers current file extent to allow a future restore
096: * to ignore subsequent appended data.
097: *
098: * @param file
099: * @throws IOException
100: */
101: public void snapshotAppendOnlyFile(File file) throws IOException {
102: // write filename
103: String name = file.getName();
104: writeUTF(name);
105: // write current file length
106: writeLong(file.length());
107: File auxDir = getAuxiliaryDirectory();
108: if (!auxDir.exists()) {
109: auxDir.mkdirs();
110: }
111: File destination = new File(auxDir, name);
112: hardlinkOrCopy(file, destination);
113: }
114:
115: /**
116: * Create a backup of this given file, first by trying a "hard
117: * link", then by using a copy if hard linking is unavailable
118: * (either because it is unsupported or the origin and checkpoint
119: * directories are on different volumes).
120: *
121: * @param file
122: * @param destination
123: * @throws IOException
124: */
125: private void hardlinkOrCopy(File file, File destination)
126: throws IOException {
127: // For Linux/UNIX, try a hard link first.
128: Process link = Runtime.getRuntime().exec(
129: "ln " + file.getAbsolutePath() + " "
130: + destination.getAbsolutePath());
131: // TODO NTFS also supports hard links; add appropriate try
132: try {
133: link.waitFor();
134: } catch (InterruptedException e) {
135: // TODO Auto-generated catch block
136: e.printStackTrace();
137: }
138: if (link.exitValue() != 0) {
139: // hard link failed
140: FileUtils.copyFile(file, destination);
141: }
142: }
143:
144: }
|