blob: 781787e7d46d7a6d1f984bc51943f913cdc702e2 [file] [log] [blame]
// Copyright 2009 Google Inc.
//
// 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.
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/** Main class for a JAR file to run code from "WEB-INF/lib". */
public class ExecutableWarMain {
private static final String EXECUTABLE_WAR_PACKAGE = "Executable-War-Package";
private static final String myURL = myURL();
public static void main(final String argv[]) throws Exception {
if (argv.length > 0) {
final String action = argv[0];
if ("-p".equals(action) || "--cat".equals(action)) {
// Copy the contents of a file to System.out
//
if (argv.length == 2) {
cat(argv[1]);
} else {
System.err.println("usage: [-p|--cat] <filename>");
System.exit(1);
}
return;
} else if ("-l".equals(action) || "--ls".equals(action)) {
// List the available files under WEB-INF/
//
if (argv.length == 1) {
ls();
} else {
System.err.println("usage: [-l|--ls]");
System.exit(1);
}
return;
} else if (!action.startsWith("-")) {
// Run an arbitrary application class
//
final ClassLoader cl = fullClassLoader();
Thread.currentThread().setContextClassLoader(cl);
runMain(cl, argv);
return;
}
}
System.err.println("usage: CommandName [args...]");
System.err.println("usage: [-p|--cat] <filename>");
System.err.println("usage: [-l|--ls]");
System.exit(1);
}
private static void cat(String fileName) throws IOException {
while (fileName.startsWith("/")) {
fileName = fileName.substring(1);
}
final InputStream in =
ExecutableWarMain.class.getResourceAsStream("WEB-INF/" + fileName);
if (in == null) {
System.err.println("error: no such file " + fileName);
System.exit(1);
}
try {
try {
final byte[] buf = new byte[4096];
int n;
while ((n = in.read(buf)) >= 0) {
System.out.write(buf, 0, n);
}
} finally {
System.out.flush();
}
} finally {
in.close();
}
}
private static void ls() throws IOException {
final ZipInputStream zf = openMyZip();
try {
ZipEntry ze;
while ((ze = zf.getNextEntry()) != null) {
boolean show = true;
show &= ze.getName().startsWith("WEB-INF/");
show &= !ze.isDirectory();
show &= !ze.getName().startsWith("WEB-INF/lib/");
show &= !ze.getName().equals("WEB-INF/web.xml");
zf.closeEntry();
if (show) {
System.out.println(ze.getName().substring("WEB-INF/".length()));
}
}
} finally {
zf.close();
}
}
private static void runMain(final ClassLoader loader, final String[] origArgv)
throws Exception {
String name = origArgv[0];
final String[] argv = new String[origArgv.length - 1];
System.arraycopy(origArgv, 1, argv, 0, argv.length);
final Attributes att = myManifest();
String pkg = att.getValue(EXECUTABLE_WAR_PACKAGE);
if (pkg == null) {
pkg = "";
} else {
pkg = pkg + ".";
}
Class<?> clazz;
try {
try {
clazz = Class.forName(pkg + name, true, loader);
} catch (ClassNotFoundException cnfe) {
if (name.equals(name.toLowerCase())) {
final String nUC = name.substring(0, 1).toUpperCase();
clazz = Class.forName(pkg + nUC + name.substring(1), true, loader);
} else {
throw cnfe;
}
}
} catch (ClassNotFoundException cnfe) {
System.err.println("fatal: unknown command " + name);
System.err.println(" (Class " + pkg + name + " not found)");
System.exit(1);
return;
}
final Method main;
try {
main = clazz.getMethod("main", argv.getClass());
} catch (SecurityException e) {
System.err.println("fatal: unknown command " + name);
System.exit(1);
return;
} catch (NoSuchMethodException e) {
System.err.println("fatal: unknown command " + name);
System.exit(1);
return;
}
main.invoke(null, new Object[] {argv});
}
private static Attributes myManifest() throws IOException,
MalformedURLException {
final InputStream mfin =
new URL("jar:" + myURL + "!/META-INF/MANIFEST.MF").openStream();
try {
return new Manifest(mfin).getMainAttributes();
} finally {
mfin.close();
}
}
private static ClassLoader fullClassLoader() {
final ArrayList<URL> paths = new ArrayList<URL>();
try {
final ZipInputStream zf = openMyZip();
try {
ZipEntry ze;
while ((ze = zf.getNextEntry()) != null) {
if (ze.getName().startsWith("WEB-INF/lib/")) {
// Try to derive the name of the temporary file so it
// doesn't completely suck. Best if we can make it
// match the name it was in the WAR.
//
String name = ze.getName().substring("WEB-INF/lib/".length());
if (name.lastIndexOf('/') >= 0) {
name = name.substring(name.lastIndexOf('/') + 1);
}
if (name.lastIndexOf('.') >= 0) {
name = name.substring(0, name.lastIndexOf('.'));
}
if (name.length() == 0) {
name = "ewar";
}
final File tmp = File.createTempFile(name, "jar");
tmp.deleteOnExit();
final FileOutputStream out = new FileOutputStream(tmp);
try {
final byte[] buf = new byte[4096];
int n;
while ((n = zf.read(buf, 0, buf.length)) > 0) {
out.write(buf, 0, n);
}
} finally {
out.close();
}
paths.add(tmp.toURL());
}
zf.closeEntry();
}
} finally {
zf.close();
}
} catch (IOException e) {
throw new LinkageError("Cannot unpack libs from " + myURL);
}
if (paths.isEmpty()) {
throw new LinkageError("No files under WEB-INF/lib/");
}
return new URLClassLoader(paths.toArray(new URL[paths.size()]));
}
private static ZipInputStream openMyZip() throws IOException,
MalformedURLException {
final URL u = new URL(myURL);
return new ZipInputStream(new BufferedInputStream(u.openStream()));
}
private static String myURL() {
final String myName =
ExecutableWarMain.class.getName().replace('.', '/') + ".class";
final URL u = ExecutableWarMain.class.getClassLoader().getResource(myName);
if (u == null) {
throw new LinkageError("Cannot locate " + myName);
}
if (!"jar".equals(u.getProtocol())) {
throw new LinkageError("Expected jar: URL: " + u);
}
String path = u.toExternalForm();
if (path == null) {
throw new LinkageError("Expected jar: URL: " + u);
}
final int bang = path.indexOf('!');
if (bang < 0) {
throw new LinkageError("Expected jar: URL: " + u);
}
return path.substring(4, bang);
}
}