KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > de > schlichtherle > io > archive > tar > TarOutputArchive


1 /*
2  * TarOutputArchive.java
3  *
4  * Created on 28. Februar 2006, 20:17
5  */

6 /*
7  * Copyright 2006 Schlichtherle IT Services
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package de.schlichtherle.io.archive.tar;
23
24 import de.schlichtherle.io.ChainableIOException;
25 import de.schlichtherle.io.InputIOException;
26 import de.schlichtherle.io.OutputArchiveMetaData;
27 import de.schlichtherle.io.archive.spi.ArchiveEntry;
28 import de.schlichtherle.io.archive.spi.OutputArchive;
29 import de.schlichtherle.io.archive.spi.OutputArchiveBusyException;
30
31 import java.io.File JavaDoc;
32 import java.io.FileInputStream JavaDoc;
33 import java.io.FileNotFoundException JavaDoc;
34 import java.io.FileOutputStream JavaDoc;
35 import java.io.FilterOutputStream JavaDoc;
36 import java.io.IOException JavaDoc;
37 import java.io.InputStream JavaDoc;
38 import java.io.OutputStream JavaDoc;
39 import java.util.Collections JavaDoc;
40 import java.util.Enumeration JavaDoc;
41 import java.util.HashMap JavaDoc;
42 import java.util.Iterator JavaDoc;
43 import java.util.Map JavaDoc;
44
45 import org.apache.tools.tar.TarOutputStream;
46
47 /**
48  * An implementation of {@link OutputArchive} to write TAR archives.
49  * Because the TAR file format is so dumb that we need to know each entry's
50  * length in advance, we write the entries to a temp file before actually
51  * copying them to the underlying TarOutputStream.
52  *
53  * @author Christian Schlichtherle
54  * @version @version@
55  * @since TrueZIP 6.0
56  */

