NeuronRobotics / nrjavaserial

A Java Serial Port system. This is a fork of the RXTX project that uses in jar loading of the native code.
Other
345 stars 143 forks source link

Incompatibility with older "RxTx" library #54

Closed rtrcka closed 4 years ago

rtrcka commented 8 years ago

Hello,

I'm new here but I would like to ask you for support. I would like to use "nrjavaserial" instead of the older RxTx 2.1.7 library, unfortunately there are differences in the interface. The first I have met is following difference in class "gnu.io.CommPortIdentifier":

RxTx: public synchronized CommPort open(String paramString, int paramInt) throws PortInUseException;

NRJavaSerial public RXTXPort open(String TheOwner, int i) throws PortInUseException;

Class "RXTXPort" extends the abstract class "CommPort" but unfortunately the exact prototype is required by JRE, so this incompatibility disables simple library replacement. I get the following error when used the NRJavaSerial library and called this "open(String, int)" method in code compiled with RxTx library:

java.lang.NoSuchMethodError: "gnu.io.CommPortIdentifier.open(Ljava/lang/String;I)Lgnu/io/CommPort;"

Would you be so kind to verify and slightly update the interface so that both these libraries are mutually compatible / replace-able?

I'm sorry if this topic was already discussed here in another thread/issue.

Thank you in advance, Robert

madhephaestus commented 8 years ago

NrjavaSerial forked form RXTX at 2.2 and we have never touched these interfaces in our fork. i do not know when or why this was changed, it happened before i started working on it. RXTXPort extends SerialPort which extends CommPort. This means returning a RXTXPort based on the "is-a" principal, is returning a CommPort. Im not sure how this is causing you a problem, other than maybe you somehow managed to include both libraries in your jar at the same time, causing a classpathing conflict (which would have that error, or something similarly cryptic). can you post a gist, or link to the line in github sources, to a code snippet of the offending line in your sources, maybe something else strange is going on...

rtrcka commented 8 years ago

We don't use both RxTx/NRJavaSerial libraries at one moment but one of them depending on customer environment. I have found sources for RxTx 2.2pre5 here: "http://neophob.com/2011/05/serial-library-rxtx-v2-2pre5/". I'm not sure if there was published a higher build number.

If you go into this release, there is following source fragment in class "gnu.io.CommPortIdentifier": public synchronized CommPort open(String TheOwner, int i) throws gnu.io.PortInUseException { ... }

while NRJavaSerial source code contains following prototype: public RXTXPort open(String TheOwner, int i) throws gnu.io.PortInUseException {...}

When we compile our application with RxTx and use NRJavaSerial in runtime, then we get the mentioned NoSuchMethodError. Recompilation is not possible at this moment, as we would loose required compatibility with RxTx.

Could you please post a link to RxTx sources that you forked the NRJavaSerial from?

andyrozman commented 8 years ago

Maybe you need to change your implementation a little. i used Rxtx 2.1.x and later Rtxtx 2.2, and when switching to NrJavaSerial I didn't need to change my implementation much.

(this might work only with 2.2 or NRS, 2.1 might have problem with that) SerialPort serialPort = portIdentifier.open("owner", (int) timeOut)

If you are not sure what is in implementation you can use like that: (this will work with 2.1, 2.2 and NRS) SerialPort serialPort = (SerialPort)portIdentifier.open("owner", (int) timeOut)

If you need RXTXPort explicitly do instanceof check.

