Get all classes from a Jar file using Reflection - java

I need to get all the Classes from a Jar file that's on the ClassPath and I used this article for my solution. The methods from the article work when I set the path to a package name but does not work if the package is in a Jar file.
Under the comments people posted code which they claim will work for Jar files (they modified the articles code). I've looked over their code and it seems like it should work, and it does work for packages that are not in jar files, but they did not specify what to pass in as the argument telling the methods the package is from a jar file. I've tried every combination I could think of but nothing will work.
So, with a package that's not in a Jar file, this would be the working way to use the methods,
Class[] myClasses = getClassesInPackage("my.package.name", null);
And it's supposed to work with a package in a Jar file but I don't know what to replace "my.package.name" with. I've tried just giving the package name that's on the Jar file but it doesn't find anything.
My idea is it's something similar to "myJarName" or "myJarName.my.package.name" but none of these are working.
Here is the full code,
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TreeSet;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static java.lang.System.out;
public class ClassSpy {
public static void main(String... args) {
try {
Class[] myClasses = getClassesInPackage("you.package.name.here", null);
for(Class index: myClasses)
out.format("Class:%n %s%n%n", index.getCanonicalName());
}
catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
/**
* Scans all classes accessible from the context
* class loader which belong to the given package
* and subpackages. Adapted from
* http://snippets.dzone.com/posts/show/4831
* and extended to support use of JAR files
* #param packageName The base package
* #param regexFilter an optional class name pattern.
* #return The classes
*/
public static Class[] getClassesInPackage(String packageName, String regexFilter) {
Pattern regex = null;
if (regexFilter != null)
regex = Pattern.compile(regexFilter);
try {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
assert classLoader != null;
String path = packageName.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
List<String> dirs = new ArrayList<String>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(resource.getFile());
}
TreeSet<String> classes = new TreeSet<String>();
for (String directory : dirs) {
classes.addAll(findClasses(directory, packageName, regex));
}
ArrayList classList = new ArrayList();
for (String clazz : classes) {
classList.add(Class.forName(clazz));
}
return (Class[]) classList.toArray(new Class[classes.size()]);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Recursive method used to find all classes in a given path
* (directory or zip file url). Directories are searched recursively.
* (zip files are Adapted from http://snippets.dzone.com/posts/show/4831
* and extended to support use of JAR files #param path The base
* directory or url from which to search. #param packageName The package
* name for classes found inside the base directory #param regex an
* optional class name pattern. e.g. .*Test*
* #return The classes
*/
private static TreeSet findClasses(String path, String packageName, Pattern regex) throws Exception {
TreeSet classes = new TreeSet();
if (path.startsWith("file:") && path.contains("!")) {
String[] split = path.split("!");
URL jar = new URL(split[0]);
ZipInputStream zip = new ZipInputStream(jar.openStream());
ZipEntry entry;
while ((entry = zip.getNextEntry()) != null) {
System.out.println("Here 1");
if (entry.getName().endsWith(".class")) {
System.out.println("Here 2");
String className = entry.getName()
.replaceAll("[$].*", "")
.replaceAll("[.]class", "")
.replace('/', '.');
if (className.startsWith(packageName) && (regex == null
|| regex.matcher(className).matches()))
classes.add(className);
}
}
}
File dir = new File(path);
if (!dir.exists()) {
return classes;
}
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
assert !file.getName().contains(".");
classes.addAll(findClasses(file.getAbsolutePath()
, packageName + "." + file.getName()
, regex));
}
else if (file.getName().endsWith(".class")) {
String className = packageName + '.' + file
.getName()
.substring(0, file.getName().length() - 6);
if (regex == null || regex.matcher(className).matches())
classes.add(className);
}
}
return classes;
}
}
You should be able to copy all of the code and compile it (some imports won't be used because I'm only showing the parts related to the problem and not the whole program).
For a working demonstration you can change "my.package.name" to some package you have that's on the classpath.

Class[] myClasses = getClassesInPackage("my.package.name", null);
Ive never used this method before if i would need to do what you're trying to do i would just use
Classname name = new Classname();

Related

java zip filesystem reading zip entries

SO, from what I've gathered, one is supposed to be able to create a filesystem from a zip from java 7 and beyond. I'm trying this, the ultimate goal is to use the File object and access these files, just as if I accessed an unzipped file.
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class MainZipTest {
public static void main(String[] args) throws IOException, URISyntaxException {
Map<String, String> env = new HashMap<>();
env.put("read", "true");
File file = new File("C:/pathtoazip/data.zip");
URI uri = file.toURI();
String path = "jar:" + uri;
FileSystem fs = FileSystems.newFileSystem(URI.create(path), env);
for (Path p : fs.getRootDirectories()) {
System.out.println("root" + p);
//says "/"
System.out.println(new File(p.toString()).exists());
for (File f : new File(p.toString()).listFiles())
System.out.println(f.getAbsolutePath());
//lists the contents of my c drive!
}
System.out.println(new File("somefile.txt").exists());
System.out.println(fs.getPath("somefile.txt").toFile().exists());
System.out.println(new File("/somefile.txt").exists());
System.out.println(fs.getPath("/somefile.txt").toFile().exists());
}
}
it all prints "false". What am I doing wrong here? Or am I wrong in my assumption that I can access these files through the File object? If so, how does one access them?
Path was introduced as generalization of File (disk file). A Path can be inside a zip file, an URL, and more.
You can use Files with Path for similar File functionality.
for (Path p : fs.getRootDirectories()) {
System.out.println("root: " + p);
System.out.println(Files.exists(p));
Files.list(p).forEach(f -> System.out.println(f.toAbsolutePath()));
}
Note that a Path, like from a zip will maintain its actual file system view (fs, the zip).
So avoid File.

Java - traverse through folders and do something if intended folder is present

Being very new to Java, I'm unable to bring a small concept to a syntactic form. Apologies.
My project structure looks like below & i'm trying to walk thru the sub folders of applications
directory & search for a folder named conduit, if present, create a new folder called base parallel to it.
At best I came up with the below code, post that, kind of struggling.
/home/project_A/applications
|sub_project_A
|target
|conduit
|sub_project_B
|target
|conduit
|sub_project_C
|target
|class
|sub_project_D
|target
|conduit
public class Test
{
public static void main(String[] args)
{
Path currentRelativePath = Paths.get("");
String s = currentRelativePath.toAbsolutePath().toString();
String appDir = s + "/applications";
System.out.println("Directory Exists" + appDir);
}
}
You can use BFS to traverse the sub directories:
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.Queue;
public class MyClass {
public static void main(String[] args) {
Path currentRelativePath = Paths.get("");
Queue<Path> paths = new LinkedList<>();
paths.add(currentRelativePath);
while(!paths.isEmpty()) {
Path current = paths.poll();
File currentFile = current.toFile();
if(currentFile.isDirectory()) {
if(currentFile.getName().equals("conduit")) {
// Found the directory called conduit, Do what you have to do here
}else {
for(String fileName : currentFile.list()) {
paths.add(Paths.get(currentFile.getAbsolutePath()+"/"+fileName));
}
}
}
}
}
}
Using Files.find is a good way to traverse a series of directories quickly to find files or directories matching any criteria you need. Here is an example which prints off the matches:
BiPredicate<Path, BasicFileAttributes> predicate = (p,a) -> a.isDirectory() && "conduit".equals(p.getFileName().toString());
try(var dirs = Files.find(Path.of("."), Integer.MAX_VALUE, predicate)) {
dirs.forEach(p -> {
System.out.println("Found "+p+", create if not exists: "+p.resolveSibling("base"));
}
);
}

Moving large files in java

I have to move files from one directory to other directory.
Am using property file. So the source and destination path is stored in property file.
Am haivng property reader class also.
In my source directory am having lots of files. One file should move to other directory if its complete the operation.
File size is more than 500MB.
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import static java.nio.file.StandardCopyOption.*;
public class Main1
{
public static String primarydir="";
public static String secondarydir="";
public static void main(String[] argv)
throws Exception
{
primarydir=PropertyReader.getProperty("primarydir");
System.out.println(primarydir);
secondarydir=PropertyReader.getProperty("secondarydir");
File dir = new File(primarydir);
secondarydir=PropertyReader.getProperty("secondarydir");
String[] children = dir.list();
if (children == null)
{
System.out.println("does not exist or is not a directory");
}
else
{
for (int i = 0; i < children.length; i++)
{
String filename = children[i];
System.out.println(filename);
try
{
File oldFile = new File(primarydir,children[i]);
System.out.println( "Before Moving"+oldFile.getName());
if (oldFile.renameTo(new File(secondarydir+oldFile.getName())))
{
System.out.println("The file was moved successfully to the new folder");
}
else
{
System.out.println("The File was not moved.");
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
System.out.println("ok");
}
}
}
My code is not moving the file into the correct path.
This is my property file
primarydir=C:/Desktop/A
secondarydir=D:/B
enter code here
Files should be in B drive. How to do? Any one can help me..!!
Change this:
oldFile.renameTo(new File(secondarydir+oldFile.getName()))
To this:
oldFile.renameTo(new File(secondarydir, oldFile.getName()))
It's best not to use string concatenation to join path segments, as the proper way to do it may be platform-dependent.
Edit: If you can use JDK 1.7 APIs, you can use Files.move() instead of File.renameTo()
Code - a java method:
/**
* copy by transfer, use this for cross partition copy,
* #param sFile source file,
* #param tFile target file,
* #throws IOException
*/
public static void copyByTransfer(File sFile, File tFile) throws IOException {
FileInputStream fInput = new FileInputStream(sFile);
FileOutputStream fOutput = new FileOutputStream(tFile);
FileChannel fReadChannel = fInput.getChannel();
FileChannel fWriteChannel = fOutput.getChannel();
fReadChannel.transferTo(0, fReadChannel.size(), fWriteChannel);
fReadChannel.close();
fWriteChannel.close();
fInput.close();
fOutput.close();
}
The method use nio, it make use os underling operation to improve performance.
Here is the import code:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
If you are in eclipse, just use ctrl + shift + o.

Learning Java Compiler APIs,why does trees.getElement(treepath) return null?

I'm trying to parse a java file with Java Compiler APIs.
The documents are very poor. After hours of digging I still cannot get the Trees#getElement work for me. Here's my code:
import com.sun.source.tree.*;
import com.sun.source.util.*;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class CodeAnalyzerTreeVisitor extends TreePathScanner<Object, Trees> {
#Override
public Object visitClass(ClassTree classTree, Trees trees) {
System.out.println("className " + classTree.getSimpleName());
//prints name of class
TreePath path = getCurrentPath();
printLocationAndSource(trees, path, classTree);
//prints the original source code
while (path != null) {
System.out.println("treepath");
System.out.println(trees.getElement(path));
path = path.getParentPath();
}
//it prints several nulls here
//why?
return super.visitClass(classTree, trees);
}
public static void printLocationAndSource(Trees trees,
TreePath path, Tree tree) {
SourcePositions sourcePosition = trees.getSourcePositions();
long startPosition = sourcePosition.
getStartPosition(path.getCompilationUnit(), tree);
long endPosition = sourcePosition.
getEndPosition(path.getCompilationUnit(), tree);
JavaFileObject file = path.getCompilationUnit().getSourceFile();
CharBuffer sourceContent = null;
try {
sourceContent = CharBuffer.wrap(file.getCharContent(true).toString().toCharArray());
} catch (IOException e) {
e.printStackTrace();
}
CharBuffer relatedSource = null;
if (sourceContent != null) {
relatedSource = sourceContent.subSequence((int) startPosition, (int) endPosition);
}
System.out.println("start: " + startPosition + " end: " + endPosition);
// System.out.println("source: "+relatedSource);
System.out.println();
}
}
public class JavaParser {
private static final JavaCompiler javac
= ToolProvider.getSystemJavaCompiler();
private static final String filePath = "/home/pinyin/Source/hadoop-common/hadoop-yarn-project/hadoop-ya" +
"rn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/ya" +
"rn/server/resourcemanager/ResourceManager.java";
public static void main(String[] args) throws IOException {
StandardJavaFileManager jfm = javac.getStandardFileManager(null, null, null);
Iterable<? extends javax.tools.JavaFileObject> javaFileObjects = jfm.getJavaFileObjects(filePath);
String[] sourcePathParam = {
"-sourcepath",
"/home/pinyin/Source/hadoop-common/hadoop-yarn-project/hadoop-yarn/" +
"hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/"
};
List<String> params = new ArrayList<String>();
params.addAll(Arrays.asList(sourcePathParam));
JavacTask task = (JavacTask) javac.getTask(null, jfm, null, params, null, javaFileObjects);
Iterable<? extends CompilationUnitTree> asts = task.parse();
Trees trees = Trees.instance(task);
for (CompilationUnitTree ast : asts) {
new CodeAnalyzerTreeVisitor().scan(ast, trees);
}
}
}
The lines about params and -sourcepath are added because I thought the compiler is trying to find the source file in the wrong places. They didn't work.
I'm still trying to understand how the Trees, javac and related JSRs work together, are there any recommended documents for beginners?
Thanks for your help.
edit:
The java file I'm trying to analyze is:
https://github.com/apache/hadoop-common/blob/trunk/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ResourceManager.java
The file can be compiled without errors in its maven project, but its dependencies are not passed to javac in my situation. I'm not sure if this is the problem.
The trees.getElement returns null in the middle part of the code above, while the other parts seems to work well.
According to this answer, it seems that the Elements' information is not usable until the compilation is completed.
So calling task.analyze() solved my problem. Although javac is complaining about missing dependencies.
Please correct me if I'm wrong, thanks.

Creating a directory wherever jar file is located through java

I have already surveyed SO for an answer, and could not find an appropriate one.
When I launch my program from a jar I need to create a folder in the directory where the jar file is located. It should not matter where the user saves the jar file.
Here is the newest code I was playing with: A System.out.println will print out the correct directory but the folder will not be created. In contrast,everything is being saved to my System32 folder as of now.
public static String getProgramPath() throws IOException{
String currentdir = System.getProperty("user.dir");
currentdir = currentdir.replace( "\\", "/" );
return currentdir;
}
File dir = new File(getProgramPath() + "Comics/");//The name of the directory to create
dir.mkdir();//Creates the directory
To get a Jar's path can be a little trickier than simply getting the user.dir directory. I can't remember the details why, but user.dir does not return this path reliably in all situations. If you absolutely must get the jar's path, then you need to do a little black magic and first get the class's protectionDomain. Something like:
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import javax.swing.JOptionPane;
public class MkDirForMe {
public static void main(String[] args) {
try {
String path = getProgramPath2();
String fileSeparator = System.getProperty("file.separator");
String newDir = path + fileSeparator + "newDir2" + fileSeparator;
JOptionPane.showMessageDialog(null, newDir);
File file = new File(newDir);
file.mkdir();
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getProgramPath2() throws UnsupportedEncodingException {
URL url = MkDirForMe.class.getProtectionDomain().getCodeSource().getLocation();
String jarPath = URLDecoder.decode(url.getFile(), "UTF-8");
String parentPath = new File(jarPath).getParentFile().getPath();
return parentPath;
}
}
Even this isn't guaranteed to work, and you'll have to resign yourself to the fact that there are just some times (for instance for security reasons) when you won't be able to get a Jar's path.
With some changes (such as adding a "/" before Comics), I managed to create the directory where you expected it to. Here is the full code I used.
import java.io.*;
public class TestClass {
public static String getProgramPath() throws IOException{
String currentdir = System.getProperty("user.dir");
currentdir = currentdir.replace( "\\", "/" );
return currentdir;
}
public static void main(String[] argv) {
try {
String d = getProgramPath() + "/Comics/";
System.out.println("Making directory at " + d);
File dir = new File(d);//The name of the directory to create
dir.mkdir();//Creates the directory
}
catch (Exception e) { System.out.println("Exception occured" + e);}
}
}
In the future, please don't hard code things like "/" and such. Use built-in libraries which will ask the OS what is right in this case. This ensures the functionality doesn't break (as easily) cross platform.
Of course, catch the exception properly etc. This is just quick and dirty attempt to mold your code into something that works.

Categories