001/* [{ 002Copyright 2007, 2008 Nicolas Carranza <nicarran at gmail.com> 003 004This file is part of jpen. 005 006jpen is free software: you can redistribute it and/or modify 007it under the terms of the GNU Lesser General Public License as published by 008the Free Software Foundation, either version 3 of the License, 009or (at your option) any later version. 010 011jpen is distributed in the hope that it will be useful, 012but WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014GNU Lesser General Public License for more details. 015 016You should have received a copy of the GNU Lesser General Public License 017along with jpen. If not, see <http://www.gnu.org/licenses/>. 018}] */ 019package jpen.provider; 020 021import java.security.AccessController; 022import java.security.PrivilegedAction; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.HashMap; 026import java.util.logging.Logger; 027import java.util.Map; 028import java.util.prefs.Preferences; 029import jpen.internal.BuildInfo; 030import jpen.PenManager; 031 032public class NativeLibraryLoader{ 033 private static final Logger L=Logger.getLogger(NativeLibraryLoader.class.getName()); 034 // static{L.setLevel(Level.ALL); } 035 private static String PREFERENCE_KEY$ARCHITECTURE="NativeLibraryLoader.architecture"; 036 037 private final Map<String, Collection<String>> dataModelToArchitectures=new HashMap<String, Collection<String>>(); 038 private boolean loaded; 039 public final int nativeVersion; 040 041 public NativeLibraryLoader(int nativeVersion){ 042 this(new String[]{""}, nativeVersion); 043 } 044 045 public NativeLibraryLoader(String[] architectures, int nativeVersion){ 046 this(architectures, architectures, nativeVersion); 047 } 048 049 public NativeLibraryLoader(){ 050 this(new String[]{""}); 051 } 052 053 public NativeLibraryLoader(String[] architectures){ 054 this(architectures, architectures); 055 } 056 057 public NativeLibraryLoader(String[] architectures32, String[] architectures64){ 058 this(architectures32, architectures64, 0); 059 } 060 061 public NativeLibraryLoader(String[] architectures32, String[] architectures64, int nativeVersion){ 062 dataModelToArchitectures.put("32", Arrays.asList(architectures32)); 063 dataModelToArchitectures.put("64", Arrays.asList(architectures64)); 064 this.nativeVersion=nativeVersion; 065 } 066 067 public synchronized void load(){ 068 if(!loaded){ 069 L.finest("v"); 070 Throwable loadExceptionCause=doLoad(); 071 loaded=true; 072 if(loadExceptionCause!=null){ 073 L.info("no suitable JNI library found"); 074 throw new LoadException(loadExceptionCause); 075 } 076 L.finest("^"); 077 } 078 } 079 080 /** 081 @return the last load exception or {@code null} if one matching library was found and loaded. 082 */ 083 private Throwable doLoad(){ 084 String preferredArchitecture=getPreferredArchitecture(); 085 if(preferredArchitecture!=null){ 086 try{ 087 loadLibrary(preferredArchitecture, nativeVersion); 088 return null; 089 }catch(Throwable t){ 090 setPreferredArchitecture(null); 091 } 092 } 093 094 Throwable loadExceptionCause=null; 095 String dataModel=getJavaVMDataModel(); 096 Collection<String> architectures=dataModelToArchitectures.get(dataModel); 097 if(architectures==null) 098 throw new IllegalStateException("Unsupported data model: "+dataModel); 099 for(String architecture: architectures){ 100 try{ 101 loadLibrary(architecture, nativeVersion); 102 setPreferredArchitecture(architecture); 103 loadExceptionCause=null; 104 break; 105 }catch(Throwable t){ 106 setPreferredArchitecture(null); 107 loadExceptionCause=t; 108 } 109 } 110 return loadExceptionCause; 111 } 112 113 public static class LoadException 114 extends RuntimeException{ 115 LoadException(Throwable cause){ 116 super(cause); 117 } 118 } 119 120 private static String getJavaVMDataModel(){ 121 String dataModel=AccessController.doPrivileged(new PrivilegedAction<String>() { 122 //@Override 123 public String run() { 124 return System.getProperty("sun.arch.data.model"); 125 } 126 }); 127 return dataModel==null? "32": dataModel; 128 } 129 130 private String getPreferredArchitecture(){ 131 return AccessController.doPrivileged(new PrivilegedAction<String>(){ 132 //@Override 133 public String run(){ 134 String override=System.getProperty("jpen.provider.architecture"); 135 if(override!=null) 136 return override; 137 Preferences preferences=Preferences.userNodeForPackage(NativeLibraryLoader.class); 138 return preferences.get(PREFERENCE_KEY$ARCHITECTURE, null); 139 } 140 }); 141 } 142 143 private static void setPreferredArchitecture(final String architecture){ 144 AccessController.doPrivileged(new PrivilegedAction<Object>(){ 145 //@Override 146 public String run(){ 147 Preferences preferences=Preferences.userNodeForPackage(NativeLibraryLoader.class); 148 if(architecture==null){ 149 preferences.remove(PREFERENCE_KEY$ARCHITECTURE); 150 } 151 else{ 152 preferences.put(PREFERENCE_KEY$ARCHITECTURE, architecture); 153 L.info("preferred architecture set"); 154 } 155 return null; 156 } 157 }); 158 } 159 160 public static final void loadLibrary(final String architecture, final int nativeVersion) { 161 AccessController.doPrivileged(new PrivilegedAction<Object>() { 162 final String jniLibName=getJniLibName(architecture, nativeVersion); 163 //@Override 164 public Object run() { 165 try{ 166 L.info("loading JPen "+PenManager.getJPenFullVersion()+" JNI library: "+jniLibName+" ..."); 167 System.loadLibrary(jniLibName); 168 L.info(jniLibName+" loaded"); 169 return null; 170 }catch(RuntimeException ex){ 171 logOnFail(); 172 throw ex; 173 }catch(Error ex){ 174 logOnFail(); 175 throw ex; 176 } 177 } 178 private void logOnFail(){ 179 L.info(jniLibName+" couldn't be loaded"); 180 } 181 }); 182 } 183 184 private static final String getJniLibName(String architecture, int nativeVersion) { 185 StringBuilder jniLibName=new StringBuilder(64); 186 jniLibName.append(BuildInfo.getModuleId()); 187 jniLibName.append("-"); 188 jniLibName.append(BuildInfo.getVersion()); 189 if(nativeVersion!=0){ // backwards compatibility 190 jniLibName.append("-"); 191 jniLibName.append(nativeVersion); 192 } 193 if(architecture!=null && architecture.trim().length()!=0){ 194 jniLibName.append("-"); 195 jniLibName.append(architecture); 196 } 197 return jniLibName.toString(); 198 } 199}