package net.gnehzr.tnoodle.utils;
import static net.gnehzr.tnoodle.utils.GwtSafeUtils.azzert;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.LinkedHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Random;
public final class Utils {
private static final Logger l = Logger.getLogger(Utils.class.getName());
private static final String RESOURCE_FOLDER = "tnoodle_resources";
private static final String DEVEL_VERSION = "devel";
public static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy/MM/dd");
private Utils() {}
public static String throwableToString(Throwable e) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(bytes));
return bytes.toString();
}
public static void copyFile(File sourceFile, File destFile) throws IOException {
if(!destFile.exists()) {
destFile.createNewFile();
}
FileChannel source = null;
FileChannel destination = null;
try {
source = new FileInputStream(sourceFile).getChannel();
destination = new FileOutputStream(destFile).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {
if(source != null) {
source.close();
}
if(destination != null) {
destination.close();
}
}
}
public static File getResourceDirectory() {
return getResourceDirectory(true);
}
private static File getResourceDirectory(boolean assertExists) {
File f = new File(getProgramDirectory(), RESOURCE_FOLDER);
String version = getVersion();
if(!version.equals(DEVEL_VERSION)) {
// Each version of tnoodle extracts its resources
// to its own subdirectory of RESOURCE_FOLDER
f = new File(f, version);
}
if(assertExists) {
if(!f.isDirectory()) {
l.log(Level.SEVERE, f.getAbsolutePath() + " does not exist, or is not a directory!");
azzert(f.isDirectory());
}
}
return f;
}
/**
* @return A File representing the directory in which this program resides.
* If this is a jar file, this should be obvious, otherwise it's the directory in which
* our calling class resides.
*/
public static File getProgramDirectory() {
File programDirectory = getJarFileOrDirectory();
if(programDirectory.isFile()) { //this should indicate a jar file
programDirectory = programDirectory.getParentFile();
}
return programDirectory;
}
private static Class<?> getCallerClass() {
Class<?> callerClass = null;
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
for (int i = 2; i < stElements.length; i++) {
StackTraceElement ste = stElements[i];
try {
callerClass = Class.forName(ste.getClassName());
} catch(ClassNotFoundException e) {
// Classes that are part of a web app were loaded with the
// servlet container's classloader, so we can't necessarily
// find them.
return null;
}
if(!Utils.class.getPackage().equals(callerClass.getPackage())) {
return callerClass;
}
}
return null;
}
private static File getJarFileOrDirectory() {
Class<?> callerClass = getCallerClass();
if(callerClass == null) {
// We don't know where the class is (it was probably loaded
// by the servlet container), so just use Util.
callerClass = Utils.class;
}
File programDirectory;
try {
programDirectory = new File(callerClass.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
} catch (URISyntaxException e) {
return new File(".");
}
return programDirectory;
}
public static File getJarFile() {
File potentialJarFile = getJarFileOrDirectory();
if(potentialJarFile.isFile()) {
return potentialJarFile;
}
return null;
}
/**
* Copied from http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/io/Files.java from http://stackoverflow.com/questions/617414/create-a-temporary-directory-in-java
* Atomically creates a new directory somewhere beneath the system's
* temporary directory (as defined by the {@code java.io.tmpdir} system
* property), and returns its name.
*
* <p>Use this method instead of {@link File#createTempFile(String, String)}
* when you wish to create a directory, not a regular file. A common pitfall
* is to call {@code createTempFile}, delete the file and create a
* directory in its place, but this leads a race condition which can be
* exploited to create security vulnerabilities, especially when executable
* files are to be written into the directory.
*
* <p>This method assumes that the temporary volume is writable, has free
* inodes and free blocks, and that it will not be called thousands of times
* per second.
*
* @return the newly-created directory
* @throws IllegalStateException if the directory could not be created
*/
/** Maximum loop count when creating temp directories. */
private static final int TEMP_DIR_ATTEMPTS = 10000;
public static File createTempDir() {
// Calling renameTo() on something in java.io.tmpdir to something in
// tnoodle_resources doesn't seem to work. We opt to just put temp folders
// in the same directory as the jar file.
//File baseDir = new File(System.getProperty("java.io.tmpdir"));
File baseDir = getProgramDirectory();
String baseName = System.currentTimeMillis() + "-";
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
File tempDir = new File(baseDir, baseName + counter);
if (tempDir.mkdir()) {
return tempDir;
}
}
throw new IllegalStateException("Failed to create directory within "
+ TEMP_DIR_ATTEMPTS + " attempts (tried "
+ baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
}
public static void doFirstRunStuff() throws FileNotFoundException, IOException {
File jarFile = getJarFile();
if(jarFile != null) {
File resourceDirectory = getResourceDirectory(false);
if(resourceDirectory.isDirectory()) {
// If the resource folder already exists, we don't bother re-extracting the
// files.
return;
}
File tempResourceDirectory = createTempDir();
JarInputStream jarIs = new JarInputStream(new FileInputStream(jarFile));
JarEntry entry;
byte[] buf = new byte[1024];
while((entry = jarIs.getNextJarEntry()) != null) {
if(entry.isDirectory()) {
continue;
}
if(entry.getName().startsWith(RESOURCE_FOLDER)) {
// Remove the leading RESOURCE_FOLDER from the filename,
// so we can put it in resourceDirectory directly.
String destName = entry.getName().substring(RESOURCE_FOLDER.length());
File destFile = new File(tempResourceDirectory, destName);
destFile.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(destFile);
int n;
while ((n = jarIs.read(buf, 0, buf.length)) > -1) {
out.write(buf, 0, n);
}
out.close();
}
jarIs.closeEntry();
}
jarIs.close();
resourceDirectory.getParentFile().mkdirs();
azzert(tempResourceDirectory.renameTo(resourceDirectory));
}
}
public static String getProjectName() {
Package p = Utils.class.getPackage();
String name = p.getImplementationTitle();
if(name == null) {
name = getCallerClass().getName();
}
return name;
}
public static String getVersion() {
Package p = Utils.class.getPackage();
String version = p.getImplementationVersion();
if(version == null) {
version = DEVEL_VERSION;
}
return version;
}
public static LinkedHashMap<String, String> parseQuery(String query) {
LinkedHashMap<String, String> queryMap = new LinkedHashMap<String, String>();
if(query == null) {
return queryMap;
}
String[] pairs = query.split("&");
for(String pair : pairs) {
String[] key_value = pair.split("=");
if(key_value.length == 1) {
queryMap.put(key_value[0], ""); // this allows for flags such as http://foo/blah?kill&burn
} else {
try {
queryMap.put(key_value[0], URLDecoder.decode(key_value[1], "utf-8"));
} catch (UnsupportedEncodingException e) {
queryMap.put(key_value[0], key_value[1]); //worst case, just put the undecoded string
}
}
}
return queryMap;
}
public static File getWebappsDir() {
return new File(getResourceDirectory(), "/webapps/");
}
public static File getWebappDir(String webappName) {
if(webappName == null) {
webappName = "ROOT";
}
return new File(getWebappsDir(), webappName);
}
private static Random r;
public static Random getSeededRandom() {
if(r != null) {
return r;
}
String randSeedEnvVar = "TNOODLE_RANDSEED";
String seed = System.getenv(randSeedEnvVar);
if(seed == null) {
seed = "" + System.currentTimeMillis();
}
System.out.println("Using TNOODLE_RANDSEED=" + seed);
r = new Random(seed.hashCode());
return r;
}
}