Java - getOpenFileDescriptorCount for Windows - java

How to get the number of open File descriptors under Windows?
On unix there is this:
UnixOperatingSystemMXBean.getOpenFileDescriptorCount()
But there doesn't seem to be an equivalent for windows?

This was going to be a comment but got a little long winded.
Conflicting answers as to why there may be a lack of equivalence here on ServerFault: Windows Server 2008 R2 max open files limit. TLDR: Windows is only limited by available hardware vs Windows is limited by 32 vs 64 bit implementation (MS Technet Blog Post - Pushing the Limits of Windows: Handles). Granted, this is old information.
But! if you note the JavaDocs for the com.sun.management package, you will of course note the conspicuous absence of a Windows version of the the UnixOperatingSystemMXBean that would extend OperatingSystemMXBean to provide the functionality. Even UnixOperatingSystemMXBean only exists to provide getMaxFileDescriptorCount() and getOpenFileDescriptorCount() so it seems unlikely that Windows has the same concept.
Edit:
I did find a nice little program that sort of shows this off, which I tweaked.
Descriptors.java
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Method;
class Descriptors {
public static void main(String [ ] args) {
System.out.println(osMxBean.getClass().getName());
OperatingSystemMXBean osMxBean = ManagementFactory.getOperatingSystemMXBean();
try {
Method getMaxFileDescriptorCountField = osMxBean.getClass().getDeclaredMethod("getMaxFileDescriptorCount");
Method getOpenFileDescriptorCountField = osMxBean.getClass().getDeclaredMethod("getOpenFileDescriptorCount");
getMaxFileDescriptorCountField.setAccessible(true);
getOpenFileDescriptorCountField.setAccessible(true);
System.out.println(getOpenFileDescriptorCountField.invoke(osMxBean) + "/" + getMaxFileDescriptorCountField.invoke(osMxBean));
} catch (Exception e) {
e.printStackTrace();
}
}
}
On Linux:
com.sun.management.UnixOperatingSystem
11/2048
On Windows:
sun.management.OperatingSystemImpl
java.lang.NoSuchMethodException:
sun.management.OperatingSystemImpl.getMaxFileDescriptorCount()
at java.lang.Class.getDeclaredMethod(Unknown Source)
at Descriptors.main(Descriptors.java:10)

Related

How to get the full stacktrace of a StackOverflowError without restarting the application

