001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/ http://izpack.codehaus.org/
005: *
006: * Copyright 2007 Dennis Reil
007: *
008: * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
009: * in compliance with the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software distributed under the License
014: * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
015: * or implied. See the License for the specific language governing permissions and limitations under
016: * the License.
017: */
018: package com.izforge.izpack.io;
019:
020: import java.io.File;
021: import java.io.FileInputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.util.zip.GZIPInputStream;
025:
026: import com.izforge.izpack.util.Debug;
027:
028: /**
029: * An inputstream which transparently spans over multiple volumes. The amount of volumes has to be
030: * specified
031: *
032: * @author Dennis Reil, <Dennis.Reil@reddot.de>
033: */
034: public class FileSpanningInputStream extends InputStream {
035:
036: private static final int EOF = -1;
037:
038: protected FileInputStream fileinputstream;
039:
040: protected String volumename;
041:
042: protected int currentvolumeindex;
043:
044: protected int volumestotal;
045:
046: protected static boolean nextvolumenotfound = false;
047:
048: protected long filepointer;
049:
050: protected GZIPInputStream zippedinputstream;
051:
052: protected byte[] magicnumber;
053:
054: public FileSpanningInputStream(File volume, int volumestotal)
055: throws IOException {
056: fileinputstream = new FileInputStream(volume);
057: zippedinputstream = new GZIPInputStream(fileinputstream);
058: currentvolumeindex = 0;
059: volumename = volume.getAbsolutePath();
060: this .volumestotal = volumestotal;
061: filepointer = 0;
062:
063: // read magic number
064: this .magicnumber = new byte[FileSpanningOutputStream.MAGIC_NUMER_LENGTH];
065: zippedinputstream.read(this .magicnumber);
066: // this.read(this.magicnumber);
067: Debug.trace("Opening stream to " + volume + " magicnr is "
068: + magicnumber);
069: // reset filepointer
070: filepointer = 0;
071: }
072:
073: public FileSpanningInputStream(String volumename, int volumestotal)
074: throws IOException {
075: this (new File(volumename), volumestotal);
076: }
077:
078: /**
079: * checks if the MagicNumber of this stream is valid. The stream has to be opened right before.
080: *
081: * @return wether the magic number is valid or not
082: * @throws IOException
083: */
084: private boolean isMagicNumberValid() throws IOException {
085: Debug.trace("trying to read magic number");
086: boolean valid = false;
087: byte[] magicnumberofvolume = new byte[FileSpanningOutputStream.MAGIC_NUMER_LENGTH];
088: long oldfilepointer = this .filepointer;
089: // this.read(magicnumberofvolume);
090: this .zippedinputstream.read(magicnumberofvolume);
091: this .filepointer = oldfilepointer;
092: Debug.trace("MagicNr is " + magicnumberofvolume);
093: if ((magicnumberofvolume != null) && (this .magicnumber != null)) {
094: if (magicnumberofvolume.length != this .magicnumber.length) {
095: // magicnumbers aren't valid
096: valid = false;
097: } else {
098: boolean errorfound = false;
099: // check if magicnumbers are identical
100: for (int i = 0; i < magicnumberofvolume.length; i++) {
101: byte op1 = magicnumberofvolume[i];
102: byte op2 = this .magicnumber[i];
103: if (op1 != op2) {
104: errorfound = true;
105: break;
106: }
107: }
108: valid = !errorfound;
109: }
110: }
111: return valid;
112: }
113:
114: /**
115: * creates an inputstream to the next volume
116: *
117: * @return true - an inputstream to the next volume has been created false - the last volume was
118: * reached
119: * @throws IOException
120: */
121: private boolean createInputStreamToNextVolume() throws IOException {
122: currentvolumeindex++;
123: // have we reached the last volume?
124: if (currentvolumeindex >= volumestotal) {
125: Debug.trace("last volume reached.");
126: return false;
127: }
128: // the next volume name
129: String nextvolumename = volumename + "." + currentvolumeindex;
130: Debug.trace("Trying to use next volume: " + nextvolumename);
131: File nextvolumefile = new File(nextvolumename);
132: if (!nextvolumefile.exists()) {
133: currentvolumeindex--;
134: nextvolumenotfound = true;
135: Debug.trace("volume not found");
136: throw new VolumeNotFoundException(nextvolumename
137: + "was not found.", nextvolumename);
138: }
139: Debug.trace("next volume found.");
140: // try to open new stream to next volume
141: fileinputstream = new FileInputStream(nextvolumefile);
142: zippedinputstream = new GZIPInputStream(fileinputstream);
143: // check magic number
144: if (!this .isMagicNumberValid()) {
145: currentvolumeindex--;
146: nextvolumenotfound = true;
147: Debug
148: .trace("volume found, but magic number incorrect. Maybe not a volume of the same version.");
149: throw new CorruptVolumeException(
150: nextvolumename
151: + "was found, but has magic number error. Maybe not the right version?",
152: nextvolumename);
153: }
154: // everything fine
155: nextvolumenotfound = false;
156: return true;
157: }
158:
159: /*
160: * (non-Javadoc)
161: *
162: * @see java.io.InputStream#available()
163: */
164: public int available() throws IOException {
165: if (nextvolumenotfound) {
166: createInputStreamToNextVolume();
167: }
168: // return fileinputstream.available();
169: return zippedinputstream.available();
170: }
171:
172: /*
173: * (non-Javadoc)
174: *
175: * @see java.io.InputStream#close()
176: */
177: public void close() throws IOException {
178: zippedinputstream.close();
179: fileinputstream.close();
180: }
181:
182: /*
183: * (non-Javadoc)
184: *
185: * @see java.io.InputStream#read()
186: */
187: public int read() throws IOException {
188: if (nextvolumenotfound) {
189: // the next volume was not found, so try to create a new input stream to next volume
190: createInputStreamToNextVolume();
191: }
192: int nextbyte = zippedinputstream.read();
193: filepointer++;
194: if (nextbyte == EOF) {
195: // if end of file is reached, try to open InputStream to next volume
196: // close the inputstream
197: try {
198: zippedinputstream.close();
199: } catch (Exception e) {
200: // do nothing
201: }
202:
203: if (createInputStreamToNextVolume()) {
204: // try to read next byte
205: nextbyte = zippedinputstream.read();
206: filepointer++;
207: }
208: }
209: return nextbyte;
210: }
211:
212: /*
213: * (non-Javadoc)
214: *
215: * @see java.io.InputStream#read(byte[], int, int)
216: */
217: public int read(byte[] b, int off, int len) throws IOException {
218: if (nextvolumenotfound) {
219: // the next volume was not found, so try to create a new input stream to next volume
220: createInputStreamToNextVolume();
221: }
222: int bytesread = zippedinputstream.read(b, off, len);
223: filepointer += bytesread;
224: if (bytesread == EOF) {
225: filepointer++; // bytesread was -1;
226: System.out.println("EOF reached.");
227: // close the inputstream
228: try {
229: zippedinputstream.close();
230: } catch (Exception e) {
231: // do nothing
232: }
233: // try to open next volume
234: if (createInputStreamToNextVolume()) {
235: // try to read next bytes
236: Debug.trace("next volume opened, continuing read");
237: bytesread = zippedinputstream.read(b, off, len);
238: filepointer += bytesread;
239: // System.out.println("read into buffer: " + bytesread + " Bytes");
240: }
241: }
242: // System.out.println("return from read into buffer: " + bytesread + " Bytes");
243: return bytesread;
244: }
245:
246: /*
247: * (non-Javadoc)
248: *
249: * @see java.io.InputStream#read(byte[])
250: */
251: public int read(byte[] b) throws IOException {
252: return this .read(b, 0, b.length);
253: }
254:
255: /*
256: * (non-Javadoc)
257: *
258: * @see java.io.InputStream#skip(long)
259: */
260: public long skip(long n) throws IOException {
261: if (nextvolumenotfound) {
262: // the next volume was not found, so try to create a new input stream to next volume
263: createInputStreamToNextVolume();
264: }
265: long bytesskipped = 0;
266: byte[] buffer = new byte[4096];
267: try {
268: while (bytesskipped < n) {
269: int maxBytes = (int) Math.min(n - bytesskipped,
270: buffer.length);
271:
272: int bytesInBuffer = this .read(buffer, 0, maxBytes);
273: if (bytesInBuffer == -1)
274: throw new IOException(
275: "Unexpected end of stream (installer corrupted?)");
276:
277: bytesskipped += bytesInBuffer;
278: }
279: } catch (VolumeNotFoundException vnfe) {
280: vnfe.setAlreadyskippedbytes(bytesskipped);
281: throw vnfe;
282: }
283: return bytesskipped;
284: }
285:
286: /**
287: * Returns the name of the volume
288: *
289: * @return the name of the volume
290: */
291: public String getVolumename() {
292: return volumename;
293: }
294:
295: /**
296: * Sets the volumename
297: *
298: * @param volumename
299: */
300: public void setVolumename(String volumename) {
301: Debug.trace("new volumename: " + volumename);
302: // try to get the volumename from the given volume file
303: // the first volume has no suffix, additional volumes have a .INDEX# suffix
304: String volumesuffix = "." + currentvolumeindex;
305: String nextvolumesuffix = "." + (currentvolumeindex + 1);
306: if (volumename.endsWith(volumesuffix)) {
307: this .volumename = volumename.substring(0, volumename
308: .lastIndexOf(volumesuffix));
309: } else if (volumename.endsWith(nextvolumesuffix)) {
310: this .volumename = volumename.substring(0, volumename
311: .lastIndexOf(nextvolumesuffix));
312: } else {
313: this .volumename = volumename;
314: }
315: Debug.trace("Set volumename to: " + this .volumename);
316: }
317:
318: /**
319: * Returns the current position in the file. Notice: this is the global position in all volumes.
320: *
321: * @return the current position in file.
322: */
323: public long getFilepointer() {
324: return filepointer;
325: }
326:
327: }
|