Sxslib#13
Conversation
…res. Look for jnidispatch-os-arch first and then jnidispatch, load from jar if neither exist.
…Windows architectures. Look for jnidispatch-os-arch first and then jnidispatch, load from jar if neither exist.
|
would it be sufficient to be able to specify the jnidispatch name prior to JNA load, for instance by setting a jna.library.name property? On Jul 20, 2011, at 6:27 PM, tussis wrote:
|
|
Thank you for reading my explanation above - I know it was rather lengthy. The crux of the problem in the situations we find our code running is that we have little control of the execution environment. The code can be loaded by processes written by third parties and in environments that are very different from traditional JVMs. In particular, being able to be used in a stateless web application environment is the worst. What I'm trying to avoid most is burdening our API users with tricky initialization problems or shift the "execution setup" problem to them. Are you suggesting that some of the initialization code we write as part of our distributed classes set this (system) property based on the environment we find ourselves in? Then the the JNA code would use that name if the property is set? Obviously, that type of thing would work. However, it seems onerous to require that of any JNA user in this situation re-write such code for various modules. However, I may just be misunderstanding your approach. The only real difference I made in Native.java was (after searching the boot path) in the Windows case attempt loadlibrary of platform-specific-named version of the library then try the default name. Please ignore the interim commit since it did this for all platforms and even the boot path which was too much. Thanks again for your patience on this topic. It would greatly help our deployment issues if this change (or similar) could be incorporated into the main. |
|
On Jul 20, 2011, at 9:02 PM, tussis wrote:
I don't think it too onerous to require of those that use JNA in peculiar situations to tweak a few settings to allow JNA to work in their environment, rather than customizing JNA itself to work in that specific environment. Your library/module would do this before the first access of JNA (basically before JNA attempts to load its dispatch library): If not set, JNA uses a default. If set, you can point it at pretty much any path in your system, relative or absolute. This is both simpler than, and less specific, than trying out some name variations looking for a match. Whether the property is set or not, JNA's code path remains unchanged, and only the data used is different. |
|
Hi Chris, On Jul 23, 2011, at 2:57 PM, Chris Milam wrote:
In most cases where JNA is being used in an environment is regularly loaded and unloaded, the most common solution has been to incorporate JNA into the container environment rather than having it attached to the transient module. Whatever the actual solution, though, the automatic unpacking and disposal of the JNA native library is not appropriate for these situations, if for no other reason than because it's a waste of resources to repeatedly unpack the jnidispatch library. The issue from JNA's perspective is how best to allow these custom configurations the latitude they need for a successful deployment. In almost all cases, this is done by providing the jnidispatch library in the system PATH. That PATH is going to be unique for 32-bit and 64-bit processes. Now it may be that in the case of some ikvm deployments (and it would help here if you gave a full description of said deployment), only part of PATH is unique, and it's only the non-unique part of PATH you get to write to.
I think if you carefully consider the scenarios in which a user would need steps 2-4, they really have nothing to do with JNA and everything to do with the "solution" that you're providing. If someone is using JNA outside of your module, with or without some other 3rd-party module, they will have already encountered the issue you are trying to address, and will already have implemented a solution for it. How likely is that to be your solution? If they have a solution, then your code will already be set up to use JNA properly, without any extra configuration. If they have a solution and follow your instructions, they may very well break their existing setup. Your main issue boils down to this:
Possible options:
The solution JNA already provides (#2):
Your suggestion (variant #1):
My suggestion (combination of #1 and #3)
I certainly appreciate that you have deployment issues, that you're under pressure to solve them, and I'm more than willing to try to find something that makes it possible for you to deploy in as clean a manner as possible. And if you want to pay me to come up with that solution, that's fine, too. But I can't just take patches from everyone who insists that "this is just a small patch" or "it won't interfere with anything else". There are very few people I trust to submit patches like that, and they actually don't make patches like that without providing accompanying tests. |
|
I think having the combination as you suggest for #1 and #3 is very good in general. However for many of the specific cases we have to deal with we cannot set Java properties for the host process at start-up (usually a Windows service like IIS or Sharepoint Server). Again, this may be outside the intended use of JNA since we are extending its use into the Microsoft .NET CLR via IKVM. So it could be that I should just close this pull request and maintain a fork that we distribute to accommodate these requirements of a very foreign platform. I completely understand that you would not take updates to JNA from some random dude in github. I wasn't sure how to provide you with the tests I've been doing since it doesn't lend itself to incorporation into jUnit. I have a batch file that sets up the environment in very specific ways to test the following: • Marker file is created when the temporary DLL is created and not deleted before exit - the current implementation fails to do this on very simple test cases and leaves DLLs in the %TEMP% directory as noted earlier It uses a simple test program written in Java to invoke a windows API call (GetUserNameEx in Secur32.dll). The PATH and TEMP environment variables are tightly controlled by the test script. It is meant to run on a 64-bit Windows OS and runs test for both 32-bit and 64-bit JRE. Specifically the following tests are run (for each architecture):
When I run the tests with JNA built from the current master, there are failures - specifically it fails to create marker files for tests 1, 4 and 6. Obviously, it fails test 7 as well because it is doesn't have the side-by-side library feature that I added in my fork. All these tests pass with the sxslib branch. I'm not sure how to incorporate these types of tests into the JNA project. I would be happy to do so if you could provide me with a direction you see is appropriate. I will include the batch file and test program here. |
|
I've amended the "mark for deletion" code to actually get called. Let me know if that part works in your tests. In your situation, is it indeterminate whether your code will run before the JNA static initialization code? When compiling, does iKVM do a dependency analysis to determine the order to load things? It's my understanding that it doesn't really perform class loading in the typical Java fashion, but I'm not familiar with the details. If you can require that your code is loaded first, then System.setProperty("jna.library.name") would do the trick (you don't have to do that at VM launch, only before the Native class is loaded). |
|
I got a copy of the latest changes and ran my tests. The "mark for deletion" now works properly. Thanks for fixing that. And also for adding the feature to disable unpacking. IKVM class loading is indeed very tricky because of the way .NET Framework assemblies work (a class loader for each + a system class loader). This imposes constraints on packaging that seem unnatural in the Java world. Details can be found here: IKVM Class Loading Architecture. I believe that IKVM does correctly initialize class statics based on dependency. And I appreciate that using this knowledge and a new property to initialize JNA could work. However, I believe this would make the modules we supply more fragile over time. The situation where this is most risky is in a web service. The customer is actually writing the Web Service in regular .NET (C#) and uses our IKVM'd versions of custom APIs. There are several independent "entry" classes so to speak packaged in assemblies/jars that correspond to different logical areas of access to the various server functional groups. So, right at the outset, that's some duplicated "init" code for JNA in each of the different packages. Okay, that's not too bad. However, as time goes by we will definitely be using more JNA code to access features in Windows. Each time we will have to review the code path to ensure that the JNA init code still will be called before any possible first access to JNA. As the surface area of our APIs increase over time so does the susceptibility to jndispatch init failures because of platform issues. The technique I've implemented in the sxslib branch has no such issues since it is handled in a standard manner in the core of JNA. Basically, I'm trying to save future maintenance problems/risks for our modules. |
|
On Aug 8, 2011, at 5:44 PM, tussis wrote:
Good to hear that it works this time :)
I don't quite follow how your modules would become more fragile over time. I think you are suggesting that an increased number of modules means increased administrative overhead of ensuring the modules adhere to certain startup requirements. Presumably all your modules have (or could have) some sort of initialization already, in a static initializer or otherwise; if this includes nothing more than access to a central JNA initialization class, then I don't see an issue, other than requiring that your modules be written properly. Since they have to do something to access a JNA library mapping anyway that's not exactly a burdensome requirement. The following is only one method of doing so, I'm sure you can think of others. Even if you "compile" these separately, they're still going to be performing the same initialization (whether the common code is duplicated or not may be important to you, but is really outside the scope of JNA). interface JNAUsage { class MyModule1 implements JNAUsage { class MyModule2 implements JNAUsage { So the only review you have to do here is to ensure each new module has the proper "implements" clause. That's the sort of thing you could easily automate in a unit test or integration tests. |
|
I understand your position on this feature and will close this pull request. Since this is mainly a problem with usage in a non-Java environment, we'll just deal with this particular problem and other troubles that .NET brings. Again thank you for fixing the temporary DLL mark/delete problem. |
Please consider the following scenarios that warrant the inclusion of side-by-side deployment of jnidispatch libraries on Windows 64-bit OS with WoW support for 32-bit programs.
We have an API module that is written in Java and distributed in one of two ways on Windows platforms:
We support multiple windows platforms and the code may be used in a JVM (JRE) or a .NET application (CLR).
In the case where the .NET API programmer is creating a module to be deployed to the Windows GAC (Global Assembly Cache), the programmer
Our API documentation states for the API user to copy DLLs to the assemly's bin folder:
This allows the module to be loaded without problem by either 64-bit or 32-bit Windows processes from the single location in the Windows System GAC.
If the shut-down-on-exit bug were not an issue or the fix worked reliably and did not leave stray DLLs, we would have no further problems. Also, it obviously preferable to deploy the jnidispatch DLLs since it is more efficient than creating and deleting temporary DLLs every time a process runs.
So, it seems that having a simple technique for finding the platform-specific jnidispatch library by naming convention would be unobtrusive and easy to deploy for our customers. We would simply add the two DLLs (jnidispatch-win32-amd64.dll and jnidispath-win32-x86.dll) to the assembly's bin directory.