1616
1717package vanilla .java .affinity .impl ;
1818
19- import vanilla .java .affinity .IAffinity ;
20-
19+ import java .io .BufferedInputStream ;
20+ import java .io .ByteArrayOutputStream ;
21+ import java .io .File ;
22+ import java .io .FileInputStream ;
23+ import java .io .FileOutputStream ;
24+ import java .io .IOException ;
25+ import java .io .InputStream ;
26+ import java .security .DigestInputStream ;
27+ import java .security .MessageDigest ;
28+ import java .security .NoSuchAlgorithmException ;
29+ import java .util .Properties ;
2130import java .util .logging .Level ;
2231import java .util .logging .Logger ;
2332
33+ import vanilla .java .affinity .IAffinity ;
34+
2435/**
2536 * @author peter.lawrey
2637 */
2738public enum NativeAffinity implements IAffinity {
2839 INSTANCE ;
2940
3041 public static final boolean LOADED ;
31- private static final Logger LOGGER = Logger .getLogger (NativeAffinity .class .getName ());
42+ private static final Logger LOGGER = Logger .getLogger (NativeAffinity .class
43+ .getName ());
3244
3345 static {
34- boolean loaded ;
35- try {
36- System .loadLibrary ("affinity" );
37- loaded = true ;
38- } catch (UnsatisfiedLinkError ule ) {
39- if (LOGGER .isLoggable (Level .INFO ))
40- LOGGER .info ("Unable to find libaffinity in " + System .getProperty ("java.library.path" ) + " " + ule );
41- loaded = false ;
42- }
43- LOADED = loaded ;
46+ LOADED = loadAffinityNativeLibrary ();
4447 }
4548
4649 private native static long getAffinity0 ();
@@ -56,4 +59,204 @@ public long getAffinity() {
5659 public void setAffinity (long affinity ) {
5760 setAffinity0 (affinity );
5861 }
62+
63+ private static boolean initialize () {
64+ return loadAffinityNativeLibrary ();
65+ }
66+
67+ /**
68+ * Computes the MD5 value of the input stream
69+ *
70+ * @param input
71+ * @return
72+ * @throws IOException
73+ * @throws NoSuchAlgorithmException
74+ */
75+ private static String md5sum (InputStream input ) throws IOException {
76+ BufferedInputStream in = new BufferedInputStream (input );
77+
78+ try {
79+ MessageDigest digest = java .security .MessageDigest
80+ .getInstance ("MD5" );
81+ DigestInputStream digestInputStream = new DigestInputStream (in ,
82+ digest );
83+ for (; digestInputStream .read () >= 0 ;) {
84+
85+ }
86+ ByteArrayOutputStream md5out = new ByteArrayOutputStream ();
87+ md5out .write (digest .digest ());
88+ return md5out .toString ();
89+ } catch (NoSuchAlgorithmException e ) {
90+ throw new IllegalStateException ("MD5 algorithm is not available: "
91+ + e );
92+ } finally {
93+ in .close ();
94+ }
95+ }
96+
97+ /**
98+ * Extract the specified library file to the target folder
99+ *
100+ * @param libFolderForCurrentOS
101+ * @param libraryFileName
102+ * @param targetFolder
103+ * @return
104+ */
105+ private static boolean extractAndLoadLibraryFile (
106+ String libFolderForCurrentOS , String libraryFileName ,
107+ String targetFolder ) {
108+ String nativeLibraryFilePath = libFolderForCurrentOS + "/"
109+ + libraryFileName ;
110+ final String prefix = "javaaffinity-" + getVersion () + "-" ;
111+
112+ String extractedLibFileName = prefix + libraryFileName ;
113+ File extractedLibFile = new File (targetFolder , extractedLibFileName );
114+
115+ try {
116+ if (extractedLibFile .exists ()) {
117+ // test md5sum value
118+ String md5sum1 = md5sum (NativeAffinity .class
119+ .getResourceAsStream (nativeLibraryFilePath ));
120+ String md5sum2 = md5sum (new FileInputStream (extractedLibFile ));
121+
122+ if (md5sum1 .equals (md5sum2 )) {
123+ return loadNativeLibrary (targetFolder , extractedLibFileName );
124+ } else {
125+ // remove old native library file
126+ boolean deletionSucceeded = extractedLibFile .delete ();
127+ if (!deletionSucceeded ) {
128+ throw new IOException (
129+ "failed to remove existing native library file: "
130+ + extractedLibFile .getAbsolutePath ());
131+ }
132+ }
133+ }
134+
135+ // extract file into the current directory
136+ InputStream reader = NativeAffinity .class
137+ .getResourceAsStream (nativeLibraryFilePath );
138+ FileOutputStream writer = new FileOutputStream (extractedLibFile );
139+ byte [] buffer = new byte [1024 ];
140+ int bytesRead = 0 ;
141+ while ((bytesRead = reader .read (buffer )) != -1 ) {
142+ writer .write (buffer , 0 , bytesRead );
143+ }
144+
145+ writer .close ();
146+ reader .close ();
147+
148+ if (!System .getProperty ("os.name" ).contains ("Windows" )) {
149+ try {
150+ Runtime .getRuntime ()
151+ .exec (new String [] { "chmod" , "755" ,
152+ extractedLibFile .getAbsolutePath () })
153+ .waitFor ();
154+ } catch (Throwable e ) {
155+ }
156+ }
157+
158+ } catch (IOException e ) {
159+ // TODO something with exception - don't know what to do with it
160+ // using JUL
161+ if (LOGGER .isLoggable (Level .INFO )) {
162+ LOGGER .info ("Unable to extract libaffinity in " + targetFolder );
163+ }
164+ return false ;
165+ }
166+ return loadNativeLibrary (targetFolder , extractedLibFileName );
167+
168+ }
169+
170+ private static synchronized boolean loadNativeLibrary (String path ,
171+ String name ) {
172+ File libPath = new File (path , name );
173+ if (libPath .exists ()) {
174+
175+ try {
176+ System .load (libPath .getAbsolutePath ());
177+ return true ;
178+ } catch (UnsatisfiedLinkError e ) {
179+ // TODO something with e
180+ if (LOGGER .isLoggable (Level .INFO )) {
181+ LOGGER .info ("Unable to find libaffinity in " + path );
182+ }
183+ return false ;
184+ }
185+ } else {
186+ return false ;
187+ }
188+ }
189+
190+ private static boolean loadAffinityNativeLibrary () {
191+ if (LOADED ) {
192+ return LOADED ;
193+ }
194+ String affinityNativeLibraryName = System .mapLibraryName ("affinity" );
195+
196+ // Load the os-dependent library from a jar file
197+ String affinityNativeLibraryPath = "/vanilla/java/affinity/native/"
198+ + getNativeLibFolderPathForCurrentOS ();
199+
200+ if (NativeAffinity .class .getResource (affinityNativeLibraryPath
201+ + File .separator + affinityNativeLibraryName ) == null ) {
202+ return false ;
203+ }
204+
205+ // temporary library folder
206+ String tempFolder = new File (System .getProperty ("java.io.tmpdir" ))
207+ .getAbsolutePath ();
208+ // Try extracting the library from jar
209+ return extractAndLoadLibraryFile (affinityNativeLibraryPath ,
210+ affinityNativeLibraryName , tempFolder );
211+ }
212+
213+ private static String getVersion () {
214+
215+ InputStream versionFile = NativeAffinity .class
216+ .getResourceAsStream ("/META-INF/maven/vanilla.java/affinity/pom.properties" );
217+
218+ String version = "unknown" ;
219+ try {
220+ if (versionFile != null ) {
221+ Properties versionData = new Properties ();
222+ versionData .load (versionFile );
223+ version = versionData .getProperty ("version" , version );
224+ version = version .trim ().replaceAll ("[^0-9\\ .]" , "" );
225+ }
226+ } catch (IOException e ) {
227+ if (LOGGER .isLoggable (Level .INFO )) {
228+ LOGGER .info ("Unable to find libaffinity version from maven metadata" );
229+ }
230+ }
231+ return version ;
232+ }
233+
234+ private static String getNativeLibFolderPathForCurrentOS () {
235+ return getOSName () + "/" + getArchName ();
236+ }
237+
238+ private static String getOSName () {
239+ return translateOSNameToFolderName (System .getProperty ("os.name" ));
240+ }
241+
242+ private static String getArchName () {
243+ return translateArchNameToFolderName (System .getProperty ("os.arch" ));
244+ }
245+
246+ private static String translateOSNameToFolderName (String osName ) {
247+ if (osName .contains ("Windows" )) {
248+ return "Windows" ;
249+ } else if (osName .contains ("Mac" )) {
250+ return "Mac" ;
251+ } else if (osName .contains ("Linux" )) {
252+ return "Linux" ;
253+ } else {
254+ return osName .replaceAll ("\\ W" , "" );
255+ }
256+ }
257+
258+ private static String translateArchNameToFolderName (String archName ) {
259+ return archName .replaceAll ("\\ W" , "" );
260+ }
261+
59262}
0 commit comments