001: /*
002: JSPWiki - a JSP-based WikiWiki clone.
003:
004: Copyright (C) 2001 Janne Jalkanen (Janne.Jalkanen@iki.fi)
005:
006: This program is free software; you can redistribute it and/or modify
007: it under the terms of the GNU Lesser General Public License as published by
008: the Free Software Foundation; either version 2.1 of the License, or
009: (at your option) any later version.
010:
011: This program is distributed in the hope that it will be useful,
012: but WITHOUT ANY WARRANTY; without even the implied warranty of
013: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: GNU Lesser General Public License for more details.
015:
016: You should have received a copy of the GNU Lesser General Public License
017: along with this program; if not, write to the Free Software
018: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: */
020:
021: package com.ecyrd.jspwiki;
022:
023: import java.io.*;
024: import java.nio.ByteBuffer;
025: import java.nio.CharBuffer;
026: import java.nio.charset.CharacterCodingException;
027: import java.nio.charset.Charset;
028: import java.nio.charset.CharsetDecoder;
029: import java.nio.charset.CodingErrorAction;
030:
031: import org.apache.log4j.Logger;
032:
033: /**
034: * Generic utilities related to file and stream handling.
035: */
036: // FIXME3.0: This class will move to "util" directory in 3.0
037: public final class FileUtil {
038: /** Size of the buffer used when copying large chunks of data. */
039: private static final int BUFFER_SIZE = 4096;
040: private static final Logger log = Logger.getLogger(FileUtil.class);
041:
042: /**
043: * Private constructor prevents instantiation.
044: */
045: private FileUtil() {
046: }
047:
048: /**
049: * Makes a new temporary file and writes content into it. The temporary
050: * file is created using <code>File.createTempFile()</code>, and the usual
051: * semantics apply. The files are not deleted automatically in exit.
052: *
053: * @param content Initial content of the temporary file.
054: * @param encoding Encoding to use.
055: * @return The handle to the new temporary file
056: * @throws IOException If the content creation failed.
057: * @see java.io.File#createTempFile(String,String,File)
058: */
059: public static File newTmpFile(String content, String encoding)
060: throws IOException {
061: Writer out = null;
062: Reader in = null;
063: File f = null;
064:
065: try {
066: f = File.createTempFile("jspwiki", null);
067:
068: in = new StringReader(content);
069:
070: out = new OutputStreamWriter(new FileOutputStream(f),
071: encoding);
072:
073: copyContents(in, out);
074: } finally {
075: if (in != null)
076: in.close();
077: if (out != null)
078: out.close();
079: }
080:
081: return f;
082: }
083:
084: /**
085: * Creates a new temporary file using the default encoding
086: * of ISO-8859-1 (Latin1).
087: *
088: * @param content The content to put into the file.
089: * @throws IOException If writing was unsuccessful.
090: * @return A handle to the newly created file.
091: * @see #newTmpFile( String, String )
092: * @see java.io.File#createTempFile(String,String,File)
093: */
094: public static File newTmpFile(String content) throws IOException {
095: return newTmpFile(content, "ISO-8859-1");
096: }
097:
098: /**
099: * Runs a simple command in given directory.
100: * The environment is inherited from the parent process (e.g. the
101: * one in which this Java VM runs).
102: *
103: * @return Standard output from the command.
104: * @param command The command to run
105: * @param directory The working directory to run the command in
106: * @throws IOException If the command failed
107: * @throws InterruptedException If the command was halted
108: */
109: public static String runSimpleCommand(String command,
110: String directory) throws IOException, InterruptedException {
111: StringBuffer result = new StringBuffer();
112:
113: log.info("Running simple command " + command + " in "
114: + directory);
115:
116: Process process = Runtime.getRuntime().exec(command, null,
117: new File(directory));
118:
119: BufferedReader stdout = null;
120: BufferedReader stderr = null;
121:
122: try {
123: stdout = new BufferedReader(new InputStreamReader(process
124: .getInputStream()));
125: stderr = new BufferedReader(new InputStreamReader(process
126: .getErrorStream()));
127:
128: String line;
129:
130: while ((line = stdout.readLine()) != null) {
131: result.append(line + "\n");
132: }
133:
134: StringBuffer error = new StringBuffer();
135: while ((line = stderr.readLine()) != null) {
136: error.append(line + "\n");
137: }
138:
139: if (error.length() > 0) {
140: log.error("Command failed, error stream is: " + error);
141: }
142:
143: process.waitFor();
144:
145: } finally {
146: // we must close all by exec(..) opened streams: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
147: process.getInputStream().close();
148: if (stdout != null)
149: stdout.close();
150: if (stderr != null)
151: stderr.close();
152: }
153:
154: return result.toString();
155: }
156:
157: /**
158: * Just copies all characters from <I>in</I> to <I>out</I>. The copying
159: * is performed using a buffer of bytes.
160: *
161: * @since 1.5.8
162: * @param in The reader to copy from
163: * @param out The reader to copy to
164: * @throws IOException If reading or writing failed.
165: */
166: public static void copyContents(Reader in, Writer out)
167: throws IOException {
168: char[] buf = new char[BUFFER_SIZE];
169: int bytesRead = 0;
170:
171: while ((bytesRead = in.read(buf)) > 0) {
172: out.write(buf, 0, bytesRead);
173: }
174:
175: out.flush();
176: }
177:
178: /**
179: * Just copies all bytes from <I>in</I> to <I>out</I>. The copying is
180: * performed using a buffer of bytes.
181: *
182: * @since 1.9.31
183: * @param in The inputstream to copy from
184: * @param out The outputstream to copy to
185: * @throws IOException In case reading or writing fails.
186: */
187: public static void copyContents(InputStream in, OutputStream out)
188: throws IOException {
189: byte[] buf = new byte[BUFFER_SIZE];
190: int bytesRead = 0;
191:
192: while ((bytesRead = in.read(buf)) > 0) {
193: out.write(buf, 0, bytesRead);
194: }
195:
196: out.flush();
197: }
198:
199: /**
200: * Reads in file contents.
201: * <P>
202: * This method is smart and falls back to ISO-8859-1 if the input stream does not
203: * seem to be in the specified encoding.
204: *
205: * @param input The InputStream to read from.
206: * @param encoding The encoding to assume at first.
207: * @return A String, interpreted in the "encoding", or, if it fails, in Latin1.
208: * @throws IOException If the stream cannot be read or the stream cannot be
209: * decoded (even) in Latin1
210: */
211: public static String readContents(InputStream input, String encoding)
212: throws IOException {
213: ByteArrayOutputStream out = new ByteArrayOutputStream();
214: FileUtil.copyContents(input, out);
215:
216: ByteBuffer bbuf = ByteBuffer.wrap(out.toByteArray());
217:
218: Charset cset = Charset.forName(encoding);
219: CharsetDecoder csetdecoder = cset.newDecoder();
220:
221: csetdecoder.onMalformedInput(CodingErrorAction.REPORT);
222: csetdecoder.onUnmappableCharacter(CodingErrorAction.REPORT);
223:
224: try {
225: CharBuffer cbuf = csetdecoder.decode(bbuf);
226:
227: return cbuf.toString();
228: } catch (CharacterCodingException e) {
229: Charset latin1 = Charset.forName("ISO-8859-1");
230: CharsetDecoder l1decoder = latin1.newDecoder();
231:
232: l1decoder.onMalformedInput(CodingErrorAction.REPORT);
233: l1decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
234:
235: try {
236: bbuf = ByteBuffer.wrap(out.toByteArray());
237:
238: CharBuffer cbuf = l1decoder.decode(bbuf);
239:
240: return cbuf.toString();
241: } catch (CharacterCodingException ex) {
242: throw (CharacterCodingException) ex.fillInStackTrace();
243: }
244: }
245: }
246:
247: /**
248: * Returns the full contents of the Reader as a String.
249: *
250: * @since 1.5.8
251: * @param in The reader from which the contents shall be read.
252: * @return String read from the Reader
253: * @throws IOException If reading fails.
254: */
255: public static String readContents(Reader in) throws IOException {
256: Writer out = null;
257:
258: try {
259: out = new StringWriter();
260:
261: copyContents(in, out);
262:
263: return out.toString();
264: } finally {
265: try {
266: out.close();
267: } catch (Exception e) {
268: log
269: .error("Not able to close the stream while reading contents.");
270: }
271: }
272: }
273:
274: /**
275: * Returns the class and method name (+a line number) in which the
276: * Throwable was thrown.
277: *
278: * @param t A Throwable to analyze.
279: * @return A human-readable string stating the class and method. Do not rely
280: * the format to be anything fixed.
281: */
282: public static String getThrowingMethod(Throwable t) {
283: StackTraceElement[] trace = t.getStackTrace();
284: StringBuffer sb = new StringBuffer();
285:
286: if (trace == null || trace.length == 0) {
287: sb.append("[Stack trace not available]");
288: } else {
289: sb.append(trace[0].isNativeMethod() ? "native method" : "");
290: sb.append(trace[0].getClassName());
291: sb.append(".");
292: sb.append(trace[0].getMethodName() + "(), line "
293: + trace[0].getLineNumber());
294: }
295: return sb.toString();
296: }
297: }
|