Spring running external JUnit Jar causes java.lang.Exception: No runnable methods - java

I have project where a user creates and uploads a jar filled with JUnit tests. My project is running in Spring and for some reason it will not run. I can hard code some JUnit tests and they work fine but once I put the exact code in a .jar file the file won't run. This is my first question on Stack Overflow and I would appreciate any help.
Here is my code
simpletest.jar
import org.junit.Test;
public class simpletest {
#Test
public void sayHello() {
System.out.println("Simple test ran");
}}
runjar.java
URL[] classLoaderUrls = new URL[]{new URL("file:///c:/Work/simpletest.jar")};
URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls);
Class<?> appiumClass = null;
try {
appiumClass = urlClassLoader.loadClass("simpletest.simpletest");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Run the class using JUnitCore
JUnitCore junit = new JUnitCore();
Result result = junit.run(appiumClass);
//Display if test was successful
System.out.println(result.wasSuccessful());

When you are loading the URLClassLoader you need to supply the current ClassLoader for it to load the required JUnit libraries.
//Code to run junit test
URLClassLoader urlClassLoader = new URLClassLoader(classLoaderUrls, getClass().getClassLoader());

Related

Load an external library inside a jar

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()
);

TESTNG | call testng.xml file from main method from inside a runnable jar

