Java: How to determine startup arguments without the main line access? - java

I'm curious to know if it is possible to determine the startup arguments of a Java application when you don't have access to the arguments array from the main line.
public static void main(String[] args)
{
// Invokes my code without providing arguments.
mycode();
}
public static void mycode()
{
// Attempting to determine arguments here.
// TODO GetArgs()
}
I'm in this situation by doing some plugin work and the core application does not provide a list of startup arguments. Any thoughts?

Not unless the plugin is given them, but that kind of goes without saying (at least in Java).
If you need to set some options specifically for your plugin, but can't access the command line, there are at least two options:
Options file (several mechanisms)
Set system params on the command line
For the second option, just use the normal -D option and namespace the param name, like:
java -Dcom.davelnewton.plugins.foo.bar=anOptionValue etc...
Retrieve them via System.getProperty(anOptionName) or one of its cousins.
IMO an options file is a better (ahem) option, even if you're specifying the file path on the command line, but if you only have an option or two, maybe not--just depends.

Pass the arguments to the function?
public static void main(String[] args)
{
mycode(args);
}
public static void mycode(String[] args)
{
String arg1 = args[0];
}

You could do:
public static void main(String[] args)
{
System.setProperty("cmdArgs", args);
mycode();
}
public static void mycode()
{
// Attempting to determine arguments here.
String[] args = System.getProperty("cmdArgs");
}

Related

Second main() class does not see variables initialized in first main() class

