1 /*
2  * This file is part of the OpenNMS(R) Application.
3  *
4  * OpenNMS(R) is Copyright (C) 2002-2015 The OpenNMS Group, Inc.  All rights reserved.
5  * OpenNMS(R) is a derivative work, containing both original code, included code and modified
6  * code that was published under the GNU General Public License. Copyrights for modified
7  * and included code are below.
8  *
9  * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
10  *
11  * Modifications:
12  *
13  * 2007 May 21: Better logging around loading of the shared library, format
14  *              code, and eliminate m_loaded member. - dj@opennms.org
15  * 2003 Jan 31: Cleaned up some unused imports.
16  *
17  * Orignal code base Copyright (C) 1999-2001 Oculan Corp.  All rights reserved.
18  *
19  * This program is free software; you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation; either version 2 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program; if not, write to the Free Software
31  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32  *
33  * For more information contact:
34  *      OpenNMS Licensing       <license@opennms.org>
35  *      http://www.opennms.org/
36  *      http://www.opennms.com/
37  */
38 package org.opennms.netmgt.rrd.rrdtool;
39 
40 import java.io.File;
41 import java.util.LinkedHashSet;
42 import java.util.Set;
43 
44 /**
45  * This is a singleton class which provides an interface through which RRD
46  * (Round Robin Database) functions (rrd_create(), rrd_update(), and others) can
47  * be accessed from Java code.
48  *
49  * <pre>
50  * The native method 'launch()' takes a single argument which is a
51  *  RRD command string of similar format to what RRDtool takes.  Please
52  *  note the following examples:
53  *
54  *  	&quot;create test.rrd --start N DS:ifOctetsIn:COUNTER:600:U:U \
55  *        	RRA:AVERAGE:0.5:1:24&quot;
56  *
57  *  	&quot;update test.rrd --template:ifOctetsIn N:123456789&quot;
58  *
59  *  Refer to www.rrdtool.org for additional examples and information on
60  *  the format of rrdtool command strings.
61  *
62  *  Currently only 'create', 'update', and 'fetch' commands are supported.
63  * </pre>
64  *
65  * @author <A HREF="mailto:mike@opennms.org">Mike </A>
66  * @author <A HREF="http://www.opennms.org/">OpenNMS </A>
67  *
68  * @version 1.1.1.1
69  *
70  */
71 public final class Interface {
72     private static final String LIBRARY_NAME = "jrrd";
73     private static final String PROPERTY_NAME = "opennms.library.jrrd";
74 
75     /**
76      * The singleton instance of the interface
77      */
78     private static Interface s_singleton = null;
79 
80     /**
81      * Native method implemented in C which provides an interface to the
82      * lower-level RRD functions
83      *
84      * WARNING: The RRD C api (rrd_update(), rrd_create(), etc...) relies on
85      * getopt() & therefore is not thread safe. This method is therefore
86      * synchronized in order to prevent more than one thread access at a time.
87      *
88      * @param cmd
89      *            RRDtool style command string to be executed. Currently
90      *            supported RRD commands are: 'create' - calls rrd_create()
91      *            'update' - calls rrd_update() 'fetch' - calls rrd_fetch()
92      *
93      * @return array of Java String objects
94      *
95      * In the case of rrd_fetch() the returned String array has the following
96      * characteristics:
97      *
98      * String[0] = error text if command failed or NULL if successful String[1] =
99      * for 'fetch' cmd: data source names String[2..n] = for 'fetch' cmd:
100      * retrieved data rows for each interval between start and end parms
101      */
launch(String cmd)102     public static synchronized native String[] launch(String cmd);
103 
104     /**
105      * Load the jrrd library and create the singleton instance of the interface.
106      *
107      * @throws SecurityException
108      *             if we don't have permission to load the library
109      * @throws UnsatisfiedLinkError
110      *             if the library doesn't exist
111      */
init()112     public static synchronized void init() throws SecurityException, UnsatisfiedLinkError {
113         if (isLoaded()) {
114             // init already called - return
115             // to reload, reload() will need to be called
116             return;
117         }
118 
119         setInstance(new Interface());
120     }
121 
isLoaded()122     private static boolean isLoaded() {
123         return s_singleton != null;
124     }
125 
126     /**
127      * Reload the jrrd library and create the singleton instance of the
128      * interface.
129      *
130      * @throws SecurityException
131      *             if we don't have permission to load the library
132      * @throws UnsatisfiedLinkError
133      *             if the library doesn't exist
134      */
reload()135     public static synchronized void reload() throws SecurityException, UnsatisfiedLinkError {
136         setInstance(null);
137 
138         init();
139     }
140 
141     /**
142      * Constructor. Responsible for loading the jrrd shared/dynamic link library
143      * which contains the implementation of the 'launch()' native method.
144      *
145      * @throws SecurityException
146      *             if we don't have permission to load the library
147      * @throws UnsatisfiedLinkError
148      *             if the library doesn't exist
149      */
Interface()150     private Interface() throws SecurityException, UnsatisfiedLinkError {
151         String jniPath = System.getProperty(PROPERTY_NAME);
152         try {
153             debug("System property '" + PROPERTY_NAME + "' set to '" + System.getProperty(PROPERTY_NAME) + ".  Attempting to load " + LIBRARY_NAME + " library from this location.");
154             System.load(jniPath);
155         } catch (final Throwable t) {
156             debug("System property '" + PROPERTY_NAME + "' not set.  Attempting to find library.");
157             System.loadLibrary(LIBRARY_NAME);
158         }
159         info("Successfully loaded " + LIBRARY_NAME + " library.");
160     }
161 
loadLibrary()162     private static void loadLibrary() {
163         final Set<String> searchPaths = new LinkedHashSet<String>();
164 
165         if (System.getProperty("java.library.path") != null) {
166             for (final String entry : System.getProperty("java.library.path").split(File.pathSeparator)) {
167                 searchPaths.add(entry);
168             }
169         }
170 
171         for (final String entry : new String[] {
172                 "/usr/lib64/jni",
173                 "/usr/lib64",
174                 "/usr/local/lib64",
175                 "/usr/lib/jni",
176                 "/usr/lib",
177                 "/usr/local/lib"
178         }) {
179             searchPaths.add(entry);
180         }
181 
182         for (final String path : searchPaths) {
183             for (final String prefix : new String[] { "", "lib" }) {
184                 for (final String suffix : new String[] { ".jnilib", ".dylib", ".so" }) {
185                     final File f = new File(path + File.separator + prefix + LIBRARY_NAME + suffix);
186                     if (f.exists()) {
187                         try {
188                             System.load(f.getCanonicalPath());
189                             return;
190                         } catch (final Throwable t) {
191                             // failed, keep looping and hope for a match
192                         }
193                     }
194                 }
195             }
196         }
197         debug("Unable to locate '" + LIBRARY_NAME + "' in common paths.  Attempting System.loadLibrary() as a last resort.");
198         System.loadLibrary(LIBRARY_NAME);
199     }
200 
201     /**
202      * Return the singleton instance of this class.
203      *
204      * @return The current instance.
205      *
206      * @throws java.lang.IllegalStateException
207      *             Thrown if the interface has not yet been initialized.
208      */
getInstance()209     public static synchronized Interface getInstance() {
210         assertState(isLoaded(), "The RRD JNI interface has not been initialized");
211 
212         return s_singleton;
213     }
214 
setInstance(Interface instance)215     public static synchronized void setInstance(Interface instance) {
216         s_singleton = instance;
217     }
218 
debug(String msg)219     public static void debug(String msg) {
220         System.err.println("[DEBUG] "+msg);
221     }
222 
info(String msg)223     public static void info(String msg) {
224         System.err.println("[INFO] "+msg);
225     }
226 
assertState(boolean check, String msg)227     public static void assertState(boolean check, String msg) {
228         if (!check) {
229             throw new IllegalStateException(msg);
230         }
231     }
232 
233     /**
234      * Debug purposes only
235      */
main(String[] argv)236     public static void main(String[] argv) {
237         try {
238             // initialize the interface
239             Interface.reload();
240 
241             // build create command
242             // Build RRD create command prefix
243             String filename = argv[0];
244             System.out.println("filename=" + filename);
245             String cmd = "create \"" + filename + "\" --start N" + " --step 900 DS:test:COUNTER:900:0:100 RRA:MIN:0.5:1:1000";
246 
247             // issue rrd command
248             System.out.println("issuing RRD cmd: " + cmd);
249             Interface.launch(cmd);
250             System.out.println("command completed.");
251         } catch (Throwable t) {
252             System.err.println("unexpected error, reason: " + t);
253             t.printStackTrace();
254         }
255     }
256 }
257