I am trying to add a properties file to the classpath dynamically as below
try {
File fileToAdd = new File(FILE_PATH);
URL u = fileToAdd.toURL();
ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
if (sysLoader instanceof URLClassLoader) {
sysLoader = (URLClassLoader) sysLoader;
Class<URLClassLoader> sysLoaderClass = URLClassLoader.class;
// use reflection to invoke the private addURL method
Method method = sysLoaderClass.getDeclaredMethod("addURL",
new Class[] { URL.class });
method.setAccessible(true);
method.invoke(sysLoader, new Object[] { u });
}
} catch (Exception e) {
logger.error(e.getMessage());
}
But i cant see this file in my classpath. When i checked it using
System.getProperty("java.class.path")
I cant see my file in this list. Am i missing anything here?
you can't add the URL of the properties file, you have to add the URL of the directory in which the properties file resides in. As in: method.invoke(sysLoader, fileToAdd.getParent().toURL());
then you can use ClassLoader.getResourceAsStream("my.properties"); and the ClassLoader will search the newly added directory for the file.
from URLClassLoader
"This class loader is used to load classes and resources from a search path of URLs referring to both JAR files and directories. Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL is assumed to refer to a JAR file which will be opened as needed."
Perhaps try this code, but changing java.library.path or keep it the way it is if you can live with using the library path instead.
/**
* Allows you to add a path to the library path during runtime
* #param dllLocation The path you would like to add
* #return True if the operation completed successfully, false otherwise
*/
public boolean addDllLocationToPath(final String dllLocation)
{
//our return value
boolean retVal = false;
try
{
System.setProperty("java.library.path", System.getProperty("java.library.path") + ";" + dllLocation);
//get the sys path field
Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
fieldSysPath.setAccessible(true);
fieldSysPath.set(null, null);
retVal = true;
}
catch (Exception e)
{
System.err.println("Could not modify path");
}
return retVal;
}
Related
I have a jar file that I load dynamically,
inside it there is in lib/ another jar (external library) that I use in main (import it.xxx.xx).
How do I load also this external library dynamically in classpath?
My code doesn't work:
public static void runOne(String jar, String class_name, Optional<String> test_name,
TestExecutionListener listener) throws Exception {
Launcher launcher = LauncherFactory.create();
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { new File(pathJars+"/"+jar).toURI().toURL() },
ServiceUtil.class.getClassLoader()
);
loader.getClass();
addURL(loader); <--here i want add a jar to classpath!
Class cls=loader.loadClass(class_name);
Constructor constructor = cls.getConstructor();
constructor.newInstance();
LauncherDiscoveryRequest request;
if (test_name.isPresent()) {
Method m = cls.getMethod(test_name.get());
request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectMethod(cls,m))
.build();
}
else{
request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(cls))
.build();
}
TestPlan testPlan = launcher.discover(request);
launcher.registerTestExecutionListeners(listener);
launcher.execute(request);
//launcher.execute(request);
loader=null;
System.gc();
}
public static void addURL(ClassLoader loader) throws IOException {
URL u=loader.getResource("lib/sem-platform-sdk-1.0-SNAPSHOT.jar");
Class[] parameters = new Class[]{URL.class};
URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysloader, new Object[]{u});
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}//end try catch
}//end method
Thanks
This is generally done with a build tool (e.g. maven or gradle). I don't know if you are using one of these. They make life so much easier.
We use Maven with the Apache Shade plugin to do exactly this. Maven has commands to set up the configuration for you automatically, then you add the Shade plugin to the resulting configuration file (pom.xml).
https://maven.apache.org/plugins/maven-shade-plugin/index.html
If I understand your problem correctly, you want the classes loaded from the jar file to be able to access the classes in the nested jar file. You can accomplish this by creating a ClassLoader with one entry for the jar file and another entry for the nested jar file.
Java has a special URL scheme, jar:, for referring to a jar entry directly. (This scheme and syntax is described in the documentation of JarURLConnection.) So you can construct your ClassLoader this way:
URL jarURL = new File(pathJars+"/"+jar).toURI().toURL();
URL semURL = new URL("jar:" + jarURL + "!/"
+ "lib/sem-platform-sdk-1.0-SNAPSHOT.jar");
ClassLoader loader = URLClassLoader.newInstance(
new URL[] { jarURL, semURL },
ServiceUtil.class.getClassLoader()
);
I am developing a class loader that will load plugins into my software. I have a jar file with two things in it, the package containing my code, and a text file containing the name of the class that I want to load from the jar. Is there a way for my app to read the text in the file and get the class name, then load the class with that name from the jar file?
This is the code that works:
content = new Scanner(new File("plugins/" + listOfFiles[i].getName().replaceAll(".jar", "") + "/" + "plugin.cfg")).useDelimiter("\\Z").next();
URL[] urls = null;
try {
File dir = new File("plugins/" + listOfFiles[i].getName());
URL url = dir.toURL();
urls = new URL[]{url};
} catch (MalformedURLException e) {
}
try {
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass(content.replaceAll("Main-Class:", ""));
Method enable = cls.getMethod("enable", (Class<?>[]) null);
enable.invoke(enable, null);
}catch (Exception e) {
System.out.println("One of the installed plugins might have an invalid plugin.cfg.");
}
The code reads a file with the name of the main class in it, and then loads that class from the jar file it extracted earlier.
I have written an application which manages several plugins which are provided as jars. I load the plugin classes using an URLClassLoader which works as supposed.
But now I am writing a plugin which loads some resources which are stored inside the jar. If I start this plugin as a standalone application everything works, but if I start it from inside my application I get a NullPointerException when I try to open the resources InputStream.
I open the stream like this:
this.getClass().getResourceAsStream("/templates/template.html");
My Eclipse project structure looks like:
src
|
+ My source files
resources
|
+ templates
|
+ template.html
The following loads my plugins:
private List<Class<?>> loadClasses(final File[] jars) {
List<Class<?>> classes = new ArrayList<Class<?>>();
URL[] urls = getJarURLs(jars);
URLClassLoader loader = new URLClassLoader(urls);
for (File jar : jars) {
JarFile jarFile = null;
try {
jarFile = new JarFile(jar);
} catch (IOException e) {
// Skip this jar if it can not be opened
continue;
}
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (isClassFile(entry.getName())) {
String className = entry.getName().replace("/", ".").replace(".class", "");
Class<?> cls = null;
try {
cls = loader.loadClass(className);
} catch (ClassNotFoundException e) {
// Skip this jar if a class inside it can not be loaded
continue;
}
classes.add(cls);
}
}
try {
jarFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
loader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return classes;
}
/**
* Checks if a path points to a class file or not.
*
* #param path the filename to check
* #return {#code true} if the path points to a class file or {#code false}
* if not
*/
private boolean isClassFile(final String path) {
return path.toLowerCase().endsWith(".class") && !path.toLowerCase().contains("package-info");
}
Then I make instances from this classes using newInstance().
I think that the root path of the plugins jar is not the same as the root path of the application or that not all contents of the jar files are loaded or both...
Can someone help me?
First note, that using getClass().getResource(...) also delegates to a ClassLoader, which is also responsible for loading resources. Which class loader is used? It is the same class loader, with which the class was loaded. Point.
In your code, you build up an URLClassLoader for loading some classes. So the same URLClassLoader will then be used for loading the resources, if the above mentioned call comes from a class inside your plugin.
This all seems to be ok ... but you did a little mistake. At the end of loading you also closed the loader. This will prevent subsequent calls to loadClass or getResource from returning anything meaningful. In fact, it could null, as now the loader cannot load the resource anymore.
Conclusion: Do not close the URLClassLoader, if you still need it ofr loading purposes. Instead keep the reference to this class loader and close it at the end of your program runtime.
the problem is next: i took the base classLoader code from here. but my classLoader is specific from a point, that it must be able to load classes from a filesystem(let's take WinOS), so in classLoader must be some setAdditionalPath() method, which sets a path(a directory on a filesystem), from which we'll load class(only *.class, no jars). here is code, which modifies the loader from a link(you can see, that only loadClass is modified), but it doesn't work properly:
public void setAdditionalPath(String dir) {
if(dir == null) {
throw new NullPointerException("");
}
this.Path = dir;
}
public Loader(){
super(Loader.class.getClassLoader());
}
public Class loadClass(String className) throws ClassNotFoundException {
if(Path.length() != 0) {
File file = new File(Path);
try {
// Convert File to an URL
URL url = file.toURL();
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
ClassLoader c = cl.getSystemClassLoader();
Class cls = c.loadClass(className);
return cls;
} catch (MalformedURLException e) {
} catch (ClassNotFoundException e) {
}
}
return findClass(Path);
}
I'd grateful if anyone helps :)
You can just use framework provided java.net.URLClassLoader. No need to write your own. It supports loading of classes from directories and JAR files.
Any URL that ends with a '/' is assumed to refer to a directory.
Otherwise, the URL is assumed to refer to a JAR file which will be
opened as needed.
It also supports a parent class loader. If this class loader does not suite your requirements, perhaps you can specify in more detail what you need. And in any case, you can look at the source and derive your own class loader class based on that.
Here is a short working snippet of code that should demostrate how to load a class by name from a URLClassLoader:
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// This URL for a directory will be searched *recursively*
URL classes =
new URL( "file:///D:/code/myCustomClassesAreUnderThisFolder/" );
ClassLoader custom =
new URLClassLoader( new URL[] { classes }, systemClassLoader );
// this class should be loaded from your directory
Class< ? > clazz = custom.loadClass( "my.custom.class.Name" );
// this class will be loaded as well, because you specified the system
// class loader as the parent
Class< ? > clazzString = custom.loadClass( "java.lang.String" );
Hiii...
I want to get the content of properties file into InputStream class object using getSystemResourceAsStream(). I have built the sample code. It works well using main() method,but when i deploy the project and run on the server, properties file path cannot obtained ... so inputstream object store null value.
Sample code is here..
public class ReadPropertyFromFile {
public static Logger logger = Logger.getLogger(ReadPropertyFromFile.class);
public static String readProperty(String fileName, String propertyName) {
String value = null;
try {
//fileName = "api.properties";
//propertyName = "api_loginid";
System.out.println("11111111...In the read proprty file.....");
// ClassLoader loader = ClassLoader.getSystemClassLoader();
InputStream inStream = ClassLoader.getSystemResourceAsStream(fileName);
System.out.println("In the read proprty file.....");
System.out.println("File Name :" + fileName);
System.out.println("instream = "+inStream);
Properties prop = new Properties();
try {
prop.load(inStream);
value = prop.getProperty(propertyName);
} catch (Exception e) {
logger.warn("Error occured while reading property " + propertyName + " = ", e);
return null;
}
} catch (Exception e) {
System.out.println("Exception = " + e);
}
return value;
}
public static void main(String args[]) {
System.out.println("prop value = " + ReadPropertyFromFile.readProperty("api.properties", "api_loginid"));
}
}
i deploy the project and run on the server,
This sounds like a JSP/Servlet webapplication. In that case, you need to use the ClassLoader which is obtained as follows:
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
This one has access to the all classpath paths tied to the webapplication in question and you're not anymore dependent on which parent classloader (a webapp has more than one!) has loaded your class.
Then, on this classloader, you need to just call getResourceAsStream() to get a classpath resource as stream, not the getSystemResourceAsStream() which is dependent on how the webapplication is started. You don't want to be dependent on that as well since you have no control over it at external hosting:
InputStream input = classLoader.getResourceAsStream("filename.extension");
This is finally more robust than your initial getSystemResourceAsStream() approach and the Class#getResourceAsStream() as suggested by others.
The SystemClassLoader loads resources from java.class.path witch maps to the system variable CLASSPATH. In your local application, you probably have the resource your trying to load configured in java.class.path variable. In the server, it's another story because most probably the server loads your resources from another class loader.
Try using the ClassLoader that loaded class using the correct path:
getClass().getResourceAsStream(fileName);
This article might also be useful.
Try using getResourceAsStream() instead of getSystemResourceAsStream().