/********************************************************************************
*
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2003, ThoughtWorks, Inc.
* 200 E. Randolph, 25th Floor
* Chicago, IL 60601 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* + Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
********************************************************************************/
package net.sourceforge.cruisecontrol.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.Deflater;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.log4j.Logger;
/**
* Class which passes data stored StdoutBuffer through GZip compression. The StdoutBuffer,
* thus, stores the compressed data.
*
* @author <a href="mailto:dtihelka@kky.zcu.cz">Dan Tihelka</a>
*/
public final class GZippedStdoutBuffer extends StdoutBuffer {
/**
* Constructor.
* @param log the instance of logger through which to log.
*/
public GZippedStdoutBuffer(Logger log) {
super(log);
} // GZippedStdoutBuffer
/*
* ----------- PROTECTED BLOCK -----------
*/
/**
* The overriding of {@link StdoutBuffer#dataEncoder(OutputStream)}
* @return the GZipOutputStream data encoder
*/
@Override
protected OutputStream dataEncoder(OutputStream stream) {
try {
returnBaseStream = false;
/* Create GZIP with the best compression on */
return new GZIPOutputStream(stream) {
{ def.setLevel(Deflater.BEST_COMPRESSION); }
};
} catch (IOException e) {
log.error("Cannot create GZipped chunker, returning the chunker from base class", e);
returnBaseStream = true;
return super.dataEncoder(stream);
}
}
/**
* The overriding of {@link StdoutBuffer#dataDecoder(InputStream)}
* @return the {@link GZIPInputStream} data decoder.
*/
@Override
protected InputStream dataDecoder(InputStream stream) {
/* Get the delayed input stream, otherwise GZIPInputStream constructor will block if there
* are no data to read in stream ... */
return returnBaseStream ? super.dataDecoder(stream)
: new DelayedGZipInputStream(stream);
}
/*
* ----------- ATTRIBUTES -----------
*/
/**
* Set to <code>true</code>, if the allocation of {@link GZippedStdoutBuffer} in
* {@link #dataEncoder(OutputStream)} failed and {@link StdoutBuffer#dataEncoder(OutputStream)}
* was returned.
*/
private boolean returnBaseStream = false;
/*
* ----------- INNER CLASSES -----------
*/
/**
* Wrapper for GZIPInputStream. It delays the instantiation of {@link GZIPInputStream} until
* something is read from it as {@link GZIPInputStream#GZIPInputStream(InputStream)} reads a
* header from the stream provided. When the header is not available yet, the constructor is
* blocked which may lead to deadlock in {@link StdoutBuffer#getContent()} call.
* </p>
* It passes all the calls to the GZIPInputStream class.
*/
private class DelayedGZipInputStream extends InputStream {
/**
* Constructor
* @param stream the stream passed to {@link GZIPInputStream} when instantiated
*/
public DelayedGZipInputStream(final InputStream stream) {
this.stream = stream;
}
/**
* Implementation of {@link InputStream#available()}
*/
@Override
public final int available() throws IOException {
return instantiate().available();
} // available
/**
* Implementation of {@link InputStream#close()}
*/
@Override
public final void close() throws IOException {
instantiate().close();
} // close
/**
* Implementation of {@link InputStream#mark(int)}; does nothing
*/
@Override
public final void mark(int readlimit) { /* Nothing here */
} // mark
/**
* Implementation of InputStream#markSupported(); always returns <code>false</code>
*/
@Override
public final boolean markSupported() {
return false;
} // markSupported
/**
* Implementation of {@link InputStream#read()}
*/
@Override
public final int read() throws IOException {
return instantiate().read();
}
/**
* Implementation of {@link InputStream#read(byte[], int, int)}
*/
@Override
public final int read(byte[] outBuff, int from, int len) throws IOException {
return instantiate().read(outBuff, from, len);
} // read
/**
* Implementation of {@link InputStream#reset()}
*/
@Override
public final void reset() throws IOException {
instantiate().reset();
} // reset
/**
* Implementation of {@link InputStream#skip(long)}
*/
@Override
public final long skip(long num) throws IOException {
return instantiate().skip(num);
} // skip
/**
* @return If {@link #instantiated} == <code>false</code> it instantiates {@link GZIPInputStream} with
* input set to the current {@link #stream}. If {@link #instantiated} == <code>true</code>,
* the {@link #stream} (instance of {@link GZIPInputStream}).
* @exception IOException if an I/O error has occurred
*/
private InputStream instantiate() throws IOException {
/* Instantiate, if not instantiated yet */
if (!instantiated) {
stream = new GZIPInputStream(stream, chunkSize);
instantiated = true;
}
/* Return the current stream instance */
return stream;
}
/**
* Contains <code>true</code> if {@link #stream} is instance of {@link GZIPInputStream}, or
* <code>false</code> if it still is the stream instance set in the constructor.
*/
private boolean instantiated;
/**
* The input stream which the data are read from.
*/
private InputStream stream;
} // DelayedGZipInputStream
} // GZippedStdoutBuffer