Mercurial > hg > thermostat-ng > agent
changeset 2719:597f67d95197
Remove agent proxy
Reviewed-by: jkang
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-June/023968.html
Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2017-July/024007.html
author | Elliott Baron <ebaron@redhat.com> |
---|---|
date | Tue, 11 Jul 2017 17:00:25 -0400 |
parents | 084cba24ae87 |
children | 3beac5ce66b4 |
files | agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java agent/core/pom.xml agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/AgentProxyClient.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/AgentProxyFilter.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolControl.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolTracker.java agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/ManagementAgentAttacher.java agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/AgentProxyClientTest.java agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolTrackerTest.java agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/ManagementAgentAttacherTest.java agent/pom.xml agent/proxy/pom.xml agent/proxy/server/pom.xml agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxy.java agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImpl.java agent/proxy/server/src/test/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImplTest.java agent/proxy/server/src/test/java/com/redhat/thermostat/agent/proxy/server/AgentProxyTest.java distribution/pom.xml distribution/scripts/thermostat-agent-proxy |
diffstat | 25 files changed, 438 insertions(+), 2349 deletions(-) [+] |
line wrap: on
line diff
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java Tue Jul 11 17:00:25 2017 -0400 @@ -48,8 +48,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -66,6 +64,8 @@ import com.redhat.thermostat.agent.Agent; import com.redhat.thermostat.agent.cli.internal.AgentApplication.ConfigurationCreator; import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; import com.redhat.thermostat.backend.BackendRegistry; import com.redhat.thermostat.common.ExitStatus; import com.redhat.thermostat.common.LaunchException; @@ -76,7 +76,6 @@ import com.redhat.thermostat.shared.config.InvalidConfigurationException; import com.redhat.thermostat.storage.core.WriterID; import com.redhat.thermostat.testutils.StubBundleContext; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolControl; @RunWith(PowerMockRunner.class) public class AgentApplicationTest { @@ -102,7 +101,6 @@ context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null); BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class); context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null); - context.registerService(MXBeanConnectionPoolControl.class, mock(MXBeanConnectionPoolControl.class), null); writerId = mock(WriterID.class); exitStatus = mock(ExitStatus.class);
--- a/agent/core/pom.xml Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/pom.xml Tue Jul 11 17:00:25 2017 -0400 @@ -89,11 +89,6 @@ </dependency> <dependency> <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-agent-ipc-server</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> <artifactId>thermostat-process-handler</artifactId> <version>${project.version}</version> </dependency>
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java Tue Jul 11 17:00:25 2017 -0400 @@ -36,7 +36,6 @@ package com.redhat.thermostat.agent; -import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; @@ -55,8 +54,6 @@ import com.redhat.thermostat.storage.core.WriterID; import com.redhat.thermostat.storage.model.AgentInformation; import com.redhat.thermostat.storage.model.BackendInformation; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolControl; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolTracker; /** * Represents the Agent running on a host. @@ -71,12 +68,9 @@ private final AgentInfoDAO agentDao; private final BackendInfoDAO backendDao; private final WriterID writerID; - private final MXBeanConnectionPoolTracker poolTracker; - private MXBeanConnectionPoolControl pool; private AgentInformation agentInfo; private boolean started = false; - private ActionListener<ThermostatExtensionRegistry.Action> backendRegistryListener = new ActionListener<ThermostatExtensionRegistry.Action>() @@ -126,20 +120,11 @@ public Agent(BackendRegistry registry, AgentStartupConfiguration config, AgentInfoDAO agentInfoDao, BackendInfoDAO backendInfoDao, WriterID writerId) { - this(registry, config, agentInfoDao, backendInfoDao, writerId, new MXBeanConnectionPoolTracker()); - } - - Agent(BackendRegistry registry, AgentStartupConfiguration config, - AgentInfoDAO agentInfoDao, BackendInfoDAO backendInfoDao, WriterID writerId, - MXBeanConnectionPoolTracker poolTracker) { this.backendRegistry = registry; this.config = config; this.agentDao = agentInfoDao; this.backendDao = backendInfoDao; this.writerID = writerId; - // Need MXBeanConnectionPool without breaking 1.x API - this.poolTracker = poolTracker; - poolTracker.open(); backendInfos = new ConcurrentHashMap<>(); @@ -153,13 +138,7 @@ agentDao.addAgentInformation(agentInfo); backendRegistry.start(); - try { - pool = poolTracker.getPoolWithTimeout(); - pool.start(); - started = true; - } catch (IOException e) { - throw new LaunchException("Failed to start JMX services for agent", e); - } + started = true; } else { logger.warning("Attempt to start agent when already started."); } @@ -180,7 +159,6 @@ public synchronized void stop() { if (started) { - backendRegistry.stop(); if (config.purge()) { @@ -189,14 +167,6 @@ updateAgentStatusToStopped(); } - if (pool.isStarted()) { - try { - pool.shutdown(); - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to cleanly shut down JMX services", e); - } - } - poolTracker.close(); started = false; } else { logger.warning("Attempt to stop agent which is not active");
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/internal/Activator.java Tue Jul 11 17:00:25 2017 -0400 @@ -36,12 +36,10 @@ package com.redhat.thermostat.agent.internal; -import java.io.IOException; +import java.io.File; import java.util.logging.Level; import java.util.logging.Logger; -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -49,29 +47,34 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; import com.redhat.thermostat.agent.config.AgentConfigsUtils; -import com.redhat.thermostat.agent.ipc.server.AgentIPCService; -import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool; -import com.redhat.thermostat.common.MultipleServiceTracker; -import com.redhat.thermostat.common.MultipleServiceTracker.Action; -import com.redhat.thermostat.common.MultipleServiceTracker.DependencyProvider; -import com.redhat.thermostat.common.portability.UserNameUtil; +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.shared.config.CommonPaths; import com.redhat.thermostat.shared.config.InvalidConfigurationException; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolControl; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; - public class Activator implements BundleActivator { private static final Logger logger = LoggingUtils.getLogger(Activator.class); + private final AgentConfigSetter configSetter; private ServiceTracker<CommonPaths, CommonPaths> commonPathsTracker; - private MultipleServiceTracker agentIPCTracker; - private MXBeanConnectionPoolControl pool; + + public Activator() { + this(new AgentConfigSetter()); + } + + Activator(AgentConfigSetter configSetter) { + this.configSetter = configSetter; + } @Override public void start(final BundleContext context) throws Exception { + AgentInfoDAO agentInfoDAO = new AgentInfoDAOImpl(); + context.registerService(AgentInfoDAO.class, agentInfoDAO, null); + + BackendInfoDAO backendInfoDAO = new BackendInfoDAOImpl(); + context.registerService(BackendInfoDAO.class, backendInfoDAO, null); // Track common paths separately and register storage credentials quickly // We need to do this since otherwise no storage credentials will be @@ -82,8 +85,7 @@ public CommonPaths addingService(ServiceReference<CommonPaths> ref) { CommonPaths paths = context.getService(ref); try { - AgentConfigsUtils.setConfigFiles(paths.getSystemAgentConfigurationFile(), paths.getUserAgentConfigurationFile()); - + configSetter.setConfigFiles(paths.getSystemAgentConfigurationFile(), paths.getUserAgentConfigurationFile()); } catch (InvalidConfigurationException e) { logger.log(Level.SEVERE, "Failed to start agent services", e); } @@ -101,57 +103,19 @@ } }); - // Track IPC related deps separately from CommonPaths/StorageCredentials - Class<?>[] deps = new Class<?>[] { CommonPaths.class, AgentIPCService.class, UserNameUtil.class }; - agentIPCTracker = new MultipleServiceTracker(context, deps, new Action() { - - @Override - public void dependenciesAvailable(DependencyProvider services) { - - try { - AgentInfoDAO agentInfoDAO = new AgentInfoDAOImpl(); - context.registerService(AgentInfoDAO.class, agentInfoDAO, null); - - BackendInfoDAO backendInfoDAO = new BackendInfoDAOImpl(); - context.registerService(BackendInfoDAO.class, backendInfoDAO, null); - - } catch (Exception e) { - throw new RuntimeException(e); - } - - AgentIPCService ipcService = services.get(AgentIPCService.class); - CommonPaths paths = services.get(CommonPaths.class); - UserNameUtil util = services.get(UserNameUtil.class); - pool = new MXBeanConnectionPoolImpl(paths.getSystemBinRoot(), util, ipcService); - context.registerService(MXBeanConnectionPool.class, pool, null); - // Used only internally - context.registerService(MXBeanConnectionPoolControl.class, pool, null); - } - - @Override - public void dependenciesUnavailable() { - try { - if (pool != null && pool.isStarted()) { - pool.shutdown(); - } - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to clean up IPC server", e); - } - } - }); commonPathsTracker.open(); - agentIPCTracker.open(); } @Override public void stop(BundleContext context) throws Exception { commonPathsTracker.close(); - agentIPCTracker.close(); } - - // Testing hook. - void setPool(MXBeanConnectionPoolControl pool) { - this.pool = pool; + + // For testing purposes + static class AgentConfigSetter { + void setConfigFiles(File systemConfigFile, File userConfigFile) { + AgentConfigsUtils.setConfigFiles(systemConfigFile, userConfigFile); + } } }
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/AgentProxyClient.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import java.io.File; -import java.io.IOException; - -import com.redhat.thermostat.common.ExitStatus; -import com.redhat.thermostat.shared.config.OS; - -class AgentProxyClient { - - private static final String SERVER_NAME = "thermostat-agent-proxy"; - - private final int pid; - private final ProcessCreator procCreator; - private final File binPath; - private final String username; - private final File ipcConfigFile; - private final String serverName; - - AgentProxyClient(int pid, String user, File binPath, File ipcConfigFile, String serverName) { - this(pid, user, binPath, ipcConfigFile, serverName, new ProcessCreator()); - } - - AgentProxyClient(int pid, String user, File binPath, File ipcConfigFile, String serverName, ProcessCreator procCreator) { - this.pid = pid; - this.binPath = binPath; - this.procCreator = procCreator; - this.username = user; - this.ipcConfigFile = ipcConfigFile; - this.serverName = serverName; - } - - void runProcess() throws IOException, InterruptedException { - // Start the agent proxy - String serverPath = binPath + File.separator + SERVER_NAME; - String[] args = OS.IS_UNIX - ? new String[] { serverPath, String.valueOf(pid), username, ipcConfigFile.getAbsolutePath(), serverName } - : new String[] { "cmd", "/C", serverPath+".cmd", String.valueOf(pid), username, ipcConfigFile.getAbsolutePath(), serverName }; - ProcessBuilder builder = new ProcessBuilder(args); - builder.inheritIO(); - Process proxy = procCreator.startProcess(builder); - - try { - // Wait for process to terminate - proxy.waitFor(); - if (proxy.exitValue() != ExitStatus.EXIT_SUCCESS) { - throw new IOException("Agent proxy for " + pid + " exited with non-zero exit code"); - } - } finally { - proxy.destroy(); - } - } - - static class ProcessCreator { - Process startProcess(ProcessBuilder builder) throws IOException { - return builder.start(); - } - } - -} -
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/AgentProxyFilter.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import com.redhat.thermostat.common.Filter; -import org.apache.felix.scr.annotations.Component; -import org.apache.felix.scr.annotations.Service; - -/** - * Prevents Agent Proxies from being monitored, which would create - * an infinite chain of agent proxies being created. - */ -@Component -@Service(value = AgentProxyFilter.class) -public class AgentProxyFilter extends Filter<String> { - - private static final String AGENT_PROXY_CLASS = "com.redhat.thermostat.agent.proxy.server.AgentProxy"; - - @Override - public boolean matches(String mainClass) { - return AGENT_PROXY_CLASS.equals(mainClass); - } - -} -
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolControl.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import java.io.IOException; - -import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool; - -public interface MXBeanConnectionPoolControl extends MXBeanConnectionPool { - - void start() throws IOException; - - boolean isStarted(); - - void shutdown() throws IOException; - -}
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImpl.java Tue Jul 11 17:00:25 2017 -0400 @@ -36,217 +36,53 @@ package com.redhat.thermostat.utils.management.internal; -import java.io.File; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.nio.file.FileSystems; -import java.nio.file.attribute.UserPrincipal; -import java.nio.file.attribute.UserPrincipalLookupService; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Objects; -import java.util.Set; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonParser; -import com.redhat.thermostat.agent.ipc.server.AgentIPCService; -import com.redhat.thermostat.agent.ipc.server.IPCMessage; -import com.redhat.thermostat.agent.ipc.server.ThermostatIPCCallbacks; -import com.redhat.thermostat.common.portability.ProcessUserInfo; -import com.redhat.thermostat.common.portability.ProcessUserInfoBuilder; -import com.redhat.thermostat.common.portability.ProcessUserInfoBuilderFactory; -import com.redhat.thermostat.common.portability.linux.ProcDataSource; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; + import com.redhat.thermostat.agent.utils.management.MXBeanConnection; import com.redhat.thermostat.agent.utils.management.MXBeanConnectionException; import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool; -import com.redhat.thermostat.common.portability.UserNameUtil; - -public class MXBeanConnectionPoolImpl implements MXBeanConnectionPoolControl, ThermostatIPCCallbacks { - private static final Object CURRENT_ENTRY_LOCK = new Object(); - private static final String IPC_SERVER_PREFIX = "agent-proxy"; - static final String JSON_PID = "pid"; - static final String JSON_JMX_URL = "jmxUrl"; - - // pid -> (usageCount, actualObject) +@Component +@Service(value = MXBeanConnectionPool.class) +public class MXBeanConnectionPoolImpl implements MXBeanConnectionPool { + + // Keys are PIDs of target JVMs private final Map<Integer, MXBeanConnectionPoolEntry> pool; - private final ConnectorCreator creator; - private final File binPath; - private final ProcessUserInfoBuilder userInfoBuilder; - private final AgentIPCService ipcService; - private final FileSystemUtils fsUtils; - // Keep track of IPC servers we created - private final Set<String> ipcServerNames; + private final ManagementAgentHelper helper; - /** - * Current {@link MXBeanConnectionPoolEntry} being created by {@link #acquire(int)} for use - * by {@link #messageReceived(IPCMessage)}. - * Since {@link #acquire(int)} is a synchronized method and blocks until - * {@link #messageReceived(IPCMessage)} is invoked, only one entry can be processed at a time. - * Access/modification must be synchronized using {@link #CURRENT_ENTRY_LOCK}. - */ - private MXBeanConnectionPoolEntry currentNewEntry; - private boolean started; - - public MXBeanConnectionPoolImpl(File binPath, UserNameUtil userNameUtil, AgentIPCService ipcService) { - this(new ConnectorCreator(), binPath, ProcessUserInfoBuilderFactory.createBuilder(new ProcDataSource(), userNameUtil), - ipcService, new FileSystemUtils()); + public MXBeanConnectionPoolImpl() { + this(new ManagementAgentHelper()); } - MXBeanConnectionPoolImpl(ConnectorCreator connectorCreator, File binPath, ProcessUserInfoBuilder userInfoBuilder, - AgentIPCService ipcService, FileSystemUtils fsUtils) { + MXBeanConnectionPoolImpl(ManagementAgentHelper helper) { this.pool = new HashMap<>(); - this.creator = connectorCreator; - this.binPath = binPath; - this.userInfoBuilder = userInfoBuilder; - this.ipcService = ipcService; - this.fsUtils = fsUtils; - this.currentNewEntry = null; - this.started = false; - this.ipcServerNames = new HashSet<>(); + this.helper = helper; } @Override - public synchronized void start() throws IOException { - this.started = true; - } - - @Override - public synchronized boolean isStarted() { - return started; - } - - @Override - public synchronized void shutdown() throws IOException { - this.started = false; - - // Delete all IPC servers created by this class - Set<String> serverNames = new HashSet<>(ipcServerNames); - for (String serverName : serverNames) { - deleteServerIfExists(serverName); - ipcServerNames.remove(serverName); - } - } - - private void deleteServerIfExists(String serverName) throws IOException { - if (ipcService.serverExists(serverName)) { - ipcService.destroyServer(serverName); - } - } - - @Override - public void messageReceived(IPCMessage message) { - synchronized (CURRENT_ENTRY_LOCK) { - MXBeanConnectionPoolEntry entry = currentNewEntry; - Objects.requireNonNull(entry, "currentNewEntry was null, should never happen"); - - ByteBuffer buf = message.get(); - CharBuffer charBuf = Charset.forName("UTF-8").decode(buf); - String dataString = charBuf.toString(); - try { - // Deserialize JSON data - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.create(); - JsonParser parser = new JsonParser(); - - // Get root of JsonObject tree - JsonElement parsed = parser.parse(dataString); - requireNonNull(parsed, "Received empty JSON data"); - if (!parsed.isJsonObject()) { - throw new IOException("Malformed data from agent proxy"); - } - JsonObject jsonObj = parsed.getAsJsonObject(); - - int pid = getPidFromJson(gson, jsonObj); - // Verify PID is correct - if (entry.getPid() != pid) { - throw new IOException("Expected message for PID: " + currentNewEntry.getPid() - + ", got message for PID: " + pid); - } - - String jmxUrl = getJmxUrlFromJson(gson, jsonObj, pid); - entry.setJmxUrl(jmxUrl); - } catch (JsonParseException | IOException e) { - entry.setException(e); - } - } - } - - private int getPidFromJson(Gson gson, JsonObject json) throws IOException { - JsonElement jsonPid = json.get(JSON_PID); - requireNonNull(jsonPid, "No PID received from agent proxy"); - return gson.fromJson(jsonPid, Integer.class); - } - - private String getJmxUrlFromJson(Gson gson, JsonObject json, int pid) throws IOException { - JsonElement jsonJmxUrl = json.get(JSON_JMX_URL); - requireNonNull(jsonJmxUrl, "No JMX service URL received from agent proxy for PID: " + pid); - return gson.fromJson(jsonJmxUrl, String.class); - } - - private void requireNonNull(JsonElement element, String errorMessage) throws IOException { - if (element == null || element.isJsonNull()) { - throw new IOException(errorMessage); - } - } - - @Override public synchronized MXBeanConnection acquire(int pid) throws MXBeanConnectionException { - checkRunning(); MXBeanConnectionPoolEntry data = pool.get(pid); if (data == null) { - MXBeanConnector connector = null; - ProcessUserInfo info = userInfoBuilder.build(pid); - String username = info.getUsername(); - if (username == null) { - throw new MXBeanConnectionException("Unable to determine owner of " + pid); - } - // Create an Agent Proxy IPC server for this user if it does not already exist - String serverName = IPC_SERVER_PREFIX + "-" + String.valueOf(info.getUid()); + data = new MXBeanConnectionPoolEntry(pid); try { - // Check if we created an IPC server for this user already - if (!ipcServerNames.contains(serverName)) { - createIPCServer(username, serverName); - } + // Attach to JVM and retrieve JMX service URL + ManagementAgentAttacher attacher = helper.createAttacher(pid); + attacher.attach(); + String jmxUrl = attacher.getConnectorAddress(); - data = new MXBeanConnectionPoolEntry(pid); - // Synchronized to ensure any previous callback has completely finished - // before changing currentNewEntry - synchronized (CURRENT_ENTRY_LOCK) { - this.currentNewEntry = data; - } - // Add this to the map early, so our callback can find it - pool.put(pid, data); - - // Start agent proxy which will send the JMX service URL to the IPC server we created - File configFile = ipcService.getConfigurationFile(); - AgentProxyClient proxy = creator.createAgentProxy(pid, username, binPath, configFile, serverName); - proxy.runProcess(); // Process completed when this returns - - // Block until we get a JMX service URL, or Exception - String jmxUrl = data.getJmxUrlOrBlock(); - connector = creator.createConnector(jmxUrl); + // Connect using JMX service URL + MXBeanConnector connector = helper.createConnector(jmxUrl); MXBeanConnectionImpl connection = connector.connect(); data.setConnection(connection); + pool.put(pid, data); } catch (IOException e) { pool.remove(pid); throw new MXBeanConnectionException(e); - } catch (InterruptedException e) { - pool.remove(pid); - Thread.currentThread().interrupt(); - throw new MXBeanConnectionException(e); - } finally { - // Reset currentNewEntry - synchronized (CURRENT_ENTRY_LOCK) { - this.currentNewEntry = null; - } } } else { data.incrementUsageCount(); @@ -254,24 +90,8 @@ return data.getConnection(); } - private void createIPCServer(String username, String serverName) throws IOException { - // Lookup UserPrincipal using username - UserPrincipalLookupService lookup = fsUtils.getUserPrincipalLookupService(); - UserPrincipal principal = lookup.lookupPrincipalByName(username); - deleteServerIfExists(serverName); // Chance of old server left behind - ipcService.createServer(serverName, this, principal); - ipcServerNames.add(serverName); - } - - private void checkRunning() throws MXBeanConnectionException { - if (!started) { - throw new MXBeanConnectionException(MXBeanConnectionPool.class.getSimpleName() + " service is not running"); - } - } - @Override public synchronized void release(int pid, MXBeanConnection toRelease) throws MXBeanConnectionException { - checkRunning(); MXBeanConnectionPoolEntry data = pool.get(pid); if (data == null) { throw new MXBeanConnectionException("Unknown pid: " + pid); @@ -295,9 +115,9 @@ } } - static class ConnectorCreator { - AgentProxyClient createAgentProxy(int pid, String user, File binPath, File ipcConfigFile, String serverName) { - return new AgentProxyClient(pid, user, binPath, ipcConfigFile, serverName); + static class ManagementAgentHelper { + ManagementAgentAttacher createAttacher(int pid) { + return new ManagementAgentAttacher(pid); } MXBeanConnector createConnector(String jmxUrl) throws IOException { @@ -306,21 +126,5 @@ } } - static class FileSystemUtils { - UserPrincipalLookupService getUserPrincipalLookupService() { - return FileSystems.getDefault().getUserPrincipalLookupService(); - } - } - - // For testing purposes - MXBeanConnectionPoolEntry getPoolEntry(int pid) { - return pool.get(pid); - } - - // For testing purposes - synchronized Set<String> getIPCServerNames() { - return ipcServerNames; - } - }
--- a/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolTracker.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,121 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; -import org.osgi.framework.ServiceReference; -import org.osgi.util.tracker.ServiceTracker; - -import com.redhat.thermostat.agent.Agent; -import com.redhat.thermostat.common.LaunchException; - -public class MXBeanConnectionPoolTracker extends ServiceTracker { - - private static final long TIMEOUT_MS = 5000L; - - private final LatchCreator latchCreator; - private CountDownLatch latch; - private MXBeanConnectionPoolControl pool; - - - public MXBeanConnectionPoolTracker() { - this(findBundleContext(), new LatchCreator()); - } - - MXBeanConnectionPoolTracker(BundleContext context, LatchCreator latchCreator) { - super(context, MXBeanConnectionPoolControl.class.getName(), null); - this.latchCreator = latchCreator; - this.latch = latchCreator.create(); - this.pool = null; - } - - @Override - public synchronized Object addingService(ServiceReference reference) { - MXBeanConnectionPoolControl pool = (MXBeanConnectionPoolControl) super.addingService(reference); - this.pool = pool; - latch.countDown(); - return pool; - } - - @Override - public synchronized void removedService(ServiceReference reference, Object service) { - // Reset latch - latch = latchCreator.create(); - pool = null; - super.removedService(reference, service); - } - - public synchronized MXBeanConnectionPoolControl getPoolWithTimeout() throws LaunchException { - // Wait for pool to become available - try { - latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - // Throw an exception if pool is still unavailable, or we got the wrong implementation type - if (pool == null) { - throw new LaunchException("JMX connection service is unavailable"); - } - return pool; - } - - // Used only by no-arg constructor - private static BundleContext findBundleContext() { - Bundle bundle = FrameworkUtil.getBundle(Agent.class); - Objects.requireNonNull(bundle, "No bundle associated with class: " + Agent.class.getName()); - return bundle.getBundleContext(); - } - - static class LatchCreator { - CountDownLatch create() { - return new CountDownLatch(1); - } - } - - // For testing purposes only - synchronized MXBeanConnectionPoolControl getPool() { - return pool; - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/utils/management/internal/ManagementAgentAttacher.java Tue Jul 11 17:00:25 2017 -0400 @@ -0,0 +1,166 @@ +/* + * 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 + * <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.management.internal; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.redhat.thermostat.common.utils.LoggingUtils; +import com.sun.tools.attach.AgentInitializationException; +import com.sun.tools.attach.AgentLoadException; +import com.sun.tools.attach.AttachNotSupportedException; +import com.sun.tools.attach.VirtualMachine; + +class ManagementAgentAttacher { + + private static final Logger logger = LoggingUtils.getLogger(ManagementAgentAttacher.class); + private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress"; + private static final String JCMD_NAME = "jcmd"; + private static final String JCMD_MANAGEMENT_AGENT_START_LOCAL = "ManagementAgent.start_local"; + + private final int pid; + private final VirtualMachineUtils vmUtils; + + private VirtualMachine vm; + private boolean attached; + private String connectorAddress; + + ManagementAgentAttacher(int pid) { + this(pid, new VirtualMachineUtils()); + } + + ManagementAgentAttacher(int pid, VirtualMachineUtils vmUtils) { + this.pid = pid; + this.vmUtils = vmUtils; + } + + void attach() throws IOException { + try { + vm = vmUtils.attach(String.valueOf(pid)); + attached = true; + + Properties props = vm.getAgentProperties(); + connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); + if (connectorAddress == null) { + props = vm.getSystemProperties(); + startManagementAgent(props); + logger.fine("Started management agent for vm '" + pid + "'"); + props = vm.getAgentProperties(); + connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); + } + } catch (IOException | AgentLoadException | AgentInitializationException | AttachNotSupportedException e) { + throw new IOException("Failed to load management agent into VM (pid: " + pid + ")", e); + } + } + + private void startManagementAgent(Properties props) throws IOException, + AgentLoadException, + AgentInitializationException { + String home = props.getProperty("java.home"); + try { + // JDK 7 and up have jcmd with JMX management agent start support. + startManagementAgentUsingJcmd(home, pid); + } catch (Exception e) { + // Fall back to old management-agent.jar behaviour. + logger.log(Level.FINE, "Failed to activate JMX agent via jcmd.", e); + startManagementAgentUsingJavaAgent(home); + } + } + + private void startManagementAgentUsingJcmd(String home, int vmPid) throws IOException, InterruptedException { + String jcmd = home + File.separator + "bin" + File.separator + JCMD_NAME; + File jcmdFile = new File(jcmd); + if (!jcmdFile.exists()) { + // java.home might be JRE home. Try one level up. + File binDir = new File(new File(home).getParentFile(), "bin"); + jcmd = binDir.getAbsolutePath() + File.separator + JCMD_NAME; + } + String[] args = new String[] { jcmd, + Integer.toString(vmPid), + JCMD_MANAGEMENT_AGENT_START_LOCAL }; + List<String> jcmdArgs = Arrays.asList(args); + logger.fine("Starting JMX management agent via JCMD: " + jcmdArgs); + Process process = startProcess(jcmdArgs); + int result = process.waitFor(); + if (result != 0) { + throw new IllegalStateException("Failed to execute jcmd. Exit code was: " + result); + } + } + + // Package private for testing + Process startProcess(List<String> jcmdArgs) throws IOException { + ProcessBuilder builder = new ProcessBuilder(jcmdArgs); + return builder.start(); + } + + private void startManagementAgentUsingJavaAgent(String home) throws AgentLoadException, AgentInitializationException, IOException { + String agent = home + File.separator + "lib" + File.separator + "management-agent.jar"; + logger.fine("Loading '" + agent + "' into VM (pid: " + pid + ")"); + vm.loadAgent(agent); + } + + boolean isAttached() { + return attached; + } + + String getConnectorAddress() throws IOException { + if (!attached) { + throw new IOException("Agent not attached to target VM"); + } + return connectorAddress; + } + + void detach() throws IOException { + if (attached) { + vm.detach(); + attached = false; + } + } + + static class VirtualMachineUtils { + VirtualMachine attach(String pid) throws AttachNotSupportedException, IOException { + return VirtualMachine.attach(pid); + } + } + +} +
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java Tue Jul 11 17:00:25 2017 -0400 @@ -48,13 +48,13 @@ import java.util.UUID; -import com.redhat.thermostat.agent.dao.AgentInfoDAO; -import com.redhat.thermostat.agent.dao.BackendInfoDAO; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.redhat.thermostat.agent.config.AgentStartupConfiguration; +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; import com.redhat.thermostat.backend.Backend; import com.redhat.thermostat.backend.BackendRegistry; import com.redhat.thermostat.common.ActionEvent; @@ -63,8 +63,6 @@ import com.redhat.thermostat.storage.core.WriterID; import com.redhat.thermostat.storage.model.AgentInformation; import com.redhat.thermostat.storage.model.BackendInformation; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolTracker; @SuppressWarnings({ "rawtypes", "unchecked" }) public class AgentTest { @@ -75,8 +73,6 @@ private AgentInfoDAO agentInfoDao; private BackendInfoDAO backendInfoDao; - private MXBeanConnectionPoolImpl pool; - private MXBeanConnectionPoolTracker poolTracker; @Before public void setUp() throws Exception { @@ -95,16 +91,12 @@ when(backend.isActive()).thenReturn(true); backendRegistry = mock(BackendRegistry.class); - pool = mock(MXBeanConnectionPoolImpl.class); - when(pool.isStarted()).thenReturn(true); - poolTracker = mock(MXBeanConnectionPoolTracker.class); - when(poolTracker.getPoolWithTimeout()).thenReturn(pool); } @SuppressWarnings("unused") @Test public void testAgentInit() throws Exception { - Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, null, poolTracker); + Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, null); verify(backendRegistry).addActionListener(any(ActionListener.class)); } @@ -116,7 +108,7 @@ UUID uuid = UUID.randomUUID(); WriterID id = mock(WriterID.class); when(id.getWriterID()).thenReturn(uuid.toString()); - Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id, poolTracker); + Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id); agent.start(); @@ -127,8 +119,6 @@ verify(agentInfoDao).addAgentInformation(argument.capture()); assertEquals(123, argument.getValue().getStartTime()); assertEquals(uuid.toString(), argument.getValue().getAgentId()); - - verify(pool).start(); } @Test @@ -137,7 +127,7 @@ // Start agent. WriterID id = mock(WriterID.class); - Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id, poolTracker); + Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id); verify(backendRegistry).addActionListener(backendListener.capture()); agent.start(); @@ -185,7 +175,7 @@ UUID uuid = UUID.randomUUID(); WriterID id = mock(WriterID.class); when(id.getWriterID()).thenReturn(uuid.toString()); - Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id, poolTracker); + Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id); agent.start(); // stop agent @@ -196,8 +186,6 @@ ArgumentCaptor<AgentInformation> argument = ArgumentCaptor.forClass(AgentInformation.class); verify(agentInfoDao, never()).updateAgentInformation(argument.capture()); //verify(storage, times(1)).purge(uuid.toString()); TODO - - verify(pool).shutdown(); } @Test @@ -208,7 +196,7 @@ when(config.purge()).thenReturn(false); WriterID id = mock(WriterID.class); - Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id, poolTracker); + Agent agent = new Agent(backendRegistry, config, agentInfoDao, backendInfoDao, id); agent.start(); // stop agent @@ -218,8 +206,6 @@ verify(agentInfoDao).updateAgentInformation(isA(AgentInformation.class)); //verify(storage, times(0)).purge(anyString()); TODO - - verify(pool).shutdown(); } }
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/internal/ActivatorTest.java Tue Jul 11 17:00:25 2017 -0400 @@ -37,85 +37,47 @@ package com.redhat.thermostat.agent.internal; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; import java.io.File; -import com.redhat.thermostat.common.portability.UserNameUtil; -import org.junit.Before; import org.junit.Test; -import org.osgi.framework.ServiceRegistration; -import com.redhat.thermostat.agent.ipc.server.AgentIPCService; -import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool; +import com.redhat.thermostat.agent.dao.AgentInfoDAO; +import com.redhat.thermostat.agent.dao.BackendInfoDAO; +import com.redhat.thermostat.agent.internal.Activator.AgentConfigSetter; import com.redhat.thermostat.shared.config.CommonPaths; -import com.redhat.thermostat.shared.config.NativeLibraryResolver; import com.redhat.thermostat.testutils.StubBundleContext; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolControl; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl; public class ActivatorTest { - private StubBundleContext context; - private ServiceRegistration ipcReg; - - @Before - public void setup() { - CommonPaths paths = mock(CommonPaths.class); - when(paths.getSystemNativeLibsRoot()).thenReturn(new File("target")); - when(paths.getUserAgentAuthConfigFile()).thenReturn(new File("not.exist.does.not.matter")); - NativeLibraryResolver.setCommonPaths(paths); - - context = new StubBundleContext(); - context.registerService(CommonPaths.class.getName(), paths, null); - - // required by MXBeanConnectionPoolImpl - UserNameUtil userNameUtil = mock(UserNameUtil.class); - context.registerService(UserNameUtil.class.getName(), userNameUtil, null); - - AgentIPCService ipcService = mock(AgentIPCService.class); - ipcReg = context.registerService(AgentIPCService.class.getName(), ipcService, null); - } - @Test public void verifyServiceIsRegistered() throws Exception { + StubBundleContext context = new StubBundleContext(); Activator activator = new Activator(); activator.start(context); - assertTrue(context.isServiceRegistered(MXBeanConnectionPool.class.getName(), MXBeanConnectionPoolImpl.class)); - assertTrue(context.isServiceRegistered(MXBeanConnectionPoolControl.class.getName(), MXBeanConnectionPoolImpl.class)); - } - - @Test - public void verifyPoolShutdown() throws Exception { - Activator activator = new Activator(); - activator.start(context); - - MXBeanConnectionPoolImpl pool = mock(MXBeanConnectionPoolImpl.class); - when(pool.isStarted()).thenReturn(true); - activator.setPool(pool); - - // Remove tracked service - ipcReg.unregister(); - - verify(pool).shutdown(); + assertTrue(context.isServiceRegistered(AgentInfoDAO.class.getName(), AgentInfoDAOImpl.class)); + assertTrue(context.isServiceRegistered(BackendInfoDAO.class.getName(), BackendInfoDAOImpl.class)); } @Test - public void verifyPoolShutdownNotStarted() throws Exception { - Activator activator = new Activator(); + public void verifyAgentConfig() throws Exception { + StubBundleContext context = new StubBundleContext(); + + CommonPaths paths = mock(CommonPaths.class); + File sysPropFile = mock(File.class); + when(paths.getSystemAgentConfigurationFile()).thenReturn(sysPropFile); + File userPropFile = mock(File.class); + when(paths.getUserAgentConfigurationFile()).thenReturn(userPropFile); + context.registerService(CommonPaths.class.getName(), paths, null); + + AgentConfigSetter configSetter = mock(AgentConfigSetter.class); + Activator activator = new Activator(configSetter); activator.start(context); - MXBeanConnectionPoolImpl pool = mock(MXBeanConnectionPoolImpl.class); - activator.setPool(pool); + verify(configSetter).setConfigFiles(sysPropFile, userPropFile); + } - // Remove tracked service - ipcReg.unregister(); - - verify(pool, never()).shutdown(); - } }
--- a/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/AgentProxyClientTest.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.lang.ProcessBuilder.Redirect; -import java.util.List; - -import com.redhat.thermostat.shared.config.OS; -import com.redhat.thermostat.testutils.TestUtils; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import com.redhat.thermostat.utils.management.internal.AgentProxyClient.ProcessCreator; - -public class AgentProxyClientTest { - - private static final String SERVER_NAME = "agent-proxy-2000"; - - private AgentProxyClient client; - private String user; - private File binPath; - private File ipcConfigFile; - private Process proxy; - private ProcessCreator procCreator; - - @Before - public void setup() throws Exception { - binPath = new File("/path/to/thermostat/bin"); - user = "Hello"; - ipcConfigFile = new File("/path/to/ipc/config"); - procCreator = mock(ProcessCreator.class); - proxy = mock(Process.class); - when(procCreator.startProcess(any(ProcessBuilder.class))).thenReturn(proxy); - client = new AgentProxyClient(9000, user, binPath, ipcConfigFile, SERVER_NAME, procCreator); - } - - @Test - public void testStart() throws Exception { - when(proxy.exitValue()).thenReturn(0); - client.runProcess(); - - ArgumentCaptor<ProcessBuilder> builderCaptor = ArgumentCaptor.forClass(ProcessBuilder.class); - verify(procCreator).startProcess(builderCaptor.capture()); - ProcessBuilder builder = builderCaptor.getValue(); - - // Check I/O redirection - assertEquals(Redirect.INHERIT, builder.redirectInput()); - assertEquals(Redirect.INHERIT, builder.redirectOutput()); - assertEquals(Redirect.INHERIT, builder.redirectError()); - - // Check process arguments - List<String> args = builder.command(); - int expectedArgCount = OS.IS_WINDOWS ? 7 : 5; // account for extra "cmd", "/c" args on Windows - assertEquals(expectedArgCount, args.size()); - - if (OS.IS_WINDOWS) { - final String arg2 = TestUtils.convertWinPathToUnixPath(args.get(2)); - final String arg5 = TestUtils.convertWinPathToUnixPath(args.get(5)); - - assertEquals("cmd", args.get(0)); - assertEquals("/C", args.get(1)); - assertEquals("/path/to/thermostat/bin/thermostat-agent-proxy.cmd", arg2); - assertEquals("9000", args.get(3)); - assertEquals("Hello", args.get(4)); - assertEquals("/path/to/ipc/config", arg5); - assertEquals(SERVER_NAME, args.get(6)); - } else { - assertEquals("/path/to/thermostat/bin/thermostat-agent-proxy", args.get(0)); - assertEquals("9000", args.get(1)); - assertEquals("Hello", args.get(2)); - assertEquals("/path/to/ipc/config", args.get(3)); - assertEquals(SERVER_NAME, args.get(4)); - } - - // Check cleanup - verify(proxy).waitFor(); - verify(proxy).destroy(); - } - - @Test - public void testStartBadExit() throws Exception { - when(proxy.exitValue()).thenReturn(-1); - - try { - client.runProcess(); - fail("Expected IOException"); - } catch (IOException e) { - verify(proxy).destroy(); - } - } - - @Test - public void testStartInterrupted() throws Exception { - when(proxy.waitFor()).thenThrow(new InterruptedException()); - - try { - client.runProcess(); - fail("Expected InterruptedException"); - } catch (InterruptedException e) { - verify(proxy).destroy(); - } - } - -} -
--- a/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolImplTest.java Tue Jul 11 17:00:25 2017 -0400 @@ -37,298 +37,61 @@ package com.redhat.thermostat.utils.management.internal; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import java.io.File; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.nio.file.attribute.UserPrincipal; -import java.nio.file.attribute.UserPrincipalLookupService; - -import com.redhat.thermostat.common.portability.ProcessUserInfo; -import com.redhat.thermostat.common.portability.ProcessUserInfoBuilder; import org.junit.Before; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.redhat.thermostat.agent.ipc.server.AgentIPCService; -import com.redhat.thermostat.agent.ipc.server.IPCMessage; import com.redhat.thermostat.agent.utils.management.MXBeanConnection; import com.redhat.thermostat.agent.utils.management.MXBeanConnectionException; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl.ConnectorCreator; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl.FileSystemUtils; +import com.redhat.thermostat.common.portability.ProcessUserInfo; +import com.redhat.thermostat.common.portability.ProcessUserInfoBuilder; +import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolImpl.ManagementAgentHelper; public class MXBeanConnectionPoolImplTest { - private static final String IPC_SERVER_NAME = "agent-proxy-8000"; - - private File binDir; - private AgentIPCService ipcService; - private AgentProxyClient proxy; + private static final String JMX_URL = "jmxUrl://hello"; + + private ManagementAgentAttacher attacher; private MXBeanConnectionPoolImpl pool; private MXBeanConnectionImpl connection; - private ConnectorCreator creator; + private ManagementAgentHelper helper; private MXBeanConnector connector; - private File ipcConfigFile; - private FileSystemUtils fsUtils; - private UserPrincipalLookupService lookup; - private UserPrincipal principal; private ProcessUserInfoBuilder builder; @Before public void setup() throws Exception { - binDir = mock(File.class); - ipcService = mock(AgentIPCService.class); connection = mock(MXBeanConnectionImpl.class); connector = mock(MXBeanConnector.class); - creator = mock(ConnectorCreator.class); - ipcConfigFile = mock(File.class); - fsUtils = mock(FileSystemUtils.class); + helper = mock(ManagementAgentHelper.class); - proxy = mock(AgentProxyClient.class); - when(creator.createConnector("jmxUrl://hello")).thenReturn(connector); + attacher = mock(ManagementAgentAttacher.class); + when(attacher.getConnectorAddress()).thenReturn(JMX_URL); + when(helper.createAttacher(8000)).thenReturn(attacher); + when(helper.createConnector(JMX_URL)).thenReturn(connector); when(connector.connect()).thenReturn(connection); builder = mock(ProcessUserInfoBuilder.class); ProcessUserInfo info = new ProcessUserInfo(8000, "Test"); when(builder.build(8000)).thenReturn(info); - lookup = mock(UserPrincipalLookupService.class); - when(fsUtils.getUserPrincipalLookupService()).thenReturn(lookup); - principal = mock(UserPrincipal.class); - when(lookup.lookupPrincipalByName("Test")).thenReturn(principal); - - when(ipcService.getConfigurationFile()).thenReturn(ipcConfigFile); - pool = new MXBeanConnectionPoolImpl(creator, binDir, builder, ipcService, fsUtils); - } - - @Test - public void testStart() throws Exception { - assertFalse(pool.isStarted()); - pool.start(); - assertTrue(pool.isStarted()); - } - - @Test - public void testShutdown() throws Exception { - pool.start(); - assertTrue(pool.isStarted()); - pool.shutdown(); - assertFalse(pool.isStarted()); - - } - - @Test - public void testShutdownCleanup() throws Exception { - pool.getIPCServerNames().add(IPC_SERVER_NAME); - pool.shutdown(); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService, never()).destroyServer(IPC_SERVER_NAME); - assertTrue(pool.getIPCServerNames().isEmpty()); - } - - @Test - public void testShutdownCleanupServerExists() throws Exception { - pool.getIPCServerNames().add(IPC_SERVER_NAME); - when(ipcService.serverExists(IPC_SERVER_NAME)).thenReturn(true); - pool.shutdown(); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService).destroyServer(IPC_SERVER_NAME); - assertTrue(pool.getIPCServerNames().isEmpty()); - } - - @Test - public void testShutdownCleanupMultipleServers() throws Exception { - pool.getIPCServerNames().add(IPC_SERVER_NAME); - pool.getIPCServerNames().add("agent-proxy-1001"); - - when(ipcService.serverExists(IPC_SERVER_NAME)).thenReturn(true); - when(ipcService.serverExists("agent-proxy-1001")).thenReturn(true); - - pool.shutdown(); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService).destroyServer(IPC_SERVER_NAME); - verify(ipcService).serverExists("agent-proxy-1001"); - verify(ipcService).destroyServer("agent-proxy-1001"); - assertTrue(pool.getIPCServerNames().isEmpty()); + pool = new MXBeanConnectionPoolImpl(helper); } @Test public void testAcquire() throws Exception { - final byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - - pool.start(); MXBeanConnection result = pool.acquire(8000); - verify(lookup).lookupPrincipalByName("Test"); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService, never()).destroyServer(IPC_SERVER_NAME); - verify(ipcService).createServer(IPC_SERVER_NAME, pool, principal); - verify(creator).createConnector("jmxUrl://hello"); - - assertNotNull(result); - assertEquals(result, connection); - - verify(connector).connect(); - } - - private void invokeCallbacksOnProxyCreation(final byte[] data) { - invokeCallbacksOnProxyCreation(data, 8000, "Test", IPC_SERVER_NAME); - } - - private void invokeCallbacksOnProxyCreation(final byte[] data, int pid, String username, String ipcServerName) { - when(creator.createAgentProxy(pid, username, binDir, ipcConfigFile, ipcServerName)).thenAnswer(new Answer<AgentProxyClient>() { - @Override - public AgentProxyClient answer(InvocationOnMock invocation) throws Throwable { - // Invoke callback - IPCMessage message = mock(IPCMessage.class); - when(message.get()).thenReturn(ByteBuffer.wrap(data)); - pool.messageReceived(message); - return proxy; - } - }); - } - - @Test - public void testAcquireNoPid() throws Exception { - final byte[] data = getJsonString(null, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireBadPid() throws Exception { - final byte[] data = getJsonString(9000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireNullPid() throws Exception { - final byte[] data = getJsonString(null, "jmxUrl://hello", true); - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireNoJmxUrl() throws Exception { - final byte[] data = getJsonString(8000, null); - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireNullJmxUrl() throws Exception { - final byte[] data = getJsonString(8000, null, true); - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireBadCast() throws Exception { - GsonBuilder gsonBuilder = new GsonBuilder(); - Gson gson = gsonBuilder.create(); - JsonObject jsonData = new JsonObject(); - jsonData.addProperty(MXBeanConnectionPoolImpl.JSON_PID, "this is not an integer"); - jsonData.addProperty(MXBeanConnectionPoolImpl.JSON_JMX_URL, "jmxUrl://hello"); - - String jsonString = gson.toJson(jsonData); - final byte[] data = jsonString.getBytes(Charset.forName("UTF-8")); - - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireBadJson() throws Exception { - String jsonString = "not a json string"; - final byte[] data = jsonString.getBytes(Charset.forName("UTF-8")); - - invokeCallbacksOnProxyCreation(data); - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireNotRunning() throws Exception { - try { - pool.acquire(8000); - fail("Expected MXBeanConnectionException"); - } catch (MXBeanConnectionException e) { - verify(creator, never()).createConnector("jmxUrl://hello"); - verify(connector, never()).connect(); - } - } - - @Test - public void testAcquireOldServer() throws Exception { - final byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - when(ipcService.serverExists(IPC_SERVER_NAME)).thenReturn(true); - - pool.start(); - MXBeanConnection result = pool.acquire(8000); - - verify(lookup).lookupPrincipalByName("Test"); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService).destroyServer(IPC_SERVER_NAME); - verify(ipcService).createServer(IPC_SERVER_NAME, pool, principal); - verify(creator).createConnector("jmxUrl://hello"); + verify(helper).createAttacher(8000); + verify(attacher).attach(); + verify(attacher).getConnectorAddress(); + verify(helper).createConnector(JMX_URL); assertNotNull(result); assertEquals(result, connection); @@ -337,11 +100,7 @@ } @Test - public void testAcquireTwiceSameUser() throws Exception { - byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - - pool.start(); + public void testAcquireTwice() throws Exception { MXBeanConnection connection1 = pool.acquire(8000); verify(connector).connect(); @@ -349,10 +108,10 @@ MXBeanConnection connection2 = pool.acquire(8000); // Should only be invoked once - verify(lookup).lookupPrincipalByName("Test"); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService, never()).destroyServer(IPC_SERVER_NAME); - verify(ipcService).createServer(IPC_SERVER_NAME, pool, principal); + verify(helper).createAttacher(8000); + verify(attacher).attach(); + verify(attacher).getConnectorAddress(); + verify(helper).createConnector(JMX_URL); assertEquals(connection1, connection); assertEquals(connection2, connection); @@ -361,49 +120,7 @@ } @Test - public void testAcquireTwiceDifferentUser() throws Exception { - byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - data = getJsonString(8001, "jmxUrl://hello1"); - invokeCallbacksOnProxyCreation(data, 8001, "Test1", "agent-proxy-1001"); - - ProcessUserInfo info = new ProcessUserInfo(1001, "Test1"); - when(builder.build(8001)).thenReturn(info); - UserPrincipal otherPrincipal = mock(UserPrincipal.class); - when(lookup.lookupPrincipalByName("Test1")).thenReturn(otherPrincipal); - MXBeanConnector otherConnector = mock(MXBeanConnector.class); - when(creator.createConnector("jmxUrl://hello1")).thenReturn(otherConnector); - MXBeanConnectionImpl otherConnection = mock(MXBeanConnectionImpl.class); - when(otherConnector.connect()).thenReturn(otherConnection); - - pool.start(); - MXBeanConnection connection1 = pool.acquire(8000); - MXBeanConnection connection2 = pool.acquire(8001); - - verify(lookup).lookupPrincipalByName("Test"); - verify(ipcService).serverExists(IPC_SERVER_NAME); - verify(ipcService, never()).destroyServer(IPC_SERVER_NAME); - verify(ipcService).createServer(IPC_SERVER_NAME, pool, principal); - verify(creator).createConnector("jmxUrl://hello"); - verify(connector).connect(); - - verify(lookup).lookupPrincipalByName("Test1"); - verify(ipcService).serverExists("agent-proxy-1001"); - verify(ipcService, never()).destroyServer("agent-proxy-1001"); - verify(ipcService).createServer("agent-proxy-1001", pool, otherPrincipal); - verify(creator).createConnector("jmxUrl://hello1"); - verify(otherConnector).connect(); - - assertEquals(connection1, connection); - assertEquals(connection2, otherConnection); - } - - @Test public void testRelease() throws Exception { - byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - - pool.start(); MXBeanConnection result = pool.acquire(8000); verify(connection, never()).close(); @@ -415,10 +132,6 @@ @Test public void testReleaseTwice() throws Exception { - byte[] data = getJsonString(8000, "jmxUrl://hello"); - invokeCallbacksOnProxyCreation(data); - - pool.start(); // connection1 == connection1 == actualConnection MXBeanConnection connection1 = pool.acquire(8000); MXBeanConnection connection2 = pool.acquire(8000); @@ -437,24 +150,5 @@ pool.release(8000, connection); } - private byte[] getJsonString(Integer pid, String jmxUrl) { - return getJsonString(pid, jmxUrl, false); - } - - private byte[] getJsonString(Integer pid, String jmxUrl, boolean serializeNulls) { - GsonBuilder gsonBuilder = new GsonBuilder(); - if (serializeNulls) { - gsonBuilder.serializeNulls(); - } - Gson gson = gsonBuilder.create(); - JsonObject jsonData = new JsonObject(); - - jsonData.addProperty(MXBeanConnectionPoolImpl.JSON_PID, pid); - jsonData.addProperty(MXBeanConnectionPoolImpl.JSON_JMX_URL, jmxUrl); - - String jsonString = gson.toJson(jsonData); - return jsonString.getBytes(Charset.forName("UTF-8")); - } - }
--- a/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/MXBeanConnectionPoolTrackerTest.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -/* - * 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 - * <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.management.internal; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.junit.Before; -import org.junit.Test; -import org.osgi.framework.BundleContext; -import org.osgi.framework.ServiceReference; - -import com.redhat.thermostat.common.LaunchException; -import com.redhat.thermostat.utils.management.internal.MXBeanConnectionPoolTracker.LatchCreator; - -public class MXBeanConnectionPoolTrackerTest { - - private MXBeanConnectionPoolTracker tracker; - private BundleContext context; - private CountDownLatch latch; - private LatchCreator latchCreator; - private MXBeanConnectionPoolControl pool; - private ServiceReference ref; - - @Before - public void setUp() { - context = mock(BundleContext.class); - latchCreator = mock(LatchCreator.class); - latch = mock(CountDownLatch.class); - when(latchCreator.create()).thenReturn(latch); - tracker = new MXBeanConnectionPoolTracker(context, latchCreator); - pool = mock(MXBeanConnectionPoolControl.class); - ref = mock(ServiceReference.class); - } - - @Test - public void testAddingService() { - Object result = addService(); - - assertEquals(pool, result); - assertEquals(pool, tracker.getPool()); - verify(latch).countDown(); - } - - private Object addService() { - when(context.getService(ref)).thenReturn(pool); - Object result = tracker.addingService(ref); - return result; - } - - @Test - public void testRemovedService() { - addService(); - tracker.removedService(ref, pool); - assertNull(tracker.getPool()); - verify(context).ungetService(ref); // Ensures super called - verify(latchCreator, times(2)).create(); - } - - @Test - public void testGetPoolWithTimeout() throws Exception { - addService(); - MXBeanConnectionPoolControl result = tracker.getPoolWithTimeout(); - assertEquals(pool, result); - verify(latch).await(anyLong(), any(TimeUnit.class)); - } - - @Test - public void testGetPoolWithTimeoutNoPool() throws Exception { - try { - tracker.getPoolWithTimeout(); - fail("Expected LaunchException"); - } catch (LaunchException ignored) { - verify(latch).await(anyLong(), any(TimeUnit.class)); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/utils/management/internal/ManagementAgentAttacherTest.java Tue Jul 11 17:00:25 2017 -0400 @@ -0,0 +1,171 @@ +/* + * 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 + * <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.management.internal; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.utils.management.internal.ManagementAgentAttacher.VirtualMachineUtils; +import com.sun.tools.attach.VirtualMachine; + +public class ManagementAgentAttacherTest { + + private static final int SUCCESS = 0; + private static final int FAILURE = 3; + private static final int VM_PID = 0; + private static final String PATH_TO_JAVA_HOME = new File("/path/to/java/home").getAbsolutePath(); + private ManagementAgentAttacher control; + private VirtualMachine vm; + private VirtualMachineUtils vmUtils; + + @Before + public void setup() throws Exception { + vmUtils = mock(VirtualMachineUtils.class); + vm = mock(VirtualMachine.class); + + // Mock VM properties + Properties agentProps = mock(Properties.class); + when(agentProps.getProperty("com.sun.management.jmxremote.localConnectorAddress")) + .thenReturn(null).thenReturn("myJmxUrl"); + when(vm.getAgentProperties()).thenReturn(agentProps); + Properties sysProps = mock(Properties.class); + when(sysProps.getProperty("java.home")).thenReturn(PATH_TO_JAVA_HOME); + when(vm.getSystemProperties()).thenReturn(sysProps); + + when(vmUtils.attach(anyString())).thenReturn(vm); + control = new ManagementAgentAttacher(VM_PID, vmUtils); + } + + @Test + public void testAttachJcmd() throws Exception { + final Process process = mock(Process.class); + when(process.waitFor()).thenReturn(SUCCESS); + @SuppressWarnings("unchecked") + final List<String>[] startProcessArgs = new List[1]; + ManagementAgentAttacher controlUnderTest = new ManagementAgentAttacher(VM_PID, vmUtils) { + @Override + Process startProcess(List<String> args) { + startProcessArgs[0] = args; + return process; + } + }; + controlUnderTest.attach(); + List<String> args = startProcessArgs[0]; + assertNotNull(args); + String[] expectedArgs = new String[] { + new File("/path/to/java/bin/jcmd").getAbsolutePath(), // Uses the jrePath.getParentFile() path + Integer.toString(VM_PID), + "ManagementAgent.start_local" + }; + List<String> expectedList = Arrays.asList(expectedArgs); + assertEquals(expectedList, args); + + verify(vmUtils).attach(Integer.toString(VM_PID)); + verify(vm, times(2)).getAgentProperties(); + verify(vm).getSystemProperties(); + verify(vm, times(0)).loadAgent(PATH_TO_JAVA_HOME + File.separator + "lib" + File.separator + "management-agent.jar"); + } + + @Test + public void testAttachManagementJar() throws Exception { + final Process process = mock(Process.class); + when(process.waitFor()).thenReturn(FAILURE); + ManagementAgentAttacher controlUnderTest = new ManagementAgentAttacher(VM_PID, vmUtils) { + @Override + Process startProcess(List<String> args) { + return process; // pretending jcmd process start fails + } + }; + controlUnderTest.attach(); + + verify(vmUtils).attach(Integer.toString(VM_PID)); + verify(vm, times(2)).getAgentProperties(); + verify(vm).getSystemProperties(); + verify(vm).loadAgent(PATH_TO_JAVA_HOME + File.separator + "lib" + File.separator + "management-agent.jar"); + } + + @Test + public void testIsAttached() throws Exception { + assertFalse(control.isAttached()); + control.attach(); + assertTrue(control.isAttached()); + } + + @Test + public void testGetAddress() throws Exception { + control.attach(); + String addr = control.getConnectorAddress(); + assertEquals("myJmxUrl", addr); + } + + @Test(expected=IOException.class) + public void testGetAddressNotAttached() throws Exception { + control.getConnectorAddress(); + } + + @Test + public void testDetach() throws Exception { + control.attach(); + control.detach(); + verify(vm).detach(); + } + + @Test + public void testDetachNotAttached() throws Exception { + control.detach(); + verify(vm, never()).detach(); + } + +} +
--- a/agent/pom.xml Mon Jul 10 14:49:31 2017 -0400 +++ b/agent/pom.xml Tue Jul 11 17:00:25 2017 -0400 @@ -62,7 +62,6 @@ <module>cli</module> <module>core</module> <module>ipc</module> - <module>proxy</module> </modules> </project>
--- a/agent/proxy/pom.xml Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - 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 - <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. - ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <parent> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-agent</artifactId> - <version>1.99.12-SNAPSHOT</version> - </parent> - - <artifactId>thermostat-agent-proxy</artifactId> - <packaging>pom</packaging> - - <name>Thermostat Agent Proxy</name> - - <modules> - <module>server</module> - </modules> - -</project> -
--- a/agent/proxy/server/pom.xml Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,110 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - 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 - <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. - ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <parent> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-agent-proxy</artifactId> - <version>1.99.12-SNAPSHOT</version> - </parent> - - <artifactId>thermostat-agent-proxy-server</artifactId> - <name>Thermostat Agent Proxy Server</name> - - <build> - <pluginManagement> - <plugins> - <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.--> - <plugin> - <groupId>org.eclipse.m2e</groupId> - <artifactId>lifecycle-mapping</artifactId> - <configuration> - <lifecycleMappingMetadata> - <pluginExecutions> - <pluginExecution> - <pluginExecutionFilter> - <groupId>org.codehaus.mojo</groupId> - <artifactId>exec-maven-plugin</artifactId> - <versionRange>[1.2.1,)</versionRange> - <goals> - <goal>exec</goal> - </goals> - </pluginExecutionFilter> - <action> - <ignore></ignore> - </action> - </pluginExecution> - </pluginExecutions> - </lifecycleMappingMetadata> - </configuration> - </plugin> - </plugins> - </pluginManagement> - </build> - - <dependencies> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-common-core</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-agent-ipc-client</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.google.code.gson</groupId> - <artifactId>gson</artifactId> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-core</artifactId> - <scope>test</scope> - </dependency> - </dependencies> - -</project> -
--- a/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxy.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -/* - * 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 - * <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.agent.proxy.server; - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.redhat.thermostat.agent.ipc.client.ClientIPCService; -import com.redhat.thermostat.agent.ipc.client.ClientIPCServiceFactory; -import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel; -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.redhat.thermostat.shared.config.NativeLibraryResolver; -import com.redhat.thermostat.shared.config.OS; -import com.redhat.thermostat.shared.config.internal.CommonPathsImpl; -import com.sun.tools.attach.AttachNotSupportedException; - -public class AgentProxy { - - private static final Logger logger = LoggingUtils.getLogger(AgentProxy.class); - static final String CONFIG_FILE_PROP = "ipcConfigFile"; - static final String JSON_PID = "pid"; - static final String JSON_JMX_URL = "jmxUrl"; - - private static ControlCreator creator = new ControlCreator(); - private static ClientIPCService ipcService = null; - - public static void main(String[] args) throws IOException { - if (args.length < 2) { - usage(); - } - - // Windows named pipes has some native code - must set paths to find the DLL - if (OS.IS_WINDOWS) { - NativeLibraryResolver.setCommonPaths(new CommonPathsImpl()); - } - - // Get IPC configuration file location from system property - String configFileStr = System.getProperty(CONFIG_FILE_PROP); - if (configFileStr == null) { - throw new IOException("Unknown IPC configuration file location"); - } - File configFile = new File(configFileStr); - if (ipcService == null) { // Only non-null for testing - ipcService = ClientIPCServiceFactory.getIPCService(configFile); - } - - int pid = -1; - try { - // First argument is pid of target VM - pid = Integer.parseInt(args[0]); - } catch (NumberFormatException e) { - usage(); - } - String ipcServerName = args[1]; - // Connect to IPC server - IPCMessageChannel channel = ipcService.connectToServer(ipcServerName); - - // Start proxy agent - AgentProxyControlImpl agent = creator.create(pid); - - try { - attachToTarget(pid, agent); - String connectorAddress = getJMXServiceURL(agent); - sendConnectionInfo(channel, pid, connectorAddress); - } finally { - cleanup(agent, channel, pid); - } - } - - private static void attachToTarget(int pid, AgentProxyControlImpl agent) throws IOException { - try { - agent.attach(); - } catch (AttachNotSupportedException | IOException e) { - throw new IOException("Failed to attach to VM (pid: " + pid + ")", e); - } - } - - private static String getJMXServiceURL(AgentProxyControlImpl agent) throws IOException { - try { - return agent.getConnectorAddress(); - } catch (IOException e) { - throw new IOException("Failed to retrieve JMX connection URL", e); - } - } - - private static void sendConnectionInfo(IPCMessageChannel channel, int pid, String connectorAddress) throws IOException { - try { - // As JSON, write pid first, followed by JMX service URL - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.create(); - JsonObject data = new JsonObject(); - data.addProperty(JSON_PID, pid); - data.addProperty(JSON_JMX_URL, connectorAddress); - - String jsonData = gson.toJson(data); - ByteBuffer buf = ByteBuffer.wrap(jsonData.getBytes("UTF-8")); - channel.writeMessage(buf); - } catch (IOException e) { - throw new IOException("Failed to send JMX connection information to agent", e); - } - } - - private static void cleanup(AgentProxyControlImpl agent, IPCMessageChannel channel, int pid) { - try { - channel.close(); - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to close channel with agent for VM (pid: " + pid + ")", e); - } - - if (agent.isAttached()) { - try { - agent.detach(); - } catch (IOException e) { - logger.log(Level.WARNING, "Failed to detach from VM (pid: " + pid + ")", e); - } - } - } - - private static void usage() { - throw new RuntimeException("usage: java " + AgentProxy.class.getName() + " <pidOfTargetJvm> <userNameOfJvmOwner>"); - } - - static class ControlCreator { - AgentProxyControlImpl create(int pid) { - return new AgentProxyControlImpl(pid); - } - } - - /* - * For testing purposes only. - */ - static void setControlCreator(ControlCreator creator) { - AgentProxy.creator = creator; - } - - /* - * For testing purposes only. - */ - static void setIPCService(ClientIPCService ipcService) { - AgentProxy.ipcService = ipcService; - } - -} -
--- a/agent/proxy/server/src/main/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImpl.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,169 +0,0 @@ -/* - * 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 - * <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.agent.proxy.server; - -import java.io.File; -import java.io.IOException; -import java.rmi.RemoteException; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.redhat.thermostat.common.utils.LoggingUtils; -import com.sun.tools.attach.AgentInitializationException; -import com.sun.tools.attach.AgentLoadException; -import com.sun.tools.attach.AttachNotSupportedException; -import com.sun.tools.attach.VirtualMachine; - -class AgentProxyControlImpl { - - private static final Logger logger = LoggingUtils.getLogger(AgentProxyControlImpl.class); - private static final String CONNECTOR_ADDRESS_PROPERTY = "com.sun.management.jmxremote.localConnectorAddress"; - private static final String JCMD_NAME = "jcmd"; - private static final String JCMD_MANAGEMENT_AGENT_START_LOCAL = "ManagementAgent.start_local"; - - private final int pid; - private final VirtualMachineUtils vmUtils; - - private VirtualMachine vm; - private boolean attached; - private String connectorAddress; - - AgentProxyControlImpl(int pid) { - this(pid, new VirtualMachineUtils()); - } - - AgentProxyControlImpl(int pid, VirtualMachineUtils vmUtils) { - this.pid = pid; - this.vmUtils = vmUtils; - } - - void attach() throws AttachNotSupportedException, IOException { - vm = vmUtils.attach(String.valueOf(pid)); - attached = true; - - Properties props = vm.getAgentProperties(); - connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); - if (connectorAddress == null) { - String home = null; - String agent = null; - try { - props = vm.getSystemProperties(); - startManagementAgent(props); - logger.fine("Started management agent for vm '" + pid + "'"); - props = vm.getAgentProperties(); - connectorAddress = props.getProperty(CONNECTOR_ADDRESS_PROPERTY); - } catch (IOException | AgentLoadException | AgentInitializationException e) { - throw new RemoteException("Failed to load agent ('" + agent + "', from home '" + home + "') into VM (pid: " + pid + ")", e); - } - } - } - - private void startManagementAgent(Properties props) throws IOException, - AgentLoadException, - AgentInitializationException { - String home = props.getProperty("java.home"); - try { - // JDK 7 and up have jcmd with JMX management agent start support. - startManagementAgentUsingJcmd(home, pid); - } catch (Exception e) { - // Fall back to old management-agent.jar behaviour. - logger.log(Level.FINE, "Failed to activate JMX agent via jcmd.", e); - startManagementAgentUsingJavaAgent(home); - } - } - - private void startManagementAgentUsingJcmd(String home, int vmPid) throws IOException, InterruptedException { - String jcmd = home + File.separator + "bin" + File.separator + JCMD_NAME; - File jcmdFile = new File(jcmd); - if (!jcmdFile.exists()) { - // java.home might be JRE home. Try one level up. - File binDir = new File(new File(home).getParentFile(), "bin"); - jcmd = binDir.getAbsolutePath() + File.separator + JCMD_NAME; - } - String[] args = new String[] { jcmd, - Integer.toString(vmPid), - JCMD_MANAGEMENT_AGENT_START_LOCAL }; - List<String> jcmdArgs = Arrays.asList(args); - logger.fine("Starting JMX management agent via JCMD: " + jcmdArgs); - Process process = startProcess(jcmdArgs); - int result = process.waitFor(); - if (result != 0) { - throw new IllegalStateException("Failed to execute jcmd. Exit code was: " + result); - } - } - - // Package private for testing - Process startProcess(List<String> jcmdArgs) throws IOException { - ProcessBuilder builder = new ProcessBuilder(jcmdArgs); - return builder.start(); - } - - private void startManagementAgentUsingJavaAgent(String home) throws AgentLoadException, AgentInitializationException, IOException { - String agent = home + File.separator + "lib" + File.separator + "management-agent.jar"; - logger.fine("Loading '" + agent + "' into VM (pid: " + pid + ")"); - vm.loadAgent(agent); - } - - boolean isAttached() { - return attached; - } - - String getConnectorAddress() throws IOException { - if (!attached) { - throw new IOException("Agent not attached to target VM"); - } - return connectorAddress; - } - - void detach() throws IOException { - if (attached) { - vm.detach(); - attached = false; - } - } - - static class VirtualMachineUtils { - VirtualMachine attach(String pid) throws AttachNotSupportedException, IOException { - return VirtualMachine.attach(pid); - } - } - -} -
--- a/agent/proxy/server/src/test/java/com/redhat/thermostat/agent/proxy/server/AgentProxyControlImplTest.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -/* - * 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 - * <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.agent.proxy.server; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Properties; - -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.agent.proxy.server.AgentProxyControlImpl.VirtualMachineUtils; -import com.sun.tools.attach.VirtualMachine; - -public class AgentProxyControlImplTest { - - private static final int SUCCESS = 0; - private static final int FAILURE = 3; - private static final int VM_PID = 0; - private static final String PATH_TO_JAVA_HOME = new File("/path/to/java/home").getAbsolutePath(); - private AgentProxyControlImpl control; - private VirtualMachine vm; - private VirtualMachineUtils vmUtils; - - @Before - public void setup() throws Exception { - vmUtils = mock(VirtualMachineUtils.class); - vm = mock(VirtualMachine.class); - - // Mock VM properties - Properties agentProps = mock(Properties.class); - when(agentProps.getProperty("com.sun.management.jmxremote.localConnectorAddress")) - .thenReturn(null).thenReturn("myJmxUrl"); - when(vm.getAgentProperties()).thenReturn(agentProps); - Properties sysProps = mock(Properties.class); - when(sysProps.getProperty("java.home")).thenReturn(PATH_TO_JAVA_HOME); - when(vm.getSystemProperties()).thenReturn(sysProps); - - when(vmUtils.attach(anyString())).thenReturn(vm); - control = new AgentProxyControlImpl(VM_PID, vmUtils); - } - - @Test - public void testAttachJcmd() throws Exception { - final Process process = mock(Process.class); - when(process.waitFor()).thenReturn(SUCCESS); - @SuppressWarnings("unchecked") - final List<String>[] startProcessArgs = new List[1]; - AgentProxyControlImpl controlUnderTest = new AgentProxyControlImpl(VM_PID, vmUtils) { - @Override - Process startProcess(List<String> args) { - startProcessArgs[0] = args; - return process; - } - }; - controlUnderTest.attach(); - List<String> args = startProcessArgs[0]; - assertNotNull(args); - String[] expectedArgs = new String[] { - new File("/path/to/java/bin/jcmd").getAbsolutePath(), // Uses the jrePath.getParentFile() path - Integer.toString(VM_PID), - "ManagementAgent.start_local" - }; - List<String> expectedList = Arrays.asList(expectedArgs); - assertEquals(expectedList, args); - - verify(vmUtils).attach(Integer.toString(VM_PID)); - verify(vm, times(2)).getAgentProperties(); - verify(vm).getSystemProperties(); - verify(vm, times(0)).loadAgent(PATH_TO_JAVA_HOME + File.separator + "lib" + File.separator + "management-agent.jar"); - } - - @Test - public void testAttachManagementJar() throws Exception { - final Process process = mock(Process.class); - when(process.waitFor()).thenReturn(FAILURE); - AgentProxyControlImpl controlUnderTest = new AgentProxyControlImpl(VM_PID, vmUtils) { - @Override - Process startProcess(List<String> args) { - return process; // pretending jcmd process start fails - } - }; - controlUnderTest.attach(); - - verify(vmUtils).attach(Integer.toString(VM_PID)); - verify(vm, times(2)).getAgentProperties(); - verify(vm).getSystemProperties(); - verify(vm).loadAgent(PATH_TO_JAVA_HOME + File.separator + "lib" + File.separator + "management-agent.jar"); - } - - @Test - public void testIsAttached() throws Exception { - assertFalse(control.isAttached()); - control.attach(); - assertTrue(control.isAttached()); - } - - @Test - public void testGetAddress() throws Exception { - control.attach(); - String addr = control.getConnectorAddress(); - assertEquals("myJmxUrl", addr); - } - - @Test(expected=IOException.class) - public void testGetAddressNotAttached() throws Exception { - control.getConnectorAddress(); - } - - @Test - public void testDetach() throws Exception { - control.attach(); - control.detach(); - verify(vm).detach(); - } - - @Test - public void testDetachNotAttached() throws Exception { - control.detach(); - verify(vm, never()).detach(); - } - -} -
--- a/agent/proxy/server/src/test/java/com/redhat/thermostat/agent/proxy/server/AgentProxyTest.java Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ -/* - * 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 - * <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.agent.proxy.server; - -import static org.junit.Assert.fail; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.io.IOException; -import java.nio.ByteBuffer; - -import com.redhat.thermostat.shared.config.OS; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; -import com.redhat.thermostat.agent.ipc.client.ClientIPCService; -import com.redhat.thermostat.agent.ipc.client.IPCMessageChannel; -import com.redhat.thermostat.agent.proxy.server.AgentProxy.ControlCreator; - -public class AgentProxyTest { - - private static final String JMX_URL = "service:jmx:rmi://myHost:1099/blah"; - private static final String IPC_SERVER_NAME = "agent-proxy-8000"; - private static final String IGNORED_PATH = "/"; - private static final String THERMOSTAT_HOME_PROP = "THERMOSTAT_HOME"; - private static final String USER_THERMOSTAT_HOME_PROP = "USER_THERMOSTAT_HOME"; - - private AgentProxyControlImpl control; - private ClientIPCService ipcService; - private IPCMessageChannel channel; - - @Before - public void setup() throws Exception { - System.setProperty(AgentProxy.CONFIG_FILE_PROP, "/path/to/config/file"); - ControlCreator creator = mock(ControlCreator.class); - control = mock(AgentProxyControlImpl.class); - when(control.getConnectorAddress()).thenReturn(JMX_URL); - when(control.isAttached()).thenReturn(true); - when(creator.create(anyInt())).thenReturn(control); - AgentProxy.setControlCreator(creator); - - ipcService = mock(ClientIPCService.class); - channel = mock(IPCMessageChannel.class); - when(ipcService.connectToServer(IPC_SERVER_NAME)).thenReturn(channel); - AgentProxy.setIPCService(ipcService); - - if (OS.IS_WINDOWS) { - // Agent Proxy now uses a native DLL for Windows named pipes on Windows - // because of this, CommonPathsImpl attempts to initialize. - // CommonPathsImpl has not defaults for these values - System.setProperty(THERMOSTAT_HOME_PROP, IGNORED_PATH); - System.setProperty(USER_THERMOSTAT_HOME_PROP, IGNORED_PATH); - } - } - - @After - public void teardown() throws Exception { - System.clearProperty(AgentProxy.CONFIG_FILE_PROP); - System.clearProperty(THERMOSTAT_HOME_PROP); - System.clearProperty(USER_THERMOSTAT_HOME_PROP); - AgentProxy.setControlCreator(new ControlCreator()); - AgentProxy.setIPCService(null); - } - - @Test - public void testMainSuccess() throws Exception { - // Invoke main with PID of 8000 - AgentProxy.main(new String[] { "8000", IPC_SERVER_NAME }); - - verify(control).attach(); - verify(control).getConnectorAddress(); - - // Create a buffer with the expected data - GsonBuilder builder = new GsonBuilder(); - Gson gson = builder.create(); - JsonObject data = new JsonObject(); - data.addProperty(AgentProxy.JSON_PID, 8000); - data.addProperty(AgentProxy.JSON_JMX_URL, JMX_URL); - String jsonData = gson.toJson(data); - ByteBuffer jsonBuf = ByteBuffer.wrap(jsonData.getBytes("UTF-8")); - - verify(channel).writeMessage(eq(jsonBuf)); - verify(channel).close(); - verify(control).detach(); - } - - @Test - public void testMainAttachFails() throws Exception { - // Simulate failure binding the login object - doThrow(new IOException()).when(control).attach(); - when(control.isAttached()).thenReturn(false); - - try { - // Invoke main with PID of 0 - AgentProxy.main(new String[] { "0", IPC_SERVER_NAME }); - fail("Expected IOException"); - } catch (IOException e) { - // Should only call attach and close channel - verify(control).attach(); - verify(control, never()).getConnectorAddress(); - - verify(channel, never()).writeMessage(any(ByteBuffer.class)); - verify(channel).close(); - - verify(control, never()).detach(); - } - } - - @Test - public void testMainGetAddressFails() throws Exception { - // Simulate failure binding the login object - doThrow(new IOException()).when(control).getConnectorAddress(); - - try { - // Invoke main with PID of 0 - AgentProxy.main(new String[] { "0", IPC_SERVER_NAME }); - fail("Expected IOException"); - } catch (IOException e) { - verify(control).attach(); - verify(control).getConnectorAddress(); - - // Should detach and close channel, but not send URL - verify(channel, never()).writeMessage(any(ByteBuffer.class)); - verify(channel).close(); - - verify(control).detach(); - } - } - - @Test - public void testMainSendAddressFails() throws Exception { - // Simulate failure binding the login object - doThrow(new IOException()).when(channel).writeMessage(any(ByteBuffer.class)); - - try { - // Invoke main with PID of 0 - AgentProxy.main(new String[] { "0", IPC_SERVER_NAME }); - fail("Expected IOException"); - } catch (IOException e) { - verify(control).attach(); - verify(control).getConnectorAddress(); - - // Should still detach and close channel - verify(channel).writeMessage(any(ByteBuffer.class)); - verify(channel).close(); - - verify(control).detach(); - } - } - - @Test - public void testMainDetachFails() throws Exception { - // Simulate failure binding the login object - doThrow(new IOException()).when(control).detach(); - - // Invoke main with PID of 0 - AgentProxy.main(new String[] { "0", IPC_SERVER_NAME }); - - // All should be called, should not be fatal - verify(control).attach(); - verify(control).getConnectorAddress(); - - verify(channel).writeMessage(any(ByteBuffer.class)); - verify(channel).close(); - - verify(control).detach(); - } - - @Test - public void testMainCloseFails() throws Exception { - // Simulate failure binding the login object - doThrow(new IOException()).when(channel).close(); - - // Invoke main with PID of 0 - AgentProxy.main(new String[] { "0", IPC_SERVER_NAME }); - - // All should be called, should not be fatal - verify(control).attach(); - verify(control).getConnectorAddress(); - - verify(channel).writeMessage(any(ByteBuffer.class)); - verify(channel).close(); - - verify(control).detach(); - } - -} -
--- a/distribution/pom.xml Mon Jul 10 14:49:31 2017 -0400 +++ b/distribution/pom.xml Tue Jul 11 17:00:25 2017 -0400 @@ -423,11 +423,6 @@ </dependency> <dependency> <groupId>com.redhat.thermostat</groupId> - <artifactId>thermostat-agent-proxy-server</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>com.redhat.thermostat</groupId> <artifactId>thermostat-agent-ipc-tcpsocket-server</artifactId> <version>${project.version}</version> </dependency>
--- a/distribution/scripts/thermostat-agent-proxy Mon Jul 10 14:49:31 2017 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -#!/bin/bash -# -# Copyright 2012-2014 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. -# -##################################################################### -# -if [ "$#" -lt 4 ]; then - echo "usage: $0 <pidOfTargetJvm> <userNameOfJvmOwner> <ipcConfigFile> <ipcServerName>" >&2 - exit 1 -fi -TARGET_PID="$1" -TARGET_USER="$2" -CONFIG_FILE="$3" -IPC_SERVER_NAME="$4" - -# Source thermostat-ipc-client-common from same directory as this script -# Defines IPC_CLASSPATH variable with JARs necessary for the IPC service -. "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"/thermostat-ipc-client-common - -# Ensure thermostat-ipc-client-common sourced correctly -if [ -z "${IPC_CLASSPATH}" ]; then - echo "Classpath not properly defined for command channel" >&2 - exit 1 -fi - -# Need tools from the JVM -TOOLS_JAR="${JAVA_HOME}/lib/tools.jar" - -# Additional JARs necessary for the agent proxy -IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-common-core-@project.version@.jar" -IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-shared-config-@project.version@.jar" -IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/thermostat-agent-proxy-server-@project.version@.jar" -IPC_CLASSPATH="${IPC_CLASSPATH}:${THERMOSTAT_LIBS}/gson-@gson.version@.jar" -IPC_CLASSPATH="${TOOLS_JAR}:${IPC_CLASSPATH}" - -AGENT_PROXY_CLASS="com.redhat.thermostat.agent.proxy.server.AgentProxy" - -# Set this to remote debug -if [ x"$THERMOSTAT_DEBUG" != x ] ; then - DEBUG_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=1082" -fi - -# Start server -if [ $CYGWIN_MODE -eq 1 ]; then - CONFIG_FILE_ARG="-DipcConfigFile=`cygpath -w ${CONFIG_FILE}`" - # Drop permissions, if root - if [ "$(id -u)" -eq 0 ]; then - /bin/su -s /bin/bash -c "${JAVA} -cp `cygpath -w -p ${IPC_CLASSPATH}` ${CONFIG_FILE_ARG} ${LOGGING_ARGS} ${DEBUG_OPTS} ${AGENT_PROXY_CLASS} ${TARGET_PID} ${IPC_SERVER_NAME}" "${TARGET_USER}" - else - ${JAVA} -cp `cygpath -w -p ${IPC_CLASSPATH}` "${CONFIG_FILE_ARG}" ${DEBUG_OPTS} ${LOGGING_ARGS} ${AGENT_PROXY_CLASS} "${TARGET_PID}" "${IPC_SERVER_NAME}" - fi -else - CONFIG_FILE_ARG="-DipcConfigFile=${CONFIG_FILE}" - # Drop permissions, if root - if [ "$(id -u)" -eq 0 ]; then - /bin/su -s /bin/bash -c "${JAVA} -cp ${IPC_CLASSPATH} ${CONFIG_FILE_ARG} ${LOGGING_ARGS} ${DEBUG_OPTS} ${AGENT_PROXY_CLASS} ${TARGET_PID} ${IPC_SERVER_NAME}" "${TARGET_USER}" - else - ${JAVA} -cp ${IPC_CLASSPATH} "${CONFIG_FILE_ARG}" ${DEBUG_OPTS} ${LOGGING_ARGS} ${AGENT_PROXY_CLASS} "${TARGET_PID}" "${IPC_SERVER_NAME}" - fi -fi