57 public class TarOutputArchive
58         extends TarOutputStream
59         implements OutputArchive {
60
61     /**
62      * Maps entry names to tar entries [String -> TarEntry].
63      */

64     private final Map JavaDoc entries = new HashMap JavaDoc();
65
66     private boolean busy;
67
68     private final Map JavaDoc temps = new HashMap JavaDoc();
69
70     private OutputArchiveMetaData metaData;
71
72     public TarOutputArchive(OutputStream JavaDoc out) {
73         super(out);
74     }
75
76     public int getNumArchiveEntries() {
77         return entries.size();
78     }
79
80     public Enumeration JavaDoc getArchiveEntries() {
81         return Collections.enumeration(entries.values());
82     }
83
84     public ArchiveEntry getArchiveEntry(String JavaDoc name) {
85         return (TarEntry) entries.get(name);
86     }
87
88     public OutputStream JavaDoc getOutputStream(
89             final ArchiveEntry entry,
90             final ArchiveEntry srcEntry)
91     throws IOException {
92         final TarEntry tarEntry = (TarEntry) entry;
93         if (srcEntry instanceof TarEntry)
94             tarEntry.setSize(((TarEntry) srcEntry).getSize());
95
96         return createEntryOutputStream(tarEntry, srcEntry);
97     }
98
99     protected boolean busy() {
100         return busy;
101     }
102
103     protected OutputStream JavaDoc createEntryOutputStream(
104             final TarEntry entry,
105             final ArchiveEntry srcEntry)
106     throws IOException {
107         if (srcEntry instanceof TarEntry && !busy()) { // FIXME: Explain this!
108
return new EntryOutputStream(entry);
109         } else {
110             final File JavaDoc temp = File.createTempFile("tar", null);
111             return new EntryTempOutputStream(entry, temp);
112         }
113     }
114
115     /**
116      * This entry output stream writes directly to our subclass.
117      * It may be used only if <code>entry</code> holds enough information to
118      * write the TAR header and this TarOutputStream is not currently busy
119      * writing another entry.
120      * This is detected by {@link #getOutputStream} in advance.
121      */

122     protected class EntryOutputStream extends FilterOutputStream JavaDoc {
123
124         private boolean closed;
125
126         public EntryOutputStream(TarEntry entry)
127         throws IOException {
128             super(TarOutputArchive.this);
129             assert !busy();
130             putNextEntry(entry);
131             busy = true;
132             entries.put(entry.getName(), entry);
133         }
134
135         public void write(int b) throws IOException {
136             out.write(b);
137         }
138
139         public void write(byte[] b) throws IOException {
140             out.write(b);
141         }
142
143         public void write(byte[] b, int off, int len) throws IOException {
144             out.write(b, off, len);
145         }
146
147         public void close() throws IOException {
148             if (closed)
149                 return;
150
151             // Order is important here!
152
closed = true;
153             busy = false;
154             closeEntry();
155             storeAllRemainingTempEntries();
156         }
157     }
158     
159     /**
160      * This entry output stream writes the entry to a temporary file.
161      * Upon a call to <code>close()</code>, the temporary file provided to
162      * its constructor is then copied to this <code>TarOutputStream</code>
163      * and finally deleted.
164      */

165     protected class EntryTempOutputStream extends FileOutputStream JavaDoc {
166
167         private final TarEntry entry;
168         private final File JavaDoc temp;
169         private boolean closed;
170
171         public EntryTempOutputStream(
172                 final TarEntry entry,
173                 final File JavaDoc temp)
174         throws IOException {
175             super(temp);
176             this.entry = entry;
177             this.temp = temp;
178             entries.put(entry.getName(), entry);
179         }
180
181         public void close() throws IOException {
182             // There is a finalizer defined in the super class, so we need
183
// to protect against multiple calls of this method
184
// (it's a good idea anyway)
185
if (closed)
186                 return;
187
188             // Order is important here!
189
closed = true;
190             super.close();
191             temps.put(entry, temp);
192             if (!busy())
193                 storeAllRemainingTempEntries();
194         }
195     }
196
197     private void storeAllRemainingTempEntries()
198     throws IOException {
199         ChainableIOException exception = null;
200
201         for (Iterator JavaDoc it = temps.entrySet().iterator(); it.hasNext();) {
202             final Map.Entry JavaDoc elem = (Map.Entry JavaDoc) it.next();
203             final TarEntry entry = (TarEntry) elem.getKey();
204             final File JavaDoc temp = (File JavaDoc) elem.getValue();
205             try {
206                 storeTempEntry(entry, temp);
207             } catch (FileNotFoundException JavaDoc failure) {
208                 // Input exception - let's continue!
209
exception = new ChainableIOException(exception, failure);
210             } catch (InputIOException failure) {
211                 // Input exception - let's continue!
212
exception = new ChainableIOException(exception, failure);
213             } catch (IOException failure) {
214                 // Something's wrong writing this TarOutputStream!
215
throw new ChainableIOException(exception, failure);
216             } finally {
217                 // Remove entry anyway - if something is wrong opening or reading
218
// the temp file, there is probably an error in this code and
219
// we can't really recover from this situation anyway.
220
// Removing this temp ensures that we will not see an exception
221
// for the same temp in future again.
222
// In addition, storeTempEntry() has removed the temp file anyway.
223
it.remove();
224             }
225         }
226
227         if (exception != null)
228             throw exception.sortPriority();
229         
230         assert temps.isEmpty();
231     }
232
233     private void storeTempEntry(final TarEntry entry, final File JavaDoc temp)
234     throws IOException {
235         assert !busy();
236
237         try {
238             final InputStream JavaDoc in = new FileInputStream JavaDoc(temp);
239             try {
240                 entry.setSize(temp.length());
241                 putNextEntry(entry);
242                 try {
243                     // Use asynchronous high-performance data pump!
244
de.schlichtherle.io.File.cat(in, this);
245                 } finally {
246                     closeEntry();
247                 }
248             } finally {
249                 in.close();
250             }
251         } finally {
252             if (!temp.delete()) // may fail on Windoze if in.close() failed!
253
temp.deleteOnExit(); // we're bullish never to leavy any temps!
254
}
255     }
256
257     public void storeDirectory(ArchiveEntry entry)
258     throws IOException {
259         assert entry.isDirectory();
260         final TarEntry te = (TarEntry) entry;
261         te.setSize(0);
262
263         // Order is important!
264
putNextEntry(te);
265         entries.put(te.getName(), te);
266         closeEntry();
267     }
268
269     public void close() throws IOException {
270         try {
271             super.close();
272         } finally {
273             deleteAllRemainingTempEntries();
274         }
275     }
276
277     private void deleteAllRemainingTempEntries()
278     throws IOException {
279         for (Iterator JavaDoc it = temps.entrySet().iterator(); it.hasNext();) {
280             final Map.Entry JavaDoc elem = (Map.Entry JavaDoc) it.next();
281             final File JavaDoc temp = (File JavaDoc) elem.getValue();
282             if (!temp.delete()) // may fail on Windoze if in.close() failed!
283
temp.deleteOnExit(); // we're bullish never to leavy any temps!
284
it.remove();
285         }
286         assert temps.isEmpty();
287     }
288
289     //
290
// Metadata stuff.
291
//
292

293     public OutputArchiveMetaData getMetaData() {
294         return metaData;
295     }
296
297     public void setMetaData(OutputArchiveMetaData metaData) {
298         this.metaData = metaData;
299     }
300 }
301
Popular Tags