Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What causes a Java library to behave differently when called by JRuby?

I am new to the Java world, but am familiar with Ruby. I am trying to write a program that interacts with some third-party jar files.

While the libraries seem to behave fine if called from Java, they behave incorrectly when I call them in JRuby. This is a problem because I would really like to use JRuby. For example, the two programs below try to do exactly the same thing but they produce different output:

This Java program behaves correctly.

I developed the Java program below in Netbeans and ran it by pressing F6 (Run Main Project). The Libraries folder for the project is set to "C:\Program Files (x86)\Microchip\MPLABX\mplab_ide\lib\nblibraries.properties". When I run it, it prints "pins: 17".

package pinbug1;    
import com.microchip.mplab.mdbcore.assemblies.Assembly;
import com.microchip.mplab.mdbcore.assemblies.AssemblyFactory;
import com.microchip.mplab.mdbcore.simulator.PinSet;
import com.microchip.mplab.mdbcore.simulator.Simulator;
import org.openide.util.Lookup;

public class PinBug1
{
    public static void main(String[] args)
    {
        AssemblyFactory assemblyFactory = Lookup.getDefault().lookup(AssemblyFactory.class);
        Assembly assembly = assemblyFactory.Create("PIC18F14K50");

        Simulator simulator = assembly.getLookup().lookup(Simulator.class);
        int num = simulator.getDataStore().getProcessor().getPinSet().getNumPins();
        System.out.println("pins: " + num);   // prints "pins: 17"
    }

}

This JRuby program behaves incorrectly.

I ran the JRuby program below by just typing jruby bug_reproduce.rb and it printed "pins: 0". I would expect it to print "pins: 17" like the Java program.

["mplab_ide/mdbcore/modules/*.jar",
 "mplab_ide/mplablibs/modules/*.jar",
 "mplab_ide/mplablibs/modules/ext/*.jar",
 "mplab_ide/platform/lib/org-openide-util*.jar",
 "mplab_ide/mdbcore/modules/ext/org-openide-filesystems.jar"
].each do |pattern|
  Dir.glob("C:/Program Files (x86)/Microchip/MPLABX/" + pattern).each do |x|
    require x
  end
end

assemblyFactory = org.openide.util.Lookup.getDefault.lookup(com.microchip.mplab.mdbcore.assemblies.AssemblyFactory.java_class)
assembly = assemblyFactory.create("PIC18F14K50")
simulator = assembly.getLookup.lookup(com.microchip.mplab.mdbcore.simulator.Simulator.java_class)
num = simulator.getDataStore.getProcessor.getPinSet.getNumPins
puts "pins: #{num}"    # => pins: 0

More details

There are about 80 third-party jar files. They are provided by Microchip as part of MPLAB X and implement a simulator for their microcontrollers. The jar files come with MPLAB X and I also downloaded the MPLAB X SDK to get help with using them. I am using lots of undocumented features of the libraries, but I don't see any alternative.

I am using Windows 7 64-bit SP1. I have the following Java-related things installed and listed under "Programs and Features":

  • Java 7 Update 17
  • Java 7 Update 17 (64-bit)
  • Java SE Development Kit 7 Update 17 (64-bit)
  • Java(TM) 6 Update 22 (64-bit)
  • Java(TM) 6 Update 29
  • Java(TM) SE Development Kit 6 Update 22 (64-bit)
  • JRuby 1.7.3
  • IntelliJ IDEA Community Edition 12.0.4
  • Netbeans IDE 7.3
  • MPLAB X IDE v1.70

I used System.getProperty("java.version") to verify that both of my programs are running under Java 1.6.0_22. That is good, because I followed the instructions in the MPLAB X SDK that say "For best results, use the exact same JDK that built the IDE/MDBCore your code will be talking to. For MPLAB X v1.70, this is JDK 6u22 from Oracle." I only installed JDK 7u17 after I encountered this problem, and it didn't make a difference.

I was able to find a workaround to the specific problem identified in the examples, but then I continued my development and ran into another problem where the libraries behaved differently. This makes me think that I am doing something fundamentally wrong in the way I use JRuby.