RXTX 2.2 was little buggy (even release) as I remember and you got version 2 binaries for version 1 rxtx.jar (or something like that). I know that I have rebuild java sources, because of this (so that I didn't get warning each time I loaded binaries).

NOTE: Release you are referencing is NOT official build... Here is link to original RXTX page... http://rxtx.qbang.org/wiki/index.php/Download

cxbrooks commented 8 years ago

I ran in to a similar issue at runtime with the XBeeJava library at https://github.com/digidotcom/XBeeJavaLibrary with nrjavaserial. The error at runtime was:

Exception in thread "main" java.lang.NoSuchMethodError:  gnu.io.CommPortIdentifier.open(Ljava/lang/String;I)Lgnu/io/CommPort;
    at  com.digi.xbee.api.connection.serial.SerialPortRxTx.open(SerialPortRxTx.java:167)
at com.digi.xbee.api.XBeeDevice.open(XBeeDevice.java:195)
at ptolemy.actor.lib.jjs.modules.xbee.XBeeHello.main(XBeeHello.java:20)

My solution was to recompile XBeeJava.

Details: The key line in the message is:

gnu.io.CommPortIdentifier.open(Ljava/lang/String;I)Lgnu/io/CommPort;

http://rxtx.qbang.org/pub/rxtx/rxtx-2.1-7/src/CommPortIdentifier.java, which is similar to the version of rxtx that is shipped with XBeeJava has this method:

public synchronized CommPort open(String TheOwner, int i) 

https://github.com/NeuronRobotics/nrjavaserial/blob/86b44454cebc7ba29c2032e904cfbe4eb098b841/src/main/java/gnu/io/CommPortIdentifier.java, which is what is in nrjavaserial has:

public RXTXPort open(String TheOwner, int i) 

However, RXTXPort extends SerialPort which extends CommPort, so this should work (I think). For reference, Line 167 of SerialPortRxTx.java is below:

serialPort = (RXTXPort)portIdentifier.open(PORT_ALIAS + " " + port, receiveTimeout); 

To replicate this:

  1. Download the small example file from https://docs.digi.com/display/XBJL /Add+the+application+source+code and create MainApp.java
  2. Remove the package line from MainApp.java
  3. Download XBeeJava from https://github.com/digidotcom/XBeeJavaLibrary/releases/download/v1.1.0/XBJL-1.1.0.zip and unzip
  4. Download the latest release of nrjavaserial from https://github.com/NeuronRobotics/nrjavaserial/releases/download/3.11.0/nrjavaserial-3.11.0.jar
  5. Compile: javac -classpath ./XBJL-1.1.0/xbjlib-1.1.0.jar:./XBJL-1.1.0/extra-libs/slf4j-api-1.7.12.jar:./XBJL-1.1.0/extra-libs/slf4j-jdk14-1.7.12.jar:nrjavaserial-3.11.0.jar MainApp.java
  6. Run

    bash-3.2$ java -classpath .:./XBJL-1.1.0/xbjlib-1.1.0.jar:./XBJL-1.1.0/extra-libs/slf4j-api-1.7.12.jar:./XBJL-1.1.0/extra-libs/slf4j-jdk14-1.7.12.jar:nrjavaserial-3.11.0.jar MainApp Dec 10, 2015 11:44:58 AM com.digi.xbee.api.XBeeDevice open INFO: [COM1 - 9600/8/N/1/N] Opening the connection interface...

    Error com.digi.xbee.api.exceptions.InvalidInterfaceException: No such port: COM1 at com.digi.xbee.api.connection.serial.SerialPortRxTx.open(SerialPortRxTx.java:163) at com.digi.xbee.api.XBeeDevice.open(XBeeDevice.java:195) at MainApp.main(MainApp.java:20) Caused by: gnu.io.NoSuchPortException at gnu.io.CommPortIdentifier.getPortIdentifier(CommPortIdentifier.java:273) at com.digi.xbee.api.connection.serial.SerialPortRxTx.open(SerialPortRxTx.java:161) ... 2 more bash-3.2$

It would be great if nrjavaserial was drop-in compatible with rxtx, but I don't have a solution for this issue.

andyrozman commented 8 years ago

It is better to cast it into SerialPort. If you do that it will work with old version and new...

SerialPort is extended from ComPort and instance you get returned is definitely from SerialPort (RXTXPort extends that one). If you are not sure if user selected correct portIdentifier, you could do check here:

CommPort rawPort = portIdentifier.open("owner", receiveTimeout); 

if (!(rawPort instanceof SerialPort))
{
    throw new Exception("Port instance is not serial port");
}

SerialPort serialPort = (SerialPort)rawPort;

You could try to cast it into RXTXPort here, but not if you use older version of RXTX...

madhephaestus commented 8 years ago

I do not know what the justification for this change is in the first place. What are the implications of changing this API back the the RXTX version?

I do not know of anything on my end that this would mess up.

cxbrooks commented 8 years ago

Right. Just to be clear, I'm not necessarily advocating reverting the nrjavaserial API back to the RXTX version.

In the perfect world, nrjavaserial would be a drop in replacement for RXTX.

If that is not possible, as a workaround, it would be good if we could have a porting guide that would tell rxtx users what they need to do to modify their code so that it could work with both the legacy RXTX and nrjavaserial.

At some point, I'll take a look at the XBeeJava code and see what can be done. The short term workaround is to recompile.

andyrozman commented 8 years ago

I just made compare of code... It seems that NRJS has changed stuff a little... But problem is other way arround... If you correctly implemented your code for RXTX, you could use NRJS. But if you implemented for NRJS, you could not revert back to RXTX.

I made compare RXTX 2.2 with latest NRJS.

NRJS retrurn RXTXPort

public RXTXPort open(String TheOwner, int i) 

, while RXTX returns ComPort.

public CommPort open(String TheOwner, int i) 

Rule of thumb when developing Java application is, that you need to be careful with dependencies... If library1 (v1) is build with library2 (v1.5), you CAN'T change library2 to any other version of library... If your XBee works with RXTX 2.0.7, you can't go and make it work with RXTX 2.2 (or even NRJS x.x) without checking if it will work (opening both libraries in development environment shows where the problem lies) and recompiling and testing both with some test code (or test devices)... Now it might work with other versions (or even other library), but you need to check it...

In this case if you implemented your application for rxtx 2.1.7 (or rxtx 2.2), you could use latest nrjavaserial, but not other way around...

NOTE: We could change this implementation, but problem is that everybody, that started with using NRJavaSerial, would need to change his/hers code...

If you see my comment above, you could write your implementation so, that it could use both libraries.

rtrcka commented 8 years ago

Hello,

I have spent some time by deeper test of the functionality and I have prepared a very simple test utility that just opens and closes the serial port COM1. It's done for STS/Eclipse tool, but the main idea you can read from the Java classes directly.

Class TestSerialPort tries to open/close the port directly by calling CommPort port = CommPortIdentifier.getPortIdentifier(portName).open(portName, 1000);. This would succeed only if serial library used for compilation is the same as the library used in runtime. Library mismatch leads to the NoSuchMethodError.

Class TestSerialPortReflection tries to open/close the port directly as TestSerialPort, but in case of thrown NoSuchMethodError will call the method open(String TheOwner, int i) by reflection on the object of the CommPortIdentifier class. This will succeed in case of library mismatch too, however it is very ugly way and unfortunately can be realized in code that is managed by myself, not in 3rd party libraries that I have to use as-is.

You can see the results in the log files in folder "results". The complete project (Eclipse project files, Java sources, classes, libs, log files) you can download here: 20151211 - nrjavaserial_rxtx.zip

rtrcka commented 8 years ago

Just one more idea to make it compatible ... would be possible to add new method prototype public CommPort open(String TheOwner, int i) to the NRJS class CommPortIdentifier, that will do the same as already existing method public RXTXPort open(String TheOwner, int i) (preferably will one call another with casting of return value if needed)? That will not harm the code of existing users of NRJS (no need to recompile their apps) and would bring a method prototype compatibility for users of RxTx.

Of course there would be other incompatibilities in other classes, this one was the first one that I met. So the interfaces should be reviewed in detaild for such differences.

andyrozman commented 8 years ago

No this will not be possible... We would need to add method with other name (or other parameters)... You can't have two methods with same name, same parameters and different return value....

So:

public RXTXPort open(String TheOwner, int i) 
public CommPort open(String TheOwner, int i) 

at same time not possible... If you called this method, JDK couldn't know which one you called... You could do like this:

public RXTXPort open(String TheOwner, int i) 
public CommPort openOld(String TheOwner, int i) 

or

public RXTXPort open(String TheOwner, int i) 
public CommPort open(String TheOwner, int i, boolean fakeParameter) 

But neither of solutions would help at this time.

edwardrf commented 8 years ago

For anyone who comes to this page from google searching for the strange exception you've got form RXTX like this

java.lang.NoSuchMethodError: "gnu.io.CommPortIdentifier.open(Ljava/lang/String;I)Lgnu/io/CommPort;

What could happened is you have installed RXTX library to your system where the code you are running is using nrjavaserial, which gets overridden by the system rxtx. And because they have a different signature, the code refuse to run.

This problem is not easily found as the error is about gnu.io with no reference to nrjavaserial, and there is no report for conflict at loading.

I had to remove my system wide installation of rxtx to make it work.

DerekCook commented 7 years ago

Hi.

I just thought I'd say I was having the NoSuchMethod exception on open, and the problem was exactly as explained by edwardrf.

In the summer I was developing an application interfacing to an Arduino, on a PC laptop whilst sitting outside in the fine Welsh sunshine. Part of the job was to move from RXTX to NRSerial due to the advantages of embedded native code meaning less faff for my users.

With Autumn now here, I have moved this application for further development onto my Mac and this morning I was getting this problem, simply because I forgot that I had RXTX installed in my Java Extensions directory, which was overriding NRSerial.

I deleted RXTX and all is now fine.

Lolmewn commented 7 years ago

I have the same issue. When I do have the RXTXcomm.jar in the lib from JAVA_HOME, I can print all ports but not open them due to this error. When I remove the jar, I get the following error:

java.lang.UnsatisfiedLinkError: gnu.io.RXTXCommDriver.nativeGetVersion()Ljava/lang/String; thrown while loading gnu.io.RXTXCommDriver
java.lang.NoClassDefFoundError: Could not initialize class gnu.io.RXTXCommDriver thrown while loading gnu.io.RXTXCommDriver

I cannot seem to use the makefile for arm as there are packages missing (Raspberry Pi 3):

pi@dex:~/jserial/nrjavaserial $ make arm
sudo apt-get install g++-arm-linux-gnueabihf g++-arm-linux-gnueabi
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package g++-arm-linux-gnueabihf
E: Couldn't find any package by regex 'g++-arm-linux-gnueabihf'
E: Unable to locate package g++-arm-linux-gnueabi
E: Couldn't find any package by regex 'g++-arm-linux-gnueabi'
Makefile:29: recipe for target 'arm' failed
make: *** [arm] Error 100

I also tried running the code while having librxtx-java installed from apt-get, but it seems it doesn't matter whether or not that is installed. Help would be appreciated.

frog75 commented 7 years ago

So basically what you are saying is that this project is NOT compliant with javax.comm version of CommPortIdentifier? https://docs.oracle.com/cd/E17802_01/products/products/javacomm/reference/api/javax/comm/CommPortIdentifier.html#open(java.lang.String, int)

Dretch commented 1 year ago

I was able to workaround this issue without recompiling the library that depends on nrjavaserial, by rewriting the CommPortIdentifier class file to add the method with the CommPort return type (the generated method simply delegates to the method that returns RXTXPort, and the rewritten class file is placed on the classpath ahead of the nrjavaserial library).

I am not at liberty to share the code for this, but it is pretty simple - this is a good starting point: https://danamlund.dk/backwards_compatibility_with_java_bytecode_editing.html