search paths where one native library depends on another - java

I'm using JNA and Java but I think this question affects any native-to-nonnative bridge.
I have a Java application which relies on lib1.dylib, and lib1.dylib relies on lib2.dylib.
I want to put everything inside of my .app file on Mac. I can easily put lib1.dylib inside and set java.classpath (or NativeLibrary.addSearchPath()) to tell the JVM where to find lib1.dylib. The trouble is, I don't know how to communicate that lib1.dylib's dependencies are also in the location I provided. The result is that lib1 is loaded fine, but then lib2 can't be found since it's not in the operating system's library path.
Anyone know how I can overcome this problem? I imagine it must come up plenty in big projects with large numbers of shared libraries.

I've come across this problem before, and have just run into it again today. You may be able to get around it by adding the VM argument "-Djava.library.path=/path/to/other/libs", but I seem to remember Java only uses that to search for the intial library and then uses the system PATH to look for any dependencies.
A few solutions I've tried before:
1) Use System.load(absolutePath) on the dependent library before loading your library. Doesn't make your program ultra-portable though, unless you always know where that library is going to be.
2) In a case where lib1 depends on lib2, I actually used SetCurrentDirectory (Windows, not sure of the Mac equivalent) in the native code before it linked to any of the dependent libs, and that seemed to work. Again, requires knowing where the other libs are.
3) On Windows, could dump the dependent libraries in c:\windows\system32, and it finds them.
A few helpful posts on a similar topic (Windows-specific, but I think the problem is the same):
http://www.realityinteractive.com/rgrzywinski/archives/000219.html
http://www.velocityreviews.com/forums/t387618-jni-library-path.html

I've found a solution for MacOSX based on the idea in (2) from Stew:
Using Mac's JarBundler (or the Ant task of the same name) set the workingdirectory variable to $JAVAROOT and make sure your dylibs are in the Contents/Resources/Java part of the .app. If you do this the dynamic linker will find all the dependency dylibs because it will be the present directory. Java will also find the original dylib (the one that has all the dependencies) for the same reason.
Ant code:
<target name="package_mac_app" depends="package_jar, compile_native" description="bundle the runnable jar into a Mac Application -- requires JarBundler ANT Task">
<taskdef name="jarbundler" classname="net.sourceforge.jarbundler.JarBundler"/>
<echo message="CREATING MAC .app EXECUTABLE"/>
<jarbundler dir="${dist}"
name="${appname}"
mainclass="myPackage.myMainClass"
icon="${icon_location}"
jvmversion="1.5+"
infostring="${appname}"
shortname="${appshortname}"
bundleid="${com.mycompany.mydepartment.myprogram}"
jar="${run_jar_location}"
workingdirectory="$JAVAROOT">
<javafilelist dir="${dylib_location}" files="my-lib.dylib"/>
<javafilelist dir="${dylib_location}" files="dependent-lib.dylib"/>
</jarbundler>
</target>

Related

Xamarin and APK Signing - Change path to JarSigner

