# HG changeset patch # User Miloslav Zezulka # Date 1504691363 -7200 # Node ID 2397ba60a7d6bdd30a4146c6e1cfec9491aba180 # Parent f902a9f69e4b3a07ba045a8bfa836aed5c9953bd Record loaded native libraries for a given jvm Reviewed-by: jerboaa, stooke, neugens Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-August/024516.html diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/JvmStatHostListener.java --- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/JvmStatHostListener.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/JvmStatHostListener.java Wed Sep 06 11:49:23 2017 +0200 @@ -52,6 +52,7 @@ import com.redhat.thermostat.jvm.overview.agent.VmStatusListener.Status; import com.redhat.thermostat.jvm.overview.agent.internal.model.InfoBuilderFactory; import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAO; +import com.redhat.thermostat.jvm.overview.agent.internal.model.VmNativeLibsExtractorFactory; import com.redhat.thermostat.jvm.overview.agent.model.VmInfo; import com.redhat.thermostat.storage.core.WriterID; @@ -149,8 +150,7 @@ JvmStatDataExtractor extractor) throws MonitorException { Map properties = new HashMap(); Map environment = InfoBuilderFactory.INSTANCE.createProcessEnvironmentBuilder().build(vmPid); - // TODO actually figure out the loaded libraries. - String[] loadedNativeLibraries = new String[0]; + String[] loadedNativeLibraries = VmNativeLibsExtractorFactory.getInstance(vmPid).getNativeLibs(); ProcessUserInfo userInfo = userInfoBuilder.build(vmPid); VmInfo info = new VmInfo(writerId.getWriterID(), vmId, vmPid, extractor.getVmStartTime(), stopTime, extractor.getJavaVersion(), extractor.getJavaHome(), diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAO.java --- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAO.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAO.java Wed Sep 06 11:49:23 2017 +0200 @@ -59,5 +59,7 @@ void putVmInfo(VmInfo info); void putVmStoppedTime(String agentId, String vmId, long since); + + void updateVmNativeLibs(String vmId, String[] newLibs); } diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java --- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImpl.java Wed Sep 06 11:49:23 2017 +0200 @@ -70,7 +70,7 @@ private static final String VM_PATH = "/jvms/"; private final Logger logger = LoggingUtils.getLogger(VmInfoDAOImpl.class); - + private final JsonHelper jsonHelper; private final ConfigurationCreator configCreator; @@ -119,7 +119,8 @@ URI uri = getAddURI(); httpRequestService.sendHttpRequest(json, uri, HttpRequestService.Method.POST); } catch (IOException | RequestFailedException e) { - logger.log(Level.WARNING, "Failed to send JVM information to web gateway", e); + logger.log(Level.WARNING, "Failed to send JVM information to web gateway"); + logger.log(Level.INFO, e.getMessage()); } } @@ -128,14 +129,29 @@ URI uri = getUpdateURI(vmId); try { // Encode as JSON and send as PUT request - VmInfoUpdate update = new VmInfoUpdate(timestamp); + VmInfoUpdate update = new VmInfoLongUpdate(timestamp); String json = jsonHelper.toJson(update); httpRequestService.sendHttpRequest(json, uri, HttpRequestService.Method.PUT); } catch (IOException | RequestFailedException e) { - logger.log(Level.WARNING, "Failed to send JVM information update to web gateway at: " + uri, e); + logger.log(Level.WARNING, "Failed to send JVM information update to web gateway at: " + uri); + logger.log(Level.INFO, e.getMessage()); } } - + + @Override + public void updateVmNativeLibs(String vmId, String[] newLibs) { + URI uri = getUpdateURI(vmId); + try { + // Encode as JSON and send as PUT request + VmInfoUpdate update = new VmInfoStringArrayUpdate(newLibs); + String json = jsonHelper.toJson(update); + httpRequestService.sendHttpRequest(json, uri, HttpRequestService.Method.PUT); + } catch (IOException | RequestFailedException e) { + logger.log(Level.WARNING, "Failed to send JVM information update to web gateway at: " + uri); + logger.log(Level.INFO, e.getMessage()); + } + } + private URI getAddURI() { StringBuilder builder = new StringBuilder(); builder.append(SYSTEM_PATH); @@ -151,7 +167,7 @@ builder.append(vmId); return gatewayURL.resolve(builder.toString()); } - + protected void bindHttpRequestService(HttpRequestService httpRequestService) { this.httpRequestService = httpRequestService; } @@ -168,39 +184,53 @@ protected void unbindSystemId(SystemID id) { this.systemID = null; } - - static class VmInfoUpdate { - - private final long stoppedTime; - - VmInfoUpdate(long stoppedTime) { - this.stoppedTime = stoppedTime; + + static abstract class VmInfoUpdate { + + private final T param; + + VmInfoUpdate(T param) { + this.param = param; } - - long getStoppedTime() { - return stoppedTime; + + T getParam() { + return param; } } - + + static class VmInfoLongUpdate extends VmInfoUpdate { + + public VmInfoLongUpdate(Long param) { + super(param); + } + } + + static class VmInfoStringArrayUpdate extends VmInfoUpdate { + + public VmInfoStringArrayUpdate(String[] param) { + super(param); + } + } + // For testing purposes static class JsonHelper { - + private final VmInfoTypeAdapter typeAdapter; private final VmInfoUpdateTypeAdapter updateTypeAdapter; - + public JsonHelper(VmInfoTypeAdapter typeAdapter, VmInfoUpdateTypeAdapter updateTypeAdapter) { this.typeAdapter = typeAdapter; this.updateTypeAdapter = updateTypeAdapter; } - + String toJson(List infos) throws IOException { return typeAdapter.toJson(infos); } - - String toJson(VmInfoUpdate update) throws IOException { + + String toJson(VmInfoUpdate update) throws IOException { return updateTypeAdapter.toJson(update); } - + } // For Testing purposes @@ -212,4 +242,3 @@ } } - diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapter.java --- a/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapter.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapter.java Wed Sep 06 11:49:23 2017 +0200 @@ -45,11 +45,13 @@ import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; +import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAOImpl.VmInfoLongUpdate; +import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAOImpl.VmInfoStringArrayUpdate; import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAOImpl.VmInfoUpdate; import com.redhat.thermostat.jvm.overview.agent.model.VmInfo; public class VmInfoTypeAdapter extends TypeAdapter> { - + private static final String AGENT_ID = "agentId"; private static final String JVM_ID = "jvmId"; private static final String JVM_PID = "jvmPid"; @@ -76,11 +78,11 @@ public void write(JsonWriter out, List value) throws IOException { // Request is an array of VmInfo objects out.beginArray(); - + for (VmInfo info : value) { writeVmInfo(out, info); } - + out.endArray(); } @@ -124,14 +126,14 @@ writeLong(out, info.getUid()); out.name(USERNAME); out.value(info.getUsername()); - + out.endObject(); } - + private void writeStringMap(JsonWriter out, Map map) throws IOException { // Write contents of Map as an array of JSON objects out.beginArray(); - + Set> entries = map.entrySet(); for (Map.Entry entry : entries) { // Create JSON object with key and value labeled as JSON names @@ -142,21 +144,21 @@ out.value(entry.getValue()); out.endObject(); } - + out.endArray(); } - - private void writeStringArray(JsonWriter out, String[] array) throws IOException { + + private static void writeStringArray(JsonWriter out, String[] array) throws IOException { // Write String[] as JSON array out.beginArray(); - + for (String item : array) { out.value(item); } - + out.endArray(); } - + private static void writeLong(JsonWriter out, long value) throws IOException { // Write MongoDB representation of a Long out.beginObject(); @@ -169,22 +171,31 @@ public List read(JsonReader in) throws IOException { throw new UnsupportedOperationException(); } - + static class VmInfoUpdateTypeAdapter extends TypeAdapter { private static final String SET = "set"; - + @Override public void write(JsonWriter out, VmInfoUpdate value) throws IOException { // List fields to update as part of a JSON object with name "set" out.beginObject(); out.name(SET); - + out.beginObject(); - out.name(STOP_TIME_STAMP); - writeLong(out, value.getStoppedTime()); + if (value instanceof VmInfoLongUpdate) { + VmInfoLongUpdate castVal = (VmInfoLongUpdate) value; + out.name(STOP_TIME_STAMP); + writeLong(out, castVal.getParam()); + } else if (value instanceof VmInfoStringArrayUpdate) { + VmInfoStringArrayUpdate castVal = (VmInfoStringArrayUpdate) value; + out.name(LOADED_NATIVE_LIBRARIES); + writeStringArray(out, castVal.getParam()); + } else { + throw new IllegalArgumentException("illegal update data type"); + } out.endObject(); - + out.endObject(); } @@ -192,7 +203,7 @@ public VmInfoUpdate read(JsonReader in) throws IOException { throw new UnsupportedOperationException(); } - + } - + } diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmLinuxNativeLibsExtractor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmLinuxNativeLibsExtractor.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,93 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +import com.redhat.thermostat.common.utils.LoggingUtils; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class VmLinuxNativeLibsExtractor implements VmNativeLibsExtractor { + + private static final Logger LOGGER + = LoggingUtils.getLogger(VmInfoDAOImpl.class); + + private final Integer pid; + + public VmLinuxNativeLibsExtractor(Integer pid) { + Objects.requireNonNull(pid); + this.pid = pid; + } + + private String[] getNativeLibsFromReader(File nativeLibFile) { + final String soGrep = ".+\\.so.*"; + Set result = new HashSet<>(); + try (BufferedReader br + = new BufferedReader(new FileReader(nativeLibFile))) { + String next = br.readLine(); + while (next != null) { + next = next.trim(); + if (next.matches(soGrep)) { + String candidate = next.substring(next.lastIndexOf(' ') + 1); + result.add(candidate); + } + next = br.readLine(); + } + return result.toArray(new String[0]); + } catch (IOException ex) { + LOGGER.log(Level.WARNING, "Unable to retrieve native libraries."); + LOGGER.log(Level.INFO, ex.getMessage()); + return new String[0]; + } + } + + @Override + public String[] getNativeLibs() { + return getNativeLibsFromReader(new File(String.format("/proc/%d/maps", pid))); + } + + // for testing purposes only + String[] getNativeLibs(File testFile) { + return getNativeLibsFromReader(testFile); + } +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibrariesBackend.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibrariesBackend.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,96 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +import com.redhat.thermostat.backend.Backend; +import com.redhat.thermostat.common.Ordered; +import com.redhat.thermostat.jvm.overview.agent.VmListenerBackend; +import com.redhat.thermostat.jvm.overview.agent.VmUpdateListener; +import com.redhat.thermostat.storage.core.WriterID; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; + +@Component +@Service(value = Backend.class) +public class VmNativeLibrariesBackend extends VmListenerBackend { + + private final ListenerCreator listenerCreator; + public static final int ORDER = Ordered.ORDER_MEMORY_GROUP + 60; + + public VmNativeLibrariesBackend() { + this(new ListenerCreator()); + } + + VmNativeLibrariesBackend(ListenerCreator creator) { + super("VM Native Libs Retrieval Backend", "Gathers loaded native libraries for a given JVM", "Red Hat, Inc.", true); + this.listenerCreator = creator; + } + + @Reference + private VmInfoDAO vmInfoDao; + @Reference + private WriterID writerId; + + @Override + protected VmUpdateListener createVmListener(String writerId, String vmId, int pid) { + return listenerCreator.create(vmInfoDao, vmId, pid); + } + + @Override + public int getOrderValue() { + return ORDER; + } + + // DS bind method + protected void bindVmGcStats(VmInfoDAO dao) { + this.vmInfoDao = dao; + } + + // DS bind method + protected void bindWriterId(WriterID id) { + this.writerId = id; + } + + // For testing purposes + static class ListenerCreator { + + VmNativeLibsVmListener create(VmInfoDAO dao, String vmId, int pid) { + return new VmNativeLibsVmListener(dao, vmId, pid); + } + } +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractor.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +public interface VmNativeLibsExtractor { + + /** + * Returns an array of native libraries names loaded by the JVM. Extracting + * native libraries is OS specific and it is suggested that each + * implementation contains name of the OS the implementation is related to. + */ + String[] getNativeLibs(); +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractorFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsExtractorFactory.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,50 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +import com.redhat.thermostat.shared.config.OS; + +public final class VmNativeLibsExtractorFactory { + + public static VmNativeLibsExtractor getInstance(Integer pid) { + if (OS.IS_LINUX) { + return new VmLinuxNativeLibsExtractor(pid); + } else { + throw new UnsupportedOperationException("Extractor for the given OS not supported yet."); + } + } +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsVmListener.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/main/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmNativeLibsVmListener.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.jvm.overview.agent.VmUpdate; +import com.redhat.thermostat.jvm.overview.agent.VmUpdateException; +import com.redhat.thermostat.jvm.overview.agent.VmUpdateListener; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class VmNativeLibsVmListener implements VmUpdateListener { + + private static final Logger logger = LoggingUtils.getLogger(VmNativeLibsVmListener.class); + + private final VmInfoDAO vmInfoDAO; + private final String vmId; + private final int pid; + private final VmNativeLibsExtractor libExtractor; + + public VmNativeLibsVmListener(VmInfoDAO vmInfoDAO, String vmId, int pid) { + this.vmInfoDAO = vmInfoDAO; + this.vmId = vmId; + this.pid = pid; + this.libExtractor = VmNativeLibsExtractorFactory.getInstance(pid); + } + + @Override + public void countersUpdated(VmUpdate update) { + VmClassloaderInfoExtractor extractor = new VmClassloaderInfoExtractor(update); + recordNativeLibs(extractor); + } + + /** + * Heuristic for when to extract native libraries: either number of loaded + * classes counter changed or this counter did not change but N different + * classes were loaded instead of N unloaded ones before next VmEvent was + * issued, therefore leaving the amount of loaded classes the same but with + * different set of classes. + */ + void recordNativeLibs(VmClassloaderInfoExtractor extractor) { + try { + Long loadedClassesCount = extractor.getLoadedClassesCount(); + Long unloadedClassesCount = extractor.getUnloadedClassesCount(); + if (loadedClassesCount != null || unloadedClassesCount != null) { + vmInfoDAO.updateVmNativeLibs(vmId, libExtractor.getNativeLibs()); + } + } catch (VmUpdateException ex) { + logger.log(Level.WARNING, "Error gathering native libs for VM " + vmId, ex); + } + } + +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java --- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoDAOImplTest.java Wed Sep 06 11:49:23 2017 +0200 @@ -69,7 +69,7 @@ private static final URI UPDATE_URI = GATEWAY_URI.resolve("systems/foo/jvms/vmId"); private static final String SOME_JSON = "{\"some\" : \"json\"}"; private static final String SOME_OTHER_JSON = "{\"some\" : {\"other\" : \"json\"}}"; - + private VmInfo info; private JsonHelper jsonHelper; private HttpRequestService httpRequestService; @@ -122,7 +122,7 @@ dao.bindHttpRequestService(httpRequestService); dao.activate(); dao.putVmInfo(info); - + verify(jsonHelper).toJson(eq(Arrays.asList(info))); verify(httpRequestService).sendHttpRequest(SOME_JSON, POST_URI, HttpRequestService.Method.POST); } @@ -138,10 +138,9 @@ ArgumentCaptor updateCaptor = ArgumentCaptor.forClass(VmInfoUpdate.class); verify(jsonHelper).toJson(updateCaptor.capture()); VmInfoUpdate update = updateCaptor.getValue(); - assertEquals(3L, update.getStoppedTime()); - + assertEquals(3L, update.getParam()); + verify(httpRequestService).sendHttpRequest(SOME_OTHER_JSON, UPDATE_URI, HttpRequestService.Method.PUT); } } - diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapterTest.java --- a/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapterTest.java Mon Sep 04 14:39:53 2017 +0200 +++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmInfoTypeAdapterTest.java Wed Sep 06 11:49:23 2017 +0200 @@ -36,6 +36,8 @@ package com.redhat.thermostat.jvm.overview.agent.internal.model; +import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAOImpl.VmInfoLongUpdate; +import com.redhat.thermostat.jvm.overview.agent.internal.model.VmInfoDAOImpl.VmInfoStringArrayUpdate; import static com.redhat.thermostat.testutils.JsonUtils.assertJsonEquals; import java.util.Arrays; @@ -49,7 +51,7 @@ import org.junit.Test; public class VmInfoTypeAdapterTest { - + @Test public void testWrite() throws Exception { VmInfoTypeAdapter adapter = new VmInfoTypeAdapter(); @@ -68,16 +70,16 @@ "\"environment\":[{\"key\":\"A\",\"value\":\"B\"},{\"key\":\"C\",\"value\":\"D\"}]," + "\"loadedNativeLibraries\":[\"libhello\",\"libworld\"],\"uid\":{\"$numberLong\":\"5678\"}," + "\"username\":\"user\"}]"; - + final Map props = new HashMap<>(); props.put("A", "B"); props.put("C", "D"); final Map env = new HashMap<>(); env.put("E", "F"); env.put("G", "H"); - VmInfo first = new VmInfo("agent1", "vm1", 8000, 50000L, Long.MIN_VALUE, "1.8.0", "/path/to/java", "myClass", + VmInfo first = new VmInfo("agent1", "vm1", 8000, 50000L, Long.MIN_VALUE, "1.8.0", "/path/to/java", "myClass", "java myClass", "myJVM", "interesting", "1800", "-Dhello", props, env, new String[0], 1234L, "test"); - + final Map props2 = new HashMap<>(); final Map env2 = new HashMap<>(); env2.put("A", "B"); @@ -86,19 +88,29 @@ VmInfo second = new VmInfo("agent2", "vm2", 9000, 100000L, 200000L, "1.7.0", "/path/to/jre", "myOtherClass", "otherClass.sh", "myOtherJVM", "info", "1700", "-Dworld", props2, env2, libs, 5678L, "user"); List infos = Arrays.asList(first, second); - + String json = adapter.toJson(infos); assertJsonEquals(expected, json); } - + @Test - public void testUpdate() throws Exception { + public void testUpdateLong() throws Exception { VmInfoUpdateTypeAdapter adapter = new VmInfoUpdateTypeAdapter(); final String expected = "{\"set\":{\"stopTime\":{\"$numberLong\":\"5000\"}}}"; - - VmInfoUpdate update = new VmInfoUpdate(5000L); + + VmInfoUpdate update = new VmInfoLongUpdate(5000L); String json = adapter.toJson(update); assertJsonEquals(expected, json); } - + + @Test + public void testUpdateStringArray() throws Exception { + VmInfoUpdateTypeAdapter adapter = new VmInfoUpdateTypeAdapter(); + final String expected = "{\"set\":{\"loadedNativeLibraries\":[\"libupdate\",\"libthree\",\"libitems\"]}}"; + String[] libs = new String[]{"libupdate", "libthree", "libitems"}; + VmInfoUpdate update = new VmInfoStringArrayUpdate(libs); + String json = adapter.toJson(update); + assertJsonEquals(expected, json); + } + } diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmLinuxNativeLibsExtractorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/test/java/com/redhat/thermostat/jvm/overview/agent/internal/model/VmLinuxNativeLibsExtractorTest.java Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,109 @@ +/* + * Copyright 2012-2017 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.jvm.overview.agent.internal.model; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Arrays; +import java.util.HashSet; +import org.junit.Before; +import org.junit.Test; + +public class VmLinuxNativeLibsExtractorTest { + + private VmLinuxNativeLibsExtractor extractor; + + private static final Integer VM_PID = 0; + + private final File THREE_LIBS = getFileFromTestSources("native_lib_three_libs"); + private final File NO_LIBS = getFileFromTestSources("native_lib_no_libs"); + private final File EMPTY = getFileFromTestSources("native_lib_empty"); + + private File getFileFromTestSources(String path) { + path = '/' + path; + return new File(decodeFilePath(this.getClass().getResource(path))); + } + + private String decodeFilePath(URL url) { + try { + return URLDecoder.decode(url.getFile(), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new AssertionError(ex); + } + } + + @Before + public void setup() { + extractor = new VmLinuxNativeLibsExtractor(VM_PID); + } + + @Test + public void threeLibs() { + HashSet expectedLibs = new HashSet<>(Arrays.asList("/usr/foo/libhello.so", + "/usr/bar/libworld.so.0.18.0", "/tmp/libnew.so.so")); + + // Cannot compare arrays directly since the implementation internally gathers libs + // to a Set, which might yield a different ordering of items in the collection in the end + assertEquals(expectedLibs, new HashSet<>(Arrays.asList(extractor.getNativeLibs(THREE_LIBS)))); + } + + @Test + public void noLibs() { + String[] result = extractor.getNativeLibs(NO_LIBS); + int numExpectedLibs = 0; + + assertNotNull(result); + assertEquals(result.length, numExpectedLibs); + + } + + @Test + public void empty() { + String[] result = extractor.getNativeLibs(EMPTY); + int numExpectedLibs = 0; + + assertNotNull(result); + assertEquals(result.length, numExpectedLibs); + + } + +} diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/resources/native_lib_empty --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/test/resources/native_lib_empty Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,1 @@ + diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/resources/native_lib_no_libs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/test/resources/native_lib_no_libs Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,3 @@ +000000000000-00000000f000 r-xp 00000000 fd:01 1234567 /usr/foo/libhello +00000000f000-000000050000 ---p 00085000 fd:02 1234567 /usr/bar/libworld +000000050000-000000e00000 r--p 00085000 fd:03 1234567 /tmp/libnew diff -r f902a9f69e4b -r 2397ba60a7d6 plugins/jvm-overview/agent/src/test/resources/native_lib_three_libs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/jvm-overview/agent/src/test/resources/native_lib_three_libs Wed Sep 06 11:49:23 2017 +0200 @@ -0,0 +1,4 @@ +000000000000-00000000f000 r-xp 00000000 fd:01 1234567 /usr/foo/libhello.so +00000000f000-000000050000 ---p 00085000 fd:02 1234567 /usr/bar/libworld.so.0.18.0 +000000050000-000000e00000 r--p 00085000 fd:03 1234567 /tmp/libnew.so.so +