1

I have a URL list of jar files, also I have whole path classname like com.mycompany.myproject.Test, how can I search in these jar files and get the .class file in it? I use it for decompiling.

String classname = "com.mycompany.myproject.Test";
URI uri = Tool.searchClass(jarList, classname);
// decompile .class
...

Any sample codes like this?

Add: Shell script is good, but is there a way in java code can do this job?

Add: I just wrote a static method by using java.util.jar.JarFile to handle this, hope this helps other people

The following code is tested and works properly:

/**
 * Search the class by classname specified in jarFile, if found and destFolder specified
 * Copy the .class file into destFolder with whole path.
 * @param classname
 * @param jarFile
 * @param destFolder
 * @return
 */
public static File searchCompressedFile(String classname, File jarFile, String destFolder) {
    try {
        // change classname "." to "/"
        if(classname.contains(".")){
            classname = classname.replace(".", "/");
        }

        JarFile jarF = new JarFile(jarFile);
        Enumeration<JarEntry> jarEntries = jarF.entries();

        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            if (jarEntry.getName().indexOf(classname) >= 0) {
                String filePath = jarFile.getAbsolutePath();
                System.out.println(classname + " is in " + filePath + "--" + jarEntry.getName());

                if (destFolder != null && !"".equals(destFolder)) {
                    // Make folder if dest folder not existed.
                    File destF = new File(destFolder);
                    if (!destF.exists()) {
                        destF.mkdirs();
                    }

                    File f = new File(destFolder + File.separator + jarEntry.getName());
                    if(!f.getParentFile().exists()){
                        f.getParentFile().mkdirs();
                    }
                    InputStream is = jarF.getInputStream(jarEntry);
                    FileOutputStream fos = new java.io.FileOutputStream(f);
                    while (is.available() > 0) {
                        fos.write(is.read());
                    }
                    fos.close();
                    is.close();

                    return f;
                }
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    System.out.println("Class not found in jar");
    return null;
}
JerryCai
  • 1,663
  • 4
  • 21
  • 36

5 Answers5

1

jar file is actually a .zip. It can be searched using java.util.zip.ZipInputStream as follows:

import java.util.zip.ZipInputStream;
import java.util.zip.ZipEntry;

public class Test {

    public static void main(String args[]){ 
        try{
                CodeSource src = Main.class.getProtectionDomain().getCodeSource();
                if( src != null ) {
                  URL jar = src.getLocation();  
                  ZipInputStream zip = new ZipInputStream(jar.openStream());                      
                  ZipEntry zipEntry = null;
                  while((zipEntry = zip.getNextEntry()) != null){
                    String entryName = zipEntry.getName();
                    // do what you want
                  }
                }
        }
        catch (Exception e){            
        }       
    }   
}

I also found another answer that uses the class JarFile

Community
  • 1
  • 1
Nir Alfasi
  • 53,191
  • 11
  • 86
  • 129
  • oh, that's terrible way because I have thousands of jar files. – JerryCai Jul 02 '12 at 05:16
  • @JerryCai I modified my answer, but I'm still using the same idea of treating it as a zip-file. – Nir Alfasi Jul 02 '12 at 05:24
  • yes, that answer is great, it doesn't need unzip all the jar files. During the time you answered me I also found a solution of `JarFile` you just mentioned, maybe that's more common in handling Jar file, please check my answer. – JerryCai Jul 02 '12 at 06:57
1

I have this shell script called findClass.sh in my ~/bin folder:

echo "Finding $2 in $1."
for jar in `find $1 -name \*.jar`
do
   echo -n "."
   for class in `jar tf $jar`
   do
     if [[ "`echo $class | grep $2`" = ""  ]] 
     then
        grep test /dev/null 
     else 
        echo -e "\n$jar : $class"
     fi
     done

 done

If your directory of .jar files is particularly large it can take a while to find. I've used it with hundreds of jars and it's slow, but effective.

Justin Thomas
  • 5,680
  • 3
  • 38
  • 63
  • thanks for your reply. I'm not good at shell, I have to call the script in java code, so if I pass you the first paremeter: all the jar files location split with "," the second is classname, how can I call your script and get the .class file in java? – JerryCai Jul 02 '12 at 04:48
1

Use the loadClass()method of java.net.URLClassLoader.

Nate
  • 31,017
  • 13
  • 83
  • 207
user207421
  • 305,947
  • 44
  • 307
  • 483
1

the EJP's tip is a reference point. It's a real case you have non-local urls, so it's more correct to create a classloader, load a class, and get its bytecode. Maybe somehow like that:

    URLClassLoader classLoader = new URLClassLoader(list.toArray(new URL[0]));
    Class<?> clazz = classLoader.loadClass(path);
    File tmp = new File(System.getProperty("java.io.tmpdir"), path.replace(".", "_") + ".class");
    FileChannel tmpChannel = new FileOutputStream(tmp).getChannel();
    InputStream is = clazz.getResourceAsStream("/" + path.replace(".", "/") + ".class");
    tmpChannel.transferFrom(Channels.newChannel(is), 0, Long.MAX_VALUE);
    URI uri = tmp.toURI();
Pavel K.
  • 436
  • 4
  • 11
  • well, maybe, if all the urls refer to files on the disk. – Pavel K. Jul 02 '12 at 07:37
  • moreover, with classloader, you don't have to explore each jar for each classname. Suppose you don't know what jar contains a class you're interested, so you need to run through all the jars to find where the class lies. IMHO this way is inefficient. So create a classloader for a bunch of urls, and use this single instance to find all classes you need – Pavel K. Jul 02 '12 at 07:49
0

I'm using simple bash script for decompiling.

#!/bin/bash

if [ -z "$1" ] ; then
    echo "There is no argument please enter jar file name"
    exit -1
fi

FNAME=$1
FNAME_SHORT=`basename $1`
DNAME=${FNAME_SHORT/.*/}_jar

CLASS_DIR=${DNAME}/cls
SRC_DIR=${DNAME}/java

mkdir -p $CLASS_DIR
mkdir -p $SRC_DIR
echo "Unpacking ${FNAME}.."
( cd ${CLASS_DIR} ; unzip -o ../../${FNAME} ) > /dev/null 2>&1 || exit -1
CLASSES="$(cd $DNAME ; find cls -name "*.class" -print)"
if [ ! -z "$CLASSES" ] ; then
    for C in $CLASSES ; do
        CNAME=${C/.*/}
        CNAME=${CNAME#cls/}
        JFILE=$SRC_DIR/${CNAME}.java
        CNAME=${CNAME//\//.}
        if [ -f $JFILE ] ; then
            echo "Skipping $C..."
            continue
        fi
        echo -n "Decompile $C to $DNAME..."
        jad -o -f -d $DNAME/java -lnc -s .java -r $DNAME/$C > /dev/null 2>&1
        if [ $? != 0 ] ; then
            echo "FAILED"
            javap -c -classpath $CLASS_DIR ${CNAME} > $JFILE
        else
            echo "OK"
        fi
    done
fi
Younggun Kim
  • 938
  • 10
  • 26
  • thanks for your script, but I'm not good at bash, if I want to use your script in java code, how can I do? The input is jar file URL and classname, can you get me back the source code? – JerryCai Jul 02 '12 at 04:39
  • My script is named 'decode' and I use it under command shell like this: `$ for jarfile in *.jar; do decode $jarfile; done;` – Younggun Kim Jul 02 '12 at 05:08