View Javadoc

1   /* ObjectPlusFilesOutputStream
2   *
3   * $Id: ObjectPlusFilesOutputStream.java 4646 2006-09-22 17:23:04Z paul_jack $
4   *
5   * Created on Apr 28, 2004
6   *
7   * Copyright (C) 2004 Internet Archive.
8   *
9   * This file is part of the Heritrix web crawler (crawler.archive.org).
10  *
11  * Heritrix is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * any later version.
15  *
16  * Heritrix is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser Public License
22  * along with Heritrix; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25  package org.archive.io;
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.io.ObjectOutputStream;
30  import java.io.OutputStream;
31  import java.util.LinkedList;
32  
33  import org.archive.util.FileUtils;
34  
35  
36  /***
37   * Enhanced ObjectOutputStream which maintains (a stack of) auxiliary
38   * directories and offers convenience methods for serialized objects
39   * to save their related disk files alongside their serialized version.
40   *
41   * @author gojomo
42   */
43  public class ObjectPlusFilesOutputStream extends ObjectOutputStream {
44      LinkedList<File> auxiliaryDirectoryStack = new LinkedList<File>();
45  
46      /***
47       * Constructor
48       *
49       * @param out
50       * @param topDirectory
51       * @throws java.io.IOException
52       */
53      public ObjectPlusFilesOutputStream(OutputStream out, File topDirectory) throws IOException {
54          super(out);
55          auxiliaryDirectoryStack.addFirst(topDirectory);
56      }
57  
58      /***
59       * Add another subdirectory for any file-capture needs during the
60       * current serialization.
61       *
62       * @param dir
63       */
64      public void pushAuxiliaryDirectory(String dir) {
65          auxiliaryDirectoryStack.addFirst(new File(getAuxiliaryDirectory(),dir));
66      }
67  
68      /***
69       * Remove the top subdirectory.
70       *
71       */
72      public void popAuxiliaryDirectory() {
73          auxiliaryDirectoryStack.removeFirst();
74      }
75  
76      /***
77       * Return the current auxiliary directory for storing
78       * files associated with serialized objects.
79       *
80       * @return Auxillary directory.
81       */
82      public File getAuxiliaryDirectory() {
83          return (File)auxiliaryDirectoryStack.getFirst();
84      }
85  
86      /***
87       * Store a snapshot of an object's supporting file to the
88       * current auxiliary directory. Should only be used for
89       * files which are strictly appended-to, because it tries
90       * to use a "hard link" where possible (meaning that
91       * future edits to the original file's contents will
92       * also affect the snapshot).
93       *
94       * Remembers current file extent to allow a future restore
95       * to ignore subsequent appended data.
96       *
97       * @param file
98       * @throws IOException
99       */
100     public void snapshotAppendOnlyFile(File file) throws IOException {
101         // write filename
102         String name = file.getName();
103         writeUTF(name);
104         // write current file length
105         writeLong(file.length());
106         File auxDir = getAuxiliaryDirectory();
107         if(!auxDir.exists()) {
108         	auxDir.mkdirs();
109         }
110         File destination = new File(auxDir,name);
111         hardlinkOrCopy(file, destination);
112     }
113 
114 	/***
115      * Create a backup of this given file, first by trying a "hard
116      * link", then by using a copy if hard linking is unavailable
117      * (either because it is unsupported or the origin and checkpoint
118      * directories are on different volumes).
119      *
120 	 * @param file
121 	 * @param destination
122      * @throws IOException
123 	 */
124 	private void hardlinkOrCopy(File file, File destination) throws IOException {
125 		// For Linux/UNIX, try a hard link first.
126         Process link = Runtime.getRuntime().exec("ln "+file.getAbsolutePath()+" "+destination.getAbsolutePath());
127         // TODO NTFS also supports hard links; add appropriate try
128         try {
129 			link.waitFor();
130 		} catch (InterruptedException e) {
131 			// TODO Auto-generated catch block
132 			e.printStackTrace();
133 		}
134         if(link.exitValue()!=0) {
135         	// hard link failed
136             FileUtils.copyFile(file,destination);
137         }
138 	}
139 
140 }