Thinking that a differing class path might cause this problem, I tried getting the java program to print out its class path and then edited my JRuby program to require precisely the files in that list, but it made no difference.

Questions

  • Do you know of anything that might cause code in JAR files to behave differently when called from JRuby instead of Java?
  • What version of the JDK does JRuby 1.7.3 use, or does that question even make sense?

Update: SOLVED

Thanks to D3mon-1stVFW for actually getting MPLAB X and solving my problem for me! For those who are interested in the nitty gritty details: The number of pins was 0 because the pins are lazy loaded when they are accessed with PinSet.getPin(String). Normally all pins would have been loaded because the peripherals load them, but under JRuby no peripherals were detected. This is because the periphal document could not be found. This is because PerDocumentLocator.findDocs() returned an empty list. PerDocumentLocator failed because com.microchip.mplab.open.util.pathretrieval.PathRetrieval.getPath(com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator.class)) was returning the wrong thing.

Consider the following code, which is similar to what is happening inside PathRetrieval.getPath (except there it was written in Java):

com.microchip.mplab.libs.MPLABDocumentLocator.MPLABDocumentLocator.java_class.resource("MPLABDocumentLocator.class").getFile()

If I follow D3mon-1stVFW's tip and add JAR files to the $CLASSPATH, then that code returns:

file:C:/Program Files (x86)/Microchip/MPLABX/mplab_ide/mplablibs/modules/com-mi crochip-mplab-libs-MPLABDocumentLocator.jar!/com/microchip/mplab/libs/MPLABDocum entLocator/MPLABDocumentLocator.class

However, if I don't add things to the class path, then that code strangely returns:

file:C:%5CProgram%20Files%20(x86)%5CMicrochip%5CMPLABX%5Cmplab_ide%5Cmplablibs% 5Cmodules%5Ccom-microchip-mplab-libs-MPLABDocumentLocator.jar!/com/microchip/mpl ab/libs/MPLABDocumentLocator/MPLABDocumentLocator.class"

The %5C is actually the code for a backslash. The Microchip code in PathRetrieval.getPath does a lot of string manipulation and does not properly handle the case where slashes are represented by %5C. If anyone has any further insight about why the %5Cs are appearing, I would be interested to know, but my problem is solved.

Conclusion: Sometimes Java's getResource() returns a URL with %5C instead of slashes in it and this is affected by what is on the CLASSPATH. If you want to be safe, add the jar file to $CLASSPATH before requiring it, like this:

require 'java'
$CLASSPATH << jar_filename
require jar_filename
like image 711
David Grayson Avatar asked Apr 03 '13 17:04

David Grayson


1 Answers

I was able to get the expected results using this implementation. The main difference in this implantation is adding the jars to the classpath. If you comment this line ($CLASSPATH << jar_file) you will get 0 pins. (Explanation in the bottom of the question)

require 'java'

Dir.glob("C:/MyCustomLibraries/MATLAB/*.jar").each do |jar_file| #Has all MPLab jars except org.netbeans.*
  $CLASSPATH << jar_file
  require jar_file
end

module Mplab
  include_package "org.openide.util" #Lookup
  include_package "com.microchip.mplab.mdbcore.simulator" #PinSet, Simulator
  include_package "com.microchip.mplab.mdbcore.assemblies" #Assembly, AssemblyFactory
end

assembly_factory = Mplab::Lookup.getDefault.lookup(Mplab::AssemblyFactory.java_class)
assembly = assembly_factory.create("PIC18F14K50")
simulator = assembly.getLookup.lookup(Mplab::Simulator.java_class)
num = simulator.getDataStore.getProcessor.getPinSet.getNumPins
puts "pins: #{num}" 

outputs

content/mplab/mplab.deviceSupport

content/mplab/MPHeader.xml

content/mplab/PluginBoardSupport.xml

pins: 17

like image 101
Sully Avatar answered Sep 27 '22 18:09

Sully