package de.flower.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.UrlResource;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
/**
* Class with some usefull helper methods related to I/O operations.
*
* @author flowerrrr
*/
public class IO {
private final static Logger log = LoggerFactory.getLogger(IO.class);
// prefix for all property keys
private static final String KEYPREFIX = IO.class.getName() + ".";
/**
* By setting this sytem property you can control the connection timeout that is used in the method {@link #loadResourceAsStream(String)}. If not set, the system default is used. Usefull for testing, if you don't want to wait for ages for a timeout to occur.
*/
public static final String URLCONNECTION_TIMEOUT = KEYPREFIX + "urlconnection.timeout";
/**
* The Enum CharacterEncoding.
*/
public enum CharacterEncoding {
/**
* The ASCII.
*/
ASCII("US-ASCII"),
/**
* The IS o8859.
*/
ISO8859("ISO-8859-1"),
/**
* The UT f8.
*/
UTF8("UTF-8");
/**
* The encoding.
*/
private String encoding;
/**
* Instantiates a new character encoding.
*
* @param encoding the encoding
*/
CharacterEncoding(String encoding) {
this.encoding = encoding;
}
/*
* (non-Javadoc)
*
* @see java.lang.Enum#toString()
*/
@Override
public String toString() {
return encoding;
}
/**
* From string.
*
* @param encoding the encoding
* @return the character encoding
*/
public static CharacterEncoding fromString(String encoding) {
for (CharacterEncoding ce : CharacterEncoding.values()) {
if (ce.toString().equals(encoding)) {
return ce;
}
}
throw new IllegalArgumentException("invalid encoding [" + encoding
+ "]");
}
}
;
private IO() {
}
/**
* Prints formatted representations of objects to an PrintStream. Can be
* used to dump objects recursively to log target.
*
* @param o -
* Object to print
* @param ps -
* printstream used to print the object (System.out for example).
*/
public static void print(Object o, PrintStream ps) {
ps.println(toString(o).replaceAll(", ", ",\n "));
}
/**
* Returns formatted representations of objects.
*
* @param o -
* Object to print
* @return - String holding formatted representation of the object.
*/
public static String toString(Object o) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.print(o);
pw.flush();
return sw.toString();
}
/**
* Saves the content to a text file. This style of implementation throws all
* exceptions to the caller.
*
* @param fileName the file name
* @param content string to save
* @param charsetName the charset name
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void saveToFile(String fileName, String content,
CharacterEncoding charsetName) throws IOException {
// declared here only to make visible to finally clause; generic
// reference
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(fileName), charsetName.toString()));
writer.write(content);
writer.close();
} finally {
// always close the streams otherwise we would leak memory.
if (writer != null) {
writer.close();
}
}
}
/**
* Read content of a text file. This style of implementation throws all
* exceptions to the caller.
*
* @param fileName the file name
* @param charsetName the charset name
* @return the string
* @throws IOException Signals that an I/O exception has occurred.
*/
public static String readFromFile(String fileName, String charsetName) {
log.debug("Trying to load file [" + System.getProperty("user.dir")
+ "/" + fileName);
try {
InputStream in = new FileInputStream(fileName);
return readFromInputStream(in, charsetName);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Here is a useful method to convert from InputStream to String.
*
* @param in InputStream
* @param charsetName the charset name
* @return string
* @throws IOException Signals that an I/O exception has occurred.
* @see <a
* href="http://www.bubble-media.com/cgi-bin/articles/archives/000038.html">here</a>
*/
public static String readFromInputStream(java.io.InputStream in,
String charsetName) {
StringBuilder contents = new StringBuilder();
if (in == null) {
throw new IllegalArgumentException("inputstream cannot be null.");
}
try (BufferedReader input = new BufferedReader(new InputStreamReader(in, charsetName)))
{
String line; // not declared within while loop
/*
* readLine is a bit quirky : it returns the content of a line MINUS the
* newline. it returns null only for the END of the stream. it returns
* an empty String if two newlines appear in a row.
*/
while ((line = input.readLine()) != null) {
contents.append(line);
contents.append(System.getProperty("line.separator"));
}
return contents.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Reads a stream into a byte arry. Does not work for reading inputstreams
* based on external links.
*
* @param in the in
* @return the byte[]
* @throws IOException Signals that an I/O exception has occurred.
* @see <a
* href="http://javaalmanac.com/egs/java.io/File2ByteArray.html">http://javaalmanac.com/egs/java.io/File2ByteArray.html</a>
*/
public static byte[] readFromInputStream(InputStream in) throws IOException {
if (in == null) {
throw new IllegalArgumentException("inputstream cannot be null.");
}
// Get the size of the file
long length = in.available();
if (log.isDebugEnabled()) {
log.debug("readFromInputStream(): Inputstream.available() = "
+ length);
}
if (length > Integer.MAX_VALUE) {
// Stream is too large
throw new IOException("stream is too large to read.");
}
// Create the byte array to hold the data
byte[] bytes = new byte[(int) length];
// Read in the bytes
int offset = 0;
int numRead = 0;
while (offset < bytes.length
&& (numRead = in.read(bytes, offset, bytes.length - offset)) >= 0) {
offset += numRead;
}
// Ensure all the bytes have been read in
if (offset < bytes.length) {
throw new IOException("Could not completely read stream.");
}
if (log.isDebugEnabled()) {
log.debug("readFromInputStream(): Read [" + bytes.length
+ "] bytes from input stream.");
}
return bytes;
}
/**
* Try to locate the resource within the classpath by name as is. Does not
* require leading slash (fully qualified path). Method will use the
* classloader of the method-invoker or if <code>frame</code> is greater
* than zero it walks up the call-stack to find the appropiate object.
*
* @param resource classpath resource name
* @param frame the frame
* @return the InputStream of the requested resource file or null if it does
* not exist
*/
public static InputStream loadClasspathResourceAsStream(String resource,
int frame) {
/**
* The Class CurrentClassGetter.
*
* use this trick to get the class loader of the calling method.
* this allows to load resources with relative paths to the loading
* class file.
*/
class CurrentClassGetter extends SecurityManager {
public Class<?> getClass(int frame) {
return getClassContext()[frame];
}
}
CurrentClassGetter ccg = new CurrentClassGetter();
Class<?> claz = ccg.getClass(2 + frame); // get class of method
// invoker.
return loadClasspathResourceAsStream(resource, claz);
}
/**
* Load classpath resource as stream using class loader <code>claz</code>.
* Method was introduced as workaround for javarebel error.
*
* @param resource the resource
* @param claz the claz used as loader.
* @return the input stream
*/
public static InputStream loadClasspathResourceAsStream(String resource, Class<?> claz) {
if (resource == null) {
throw new IllegalArgumentException("Resource id must not be <null>");
}
InputStream is = claz.getResourceAsStream(resource);
if (is == null) {
log.error("could not load resource [" + resource
+ "] using classpath loader of class [" + claz.getName()
+ "]");
}
return is;
}
/**
* Convienience method for calling loadResourceAsStream without specifying
* the frame of the call stack. Uses the classloader of the method-invoker.
* Does not work when javarebel is used together with relative resource paths.
*
* @param resource the resource
* @return the input stream
*/
public static InputStream loadClasspathResourceAsStream(String resource) {
return loadClasspathResourceAsStream(resource, 1);
}
/**
* Wrapper for {@link #loadResourceAsStream(String, int)}. Uses the system
* default timeout when using an URLConnection.
*
* @param resourcePath the resource path
* @return the input stream
* @throws IOException Signals that an I/O exception has occurred.
*/
public static InputStream loadResourceAsStream(String resourcePath)
throws IOException {
int timeoutMs = -1; // if -1 the system default is used.
String tmp = System.getProperty(URLCONNECTION_TIMEOUT, "" + timeoutMs);
timeoutMs = Integer.parseInt(tmp);
return loadResourceAsStream(resourcePath, timeoutMs, null);
}
/**
* Load resource as stream.
*
* @param resourcePath the resource path
* @param timeoutMs connection timeout in milliseconds if < 0 the java default
* timeout is used.
* @param postBody the post body
* @return the input stream
* @throws IOException Signals that an I/O exception has occurred.
*/
public static InputStream loadResourceAsStream(String resourcePath,
int timeoutMs, String postBody) throws IOException {
InputStream is;
log.debug("Fetching resource from " + resourcePath);
switch (getResourceType(resourcePath)) {
case URL: {
URL u = (new UrlResource(resourcePath)).getURL();
URLConnection con = u.openConnection();
con.setUseCaches(false);
if (timeoutMs >= 0) {
con.setConnectTimeout(timeoutMs);
}
if (postBody != null) {
// if requestProperties is set then post request to
// webserver
con.setDoOutput(true);
// OutputStreamWriter out = new
// OutputStreamWriter(con.getOutputStream());
PrintWriter out = new PrintWriter(con.getOutputStream());
out.println(postBody);
out.close();
}
is = con.getInputStream();
break;
}
case URLFILE: {
UrlResource urlResource = new UrlResource(resourcePath);
is = new BufferedInputStream(new FileInputStream(urlResource
.getFile()));
break;
}
case CLASSPATH: {
// the url is a classpath resource
is = loadClasspathResourceAsStream(resourcePath.replace(
"classpath:", ""));
break;
}
case FILE: {
// url is a file name
is = new BufferedInputStream(new FileInputStream(resourcePath));
break;
}
default:
throw new IllegalArgumentException("unknown resourceType for url "
+ resourcePath);
}
return is;
}
/**
* The Enum ResourceType.
*/
public enum ResourceType {
/**
* The URL.
*/
URL,
/**
* The URLFILE.
*/
URLFILE,
/**
* The FILE.
*/
FILE,
/**
* The CLASSPATH.
*/
CLASSPATH
}
;
/**
* Gets the resource type.
*
* @param url the url
* @return the resource type
*/
public static ResourceType getResourceType(String url) {
if (url == null || url.equals("")) {
throw new IllegalArgumentException("invalid argument: url");
}
if (url.startsWith("http:") || url.startsWith("https:")) {
return ResourceType.URL;
} else if (url.startsWith("file:")) {
return ResourceType.URLFILE;
} else if (url.startsWith("classpath:")) {
// the url is a classpath resource
return ResourceType.CLASSPATH;
} else {
// url is a file name
return ResourceType.FILE;
}
}
/**
* Try to locate the resource within the classpath by name as is. Does not
* require leading slash (fully qualified path).
*
* @param resource resource name
* @param frame frame of callstack. used to retrieve the classloader of the
* calling method.
* @return the InputStream of the requested resource file or null if it does
* not exist
*/
public static URL loadResource(String resource, int frame) {
/**
* The Class CurrentClassGetter.
*
* use this trick to get the class loader of the calling method.
* this allows to load resources with relative paths to the loading
* class file.
*/
class CurrentClassGetter extends SecurityManager {
public Class<?> getClass(int frame) {
return getClassContext()[frame];
}
}
CurrentClassGetter ccg = new CurrentClassGetter();
Class<?> claz = ccg.getClass(2 + frame); // get class of method
// invoker.
if (resource == null) {
throw new IllegalArgumentException("Resource id must not be <null>");
}
URL url = claz.getResource(resource);
if (url == null) {
log.error("could not load resource [" + resource
+ "] using classpath loader of class [" + claz.getName()
+ "]");
}
return url;
}
/**
* Convienience method for calling loadResource without specifying the frame
* of the call stack. Uses the classloader of the method-invoker.
*
* @param resource the resource
* @return the URL
*/
public static URL loadResource(String resource) {
return loadResource(resource, 1);
}
/**
* Encodes an input stream as base64 string.
*
* @param path the path
* @param force the force
*
* @return the base64 encoded input stream as string
*/
/*
public static String classPathResourceToBase64(
ClassPathResource classPathResource) {
try {
final int chunkSize = 2048;
byte[] buf = new byte[chunkSize];
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(
chunkSize);
int count;
InputStream inputStream = classPathResource.getInputStream();
while ((count = inputStream.read(buf)) != -1) {
byteStream.write(buf, 0, count);
}
return new String(Base64.encodeBase64(byteStream.toByteArray()));
} catch (Exception e) {
log.error(e);
}
return null;
}
*/
}