/*
* Copyright 2014-2016 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.projectodd.wunderboss;
import org.slf4j.Logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ApplicationRunner {
public ApplicationRunner(String name) {
this.name = name;
}
public void start(String[] args) throws Exception {
loadProperties();
extractJar();
updateClassPath();
copyConfigProperties();
WunderBoss.putOption("argv", args);
WunderBoss.putOption("root", properties.getProperty("root"));
String language = requiredProperty(properties, "language");
log.info("Initializing " + name + " as " + language);
WunderBoss.findLanguage(language)
.eval(requiredProperty(properties, "init"));
}
protected void copyConfigProperties() {
for (String key : this.properties.stringPropertyNames()) {
if (key.startsWith("config.")) {
WunderBoss.putOption(key.substring(7), this.properties.getProperty(key));
}
}
}
protected void loadProperties() throws Exception {
String internalPath = "META-INF/app.properties";
log.debug("Looking for properties file at {}", internalPath);
InputStream configStream = WunderBoss.classLoader().getResourceAsStream(internalPath);
if (configStream != null) {
log.debug("Found properties file {}", internalPath);
properties.load(configStream);
}
Properties externalProperties = new Properties();
String externalPath = jarURL().getPath();
if (externalPath.endsWith(".jar")) {
externalPath = externalPath.replace(".jar", ".properties");
log.debug("Looking for properties file at {}", externalPath);
File externalFile = new File(externalPath);
if (externalFile.exists()) {
log.debug("Found properties file {}", externalPath);
externalProperties.load(new FileInputStream(externalFile));
properties.putAll(externalProperties);
}
}
}
public void stop() {
if (extractRoot != null) {
Utils.deleteRecursively(new File(extractRoot));
}
}
protected void extractJar() throws Exception {
if (!properties.containsKey("extract_paths")) {
return;
}
String[] extractPaths = Utils.classpathStringToArray(properties.getProperty("extract_paths"));
extractRoot = Files.createTempDirectory("wunderboss").toFile().getAbsolutePath();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
Utils.deleteRecursively(new File(extractRoot));
}
});
URLConnection jarConnection = jarURL().openConnection();
InputStream jarInputStream = jarConnection.getInputStream();
ZipInputStream zipStream;
if (jarInputStream instanceof ZipInputStream) {
zipStream = (ZipInputStream) jarInputStream;
} else {
zipStream = new ZipInputStream(jarInputStream);
}
ZipEntry zipEntry = null;
byte[] buffer = new byte[4096];
while ((zipEntry = zipStream.getNextEntry()) != null) {
String name = zipEntry.getName();
// Only extract entries in the specified directories
boolean match = false;
for (String extractPath : extractPaths) {
if (name.startsWith(extractPath)) {
match = true;
break;
}
}
if (!match) {
continue;
}
File file = new File(extractRoot + "/" + name);
if (zipEntry.isDirectory()) {
file.mkdirs();
} else {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
FileOutputStream extractStream = new FileOutputStream(file);
try {
int bytesRead = -1;
while ((bytesRead = zipStream.read(buffer)) != -1) {
extractStream.write(buffer, 0, bytesRead);
}
} finally {
extractStream.close();
}
}
zipStream.closeEntry();
}
for (String key : properties.stringPropertyNames()) {
String value = properties.getProperty(key);
if (value.contains("${extract_root}")) {
properties.setProperty(key, value.replace("${extract_root}", extractRoot));
}
}
WunderBoss.putOption("extract-root", extractRoot);
}
protected void updateClassPath() throws Exception {
if (properties.containsKey("classpath")) {
classPathAdditions.addAll(Utils.classpathStringToFiles(properties.getProperty("classpath")));
}
for (File file : classPathAdditions) {
try {
WunderBoss.updateClassPath(file.toURI().toURL());
} catch (MalformedURLException ignored) {}
}
}
protected String requiredProperty(Properties properties, String key) {
if (properties.containsKey(key)) {
return properties.getProperty(key);
} else {
throw new IllegalArgumentException("Required option " + key + " not provided.");
}
}
protected URL jarURL() {
String mainPath = ApplicationRunner.class.getName().replace(".", "/") + ".class";
String mainUrl = ApplicationRunner.class.getClassLoader().getResource(mainPath).toString();
int from = "jar:file:".length();
int to = mainUrl.indexOf("!/");
try {
return new URL("file:///" + mainUrl.substring(from, to));
} catch (MalformedURLException e) {
throw new RuntimeException("Error determining jar path", e);
}
}
protected String name;
protected Properties properties = new Properties();
protected String extractRoot;
protected List<File> classPathAdditions = new ArrayList<>();
private static final Logger log = WunderBoss.logger("org.projectodd.wunderboss");
}