When I build my Xamarin project the version of the jarsigner tool it uses is ALWAYS from \Java\jdk1.6.0_39\bin\
I was wondering if we could change to different version of JDK.
Looking at the build output it seems to boil down to whatecer MSBuild../Xamerin/Android.Build.Tasks.dll tells it.... (see below)
Is there a way to get the build to point to another path...a later version of the JDK?whatever
11>Using "AndroidSignPackage" task from assembly "C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Build.Tasks.dll".
11>Task "AndroidSignPackage"
11> C:\Program Files (x86)\Java\jdk1.6.0_39\\bin\jarsigner.exe
Looking at the Xamarin's Custom MSBuild Task Library (C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Build.Tasks.dll) AndroidSignPackage extends AndroidToolTask which extends the built in ToolTask class. It also looks like they properly implemented it as well so you should be able to simply pass the additional optional parameter ToolPath.
If you're calling the task directly from MSBuild as part of a custom build process the command might looks like this:
<AndroidSignPackage
UnsignedApk="pathtounsignedapk"
SignedApkDirectory="signedapkoutputdir"
Keystore="yourkeystorelocation"
KeyAlias="thekeyaliasusedtosign"
StorePass="thepasswordforthekeystore"
ToolPath="NEWPATHTOJAVASDK" />
If you're trying to do this integrated within the Visual Studio Environment you'll need to start groveling around in their *.Targets file and chase it down, if you're not comfortable with MSBuild I do not recommend doing so.
BEYOND THIS POINT NO WARRANTY GROVELING IN UNDOCUMENTED LAND COULD AND WILL BREAK AT THE LEAST OPPORTUNE TIME AS PER MURPHY
In my version of the Xamarin Toolchain in Xamarin.Android.Common.targets (C:\Program Files (x86)\MSBuild\Xamarin\Android\Xamarin.Android.Common.targets) we see that the AndroidSignPackage is called from within the _Sign target, it appears that, at least in this version, are passing the ToolPath attribute, which in this version is defined as $(JarsignerToolPath), looking further up in the .targets file we see that this is defined as follows:
<CreateProperty Value="$(_JavaSdkDirectory)\bin">
<Output TaskParameter="Value" PropertyName="JarsignerToolPath"
Condition="'$(JarsignerToolPath)' == ''"
/>
</CreateProperty>
It looks like they were really nice (at least in this version) at validate that $(JarsignerToolPath) is not defined prior to setting this value, if it is this task will not do anything and take the existing value.
At this point in time you have a couple of options, what it boils down to is they'll respect the MSBuild Property $(JarsignerToolPath) however it comes in before this build process. If you read the documentation on MSBuild you'll see that you can define that property in a couple of ways.
The most popular (and my recommendation) is to declare it straight up in your MSBuild Script (remember that CSPROJ files are just MSBuild Scripts) in one of the property groups (I'd recommend under a build configuration) you can simply define this property explicitly for example:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|JDK17' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
... (Additional properties trimmed) ...
<JarsignerToolPath>C:\Program Files (x86)\Java\jdk1.7.0_71\bin</JarsignerToolPath>
</PropertyGroup>
Alternatively you can set an Environment variable prior to launching Visual Studio/MSBuild process performing the build that sets the JarsignerToolPath to the correct property.
Obviously you can use the above knowledge to take it even further and look to research if you could replace $(_JavaSdkDirectory)...

Matlab and JDDE

Update: The problem was solved with the help of MathWorks. I've published the answer below.
I need to control a program (Zemax) from Matlab. Unfortunately, Zemax only supports DDE for such control, which Matlab does not support any more. It works, but stops working on 64 bit platform after a few (presumable 63) DDE calls.
I wonder if there are working solutions. I could probably program a DLL with correct DDE support and then use DDE in Matlab via this DLL. This is a major effort for me. A more suitable solution would be to use Java DDE methods. Following another post here, I've discovered the JDDE library. However I cannot make it work: Even if I am in the directory with the DLL and JAR files, executing
import pretty-tools.JDDE-2.0.3.*
works fine but calling
a = com.pretty_tools.dde.client.DDEClientConversation()
afterwards (as done here) results in
Undefined variable "com" or class "com.pretty_tools.dde.client.DDEClientConversation".
I have very limited writing privileges on my PC, so I have added the javaclasspath.txt file with the jar/dll location to the directory indicated by prefdir. The file looks like this:
C:\Users\xxxxxxxx\Documents\matlab toolbox\jdde\pretty-tools-JDDE-2.0.3.jar
Calling javaclasspath shows a long listing with the last lines being:
...
C:\Program Files\MATLAB\R2012b\java\jarext\webservices\ws_client_core\mw-service-client-core.jar
C:\Users\kkarapet\Documents\matlab toolbox\jdde\pretty-tools-JDDE-2.0.3.jar
DYNAMIC JAVA PATH
<empty>
So path seems to be set correctly. What am I doing wrong?
With the help of MathWorks support, I've found the answer. Here is how to make JDDE work with Matlab 2012b, without admin privileges:
Download and unpack JDDE files (DLLs and JAR) into some folder. Let's say it's $path-to-jdde$\.
In Matlab, type prefdir. Open the resulting directory and create two files there, javaclasspath.txt and javalibrarypath.txt.
In javaclasspath.txt, add $path-to-jdde$\pretty-tools-JDDE-2.0.3.jar.
In javalibrarypath.txt, add $path-to-jdde$\.
Restart Matlab.
Now call ddeConv = com.pretty_tools.dde.client.DDEClientConversation; and start using the created object as described in JavaDoc. E.g. to connect to Zemax, run Zemax and then in call ddeConv.connect('Zemax', 'abc').
Step 2 above can only be done starting Matlab version R2012b. With an older version, if you have the write rights on the Matlab installation directory, you should be able to replace step 2 by editing the files librarypath.txt and classpath.txt in $MATLABROOT$\toolbox\local. I could not verify it so if you confirm it please let me know in the comment below.

How to Define Paths to Frameworks on a Mac in Java?

I am helping to code a stop-motion program that is to be cross platform, and on windows it works great. For those who do not know, stop motion is just a fancy term for animation. This program allows users to plug in Nikons, Canons, and Webcams into the computer, and have the program display a live view of the scene, and then have the ability to manually control the camera from there. Included is a framework file from canon for the camera, with a path defined as shown
import com.sun.jna.Native;
initialization and such
public static EdSdkLibrary EDSDK = (EdSdkLibrary) Native.loadLibrary("Macintosh/EDSDK.framework/EDSDK",EdSdkLibrary.class, options);
The error is thrown at the "public static int..." saying that the image is not found. I have tried numerous times redefining the path, moving the framework, and using various other frameworks identical to the one I'm using. Remember, this works flawlessly on Windows, but on Mac there is a problem.
Are frameworks different on macs, or are they to be defined differently? I have looked and found no other solutions.
EDIT: Okay, I defined the path and it now has this symbol > with no text next to it. WHat do I do now?
EDIT: It is saying that this % is not a command. Without it, it still fails to work.
JNA will successively attempt to load frameworks from ~/Library/Frameworks, /Library/Frameworks, and /System/Library/Frameworks, based on the core framework name (EDSDK in this case).
If the loadLibrary call succeeds, then the library was found. If the library was not found, you'll get an UnsatisfiedLinkError.
Frameworks are basically bundles of a shared library with other resources; ESDK.framework/ESDK is the actual shared library (for frameworks, OSX omits the "dyld" suffix normally found on a shared library on OSX).
EDIT
Here's how to make a symlink so that the paths look more like what JNA is expecting. From a terminal (run Terminal.app):
% ln -s /your/complete/path/to/Macintosh/EDSDK.framework ~/Library/Frameworks/EDSDK.framework
When this is done successfully, you should see the following when listing (ls) the symlink:
% ls -l ~/Library/Frameworks/EDSDK.framework
lrwxrwxr-x 1 YOU YOU 50 Mar 31 01:13 /Users/YOU/Library/Frameworks/EDSDK.framework -> /your/complete/path/to/Macintosh/EDSDK/Framework/EDSDK.framework
You should see the symlink path (where JNA will look) on the left, with the path to the real file on the right. If not, delete the symlink file and try again. Note that you may need to create the directory ~/Library/Frameworks first; it may not yet exist.
Finally, make sure that the library you're trying to load matches the VM you're trying to load with; 64-bit with 64-bit, 32-bit with 32-bit. Canon does not provide a universal binary of their library, so you'll need to point to one or the other or merge the two using lipo.
Not really an answer, but more information on the same problem, which I'm experiencing myself.
I can add that JNA will find my frameworks if they're in one of the standard public locations an executable looks for its frameworks, i.e.
~/Library/Frameworks - (public frameworks for the use of the current user)
/Library/Frameworks - (public frameworks for the use of any user)
/System/Library/Frameworks - (public system frameworks)
However, If I want my custom framework to be private - i.e. - not discoverable to other processes than my java vm -- then for some reason JNA doesn't do it.
I know MacOS dynamic loader, when trying to locate a library/framework for any normal (native) MacOS process, does NOT start searching the above locations, but first within several standard "private" locations: (also known as rpath search-path)
in the "Frameworks" directory at the same location as the binary from which the process was loaded: e.g. path/to/my/binary/Frameworks/mySDK.framework
in the "Frameworks" directory at the place where dynamic loader loaded the process (in Application bundles, that would be the myApp.app/Contents/Frameworks/mySDK.framework folder.
So, you can usually create a 'Frameworks' directory of your own right next to your binary, and place your framework in it.
However - JNA misses that. I tried to create a "Frameworks" directory within the "Zulu" - in zulu-11.jre/Contents/Home/bin right next to the 'java' binary, and in other places - but JNA won't find in any of them.
I wonder why, and if there is any documentation for that.
The trick of installing a symlink to my custom framework in /Library/Frameworks may serve you, but I cannot allow other processes to find or load my framework.

How to track down JAR with corrupt JAR Index that cause InvalidJarIndexException in Tomcat in Eclipse

I am working with development of an application which, among other pieces of code, contains a number of servlets. The development environment I use is Eclipse (3.2.1, which is rather old) in which I run a Tomcat server (5.5.23, rather old as well) using the Eclipse Tomcat Wrapper plug-in for the task. All this runs on a RedHat 5.2 Linux system.
The Java runtime I use is JDK 1.6.0(21), which I upgraded to (from a previous JDK 1.5 version) quite recently and as far as I can recall, the software combination above (together with the application I'm working with) did actually work: I could start the Tomcat server, it got up without errors or complaints and the application's servlets were available on port 8080.
However, something has changed somewhere (could be in the application jarfiles themselves, I'm suspicious of essentially everything on the host to be the root cause of this). Now, when I try to start up the Tomcat server, I get the error sun.misc.InvalidJarIndexException in the console output. This happens for the following classes and methods:
org.apache.commons.modeler.Registry registerComponent (happens 3 times)
org.apache.catalina.core.StandardServer initialize (happens once)
org.apache.catalina.connector.Connector start (happens twice)
I did find this stack overflow question regarding how to find the JAR of a Java Class useful and I did run find /usr -name \*name-of-suspected-jar\*.jar a few times to track down a number of suggested offending JARS. I also tried to check the runtime configuration of the Tomcat server in Eclipse, but could really not match the JAR files on the system with the CLASSPATH of neither the Tomcat runtime setup (or with the CLASSPATH used in the environment when starting Eclipse). That effort probably requires some more rigor on my part but before doing that (and that is why I right now don't post all the gory details regarding CLASSPATHs here), I did a read up on exactly what InvalidJarIndexException really is about.
So, JAR files may contain an optional INDEX.LIST file which contains information about what classes (and methods?) to find in the JAR file. The idea is to short-circuit the search throughout all JARS in the CLASSPATH which is useful in a number of circumstances. Problem is when the INDEX.LIST file happens to be corrupt (or, is believed to be corrupt), that causes the loading of the class to be completely given up (the class loader does not fall back to searching all JARs in the CLASSPATH) and the error InvalidJarIndexException to be thrown. To make things more messy, the order in which JARs are searched might affect how the class loader treats the INDEX.LIST file: the INDEX.LIST file of one JAR might refer to other JARS and if those referred to JARS are not in sync with the first JAR's INDEX.LIST file, the class loader fails with this InvalidJarIndexException error.
So (according to this StackOverflow question), it seems like this error can be thrown not only because a JAR file has a corrupt INDEX.LIST, it seems it can even be thrown on a JAR even if the JAR has a valid INDEX.LIST or legitimately is lacking a INDEX.LIST simply because a previously searched JAR has confused the class loader. (To put in another way, as things are, this exception might be thrown even for "innocent" non-corrupted JAR files due to offenders elsewhere on the system).
So, after writing a mere novel, here comes my main set of questions:
What is the best way to track down the precise .jar file for which each InvalidJarIndexException is thrown?
What is the best way check if a randomly picked .jar file has an INDEX.LIST file and if so, if said file is valid (that is, non-corrupt)? What tools exist for this task?
Is there an efficient way to automatically deduce the search order of .jar files? I can try to follow the CLASSPATH manually but to be honest, that is error prone and tedious.
Is there an efficient way to figure out what .jar file there is in a search order which might confuse the class loader to accuse innocent, non-corrupt .jar files later in the search to have incorrect INDEX.LIST files?
Disclaimer: I know I run old versions software (even if I have the latest updates of my Redhat 5.2 installed though) and I know a knee-jerk reaction for many people is to suggest that I don't put any effort whatsoever in debugging this but instead upgrade to a more recent version of Tomcat, Eclipse and Linux (Java is recent though). The reason I would prefer not to is that after looking into things, I've found it rather messy to do an upgrade or to try to install a separate modern Tomcat or Eclipse next to the RHEL5.2 provided Tomcat/Eclipse I use today. Also, I consider this kind of troubleshooting an opportunity to learn some useful nitty gritty details about Java and it's associated tools and features. Figuring out how the class loading works and what causes it to throw this InvalidJarIndexException on my system would be very educating!
(But if this troubleshooting fails, I'll seriously consider to use a modern Linux, Eclipse and Tomcat... I promise)
Take the following steps to diagnose the problem:
Add an exception breakpoint in Eclipse (it's the J with an
exclamation mark icon), and set it to halt for caught and uncaught
exceptions, of type InvalidJarIndexException.
Start debugging your program.
Eclipse will halt at your exception breakpoint, when the InvalidJarIndexException is thrown. Even without the source for URLClassPath, you will still be able to inspect the variables on the stack leading to the exception, including the name of the class that URLClassPath is attempting to locate. Knowing the name of the class should significantly narrow the list of JAR's you need to examine.
Perhaps you've locally added a new class to a package and the contents of that package are described by the index file in a stale JAR on your classpath?
Try Tattletale which is a good reporting tool for jars. What I have done in this case was to eliminate INDEX.LIST from jars one by one until I did not get InvalidJarIndexException any more

Java Reflection not working on my system - working for team members

I am working on a team project in Java. One requirement is that we dynamically populate a drop-down menu of all classes that implement a certain interface. New classes can be added after compile time. To accomplish this we are using reflection.
Problem: All of the drop-down menus are blank on my system. I cannot for the life of me figure out why they are not populating. All other 5 team members have it working on their system.
Things I tired that didn't work:
1) Installing most recent eclipse (galileo) because rest team was using it
2) Re-install most recent java release (jdk1.6.0-17 and jre6)
3) Check PATH and JAVA_HOME variables
Any thoughts as to what else I can try or if something I did should have solved it and didn't? It is driving me crazy.
Edit:
I should have been clearer that we are developing in a team. We are using SVN for version control and we are all running the exact same source code. I even tried checking out a fresh copy of the entire tree from SVN, but I had the same issue with reflection on my system while it worked for teammates.
The team created an executable jar and that ran on everyone's system fine except for mine. Everything worked for me except the reflection bit.
You need to debug your application. This means you have to systematically explore possible causes of the problem. Here are some things that come to mind:
Could your GUI be failing rather than reflection? What if you output with System.out.println() rather than your menu?
Is your reflection code throwing an exception, and are you ignoring it?
Is your reflection code actually being called? Toss a println() in there to be sure!
Is the test for the interface suffering from a typo or similar error that's causing it to fail? Try finding classes that implement Serializable instead!
Is your reflection test running in the main thread and trying to update your GUI? You need to use SwingUtilities.invokeAndWait to get an update to the Swing worker thread.
You're working with Eclipse; Eclipse has a fantastic debugger. Set a breakpoint near where your main action is and then single step through the code.
PATH and JAVA_HOME won't help. PATH only affects dynamically-linked libraries ("native code"). JAVA_HOME is a scripting variable that happens to be used by some Java-based utilities like Ant and Tomcat; it means nothing to the Java runtime itself.
You need to be investigating the classpath, which should be specified by the -classpath option to the java command, in the Build Path in your Eclipse project properties, or in the Class-Path attribute of the main section of a JAR file if you're launching java with the -jar option.
From within your code, you should be able to list the contents of your classpath by examining the system property, "java.class.path"
System.out.println(System.getProperty("java.class.path"));
Problem solution:
Classpath leading to source code must have no spaces in it.
I am running windows XP and, for whatever reason, if the classpath that leads to the jar file or source code that is using reflection has any spaces in it, then the reflection fails.
I took the jar file that works for the rest of my team and ran it from C:\ on my system and the reflection worked perfectly fine.
I do not know why this is so please comment if you know what is happening.
Might be a long shot, but look for differences in security settings for you and your team mates. Article describing more details http://www.ibm.com/developerworks/library/j-dyn0603/ heading "Security and reflection"

Categories