/hudson-core/src/main/java/hudson/util/CompressedFile.java

http://github.com/hudson/hudson · Java · 165 lines · 81 code · 16 blank · 68 comment · 5 complexity · 521eb5c8a6564d2af09c6adc92c866ae MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.util;
  25. import hudson.Util;
  26. import java.io.File;
  27. import java.io.FileInputStream;
  28. import java.io.FileNotFoundException;
  29. import java.io.FileOutputStream;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.io.InputStreamReader;
  33. import java.io.OutputStream;
  34. import java.io.Reader;
  35. import java.util.concurrent.ExecutorService;
  36. import java.util.concurrent.LinkedBlockingQueue;
  37. import java.util.concurrent.ThreadPoolExecutor;
  38. import java.util.concurrent.TimeUnit;
  39. import java.util.logging.Level;
  40. import java.util.logging.Logger;
  41. import java.util.zip.GZIPInputStream;
  42. import java.util.zip.GZIPOutputStream;
  43. /**
  44. * Represents write-once read-many file that can be optiionally compressed
  45. * to save disk space. This is used for console output and other bulky data.
  46. *
  47. * <p>
  48. * In this class, the data on the disk can be one of two states:
  49. * <ol>
  50. * <li>Uncompressed, in which case the original data is available in the specified file name.
  51. * <li>Compressed, in which case the gzip-compressed data is available in the specifiled file name + ".gz" extension.
  52. * </ol>
  53. *
  54. * Once the file is written and completed, it can be compressed asynchronously
  55. * by {@link #compress()}.
  56. *
  57. * @author Kohsuke Kawaguchi
  58. */
  59. public class CompressedFile {
  60. /**
  61. * The name of the raw file.
  62. */
  63. private final File file;
  64. /**
  65. * The name of the compressed file.
  66. */
  67. private final File gz;
  68. public CompressedFile(File file) {
  69. this.file = file;
  70. this.gz = new File(file.getParentFile(),file.getName()+".gz");
  71. }
  72. /**
  73. * Gets the OutputStream to write to the file.
  74. */
  75. public OutputStream write() throws FileNotFoundException {
  76. if(gz.exists())
  77. gz.delete();
  78. return new FileOutputStream(file);
  79. }
  80. /**
  81. * Reads the contents of a file.
  82. */
  83. public InputStream read() throws IOException {
  84. if(file.exists())
  85. return new FileInputStream(file);
  86. // check if the compressed file exists
  87. if(gz.exists())
  88. return new GZIPInputStream(new FileInputStream(gz));
  89. // no such file
  90. throw new FileNotFoundException(file.getName());
  91. }
  92. /**
  93. * Loads the file content as a string.
  94. */
  95. public String loadAsString() throws IOException {
  96. long sizeGuess;
  97. if(file.exists())
  98. sizeGuess = file.length();
  99. else
  100. if(gz.exists())
  101. sizeGuess = gz.length()*2;
  102. else
  103. return "";
  104. StringBuilder str = new StringBuilder((int)sizeGuess);
  105. Reader r = new InputStreamReader(read());
  106. char[] buf = new char[8192];
  107. int len;
  108. while((len=r.read(buf,0,buf.length))>0)
  109. str.append(buf,0,len);
  110. r.close();
  111. return str.toString();
  112. }
  113. /**
  114. * Asynchronously schedules the compression of this file.
  115. *
  116. * <p>
  117. * Once the file is compressed, the original will be removed and
  118. * the further reading will be done from the compressed stream.
  119. */
  120. public void compress() {
  121. compressionThread.submit(new Runnable() {
  122. public void run() {
  123. try {
  124. InputStream in = read();
  125. OutputStream out = new GZIPOutputStream(new FileOutputStream(gz));
  126. try {
  127. Util.copyStream(in,out);
  128. } finally {
  129. in.close();
  130. out.close();
  131. }
  132. // if the compressed file is created successfully, remove the original
  133. file.delete();
  134. } catch (IOException e) {
  135. LOGGER.log(Level.WARNING, "Failed to compress "+file,e);
  136. gz.delete(); // in case a processing is left in the middle
  137. }
  138. }
  139. });
  140. }
  141. /**
  142. * Executor used for compression. Limited up to one thread since
  143. * this should be a fairly low-priority task.
  144. */
  145. private static final ExecutorService compressionThread = new ThreadPoolExecutor(
  146. 0, 1, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(),
  147. new ExceptionCatchingThreadFactory(new DaemonThreadFactory()));
  148. private static final Logger LOGGER = Logger.getLogger(CompressedFile.class.getName());
  149. }