/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.build.packager;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.CodeVisitor;
/**
* Class for searching main methods in a jar.
*
* @author fabien
*
*/
public class MainFinder {
/**
* Search for the main classes in the jars/resources.
* Starts by looking in the jars manifests and, if nothing is found,
* then scans the jars/resources for main classes.
*
* @param userJar
* @return the names of the main classes.
* @throws FileNotFoundException
* @throws IOException
*/
public static List<String> searchMain(File userJar) throws FileNotFoundException, IOException {
List<String> mainList = new ArrayList<String>();
JarFile jarFile = null;
try {
jarFile = new JarFile(userJar);
// try to find the main class from the manifest
Object value = null;
// do we have a manifest ?
if (jarFile.getManifest() != null) {
value = jarFile.getManifest().getMainAttributes().get(Attributes.Name.MAIN_CLASS);
if (value == null) {
String name = Attributes.Name.MAIN_CLASS.toString();
final Attributes attr = jarFile.getManifest().getAttributes(name);
// we have a manifest but do we have a main class defined inside ?
if (attr != null) {
value = attr.get(Attributes.Name.MAIN_CLASS);
}
}
}
if (value != null) {
mainList.add(String.valueOf(value));
} else {
// scan the jar to find the main classes
for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
final JarEntry entry = e.nextElement();
final String name = entry.getName();
InputStream is = null;
try {
if (name.endsWith(".class")) {
String className = name.substring(0, name.length() - ".class".length());
className = className.replace('/', '.');
is = jarFile.getInputStream(entry);
if (isMainClass(is, entry, className)) {
mainList.add(className);
}
}
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
// ignore
} catch (SecurityException se) {
se.printStackTrace();
// ignore
} catch (NoSuchMethodException nsme) {
// such error is expected for non-main classes => ignore
} catch (Throwable t) {
t.printStackTrace();
// ignore
} finally {
if (is != null) {
is.close();
}
}
}
}
} finally {
if (jarFile != null) {
jarFile.close();
}
}
return mainList;
}
private static boolean isMainClass(InputStream classStream, JarEntry entry, String className)
throws ClassNotFoundException, SecurityException, NoSuchMethodException, IOException {
ClassReader cr = new ClassReader(classStream);
MainClassVisitor mcv = new MainClassVisitor(NullClassVisitor.INSTANCE);
cr.accept(mcv, true);
return mcv.hasMainMethod();
}
/**
* Custom {@link ClassVisitor} used to parse a class from an InputStream.
* It helps finding a main class in a jar file.
* @author fabien
*
*/
static class MainClassVisitor extends ClassAdapter {
private boolean mainMethod = false;
public MainClassVisitor(ClassVisitor visitor) {
super(visitor);
}
@Override
public CodeVisitor visitMethod(int access, String name, String signature, String[] exceptions,
Attribute arg4) {
if ("main".equals(name) && "([Ljava/lang/String;)V".equals(signature)) {
mainMethod = true;
}
return null;
}
public boolean hasMainMethod() {
return mainMethod;
}
}
/**
* ClassVisitor doing nothing but that's needed by MainClassVisitor constructor.
* @author fabien
*
*/
static class NullClassVisitor implements ClassVisitor {
private static final NullClassVisitor INSTANCE = new NullClassVisitor();
@Override
public void visit(int arg0, int arg1, String arg2, String arg3, String[] arg4, String arg5) {
}
@Override
public void visitAttribute(Attribute arg0) {
}
@Override
public void visitEnd() {
}
@Override
public void visitField(int arg0, String arg1, String arg2, Object arg3, Attribute arg4) {
}
@Override
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
@Override
public CodeVisitor visitMethod(int arg0, String arg1, String arg2, String[] arg3,
Attribute arg4) {
return null;
}
}
}