Checking if loaded class is assignable from another? - java

So, I have a custom classloader to load classes into memory from byte arrays, and I have a problem: When I attempt to see if a class is assignable from another(ClassLoader.isAssignableFrom), it returns false, even if the compiled class extends or implements it. I assume because it's loaded by a different and custom classloader rather than the system one, so how would I fix this? The reason that I need to do this is I want to check if class files in a jar file are ClassLoaders themselves, because I'm making a java virus scanner for jar files.
Custom ClassLoader:
public class CL extends ClassLoader {
byte[] jar = null;
private HashMap<String, Class<?>> classes = new HashMap<String, Class<?>>();
private HashMap<String, byte[]> resources = new HashMap<String, byte[]>();
public CL(byte[] jar) {
this.jar = jar;
}
private JarInputStream getStream() {
try {
return new JarInputStream(new ByteArrayInputStream(jar));
}catch (IOException e) {
e.printStackTrace();
}
return null;
}
public InputStream getResourceAsStream(String name) {
if (!resources.containsKey(name)) {
try {
JarInputStream stream = getStream();
JarEntry entry = stream.getNextJarEntry();
ArrayList<JarEntry> ent = new ArrayList<JarEntry>();
while (entry != null) {
String en = entry.getName().replace("/", ".");
if (en.contains(".")) {
en = en.substring(0, en.lastIndexOf("."));
}
if (en.equals(name)) {
break;
}
ent.add(entry);
entry = stream.getNextJarEntry();
}
if (entry == null) {
for (JarEntry e : ent) {
String en = e.getName().replace("/", ".");
if (en.contains(".")) {
en = en.substring(0, en.lastIndexOf("."));
}
if (en.lastIndexOf(".") > 0 && en.substring(en.lastIndexOf(".") + 1).equals(name)) {
entry = e;
break;
}
}
}
if (entry == null) {
return null;
}
ent = null;
ByteArrayOutputStream byt = new ByteArrayOutputStream();
while (true) {
int r = stream.read();
if (r < 0) break;
byt.write(r);
}
stream.close();
byte[] reqc = byt.toByteArray();
return new ByteArrayInputStream(reqc);
}catch (IOException e) {
e.printStackTrace();
}
}else {
return new ByteArrayInputStream(resources.get(name));
}
return null;
}
public Class<?> findClass(String name) {
if (!classes.containsKey(name)) {
try {
JarInputStream stream = getStream();
JarEntry entry = stream.getNextJarEntry();
while (entry != null) {
String en = entry.getName().replace("/", ".");
if (en.contains(".")) {
en = en.substring(0, en.lastIndexOf("."));
}
if (en.equals(name)) {
break;
}
entry = stream.getNextJarEntry();
}
if (entry == null) {
return null;
}
ByteArrayOutputStream byt = new ByteArrayOutputStream();
while (true) {
int r = stream.read();
if (r < 0) break;
byt.write(r);
}
stream.close();
byte[] reqc = byt.toByteArray();
Class<?> c = defineClass(name, reqc, 0, reqc.length, CL.class.getProtectionDomain());
classes.put(name, c);
return c;
}catch (IOException e) {
e.printStackTrace();
}
}else {
return classes.get(name);
}
return null;
}
}
My code for checking if something is assignable from a classloader(cl is an instance of my classloader):
Class<?> cls = cl.findClass(fname);
boolean isCL = false;
if (cls.isAssignableFrom(ClassLoader.class)) {
isCL = true;
}
boolean bCL = false;
for (Method m : cls.getMethods()) {
String mn = m.getName();
if (isCL) {
if (mn.contains("loadClass") || mn.contains("defineClass") || mn.contains("findClass")) {
bCL = true;
}
}
}
The problem: the isAssignableFrom returns false, even if it should be true.
So, is there a fix to this? I do not want to switch classloaders, as I am initially loading from a jar, but I want to be able to load jars inside jars and zips. Thanks!

Your problem is that you are using isAssignableFrom incorrectly (albeit, it is a very confusing method). this is what you want:
ClassLoader.class.isAssignableFrom(cls)

