Generated on Fri Oct 6 16:26:43 2006 for Gecode/J by doxygen 1.4.7

LibLoader.java

Go to the documentation of this file.
00001 /*
00002 ** Written by Gregory L. Guerin, April 2002.
00003 ** Freely released AS-IS AND WITHOUT WARRANTY OF ANY KIND into the public domain.
00004 */
00005 
00006 package glguerin.util;
00007 
00008 import java.lang.reflect.*;
00009 import java.io.*;
00010 
00011 
00012 // --- Revision History ---
00013 // 29Apr2002 GLG  create a work-around for Cocoa-Java's obstinate JNI-lib stupidity
00014 // 02May2002 GLG  revise how getTinker() decides which imp to return
00015 // 02May2002 GLG  copy to tenjni package
00016 // 03May2002 GLG  revise to keep a thread-safe singleton
00017 // 21May2002 GLG  name changes
00018 // 05Nov2002 GLG  rework substantially, calling NSBundle with reflection
00019 // 18Nov2002 GLG  add diagnostics to System.err, and null-File checks
00020 // 05Dec2002 GLG  revise how property is used in makeLoader()
00021 // 10Dec2002 GLG  change package and properties
00022 // 09Aug2003 GLG  change UnsatisfiedLinkError message in load()
00023 
00024 
00076 public class LibLoader
00077 {
00083         public static boolean isVerbose = Boolean.getBoolean( "glguerin.util.LibLoader.debug" );
00084 
00085 
00087         private static LibLoader knownLoader;
00088 
00089 
00094         public static synchronized LibLoader
00095         getLoader()
00096         {
00097                 if ( knownLoader == null )
00098                 {
00099                         knownLoader = makeLoader();
00100 //                      System.err.println( "# LibLoader: " + knownLoader.getClass() + ", using " + knownLoader );
00101                 }
00102                 return ( knownLoader );
00103         }
00104 
00105 
00110         public static LibLoader
00111         makeLoader()
00112         {
00113                 // Look for a system property overtly specifying a class name.
00114                 // Absent such a property, the default is to use this LibLoader, which has a
00115                 // fallback strategy that can call a Cocoa NSBundle using reflection.
00116                 String className = System.getProperty( "glguerin.util.LibLoader.imp", "" );
00117 
00118                 if ( className != null  &&  className.length() > 0 )
00119                 {
00120                         try
00121                         {
00122                                 // If any of this throws any kind of exception, return a vanilla LibLoader instead.
00123                                 return ( (LibLoader) Class.forName( className ).newInstance() );
00124                         }
00125                         catch ( ThreadDeath mustRethrow )
00126                         {  throw mustRethrow;  }
00127                         catch ( Throwable everythingElse )
00128                         {  /* FALL THROUGH */  }
00129                 }
00130 
00131                 // Return a vanilla LibLoader instance, hoping for the best.
00132                 return ( new LibLoader() );
00133         }
00134 
00135 
00136 
00137 
00142         protected final File dirFallback;
00143 
00145         public
00146         LibLoader()
00147         {
00148                 dirFallback = makeFallbackDir();
00149                 blab();
00150         }
00151 
00153         protected void
00154         blab()
00155         {
00156                 if ( isVerbose )
00157                         System.err.println( "# LibLoader dir: " + dirFallback );
00158         }
00159         
00160 
00169         public void
00170         loadLibrary( String libName )
00171         {
00172                 // We're told not to catch Errors, but there's no other way to find out if a linkage failed.
00173                 // Failures other than UnsatisfiedLinkError get thrown up to caller.
00174                 try
00175                 {  System.loadLibrary( libName );  }
00176                 catch ( UnsatisfiedLinkError failure )
00177                 {  this.load( libName, dirFallback );  }  // use fallback scheme.
00178         }
00179 
00200         protected void
00201         load( String libName, File location )
00202         {
00203                 if ( location == null )
00204                         throw new UnsatisfiedLinkError( "Don't know how to load JNI library: " + libName );
00205 
00206                 // This hand-made scheme follows the usual Mac OS X Java JNI-lib naming pattern.
00207                 // Under Java 2 it could use System.mapLibraryName().
00208                 String libPath = new File( location, "lib" + libName + ".jnilib" ).getAbsolutePath();
00209 
00210                 if ( isVerbose )
00211                         System.err.println( "# LibLoader: trying " + libPath );
00212 
00213                 System.load( libPath );
00214         }
00215 
00216 
00226         protected File
00227         makeFallbackDir()
00228         {
00229                 try
00230                 {
00231                         // Both mainBundle() and resourcePath() take no args.
00232                         // The empty Class[] represents the method signature.
00233                         // The empty Object[] represents the invoked method's args.
00234                         Class[] noArgs = new Class[ 0 ];
00235                         Object[] nothing = new Object[ 0 ];
00236 
00237                         // Try to load an NSBundle class and reflectively invoke its methods.
00238                         // NSBundle.mainBundle() is a public static method returning an NSBundle.
00239                         // NSBundle.resourcePath() is a public instance method called on the NSBundle instance.
00240                         Class bundleClass = Class.forName( "com.apple.cocoa.foundation.NSBundle" );
00241 
00242                         if ( isVerbose )
00243                                 System.err.println( "# LibLoader got NSBundle: " + bundleClass );
00244 
00245                         // First, get the NSBundle.  A static invocation doesn't care what the target object is.
00246                         // Second, use the NSBundle to invoke the bundlePath() method.
00247                         Object theNSBundle = bundleClass.getMethod( "mainBundle", noArgs ).invoke( null, nothing );
00248                         Object thePath = bundleClass.getMethod( "resourcePath", noArgs ).invoke( theNSBundle, nothing );
00249 
00250                         // The resultant value of thePath should be a String holding a full pathname.
00251                         // Don't cast it, just use its toString() method to assemble an appropriate File.
00252                         return ( new File( thePath.toString(), "Java" ) );
00253                 }
00254                 catch ( ClassNotFoundException why )
00255                 {  /* FALL THRU */  }
00256                 catch ( NoSuchMethodException why )
00257                 {  /* FALL THRU */  }
00258                 catch ( IllegalAccessException why )
00259                 {  /* FALL THRU */  }
00260                 catch ( IllegalArgumentException why )
00261                 {  /* FALL THRU */  }
00262                 catch ( InvocationTargetException why )
00263                 {  /* FALL THRU */  }
00264 
00265                 // Here's where a "last-chance directory" can be returned.
00266                 // One option is the "user.dir" property, or ".".
00267                 // There are security implications to using the current directory as the location of native-code libraries.
00268                 // Another slightly safer option would be the "java.home" property, or some location therein.
00269                 // I've avoided all those in favor of simply returning null, so load() will fail
00270                 // if there's no NSBundle available.
00271                 return ( null );
00272         }
00273 
00274 
00276         public String
00277         toString()
00278         {  return ( "System.loadLibrary()" + File.pathSeparator + dirFallback );  }
00279 
00280 }