Is there any way where I can find out which Java options are being used by a Java application inside JVM (e.g. the ones passed through the command line)?
I want to find it on the fly when the application is running, but from outside outside the application.
Map<String, String> env = System.getenv();
for (String envName : env.keySet()) {
System.out.format("%s=%s%n", envName, env.get(envName));
}
You can use JMX:
final JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(connectorAddress));
final MBeanServerConnection mBeanServerConnection = connector.getMBeanServerConnection();
final ObjectName commandLine = new ObjectName("java.lang:type=Runtime");
final List<Attribute> arguments = mBeanServerConnection.getAttributes(commandLine, new String[] {"InputArguments"}).asList();
String[] inputArguments = (String[]) arguments.get(0).getValue();
System.out.println(Arrays.asList(inputArguments));
connector.close();
To get the JMX connection, you might find my answer to " JMX client accessible locally only " helpful.
Use JMX. It allows to get options, properties, etc. from inside as well as outside of application.
I believe that Java does not support the "INTERCEPTING" of method arguments, method args are not logged or intercepted in the JVM .
However, you can easily print out the args sent to a running class using the System.env example in the other answer on this thread .
HOWEVER, if you have control over the actual applications source code, you can follow the directions here : How do I intercept a method invocation with standard java features (no AspectJ etc)?
This will allow you to log and intercept the args being sent to the main method.
Related
I have Wildfly server in Eclipse Mars. In Open launch configuration, in section Program argumenst I have added Dmyarg.dir="Value".
Then, in application, I have code:
RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
for(String arg : mxBean.getInputArguments()){
if(arg.startsWith("Dmyarg.dir")){
String [] filePath = arg.split("=");
break;
}
}
After running my server from Eclipse, there is no argument passed.
Afrer running my server by myself, my application can find this argument.
How can I add argument in Eclipse that would be displayed there?
According to RuntimeMXBean#getInputArguments documentation it
Returns the input arguments passed to the Java virtual machine which
does not include the arguments to the main method.
So if you want to see your argument in result of getInputArguments call you need to pass it to JVM. i.e. in Eclipse IDE you should type -Dmyarg.dir="Value" in "VM arguments" filed of launch configuration. And process it as
RuntimeMXBean mxBean = ManagementFactory.getRuntimeMXBean();
for(String arg : mxBean.getInputArguments()){
if(arg.startsWith("-Dmyarg.dir")){
String [] filePath = arg.split("=");
break;
}
}
Also note that you can use next code to get value of VM argument passed as -Dmyarg.dir="Value"
System.getProperty("myarg.dir")
See documentation for java command:
-Dproperty=value
Sets a system property value.
If value is a string that contains spaces, then you must enclose the
string in double quotation marks:
java -Dmydir="some string" SomeClass
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 trying to debug an issue with the one of the java agents added to the production JVM.
In the start up script for the application java agent is properly added and has been working on other environments. But in production this agent does not seems to be working.
Is there any way to find list of java agents added ?
(This question is similar to Can a JVM retrieve a list of agents that have been loaded into it via the attach api?). For the sake of completeness, I will add this answer to both questions.)
Checking agents that have been added via command-line:
If you are interested in agents that were added to using the command line interface, you can check them by using the RuntimeMXBean.
This bean offers the method getInputArguments, which returns a list of all VM arguments.
You can iterate over the list and check it for the arguments agentpath, agentlib and javaagent, similar to the following code snippet:
RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
List<String> jvmArgs = runtimeMXBean.getInputArguments();
System.out.println("JVM arguments:");
for (String arg : jvmArgs) {
if (arg.startsWith("-agentpath") || arg.startsWith("-agentlib") || arg.startsWith("-javaagent")) {
System.out.print("***** ");
}
System.out.print(arg);
if (arg.startsWith("-agentpath") || arg.startsWith("-agentlib") || arg.startsWith("-javaagent")) {
System.out.println(" *****");
} else {
System.out.println();
}
}
Checking agents that have been added using the Attach API:
If you are interested in the agents that have been added to an application at run time using the Attach API, you can use the DiagnosticCommandMBean.
This bean offers a diagnostic command called vmDynlib, a parameterless method that returns a String that list all dynamically loaded libraries.
Here is a snippet that prints all dynamic libraries loaded by the application's VM:
ObjectName diagnosticsCommandName = new ObjectName("com.sun.management:type=DiagnosticCommand");
String operationName = "vmDynlibs";
String result = (String) ManagementFactory.getPlatformMBeanServer().invoke(diagnosticsCommandName, operationName, null, null);
System.out.println(result);
This results in an output similar to this one:
Dynamic libraries:
0x00007ff7b8600000 - 0x00007ff7b8637000 C:\Program Files\Java\jdk1.8.0_181\bin\java.exe
0x00007ffdfeb00000 - 0x00007ffdfecf0000 C:\WINDOWS\SYSTEM32\ntdll.dll
0x00007ffdfe300000 - 0x00007ffdfe3b2000 C:\WINDOWS\System32\KERNEL32.DLL
0x00007ffdfbb30000 - 0x00007ffdfbdd3000 C:\WINDOWS\System32\KERNELBASE.dll
0x00007ffdfe950000 - 0x00007ffdfe9f3000 C:\WINDOWS\System32\ADVAPI32.dll
...
You can then check this text if it contains a certain .so or .dll file.
The same inspection can be performed non-programatically.
For this, you can use the jconsole tool.
Connect to a VM, switch to the tab MBeans, select com.sun.management, select DiagnosticCommand, select Operations, select vmDynlibs, and invoke it.
In the image, you can see one of my test agents attached to the application.
The agent was attached using the Attach API, thus this agent would not be visible by checking the application's command line arguments (i.e., no -agentpath=... would be seen on the arguments) but is only visible as dynamically loaded library.
You could use an extra designated agent (attached using premain) to list down all the other agents.
This agent must be the first in the list to the java command for launching your production JVM.
Whenever any class gets added, check if it has an agentmain(String, Instrumentation) or premain(String, Instrumentation) signature method. If so, you have an agent match.
I'm working on a project that requires to show the CPU usage as well as other system information of remote machines.
People suggest using SIGAR to achieve this, but I don't know how to use it. The source code didn't quite make sense to me.
Basically, my question is that: how can I register the MBeans provided by SIGAR to the server when host IP and JMX port are provided, and how to fetch the system info from other computer afterward.
Please correct me if I'm wrong with how JMX works. Thanks in advance.
These are the names of the classes that are the Sigar built in MBeans that you can register:
org.hyperic.sigar.jmx.SigarCpu
org.hyperic.sigar.jmx.SigarCpuInfo
org.hyperic.sigar.jmx.SigarCpuPerc
org.hyperic.sigar.jmx.SigarLoadAverage
org.hyperic.sigar.jmx.SigarMem
org.hyperic.sigar.jmx.SigarProcess
org.hyperic.sigar.jmx.SigarRegistry
org.hyperic.sigar.jmx.SigarSwap
However, it will be quite complicated to deploy these remotely since Sigar depends on a native library which must be in the target JVM's lib-path when the MBeans are loaded. This means, you will need to actively load the library and MBeans on each target host you want to monitor.
You might be able to hack a way of making the target JVMs load this through a remote call, but it is non-trivial and will require you to bypass any security setups in the JVMs since by default, this is something you're not supposed to be able to do.
You can sort of hack the system part to get an easy deployment for Sigjar:
private String before;
private Sigar sigar;
/**
* Constructor - don't forget to call unload later!
*/
public SetlogSigar() throws Exception {
before = System.getProperty("java.library.path");
String path = "";
String add = getJarFolder();
if (before.contains(";"))
path = before + ";./;" + add;
else
path = before + ":./:" + add;
setSystemPath(path);
sigar = new Sigar();
}
/**
* This is needed to dynamically update the JAVA Path environment in order to load the needed native library
* Yes -rather an ugly hack...
*/
private String getJarFolder() {
// get name and path
String path = SetlogSigar.class.getProtectionDomain().getCodeSource().getLocation().getPath();
String decodedPath = path;
try {
decodedPath = URLDecoder.decode(path, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
File f = new File(decodedPath);
String absolutePath = f.getParentFile().getParentFile().getParentFile().getParent()+"/lib";
return absolutePath;
}
/**
* Unloads the JNI bindings
*/
public void unload() {
this.sigar.close();
setSystemPath(before);
}
This hack dynamically adds the folder where sigjar.jar is located to the environment variable. Just place all native libs in there and deployment gets less complicated.
Seems to me that you will have to write some wrapping objects to expose the various SIGAR outputs as JMX mbean attributes. How you do that that depends highly on what you are using to expose your JMX beans. I would write one wrapping object for each of the various different types of SIGAR output: memory, disk, ...
I've written a SimpleJMX library that might help. I'll use its format to provide an example object that you can use to expose the info via JMX. You can adapt it to whatever mechanism you are using to publish JMX means. I'm not familiar with SIGAR enough to know if my sigar code below is correct to get a ProcMem instance.
#JmxResource(description = "Show SIGAR Info", domainName = "foo")
public class SigarProcMem {
private ProcMem procMem;
{
// sorry, I'm not up on sigar so I'm not sure if this works
Sigar sigar = new Sigar();
procMem = sigar.getProcMem(sigar.getPid());
}
#JmxAttributeMethod(description = "Resident memory")
public long residentMemory() {
return procMem.getResident();
}
#JmxAttributeMethod(description = "Get the Total process virtual memory")
public long totalVirtualMemory() {
return procMem.getSize();
}
}
My Internet Explorer is set to have an automatic proxy file(so-called PAC) for web access. Is there a way to use this on my Java program, also ?
My below Java code does not seem to use proxy at all.
ArrayList<Proxy> ar = new ArrayList<Proxy>(ProxySelector.getDefault().select(new URI("http://service.myurlforproxy.com")));
for(Proxy p : ar){
System.out.println(p.toString()); //output is just DIRECT T.T it should be PROXY.
}
I also set my proxy script on Java Control Panel(Control->Java), but the same result.
and I found there's no way to set PAC file for Java programmatically.
People use http.proxyHost for System.setProperties(..) but this is a only for setting proxy host, not proxy script(PAC file).
Wow! I could load Proxy Auto-Config (PAC) file on Java. Please see below codes and package.
import com.sun.deploy.net.proxy.*;
.
.
BrowserProxyInfo b = new BrowserProxyInfo();
b.setType(ProxyType.AUTO);
b.setAutoConfigURL("http://yourhost/proxy.file.pac");
DummyAutoProxyHandler handler = new DummyAutoProxyHandler();
handler.init(b);
URL url = new URL("http://host_to_query");
ProxyInfo[] ps = handler.getProxyInfo(url);
for(ProxyInfo p : ps){
System.out.println(p.toString());
}
You already have a [com.sun.deploy.net.proxy] package on your machine!
Find [deploy.jar] ;D
Java does not have any built-in support for parsing the JS PAC file. You are on your own. What you can do is download that file and parse the proxy host from it. You should read this.
In my case, I've just figured-out what the .pac file will return, then hardcode.
Based on #Jaeh answer I used the code below.
Note that SunAutoProxyHandler implements AbstractAutoProxyHandler and there is an alternative concrete implementation called PluginAutoProxyHandler but that implementation does not appear to be as robust:
BrowserProxyInfo b = new BrowserProxyInfo();
b.setType(ProxyType.AUTO);
b.setAutoConfigURL("http://example.com/proxy.pac");
SunAutoProxyHandler handler = new SunAutoProxyHandler();
handler.init(b);
ProxyInfo[] ps = handler.getProxyInfo(new URL(url));
for(ProxyInfo p : ps){
System.out.println(p.toString());
}