I'm developing an application that requires two main() classes, first one for the actual application, and a second one for the JMX connectivity and management. The issue I'm facing is even after ensuring the first main() is executed and initializes the variables, when the second main() runs but does not see those variables and throws null exception.
Application main():
public class GatewayCore {
private static Logger logger = Logger.getLogger(GatewayCore.class);
private static ThreadedSocketInitiator threadedSocketInitiator;**
private static boolean keepAlive = true;
//private static Thread mqConnectionManager;
public static void main(String args[]) throws Exception {
__init_system();
__init_jmx();
__init_mq();
while(keepAlive) {}
}
private static void __init_system() {
try {
logger.debug("__init_system:: loading configuration file 'sessionSettings.txt'");
SessionSettings sessionSettings = new SessionSettings(new FileInputStream("sessionSettings.txt"));
logger.info("\n" + sessionSettings);
MessageStoreFactory messageStoreFactory = new FileStoreFactory(sessionSettings);
LogFactory logFactory = new FileLogFactory(sessionSettings);
MessageFactory messageFactory = new DefaultMessageFactory();
Application sessionManager = new SessionManager();
threadedSocketInitiator = new ThreadedSocketInitiator(sessionManager, messageStoreFactory, sessionSettings, logFactory, messageFactory);
...
public static ThreadedSocketInitiator getThreadedSocketInitiator() {
return threadedSocketInitiator; }
Secondary main() class, meant to be invoked for JMX-Mbean purpose:
public class RemoteCommandLine {
private static Logger logger = Logger.getLogger(RemoteCommandLine.class);
private static final String JMX_SERVICE_URL_PREFIX = "service:jmx:rmi:///jndi/rmi://";
private static final String HOST = "localhost";
private static String PORT = "24365";
private static JMXConnectionInstance jmxConnectionInstance;
private static boolean keepAlive = true;
public static void main(String[] args) throws IOException, MalformedObjectNameException, ConfigError {
logger.debug(GatewayCore.getThreadedSocketInitiator());
...
From command line, I first run:
java -classpath etdfix.jar:slf4j-api-1.7.25.jar:mina-core-2.0.16.jar:quickfixj-all-2.0.0.jar -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=24365 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false com.scb.etdfix.GatewayCore sessionSettings.txt
Wait for the inits to complete, ensuring threadedSocketInitiator has been assigned, then:
java -classpath etdfix.jar:slf4j-api-1.7.25.jar:mina-core-2.0.16.jar:quickfixj-all-2.0.0.jar com.scb.etdfix.JMX.RemoteCommandLine
Which ultimately throws a null pointer exception for the line:
logger.debug(GatewayCore.getThreadedSocketInitiator());
My plan is to have the first main() initialize the object, then pass to the second main() to do further method calls using the same object (it must be the same instance) when it is manually invoked. Both classes are compiled together into the same JAR. Please advise on how I can get around this issue or anything I can do to debug this further.
In fact, I'm thinking that this may not be possible as when the 2nd main() is invoked, from its POV the first main() isn't initialized. Therefore I should approach this by considering that they are two separate entities.
Each process (each java command) is completely separate, whether they run the same main() or not. This is a feature—the alternative would be to have unrelated parts of the system collide whenever they used a common utility.
That said, nothing stops you from calling GatewayCore.main() yourself (with the real command line or whatever other argument list) if you want to reuse its logic. It might be a good idea, though, to factor out the common code as another function: main() has many special responsibilities and programmers do not usually expect it to be called within a program.

How to set/get a value from another JVM

I have a class Normal with the following code:
public class Normal {
private static String myStr = "Not working...";
private static boolean running = true;
public static void main(String[] args) {
while(running) {
System.out.println(myStr);
}
}
}
And I have another class named Injector in another project. Its purpose is to change the values of Normal even though they are not in the same JVM:
public class Injector {
public static void main(String[] args) {
String PID = //Gets PID, which works fine
VirtualMachine vm = VirtualMachine.attach(PID);
/*
Set/Get field values for classes in vm?
*/
}
}
What I want to do is change the values myStr and running in the class Normal to "Working!" and false respectively without changing the code in Normal (Only in Injector).
Thanks in advance
You'll need two JARs:
One is Java Agent that uses Reflection to change the field value. Java Agent's main class should have agentmain entry point.
public static void agentmain(String args, Instrumentation instr) throws Exception {
Class normalClass = Class.forName("Normal");
Field myStrField = normalClass.getDeclaredField("myStr");
myStrField.setAccessible(true);
myStrField.set(null, "Working!");
}
You'll have to add MANIFEST.MF with Agent-Class attribute and pack the agent into a jar file.
The second one is a utility that uses Dynamic Attach to inject the agent jar into the running VM. Let pid be the target Java process ID.
import com.sun.tools.attach.VirtualMachine;
...
VirtualMachine vm = VirtualMachine.attach(pid);
try {
vm.loadAgent(agentJarPath, "");
} finally {
vm.detach();
}
A bit more details in the article.

Pass multiple specific files as one string arg from another java program

I am trying to pass few specific paths as a string arg in one program to call another java program. I get an ArrayIndexOutOfBoundsException error but when I pass the exact same arg in the command line it works perfectly
What is doable in command line:
program1 /filepath/{A,B,C}/*.zip
What I want to do that is giving me an error:
program2 [call program 1]:
status = ToolRunner.run(conf, program1, new String[]{"/filepath/{A,B,C}/*.zip"});
I am not sure how I can pass it that way, even if I want to pass all the paths and change the second program I am not sure how can I aggregate all the paths and assign it as input stream.
I appreciate your help,
Thank you :)
ToolRunner.run() method receives as arguments:
A Configuration object
A Tool object
A String array of arguments to be passed to the Tool object
Is your "program1" an object extending the Tool interface? Attending to the name you used, it seems it is more a refence to some "binary" or executable thing...
Please, have a look to the documentation.
ToolRunner is typically used in Hadoop applications as:
public final class MyMRClass extends Configured implements Tool {
...
public static void main(String[] args) throws Exception {
int res = ToolRunner.run(new Configuration(), new MyMRClass(), args);
System.exit(res);
} // main
#Override
public int run(String[] args) throws Exception {
Configuration conf = this.getConf();
Job job = Job.getInstance(conf, "some job name");
...
}
}

Java Chromium Embedded Framework (JCEF) usage of Chromium command lines

I try to set the --ignore-gpu-blacklist argument to JCEF but I can't find a way. The method I should be using is this one: CefApp::onBeforeCommandLineProcessing(String, CefCommandLine). But I can't find an example or good instructions of how to do it. CefCommandLine is an interface and I cannot find any implementation.
All the instructions I found are related to CEF, not JCEF and apparently there are classes that are different. Can anyone post a small example of how to pass Chromium arguments to CEF from a String str = "--ignore-gpu-blacklist"; ?
You have several possibilities to pass over arguments from JCEF to CEF/chromium.
1) The easiest way:
public static void main(String [] args) {
[...]
ArrayList<String> mySwitches = new ArrayList<>();
mySwitches.add("--persist-session-cookies=true");
CefApp app = CefApp.getInstance(mySwitches.toArray(new String[mySwitches.size()]));
CefClient client = app.createClient();
CefBrowser browser = client.createBrowser("http://www.google.com", false, false);
[...]
}
Just create a string array, containing all your switches you want to pass over and assign that array to CefApp.getInstance(..) at the very first call of that static method.
If you have only some simple settings, you can use the class CefSettings as well and pass over the object to getInstance(). Beside that, you can combine both (there are four different "getInstance()" methods).
2) Create your own CefAppHandler implementation to do some advanced stuff.
(a) Create an own AppHandler:
public class MyAppHandler extends CefAppHandlerAdapter {
public MyAppHandler(String [] args) {
super(args);
}
#Override
public void onBeforeCommandLineProcessing(String process_type, CefCommandLine command_line) {
super.onBeforeCommandLineProcessing(process_type, command_line);
if (process_type.isEmpty()) {
command_line.appendSwitchWithValue("persist-session-cookies","true");
}
}
}
(b) Pass AppHandler over to CefApp
public static void main(String [] args) {
[...]
MyAppHandler appHandler = new MyAppHandler(args);
CefApp.addAppHandler(appHandler);
CefApp app = CefApp.getInstance(args);
CefClient client = app.createClient();
CefBrowser browser = client.createBrowser("http://www.google.com", false, false);
[...]
}
Using this approach you'll do two things:
(a) you pass over the program arguments (args) to CefApp and
(b) you take the advantage of having the opportunity to manipulate the complete process of parsing the arguments within onBeforeCommandLineProcessing.
If you open the example code of JCEF detailed main frame, you will find this approach implemented in:
- tests.detailed.MainFrame.MainFrame(boolean, String, String[])
So implementing the onBeforeCommandLineProcessing is equal to CEF but written in Java instead of C/C++.
Regards,
Kai

passing values into another java project via runtime.exec()

Here is my code to invoke a java project from another java project
package pkgtry;
import java.io.*;
public class Try
{
private static void runProcess(String command) throws Exception
{
Process pro = Runtime.getRuntime().exec(command);
pro.waitFor();
InputStream inputStream = pro.getInputStream();
int b = -1;
while ( (b = inputStream.read()) != -1 )
{
System.out.write(b);
}
}
public static void main(String[] args)
{
int x=10;
try
{
runProcess("javac -d . C:\\Users\\owner\\Documents\\NetBeansProjects\\input\\src\\input\\Input.java");
runProcess("java input.Input");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
This code is working perfectly. What i want is to pass an variable say 'x' from Try.java to Input.java
i would like to know the what all changes are to be made in Try.java to send the parameter and in Input.java to receive the parameter. Thanks in advance
You need to append it to command and in Input.java in main method you will have this value stored in args parameter.
Everything you want to pass to the called program is added to its commandline, like this
runProcess("java input.Input All Parameters You Want To Pass");
In Input.java you can retrieve these parameters by reading out String[] args, like this:
public class Input {
public static void main (String[] args) {
for (String s: args) {
System.out.println(s);
}
}
}
will produce
All
Parameters
You
Want
To
Pass
But you should know that this is a rather heavy-handed way to make one piece of Java code call some other piece of Java code, you can add the jar produced by the project containing Input to the Try project and instantiate the Input class directly. Calling via the command line is slow and cumbersome, and severely limits the communication between the two classes (command line parameters in one direction, an integer return value in the other direction.

Categories