I ended up figuring out a hack of sorts, for superclasses(superinterfaces are different) use this:
cls.getSuperclass().getName().equals("java.lang.ClassLoader") Where the java.lang.ClassLoader is the fully qualified name you need to check. You do not need to have the class loaded in your main classloader.

Related

IDE is wanting to convert my Int into a boolean? and one of my methods says it cannot be applied

I am having issues. it's wanting to change int len into a boolean? And there is some issue with addNode method.
Not Quite sure what else to add there is this little box yelling at me about adding some more details.
I have all the imports needed.
public class CustomClassLoader extends ClassLoader {
private HashMap<String, ClassNode> classes = new HashMap<String, ClassNode>();
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return findClass(name);
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
ClassNode node = classes.get(name.replace('.', '/'));
if (node != null)
return nodeToClass(node);
else
return super.findClass(name);
}
public final void addNode(String name, ClassNode node) {
classes.put(node.name, node);
}
private final Class<?> nodeToClass(ClassNode node) {
if (super.findLoadedClass(node.name) != null)
return findLoadedClass(node.name);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
node.accept(cw);
byte[] b = cw.toByteArray();
return defineClass(node.name.replace('/', '.'), b, 0, b.length,
getDomain());
}
private final ProtectionDomain getDomain() {
CodeSource code = new CodeSource(null, (Certificate[]) null);
return new ProtectionDomain(code, getPermissions());
}
private final Permissions getPermissions() {
Permissions permissions = new Permissions();
permissions.add(new AllPermission());
return permissions;
}
private HashMap<String,URL> resources = new HashMap<String,URL>();
public final void addResource(String name,URL url){
resources.put(name, url);
}
protected URL findResource(String name) {
if (getSystemResource(name) == null){
if (resources.containsKey(name))
return resources.get(name);
else
return null;
}else
return getSystemResource(name);
}
public void loadClassesFromJar(File jar) throws IOException {
ZipFile jf = new ZipFile(jar);
ZipEntry e;
Enumeration<? extends ZipEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
e = entries.nextElement();
if(e.isDirectory())
continue; //skip directories, as the file names contain the slashes
InputStream in = jf.getInputStream(e);
if (e.getName().endsWith(".class")) {
// reads the class
ClassReader reader = new ClassReader(in);
ClassNode cn = new ClassNode();
reader.accept(cn, ClassReader.SKIP_DEBUG
| ClassReader.SKIP_FRAMES);
//add the class to our node list
addNode(e.getName(), cn);
} else {
//write the resource to a temporary file
File tmp = File.createTempFile("06loader", "dat");
FileOutputStream fout = new FileOutputStream(tmp);
byte[] b = new byte[1024];
int len;
while (len = in.read() >= 0) {
fout.write(b, 0, len);
}
fout.close(); // flushes and closes the file output stream
addResource(e.getName(),tmp.toURI().toURL());
tmp.deleteOnExit(); // delete the file once java closes
}
}
jf.close(); // close the jar file
}
}
It's because of this line of code:
while (len = in.read() >= 0) {
It should be:
while ((len = in.read()) >= 0) {
EDIT: As #Johnny Mopp said, it's a precedence issue and not an equality vs assignement issue.

Error while creating new File in SD Card using DocumentFile.createFile();

I am trying to create a new File in SD Card for Android 5.0 and above. So first I am making the user grant the permission through SAF. This is how I am check if the selected Directory is SD Card or Not.
public static boolean wrong_directory_selected(Uri uri, Context con)
{
final File uri_path=new File(FileUtil.getFullPathFromTreeUri(uri,con));
if(uri_path.getName().toLowerCase().equals(new File("SD CARD PATH").getName().toLowerCase()))
{
return false;
}
return true;
}
And then this is how I am Trying to Create a new File.
DocumentFile move = DocumentFile(new File("path)).createFile(mime,"name); // But I am getting java.lang.NullPointerException
Below are the methods which I am using to get the DocumentFile for the Directory to which the file has to be Created.
public static DocumentFile DocumentFile(final File file)
{
DocumentFile rootDocFile = DocumentFile.fromTreeUri(con, permission().getUri());
String[] parts = (file.getPath()).split("\\/");
for (int i = 3; i < parts.length; i++)
{
rootDocFile = rootDocFile.findFile(parts[i]);
}
return rootDocFile;
}
public static UriPermission permission()
{
for (UriPermission permissionUri : con.getContentResolver().getPersistedUriPermissions())
{
final File uri_path = new File(FileUtil.getFullPathFromTreeUri(permissionUri.getUri(), con));
if (uri_path.getName().toLowerCase().equals(new File("SD_CARD_PATH").getName().toLowerCase()))
{
return permissionUri;
}
}
return null;
}
The code is working fine most of the time but sometime I am getting java.lang.NullPointerException.
Any Help would be Grateful.
EDIT: This is my FileUtil class
public final class FileUtil {
private static final String PRIMARY_VOLUME_NAME = "primary";
#Nullable
public static String getFullPathFromTreeUri(#Nullable final Uri treeUri, Context con)
{
if (treeUri == null)
{
return null;
}
String volumePath = FileUtil.getVolumePath(FileUtil.getVolumeIdFromTreeUri(treeUri),con);
if (volumePath == null)
{
return File.separator;
}
if (volumePath.endsWith(File.separator))
{
volumePath = volumePath.substring(0, volumePath.length() - 1);
}
String documentPath = FileUtil.getDocumentPathFromTreeUri(treeUri);
if (documentPath.endsWith(File.separator))
{
documentPath = documentPath.substring(0, documentPath.length() - 1);
}
if (documentPath.length() > 0)
{
if (documentPath.startsWith(File.separator))
{
return volumePath + documentPath;
}
else {
return volumePath + File.separator + documentPath;
}
}
else
{
return volumePath;
}
}
private static String getVolumePath(final String volumeId, Context con)
{
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
{
return null;
}
try {
StorageManager mStorageManager =
(StorageManager) con.getSystemService(Context.STORAGE_SERVICE);
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method getVolumeList = mStorageManager.getClass().getMethod("getVolumeList");
Method getUuid = storageVolumeClazz.getMethod("getUuid");
Method getPath = storageVolumeClazz.getMethod("getPath");
Method isPrimary = storageVolumeClazz.getMethod("isPrimary");
Object result = getVolumeList.invoke(mStorageManager);
final int length = Array.getLength(result);
for (int i = 0; i < length; i++)
{
Object storageVolumeElement = Array.get(result, i);
String uuid = (String) getUuid.invoke(storageVolumeElement);
Boolean primary = (Boolean) isPrimary.invoke(storageVolumeElement);
// primary volume?
if (primary && PRIMARY_VOLUME_NAME.equals(volumeId))
{
return (String) getPath.invoke(storageVolumeElement);
}
// other volumes?
if (uuid != null)
{
if (uuid.equals(volumeId))
{
return (String) getPath.invoke(storageVolumeElement);
}
}
}
// not found.
return null;
}
catch (Exception ex)
{
return null;
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getVolumeIdFromTreeUri(final Uri treeUri)
{
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if (split.length > 0)
{
return split[0];
}
else
{
return null;
}
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private static String getDocumentPathFromTreeUri(final Uri treeUri)
{
final String docId = DocumentsContract.getTreeDocumentId(treeUri);
final String[] split = docId.split(":");
if ((split.length >= 2) && (split[1] != null))
{
return split[1];
}
else
{
return File.separator;
}
}
}
EDIT 2 :
The Path in which the file has to be created is fine and I have also checked the Permission URI and even that is not null.
The Values are
The path where the file has to be created- /storage/external_SD
Permission Uri- content://com.android.externalstorage.documents/tree/6634-3765%3A
EDIT 3:
I am using this library to find the SD Card path.
Continue from this answer now that you have the DocumentFile (which is a directory to create a file inside it) through the loop just use myDocumentFile.createFile(...) to create a new file on your desired directory.
// creating the file
DocumentFile documentFileNewFile = documentFileGoal.createFile(myMimeType,
myNewFileName);
Then stream to it
outputStream = getContentResolver().openOutputStream(documentFileNewFile.getUri());
inputStream = new FileInputStream(myInputFile);
...
if (outputStream != null) {
byte[] buffer = new byte[1024];
int read;
while ((read = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, read);
}
}
...
...
...
} finally {
if (inputStream != null)
inputStream.close();
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
}
Edite
Prevent findFile on a null DocumentFile by checking the value of rootDocFile on each loop. (happens when the user selects a wrong path instead of the sd-card)
for (int i = 3; i < parts.length; i++)
{
if (rootDocFile != null) {
rootDocFile = rootDocFile.findFile(parts[i]);
}
}

Add words to languagetool suggesting list

I use LanguageTool for some spellchecking and spell correction functionality in my application.
The LanguageTool documentation describes how to exclude words from spell checking (with call the addIgnoreTokens(...) method of the spell checking rule you're using).
How do you add some words (e.g., from a specific dictionary) to spell checking? That is, can LanguageTool fix words with misspellings and suggest words from my specific dictionary?
Unfortunately, the API doesn't support this I think. Without the API, you can add words to spelling.txt to get them accepted and used as suggestions. With the API, you might need to extend MorfologikSpellerRule and change this place of the code. (Disclosure: I'm the maintainer of LanguageTool)
I have similar requirement, which is load some custom words into dictionary as "suggest words", not just "ignored words". And finally I extend MorfologikSpellerRule to do this:
Create class MorfologikSpellerRuleEx extends from MorfologikSpellerRule, override the method "match()", and write my own "initSpeller()" for creating spellers.
And then for the language tool, create this custom speller rule to replace existing one.
Code:
Language lang = new AmericanEnglish();
JLanguageTool langTool = new JLanguageTool(lang);
langTool.disableRule("MORFOLOGIK_RULE_EN_US");
try {
MorfologikSpellerRuleEx spellingRule = new MorfologikSpellerRuleEx(JLanguageTool.getMessageBundle(), lang);
spellingRule.setSpellingFilePath(spellingFilePath);
//spellingFilePath is the file has my own words + words from /hunspell/spelling_en-US.txt
langTool.addRule(spellingRule);
} catch (IOException e) {
e.printStackTrace();
}
The code of my custom MorfologikSpellerRuleEx:
public class MorfologikSpellerRuleEx extends MorfologikSpellerRule {
private String spellingFilePath = null;
private boolean ignoreTaggedWords = false;
public MorfologikSpellerRuleEx(ResourceBundle messages, Language language) throws IOException {
super(messages, language);
}
#Override
public String getFileName() {
return "/en/hunspell/en_US.dict";
}
#Override
public String getId() {
return "MORFOLOGIK_SPELLING_RULE_EX";
}
#Override
public void setIgnoreTaggedWords() {
ignoreTaggedWords = true;
}
public String getSpellingFilePath() {
return spellingFilePath;
}
public void setSpellingFilePath(String spellingFilePath) {
this.spellingFilePath = spellingFilePath;
}
private void initSpellerEx(String binaryDict) throws IOException {
String plainTextDict = null;
if (JLanguageTool.getDataBroker().resourceExists(getSpellingFileName())) {
plainTextDict = getSpellingFileName();
}
if (plainTextDict != null) {
BufferedReader br = null;
if (this.spellingFilePath != null) {
try {
br = new BufferedReader(new FileReader(this.spellingFilePath));
}
catch (Exception e) {
br = null;
}
}
if (br != null) {
speller1 = new MorfologikMultiSpeller(binaryDict, br, plainTextDict, 1);
speller2 = new MorfologikMultiSpeller(binaryDict, br, plainTextDict, 2);
speller3 = new MorfologikMultiSpeller(binaryDict, br, plainTextDict, 3);
br.close();
}
else {
speller1 = new MorfologikMultiSpeller(binaryDict, plainTextDict, 1);
speller2 = new MorfologikMultiSpeller(binaryDict, plainTextDict, 2);
speller3 = new MorfologikMultiSpeller(binaryDict, plainTextDict, 3);
}
setConvertsCase(speller1.convertsCase());
} else {
throw new RuntimeException("Could not find ignore spell file in path: " + getSpellingFileName());
}
}
private boolean canBeIgnored(AnalyzedTokenReadings[] tokens, int idx, AnalyzedTokenReadings token)
throws IOException {
return token.isSentenceStart() || token.isImmunized() || token.isIgnoredBySpeller() || isUrl(token.getToken())
|| isEMail(token.getToken()) || (ignoreTaggedWords && token.isTagged()) || ignoreToken(tokens, idx);
}
#Override
public RuleMatch[] match(AnalyzedSentence sentence) throws IOException {
List<RuleMatch> ruleMatches = new ArrayList<>();
AnalyzedTokenReadings[] tokens = getSentenceWithImmunization(sentence).getTokensWithoutWhitespace();
// lazy init
if (speller1 == null) {
String binaryDict = null;
if (JLanguageTool.getDataBroker().resourceExists(getFileName())) {
binaryDict = getFileName();
}
if (binaryDict != null) {
initSpellerEx(binaryDict); //here's the change
} else {
// should not happen, as we only configure this rule (or rather its subclasses)
// when we have the resources:
return toRuleMatchArray(ruleMatches);
}
}
int idx = -1;
for (AnalyzedTokenReadings token : tokens) {
idx++;
if (canBeIgnored(tokens, idx, token)) {
continue;
}
// if we use token.getToken() we'll get ignored characters inside and speller
// will choke
String word = token.getAnalyzedToken(0).getToken();
if (tokenizingPattern() == null) {
ruleMatches.addAll(getRuleMatches(word, token.getStartPos(), sentence));
} else {
int index = 0;
Matcher m = tokenizingPattern().matcher(word);
while (m.find()) {
String match = word.subSequence(index, m.start()).toString();
ruleMatches.addAll(getRuleMatches(match, token.getStartPos() + index, sentence));
index = m.end();
}
if (index == 0) { // tokenizing char not found
ruleMatches.addAll(getRuleMatches(word, token.getStartPos(), sentence));
} else {
ruleMatches.addAll(getRuleMatches(word.subSequence(index, word.length()).toString(),
token.getStartPos() + index, sentence));
}
}
}
return toRuleMatchArray(ruleMatches);
}
}

Custom Annotation Processor - Detect Method with Annotations

I am trying to write an annotation Procssor to detect the methods that are annotated with the #PrintMethod annotation. For example in the test Class below, i want to print the codes within the test Method. Is there a way to do it?
From the AnnotationProcessor class stated below, i am only able get the method name but not the details of the method.
Test Class
public class test {
public static void main(String[] args) {
System.out.println("Args");
}
#PrintMethod
private boolean testMethod(String input) {
if(input!=null) {
return true;
}
return false;
}
}
Annotation Processor Class
public class AnnotationProcessor extends AbstractProcessor {
//......
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//retrieve test Anntoation
Set<? extends Element> ann =roundEnv.getElementsAnnotatedWith(PrintMethod.class);
//Print the Method Name
for(Element e: ann) {
String msg="Element ee :"+ee.getSimpleName().toString();
processingEnv.getMessager().printMessage( javax.tools.Diagnostic.Kind.ERROR, msg, e);
}
}
}
I was curious about this too so I decided to try and figure it out. Turns out to be easier than I expected. All you need to do is leverage the Trees api out of the proprietary tools.jar library. I've made a quick annotation processor along these lines here: https://github.com/johncarl81/printMethod
Here's the meat of it:
#SupportedSourceVersion(SourceVersion.RELEASE_6)
#SupportedAnnotationTypes("org.printMethod.PrintMethod")
public class PrintMethodAnnotationProcessor extends AbstractProcessor {
private Trees trees;
#Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
trees = Trees.instance(processingEnv); //initialize the Trees api.
}
#Override
public boolean process(Set<? extends TypeElement> typeElements, RoundEnvironment roundEnvironment) {
MethodPrintScanner visitor = new MethodPrintScanner();
for (Element e : roundEnvironment.getElementsAnnotatedWith(PrintMethod.class)) {
TreePath tp = trees.getPath(e);
// visit the annotated methods
visitor.scan(tp, trees);
}
return true;
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
And the MethodPrintScanner:
public class MethodPrintScanner extends TreePathScanner {
#Override
public Object visitMethod(MethodTree methodTree, Object o) {
System.out.println(methodTree);
return null;
}
}
You can see that we are able to visit the TreePath associated with the given annotated Element. For each method, we simply println() the methodTree which gives us the contents of the method.
Using your example, here's the output of the program during compilation:
#PrintMethod()
private boolean testMethod(String input) {
if (input != null) {
return true;
}
return false;
}
It's one thing to make it work in your IDE. But it's another to detect them once your code is packed inside jar files. The following code can manage both.
public static List<Class> getPackageClassListHavingAnnotation(String pPackageName,
Class<? extends Annotation> pAnnotation) throws Exception
{
try
{
List<Class> classList = getPackageClassList(pPackageName);
if ((pAnnotation == null) || (classList == null)) return classList;
List<Class> resultList = new ArrayList<Class>(classList.size());
outerLoop:
for (Class clazz : classList)
{
try
{
for (Method method : clazz.getMethods())
{
if (method.isAnnotationPresent(pAnnotation))
{
resultList.add(clazz);
continue outerLoop;
}
}
}
catch (Throwable e)
{
}
}
return (resultList.isEmpty()) ? null : resultList;
}
catch (Exception e)
{
return null;
}
}
It requires the following helper methods:
public static List<Class> getPackageClassList(String pPackageName) throws Exception
{
try
{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String path = pPackageName.replace('.', '/');
List<File> dirs = new ArrayList<File>();
List<JarFile> jars = new ArrayList<JarFile>();
Enumeration<URL> resources = classLoader.getResources(path);
if (resources != null)
{
String fileName;
URL resource;
File file;
while (resources.hasMoreElements())
{
resource = resources.nextElement();
fileName = resource.getFile();
if (fileName.contains("!"))
{
// jar file
resource = new URL(StringUtil.getArrayFromString(fileName, "!")[0]);
file = urlToFile(resource);
if (!file.exists()) continue;
jars.add(new JarFile(file));
}
else
{
// class file that is not in a jar file
file = urlToFile(resource);
if (!file.exists()) continue;
dirs.add(file);
}
}
}
List<Class> resultList = new ArrayList<Class>(1000);
List<Class> tmpClassList;
for (File directory : dirs)
{
tmpClassList = getPckDirClassList(directory, pPackageName);
if (tmpClassList != null) resultList.addAll(tmpClassList);
}
for (JarFile jar : jars)
{
tmpClassList = getPckJarClassList(jar, pPackageName);
if (tmpClassList != null) resultList.addAll(tmpClassList);
}
return (resultList.isEmpty()) ? null : resultList;
}
catch (Exception e)
{
return null;
}
}
private static List<Class> getPckJarClassList(JarFile pJar, String pPackageName)
{
if ((pJar == null) || (pPackageName == null)) return null;
List<Class> resultList = new ArrayList<Class>(100);
Enumeration<JarEntry> jarEntries = (pJar.entries());
JarEntry jarEntry;
String fullClassName;
while (jarEntries.hasMoreElements())
{
jarEntry = jarEntries.nextElement();
fullClassName = jarEntry.getName().replaceAll("/", ".");
if (!fullClassName.startsWith(pPackageName)) continue;
if (!fullClassName.endsWith(".class")) continue;
// do not do a Class.forName for the following path, this can crash the server
try
{
resultList.add(Class.forName(fullClassName.substring(0, fullClassName.length() - 6)));
}
catch (Throwable e)
{
}
}
return (resultList.isEmpty()) ? null : resultList;
}
/**
* Recursive method to find all classes in a package directory tree.
*/
private static List<Class> getPckDirClassList(File pDirectory, String pPackageName) throws ClassNotFoundException
{
try
{
if ((pDirectory == null) || (pPackageName == null)) return null;
if (!pDirectory.exists()) return null;
File[] files = pDirectory.listFiles();
if ((files == null) || (files.length == 0)) return null;
List<Class> resultList = new ArrayList<Class>(100);
List<Class> tmpClassList;
for (File file : files)
{
if (file.isDirectory())
{
tmpClassList = getPckDirClassList(file, pPackageName + "." + file.getName());
if (tmpClassList != null) resultList.addAll(tmpClassList);
}
else if (file.getName().endsWith(".class"))
{
try
{
resultList.add(Class.forName(pPackageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
}
catch (Throwable e)
{
}
}
}
return (resultList.isEmpty()) ? null : resultList;
}
catch (Exception e)
{
return null;
}
}
This code has been tested with .jar files on both windows and unix systems. It has also been tested with .java files in IntelliJ on windows.

Iterate over folders in JAR file directly

I have the following code to iterate over folders and files in the class path and determine the classes and get a field with a ID and print them out to a logger. This is working fine if I run this code in my IDE, but if I package my project into a JAR file and this JAR file into a EXE file with launch4j, I can't iterate over my classes again.
I get the following path if I try to iterate over my classes in the JAR/EXE file:
file:/C:/ENTWICKLUNG/java/workspaces/MyProject/MyProjectTest/MyProjectSNAPSHOT.exe!/com/abc/def
How can I achieve this to iterate over all my classes in my JAR/EXE file?
public class ClassInfoAction extends AbstractAction
{
/**
* Revision/ID of this class from SVN/CVS.
*/
public static String ID = "#(#) $Id ClassInfoAction.java 43506 2013-06-27 10:23:39Z $";
private ClassLoader classLoader = ClassLoader.getSystemClassLoader();
private ArrayList<String> classIds = new ArrayList<String>();
private ArrayList<String> classes = new ArrayList<String>();
private int countClasses = 0;
#Override
public void actionPerformed(ActionEvent e)
{
countClasses = 0;
classIds = new ArrayList<String>();
classes = new ArrayList<String>();
getAllIds();
Iterator<String> it = classIds.iterator();
while (it.hasNext())
{
countClasses++;
//here I print out the ID
}
}
private void getAllIds()
{
String tempName;
String tempAbsolutePath;
try
{
ArrayList<File> fileList = new ArrayList<File>();
Enumeration<URL> roots = ClassLoader.getSystemResources("com"); //it is a path like com/abc/def I won't do this path public
while (roots.hasMoreElements())
{
URL temp = roots.nextElement();
fileList.add(new File(temp.getPath()));
GlobalVariables.LOGGING_logger.info(temp.getPath());
}
for (int i = 0; i < fileList.size(); i++)
{
for (File file : fileList.get(i).listFiles())
{
LinkedList<File> newFileList = null;
if (file.isDirectory())
{
newFileList = (LinkedList<File>) FileUtils.listFiles(file, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE);
if (newFileList != null)
{
for (int j = 0; j < newFileList.size(); j++)
{
tempName = newFileList.get(j).getName();
tempAbsolutePath = newFileList.get(j).getAbsolutePath();
checkIDAndAdd(tempName, tempAbsolutePath);
}
}
}
else
{
tempName = file.getName();
tempAbsolutePath = file.getAbsolutePath();
checkIDAndAdd(tempName, tempAbsolutePath);
}
}
}
getIdsClasses();
}
catch (IOException e)
{
}
}
private void checkIDAndAdd(String name, String absolutePath)
{
if (name.endsWith(".class") && !name.matches(".*\\d.*") && !name.contains("$"))
{
String temp = absolutePath.replace("\\", ".");
temp = temp.substring(temp.lastIndexOf(/* Class prefix */)); //here I put in the class prefix
classes.add(FilenameUtils.removeExtension(temp));
}
}
private void getIdsClasses()
{
for (int i = 0; i < classes.size(); i++)
{
String className = classes.get(i);
Class<?> clazz = null;
try
{
clazz = Class.forName(className);
Field idField = clazz.getDeclaredField("ID");
idField.setAccessible(true);
classIds.add((String) idField.get(null));
}
catch (ClassNotFoundException e1)
{
}
catch (NoSuchFieldException e)
{
}
catch (SecurityException e)
{
}
catch (IllegalArgumentException e)
{
}
catch (IllegalAccessException e)
{
}
}
}
}
You cannot create File objects from arbitrary URLs and use the usual filesystem traversal methods. Now, I'm not sure if launch4j does make any difference, but as for iterating over the contents of plain JAR file, you can use the official API:
JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile file = connection.getJarFile();
Enumeration<JarEntry> entries = file.entries();
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
if (e.getName().startsWith("com")) {
// ...
}
}
Above snippet lists all the entries in the JAR file referenced by url, i.e. files and directories.

Categories

Resources