/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.apmrouter.nativex; import org.helios.apmrouter.util.IO; import org.helios.apmrouter.util.ReadableWritableByteChannelBuffer; import org.hyperic.sigar.Cpu; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarLoader; import java.io.*; import java.net.URL; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * <p>Title: NativeLibLoader</p> * <p>Description: Utility class to load a native library acquired as a resource from the classpath.</p> * <p>Attempts to load the named library as follows:<ol> * <li>Uses a straight load on the offchance that the library is in the <code>{java.library.path}</code></li> * <li>Attempts to read the lib file from <code>./src/main/resources/META-INF/native/</code> which is where it might be found when in dev mode</li> * <li>Looks in <code>{user.home}/.jzab/native</code></li> * <li>Writes out a temp file to <code>{java.io.tmpdir}</code>, loads it and schedules it for deletion on shutdown.</li> * </ol> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.jzab.plugin.nativex.NativeLibLoader</code></p> */ public class NativeLibLoader { /** The approximated location of the native libraries if not loaded from the JAR (usually during dev) */ private static final String NO_JAR_NATIVE_DIR = "./src/main/resources/META-INF/native/"; /** The classloader resource prefix for loading the native lib from the jar */ private static final String NATIVE_DIR_PREFIX = "META-INF/native/"; /** * Loads the native library for the current environment */ public static String loadLib() { String libName = getLibNameQuietly(); final PrintStream out = System.out; final PrintStream err = System.err; // === Try lib path === try { System.setErr(IO.NULL_PRINTSTREAM); System.setOut(IO.NULL_PRINTSTREAM); Sigar.load(); return new Sigar().getNativeLibrary().getAbsolutePath(); } catch (Throwable e) { /* No Op */ } finally { System.setErr(err); System.setOut(out); } // === Look in jZab home === File dir = new File(System.getProperty("user.home") + File.separator + ".apmrouter" + File.separator + "native" ); if(dir.exists() && dir.isDirectory()) { File libFile = new File(dir.getAbsolutePath() + File.separator + libName); if(libFile.exists()) { try { System.load(libFile.getAbsolutePath()); return libFile.getAbsolutePath(); } catch (Throwable e) {} } } // === Look in Dev Location === File devFile = new File(NO_JAR_NATIVE_DIR + libName); try { System.load(devFile.getAbsolutePath()); return devFile.getAbsolutePath(); } catch (Throwable e) { } // === Extract and write tmp=== String libFileName = extractNativeLib(libName); System.load(libFileName); return libFileName; } /** * Reads the native library from the source and writes to the temp file. * @return The file name where the lib was written * @throws IOException */ private static String extractNativeLib(String nativeLibraryName) { FileOutputStream fos = null; InputStream is = null; try { byte[] buffer = new byte[4096]; int bytesWritten = 0; int totalBytesWritten = 0; // ClassLoader ncl = NativeLibLoader.class.getClassLoader(); // URL ncs = NativeLibLoader.class.getProtectionDomain().getCodeSource().getLocation(); // System.out.println("\n\tNativeLibLoader:\n\t\tClassLoader:" + ncl + "\n\t\tCodeSource:" + ncs + "\n"); if(NativeLibLoader.class.getClassLoader()==null) { is = NativeLibLoader.class.getResourceAsStream("META-INF/native/" + nativeLibraryName); } else { is = NativeLibLoader.class.getClassLoader().getResourceAsStream("META-INF/native/" + nativeLibraryName); } if(is==null) { try { String jarUrl = "jar:" + NativeLibLoader.class.getProtectionDomain().getCodeSource().getLocation().toString() + "!/META-INF/native/" + nativeLibraryName; URL jurl = new URL(jarUrl); is = jurl.openStream(); } catch (Exception ex) {} } if(is==null) { try { is = ClassLoader.getSystemResourceAsStream("META-INF/native/" + nativeLibraryName); } catch (Exception ex) {} } if(is==null) { throw new RuntimeException("Failed to load resource [META-INF/native/" + nativeLibraryName + "]", new Throwable()); } File tmpFile = File.createTempFile("jzabTmp", nativeLibraryName); SnipeFilesRepo.getInstance().bypass(tmpFile); fos = new FileOutputStream(tmpFile, true); while((bytesWritten=is.read(buffer))!=-1) { fos.write(buffer, 0, bytesWritten); totalBytesWritten += bytesWritten; } fos.close(); is.close(); //ByteBufferStreams buff = ByteBufferStreams.readInputStreamDirect(NativeLibLoader.class.getClassLoader().getResourceAsStream("META-INF/native/" + nativeLibraryName)); // int size = buff.position(); // LOG.debug("Read [{}] bytes into buffer [{}]", size, buff); // buff.flip(); return tmpFile.getAbsolutePath(); } catch (Exception e) { throw new RuntimeException("Unable to load native library [" + nativeLibraryName + "]", e); } finally { try { is.close(); } catch (Exception e) {} try { fos.close(); } catch (Exception e) {} } } private static String getLibNameQuietly() { final PrintStream err = System.err; final PrintStream out = System.out; // Redirects err to a null output stream System.setErr(IO.NULL_PRINTSTREAM); System.setOut(IO.NULL_PRINTSTREAM); try { return SigarLoader.getNativeLibraryName(); } finally { System.setErr(err); System.setOut(out); } } /** * Adds a file to be sniped * @param name The name of the file */ static void deleteFile(CharSequence name) { SnipeFilesRepo.getInstance().bypass(new File(name.toString())); } /** * Adds a file to be sniped * @param file The file */ static void deleteFile(File file) { SnipeFilesRepo.getInstance().bypass(file); } public static void main(String[] args) { // LOG.info("Snipe Test"); // try { // for(int i = 0; i < 5; i++) { // File f = File.createTempFile("Foo", "ffo.tmp"); // SnipeFilesRepo.getInstance().bypass(f); // } // } catch (Exception e) { // LOG.error("Failed", e); // } // LOG.info("Test System Load"); // loadLib(); extractNativeLib(getLibNameQuietly()); Cpu cpu = APMSigar.getInstance().getCpu(); } /** * <p>Title: SnipeFilesRepo</p> * <p>Description: Maintains a persistent repository of files that failed to be deleted during that last shutdown and will be deleted on the next startup</p> * <p>The serialized list of files is saved to <b><code>${user.home}/.heliosJzabSnipeFiles.ser</code></b>. * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.jzab.plugin.nativex.NativeLibLoader.SnipeFilesRepo</code></p> */ static class SnipeFilesRepo extends Thread { /** The names of the files that need to be snipped */ private final Set<String> filesToSnipe = new HashSet<String>(); /** The singleton instance */ private static volatile SnipeFilesRepo instance = null; /** The singleton instance ctor lock */ private static final Object lock = new Object(); /** The name of the file where this file will be saved and loaded from */ public static final File serFile = new File(System.getProperty("user.home") + File.separator + ".heliosJzabSnipeFiles.ser"); /** * Acquires the singleton instance * @return the singleton instance */ public static SnipeFilesRepo getInstance() { if(instance==null) { synchronized(lock) { if(instance==null) { instance = new SnipeFilesRepo(); } } } return instance; } /** * {@inheritDoc} * @see java.lang.Thread#run() */ @Override public void run() { save(); } private SnipeFilesRepo() { Runtime.getRuntime().addShutdownHook(this); load(); onStart(); } /** * Deletes sniped files on startup */ private void onStart() { for(Iterator<String> iter = filesToSnipe.iterator(); iter.hasNext();) { File f = new File(iter.next()); if(f.exists()) { if(!f.delete()) { continue; } } iter.remove(); } } /** * Adds a file to be sniped * @param name The name of the file */ public void addFile(CharSequence name) { if(name!=null) { String nm = name.toString(); File f = new File(nm); if(f.exists()) { if(!f.delete()) { filesToSnipe.add(f.getAbsolutePath()); save(); } } } } /** * Testing hook to add a file without attempting to delete * @param f The file to add */ public void bypass(File f) { filesToSnipe.add(f.getAbsolutePath()); save(); } /** * Adds a file to be sniped * @param file The file */ public void addFile(File file) { if(file!=null) { if(file.exists()) { if(!file.delete()) { filesToSnipe.add(file.getAbsolutePath()); save(); } } } } /** * Attempts to read the snipe file */ @SuppressWarnings("unchecked") private void load() { if(serFile.canRead()) { FileChannel fc = null; try { int size = (int)serFile.length(); if(size<1) return; ReadableWritableByteChannelBuffer buff = ReadableWritableByteChannelBuffer.newDirectDynamic(size); fc = new RandomAccessFile(serFile, "rw").getChannel(); ByteBuffer bb = ByteBuffer.allocate(size); long bt = fc.read(bb); bb.flip(); buff.write(bb); Set<String> set = (Set<String>)new ObjectInputStream(buff.asInputStream()).readObject(); for(String s: set) { if(s!=null && !s.trim().isEmpty()) { filesToSnipe.add(s); } } } catch (Exception e) { //serFile.delete(); } finally { try { if(fc.isOpen()) fc.close(); } catch (Exception e) {} } } } /** * Saves the snipe file. Yeah, it's not an XA transaction so if the write fails after the delete, then KAPUT. */ private synchronized void save() { if(filesToSnipe.isEmpty()) return; FileChannel fc = null; int size = 0; for(String s: filesToSnipe ) { size += s.getBytes().length; } try { RandomAccessFile raf = new RandomAccessFile(serFile, "rw"); fc = raf.getChannel(); if(serFile.exists()) { fc.truncate(0); } ReadableWritableByteChannelBuffer buff = ReadableWritableByteChannelBuffer.newDirectDynamic(size); ObjectOutputStream oos = new ObjectOutputStream(buff.asOutputStream()); oos.writeObject(filesToSnipe); oos.flush(); buff.asOutputStream().flush(); //long bt = fc.transferTo(0, buff.writerIndex(), buff); long bt = fc.write(buff.toByteBuffer()); fc.force(true); fc.close(); } catch (Exception e) { } finally { try { if(fc.isOpen()) fc.close(); } catch (Exception e) {} } } } }