I am new to testng + maven and I have requirement to run scripts using runnable jar. The suite needs to be executed for multiple regions . The region name is passed as an argument while executing jar.
Now my requirement is to switch between multiple testng.xml files based on the region name provided.
All the testng.xml files are placed inside resource folder --> "resources/testngA.xml" and "resources/testngB.xml".
When I run the script from eclipse it works fine but when i try to execute the same through runnable.jar it shows java.io.FileNotFoundException exception . Can anyone please help me to fix this.
public class TestRunner {
static TestNG testng;
static List<String> suites;
public static void main(String[] args) {
String xmlFileName = "";
List<XmlSuite> suite;
String region = arg[0];
try
{
if(region.equals("A")){
xmlFileName = "resources/A_TestNG.xml";
}else if(region.equals("B")){
xmlFileName = "resources/B_TestNG.xml";
}else{
System.out.println("No matching region found")
}
suite = (List <XmlSuite>)(new Parser(xmlFileName).parse());
testng.setXmlSuites(suite);
testng.run();
}
catch (ParserConfigurationException e)
{
e.printStackTrace();
}
catch (SAXException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
When you deal with resources you should use the following approach:
1) Take resource URI
URI aTestNgURI = TestRunner.class.getClassLoader().getResource("A_TestNG.xml").toURI();
2) Obtain path to a file
String aTestNgFilePath = new File(aTestNgURI).getAbsolutePath();

Unable to run downloaded jar from other jar

I currently have the following problem:
I have created a updater jar from which a client jar is downloaded and placed in some directory (just somewhere on the disk, not associated with the directory of the updater jar). I use the following code the run the client jar from the updater:
private void startApplication() {
String url = getFilePath()+"client.jar";
URL parsedURL = null;
try {
parsedURL = new File(url).toURI().toURL();
} catch (MalformedURLException e) {
e.printStackTrace();
}
ClassLoader loader = URLClassLoader.newInstance(new URL[]{parsedURL}, getClass().getClassLoader());
Class<?> cl = null;
try {
cl = Class.forName("org.myApp.client.mainPackage.Main", true, loader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally {
loader = null;
}
Class<? extends Application> runClass = cl.asSubclass(Application.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Application> ctor = null;
try {
ctor = runClass.getConstructor();
} catch (NoSuchMethodException | SecurityException e) {
e.printStackTrace();
}
Application doRun = null;
try {
doRun = ctor.newInstance();
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
try {
doRun.start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
This code seems to work, because the Main of the client.jar gets runned. However, after its Main is started, I get an exception from the client jar. The Main from the client jar tries to load a FXML file in the upper pane. This is the exception:
ClassNotFoundException: org.myApp.client.lockscreen.LockscreenController when loading a FXML file
I do not know what triggers this error. The client jar just runs as should be, when I run it standalone.
Do I need to load all classes from the client jar from the updater jar?
Any help is greatly appreciated!
Everybody thanks for your help. I was able to fix it like this (thanks Jool, you will get all the credits):
I downloaded and runned the client jar, assuming it would have its own references. However, as Jool said, I had to add the director to the class path. What I did wrong, was that I added the directory, and not the Jar file. You have to add the JAR file too ! I did that with this code:
public void addPath(String s) throws Exception {
File f = new File(s);
URI u = f.toURI();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(urlClassLoader, new Object[]{u.toURL()});
}
And then I just called addPath(url) before running the client jar.
It is saying that the class cannot be found because it is not on your classpath.
This depends on how you build your application (Ant, Maven etc), since this determines how the location of the .jar file is known, and where the .jar file is.
If you are using an IDE, there would usually be some sort of Libraries placeholder in which you define .jars that you are dependent upon.

HtmlUnit - Class not found in src folder but run in test folder

I am programming in Java using Maven/Eclipse.
I am able to run HtmlUnit fine when running from a Unit Test. (test/)
However when I try to use the same code in the src/ folder, I receive java.lang.NoClassDefFoundError messages. The only way I have been able to resolve them is to go and manually add all the jars to the build path. But this doesn't make sense to me as the jar file shows up in my Maven Dependencies.
pom.xml (there are more dependencies in the actual pom file)
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<version>2.9</version>
<scope>test</scope>
</dependency>
The Sample block of HtmlUnit code
WebClient webClient = new WebClient();
HtmlPage page = webClient.getPage(url);
System.out.println("Executing Pages JavaScript for " + config.executeJavaScriptTime + " seconds..."); webClient.waitForBackgroundJavaScript(config.executeJavaScriptTime);
dom = cleaner.clean(page.asXml());
html = cleaner.getInnerHtml(dom);
webClient.closeAllWindows();
Any Ideas? Thanks.
The test scope which is applied to your dependency means that it is not available on the compile classpath. This way, the code you ship does not depend on test code. A more complete explanation can be found here. If the project you are building is intended to be only tests, you should remove the scope tag to let it take the default scope, compile. But in general it is correct that your shipping code, built from src, shouldn't depend on JUnit, a testing library.
As of now the only working hack I have found is to create a method annotated as #Test and run the crawler as a JUnit Test.
i.e.
public class Crawler() {
#Test
public void runAsTest() {
Crawler crawler = new Crawler();
String[] urls = new String[]{
"http://www.url.com/1",
"http://www.url.com/2",
"http://www.url.com/3"
};
crawler.crawl(urls);
try {
Thread.sleep(1000 * 60 * 15); // without this the unit test exits too early
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// the rest of the class definition
}
I would like to be able to run this from a standard main method, i.e.
public class Crawler() {
public static void main(String[] args) {
Crawler crawler = new Crawler();
String[] urls = new String[]{
"http://www.url.com/1",
"http://www.url.com/2",
"http://www.url.com/3"
};
crawler.crawl(urls);
try {
Thread.sleep(1000 * 60 * 15); // without this the unit test exits too early
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// the rest of the class definition
}

Loading jars at runtime

I am trying to add jar file to classpath at runtime. I use this code
public static void addURL(URL u) throws IOException {
URLClassLoader sysloader = (URLClassLoader) ClassLoader
.getSystemClassLoader();
Class<URLClassLoader> sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL", parameters);
method.setAccessible(true);
method.invoke(sysloader, new Object[] { u });
System.out.println(u);
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error");
}
}
System out prints this url:
file:/B:/Java/Tools/mysql-connector-java-5.1.18/mysql-connector-java-5.1.18/mysql-connector-java-5.1.18-bin.jar
I was check this path carefully, this jar exist. Even this test show that com.mysql.jdbc.
Driver class exists.
javap -classpath "B:\Java\Tools\mysql-connector-java-5.1.18\
mysql-connector-java-5.1.18\mysql-connector-java-5.1.18-bin.jar" com.mysql.jdbc.
Driver
Compiled from "Driver.java"
public class com.mysql.jdbc.Driver extends com.mysql.jdbc.NonRegisteringDriver i
mplements java.sql.Driver{
public com.mysql.jdbc.Driver() throws java.sql.SQLException;
static {};
}
But I still get java.lang.ClassNotFoundException when I use this Class.forName(driver).
What is wrong with this code?
The URL is ok, nevertheless you try to load a jar from classpath, so it means that yo need to have the file in cp first.
In your case you want to load a jar that is not in classpath so you have to use
URLClassLoader and for JAR you can use also the JARClassLoader
If you want some sample lesson on it:
http://docs.oracle.com/javase/tutorial/deployment/jar/jarclassloader.html
Here a sample I ran by myself see if helps you. It search the Logger class of Log4j that is not in my classpath, of course i got exception on invocation of the constructor since i did not pass the right params to the constructor
package org.stackoverflow;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
public class URLClassLoaderSample
{
public static void main(String[] args) throws Exception
{
File f = new File("C:\\_programs\\apache\\log4j\\v1.1.16\\log4j-1.2.16.jar");
URLClassLoader urlCl = new URLClassLoader(new URL[] { f.toURL()},System.class.getClassLoader());
Class log4jClass = urlCl.loadClass("org.apache.log4j.Logger");
log4jClass.newInstance();
}
}
Exception in thread "main" java.lang.InstantiationException: org.apache.log4j.Logger
at java.lang.Class.newInstance0(Class.java:357)
at java.lang.Class.newInstance(Class.java:325)
at org.stackoverflow.URLClassLoaderSample.main(URLClassLoaderSample.java:19)
Exception due to the wrong invocation, nevertheless at this stage we already found the class
Ok try the alternative approach with DataSource and not directly the Driver
Below is the code (working with oracle driver, i don't have my sql db, but the properties are the same)
Generally using the DataSource interface is the preferred approach since JDBC 2.0
The DataSource jar was not in the classpath neither for the test below
public static void urlCLSample2() throws Exception
{
File f = new File("C:\\_programs\\jdbc_drivers\\oracle\\v11.2\\ojdbc6.jar");
URLClassLoader urlCl = new URLClassLoader(new URL[] { f.toURL() }, System.class.getClassLoader());
// replace the data source class with MySQL data source class.
Class dsClass = urlCl.loadClass("oracle.jdbc.pool.OracleDataSource");
DataSource ds = (DataSource) dsClass.newInstance();
invokeProperty(dsClass, ds, "setServerName", String.class, "<put your server here>");
invokeProperty(dsClass, ds, "setDatabaseName", String.class, "<put your db instance here>");
invokeProperty(dsClass, ds, "setPortNumber", int.class, <put your port here>);
invokeProperty(dsClass, ds, "setDriverType",String.class, "thin");
ds.getConnection("<put your username here>", "<put your username password here>");
System.out.println("Got Connection");
}
// Helper method to invoke properties
private static void invokeProperty(Class dsClass, DataSource ds, String propertyName, Class paramClass,
Object paramValue) throws Exception
{
try
{
Method method = dsClass.getDeclaredMethod(propertyName, paramClass);
method.setAccessible(true);
method.invoke(ds, paramValue);
}
catch (Exception e)
{
throw new Exception("Failed to invoke method");
}
}

Categories

Resources