Java tutorial
/* * The MIT License * * Copyright (c) 2013, CloudBees, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.cloudbees.jenkins.support.api; import com.cloudbees.jenkins.support.SupportLogFormatter; import com.cloudbees.jenkins.support.filter.ContentFilter; import com.cloudbees.jenkins.support.filter.PrefilteredContent; import com.cloudbees.jenkins.support.util.StreamUtils; import org.apache.commons.io.IOUtils; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.NoSuchFileException; /** * Content that is stored as a file on disk. * * @author Stephen Connolly */ public class FileContent extends PrefilteredContent { protected final File file; private final long maxSize; private final boolean isBinary; private final static String ENCODING = "UTF-8"; public FileContent(String name, File file) { this(name, file, -1); } public FileContent(String name, File file, long maxSize) { super(name); this.file = file; this.maxSize = maxSize; this.isBinary = isBinary(); } @Override public void writeTo(OutputStream os) throws IOException { try { InputStream is = getInputStream(); if (maxSize == -1) { IOUtils.copy(is, os); } else { try { IOUtils.copy(new TruncatedInputStream(is, maxSize), os); } finally { is.close(); } } } catch (FileNotFoundException e) { OutputStreamWriter osw = new OutputStreamWriter(os, ENCODING); try { PrintWriter pw = new PrintWriter(osw, true); try { pw.println("--- WARNING: Could not attach " + file + " as it cannot currently be found ---"); pw.println(); SupportLogFormatter.printStackTrace(e, pw); } finally { pw.flush(); } } finally { osw.flush(); } } } @Override public void writeTo(OutputStream os, ContentFilter filter) throws IOException { if (isBinary || filter == null) { writeTo(os); } try { if (maxSize == -1) { for (String s : Files.readAllLines(file.toPath())) { String filtered = filter.filter(s); IOUtils.write(filtered, os); } } else { try (TruncatedFileReader reader = new TruncatedFileReader(file, maxSize)) { String s; while ((s = reader.readLine()) != null) { String filtered = filter.filter(s); IOUtils.write(filtered, os); } } } } catch (FileNotFoundException | NoSuchFileException e) { OutputStreamWriter osw = new OutputStreamWriter(os, ENCODING); try { PrintWriter pw = new PrintWriter(osw, true); try { pw.println("--- WARNING: Could not attach " + file + " as it cannot currently be found ---"); pw.println(); SupportLogFormatter.printStackTrace(e, pw); } finally { pw.flush(); } } finally { osw.flush(); } } } /** * Instantiates the {@link InputStream} for the {@link #file}. * @return the {@link InputStream} for the {@link #file}. * @throws IOException if something goes wrong while creating the stream for reading #file. */ protected InputStream getInputStream() throws IOException { return new FileInputStream(file); } @Override public long getTime() throws IOException { return file.lastModified(); } // Check if the file is binary or not private boolean isBinary() { try (InputStream in = getInputStream()) { long size = Files.size(file.toPath()); if (size == 0) { // Empty file, so no need to check return true; } byte[] b = new byte[(size < StreamUtils.DEFAULT_PROBE_SIZE ? (int) size : StreamUtils.DEFAULT_PROBE_SIZE)]; int read = in.read(b); if (read != b.length) { // Something went wrong, so better not to read line by line return true; } return StreamUtils.isNonWhitespaceControlCharacter(b); } catch (IOException e) { // If cannot be checked, then considered as binary, so we do not // read line by line return true; } } /** * {@link InputStream} decorator that chops off the underlying stream at the * specified length * * @author Kohsuke Kawaguchi (from org.kohsuke.stapler) */ private static final class TruncatedInputStream extends FilterInputStream { private long len; TruncatedInputStream(InputStream in, long len) { super(in); this.len = len; } @Override public int read() throws IOException { if (len <= 0) { return -1; } len--; return super.read(); } @Override public int read(byte[] b, int off, int l) throws IOException { int toRead = (int) Math.min(l, len); if (toRead <= 0) { return -1; } int r = super.read(b, off, toRead); if (r > 0) { len -= r; } return r; } @Override public int available() throws IOException { return (int) Math.min(super.available(), len); } @Override public long skip(long n) throws IOException { long r = super.skip(Math.min(len, n)); len -= r; return r; } } private static final class TruncatedFileReader extends BufferedReader { private long len; TruncatedFileReader(File file, long len) throws IOException { super(new InputStreamReader(new FileInputStream(file), ENCODING)); this.len = len; } @Override public String readLine() throws IOException { if (len <= 0) { return null; } String line = super.readLine(); if (line == null) { return null; } int length = line.getBytes(ENCODING).length; int toRead = (length <= len ? length : (int) len); len -= length; byte[] dest = new byte[toRead]; System.arraycopy(line.getBytes(ENCODING), 0, new byte[toRead], 0, toRead); return new String(dest, ENCODING); } } }