13 Replies Latest reply on Oct 26, 2007 8:48 PM by jschellSomeoneStoleMyAlias

    Problem :  Native Library already loaded in another classloader

    843829
      I created a shared library HaspKeyAPI.dll by using JNI and add path to Java class library. I need to call native functions in my servletAction class to check some information in a key. Sometimes not always, I met �java.lang.UnsatisfiedLinkError: Native Library C:\eclipse\workspace\HaspKey\SharedLib\HaspKeyAPI.dll already loaded in another classloader� problem. I have to restart tomcat server then to restart my web app.

      Does anyone know how to fix it?
      Thank you in advance!


      ////////////////////////////////////my native function class, a singleton class////////////////////////////////
      package haspkey.api;

      import java.util.Collection;
      import java.util.Vector;

      import haspkey.HardwareSecurityKey;

      Public class HaspKeyAPI {

      Public static final int KEYEXIST = 1;

      //A Singleton Class
      private static HaspKeyAPI haspKeyAPI;

      private HaspKeyAPI()
      {
      }

      public static HaspKeyAPI getSingletonObject()
      {
      if ( haspKeyAPI== null)
      haspKeyAPI = new HaspKeyAPI();          
      return haspKeyAPI;
      }

      public Object clone()
           throws CloneNotSupportedException
      {
      throw new CloneNotSupportedException();
      }

      /**********use native functions to call hasp C API***********/

      public native int keyPresent(int password1, int password2);

      public native int getId(int password1, int password2);

      public native void readBlock(int startMemoryLocation, int size,
      byte[] block, int password1, int password2);

      public native void writeBlock(int startMemoryLocation,int size, byte[] block, int password1,
      int password2);

      public native boolean decodeData(int size, byte[] block, int password1,
      int password2);

      public native boolean encodeData(int size, byte[] block, int password1,
      int password2);



      static {
      System.loadLibrary("HaspKeyAPI");
      }

      private int getPrivMachineSpeed(boolean decrypt, int pass1, int pass2, String MLOCRTypeCode){
      byte[] block = new byte[8];
      int privMachineSpeed = 0;
      boolean isNumeric = true;;
      readKeyMemory(40,8,block, decrypt, pass1, pass2);

      if(block.length!=8){
      return -1;
      }else{
      String s = new String(block);
      for(int n=0; n<3; n++){
      if(s.charAt(n)<48 || s.charAt(n)>57){
      isNumeric = false;
      }
      }

      if(isNumeric){
      privMachineSpeed = Integer.parseInt(s.substring(0,3));
      }
      }
      return privMachineSpeed;
      }

      public HardwareSecurityKey getHardwareSecurityKey(boolean decrypt, int pass1, int pass2, String AuthorizationCodes,String MLOCRTypeCode){
      HardwareSecurityKey key = new HardwareSecurityKey();
      if(keyPresent(pass1,pass2)==KEYEXIST){
      key.setPrivKeyID(getId(pass1, pass2));
      key.setPrivMachineSpeed(getPrivMachineSpeed(true, pass1,pass2, MLOCRTypeCode));

      }else{

      return null;
      }
      }


      /////////////////////////////// a class include some static final information to call the native function///////////////
      import com.bowebellhowell.haspkey.api.HaspKeyAPI;
      import com.bowebellhowell.winsort.vo.haspkey.HardwareSecurityKey;


      public class HaspKey{

      public static final String MLOCRTypeCode ="mmmmmmmmmmmmmmm";
      public static final String AuthorizationCodes = "bbbbbbbbbbbbbbbb";
      public static final int p1 = 0;
      public static final int p2 = 0;



      public static HardwareSecurityKey getHardwareSecurityKey(){
      HaspKeyAPI key = HaspKeyAPI.getSingletonObject();
      return key.getHardwareSecurityKey(true,p1,p2,AuthorizationCodes,MLOCRTypeCode);
      }

      }

      /////////////////////////////////// a servlet to call native function///////////////////////////////////
      public class LogonAction extends Action {
           
           public ActionForward execute(
                ActionMapping mapping,
                ActionForm form,
                HttpServletRequest request,
                HttpServletResponse response)
                throws Exception {
                
                     
                
                int n = HaspKey.isKeyPresent();
                if(n!=1){
                if(n==2){
                message = "KeyDriverNotMatch";          
                }else if(n==3){
                message = "KeyOldModel";
                }else if(n==4){
                message = "KeyPasswordNotMatch";
                }else if(n==-1){
                message = "keyNotExist";
                }
                request.getSession().setAttribute("message",message);
                }
           }


      java.lang.UnsatisfiedLinkError: Native Library C:\eclipse\workspace\HaspKey\SharedLib\HaspKeyAPI.dll already loaded in another classloader
      at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1551)
      at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1511)
      at java.lang.Runtime.loadLibrary0(Runtime.java:788)
      at java.lang.System.loadLibrary(System.java:834)
      at haspkey.api.HaspKeyAPI.<clinit>(HaspKeyAPI.java:76)
      at util.HaspKey.isKeyPresent(HaspKey.java:35)
      at LogonAction (LogonAction .java:65)



        • 1. Re: Problem :  Native Library already loaded in another classloader
          843829
          See: http://java.sun.com/docs/books/jni/html/design.html#8628
          • 2. Re: Problem :  Native Library already loaded in another classloader
            843829
            But I am still not clear, where in my code I loaded the library that already loaded in another class? I think I only load it once.
            • 3. Re: Problem :  Native Library already loaded in another classloader
              843829
              Tomcat (or any servlet engine for that matter) uses separate classloaders for each web application. So it sounds like you have multiple instances of your web app getting loaded at the same time by different classloaders. Search the archive for tips on getting your native-based class to be loaded by only a single Tomcat classloader, rather than by the per-app classloaders.
              • 4. Re: Problem :  Native Library already loaded in another classloader
                843829
                Tomcat (or any servlet engine for that matter) uses
                separate classloaders for each web application.
                And in fact, if you cause your webapp to get reloaded in any fashion, you'll run into this, because native libraries cannot be unloaded even when the old classloader is destroyed.

                So you need to ensure that your native library is loaded by Tomcat's common class loader: the referring class (that does the loadlibrary) must be in common/lib (not your webapp's WEB-INF/lib), and (of course) your native lib needs to in PATH or LD_LIBRARY_PATH as appropriate for the platform.
                • 5. Re: Problem :  Native Library already loaded in another classloader
                  jschellSomeoneStoleMyAlias
                  Tomcat (or any servlet engine for that matter)
                  uses
                  separate classloaders for each web application.
                  And in fact, if you cause your webapp to get reloaded
                  in any fashion, you'll run into this, because native
                  libraries cannot be unloaded even when the old
                  classloader is destroyed
                  .
                  Incorrect.

                  Since about 1.3 the Sun VM unloads native libraries loaded in the context of a class loader when that class loader is collected.
                  • 6. Re: Problem :  Native Library already loaded in another classloader
                    843829
                    Hi, I'm having the same problem. I'm using Tomcat 5.5 and even if STOP my web app, the .dll is still kept loaded by the app server. What do I have to do to get the server to unload it?
                    • 7. Re: Problem :  Native Library already loaded in another classloader
                      843829
                      I have a solution to this issue that is not perfect, but hopefully it will help someone else.

                      Recently I migrated my server which previously was run as a console application (jarred) to run as a web module in SAS 9. It was using a JNI module to get the eth0 MAC address (for cheap license control I use the MAC as a unique system key).

                      To get the system eth0 MAC I wrote a DLL for Windows and an SO for Linux to grab the eth0 MAC address and pass it out to a simple API call via a native lib.

                      I fould that when the module was loaded a second time the "already loaded" error would appear. BTW Sun: UnsatisfiedLink to convey this error is very ambiguous and misleading! I had to step through JDK code to figure this out.

                      When System.loadLibrary gets called it uses the web module Classloader. This shared library doesn't get unloaded in a timely way so the next time you deploy (e.g this is especially common when you're debugging in Netbeans) JVM rejects the load request because it's already loaded in the old web module's Classloader.

                      The solution is to place your library into a CLASSPATH location which is accessible to the System classloader (e.g. jre/lib/ext) and then use the system classloader to actually load the class and use it:

                      not complete, but you'll get the idea:

                      public String getMacAddress() throws IOException {
                      if(m_EthoLib == null) {
                      try {
                      ClassLoader sysCL = getClass().getClassLoader().getSystemClassLoader();
                      Class cb = sysCL.loadClass("settings.EthZeroMacAddress");
                      EthZeroMacAddress Q = (EthZeroMacAddress) cb.newInstance();
                      m_EthoLib = Q;
                      } catch(ClassNotFoundException e) {
                      throw new IOException(e.getMessage());
                      } catch (InstantiationException e) {
                      throw new IOException(e.getMessage());
                      } catch (IllegalAccessException e) {
                      throw new IOException(e.getMessage());
                      }
                      }
                      return m_EthoLib.getMACaddress();
                      }

                      This causes the library load to be made by the system classloader instead of the web module classloader avoiding the conflict.

                      The only catch is you have to deploy your JNI lib into the system classpath. I suspect there is probably a way to specify this file in the SAS configuration, but I haven't figured that out.
                      • 8. Re: Problem :  Native Library already loaded in another classloader
                        jschellSomeoneStoleMyAlias
                        >
                        When System.loadLibrary gets called it uses the web
                        module Classloader. This shared library doesn't get
                        unloaded in a timely way so the next time you deploy
                        (e.g this is especially common when you're debugging
                        in Netbeans) JVM rejects the load request because
                        it's already loaded in the old web module's
                        Classloader.

                        The solution is to place your library into a
                        CLASSPATH location which is accessible to the System
                        classloader (e.g. jre/lib/ext) and then use the
                        system classloader to actually load the class and use
                        it:
                        Loading it this way means that it will never be unloaded so hot deplolyments (common functionality in J2EE containers) will not work.
                        • 9. Re: Problem :  Native Library already loaded in another classloader
                          843829
                          Hi EveryBody:

                          i working in 2 servlets, these servlets import a login.jar library and login.jar call to Login.dll (a native Library)

                          then. when i start second servlet i have this message

                          java.lang.UnsatisfiedLinkError: Native Library dllLogin.dll already loaded in another classloader

                          i read about class loader and i know about a only one load for native library but i don't know how resolve this exception because both servlets need dllLogin.dll to validate the user

                          please anybody knows how or have any idea?

                          Kind Regards
                          Edson
                          --sorry i know. my english is very bad                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
                          • 10. Re: Problem :  Native Library already loaded in another classloader
                            843829
                            ok i find something:

                            Default Parent to all Casoader is the system classloader object, then if i can load my dll in system ClassLoader both servlets can call a native functions. :D

                            now... how can i do this?

                            Anybody knows?

                            Regards
                            Edson
                            • 11. Re: Problem :  Native Library already loaded in another classloader
                              jschellSomeoneStoleMyAlias
                              Problem 1: If you use the system class loader then you will not be able to reload the servlets while the system is running because you won't be able to reload the dll.

                              Problem 2: If the servlets exist in different classloaders then what happens if you need to reload one servlet to use a new dll but the other one must continue to use the old one?

                              If both servlets are in the same classloader and both will be reloaded then create a helper that actually loads the dll. Both servlets call that. The helper must be loaded via the class loader of the servlets. Keep a flag to indicate that the dll was already loaded and just load it once.
                              • 12. Re: Problem :  Native Library already loaded in another classloader
                                843829
                                Thanks jschell:

                                But servlets belong to two different and independent apps deployed on the same server, I think this servlets belong two different objects classloader(�?)

                                for helper you said something how: an auxiliar class to load this dll?, if you said this, this class exist, but because 2 servlets belong 2 different apps i get the error when second servlet call this auxiliar class

                                if someone are interest i find some clues on this page
                                updated:
                                sorry the rigth page is:
                                http://www.onjava.com/pub/a/onjava/2004/06/30/classloader2.html?page=1

                                search in the article : "Inside Class Loaders: Debugging"
                                "Native Library Problems" section

                                Kind Regards
                                Edson

                                Edited by: Edson_Chavez on Oct 25, 2007 10:19 AM

                                Edited by: Edson_Chavez on Oct 25, 2007 10:40 AM
                                • 13. Re: Problem :  Native Library already loaded in another classloader
                                  jschellSomeoneStoleMyAlias
                                  Please note that no matter what you do you can only load one version of the shared library.

                                  That has nothing to do with java.

                                  So your solutions are.
                                  1. Don't use the same shared library - use different names.
                                  2. Stop coding in the container and implement your own hot loader that will exist in the VM but not the container. I suspect you will need to expose the API via java class(es) as well. Then the servlets interact with that. Because it is in the VM rather than the container both servlets can interact with it and the servlets can be reloaded at anytime. Naturally since it is outside the container managing the reload of the dll will require different steps than via the container.
                                  3. Create a seperate application which exposes the shared library functionality via sockets. The servlets communicate with that via sockets. One advantage to this approach is that if the JNI code crashes it won't take down your container. You can also implement entirely in C/C++ and skip JNI entirely.