The Raspberry Pi 3 includes BLE support. I confirmed it works by
sudo hcitool lescan
which returned the MAC and BLE 'complete local name' for neighboring advertisers.
How does one access this programmatically, in Java?
To use BLE on Raspberry Pi 3 you have to update bluez (the bluetooth core in Raspbian linux) then use the bluez D-Bus interface to interact with it.
I'm looking for writing my own java lib, but it's very difficult because there are few documentation about D-Bus in java and about bluez.
For bluez there are only the sample code in the last distribution.
For now i have write a simple script that update the bluez version to the latest:
https://gist.github.com/tongo/94338cebf4e6367d353439bca8d0a376
I have also found a blog post that talk about d-bus,java and bluez:
http://smartspacestuff.blogspot.it/2016/02/i-got-figurin-out-dbus-bluez.html
It was useful, but not very clear for me.
I hope this can help.
If you found other documentation post it.
I don't think there is a clear or easy answer available at this time. Bluetooth integration requires native components that are not part of a standard JDK.
The most common library used for using Bluetooth with Java on Linux is BlueCove. BlueCove provides extra native libraries for working with Bluetooth on linux: BlueCove-GPL or BlueCove-bluez(experimental). However, you will likely need to compile one of these yourself for use on your RPi. Methods for doing do will be dependent on your distribution and will require some significant knowledge of linux, compiling native code, etc. A quick google search shows some working examples of this for previous RPi versions. It's unclear if it will work with the BLE on RPi 3 though.
Another might be to try using "Camel Bluetooth Component", which is wrapper over Bluecove and expects libbluetooth-dev and blueman to be installed. But again, not clear if it will work with RPi 3.
If unable to get a true integration working, another option might be to simply make external Process calls out from Java to the command line Bluetooth utilities that you know already work. It depends on your use-case if this an option, but I suspect could be sufficient for many BLE specific use cases.
You can make this very simple by simply executing a command through Runtime and reading the output with a BufferedReader.
Executing the command:
Process p;
p = Runtime.getRuntime().exec(command);
p.waitFor();
Full code:
package your.package.rpicommand;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ExecuteShellCommand {
public static void main(String[] args) {
ExecuteShellCommand obj = new ExecuteShellCommand();
String domainName = "google.com";
//in mac oxs
String command = "ping -c 3 " + domainName;
//in windows
//String command = "ping -n 3 " + domainName;
String output = obj.executeCommand(command);
System.out.println(output);
}
private String executeCommand(String command) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(command);
p.waitFor();
BufferedReader reader =
new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line + "\n");
}
} catch (Exception e) {
e.printStackTrace();
}
return output.toString();
}
}
I wish I could of coded this for you but there is already plenty of example on the internet about this.
Although this is one way of doing this, you should use BlueCove or some library to scan through the Bluetooth devices.
Source: https://www.mkyong.com/java/how-to-execute-shell-command-from-java/
Related
I am working on a simple meteo station - I want to use raspberry pi 3b+ as a host, dht22 sensor and write a web application in Java (with spring boot, then deploy it to tomcat 8) and Python for retrieving sensor's data.
What I've done so far:
Python application for retrieving and displaying data. Works as expected, it just prints something like "22.5;37.4":
import Adafruit_DHT
DHT_SENSOR = Adafruit_DHT.DHT22
DHT_PIN = 4
humidity, temperature = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)
if humidity is not None and temperature is not None:
print("{0:0.1f};{1:0.1f}".format(temperature, humidity))
else:
print("FAIL")
Then I've wrote a java application, put it into .jar and checked if I am able to get sensor's data. Not a rocket science, also works as expected when I use java -jar InputTest.jar on my raspberry pi:
public static void main(String[] args) {
try {
ProcessBuilder pb = new ProcessBuilder("python", "/home/pi/Desktop/input/dht_once.py");
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println("measured: " + line);
}
process.waitFor();
} catch (IOException ) {
System.out.println(" exception " + e.getLocalizedMessage());
}
}
Then I've created a spring boot application, put my java code inside (logic same as above), packed as a war, deployed to tomcat 8 and run it. It turned out nothing is being printed (of course I've changed code to log output to logfile, it works fine, I can see other logs inside). No issues in logs, it looks like reader never returns a line.
I believe application does not wait for a process to produce output, but I have no idea why. Important thing: it takes up to few seconds to produce sensor's output. I've also changed python script just for test purposes to return value immediately:
print("22.4;33.0")
and it results in successful read by java web application. But when it has to wait few seconds for the output it kills process (process.isAlive() is false right after while loop).
I've also tried to play with sleep() on current thread to force it to wait for python process but no success.
Do you guys have any idea what can be the reason for this behavior? Is there anything more I should check?
TLDR;
Java application which creates python process works fine until I run it as a web application - then it looks like it does not wait for a process' output
I haven't found a solution yet, however I've implemented workaround/cleaner solution.
I've decided to separate totally java and python code and created microservice for data retrieving. I use flask for rest webservice (followed this tutorial https://docs.dataplicity.com/docs/control-gpios-using-rest-api) and call it directly from java.
Since this does not resolve my initial question I do not mark it as an answer, however it might help someone.
I'd like to get an id unique to a computer with Java, on Windows, MacOS and, if possible, Linux. It could be a disk UUID, motherboard S/N...
Runtime.getRuntime().exec can be used (it is not an applet).
Ideas?
The problem with MAC address is that there can be many network adapters connected to the computer. Most of the newest ones have two by default (wi-fi + cable). In such situation one would have to know which adapter's MAC address should be used. I tested MAC solution on my system, but I have 4 adapters (cable, WiFi, TAP adapter for Virtual Box and one for Bluetooth) and I was not able to decide which MAC I should take... If one would decide to use adapter which is currently in use (has addresses assigned) then new problem appears since someone can take his/her laptop and switch from cable adapter to wi-fi. With such condition MAC stored when laptop was connected through cable will now be invalid.
For example those are adapters I found in my system:
lo MS TCP Loopback interface
eth0 Intel(R) Centrino(R) Advanced-N 6205
eth1 Intel(R) 82579LM Gigabit Network Connection
eth2 VirtualBox Host-Only Ethernet Adapter
eth3 Sterownik serwera dostepu do sieci LAN Bluetooth
Code I've used to list them:
Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
while (nis.hasMoreElements()) {
NetworkInterface ni = nis.nextElement();
System.out.println(ni.getName() + " " + ni.getDisplayName());
}
From the options listen on this page, the most acceptable for me, and the one I've used in my solution is the one by #Ozhan Duz, the other one, similar to #finnw answer where he used JACOB, and worth mentioning is com4j - sample which makes use of WMI is available here:
ISWbemLocator wbemLocator = ClassFactory.createSWbemLocator();
ISWbemServices wbemServices = wbemLocator.connectServer("localhost","Root\\CIMv2","","","","",0,null);
ISWbemObjectSet result = wbemServices.execQuery("Select * from Win32_SystemEnclosure","WQL",16,null);
for(Com4jObject obj : result) {
ISWbemObject wo = obj.queryInterface(ISWbemObject.class);
System.out.println(wo.getObjectText_(0));
}
This will print some computer information together with computer Serial Number. Please note that all classes required by this example has to be generated by maven-com4j-plugin. Example configuration for maven-com4j-plugin:
<plugin>
<groupId>org.jvnet.com4j</groupId>
<artifactId>maven-com4j-plugin</artifactId>
<version>1.0</version>
<configuration>
<libId>565783C6-CB41-11D1-8B02-00600806D9B6</libId>
<package>win.wmi</package>
<outputDirectory>${project.build.directory}/generated-sources/com4j</outputDirectory>
</configuration>
<executions>
<execution>
<id>generate-wmi-bridge</id>
<goals>
<goal>gen</goal>
</goals>
</execution>
</executions>
</plugin>
Above's configuration will tell plugin to generate classes in target/generated-sources/com4j directory in the project folder.
For those who would like to see ready-to-use solution, I'm including links to the three classes I wrote to get machine SN on Windows, Linux and Mac OS:
Java code to get computer SN on Windows
Java code to get computer SN on Linux
Java code to get computer SN on Mac OS
The OSHI project provides platform-independent hardware utilities.
Maven dependency:
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>LATEST</version>
</dependency>
For instance, you could use something like the following code to identify a machine uniquely:
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.ComputerSystem;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OperatingSystem;
class ComputerIdentifier
{
static String generateLicenseKey()
{
SystemInfo systemInfo = new SystemInfo();
OperatingSystem operatingSystem = systemInfo.getOperatingSystem();
HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware();
CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor();
ComputerSystem computerSystem = hardwareAbstractionLayer.getComputerSystem();
String vendor = operatingSystem.getManufacturer();
String processorSerialNumber = computerSystem.getSerialNumber();
String processorIdentifier = centralProcessor.getIdentifier();
int processors = centralProcessor.getLogicalProcessorCount();
String delimiter = "#";
return vendor +
delimiter +
processorSerialNumber +
delimiter +
processorIdentifier +
delimiter +
processors;
}
public static void main(String[] arguments)
{
String identifier = generateLicenseKey();
System.out.println(identifier);
}
}
Output for my machine:
Microsoft#57YRD12#Intel64 Family 6 Model 60 Stepping 3#8
Your output will be different since at least the processor serial number will differ.
It is common to use the MAC address is associated with the network card.
The address is available in Java 6 through through the following API:
Java 6 Docs for Hardware Address
I haven't used it in Java, but for other network identification applications it has been helpful.
What do you want to do with this unique ID? Maybe you can do what you want without this ID.
The MAC address maybe is one option but this is not an trusted unique ID because the user can change the MAC address of a computer.
To get the motherboard or processor ID check on this link.
On Windows only, you can get the motherboard ID using WMI, through a COM bridge such as JACOB.
Example:
import java.util.Enumeration;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.EnumVariant;
import com.jacob.com.Variant;
public class Test {
public static void main(String[] args) {
ComThread.InitMTA();
try {
ActiveXComponent wmi = new ActiveXComponent("winmgmts:\\\\.");
Variant instances = wmi.invoke("InstancesOf", "Win32_BaseBoard");
Enumeration<Variant> en = new EnumVariant(instances.getDispatch());
while (en.hasMoreElements())
{
ActiveXComponent bb = new ActiveXComponent(en.nextElement().getDispatch());
System.out.println(bb.getPropertyAsString("SerialNumber"));
break;
}
} finally {
ComThread.Release();
}
}
}
And if you choose to use the MAC address to identify the machine, you can use WMI to determine whether an interface is connected via USB (if you want to exclude USB adapters.)
It's also possible to get a hard drive ID via WMI but this is unreliable.
Not Knowing all of your requirements. For example, are you trying to uniquely identify a computer from all of the computers in the world, or are you just trying to uniquely identify a computer from a set of users of your application. Also, can you create files on the system?
If you are able to create a file. You could create a file and use the creation time of the file as your unique id. If you create it in user space then it would uniquely identify a user of your application on a particular machine. If you created it somewhere global then it could uniquely identify the machine.
Again, as most things, How fast is fast enough.. or in this case, how unique is unique enough.
Be careful when using the MAC address as an identifier. I've experienced several gotchas:
On OS X, ethernet ports that are not active/up do not show up in the NetworkInterface.getNetworkInterfaces() Enumeration.
It's insanely easy to change a MAC address on cards if you've got appropriate OS privileges.
Java has a habit of not correctly identifying "virtual" interfaces. Even using the NetworkInterface.isVirtual() won't always tell you the truth.
Even with the above issues, I still think it's the best pure Java approach to hardware locking a license.
I think you should look at this link ... you can make a mixed key using several
identifiers such as mac+os+hostname+cpu id+motherboard serial number.
The usage of MAC id is most easier way if the task is about logging the unique id a system.
the change of mac id is though possible, even the change of other ids of a system are also possible is that respective device is replaced.
so, unless what for a unique id is required is not known, we may not be able to find an appropriate solution.
However, the below link is helpful extracting mac addresses.
http://www.stratos.me/2008/07/find-mac-address-using-java/
For identifying a windows machine uniquely.
Make sure when you use wmic to have a strategy of alternative methods. Since "wmic bios get serialnumber" might not work on all machines, you might need to have additional methods:
# Get serial number from bios
wmic bios get serialnumber
# If previous fails, get UUID
wmic csproduct get UUID
# If previous fails, get diskdrive serialnumber
wmic DISKDRIVE get SerialNumber
Resources:
The Best Way To Uniquely Identify A Windows Machine
http://www.nextofwindows.com/the-best-way-to-uniquely-identify-a-windows-machine/
In the java programs I have written for release I used the motherboard serial number (which is what I beleive windows use); however, this only works on windows as my function creates a temporary VB script which uses the WMI to retrieve the value.
public static String getMotherboardSerial() {
String result = "";
try {
File file = File.createTempFile("GetMBSerial",".vbs");
file.deleteOnExit();
FileWriter fw = new FileWriter(file);
String vbs =
"Set objWMIService = GetObject(\"winmgmts:\\\\.\\root\\cimv2\")\n"
+ "Set colItems = objWMIService.ExecQuery _ \n"
+ " (\"Select * from Win32_ComputerSystemProduct\") \n"
+ "For Each objItem in colItems \n"
+ " Wscript.Echo objItem.IdentifyingNumber \n"
+ "Next \n";
fw.write(vbs);
fw.close();
Process gWMI = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath());
BufferedReader input = new BufferedReader(new InputStreamReader(gWMI.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
result += line;
System.out.println(line);
}
input.close();
}
catch(Exception e){
e.printStackTrace();
}
result = result.trim();
return result;
}
I'm building a front-end for my company's internal tool kit. Half the tools are written in python and then the other half are written in several other scripting languages. So I'm building a front-end in java using swing. So far I can invoke python scripts through the following code:
public class Foo
{
public static void main(String[] args)
{
try
{
Runtime r = Runtime.getRuntime();
Process p = r.exec("python foo.py");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
p.waitFor();
String line = "";
while (br.ready())
System.out.println(br.readLine());
}
catch (Exception e)
{
String cause = e.getMessage();
if (cause.equals("python: not found"))
System.out.println("No python interpreter found.");
}
}
}
Which works beautifully but if the python script encounters any errors it doesn't print them out. How can I ensure that it also prints out any errors that it has?
The simple answer is to also read Process.getErrorStream.
The more complicated answer is that what you call Python likely refers to CPython which is just one implementation of the language. There is another implementation, Jython, which basically compiles Python into Java bytecode to be run on a JVM. This would allow tighter integration than simply invoking CPython via Java's Runtime.exec
P.S. Runtime.exec is sort of the old way of doing things. ProcessBuilder is often a much cleaner and more intuitive way of starting a sub-process in Java.
The prevois answer is Ok,here is a suggestion that you shoud release any resources of Process,like:
Process process = null;
try{
//xxxx
}catch(xxx){
}finally{
if(process!=null){
process.destroy();
}
}
The reason is that if you forgot to destroy process,the file handler involved would leak,
you will got an IOException show too many open files finally.
How can I set the focus (e.g. cmd+tab) of an arbitrary application (Java or not) from a Java program, on OSX?
Looking for an answer to this question, I came across this question, but it doesn't really help for OSX.
EDIT: one possibiltiy seems to be to use something like Quicksilver, and a Robot to send it keypresses with modifiers. I'd prefer something more portable, though, that requires less setup to make changes after it's compiled....
You should be able to reactivate an already running app using the open command that comes with OS X:
Runtime.exec("open /path/to/Whichever.app");
(Or some equivalent overload of that function.) This will also open an app if it's not running yet.
Chuck's answer tipped me off to osascript, so I decided to give it a shot straight from the command line. Managed to get it working with Runtime.exec(), osascript, and AppleScript.
Java launches an AppleScript and passes it the application name, using osascript from the command line, via Runtime.exec():
try {
List<String> shellCommandList = new ArrayList<String>();
shellCommandList.add("osascript");
shellCommandList.add("activateApplication.scpt");
shellCommandList.add(appName);
String[] shellCommand = (String[])shellCommandList.toArray(new String[0]);
Process p = Runtime.getRuntime().exec(shellCommand);
// if desired, pipe out the script's output
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String cmdOutStr = "";
while ((cmdOutStr = in.readLine()) != null) {
System.out.println(cmdOutStr);
}
// if desired, check the script's exit value
int exitValue = p.waitFor();
if (exitValue != 0) {
// TODO: error dialog
System.err.println("Invalid application name: "+ appName);
}
} catch (Exception e) {
e.printStackTrace();
}
And the AppleScript uses a run handler to capture the incoming argument:
on run (arguments)
set appName to (item 1 of arguments)
tell application appName to activate
return 0
end run
You can use the javax.script API to run AppleScripts. So you can write a script along the lines of "tell application "WhateverApp" to activate", filling in your arbitrary application for WhateverApp, and it should do what you want.
I have a CMS server that provides a client library. I'd like to be able to drive the CMS interactively from the command line.
The basic approach would be:
Create a connection to the CMS
Add the CMS connection object to the REPL context
Connect the REPL to stdout/stderr/stdin
Kick off a daemon thread for to keep the REPL running.
I was hoping that I could perhaps leverage Groovy to do this but haven't managed to get it working.
Is there a library that provides REPL support?
Can you provide a simple example?
If you don't mind using Scala as your language, you can use the Scala REPL to explore java libraries. You can do this in a number of ways, either with
$ scala -classpath yourjarfileshere.jar
or if you're using maven:
mvn scala:console
If all you're doing is playing (not scripting or anything), then this is a possible way to go.
If you wish to embed your repl, and you're still willing to use Scala, you can look at the answer to these questions: Drop into interpreter during arbitrary scala code location
and Launch Scala REPL programatically?
Groovy also has a repl, groovysh, which you can use to explore.
I got this working with Groovy.
Example
public static void main(final String[] args) {
Binding binding = new Binding();
// Configure your bindings here.
Groovysh shell = new Groovysh(binding, new IO());
shell.run(args);
}
Known Issues
However, it won't work when the app is started from Eclipse (ie using the Eclipse 'console' view). To work around this you must update the Eclipse launch configuration to pass the following VM argument:
-Djline.terminal=jline.UnsupportedTerminal.
More information
Documentation of the Groovy Shell.
Wikipedia page for REPL mentions BeanShell. Would that work?
Beanshell can be run as repl in your own thread/main within your application:
public static void main(String[] args) throws Exception{
Reader inreader = new InputStreamReader(System.in);
Interpreter i = new Interpreter(inreader, System.out, System.err, true);
try {
BufferedReader in = new BufferedReader(inreader);
String str;
while ((str = in.readLine()) != null) {
i.eval(str);
}
in.close();
} catch (Exception e) {
}
}
that example runs in eclipse fine, you type at it in the console window of eclipse then it will talk back to you fine.