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