How can I detect a Unix-like OS in Java? - 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;
}
}

Related

Java - getOpenFileDescriptorCount for Windows

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)

How to trace the route of a host using Java? [duplicate]

Java does not have primitives for ICMPs and traceroute. How to overcome this? Basically I'm building code that should run in *nix and Windows, and need a piece of code that will run in both platforms.
Here's what I wrote today to "implement" the trace route command in Java. I've only tested in windows but it should work in Linux as well although there are several traceroute tools available for Linux so most likely there need to be some checks for the existence of those programs.
public class NetworkDiagnostics{
private final String os = System.getProperty("os.name").toLowerCase();
public String traceRoute(InetAddress address){
String route = "";
try {
Process traceRt;
if(os.contains("win")) traceRt = Runtime.getRuntime().exec("tracert " + address.getHostAddress());
else traceRt = Runtime.getRuntime().exec("traceroute " + address.getHostAddress());
// read the output from the command
route = convertStreamToString(traceRt.getInputStream());
// read any errors from the attempted command
String errors = convertStreamToString(traceRt.getErrorStream());
if(errors != "") LOGGER.error(errors);
}
catch (IOException e) {
LOGGER.error("error while performing trace route command", e);
}
return route;
}
You'll need the jpcap library (maybe the SourceForge jpcap is working too) and use the ICMPPacket class to implement the desired functionality.
Here is the Java traceroute implementation using the jpcap library .

Can I simulate a specific filesystem while unit testing?

I have been googling a little and did not find an answer which suited my specific case.
I am working on a project file manager classes, and discovered that it was developped to behave differently on Windows and Unix filesystems.
More specifically, it is compensating for the case-senstivity in Unix: when a file is not found, the manager will look for it in a case-insensitive way.
Before changing this code, I would like to implement some unit tests. However, our development machines and our CIP are both on Windows, and I have no Unix machine available. The machines and IDEs are provided by the customer. Virtualization is not an option, and dual-booting is even less.
Is there a way to test both Windows and Unix mode while being platform-independant for the build? I think the ideal would be to run the whole Test Class in a mode, and then in the other, but even a more hands-on solution would be great.
In production mode, the file managers are initialized using Spring, but they are the lowest level of the chain, using directly java.io.
Versions: Java 6, JUnit 4.9
You can use Jimfs with dependency
<dependency>
<groupId>com.google.jimfs</groupId>
<artifactId>jimfs</artifactId>
<version>1.1</version>
</dependency>
Then you could create a linux, windows and Mac file systems using
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.osX());
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.windows());
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
example
class FilePathReader {
String getSystemPath(Path path) {
try {
return path
.toRealPath()
.toString();
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
}
class FilePathReaderUnitTest {
private static String DIRECTORY_NAME = "baeldung";
private FilePathReader filePathReader = new FilePathReader();
#Test
#DisplayName("Should get path on windows")
void givenWindowsSystem_shouldGetPath_thenReturnWindowsPath() throws Exception {
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.windows());
Path path = getPathToFile(fileSystem);
String stringPath = filePathReader.getSystemPath(path);
assertEquals("C:\\work\\" + DIRECTORY_NAME, stringPath);
}
#Test
#DisplayName("Should get path on unix")
void givenUnixSystem_shouldGetPath_thenReturnUnixPath() throws Exception {
FileSystem fileSystem = Jimfs.newFileSystem(Configuration.unix());
Path path = getPathToFile(fileSystem);
String stringPath = filePathReader.getSystemPath(path);
assertEquals("/work/" + DIRECTORY_NAME, stringPath);
}
private Path getPathToFile(FileSystem fileSystem) throws Exception {
Path path = fileSystem.getPath(DIRECTORY_NAME);
Files.createDirectory(path);
return path;
}
}
All this copied from Baeldung.
You could dualboot Ubuntu easily by installing it with wubi.
I've learnt that unit-test should not access the file system for different reasons (speed being one of them).
For Java 6 look into theese:
http://docs.oracle.com/javase/6/docs/api/javax/tools/JavaFileManager.html
http://docs.oracle.com/javase/6/docs/api/javax/swing/filechooser/FileSystemView.html
If you were to use Java 7 this might help you:
http://download.oracle.com/javase/7/docs/technotes/guides/io/fsp/filesystemprovider.html
you could use a VM to test it on unix. Virtual Box by Oracle is agood virtualization software. Install Ubuntu, or Fedora, or some other unix based OS' Disk Image. Transfer your files to the VM. You can directly check out from your source control into the VM and you should be good to go. Atleast I am assuming that is what you want to do : test your software in both windows and linux, but currently don't have linux at your disposal

Accessing javax.smartcardio from Linux 64 bits

I'm trying to load the smartcard terminals using the javax.smartcardio API with the following code:
public CardTerminal getReadyCardTerminal() throws CardException {
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminals = factory.terminals();
List<CardTerminal> list = terminals.list(State.CARD_PRESENT);
while (list.isEmpty()) {
terminals.waitForChange(1000);
list = terminals.list(State.CARD_PRESENT);
}
CardTerminal cardTerminal = list.get(0);
return cardTerminal;
}
... and I always get the following exception:
java.lang.IllegalStateException: no terminals
at javax.smartcardio.TerminalFactory$NoneCardTerminals.waitForChange(TerminalFactory.java:145)
On Windows Vista/7 everything works fine, but I can't get it to work on Linux. I'm using Ubuntu 12.04 64 bits.
I installed the pcscd service using the following command:
sudo apt-get install libccid pcscd libpcsclite-dev libpcsclite1
sudo service pcscd start
And the pcsc_scan command prints this:
PC/SC device scanner
V 1.4.18 (c) 2001-2011, Ludovic Rousseau <ludovic.rousseau#free.fr>
Compiled with PC/SC lite version: 1.7.4
Using reader plug'n play mechanism
Scanning present readers...
0: OMNIKEY CardMan 3x21 00 00
Tue Sep 11 15:44:49 2012
Reader 0: OMNIKEY CardMan 3x21 00 00
Card state: Card inserted,
ATR: <some hexa codes>
...
So everything looks ok, but the smartcardio just doesn't work. I'm trying with both Oracle and OpenJDK 1.7.0_05, 32 and 64 bits.
The code runs ok with OpenJDK (but not with Oracle JDK, don't know really why) on a Ubuntu 32 bits environment. So I think it is a problem with the 64 bits bridge from Java to the PC/SC library.
Any ideas?
Thanks.
I think I found a workaround for this as I just had a similar problem. In a bugreport from ubuntu it says that the javax.smartcardio library searches for the PC/SC library in the wrong directory.
By specifying the path to the PC/SC library on my machine, like the bugreport mentions, I got it working.
The paths in the bugreport are wrong for me, I'm on 64 bit fedora, where the pc/sc library are installed at /usr/lib64/libpcsclite.so.1
So the workaround for me is to specify the library path to java like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
Depending on your Linux distribution, the location of libpcsclite.so.1 actually might differ, it could also be at /lib/x86_64-linux-gnu/libpcsclite.so.1 (i.e. Kubuntu 15.04).
In that case, call it like this:
java -Dsun.security.smartcardio.library=/lib/x86_64-linux-gnu/libpcsclite.so.1
i'm using raspberry with debian arm version
find the location of libpcsclite first with:
$ ldd -r /usr/bin/pcsc_scan
and then use the libpcsclite location with:
java -Dsun.security.smartcardio.library=/usr/lib/arm-linux-gnueabihf/libpcsclite.so.1
You need to give the path to the libpcsclite.so.1 when calling your program as follows
java -Dsun.security.smartcardio.library=/path/to/libpcsclite.so.1
If you don't know the path to the library, use the following command
find /usr/lib -name libpcsclite.so.1
This usually shows you the path on your machine. I used it on both Ubuntu 10 (32bit) and Ubuntu 15(32bit and 64bit)
If you're lazy like me, what you can do is include this part of code in your program before you use the javax.smartcardio library
try {
String comm[] = { "find", "/usr", "/lib", "-name",
"libpcsclite.so.1" };
Process p = Runtime.getRuntime().exec(comm);
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((line = reader.readLine()) != null && !line.equals("")) {
if (line.contains("libpcsclite.so.1")) {
System.setProperty("sun.security.smartcardio.library",line);
break;
}
}
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
Now you can run your code from as usual without including the path to libpcsclite.so.1
For anyone else struggling with this on Ubuntu 14 with a 64 bit machine. I found the .so file is actually located in the following directory
/usr/lib/x86_64-linux-gnu/libpcsclite.so
So running my app with the setting as below worked for me
-Dsun.security.smartcardio.library=/usr/lib/x86_64-linux-gnu/libpcsclite.so
Addition to the solution with supplying the path as a parameter like this:
java -Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1
If you don't want to supply this every time you call the JVM, set it in the environment variables _JAVA_OPTIONS and/or JAVA_OPTS:
export _JAVA_OPTIONS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
export JAVA_OPTS="-Dsun.security.smartcardio.library=/usr/lib64/libpcsclite.so.1"
Since this is a workaround for bug that affects the entire system, it makes sense IMHO to apply this workaround systemwide as well.
JAVA_OPTS has local scope and has to be evaluated by scripts running your code; _JAVA_OPTIONS is supposed to be evaluated automatically by the JRE.
Yet another approach (my favorite) is to make some symbolic links.
It has the advantage that it works system-wide (no jvm arguments, no environment variables).
For my (beloved) debian jessie amd64:
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so libpcsclite.so
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1 libpcsclite.so.1
ln -s /usr/lib/x86_64-linux-gnu/libpcsclite.so.1.0.0 libpcsclite.so.1.0.0
Note: This will probably require superuser access.
Complementing #AshanPerera answer, as sometimes searching each time can be slow, you can search it at the first time, and them store the location in a file, and read it from then on:
try {
String filename = "libpcsclite.location";
File propertyFile = new File(filename);
if(propertyFile.createNewFile())
{
String commandWithArguments[] = { "find", "/usr", "/lib", "-name","libpcsclite.so.1" };
Process searchProcess = Runtime.getRuntime().exec(commandWithArguments);
BufferedReader searchReader = new BufferedReader(new InputStreamReader(searchProcess.getInputStream()));
String propertyValue;
while ( (propertyValue = searchReader.readLine()) != null && !propertyValue.equals(""))
{
if (propertyValue.contains("libpcsclite.so.1")) {
BufferedWriter propertyWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(propertyFile)));
propertyWriter.write(propertyValue);
propertyWriter.close();
System.setProperty("sun.security.smartcardio.library",propertyValue);
break;
}
}
searchProcess.waitFor();
}
else
{
BufferedReader propertyReader = new BufferedReader(new InputStreamReader(new FileInputStream(propertyFile)));
String propertyValue = propertyReader.readLine();
System.setProperty("sun.security.smartcardio.library",propertyValue);
}
} catch (Exception e) {
e.printStackTrace();
}

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.

Categories