[libvirt] [java] [PATCH 2/6] JNA: add CString class and fix memory leaks

Claudio Bley claudio.bley at gmail.com
Wed Jan 28 20:45:43 UTC 2015


Some libvirt functions were wrapped using a Java String as return
type, although they return a "char*". That means the memory allocated
by libvirt for those strings is never freed because JNA assumes a
"const char*".

Start to refactor the String handling by using a type-safe Pointer for
C Strings, which automatically frees the memory when converting from
the native pointer to the Java type.
---
 src/main/java/org/libvirt/Connect.java       | 17 +++---
 src/main/java/org/libvirt/Device.java        |  2 +-
 src/main/java/org/libvirt/NetworkFilter.java |  2 +-
 src/main/java/org/libvirt/Secret.java        |  2 +-
 src/main/java/org/libvirt/StoragePool.java   |  2 +-
 src/main/java/org/libvirt/jna/CString.java   | 85 ++++++++++++++++++++++++++++
 src/main/java/org/libvirt/jna/Libvirt.java   | 18 +++---
 7 files changed, 108 insertions(+), 20 deletions(-)
 create mode 100644 src/main/java/org/libvirt/jna/CString.java

diff --git a/src/main/java/org/libvirt/Connect.java b/src/main/java/org/libvirt/Connect.java
index e86fd83..d0bdc4d 100644
--- a/src/main/java/org/libvirt/Connect.java
+++ b/src/main/java/org/libvirt/Connect.java
@@ -7,6 +7,7 @@ import java.util.UUID;
 
 import org.libvirt.event.*;
 import org.libvirt.jna.ConnectionPointer;
+import org.libvirt.jna.CString;
 import org.libvirt.jna.DevicePointer;
 import org.libvirt.jna.DomainPointer;
 import org.libvirt.jna.InterfacePointer;
