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
assemblyFactory.Create(...)
, with a capitalized method name, in Java, butassemblyFactory.create(...)
in Ruby. I know JRuby converts inner caps (camel case) into underscore + small letters, but I'm not sure how it works with initial caps. – Hort$CLASSPATH
before requiring them. – Antecederequire
to be equivalent to "loading a JAR", whenrequire
seems to be strictly a JRuby-specific mechanism that doesn't really affect how standard JVM functionality (i.e. loading resources from a classloader) works. So, I think my point remains, there is a valid question behind this one, but as stated this isn't it considering the answer was reached by what seems like trial and error. – Brickey