Mercurial > hg > release > thermostat-0.13
changeset 1102:8757e35030f2
Add user ID to VmInfo and display in clients
This commit adds user ID information to the VmInfo Pojo, which is
gathered by SystemBackend. The UID for a VM is collected by parsing
/proc/${pid}/status, and we then get the username for the UID from the
native function getpwuid_r. Both the vm-info command and the VM Overview
tab of the Swing GUI now show the user under Process Information. I took
inspiration from the id Unix program for the formatting, which is
"uid(username)". The native component of this commit is added to
agent-core for use by system-backend, this was done on request by Mario
since agent-core already has a native component for getting the
hostname. I made this component an OSGi service primarily to hide its
implementation details.
Reviewed-by: omajid, jerboaa
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-May/006674.html
line wrap: on
line diff
--- a/agent/core/Makefile Mon May 13 16:26:16 2013 -0600 +++ b/agent/core/Makefile Tue May 21 12:43:17 2013 -0400 @@ -8,35 +8,48 @@ TARGET_DIR = target INCLUDE = -I $(TARGET_DIR) -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux -SOURCES = src/main/native/HostName.c -TARGET = $(TARGET_DIR)/HostName.c -OBJECTS = $(TARGET:.c=.o) +HOSTNAME_SOURCES = src/main/native/HostName.c +HOSTNAME_TARGET = $(TARGET_DIR)/HostName.c +HOSTNAME_OBJECTS = $(HOSTNAME_TARGET:.c=.o) + +HOSTNAME_EXECUTABLE = libHostNameWrapper.so -EXECUTABLE = libHostNameWrapper.so +USERNAME_SOURCES = src/main/native/UserNameUtilImpl.c +USERNAME_TARGET = $(TARGET_DIR)/UserNameUtilImpl.c +USERNAME_OBJECTS = $(USERNAME_TARGET:.c=.o) + +USERNAME_EXECUTABLE = libUserNameUtilWrapper.so .PHONY: -JNI_LIST = com.redhat.thermostat.utils.hostname.HostName +JNI_LIST = com.redhat.thermostat.utils.hostname.HostName\ + com.redhat.thermostat.utils.username.internal.UserNameUtilImpl $(JNI_LIST): $(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST) -all: $(JNI_LIST) init $(SOURCES) $(EXECUTABLE) +all: $(JNI_LIST) init $(HOSTNAME_SOURCES) $(HOSTNAME_EXECUTABLE) $(USERNAME_SOURCES) $(USERNAME_EXECUTABLE) .PHONY: init: - $(COPY) $(SOURCES) $(TARGET) + $(COPY) $(HOSTNAME_SOURCES) $(HOSTNAME_TARGET) + $(COPY) $(USERNAME_SOURCES) $(USERNAME_TARGET) -$(EXECUTABLE): $(OBJECTS) - $(CC) $(OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) +$(HOSTNAME_EXECUTABLE): $(HOSTNAME_OBJECTS) + $(CC) $(HOSTNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) + +$(USERNAME_EXECUTABLE): $(USERNAME_OBJECTS) + $(CC) $(USERNAME_OBJECTS) -o $(TARGET_DIR)/$@ $(MYLDFLAGS) $(LDFLAGS) .c.o: $(CC) $(MYCFLAGS) $(CFLAGS) $(INCLUDE) $< -o $@ clean-lib: - rm $(TARGET_DIR)/$(EXECUTABLE) + rm $(TARGET_DIR)/$(HOSTNAME_EXECUTABLE) + rm $(TARGET_DIR)/$(USERNAME_EXECUTABLE) clean-obj: - rm $(OBJECTS) $(TARGET) + rm $(HOSTNAME_OBJECTS) $(HOSTNAME_TARGET) + rm $(USERNAME_OBJECTS) $(USERNAME_TARGET) clean: clean-obj clean-lib
--- a/agent/core/pom.xml Mon May 13 16:26:16 2013 -0600 +++ b/agent/core/pom.xml Tue May 21 12:43:17 2013 -0400 @@ -119,12 +119,14 @@ com.redhat.thermostat.utils.hostname, com.redhat.thermostat.utils, com.redhat.thermostat.utils.management, + com.redhat.thermostat.utils.username, </Export-Package> <Private-Package> com.redhat.thermostat.agent.internal, com.redhat.thermostat.agent.locale, com.redhat.thermostat.backend.internal, com.redhat.thermostat.utils.management.internal, + com.redhat.thermostat.utils.username.internal, </Private-Package> <!-- Do not autogenerate uses clauses in Manifests --> <_nouses>true</_nouses>
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Mon May 13 16:26:16 2013 -0600 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Tue May 21 12:43:17 2013 -0400 @@ -38,23 +38,23 @@ import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceRegistration; import com.redhat.thermostat.utils.management.MXBeanConnectionPool; import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; +import com.redhat.thermostat.utils.username.UserNameUtil; +import com.redhat.thermostat.utils.username.internal.UserNameUtilImpl; public class Activator implements BundleActivator { - private ServiceRegistration registration; - @Override public void start(BundleContext context) throws Exception { - registration = context.registerService(MXBeanConnectionPool.class, new MXBeanConnectionPoolImpl(), null); + context.registerService(MXBeanConnectionPool.class, new MXBeanConnectionPoolImpl(), null); + context.registerService(UserNameUtil.class, new UserNameUtilImpl(), null); } @Override public void stop(BundleContext context) throws Exception { - registration.unregister(); + // Services automatically unregistered by framework } }
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/ProcDataSource.java Mon May 13 16:26:16 2013 -0600 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/ProcDataSource.java Tue May 21 12:43:17 2013 -0400 @@ -51,6 +51,7 @@ private static final String CPUINFO_FILE = "/proc/cpuinfo"; private static final String PID_STAT_FILE = "/proc/${pid}/stat"; + private static final String PID_STATUS_FILE = "/proc/${pid}/status"; private static final String PID_ENVIRON_FILE = "/proc/${pid}/environ"; /** @@ -87,6 +88,13 @@ public Reader getStatReader(int pid) throws IOException { return new FileReader(getPidFile(PID_STAT_FILE, pid)); } + + /** + * Returns a reader for /proc/$PID/status + */ + public Reader getStatusReader(int pid) throws IOException { + return new FileReader(getPidFile(PID_STATUS_FILE, pid)); + } /** * Returns a reader for /proc/$PID/environ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/username/UserNameUtil.java Tue May 21 12:43:17 2013 -0400 @@ -0,0 +1,52 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.utils.username; + + +/** + * Utility to query the operating system for a user name. + */ +public interface UserNameUtil { + + /** + * Returns the user name corresponding to the provided UID. + * @param uid - the UID whose name to return + * @return the user name if found, or null otherwise + */ + String getUserName(long uid); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/username/internal/UserNameUtilImpl.java Tue May 21 12:43:17 2013 -0400 @@ -0,0 +1,71 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.utils.username.internal; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.utils.username.UserNameUtil; + +public class UserNameUtilImpl implements UserNameUtil { + private static final Logger logger = LoggingUtils.getLogger(UserNameUtilImpl.class); + + static { + /* + * TODO Change to System.load + * http://icedtea.classpath.org/pipermail/thermostat/2013-May/006657.html + */ + System.loadLibrary("UserNameUtilWrapper"); + } + + public String getUserName(long uid) { + String result = null; + if (uid >= 0) { + try { + result = getUserName0(uid); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to retrieve username for uid: " + uid, e); + } + } + return result; + } + + private native String getUserName0(long uid) throws IOException; + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/native/UserNameUtilImpl.c Tue May 21 12:43:17 2013 -0400 @@ -0,0 +1,92 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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. + */ + +#include "com_redhat_thermostat_utils_username_internal_UserNameUtilImpl.h" + +#include <jni.h> +#include <pwd.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> + +static jint throw_IOException(JNIEnv *env, const char *message) { + const char *class_name = "java/io/IOException"; + jclass class = (*env)->FindClass(env, class_name); + if (class == NULL) { + return -1; + } + return (*env)->ThrowNew(env, class, message); +} + +JNIEXPORT jstring JNICALL +Java_com_redhat_thermostat_utils_username_internal_UserNameUtilImpl_getUserName0 + (JNIEnv *env, jclass ProcessUserInfoBuilderClass, jlong uid) { + size_t bufsize = sysconf(_SC_GETPW_R_SIZE_MAX); + if (bufsize < 0) { + throw_IOException(env, "Unable to retrieve recommended buffer size from sysconf"); + return NULL; + } + + char *buf = malloc(bufsize * sizeof(char)); + if (!buf) { + throw_IOException(env, "Unable to allocate buffer for username"); + return NULL; + } + + struct passwd pwd; + struct passwd *ret; + int rc = getpwuid_r(uid, &pwd, buf, bufsize, &ret); + if (rc) { + // Error occurred + const char *error_message = strerror(rc); + throw_IOException(env, error_message); + free(buf); + return NULL; + } + if (!ret) { + // No username found + char err_buf[128]; // Large enough for even the largest UIDs + snprintf(err_buf, sizeof(err_buf), "Username not found for uid: %ld", uid); + throw_IOException(env, err_buf); + free(buf); + return NULL; + } + + jstring name = (*env)->NewStringUTF(env, pwd.pw_name); + free(buf); + return name; +}
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Mon May 13 16:26:16 2013 -0600 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Tue May 21 12:43:17 2013 -0400 @@ -36,7 +36,6 @@ package com.redhat.thermostat.agent.internal; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Test; @@ -44,6 +43,8 @@ import com.redhat.thermostat.testutils.StubBundleContext; import com.redhat.thermostat.utils.management.MXBeanConnectionPool; import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; +import com.redhat.thermostat.utils.username.UserNameUtil; +import com.redhat.thermostat.utils.username.internal.UserNameUtilImpl; public class ActivatorTest { @Test @@ -56,9 +57,6 @@ activator.start(context); assertTrue(context.isServiceRegistered(MXBeanConnectionPool.class.getName(), MXBeanConnectionPoolImpl.class)); - - activator.stop(context); - - assertFalse(context.isServiceRegistered(MXBeanConnectionPool.class.getName(), MXBeanConnectionPoolImpl.class)); + assertTrue(context.isServiceRegistered(UserNameUtil.class.getName(), UserNameUtilImpl.class)); } }
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Mon May 13 16:26:16 2013 -0600 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/LocaleResources.java Tue May 21 12:43:17 2013 -0400 @@ -68,6 +68,8 @@ VM_INFO_JAVA_VERSION, VM_INFO_VIRTUAL_MACHINE, VM_INFO_VM_ARGUMENTS, + VM_INFO_USER, + VM_INFO_USER_UNKNOWN, COLUMN_HEADER_HOST_ID, COLUMN_HEADER_HOST,
--- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMInfoCommand.java Mon May 13 16:26:16 2013 -0600 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/VMInfoCommand.java Tue May 21 12:43:17 2013 -0400 @@ -117,15 +117,33 @@ } else { table.printLine(translator.localize(LocaleResources.VM_INFO_STOP_TIME).getContents(), new Date(vmInfo.getStopTimeStamp()).toString()); } + printUserInfo(vmInfo, table); table.printLine(translator.localize(LocaleResources.VM_INFO_MAIN_CLASS).getContents(), vmInfo.getMainClass()); table.printLine(translator.localize(LocaleResources.VM_INFO_COMMAND_LINE).getContents(), vmInfo.getJavaCommandLine()); table.printLine(translator.localize(LocaleResources.VM_INFO_JAVA_VERSION).getContents(), vmInfo.getJavaVersion()); table.printLine(translator.localize(LocaleResources.VM_INFO_VIRTUAL_MACHINE).getContents(), vmInfo.getVmName()); table.printLine(translator.localize(LocaleResources.VM_INFO_VM_ARGUMENTS).getContents(), vmInfo.getVmArguments()); - + PrintStream out = ctx.getConsole().getOutput(); table.render(out); } + private void printUserInfo(VmInfo vmInfo, TableRenderer table) { + // Check if we have valid user info + long uid = vmInfo.getUid(); + String user; + if (uid >= 0) { + user = String.valueOf(uid); + String username = vmInfo.getUsername(); + if (username != null) { + user += "(" + username + ")"; + } + } + else { + user = translator.localize(LocaleResources.VM_INFO_USER_UNKNOWN).getContents(); + } + table.printLine(translator.localize(LocaleResources.VM_INFO_USER).getContents(), user); + } + }
--- a/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Mon May 13 16:26:16 2013 -0600 +++ b/client/cli/src/main/resources/com/redhat/thermostat/client/cli/strings.properties Tue May 21 12:43:17 2013 -0400 @@ -26,6 +26,8 @@ VM_INFO_JAVA_VERSION = Java version: VM_INFO_VIRTUAL_MACHINE = Virtual machine: VM_INFO_VM_ARGUMENTS = VM arguments: +VM_INFO_USER = User ID: +VM_INFO_USER_UNKNOWN = <Unknown> COLUMN_HEADER_HOST_ID = HOST_ID COLUMN_HEADER_HOST = HOST
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListVMsCommandTest.java Mon May 13 16:26:16 2013 -0600 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ListVMsCommandTest.java Tue May 21 12:43:17 2013 -0400 @@ -101,7 +101,7 @@ HostRef host1 = new HostRef("123", "h1"); VmRef vm1 = new VmRef(host1, 1, "n"); - VmInfo vm1Info = new VmInfo(1, 0, 1, "", "", "", "", "", "", "", "", null, null, null); + VmInfo vm1Info = new VmInfo(1, 0, 1, "", "", "", "", "", "", "", "", null, null, null, -1, null); when(hostsDAO.getHosts()).thenReturn(Arrays.asList(host1)); when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(vm1)); when(vmsDAO.getVmInfo(eq(vm1))).thenReturn(vm1Info); @@ -130,7 +130,7 @@ VmRef vm2 = new VmRef(host1, 2, "n1"); VmRef vm3 = new VmRef(host2, 123456, "longvmname"); - VmInfo vmInfo = new VmInfo(1, 0, 1, "", "", "", "", "", "", "", "", null, null, null); + VmInfo vmInfo = new VmInfo(1, 0, 1, "", "", "", "", "", "", "", "", null, null, null, -1, null); when(vmsDAO.getVMs(host1)).thenReturn(Arrays.asList(vm1, vm2)); when(vmsDAO.getVMs(host2)).thenReturn(Arrays.asList(vm3));
--- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VMInfoCommandTest.java Mon May 13 16:26:16 2013 -0600 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/VMInfoCommandTest.java Tue May 21 12:43:17 2013 -0400 @@ -106,7 +106,7 @@ start.set(2012, 5, 7, 15, 32, 0); Calendar end = Calendar.getInstance(); end.set(2013, 10, 1, 1, 22, 0); - VmInfo vmInfo = new VmInfo(234, start.getTimeInMillis(), end.getTimeInMillis(), "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new String[0]); + VmInfo vmInfo = new VmInfo(234, start.getTimeInMillis(), end.getTimeInMillis(), "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new String[0], 2000, "myUser"); when(vmsDAO.getVmInfo(vm)).thenReturn(vmInfo); when(vmsDAO.getVmInfo(new VmRef(host, 9876, "dummy"))).thenThrow(new DAOException("Unknown VM ID: 9876")); when(vmsDAO.getVMs(host)).thenReturn(Arrays.asList(vm)); @@ -124,6 +124,55 @@ String expected = "Process ID: 234\n" + "Start time: Thu Jun 07 15:32:00 UTC 2012\n" + "Stop time: Fri Nov 01 01:22:00 UTC 2013\n" + + "User ID: 2000(myUser)\n" + + "Main class: mainClass\n" + + "Command line: commandLine\n" + + "Java version: vmVersion\n" + + "Virtual machine: vmName\n" + + "VM arguments: vmArguments\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); + } + + @Test + public void testVmInfoNoUid() throws CommandException { + VmInfo info = vmsDAO.getVmInfo(vm); + // Set parameters to those where user info cannot be obtained + info.setUid(-1); + info.setUsername(null); + context.registerService(VmInfoDAO.class, vmsDAO, null); + cmd = new VMInfoCommand(context); + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "Process ID: 234\n" + + "Start time: Thu Jun 07 15:32:00 UTC 2012\n" + + "Stop time: Fri Nov 01 01:22:00 UTC 2013\n" + + "User ID: <Unknown>\n" + + "Main class: mainClass\n" + + "Command line: commandLine\n" + + "Java version: vmVersion\n" + + "Virtual machine: vmName\n" + + "VM arguments: vmArguments\n"; + assertEquals(expected, cmdCtxFactory.getOutput()); + } + + @Test + public void testVmInfoNoUsername() throws CommandException { + VmInfo info = vmsDAO.getVmInfo(vm); + // Set parameters to those where user info cannot be obtained + info.setUid(2000); + info.setUsername(null); + context.registerService(VmInfoDAO.class, vmsDAO, null); + cmd = new VMInfoCommand(context); + SimpleArguments args = new SimpleArguments(); + args.addArgument("vmId", "234"); + args.addArgument("hostId", "123"); + cmd.run(cmdCtxFactory.createContext(args)); + String expected = "Process ID: 234\n" + + "Start time: Thu Jun 07 15:32:00 UTC 2012\n" + + "Stop time: Fri Nov 01 01:22:00 UTC 2013\n" + + "User ID: 2000\n" + "Main class: mainClass\n" + "Command line: commandLine\n" + "Java version: vmVersion\n" + @@ -157,6 +206,7 @@ String expected = "Process ID: 234\n" + "Start time: Thu Jun 07 15:32:00 UTC 2012\n" + "Stop time: Fri Nov 01 01:22:00 UTC 2013\n" + + "User ID: 2000(myUser)\n" + "Main class: mainClass\n" + "Command line: commandLine\n" + "Java version: vmVersion\n" + @@ -202,7 +252,7 @@ cmd = new VMInfoCommand(context); Calendar start = Calendar.getInstance(); start.set(2012, 5, 7, 15, 32, 0); - VmInfo vmInfo = new VmInfo(234, start.getTimeInMillis(), Long.MIN_VALUE, "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new String[0]); + VmInfo vmInfo = new VmInfo(234, start.getTimeInMillis(), Long.MIN_VALUE, "vmVersion", "javaHome", "mainClass", "commandLine", "vmName", "vmInfo", "vmVersion", "vmArguments", new HashMap<String,String>(), new HashMap<String,String>(), new String[0], 2000, "myUser"); when(vmsDAO.getVmInfo(vm)).thenReturn(vmInfo); SimpleArguments args = new SimpleArguments(); @@ -212,6 +262,7 @@ String expected = "Process ID: 234\n" + "Start time: Thu Jun 07 15:32:00 UTC 2012\n" + "Stop time: <Running>\n" + + "User ID: 2000(myUser)\n" + "Main class: mainClass\n" + "Command line: commandLine\n" + "Java version: vmVersion\n" +
--- a/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Mon May 13 16:26:16 2013 -0600 +++ b/client/swing/src/test/java/com/redhat/thermostat/client/swing/internal/MainWindowControllerImplTest.java Tue May 21 12:43:17 2013 -0400 @@ -567,7 +567,7 @@ @Test public void verityVMActionsAreShown() { - VmInfo vmInfo = new VmInfo(0, 1, 2, null, null, null, null, null, null, null, null, null, null, null); + VmInfo vmInfo = new VmInfo(0, 1, 2, null, null, null, null, null, null, null, null, null, null, null, -1, null); when(mockVmsDAO.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo); VmRef ref = mock(VmRef.class);
--- a/distribution/pom.xml Mon May 13 16:26:16 2013 -0600 +++ b/distribution/pom.xml Tue May 21 12:43:17 2013 -0400 @@ -231,6 +231,8 @@ todir="${project.build.directory}/libs/native" /> <copy file="${main.basedir}/agent/core/target/libHostNameWrapper.so" todir="${project.build.directory}/libs/native" /> + <copy file="${main.basedir}/agent/core/target/libUserNameUtilWrapper.so" + todir="${project.build.directory}/libs/native" /> </target> </configuration> <goals>
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/dao/VmInfoDAO.java Mon May 13 16:26:16 2013 -0600 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/dao/VmInfoDAO.java Tue May 21 12:43:17 2013 -0400 @@ -65,13 +65,16 @@ static final Key<List<String>> librariesKey = new Key<>("loadedNativeLibraries", false); static final Key<Long> startTimeKey = new Key<>("startTimeStamp", false); static final Key<Long> stopTimeKey = new Key<>("stopTimeStamp", false); + static final Key<Long> uidKey = new Key<>("uid", false); + static final Key<String> usernameKey = new Key<>("username", false); static final Category<VmInfo> vmInfoCategory = new Category<>("vm-info", VmInfo.class, Key.AGENT_ID, Key.VM_ID, vmPidKey, runtimeVersionKey, javaHomeKey, mainClassKey, commandLineKey, vmArgumentsKey, vmNameKey, vmInfoKey, vmVersionKey, propertiesKey, environmentKey, librariesKey, - startTimeKey, stopTimeKey); + startTimeKey, stopTimeKey, + uidKey, usernameKey); public VmInfo getVmInfo(VmRef ref);
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/model/VmInfo.java Mon May 13 16:26:16 2013 -0600 +++ b/storage/core/src/main/java/com/redhat/thermostat/storage/model/VmInfo.java Tue May 21 12:43:17 2013 -0400 @@ -98,6 +98,8 @@ private Map<String, String> properties = new HashMap<String, String>(); private Map<String, String> environment = new HashMap<String, String>(); private String[] loadedNativeLibraries; + private long uid; + private String username; public VmInfo() { /* use defaults */ @@ -107,7 +109,8 @@ String javaVersion, String javaHome, String mainClass, String commandLine, String vmName, String vmInfo, String vmVersion, String vmArguments, - Map<String, String> properties, Map<String, String> environment, String[] loadedNativeLibraries) { + Map<String, String> properties, Map<String, String> environment, String[] loadedNativeLibraries, + long uid, String username) { this.vmPid = vmPid; this.startTime = startTime; this.stopTime = stopTime; @@ -122,6 +125,8 @@ this.properties = properties; this.environment = environment; this.loadedNativeLibraries = loadedNativeLibraries; + this.uid = uid; + this.username = username; } @Persist @@ -318,5 +323,33 @@ public void setLoadedNativeLibraries(String[] loadedNativeLibraries) { this.loadedNativeLibraries = loadedNativeLibraries; } + + /** + * Returns the system user id for the owner of this JVM process, + * or -1 if an owner could not be found. + */ + @Persist + public long getUid() { + return uid; + } + + @Persist + public void setUid(long uid) { + this.uid = uid; + } + + /** + * Returns the system user name for the owner of this JVM process, + * or null if an owner could not be found. + */ + @Persist + public String getUsername() { + return username; + } + + @Persist + public void setUsername(String username) { + this.username = username; + } }
--- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/VmInfoDAOTest.java Mon May 13 16:26:16 2013 -0600 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/dao/VmInfoDAOTest.java Tue May 21 12:43:17 2013 -0400 @@ -82,6 +82,8 @@ private Map<String, String> props; private Map<String, String> env; private String[] libs; + private long uid; + private String username; @Before public void setUp() { @@ -99,6 +101,8 @@ props = new HashMap<>(); env = new HashMap<>(); libs = new String[0]; + uid = 2000; + username = "myUser"; } @Test @@ -121,7 +125,9 @@ assertTrue(keys.contains(new Key<List<String>>("loadedNativeLibraries", false))); assertTrue(keys.contains(new Key<Long>("startTimeStamp", false))); assertTrue(keys.contains(new Key<Long>("stopTimeStamp", false))); - assertEquals(16, keys.size()); + assertTrue(keys.contains(new Key<Long>("uid", false))); + assertTrue(keys.contains(new Key<Long>("username", false))); + assertEquals(18, keys.size()); } @Test @@ -130,7 +136,7 @@ Storage storage = mock(Storage.class); Query query = mock(Query.class); when(storage.createQuery(any(Category.class))).thenReturn(query); - VmInfo expected = new VmInfo(vmId, startTime, stopTime, jVersion, jHome, mainClass, commandLine, vmName, vmInfo, vmVersion, vmArgs, props, env, libs); + VmInfo expected = new VmInfo(vmId, startTime, stopTime, jVersion, jHome, mainClass, commandLine, vmName, vmInfo, vmVersion, vmArgs, props, env, libs, uid, username); Cursor cursor = mock(Cursor.class); when(cursor.hasNext()).thenReturn(true).thenReturn(false); when(cursor.next()).thenReturn(expected).thenReturn(null); @@ -262,7 +268,7 @@ VmInfo info = new VmInfo(vmId, startTime, stopTime, jVersion, jHome, mainClass, commandLine, vmName, vmInfo, vmVersion, vmArgs, - props, env, libs); + props, env, libs, uid, username); VmInfoDAO dao = new VmInfoDAOImpl(storage); dao.putVmInfo(info);
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java Mon May 13 16:26:16 2013 -0600 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/JvmStatHostListener.java Tue May 21 12:43:17 2013 -0400 @@ -40,7 +40,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; import java.util.logging.Level; import java.util.logging.Logger; @@ -52,8 +51,8 @@ import sun.jvmstat.monitor.event.HostListener; import sun.jvmstat.monitor.event.VmStatusChangeEvent; -import com.redhat.thermostat.agent.VmStatusListener; import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.backend.system.ProcessUserInfoBuilder.ProcessUserInfo; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.model.VmInfo; @@ -69,9 +68,12 @@ private VmStatusChangeNotifier notifier; - JvmStatHostListener(VmInfoDAO vmInfoDAO, VmStatusChangeNotifier notifier) { + private ProcessUserInfoBuilder userInfoBuilder; + + JvmStatHostListener(VmInfoDAO vmInfoDAO, VmStatusChangeNotifier notifier, ProcessUserInfoBuilder userInfoBuilder) { this.vmInfoDAO = vmInfoDAO; this.notifier = notifier; + this.userInfoBuilder = userInfoBuilder; } @Override @@ -135,11 +137,12 @@ Map<String, String> environment = new ProcessEnvironmentBuilder(dataSource).build(vmId); // TODO actually figure out the loaded libraries. String[] loadedNativeLibraries = new String[0]; + ProcessUserInfo userInfo = userInfoBuilder.build(vmId); VmInfo info = new VmInfo(vmId, startTime, stopTime, extractor.getJavaVersion(), extractor.getJavaHome(), extractor.getMainClass(), extractor.getCommandLine(), extractor.getVmName(), extractor.getVmInfo(), extractor.getVmVersion(), extractor.getVmArguments(), - properties, environment, loadedNativeLibraries); + properties, environment, loadedNativeLibraries, userInfo.getUid(), userInfo.getUsername()); vmInfoDAO.putVmInfo(info); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/ProcessUserInfoBuilder.java Tue May 21 12:43:17 2013 -0400 @@ -0,0 +1,131 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.backend.system; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.redhat.thermostat.utils.ProcDataSource; +import com.redhat.thermostat.utils.username.UserNameUtil; + +class ProcessUserInfoBuilder { + + private static final ProcessUserInfo NON_EXISTENT_USER = new ProcessUserInfo(); + private static final String PROC_STATUS_UID = "Uid:"; + private static final Logger logger = LoggingUtils.getLogger(ProcessUserInfoBuilder.class); + private ProcDataSource source; + private UserNameUtil userNameUtil; + + ProcessUserInfoBuilder(ProcDataSource source, UserNameUtil userNameUtil) { + this.source = source; + this.userNameUtil = userNameUtil; + } + + static class ProcessUserInfo { + + private long uid; + private String username; + + ProcessUserInfo(long uid, String username) { + this.uid = uid; + this.username = username; + } + + ProcessUserInfo() { + this.uid = -1; + this.username = null; + } + + public long getUid() { + return uid; + } + + public String getUsername() { + return username; + } + } + + ProcessUserInfo build(int pid) { + ProcessUserInfo info = NON_EXISTENT_USER; + try { + Reader reader = source.getStatusReader(pid); + long uid = getUidFromProcfs(new BufferedReader(reader)); + String name = userNameUtil.getUserName(uid); + info = new ProcessUserInfo(uid, name); + } catch (IOException e) { + logger.log(Level.WARNING, "Unable to read user info for " + pid, e); + } + + return info; + } + + /* + * Look for the following line: + * Uid: <RealUid> <EffectiveUid> <SavedUid> <FSUid> + */ + private long getUidFromProcfs(BufferedReader br) throws IOException { + long uid = -1; + String line; + while ((line = br.readLine()) != null) { + line = line.trim(); + if (line.startsWith(PROC_STATUS_UID)) { + String[] parts = line.split("\\s+"); + if (parts.length == 5) { + try { + // Use Real UID + uid = Long.parseLong(parts[1]); + } catch (NumberFormatException e) { + throw new IOException("Unexpected output from ps command", e); + } + } + else { + throw new IOException("Expected 5 parts from split /proc/${pid}/status output, got " + parts.length); + } + } + } + if (uid < 0) { + throw new IOException("Unable to determine UID from /proc/${pid}/status"); + } + return uid; + } + + +}
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Mon May 13 16:26:16 2013 -0600 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Tue May 21 12:43:17 2013 -0400 @@ -54,6 +54,7 @@ import com.redhat.thermostat.storage.dao.VmInfoDAO; import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; import com.redhat.thermostat.utils.ProcDataSource; +import com.redhat.thermostat.utils.username.UserNameUtil; public class SystemBackend extends BaseBackend { @@ -74,7 +75,7 @@ public SystemBackend(HostInfoDAO hostInfoDAO, NetworkInterfaceInfoDAO netInfoDAO, VmInfoDAO vmInfoDAO, - Version version, VmStatusChangeNotifier notifier) { + Version version, VmStatusChangeNotifier notifier, UserNameUtil userNameUtil) { super("System Backend", "Gathers basic information from the system", "Red Hat, Inc.", @@ -84,7 +85,7 @@ ProcDataSource source = new ProcDataSource(); hostInfoBuilder = new HostInfoBuilder(source); - hostListener = new JvmStatHostListener(vmInfoDAO, notifier); + hostListener = new JvmStatHostListener(vmInfoDAO, notifier, new ProcessUserInfoBuilder(source, userNameUtil)); } @Override
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java Mon May 13 16:26:16 2013 -0600 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/osgi/SystemBackendActivator.java Tue May 21 12:43:17 2013 -0400 @@ -52,6 +52,7 @@ import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.utils.username.UserNameUtil; @SuppressWarnings("rawtypes") public class SystemBackendActivator implements BundleActivator { @@ -71,7 +72,8 @@ BackendService.class, HostInfoDAO.class, NetworkInterfaceInfoDAO.class, - VmInfoDAO.class + VmInfoDAO.class, + UserNameUtil.class }; tracker = new MultipleServiceTracker(context, deps, new Action() { @Override @@ -80,8 +82,9 @@ NetworkInterfaceInfoDAO netInfoDAO = (NetworkInterfaceInfoDAO) services .get(NetworkInterfaceInfoDAO.class.getName()); VmInfoDAO vmInfoDAO = (VmInfoDAO) services.get(VmInfoDAO.class.getName()); + UserNameUtil userNameUtil = (UserNameUtil) services.get(UserNameUtil.class.getName()); Version version = new Version(context.getBundle()); - backend = new SystemBackend(hostInfoDAO, netInfoDAO, vmInfoDAO, version, notifier); + backend = new SystemBackend(hostInfoDAO, netInfoDAO, vmInfoDAO, version, notifier, userNameUtil); reg = context.registerService(Backend.class, backend, null); }
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java Mon May 13 16:26:16 2013 -0600 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/JvmStatHostListenerTest.java Tue May 21 12:43:17 2013 -0400 @@ -65,6 +65,7 @@ import sun.jvmstat.monitor.event.VmStatusChangeEvent; import com.redhat.thermostat.agent.VmStatusListener.Status; +import com.redhat.thermostat.backend.system.ProcessUserInfoBuilder.ProcessUserInfo; import com.redhat.thermostat.storage.model.VmInfo; import com.redhat.thermostat.storage.dao.VmInfoDAO; @@ -78,6 +79,8 @@ private static String INFO_VMINFO = "Info"; private static String INFO_VMNAME = "MyJVM"; private static String INFO_VMVER = "90.01"; + private static long INFO_VMUSERID = 2000; + private static String INFO_VMUSERNAME = "User"; private JvmStatHostListener hostListener; private MonitoredHost host; @@ -91,8 +94,12 @@ public void setup() throws MonitorException, URISyntaxException { vmInfoDAO = mock(VmInfoDAO.class); notifier = mock(VmStatusChangeNotifier.class); + + ProcessUserInfoBuilder userInfoBuilder = mock(ProcessUserInfoBuilder.class); + ProcessUserInfo userInfo = new ProcessUserInfo(INFO_VMUSERID, INFO_VMUSERNAME); + when(userInfoBuilder.build(any(int.class))).thenReturn(userInfo); - hostListener = new JvmStatHostListener(vmInfoDAO, notifier); + hostListener = new JvmStatHostListener(vmInfoDAO, notifier, userInfoBuilder); host = mock(MonitoredHost.class); HostIdentifier hostId = mock(HostIdentifier.class); @@ -195,5 +202,7 @@ assertEquals(INFO_VMINFO, info.getVmInfo()); assertEquals(INFO_VMNAME, info.getVmName()); assertEquals(INFO_VMVER, info.getVmVersion()); + assertEquals(INFO_VMUSERID, info.getUid()); + assertEquals(INFO_VMUSERNAME, info.getUsername()); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/ProcessUserInfoBuilderTest.java Tue May 21 12:43:17 2013 -0400 @@ -0,0 +1,97 @@ +/* + * Copyright 2012, 2013 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 + * <http://www.gnu.org/licenses/>. + * + * 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.backend.system; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.StringReader; + +import org.junit.Test; + +import com.redhat.thermostat.backend.system.ProcessUserInfoBuilder.ProcessUserInfo; +import com.redhat.thermostat.common.tools.ApplicationException; +import com.redhat.thermostat.utils.ProcDataSource; +import com.redhat.thermostat.utils.username.UserNameUtil; + +public class ProcessUserInfoBuilderTest { + + @Test + public void testBuild() throws IOException, ApplicationException { + StringReader reader = new StringReader("Uid: 2000 2000 2000 2000"); + ProcDataSource source = mock(ProcDataSource.class); + UserNameUtil util = mock(UserNameUtil.class); + when(util.getUserName(2000)).thenReturn("myUser"); + when(source.getStatusReader(anyInt())).thenReturn(reader); + ProcessUserInfoBuilder builder = new ProcessUserInfoBuilder(source, util); + ProcessUserInfo info = builder.build(0); + + assertEquals(2000, info.getUid()); + assertEquals("myUser", info.getUsername()); + } + + @Test + public void testBuildErrorUid() throws IOException, ApplicationException { + StringReader reader = new StringReader(""); + ProcDataSource source = mock(ProcDataSource.class); + UserNameUtil util = mock(UserNameUtil.class); + when(source.getStatusReader(anyInt())).thenReturn(reader); + ProcessUserInfoBuilder builder = new ProcessUserInfoBuilder(source, util); + ProcessUserInfo info = builder.build(0); + + assertEquals(-1, info.getUid()); + assertEquals(null, info.getUsername()); + } + + @Test + public void testBuildErrorUsername() throws IOException, ApplicationException { + StringReader reader = new StringReader("Uid: 2000 2000 2000 2000"); + ProcDataSource source = mock(ProcDataSource.class); + UserNameUtil util = mock(UserNameUtil.class); + when(util.getUserName(2000)).thenReturn(null); + when(source.getStatusReader(anyInt())).thenReturn(reader); + ProcessUserInfoBuilder builder = new ProcessUserInfoBuilder(source, util); + ProcessUserInfo info = builder.build(0); + + assertEquals(2000, info.getUid()); + assertEquals(null, info.getUsername()); + } + +}
--- a/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java Mon May 13 16:26:16 2013 -0600 +++ b/system-backend/src/test/java/com/redhat/thermostat/backend/system/SystemBackendTest.java Tue May 21 12:43:17 2013 -0400 @@ -49,6 +49,7 @@ import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; import com.redhat.thermostat.storage.dao.VmInfoDAO; +import com.redhat.thermostat.utils.username.UserNameUtil; public class SystemBackendTest { @@ -65,8 +66,9 @@ when(version.getVersionNumber()).thenReturn(VERSION); VmStatusChangeNotifier notifier = mock(VmStatusChangeNotifier.class); + UserNameUtil util = mock(UserNameUtil.class); - b = new SystemBackend(hDAO, nDAO, vmInfoDAO, version, notifier); + b = new SystemBackend(hDAO, nDAO, vmInfoDAO, version, notifier, util); } @Test
--- a/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/VmOverviewView.java Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/VmOverviewView.java Tue May 21 12:43:17 2013 -0400 @@ -59,6 +59,8 @@ public abstract void setVmNameAndVersion(String vmNameAndVersion); public abstract void setVmArguments(String vmArguments); + + public abstract void setUserID(String userID); }
--- a/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/internal/VmOverviewController.java Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/core/internal/VmOverviewController.java Tue May 21 12:43:17 2013 -0400 @@ -120,6 +120,18 @@ view.setVmNameAndVersion(translator.localize(LocaleResources.VM_INFO_VM_NAME_AND_VERSION, actualVmName, actualVmVersion, actualVmInfo).getContents()); view.setVmArguments(info.getVmArguments()); + long uid = info.getUid(); + if (uid >= 0) { + String user = String.valueOf(uid); + String username = info.getUsername(); + if (username != null) { + user += "(" + username + ")"; + } + view.setUserID(user); + } + else { + view.setUserID(translator.localize(LocaleResources.VM_INFO_USER_UNKNOWN).getContents()); + } } }); timer.setInitialDelay(0);
--- a/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/locale/LocaleResources.java Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-core/src/main/java/com/redhat/thermostat/vm/overview/client/locale/LocaleResources.java Tue May 21 12:43:17 2013 -0400 @@ -58,6 +58,8 @@ VM_INFO_PROPERTIES, VM_INFO_ENVIRONMENT, VM_INFO_LIBRARIES, + VM_INFO_USER, + VM_INFO_USER_UNKNOWN, ; static final String RESOURCE_BUNDLE =
--- a/vm-overview/client-core/src/main/resources/com/redhat/thermostat/vm/overview/client/locale/strings.properties Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-core/src/main/resources/com/redhat/thermostat/vm/overview/client/locale/strings.properties Tue May 21 12:43:17 2013 -0400 @@ -17,3 +17,5 @@ VM_INFO_PROPERTIES = Properties VM_INFO_ENVIRONMENT = Environment VM_INFO_LIBRARIES = Native Libraries +VM_INFO_USER = User Id +VM_INFO_USER_UNKNOWN = <Unknown>
--- a/vm-overview/client-core/src/test/java/com/redhat/thermostat/vm/overview/client/core/internal/VmOverviewControllerTest.java Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-core/src/test/java/com/redhat/thermostat/vm/overview/client/core/internal/VmOverviewControllerTest.java Tue May 21 12:43:17 2013 -0400 @@ -37,7 +37,6 @@ package com.redhat.thermostat.vm.overview.client.core.internal; import static org.junit.Assert.assertNotNull; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNotNull; import static org.mockito.Mockito.doNothing; @@ -51,7 +50,6 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -68,7 +66,6 @@ import com.redhat.thermostat.storage.model.VmInfo; import com.redhat.thermostat.vm.overview.client.core.VmOverviewView; import com.redhat.thermostat.vm.overview.client.core.VmOverviewViewProvider; -import com.redhat.thermostat.vm.overview.client.core.internal.VmOverviewController; import com.redhat.thermostat.vm.overview.client.locale.LocaleResources; public class VmOverviewControllerTest { @@ -88,6 +85,8 @@ private static final Map<String, String> PROPS = Collections.emptyMap(); private static final Map<String, String> ENV = Collections.emptyMap(); private static final String[] LIBS = new String[0]; + private static final long UID = 2000; + private static final String USERNAME = "myUser"; private Timer timer; private Runnable timerAction; @@ -96,8 +95,7 @@ private VmOverviewController controller; @SuppressWarnings({ "rawtypes", "unchecked" }) - @Before - public void setUp() { + private void createController(VmInfo info) { // Setup timer timer = mock(Timer.class); ArgumentCaptor<Runnable> timerActionCaptor = ArgumentCaptor @@ -110,14 +108,10 @@ when(appSvc.getTimerFactory()).thenReturn(timerFactory); // Setup DAOs - VmInfo vmInfo = new VmInfo(VM_PID, START_TIME, STOP_TIME, JAVA_VERSION, - JAVA_HOME, MAIN_CLASS, COMMAND_LINE, VM_NAME, VM_INFO, - VM_VERSION, VM_ARGS, PROPS, ENV, LIBS); - VmRef ref = mock(VmRef.class); VmInfoDAO vmInfoDao = mock(VmInfoDAO.class); - when(vmInfoDao.getVmInfo(any(VmRef.class))).thenReturn(vmInfo); + when(vmInfoDao.getVmInfo(eq(ref))).thenReturn(info); // Setup View ArgumentCaptor<ActionListener> listenerCaptor = ArgumentCaptor @@ -133,8 +127,41 @@ timerAction = timerActionCaptor.getValue(); } + private VmInfo createVmInfo() { + VmInfo vmInfo = new VmInfo(VM_PID, START_TIME, STOP_TIME, JAVA_VERSION, + JAVA_HOME, MAIN_CLASS, COMMAND_LINE, VM_NAME, VM_INFO, + VM_VERSION, VM_ARGS, PROPS, ENV, LIBS, UID, USERNAME); + return vmInfo; + } + @Test public void verifyViewIsUpdatedWithData() { + createController(createVmInfo()); + timerAction.run(); + + DateFormat timestampFormat = controller.getDateFormat(); + verify(view).setVmPid(eq(String.valueOf(VM_PID))); + verify(view).setVmStartTimeStamp(eq(timestampFormat.format(new Date(START_TIME)))); + verify(view).setVmStopTimeStamp(eq(timestampFormat.format(new Date(STOP_TIME)))); + verify(view).setJavaVersion(eq(JAVA_VERSION)); + verify(view).setJavaHome(eq(JAVA_HOME)); + verify(view).setMainClass(eq(MAIN_CLASS)); + verify(view).setJavaCommandLine(eq(COMMAND_LINE)); + + verify(view).setVmNameAndVersion(eq(translator.localize(LocaleResources.VM_INFO_VM_NAME_AND_VERSION, + VM_NAME, VM_VERSION, VM_INFO).getContents())); + verify(view).setVmArguments(eq(VM_ARGS)); + String userID = String.valueOf(UID) + "(" + USERNAME + ")"; + verify(view).setUserID(eq(userID)); + } + + @Test + public void verifyViewIsUpdatedWithDataNoUid() { + VmInfo vmInfo = new VmInfo(VM_PID, START_TIME, STOP_TIME, JAVA_VERSION, + JAVA_HOME, MAIN_CLASS, COMMAND_LINE, VM_NAME, VM_INFO, + VM_VERSION, VM_ARGS, PROPS, ENV, LIBS, -1, null); + createController(vmInfo); + timerAction.run(); DateFormat timestampFormat = controller.getDateFormat(); @@ -149,10 +176,40 @@ verify(view).setVmNameAndVersion(eq(translator.localize(LocaleResources.VM_INFO_VM_NAME_AND_VERSION, VM_NAME, VM_VERSION, VM_INFO).getContents())); verify(view).setVmArguments(eq(VM_ARGS)); + + // Ensure user is unknown + verify(view).setUserID(eq(translator.localize(LocaleResources.VM_INFO_USER_UNKNOWN).getContents())); + } + + @Test + public void verifyViewIsUpdatedWithDataNoUsername() { + VmInfo vmInfo = new VmInfo(VM_PID, START_TIME, STOP_TIME, JAVA_VERSION, + JAVA_HOME, MAIN_CLASS, COMMAND_LINE, VM_NAME, VM_INFO, + VM_VERSION, VM_ARGS, PROPS, ENV, LIBS, UID, null); + createController(vmInfo); + + timerAction.run(); + + DateFormat timestampFormat = controller.getDateFormat(); + verify(view).setVmPid(eq(String.valueOf(VM_PID))); + verify(view).setVmStartTimeStamp(eq(timestampFormat.format(new Date(START_TIME)))); + verify(view).setVmStopTimeStamp(eq(timestampFormat.format(new Date(STOP_TIME)))); + verify(view).setJavaVersion(eq(JAVA_VERSION)); + verify(view).setJavaHome(eq(JAVA_HOME)); + verify(view).setMainClass(eq(MAIN_CLASS)); + verify(view).setJavaCommandLine(eq(COMMAND_LINE)); + + verify(view).setVmNameAndVersion(eq(translator.localize(LocaleResources.VM_INFO_VM_NAME_AND_VERSION, + VM_NAME, VM_VERSION, VM_INFO).getContents())); + verify(view).setVmArguments(eq(VM_ARGS)); + + // Ensure only user ID is shown + verify(view).setUserID(eq(String.valueOf(UID))); } @Test public void verifyTimerIsSetUpCorrectly() { + createController(createVmInfo()); assertNotNull(timer); verify(timer).setAction(isNotNull(Runnable.class)); @@ -164,6 +221,7 @@ @Test public void verifyTimerRunsWhenNeeded() { + createController(createVmInfo()); listener.actionPerformed(new ActionEvent<>(view, Action.VISIBLE)); verify(timer).start();
--- a/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/VmOverviewPanel.java Mon May 13 16:26:16 2013 -0600 +++ b/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/VmOverviewPanel.java Tue May 21 12:43:17 2013 -0400 @@ -73,6 +73,7 @@ private final ValueField javaVersion = new ValueField(""); private final ValueField vmNameAndVersion = new ValueField(""); private final ValueField vmArguments = new ValueField(""); + private final ValueField user = new ValueField(""); public VmOverviewPanel() { super(); @@ -192,6 +193,16 @@ } @Override + public void setUserID(final String userID) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + user.setText(userID); + } + }); + } + + @Override public Component getUiComponent() { return visiblePanel; } @@ -205,6 +216,7 @@ LabelField pidLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_PROCESS_ID)); LabelField startTimeLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_START_TIME)); LabelField stopTimeLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_STOP_TIME)); + LabelField userLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_USER)); SectionHeader javaSection = new SectionHeader(translator.localize(LocaleResources.VM_INFO_SECTION_JAVA)); @@ -228,6 +240,7 @@ .addComponent(pidLabel) .addComponent(startTimeLabel) .addComponent(stopTimeLabel) + .addComponent(userLabel) .addComponent(mainClassLabel) .addComponent(javaCommandLineLabel) .addComponent(javaVersionLabel) @@ -238,6 +251,7 @@ .addComponent(pid) .addComponent(startTimeStamp) .addComponent(stopTimeStamp) + .addComponent(user) .addComponent(mainClass) .addComponent(javaCommandLine) .addComponent(javaVersion) @@ -260,6 +274,10 @@ .addGroup(gl.createParallelGroup(Alignment.LEADING, false) .addComponent(stopTimeLabel) .addComponent(stopTimeStamp)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(userLabel) + .addComponent(user)) .addPreferredGap(ComponentPlacement.UNRELATED) .addComponent(javaSection) .addPreferredGap(ComponentPlacement.RELATED)