@@ -364,7 +365,8 @@ public class Connect {
      * @throws LibvirtException
      */
     public String baselineCPU(String[] xmlCPUs) throws LibvirtException {
-        return processError(libvirt.virConnectBaselineCPU(VCP, xmlCPUs, xmlCPUs.length, 0));
+        CString result = libvirt.virConnectBaselineCPU(VCP, xmlCPUs, xmlCPUs.length, 0);
+        return processError(result).toString();
     }
 
     /**
@@ -920,7 +922,8 @@ public class Connect {
      * @throws LibvirtException
      */
     public String domainXMLFromNative(String nativeFormat, String nativeConfig, int flags) throws LibvirtException {
-        return processError(libvirt.virConnectDomainXMLFromNative(VCP, nativeFormat, nativeConfig, 0));
+        CString result = libvirt.virConnectDomainXMLFromNative(VCP, nativeFormat, nativeConfig, 0);
+        return processError(result).toString();
     }
 
     /**
@@ -932,8 +935,8 @@ public class Connect {
      * @throws LibvirtException
      */
     public String domainXMLToNative(String nativeFormat, String domainXML, int flags) throws LibvirtException {
-        String returnValue = libvirt.virConnectDomainXMLToNative(VCP, nativeFormat, domainXML, 0);
-        return processError(returnValue);
+        CString returnValue = libvirt.virConnectDomainXMLToNative(VCP, nativeFormat, domainXML, 0);
+        return processError(returnValue).toString();
     }
 
     @Override
@@ -962,8 +965,8 @@ public class Connect {
      * @throws LibvirtException
      */
     public String findStoragePoolSources(String type, String srcSpecs, int flags) throws LibvirtException {
-        String returnValue = libvirt.virConnectFindStoragePoolSources(VCP, type, srcSpecs, flags);
-        return processError(returnValue);
+        CString returnValue = libvirt.virConnectFindStoragePoolSources(VCP, type, srcSpecs, flags);
+        return processError(returnValue).toString();
     }
 
     /**
@@ -1109,7 +1112,7 @@ public class Connect {
      * @throws LibvirtException
      */
     public String getURI() throws LibvirtException {
-        return processError(libvirt.virConnectGetURI(VCP));
+        return processError(libvirt.virConnectGetURI(VCP)).toString();
     }
 
     /**
diff --git a/src/main/java/org/libvirt/Device.java b/src/main/java/org/libvirt/Device.java
index 3c876c6..04f373e 100644
--- a/src/main/java/org/libvirt/Device.java
+++ b/src/main/java/org/libvirt/Device.java
@@ -118,7 +118,7 @@ public class Device {
      * @throws LibvirtException
      */
     public String getXMLDescription() throws LibvirtException {
-        return processError(libvirt.virNodeDeviceGetXMLDesc(VDP, 0));
+        return processError(libvirt.virNodeDeviceGetXMLDesc(VDP, 0)).toString();
     }
 
     /**
diff --git a/src/main/java/org/libvirt/NetworkFilter.java b/src/main/java/org/libvirt/NetworkFilter.java
index 4f4bc6c..094a5f6 100644
--- a/src/main/java/org/libvirt/NetworkFilter.java
+++ b/src/main/java/org/libvirt/NetworkFilter.java
@@ -90,7 +90,7 @@ public class NetworkFilter {
      * @return the XML document
      */
     public String getXMLDesc() throws LibvirtException {
-        return processError(libvirt.virNWFilterGetXMLDesc(NFP, 0));
+        return processError(libvirt.virNWFilterGetXMLDesc(NFP, 0)).toString();
     }
 
     /**
diff --git a/src/main/java/org/libvirt/Secret.java b/src/main/java/org/libvirt/Secret.java
index e0ded73..a5f13ac 100644
--- a/src/main/java/org/libvirt/Secret.java
+++ b/src/main/java/org/libvirt/Secret.java
@@ -127,7 +127,7 @@ public class Secret {
      * @return the XML document
      */
     public String getXMLDesc() throws LibvirtException {
-        return processError(libvirt.virSecretGetXMLDesc(VSP, 0));
+        return processError(libvirt.virSecretGetXMLDesc(VSP, 0)).toString();
     }
 
     /**
diff --git a/src/main/java/org/libvirt/StoragePool.java b/src/main/java/org/libvirt/StoragePool.java
index 14ecab8..8caf9f5 100644
--- a/src/main/java/org/libvirt/StoragePool.java
+++ b/src/main/java/org/libvirt/StoragePool.java
@@ -207,7 +207,7 @@ public class StoragePool {
      * @return a XML document -java @throws LibvirtException
      */
     public String getXMLDesc(int flags) throws LibvirtException {
-        return processError(libvirt.virStoragePoolGetXMLDesc(VSPP, flags));
+        return processError(libvirt.virStoragePoolGetXMLDesc(VSPP, flags)).toString();
     }
 
     /**
diff --git a/src/main/java/org/libvirt/jna/CString.java b/src/main/java/org/libvirt/jna/CString.java
new file mode 100644
index 0000000..b6d9dc2
--- /dev/null
+++ b/src/main/java/org/libvirt/jna/CString.java
@@ -0,0 +1,85 @@
+package org.libvirt.jna;
+
+import java.nio.charset.Charset;
+
+import com.sun.jna.FromNativeContext;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+import com.sun.jna.PointerType;
+
+/**
+ * Represents an allocated C-String.
+ * <p>
+ * Either call {@link #toString} or {@link #free()}. Both methods make
+ * sure to reclaim the memory allocated for the string by calling
+ * Native.free.
+ */
+public class CString extends PointerType {
+    // all strings in libvirt are UTF-8 encoded
+    private final static Charset UTF8 = Charset.forName("UTF-8");
+    private final static byte NUL = 0;
+    private String string = null;
+
+    public CString() {
+        super();
+    }
+
+    public CString(Pointer p) {
+        super(p);
+    }
+
+    /**
+     * Returns a String representing the value of this C-String
+     * <p>
+     * Side-effect: frees the memory of the C-String.
+     */
+    @Override
+    public String toString() {
+        if (string == null) {
+            final Pointer ptr = getPointer();
+
+            if (ptr == null) return null;
+
+            try {
+                // N.B.  could be replaced with Pointer.getString(0L, "UTF-8")
+                //       available in JNA >= 4.x
+                final long len = ptr.indexOf(0, NUL);
+                assert (len != -1): "C-Strings must be \\0 terminated.";
+                assert (len <= Integer.MAX_VALUE): "string length exceeded " + Integer.MAX_VALUE;
+
+                if (len == 0) {
+                    string = "";
+                } else {
+                    final byte[] data = ptr.getByteArray(0, (int)len);
+
+                    string = new String(data, UTF8);
+                }
+            } finally {
+                free(ptr);
+            }
+        }
+        return string;
+    }
+
+    @Override
+    public CString fromNative(Object nativeValue, FromNativeContext context) {
+        if (nativeValue == null) return null;
+
+        return new CString((Pointer)nativeValue);
+    }
+
+    private void free(Pointer ptr) {
+        assert ptr != null;
+
+        Native.free(Pointer.nativeValue(ptr));
+        setPointer(null);
+    }
+
+    /**
+     * Free the memory used by this C-String
+     */
+    public void free() {
+        final Pointer ptr = getPointer();
+        if (ptr != null) free(ptr);
+    }
+}
diff --git a/src/main/java/org/libvirt/jna/Libvirt.java b/src/main/java/org/libvirt/jna/Libvirt.java
index a3cee0a..5a176b0 100644
--- a/src/main/java/org/libvirt/jna/Libvirt.java
+++ b/src/main/java/org/libvirt/jna/Libvirt.java
@@ -150,7 +150,7 @@ public interface Libvirt extends Library {
     public static int VIR_DOMAIN_SCHED_FIELD_LENGTH = 80;
 
     // Connection Functions
-    String virConnectBaselineCPU(ConnectionPointer virConnectPtr, String[] xmlCPUs, int ncpus, int flags);
+    CString virConnectBaselineCPU(ConnectionPointer virConnectPtr, String[] xmlCPUs, int ncpus, int flags);
 
     /**
      * @deprecated as of libvirt 0.6.0, all errors reported in the
@@ -175,14 +175,14 @@ public interface Libvirt extends Library {
     int virConnectIsAlive(ConnectionPointer virConnectPtr);
     int virConnectIsEncrypted(ConnectionPointer virConnectPtr) ;
     int virConnectIsSecure(ConnectionPointer virConnectPtr) ;
-    String virConnectFindStoragePoolSources(ConnectionPointer virConnectPtr, String type, String srcSpec, int flags);
+    CString virConnectFindStoragePoolSources(ConnectionPointer virConnectPtr, String type, String srcSpec, int flags);
     Pointer virConnectGetCapabilities(ConnectionPointer virConnectPtr);
     Pointer virConnectGetHostname(ConnectionPointer virConnectPtr);
     int virConnectGetLibVersion(ConnectionPointer virConnectPtr, LongByReference libVer);
     int virConnectGetMaxVcpus(ConnectionPointer virConnectPtr, String type);
     Pointer virConnectGetSysinfo(ConnectionPointer virConnectPtr, int flags);
     String virConnectGetType(ConnectionPointer virConnectPtr);
-    String virConnectGetURI(ConnectionPointer virConnectPtr);
+    CString virConnectGetURI(ConnectionPointer virConnectPtr);
     int virConnectGetVersion(ConnectionPointer virConnectPtr, LongByReference hvVer);
     int virConnectListDefinedDomains(ConnectionPointer virConnectPtr, Pointer[] name, int maxnames);
     int virConnectListDefinedNetworks(ConnectionPointer virConnectPtr, Pointer[] name, int maxnames);
@@ -218,9 +218,9 @@ public interface Libvirt extends Library {
     @Deprecated
     virError virConnGetLastError(ConnectionPointer virConnectPtr);
     void virConnResetLastError(ConnectionPointer virConnectPtr);
-    String virConnectDomainXMLFromNative(ConnectionPointer virConnectPtr, String nativeFormat,
+    CString virConnectDomainXMLFromNative(ConnectionPointer virConnectPtr, String nativeFormat,
             String nativeConfig, int flags);
-    String virConnectDomainXMLToNative(ConnectionPointer virConnectPtr, String nativeFormat, String domainXML,
+    CString virConnectDomainXMLToNative(ConnectionPointer virConnectPtr, String nativeFormat, String domainXML,
             int flags);
 
     // Global functions
@@ -350,7 +350,7 @@ public interface Libvirt extends Library {
     String virNodeDeviceGetParent(DevicePointer virDevicePointer);
     int virNodeDeviceNumOfCaps(DevicePointer virDevicePointer);
     int virNodeDeviceListCaps(DevicePointer virDevicePointer, Pointer[] names, int maxNames);
-    String virNodeDeviceGetXMLDesc(DevicePointer virDevicePointer, int flags);
+    CString virNodeDeviceGetXMLDesc(DevicePointer virDevicePointer, int flags);
     int virNodeDeviceFree(DevicePointer virDevicePointer);
     int virNodeDeviceDettach(DevicePointer virDevicePointer);
     int virNodeDeviceReAttach(DevicePointer virDevicePointer);
@@ -371,7 +371,7 @@ public interface Libvirt extends Library {
     String virStoragePoolGetName(StoragePoolPointer storagePoolPtr);
     int virStoragePoolGetUUID(StoragePoolPointer storagePoolPtr, byte[] uuidString);
     int virStoragePoolGetUUIDString(StoragePoolPointer storagePoolPtr, byte[] uuidString);
-    String virStoragePoolGetXMLDesc(StoragePoolPointer storagePoolPtr, int flags);
+    CString virStoragePoolGetXMLDesc(StoragePoolPointer storagePoolPtr, int flags);
     int virStoragePoolListVolumes(StoragePoolPointer storagePoolPtr, Pointer[] names, int maxnames);
     int virStoragePoolIsActive(StoragePoolPointer storagePoolPtr);
     int virStoragePoolIsPersistent(StoragePoolPointer storagePoolPtr);
@@ -422,7 +422,7 @@ public interface Libvirt extends Library {
     String virSecretGetUsageID(SecretPointer virSecretPtr);
     int virSecretGetUsageType(SecretPointer virSecretPtr);
     Pointer virSecretGetValue(SecretPointer virSecretPtr, SizeTByReference value_size, int flags);
-    String virSecretGetXMLDesc(SecretPointer virSecretPtr, int flags);
+    CString virSecretGetXMLDesc(SecretPointer virSecretPtr, int flags);
     SecretPointer virSecretLookupByUsage(ConnectionPointer virConnectPtr, int usageType, String usageID);
     SecretPointer virSecretLookupByUUID(ConnectionPointer virConnectPtr, byte[] uuidBytes);
     SecretPointer virSecretLookupByUUIDString(ConnectionPointer virConnectPtr, String uuidstr);
@@ -455,7 +455,7 @@ public interface Libvirt extends Library {
     int virDomainSnapshotNum(DomainPointer virDomainPtr, int flags);
 
     // Network Filter Methods
-    String virNWFilterGetXMLDesc(NetworkFilterPointer virNWFilterPtr, int flags);
+    CString virNWFilterGetXMLDesc(NetworkFilterPointer virNWFilterPtr, int flags);
     NetworkFilterPointer virNWFilterDefineXML(ConnectionPointer virConnectPtr, String xml);
     int virNWFilterFree(NetworkFilterPointer virNWFilterPtr);
     NetworkFilterPointer virNWFilterLookupByName(ConnectionPointer virConnectPtr, String name);
-- 
2.2.2




More information about the libvir-list mailing list