I have an app that will rely on a 3rd party JAR file to execute some code, this JAR has been developed by our client, we need to include a call to the JAR in our app and they have the hope that we can send the current SQL Connection as a parameter for the JAR
Having no previous experience working with this kind of scenario we all agreed it should be pretty straight-forward, but we overlooked the fact that the main class's main method only receives an array of Strings as parameter
I've googled my head off, but can't find a similar need, is it just completely off the books and impossible to do?
The call I was hoping to implement would look something like this:
final Process command = re.exec("java -jar ./MyClientsJar.jar " + arg1 + " " + arg2 + " " + SQLConnection);
command.waitFor();
But of course, when we try to define a main method like this in the client's source code:
private void MyClientsJARMainClass(String args[], Connection con){}
Eclipse's JAR export tool fails to find the main class
Any ideas other than sending user, url and pass arguments?
You are creating a new process when you call re.exec. Since its a new process you cannot share your connection defined in another process.
Try to use the jar as a library. Import the class and make them a public method that accepts the connection as a parameter. For example:
import MyClientsJARMainClass
...
String[] args = { arg1, arg2 };
MyClientsJARMainClass.mainWithConnection(args, connection);
Your client adds a method like this in their class:
public static void mainWithConnection(String[] args, Connection connection)
Related
Language: Java
Program: Connecting to a database
Question: I'm trying to connect the sqlite database by following TutorialsPoint tutorial but I keep getting the main class not found error.
Implementation: My code is below followed by my terminal commands and folder structure screenshot. But basically all my files are located in one folder including the sqlite jar file.
import java.sql.*;
public class Test {
public static void main(String[] args) {
Connection c = null;
try{
Class.forName("com.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:test.db");
} catch(Exception e) {
System.err.println(e.getClass().getName() + ": " + e.getMessage());
System.exit(0);
}
System.out.println("Opened database successfully!");
}
}
Terminal Commands
javac Test.java
java -classpath ".;sqlite-jdbc-3.23.1.jar" Test
Your problem was that you're explicitly trying to load the class com.sqlite.JDBC, whereas the driver class name must've changed somewhere along the way.
JDBC Type 4 drivers have added cleverness which allows you to specify only the connection URL, and the driver loads itself based on the beginning (i.e. jdbc:sqlite). No need to wonder what was the driver class's name.
Rant unrelated to the issue at hand:
Unfortunately people read old tutorials written by less than experts, so we constantly see Class.forName() being used, as well as the more serious issue, which is using Statement instead of PreparedStatement.
My classpath option was incorrect. I was on linux and was trying to do:
java -classpath ".;sqlite-jdbc-3.23.1.jar" Test
the correct way was
java -classpath ".:sqlite-jdbc-3.23.1.jar" Test
colon not semicolon. Unfortunately now it's giving me and error" ClassNotFoundException: com.sqlite.JDBC;
I will look into this.
Thanks for the comments which helped me find the error
Actually i want to developpe a java application witch should instrumentate another java application witch i don't have its source code..
I tried to create an agent and attach it to the jvm.. then i created an mbean and tried to connect to it.. it works when i try to instrumentate a class in my project.. but i don't know how could i instrument a distant application with my application..
here is my code: https://github.com/ammouna8ammouna/Monitoring.git
i am really new at the instrumentation world and i really need help.
If you can get the processID of the VM that you are targeting you can attach your agent using
com.sun.tools.attach.VirtualMachine
For example if you have pid, the path of your JAR target and the loader that handle the JAR you can try something like this:
private static void attach(String pid, String jarPath,
ClassLoader toolLoader) throws Exception {
Class<?> attacherLib = toolLoader.loadClass("com.sun.tools.attach.VirtualMachine");
Class<?> string = toolLoader.loadClass("java.lang.String");
Method attach = attacherLib.getMethod("attach", string);
Object instance = attach.invoke(null, pid);
Method loadAgent = attacherLib.getMethod("loadAgent", string, string);
loadAgent.invoke(instance, jarFilePath, "");
Method detach = attacherLib.getMethod("detach");
detach.invoke(instance);
}
Let me know if it's clear or you need other help.
I am wondering if there's a way to create a jar that includes some command line arguments in it, the arguments that are usually passed in the command line when one tries to start up the jar (these parameters are then passed on to the main function). Basically instead of starting my app with
java -jar myapp.jar "arg1" "arg2", I want to start my app with
java -jar myapp.jar
and have "arg1" and "arg2" passed to the main function.
The reason behind this is that I want to deploy this to different environments, and I want my jar to contain different parameters according to the environment it's being deployed at.
Maybe there's another way to achieve similar results ??
Cheers.
PS: Looking for a maven solution.
Edit: I'll add a complete example to make this a bit more clear:
Let's say I have 2 environments: "Production" and "Test". I want to run the jar in the same way no matter in what environment I deploy it. So I always want to run it with:
java -jar myapp.jar
But! In order for my 2 environments to run ok, I need the Production environment jar to start it's main method with an argument "prod" and I need the Test environment jar to start it's main method with an argument "test".
If I correctly understood your problem, in your main() you could define a simple logic to handle the case where you do not specify any input parameter; the logic could retrieve the desired values according to the correct platform/env.
As an example:
public class Test01
{
public static void main(String... aaa)
{
// Check input
if(aaa.length == 0) {
/* Insert logic to retrieve the value you want, depending on the platform/environment.
* A trivial example could be: */
aaa = new String[2];
aaa[0] = "First value";
aaa[1] = "Second value";
}
// Processing, e.g. print the 2 input values
System.out.println(aaa[0] + ", " + aaa[1]);
}
}
Fyi, I created a runnable jar using eclipse, and start the application by either
java -jar Test01.jar
or
java -jar Test01.jar arg1 arg2
Hope this helps!
One solution is to change main(String[] args) to get values from env var if they are not present in the passed arguments.
String user;
String password;
if(args.length < 2)
{
user = System.getenv("appUser");
password = System.getenv("appPassword");
} else {
user = args[0];
password = args[1];
}
You can also create another class with a main function that will call the real one.
public class CallerMyApp{
public void main(String[] args) {
String[] realArgs = {System.getenv("appUser"), System.getenv("appPassword")};
MyApp.main(realArgs);
}
}
Then to execute its something like
java -cp myapp.jar CallerMyApp
I want to create a GUI app in java for signing j2me app which is done by JadTool.jar but it is a Command Line Interface Apps. So I just want to use it as library and pass the parameters in program. How it can be done?
Check out Runtime. It will allow you to execute a command. You can use this to start your command line interface library.
Edit:
Ah, I didn't read care carefully earlier. If you're using a Java library starting a separate process is not the best solution.
Just reference the JadTool jar from your project. If the functionality you need isn't accessible in the library, edit the source and recompile. Make sure JadTool's license allows this.
If you're against editing the source (or not allowed) try using reflection to invoke the private run method you need.
A jar is just a library of classes, the fact that it can be run from the command line is caused by the presence of a main method in a class. As jadtool's source is available it's easy to see its very simple main:
public static void main(String[] args) {
int exitStatus = -1;
try {
new JadTool().run(args);
exitStatus = 0;
} catch (Exception e) {
System.err.println("\n" + e.getMessage() + "\n");
}
System.exit(exitStatus);
}
Unfortunately, that run() method is private, so calling it directly from another class won't work, leading to a reduced set of options:
#WilliamMorrison 's solution of going via Runtime - not really a library call, but it would work.
see Any way to Invoke a private method?
I would like to spawn a subprocess Java Virtual Machine (through a Java API call) and communicate with it.
Maybe I'm just not searching for the right thing on Google, but I keep getting pointed back to Runtime.exec(), which is not what I want. I know I could connect standard input/output and use various serialization techniques, or use remote method invocation, but these seem to cumbersome/heavyweight. It seems like an API call could be more robust than attempting to call Runtime.exec("/usr/bin/java -cp [system.getclasspath] ...").
The purpose / motivation of this is that dynamically reloading classes with many dependencies seems somewhat tricky (several sites mention reading a .class file and passing it to [ClassLoader].defineClass, but this doesn't handle loading of dependent class files well). If I don't need much communication bandwidth or low latency, I think a new JVM instance would work fine; it's that communicating with it seems cumbersome. In any case this question isn't high priority; I'll probably go the straightforward route and see how I can load dependent class files (e.g. ones for inner classes).
As long as the classes you want to load are in a JAR file or a directory tree separate from your main app, using an URLClassLoader and running them in a separate Thread works fine. AFAIK all Java app servers work like this, so it's definitely a proven technique.
I would definitely recommend taking a look at the class loader mechanism used by the Tomcat servlet container - they seem to have exactly the same class loading problem and seem to have solved it very well.
If you go the communication route you should consider Java RMI.
ClassRunner (shown in the listing below) uses ProcessBuilder to run the main function of any available class in your classpath using the same class path and library path as the current JVM. It will even clone the environment and the working directory of the current JVM.
Caveat: ClassRunner assumes that java is on the PATH of the current JVM. You may want to put some logic around locating java or java.exe based on System.getProperty("java.home") :)
Listing of ClassRunner.java:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ClassRunner
{
private final Class<?> classToRun;
public ClassRunner(Class<?> classToRun)
{
this.classToRun = classToRun;
}
public void run(String... args) throws Exception
{
String javaCommand = "java";
String libraryPath = "-Djava.library.path=\"" + System.getProperty("java.library.path") + "\"";
String classpath = "\"" + System.getProperty("java.class.path") + "\"";
ProcessBuilder processBuilder = new ProcessBuilder(javaCommand,
libraryPath,
"-classpath", classpath,
classToRun.getCanonicalName());
processBuilder.redirectErrorStream();
for (String arg : args) processBuilder.command().add(arg);
Process process = processBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) System.out.println(line);
reader.close();
process.waitFor();
}
public static void main(String[] args) throws Exception
{
new ClassRunner(Main.class).run("Hello");
}
}
Listing of Main.java:
public class Main
{
public static void main(String... args)
{
System.out.println("testing Main");
for (String arg : args) System.out.println(arg);
}
}