1 package org.libvirt;
2 
3 import org.libvirt.jna.Libvirt;
4 import org.libvirt.jna.LibvirtQemu;
5 import org.libvirt.jna.Libvirt.VirEventTimeoutCallback;
6 import org.libvirt.jna.CString;
7 import static org.libvirt.ErrorHandler.processError;
8 
9 import com.sun.jna.Native;
10 import com.sun.jna.Pointer;
11 import com.sun.jna.ptr.LongByReference;
12 
13 import java.util.concurrent.atomic.AtomicBoolean;
14 import java.util.concurrent.atomic.AtomicInteger;
15 
16 /**
17  * This class represents an instance of the JNA mapped libvirt
18  * library.
19  *
20  * The library will get loaded when first accessing this class.
21  *
22  * Additionally, this class contains internal methods to ease
23  * implementing the public API.
24  */
25 public final class Library {
26     private static AtomicBoolean runLoop = new AtomicBoolean();
27     private static AtomicInteger timerID = new AtomicInteger(-1);
28     private static VirEventTimeoutCallback timer = new VirEventTimeoutCallback() {
29             @Override
30             public void tick(final int id, final Pointer p) {
31                 // disable myself again right after being triggered
32                 libvirt.virEventUpdateTimeout(id, -1);
33             }
34         };
35 
36     static final Libvirt libvirt;
37     static final LibvirtQemu libvirtQemu;
38 
39     // an empty string array constant
40     // prefer this over creating empty arrays dynamically.
41     static final String[] NO_STRINGS = {};
42 
43     // Load the native part
44     static {
45         libvirt = Libvirt.INSTANCE;
46         try {
libvirt.virInitialize()47             processError(libvirt.virInitialize());
48         } catch (Exception e) {
49             e.printStackTrace();
50         }
51         try {
52             libvirtQemu = getVersion() > 9010 ? LibvirtQemu.INSTANCE : null;
53         } catch (LibvirtException e) {
54             throw new RuntimeException("libvirt error get version", e);
55         }
56     }
57 
Library()58     private Library() {}
59 
60     /**
61      * Returns the version of the native libvirt library.
62      *
63      * @return major * 1,000,000 + minor * 1,000 + release
64      * @throws LibvirtException
65      */
getVersion()66     public static long getVersion() throws LibvirtException {
67         LongByReference libVer = new LongByReference();
68         processError(libvirt.virGetVersion(libVer, null, null));
69         return libVer.getValue();
70     }
71 
72     /**
73      * Free memory pointed to by ptr.
74      */
free(final Pointer ptr)75     static void free(final Pointer ptr) {
76         Native.free(Pointer.nativeValue(ptr));
77         Pointer.nativeValue(ptr, 0L);
78     }
79 
80     /**
81      * Convert the given array of UTF-8 encoded C-Strings to an array
82      * of Strings.
83      *
84      * \note The memory used by the elements of the original array
85      *       is freed.
86      */
toStringArray(final CString[] cstrarr, final int size)87     static String[] toStringArray(final CString[] cstrarr, final int size) {
88         int i = 0;
89         try {
90             final String[] result = new String[size];
91             for (; i < size; ++i) {
92                 result[i] = cstrarr[i].toString();
93             }
94             return result;
95         } catch (RuntimeException e) {
96             for (; i < size; ++i) {
97                 if (cstrarr[i] != null) {
98                     cstrarr[i].free();
99                 }
100             }
101             throw e;
102         }
103     }
104 
105     /**
106      * Initialize the event loop.
107      *
108      * Registers a default event loop implementation based on the
109      * poll() system call.
110      * <p>
111      * Once registered, the application has to invoke
112      * {@link #processEvent} in a loop or call {@link #runEventLoop}
113      * in another thread.
114      * <p>
115      * Note: You must call this function <em>before</em> connecting to
116      *       the hypervisor.
117      *
118      * @throws LibvirtException on failure
119      *
120      * @see #processEvent
121      * @see #runLoop
122      */
initEventLoop()123     public static void initEventLoop() throws LibvirtException {
124         if (timerID.get() == -1) {
125             processError(libvirt.virEventRegisterDefaultImpl());
126 
127             // add a disabled timer which is used later to break out
128             // of the event loop
129             int id = processError(libvirt.virEventAddTimeout(-1, timer, null, null));
130 
131             // remove this timer when there already is another one
132             if (!timerID.compareAndSet(-1, id)) {
133                 libvirt.virEventRemoveTimeout(id);
134             }
135         }
136     }
137 
138     /**
139      * Run one iteration of the event loop.
140      * <p>
141      * Applications will generally want to have a thread which invokes
142      * this method in an infinite loop:
143      * <pre>
144      * {@code while (true) connection.processEvent(); }
145      * </pre>
146      * <p>
147      * Failure to do so may result in connections being closed
148      * unexpectedly as a result of keepalive timeout.
149      *
150      * @throws LibvirtException on failure
151      *
152      * @see #initEventLoop()
153      */
processEvent()154     public static void processEvent() throws LibvirtException {
155         processError(libvirt.virEventRunDefaultImpl());
156     }
157 
158     /**
159      * Runs the event loop.
160      *
161      * This method blocks until {@link #stopEventLoop} is called or an
162      * exception is thrown.
163      * <p>
164      * Usually, this method is run in another thread.
165      *
166      * @throws LibvirtException     if there was an error during the call of a
167      *                              native libvirt function
168      * @throws InterruptedException if this thread was interrupted by a call to
169      *                              {@link java.lang.Thread#interrupt() Thread.interrupt()}
170      */
runEventLoop()171     public static void runEventLoop() throws LibvirtException, InterruptedException {
172         runLoop.set(true);
173         do {
174             processEvent();
175             if (Thread.interrupted()) {
176                 throw new InterruptedException();
177             }
178         } while (runLoop.get());
179     }
180 
181     /**
182      * Stops the event loop.
183      *
184      * This methods stops an event loop when an event loop is
185      * currently running, otherwise it does nothing.
186      *
187      * @see #runEventLoop
188      */
stopEventLoop()189     public static void stopEventLoop() throws LibvirtException {
190         if (runLoop.getAndSet(false)) {
191             // fire the timer immediately
192             int timer = timerID.get();
193             if (timer >= 0) {
194                 libvirt.virEventUpdateTimeout(timer, 0);
195             }
196         }
197     }
198 
199     /**
200      * Look up a constant of an enum by its ordinal number.
201      *
202      * @return the corresponding enum constant when such a constant exists,
203      *         otherwise the element which has the biggest ordinal number
204      *         assigned.
205      *
206      * @throws IllegalArgumentException if {@code ordinal} is negative
207      */
getConstant(final Class<T> c, final int ordinal)208     static <T extends Enum<T>> T getConstant(final Class<T> c, final int ordinal) {
209         if (ordinal < 0) {
210             throw new IllegalArgumentException("ordinal must be >= 0");
211         }
212 
213         T[] a = c.getEnumConstants();
214 
215         assert a.length > 0 : "there must be at least one enum constant";
216 
217         return a[Math.min(ordinal, a.length - 1)];
218     }
219 }
220