I currently hava a running Java application, which has a bug. I don't know how to fully reproduce it and it didn't happen for weeks until now. When it occurs one times, I can reproduce it over and over again easily until I restart the application. The bug causes a StackOverflowError because of a recursion and I don't know how this happens. The printed stacktrace of the StackOverflowError isn't helpful because it contains only the repeating part, but not the more insteresting initial part, because the JVM has a limit for stacktrace entries. The -XX:MaxJavaStackTraceDepth=... can be used to set this limit as explained here. The problem is that I think I have to restart my application in order to add this flag. But if I do so, I won't be able to reproduce the bug anymore. Is there any solution how I can get the full stacktrace or set this flag without restarting the application?
I know at least two solutions.
Create HotSpot Serviceability Agent tool to find the address of MaxJavaStackTraceDepth variable in memory, and then update the memory of the process using OS-specific mechanism.
Attach a JVM TI agent that intercepts StackOverflowErrors and prints a stack trace right from the agent.
Here is the code for the first solution (as it is presumably shorter):
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;
import java.io.IOException;
import java.io.RandomAccessFile;
public class ChangeVMFlag extends Tool {
private static String pid;
#Override
public void run() {
Address addr = VM.getVM().getCommandLineFlag("MaxJavaStackTraceDepth").getAddress();
long addrValue = VM.getVM().getDebugger().getAddressValue(addr);
try (RandomAccessFile raf = new RandomAccessFile("/proc/" + pid + "/mem", "rw")) {
raf.seek(addrValue);
raf.writeInt(Integer.reverseBytes(1_000_000));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
pid = args[0];
new ChangeVMFlag().execute(new String[]{pid});
}
}
This tool changes the value of MaxJavaStackTraceDepth in the target process to 1 million.
Note: it uses Linux-specific /proc API to write into the target process' memory. Other OSes have different interfaces.
How to run
On JDK 8
java -cp .:$JAVA_HOME/lib/sa-jdi.jar ChangeVMFlag <pid>
On JDK 9+
java --add-modules=jdk.hotspot.agent \
--add-exports jdk.hotspot.agent/sun.jvm.hotspot.tools=ALL-UNNAMED \
--add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED \
--add-exports jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED \
ChangeVMFlag <pid>

Java: CaptureDeviceManager#getDeviceList() is empty?

I am trying to print out all of the capture devices that are supported using the #getDeviceList() method in the CaptureDeviceManager class and the returned Vector has a size of 0.
Why is that? I have a webcam that works - so there should be at least one. I am running Mac OS X Lion - using JMF 2.1.1e.
Thanks!
CaptureDeviceManager.getDeviceList(Format format) does not detect devices. Instead it reads from the JMF registry which is the jmf.properties file. It searches for the jmf.properties file in the classpath.
If your JMF install has succeeded, then the classpath would have been configured to include all the relevant JMF jars and directories. The JMF install comes with a jmf.properties file included in the 'lib' folder under the JMF installation directory. This means the jmf.properties would be located by JMStudio and you would usually see the JMStudio application executing correctly. (If your JMF install is under 'C:\Program Files', then run as administrator to get around UAC)
When you create your own application to detect the devices, the problem you described above might occur. I have seen a few questions related to the same problem. This is because your application's classpath might be different and might not include the environment classpath. Check out your IDE's properties here. The problem is that CaptureDeviceManager cannot find the jmf.properties file because it is not there.
As you have found out correctly, you can copy the jmf.properties file from the JMF installation folder. It would contain the correct device list since JMF detects it during the install (Check it out just to make sure anyway).
If you want do device detection yourself, then create an empty jmf.properties file and put it somewhere in your classpath (it might throw a java.io.EOFException initially during execution but that's properly handled by the JMF classes). Then use the following code for detecting webcams...
import javax.media.*;
import java.util.*;
public static void main(String[] args) {
VFWAuto vfwObj = new VFWAuto();
Vector devices = CaptureDeviceManager.getDeviceList(null);
Enumeration deviceEnum = devices.elements();
System.out.println("Device count : " + devices.size());
while (deviceEnum.hasMoreElements()) {
CaptureDeviceInfo cdi = (CaptureDeviceInfo) deviceEnum.nextElement();
System.out.println("Device : " + cdi.getName());
}
}
The code for the VFWAuto class is given below. This is part of the JMStudio source code. You can get a good idea on how the devices are detected and recorded in the registry. Put both classes in the same package when you test. Disregard the main method in the VFWAuto class.
import com.sun.media.protocol.vfw.VFWCapture;
import java.util.*;
import javax.media.*;
public class VFWAuto {
public VFWAuto() {
Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone();
Enumeration enum = devices.elements();
while (enum.hasMoreElements()) {
CaptureDeviceInfo cdi = (CaptureDeviceInfo) enum.nextElement();
String name = cdi.getName();
if (name.startsWith("vfw:"))
CaptureDeviceManager.removeDevice(cdi);
}
int nDevices = 0;
for (int i = 0; i < 10; i++) {
String name = VFWCapture.capGetDriverDescriptionName(i);
if (name != null && name.length() > 1) {
System.err.println("Found device " + name);
System.err.println("Querying device. Please wait...");
com.sun.media.protocol.vfw.VFWSourceStream.autoDetect(i);
nDevices++;
}
}
}
public static void main(String [] args) {
VFWAuto a = new VFWAuto();
System.exit(0);
}
}
Assuming you are on a Windows platform and you have a working web-cam, then this code should detect the device and populate the jmf.properties file. On the next run you can also comment out the VFWAuto section and it's object references and you can see that CaptureDeviceManager reads from the jmf.properties file.
The VFWAuto class is part of jmf.jar. You can also see the DirectSoundAuto and JavaSoundAuto classes for detecting audio devices in the JMStudio sample source code. Try it out the same way as you did for VFWAuto.
My configuration was Windows 7 64 bit + JMF 2.1.1e windows performance pack + a web-cam.
I had the same issue and I solved by invoking flush() on my ObjectInputStream object.
According to the API documentation for ObjectInputStream's constructor:
The stream header containing the magic number and version number are read from the stream and verified. This method will block until the corresponding ObjectOutputStream has written and flushed the header.
This is a very important point to be aware of when trying to send objects in both directions over a socket because opening the streams in the wrong order will cause deadlock.
Consider for example what would happen if both client and server tried to construct an ObjectInputStream from a socket's input stream, prior to either constructing the corresponding ObjectOutputStream. The ObjectInputStream constructor on the client would block, waiting for the magic number and version number to arrive over the connection, while at the same time the ObjectInputStream constructor on the server side would also block for the same reason. Hence, deadlock.
Because of this, you should always make it a practice in your code to open the ObjectOutputStream and flush it first, before you open the ObjectInputStream. The ObjectOutputStream constructor will not block, and invoking flush() will force the magic number and version number to travel over the wire. If you follow this practice in both your client and server, you shouldn't have a problem with deadlock.
Credit goes to Tim Rohaly and his explanation here.
Before calling CaptureDeviceManager.getDeviceList(), the available devices must be loaded into the memory first.
You can do it manually by running JMFRegistry after installing JMF.
or do it programmatically with the help of the extension library FMJ (Free Media in Java). Here is the code:
import java.lang.reflect.Field;
import java.util.Vector;
import javax.media.*;
import javax.media.format.RGBFormat;
import net.sf.fmj.media.cdp.GlobalCaptureDevicePlugger;
public class FMJSandbox {
static {
System.setProperty("java.library.path", "D:/fmj-sf/native/win32-x86/");
try {
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
GlobalCaptureDevicePlugger.addCaptureDevices();
Vector deviceInfo = CaptureDeviceManager.getDeviceList(new RGBFormat());
System.out.println(deviceInfo.size());
for (Object obj : deviceInfo ) {
System.out.println(obj);
}
}
}
Here is the output:
USB2.0 Camera : civil:\\?\usb#vid_5986&pid_02d3&mi_00#7&584a19f&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
RGB, -1-bit, Masks=-1:-1:-1, PixelStride=-1, LineStride=-1

Blocking a website from access for all browsers

Greetings,
I would like to ask if there's a way to block website(s) from access on a computer(s) dynamically? I mean could this functionality be coded (on java native interface)?
Your response is highly appreciated.
Thanks,
Cyril H.
Yes, you can code a simple HTTP proxy service with Java:
http://www.java2s.com/Code/Java/Network-Protocol/Asimpleproxyserver.htm
Alternatively, there are plenty of existing proxy solutions out there might suit your needs out of the box:
http://www.roseindia.net/opensource/freeproxyservers.php
You would then configure the software/devices that access websites (e.g., your browser) to point to that proxy, so that all HTTP communication passed through it.
Your proxy could then restrict access to whatever URL(s) you wanted to, based on whatever logic you wanted to code up.
If you wanted to get really fancy/secure and require folks to use the proxy (and not to choose to bypass it), you could do that, but that's probably more than you need to, given your question.
You could append entries to your hosts file using the Files class, as shown in this post: How to append text to an existing file in Java?.
This works on all platforms (yes, all of them: including Windows, Mac, Linux, Android, and more), and blocks access for all browsers, without the need for a proxy or special browser extensions (which can be deleted in most cases).
Here is some simple code to start you off. Feel free to edit it to fit your needs:
public void blockSite(String url) {
// Note that this code only works in Java 7+,
// refer to the above link about appending files for more info
// Get OS name
String OS = System.getProperty("os.name").toLowerCase();
// Use OS name to find correct location of hosts file
String hostsFile = "";
if ((OS.indexOf("win") >= 0)) {
// Doesn't work before Windows 2000
hostsFile = "C:\\Windows\\System32\\drivers\\etc\\hosts";
} else if ((OS.indexOf("mac") >= 0)) {
// Doesn't work before OS X 10.2
hostsFile = "etc/hosts";
} else if ((OS.indexOf("nux") >= 0)) {
hostsFile = "/etc/hosts";
} else {
// Handle error when platform is not Windows, Mac, or Linux
System.err.println("Sorry, but your OS doesn't support blocking.");
System.exit(0);
}
// Actually block site
Files.write(Paths.get(hostsFile),
("127.0.0.1 " + url).getBytes(),
StandardOpenOption.APPEND);
}
Imports for above method:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
Sample usage:
blockSite("www.example.com");
Note:
This needs to be run as an administrator (Windows) or using sudo (Mac, Linux).
This might not work for some platforms, as it was only tested on Ubuntu Linux.
P.S. If you're making parental control software, you should also look into blocking programs. Not all things you would want to block are on the Internet. Here is some simple code for that:
/**
Blocks programs.
#param programs - The array of process names.
#param timeout - The time between blocks, in milliseconds.
This parameter should not be set below 100, to avoid slowdown.
#author https://stackoverflow.com/users/5905216/h-a-sanger
*/
public void blockPrograms(int timeout, String...programs) throws IOException {
// Get OS name
String OS = System.getProperty("os.name").toLowerCase();
// Identify correct blocking command for OS
String command = "";
if ((OS.indexOf("win") >= 0)) {
command = "taskkill /f /im ";
} else if ((OS.indexOf("mac") >= 0) || (OS.indexOf("nux") >= 0)) {
command = "killall ";
} else {
// Handle error when platform is not Windows, Mac, or Linux
System.err.println("Sorry, but your OS doesn't support blocking.");
System.exit(0);
}
// Start blocking!
while(true) {
// Cycle through programs list
for(int i = 0; i < programs.length; i++) {
// Block program
Runtime.getRuntime().exec(command + programs[i]);
}
// Timeout
try { Thread.sleep(timeout); } catch(InterruptedException e) {}
}
}
Imports for above code:
import java.io.IOException;
Sample usage:
blockPrograms(100, "chrome", "firefox");
Again, let me note this was only tested on Ubuntu Linux.

How can I detect a Unix-like OS in Java?

Ok, I know that System.getProperty("os.name") will give me the name of the OS I'm running under, but that's not a lot of help. What I need to know is if the OS I'm running on is a 'Unix-like' OS, I don't care if it's HP-UX, AIX, Mac OS X or whatever.
From the list of possible os.name values it seems like a quick and dirty way of detecting a 'Unix-like' OS is checking if os.name does not contain "Windows". The false positives that will give me are OSes my code is very unlikely to encounter! Still, I'd love to know a better way if there is one.
Use the org.apache.commons.lang.SystemUtils utility class from Commons Lang, it has a nice IS_OS_UNIX constant. From the javadoc:
Is true if this is a POSIX compilant
system, as in any of AIX, HP-UX, Irix,
Linux, MacOSX, Solaris or SUN OS.
The field will return false if OS_NAME
is null.
And the test becomes:
if (SystemUtils.IS_OS_UNIX) {
...
}
Simple, effective, easy to read, no cryptic tricks.
I've used your scheme in production code on Windows XP, Vista, Win7, Mac OS 10.3 - 10.6 and a variety of Linux distros without an issue:
if (System.getProperty("os.name").startsWith("Windows")) {
// includes: Windows 2000, Windows 95, Windows 98, Windows NT, Windows Vista, Windows XP
} else {
// everything else
}
Essentially, detect Unix-like by not detecting Windows.
File.listRoots() will give you an array of the file system root directories.
If you are on a Unix-like system, then the array should contain a single entry "/" and on Windows systems you'll get something like ["C:", "D:", ...]
Edit: #chris_l: I totally forgot about mobile phones. Some digging turns up that Android returns a "/\0\0" - a slash followed by two null bytes (assumed to be a bug). Looks like we avoid false positives for the time being through luck and coincidence. Couldn't find good data on other phones, unfortunately.
It's probably not a good idea to run the same code on desktops and mobile phones regardless, but it is interesting to know. Looks like it comes down to needing to check for specific features instead of simply the system type.
Javadoc says: On UNIX systems the value of this
* field is '/'; on Microsoft Windows systems it is '\'.
System.out.println( File.separatorChar == '/' ? "Unix" : "Windows" );
System.getProperty("os.name"); is about the best you are going to get.
I agree with #Fuzzy in that I think the only way that Java intended you to be able to get that information was through the os.name property.
The only other things I can think of are:
Have a shell script or batch file wrapper to launch your Java app that passes in OS information using the -D argument to the JVM. Though given your description, this doesn't sound doable.
You could try to check for the existence of an OS-specific directory. For instance, you could assume the directory "/" will always exist on a Unix-like system, but not on Windows and do something like this:
if((new File("/")).exists())
{
System.out.println("I'm on a Unix system!");
}
Try to kick off a Unix-specific command line command like ls and check the return code. If it worked, you're on a Unix-like system, if not you're on Windows.
All of those solutions are really just hacks though and frankly I don't really feel all that great about any of them. You're unfortunately probably best off with your original thought. Fun, eh?
Use File.pathSeparator or File.separator. The first will return ";" in Windows and ":" in Unix. The second will return "\" in Windows and "/" in Unix.
You could try to execute the uname command - should be available on all unixoid systems.
package com.appspot.x19290;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class UnixCheck {
public static void main(String[] args) {
UnixCheck s = UnixCheck.S;
String isUnix = s.unix ? "is Unix" : "not Unix";
try {
System.out.println(isUnix + ", devnull: " + s.devnull.getPath());
} catch (NullPointerException e) {
System.out.println(isUnix + ", devnull: unknown");
}
}
public static final UnixCheck S = new UnixCheck();
public static final UnixCheck TEST = new UnixCheck(true);
public final boolean unix;
public final File devnull;
private UnixCheck() {
this(false);
}
private UnixCheck(boolean testing) {
String path;
path = testing ? "/<dev>/<no><such><null><device>" : "/dev/null";
File devnull = devnullOrNone(path);
if (devnull == null) {
this.unix = false;
path = testing ? "<no><such><null><device>" : "nul";
this.devnull = devnullOrNone(path);
} else {
this.unix = true;
this.devnull = devnull;
}
}
private static File devnullOrNone(String name) {
File file = new File(name);
if (file.isFile())
return null;
if (file.isDirectory())
return null;
try {
FileInputStream i = new FileInputStream(file);
try {
i.read();
} finally {
i.close();
}
} catch (IOException e) {
return null;
}
return file;
}
}

Is *this* really the best way to start a second JVM from Java code?

This is a followup to my own previous question and I'm kind of embarassed to ask this... But anyway: how would you start a second JVM from a standalone Java program in a system-independent way? And without relying on for instance an env variable like JAVA_HOME as that might point to a different JRE than the one that is currently running. I came up with the following code which actually works but feels just a little awkward:
public static void startSecondJVM() throws Exception {
String separator = System.getProperty("file.separator");
String classpath = System.getProperty("java.class.path");
String path = System.getProperty("java.home")
+ separator + "bin" + separator + "java";
ProcessBuilder processBuilder =
new ProcessBuilder(path, "-cp",
classpath,
AnotherClassWithMainMethod.class.getName());
Process process = processBuilder.start();
process.waitFor();
}
Also, the currently running JVM might have been started with some other parameters (-D, -X..., ...) that the second JVM would not know about.
I think that the answer is "Yes". This probably as good as you can do in Java using system independent code. But be aware that even this is only relatively system independent. For example, in some systems:
the JAVA_HOME variable may not have been set,
the command name used to launch a JVM might be different (e.g. if it is not a Sun JVM), or
the command line options might be different (e.g. if it is not a Sun JVM).
If I was aiming for maximum portability in launching a (second) JVM, I think I would do it using wrapper scripts.
It's not clear to me that you would always want to use exactly the same parameters, classpath or whatever (especially -X kind of stuff - for example, why would the child need the same heap settings as its parents) when starting a secondary process.
I would prefer to use an external configuration of some sort to define these properties for the children. It's a bit more work, but I think in the end you will need the flexibility.
To see the extent of possible configuration settings you might look at thye "Run Configurations" settings in Eclipse. Quite a few tabs worth of configuration there.
To find the java executable that your code is currently running under (i.e. the 'path' variable in your question's sample code) there is a utility method within apache ant that can help you. You don't have to build your code with ant - just use it as a library, for this one method.
It is:
org.apache.tools.ant.util.JavaEnvUtils.getJreExecutable("java")
It takes care of the sort of special cases with different JVM vendors that others have mentioned. (And looking at the source code for it, there are more special cases than I would have imagined.)
It's in ant.jar. ant is distributed under the Apache license so hopefully you can use it how you want without hassle.
Here's a way that determines the java executable which runs the current JVM using ProcessHandle.current().info().command().
The ProcessHandle API also should allow to get the arguments. This code uses them for the new JVM if available, only replacing the current class name with another sample class. (Finding the current main class inside the arguments gets harder if you don't know its name, but in this demo it's simply "this" class. And maybe you want to reuse the same JVM options or some of them, but not the program arguments.)
However, for me (openjdk version 11.0.2, Windows 10), the ProcessInfo.arguments() is empty, so the fallback else path gets executed.
package test;
import java.lang.ProcessBuilder.Redirect;
import java.lang.management.ManagementFactory;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestStartJvm {
public static void main(String[] args) throws Exception {
ProcessHandle.Info currentProcessInfo = ProcessHandle.current().info();
List<String> newProcessCommandLine = new LinkedList<>();
newProcessCommandLine.add(currentProcessInfo.command().get());
Optional<String[]> currentProcessArgs = currentProcessInfo.arguments();
if (currentProcessArgs.isPresent()) { // I know about orElse, but sometimes isPresent + get is handy
for (String arg: currentProcessArgs.get()) {
newProcessCommandLine.add(TestStartJvm.class.getName().equals(arg) ? TargetMain.class.getName() : arg);
}
} else {
System.err.println("don't know all process arguments, falling back to passed args array");
newProcessCommandLine.add("-classpath");
newProcessCommandLine.add(ManagementFactory.getRuntimeMXBean().getClassPath());
newProcessCommandLine.add(TargetMain.class.getName());
newProcessCommandLine.addAll(List.of(args));
}
ProcessBuilder newProcessBuilder = new ProcessBuilder(newProcessCommandLine).redirectOutput(Redirect.INHERIT)
.redirectError(Redirect.INHERIT);
Process newProcess = newProcessBuilder.start();
System.out.format("%s: process %s started%n", TestStartJvm.class.getName(), newProcessBuilder.command());
System.out.format("process exited with status %s%n", newProcess.waitFor());
}
static class TargetMain {
public static void main(String[] args) {
System.out.format("in %s: PID %s, args: %s%n", TargetMain.class.getName(), ProcessHandle.current().pid(),
Stream.of(args).collect(Collectors.joining(", ")));
}
}
}
Before ProcessHandle was added in Java 9, I did something like this to query the current JVM's command-line:
Let the user pass or configure a "PID to command-line" command template; under Windows, this could be wmic process where 'processid=%s' get commandline /format:list.
Determine PID using java.lang.management.ManagementFactory.getRuntimeMXBean().getPid().
Expand command template; execute; parse its output.

Categories