# HG changeset patch # User Roman Kennke # Date 1361548390 -3600 # Node ID db2b89cdef62743cf59a78ca14c51f939ae71ab6 # Parent e318ef62411da6e9673cd427d568d7eef67564f6# Parent e7ffb5dcd2dde1132f73d972b7113b114363195a Merge diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java --- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java Fri Feb 22 16:53:10 2013 +0100 @@ -84,38 +84,40 @@ Backend backend = (Backend) actionEvent.getPayload(); switch (actionEvent.getActionId()) { - case SERVICE_ADDED: { - // TODO: this backed has been already added, we should - // probably signal the user this - if (!backendInfos.containsKey(backend)) { + + case SERVICE_ADDED: { + if (!backendInfos.containsKey(backend)) { + + logger.info("Adding backend: " + backend); + + backend.activate(); - logger.info("Adding backend: " + backend); - - backend.activate(); - - BackendInformation info = createBackendInformation(backend); - backendDao.addBackendInformation(info); - backendInfos.put(backend, info); + BackendInformation info = createBackendInformation(backend); + backendDao.addBackendInformation(info); + backendInfos.put(backend, info); + } else { + logger.warning("Backend registered that agent already knows about:" + backend); + } + break; } - } - break; - case SERVICE_REMOVED: { - BackendInformation info = backendInfos.get(backend); - if (info != null) { - logger.info("removing backend: " + backend); - - backend.deactivate(); - - backendDao.removeBackendInformation(info); - backendInfos.remove(backend); + case SERVICE_REMOVED: { + BackendInformation info = backendInfos.get(backend); + if (info != null) { + logger.info("removing backend: " + backend); + + backend.deactivate(); + + backendDao.removeBackendInformation(info); + backendInfos.remove(backend); + } + break; } - } - break; - - default: - logger.log(Level.WARNING, "received unknown event from BackendRegistry: " + actionEvent.getActionId()); - break; + + default: { + logger.log(Level.WARNING, "received unknown event from BackendRegistry: " + actionEvent.getActionId()); + break; + } } } }; diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java Fri Feb 22 16:53:10 2013 +0100 @@ -36,130 +36,44 @@ package com.redhat.thermostat.backend; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - import com.redhat.thermostat.annotations.ExtensionPoint; -import com.redhat.thermostat.common.LaunchException; import com.redhat.thermostat.common.Ordered; -import com.redhat.thermostat.storage.core.Storage; /** * Represents a plugin that runs on the agent and performs monitoring of host * and applications. *

- * To register a new backend, register an instance of the class with the OSGi + * To register a new backend, register an implementation of this interface with the OSGi * service registry. * + * Implementations are encouraged to make use of existing abstract helper classes, such + * as {@link BaseBackend} or {@link VmListenerBackend} + * + * @see BaseBackend * @see VmListenerBackend */ @ExtensionPoint -public abstract class Backend implements Ordered { - - private boolean initialConfigurationComplete = false; - private boolean observeNewJvm = attachToNewProcessByDefault(); - - private Map config = new HashMap<>(); - - private BackendID id; +public interface Backend extends Ordered { - public Backend(BackendID id) { - this.id = id; - } - - /** - * - * @param configMap a map containing the settings that this backend has been configured with. - * @throws LaunchException if map contains values that this backend does not accept. - */ - protected final void setInitialConfiguration(Map configMap) throws BackendLoadException { - if (initialConfigurationComplete) { - throw new BackendLoadException(id, "The backend " + id.toString() + "may only receive initial configuration once."); - } - for (Entry e : configMap.entrySet()) { - String key = e.getKey(); - String value = e.getValue(); - try { - setConfigurationValue(key, value); - } catch (IllegalArgumentException iae) { - throw new BackendLoadException(id, "Attempt to set invalid backend configuration for " + getName() - + " backend. Key: " + key + " Value: " + value, iae); - } - } - initialConfigurationComplete = true; - } - - /** - * Set the named configuration to the given value. - * The basic special properties {@code name}, {@code version} and - * {@code description} are parsed here. - * - *

- * - * Subclasses can just override the - * {@link #setConfigurationValueImpl(String, String)} - * method if they are not interested in parsing and setting those - * properties directly. - * - * @param name - * @param value - * @throws IllegalArgumentException if either the key does not refer to a valid configuration option - * for this backend or the value is not valid for the key - */ - protected void setConfigurationValue(String name, String value) { - config.put(name, value); - } - /** * @return the name of the {@link Backend} */ - public String getName() { - return id.getSimpleName(); - } + public String getName(); /** * @returns the description of the {@link Backend} */ - public String getDescription() { - return config.get(BackendsProperties.DESCRIPTION.name()); - } + public String getDescription(); /** * @return the vendor of the {@link Backend} */ - public String getVendor() { - return config.get(BackendsProperties.VENDOR.name()); - } + public String getVendor(); /** * @return the version of the {@link Backend} */ - public String getVersion() { - return config.get(BackendsProperties.VERSION.name()); - } - - /** Get a map containing the current settings of this backend. - * Implementors of this abstract class which have some settings that - * are be configurable by the client must override this method - * to provide an appropriate map. - * - * @return a map containing the settings of this backend - */ - public Map getConfigurationMap() { - return new HashMap(); - } - - /** - * - * @param key The constant key that corresponds to the desired configuration value - * @return The current value of the configuration value corresponding to the key given. - * @throws IllegalArgumentException if the key does not refer to a valid configuration option for - * this backend - */ - public String getConfigurationValue(String key) { - return config.get(key); - } + public String getVersion(); /** * Activate the {@link Backend}. Based on the current configuration, @@ -175,7 +89,7 @@ * @return {@code true} if the backend was activated successfully or * already active. {@code false} if there was an error */ - public abstract boolean activate(); + public boolean activate(); /** * Deactivate the {@link Backend}. The backend should release any @@ -191,89 +105,25 @@ * @return {@code true} if the backend was successfully deactivated or * already inactive. {@code false} if the backend is still active. */ - public abstract boolean deactivate(); + public boolean deactivate(); /** * @return a boolean indicating whether the backend is currently active on this host */ - public abstract boolean isActive(); - - /** - * A {@link Backend} may be configured to automatically begin collecting from new Java - * processes. This method determines whether this will be the case when the backend - * is initially started. - * - * @return true if the initial backend behaviour is to attach to new java processes, false otherwise. - */ - public abstract boolean attachToNewProcessByDefault(); + public boolean isActive(); /** * Indicate whether this backend will attach to new java processes. * * @return true if this backend will attach to new java processes, false otherwise. */ - public boolean getObserveNewJvm() { - return observeNewJvm; - } + public boolean getObserveNewJvm(); /** * Set whether this backend will attach to new java processes. * * @param newValue */ - public void setObserveNewJvm(boolean newValue) { - observeNewJvm = newValue; - } - - public BackendID getID() { - return id; - } - - @Override - public int hashCode() { - String vendor = getVendor(); - String version = getVersion(); - final int prime = 31; - int result = 1; - result = prime * result + ((id == null) ? 0 : id.hashCode()); - result = prime * result + ((vendor == null) ? 0 : vendor.hashCode()); - result = prime * result + ((version == null) ? 0 : version.hashCode()); - return result; - } + public void setObserveNewJvm(boolean newValue); - @Override - public boolean equals(Object obj) { - String vendor = getVendor(); - String version = getVersion(); - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - Backend other = (Backend) obj; - if (id == null) { - if (other.id != null) - return false; - } else if (!id.equals(other.id)) - return false; - if (vendor == null) { - if (other.getVendor() != null) - return false; - } else if (!vendor.equals(other.getVendor())) - return false; - if (version == null) { - if (other.getVersion() != null) - return false; - } else if (!version.equals(other.getVersion())) - return false; - return true; - } - - @Override - public String toString() { - return "Backend [version=" + getVersion() + ", vendor=" + getVendor() - + ", description=" + getDescription() + ", id=" + id + "]"; - } } - diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java Fri Feb 22 16:52:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.backend; - -/** - * The unique identifier that identifies a backend. - */ -public class BackendID { - - private final String simpleName; - private final String className; - - public BackendID(String simpleName, String className) { - this.simpleName = simpleName; - this.className = className; - } - - public String getSimpleName() { - return simpleName; - } - - public String getClassName() { - return className; - } - - @Override - public String toString() { - return simpleName + " = " + className; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((className == null) ? 0 : className.hashCode()); - result = prime * result - + ((simpleName == null) ? 0 : simpleName.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - BackendID other = (BackendID) obj; - if (className == null) { - if (other.className != null) - return false; - } else if (!className.equals(other.className)) - return false; - if (simpleName == null) { - if (other.simpleName != null) - return false; - } else if (!simpleName.equals(other.simpleName)) - return false; - return true; - } -} - diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/BackendLoadException.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendLoadException.java Fri Feb 22 16:52:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.backend; - -/** - * An error occurred when loading or initializing a backend - */ -public class BackendLoadException extends Exception { - - private static final long serialVersionUID = 4057881401012295723L; - - private final BackendID backendId; - - public BackendLoadException(BackendID backendId, String message) { - super(message); - this.backendId = backendId; - } - - public BackendLoadException(BackendID backendId, String message, Throwable cause) { - super(message, cause); - this.backendId = backendId; - } - - public BackendID getBackend() { - return backendId; - } -} - diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java Fri Feb 22 16:53:10 2013 +0100 @@ -36,14 +36,11 @@ package com.redhat.thermostat.backend; -import java.util.logging.Logger; - import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import com.redhat.thermostat.common.ThermostatExtensionRegistry; -import com.redhat.thermostat.common.utils.LoggingUtils; /** * A registry for {@link Backend}s. {@link Backend}s are responsible to be @@ -51,8 +48,6 @@ */ public class BackendRegistry extends ThermostatExtensionRegistry { - private static final Logger logger = LoggingUtils.getLogger(BackendRegistry.class); - private static final String FILTER = "(" + Constants.OBJECTCLASS + "=" + Backend.class.getName() + ")"; public BackendRegistry(BundleContext context) throws InvalidSyntaxException { diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/BackendsProperties.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendsProperties.java Fri Feb 22 16:52:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.backend; - -/** - * Properties that any Backend needs to have, at minimum. - */ -public enum BackendsProperties { - - // FIXME: Get this info from Bundle metadata and remove this - // enum. - DESCRIPTION, - VENDOR, - VERSION; - -} - diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/BaseBackend.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BaseBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -0,0 +1,117 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.backend; + +import java.util.Objects; + +public abstract class BaseBackend implements Backend { + + private boolean observeNewJvm; + + private String name, description, vendor, version; + + public BaseBackend(String name, String description, String vendor, String version) { + this(name, description, vendor, version, false); + } + + public BaseBackend(String name, String description, String vendor, String version, boolean observeNewJvm) { + this.name = Objects.requireNonNull(name); + this.description = Objects.requireNonNull(description); + this.vendor = Objects.requireNonNull(vendor); + this.version = Objects.requireNonNull(version); + this.observeNewJvm = observeNewJvm; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getVendor() { + return vendor; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public boolean getObserveNewJvm() { + return observeNewJvm; + } + + @Override + public void setObserveNewJvm(boolean newValue) { + observeNewJvm = newValue; + } + + @Override + public int hashCode() { + return Objects.hash(name, vendor, version); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + BaseBackend other = (BaseBackend) obj; + return Objects.equals(name, other.name) && + Objects.equals(version, other.version) && + Objects.equals(vendor, other.vendor); + } + + @Override + public String toString() { + return "Backend [name=" + getName() + ", version=" + getVersion() + ", vendor=" + getVendor() + + ", description=" + getDescription() + "]"; + } +} + diff -r e318ef62411d -r db2b89cdef62 agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java --- a/agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -55,14 +55,15 @@ import com.redhat.thermostat.common.utils.LoggingUtils; /** - * This class is a convenient subclass of {@link Backend} for those that need to - * attach {@link VmListener} in response to starting and stopping of JVMs on a + * This class is a convenient subclass of {@link Backend} (via {@link BaseBackend}) for those + * that need to attach {@link VmListener} in response to starting and stopping of JVMs on a * host. * * @see VmStatusListener * @see Backend + * @see BaseBackend */ -public abstract class VmListenerBackend extends Backend implements VmStatusListener { +public abstract class VmListenerBackend extends BaseBackend implements VmStatusListener { private static final Logger logger = LoggingUtils.getLogger(VmListenerBackend.class); @@ -71,10 +72,15 @@ private final VmStatusListenerRegistrar registrar; private boolean started; - public VmListenerBackend(BackendID id, VmStatusListenerRegistrar registrar) { - super(id); + public VmListenerBackend(String backendName, String description, + String vendor, String version, VmStatusListenerRegistrar registrar) { + this(backendName, description, vendor, version, false, registrar); + } + public VmListenerBackend(String backendName, String description, + String vendor, String version, boolean observeNewJvm, VmStatusListenerRegistrar registrar) { + super(backendName, description, vendor, version, observeNewJvm); this.registrar = registrar; - + try { HostIdentifier hostId = new HostIdentifier((String) null); host = MonitoredHost.getMonitoredHost(hostId); @@ -141,7 +147,7 @@ } private void handleNewVm(int pid) { - if (attachToNewProcessByDefault()) { + if (getObserveNewJvm()) { try { MonitoredVm vm = host.getMonitoredVm(host.getHostIdentifier().resolve(new VmIdentifier(String.valueOf(pid)))); VmListener listener = createVmListener(pid); diff -r e318ef62411d -r db2b89cdef62 agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java --- a/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -48,9 +48,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Collection; - import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -93,8 +90,6 @@ when(backend.getDescription()).thenReturn("testdesc"); when(backend.getObserveNewJvm()).thenReturn(true); when(backend.activate()).thenReturn(true); // TODO: activate() should not return anything and throw exception in error case. - Collection backends = new ArrayList(); - backends.add(backend); backendRegistry = mock(BackendRegistry.class); } diff -r e318ef62411d -r db2b89cdef62 agent/core/src/test/java/com/redhat/thermostat/backend/BaseBackendTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/agent/core/src/test/java/com/redhat/thermostat/backend/BaseBackendTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -0,0 +1,171 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.backend; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; + +import org.junit.Test; + +public class BaseBackendTest { + + @Test(expected = NullPointerException.class) + public void testConstructorRejectsNullName() { + new TestBaseBackend(null, "", "", ""); + } + + @Test(expected = NullPointerException.class) + public void testConstructorRejectsNullVendor() { + new TestBaseBackend("", "", null, ""); + } + + @Test(expected = NullPointerException.class) + public void testConstructorRejectsNullVersion() { + new TestBaseBackend("", "", "", null); + } + + @Test(expected = NullPointerException.class) + public void testConstructorRejectsNullDescription() { + new TestBaseBackend("", null, "", ""); + } + + @Test + public void testConvenienceConstructorDefaultsFalseObserveNewJvm() { + Backend backend = new TestBaseBackend("", "", "", ""); + assertFalse(backend.getObserveNewJvm()); + } + + @Test + public void testConstructorValuesWorkAsExpected() { + String name = "name"; + String description = "description"; + String vendor = "vendor"; + String version = "version"; + Backend backend = new TestBaseBackend(name, description, vendor, version, true); + assertEquals(name, backend.getName()); + assertEquals(description, backend.getDescription()); + assertEquals(vendor, backend.getVendor()); + assertEquals(version, backend.getVersion()); + assertTrue(backend.getObserveNewJvm()); + } + + @Test + public void testSetObserveNewJvm() { + Backend backend = new TestBaseBackend("name", "description", "vendor", "version"); + assertFalse(backend.getObserveNewJvm()); + backend.setObserveNewJvm(true); + assertTrue(backend.getObserveNewJvm()); + backend.setObserveNewJvm(false); + assertFalse(backend.getObserveNewJvm()); + } + + @Test + public void testEquals() { + TestBaseBackend backend = new TestBaseBackend("name", "description", "vendor", "version"); + assertEquals(backend, new TestBaseBackend("name", "description", "vendor", "version")); + + // Uniquely identified by name/vendor/version, desc and other properties shouldn't matter. + assertEquals(backend, new TestBaseBackend("name", "different description", "vendor", "version")); + assertEquals(backend, new TestBaseBackend("name", "description", "vendor", "version", true)); + + // Any one of name/vendor/version should not match. + assertThat(backend, not(equalTo(new TestBaseBackend("another name", "description", "vendor", "version")))); + assertThat(backend, not(equalTo(new TestBaseBackend("name", "description", "other vendor", "version")))); + assertThat(backend, not(equalTo(new TestBaseBackend("name", "description", "vendor", "newer version")))); + } + + @Test + public void testHashcodeReturnsSameForEqualObjects() { + TestBaseBackend backend1 = new TestBaseBackend("name", "description", "vendor", "version"); + TestBaseBackend backend2 = new TestBaseBackend("name", "description", "vendor", "version"); + assertEquals(backend1, backend2); + assertEquals(backend1.hashCode(), backend2.hashCode()); + } + + @Test + public void testHashcodeReturnsSameForSubsequentCalls() { + TestBaseBackend backend = new TestBaseBackend("name", "description", "vendor", "version"); + assertEquals(backend.hashCode(), backend.hashCode()); + } + + @Test + public void testToString() { + TestBaseBackend backend = new TestBaseBackend("name", "description", "vendor", "version"); + assertEquals(backend.toString(), "Backend [name=name, version=version, vendor=vendor, description=description]"); + } + + /* + * Just some passthrough constructors and trivial implementations of abstract methods (not tested here). + */ + private class TestBaseBackend extends BaseBackend { + + public TestBaseBackend(String name, String description, String vendor, + String version) { + super(name, description, vendor, version); + } + + public TestBaseBackend(String name, String description, String vendor, + String version, boolean observeNewJvm) { + super(name, description, vendor, version, observeNewJvm); + } + + @Override + public boolean activate() { + return false; + } + + @Override + public boolean deactivate() { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public int getOrderValue() { + return 0; + } + + } +} diff -r e318ef62411d -r db2b89cdef62 agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java --- a/agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,8 +39,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.isA; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -73,7 +75,6 @@ @Before public void setup() throws URISyntaxException, MonitorException { - BackendID id = mock(BackendID.class); registrar = mock(VmStatusListenerRegistrar.class); listener = mock(VmListener.class); @@ -89,7 +90,8 @@ monitoredVm = mock(MonitoredVm.class); - backend = new TestBackend(id, registrar); + backend = new TestBackend("Test Backend", "Backend for test", "Test Co.", + "0.0.0", registrar); backend.setHost(host); } @@ -140,8 +142,13 @@ VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + // Should be no response if not observing new jvm. + backend.setObserveNewJvm(false); backend.vmStatusChanged(Status.VM_STARTED, 1); + verify(monitoredVm, times(0)).addVmListener(any(VmListener.class)); + backend.setObserveNewJvm(true); + backend.vmStatusChanged(Status.VM_STARTED, 1); verify(monitoredVm).addVmListener(listener); } @@ -151,6 +158,7 @@ VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + backend.setObserveNewJvm(true); backend.vmStatusChanged(Status.VM_ACTIVE, 1); verify(monitoredVm).addVmListener(listener); @@ -172,6 +180,7 @@ VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + backend.setObserveNewJvm(true); backend.vmStatusChanged(Status.VM_STARTED, 1); backend.vmStatusChanged(Status.VM_STOPPED, 1); @@ -197,6 +206,7 @@ MonitorException monitorException = new MonitorException(); doThrow(monitorException).when(monitoredVm).removeVmListener(listener); + backend.setObserveNewJvm(true); backend.vmStatusChanged(Status.VM_STARTED, 1); backend.vmStatusChanged(Status.VM_STOPPED, 1); @@ -211,6 +221,7 @@ VmIdentifier VM_ID = new VmIdentifier(String.valueOf(VM_PID)); when(host.getMonitoredVm(VM_ID)).thenReturn(monitoredVm); + backend.setObserveNewJvm(true); backend.vmStatusChanged(Status.VM_STARTED, 1); backend.deactivate(); verify(monitoredVm).removeVmListener(listener); @@ -218,8 +229,9 @@ private class TestBackend extends VmListenerBackend { - public TestBackend(BackendID id, VmStatusListenerRegistrar registrar) { - super(id, registrar); + public TestBackend(String name, String description, String vendor, + String version, VmStatusListenerRegistrar registrar) { + super(name, description, vendor, version, registrar); } @Override @@ -231,11 +243,6 @@ protected VmListener createVmListener(int pid) { return listener; } - - @Override - public boolean attachToNewProcessByDefault() { - return true; - } } diff -r e318ef62411d -r db2b89cdef62 client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ShellCommand.java --- a/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ShellCommand.java Fri Feb 22 16:52:26 2013 +0100 +++ b/client/cli/src/main/java/com/redhat/thermostat/client/cli/internal/ShellCommand.java Fri Feb 22 16:53:10 2013 +0100 @@ -53,6 +53,7 @@ import org.osgi.framework.ServiceReference; import com.redhat.thermostat.common.Launcher; +import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.cli.AbstractCommand; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; @@ -72,6 +73,7 @@ private static final String PROMPT = "Thermostat > "; private HistoryProvider historyProvider; + private Version version; private BundleContext bundleContext; @@ -88,12 +90,13 @@ } public ShellCommand() { - this(FrameworkUtil.getBundle(ShellCommand.class).getBundleContext(), new HistoryProvider()); + this(FrameworkUtil.getBundle(ShellCommand.class).getBundleContext(), new Version(), new HistoryProvider()); } - ShellCommand(BundleContext context, HistoryProvider provider) { + ShellCommand(BundleContext context, Version version, HistoryProvider provider) { this.historyProvider = provider; this.bundleContext = context; + this.version = version; } @Override @@ -102,6 +105,7 @@ PersistentHistory history = historyProvider.get(); try { + ctx.getConsole().getOutput().println(version.getVersionInfo()); shellMainLoop(ctx, history, term); } catch (IOException ex) { throw new CommandException(ex); diff -r e318ef62411d -r db2b89cdef62 client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ShellCommandTest.java --- a/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ShellCommandTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/client/cli/src/test/java/com/redhat/thermostat/client/cli/internal/ShellCommandTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -60,6 +60,7 @@ import com.redhat.thermostat.client.cli.internal.ShellCommand.HistoryProvider; import com.redhat.thermostat.common.Launcher; +import com.redhat.thermostat.common.Version; import com.redhat.thermostat.common.cli.Arguments; import com.redhat.thermostat.common.cli.CommandContext; import com.redhat.thermostat.common.cli.CommandException; @@ -68,13 +69,23 @@ public class ShellCommandTest { + static private final String VERSION = "Thermostat some version"; + static private final String VERSION_OUTPUT = VERSION + "\n"; + private ShellCommand cmd; + private BundleContext bundleContext; + private HistoryProvider historyProvider; + private Version version; @Before public void setUp() { bundleContext = mock(BundleContext.class); - cmd = new ShellCommand(bundleContext, new HistoryProvider()); + historyProvider = mock(HistoryProvider.class); + version = mock(Version.class); + when(version.getVersionInfo()).thenReturn(VERSION); + + cmd = new ShellCommand(bundleContext, version, historyProvider); } @After @@ -108,7 +119,7 @@ Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); cmd.run(ctx); - assertEquals("Thermostat > quit\n", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > quit\n", ctxFactory.getOutput()); assertEquals("", ctxFactory.getError()); } @@ -119,7 +130,7 @@ Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); cmd.run(ctx); - assertEquals("Thermostat > q\n", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > q\n", ctxFactory.getOutput()); assertEquals("", ctxFactory.getError()); } @@ -130,7 +141,7 @@ Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); cmd.run(ctx); - assertEquals("Thermostat > ", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > ", ctxFactory.getOutput()); assertEquals("", ctxFactory.getError()); } @@ -141,7 +152,7 @@ Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); cmd.run(ctx); - assertEquals("Thermostat > \nThermostat > exit\n", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > \nThermostat > exit\n", ctxFactory.getOutput()); } @Test @@ -150,8 +161,7 @@ when(history.previous()).thenReturn(true); when(history.current()).thenReturn("old-history-value"); - HistoryProvider provider = mock(HistoryProvider.class); - when(provider.get()).thenReturn(history); + when(historyProvider.get()).thenReturn(history); ServiceReference ref = mock(ServiceReference.class); @@ -160,15 +170,13 @@ when(bundleContext.getService(ref)).thenReturn(launcher); TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - cmd = new ShellCommand(bundleContext, provider); - // "\u001b[A" is the escape code for up-arrow. use xxd -p to generate ctxFactory.setInput("\u001b[A\nexit\n"); Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); cmd.run(ctx); - assertEquals("Thermostat > old-history-value\nThermostat > exit\n", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > old-history-value\nThermostat > exit\n", ctxFactory.getOutput()); assertEquals("", ctxFactory.getError()); verify(launcher).setArgs(new String[] {"old-history-value"}); @@ -178,8 +186,7 @@ @Test public void testHistoryIsUpdated() throws CommandException, IOException { PersistentHistory mockHistory = mock(PersistentHistory.class); - HistoryProvider provider = mock(HistoryProvider.class); - when(provider.get()).thenReturn(mockHistory); + when(historyProvider.get()).thenReturn(mockHistory); ServiceReference ref = mock(ServiceReference.class); when(bundleContext.getServiceReference(Launcher.class.getName())).thenReturn(ref); @@ -187,8 +194,6 @@ when(bundleContext.getService(ref)).thenReturn(launcher); TestCommandContextFactory ctxFactory = new TestCommandContextFactory(bundleContext); - cmd = new ShellCommand(bundleContext, provider); - ctxFactory.setInput("add-to-history\nexit\n"); Arguments args = new SimpleArguments(); CommandContext ctx = ctxFactory.createContext(args); @@ -199,7 +204,7 @@ verify(mockHistory).add("add-to-history"); verify(mockHistory).flush(); - assertEquals("Thermostat > add-to-history\nThermostat > exit\n", ctxFactory.getOutput()); + assertEquals(VERSION_OUTPUT + "Thermostat > add-to-history\nThermostat > exit\n", ctxFactory.getOutput()); assertEquals("", ctxFactory.getError()); } diff -r e318ef62411d -r db2b89cdef62 distribution/config/commands/ping.properties --- a/distribution/config/commands/ping.properties Fri Feb 22 16:52:26 2013 +0100 +++ b/distribution/config/commands/ping.properties Fri Feb 22 16:53:10 2013 +0100 @@ -8,6 +8,7 @@ gson.jar, \ mongo.jar, \ commons-beanutils.jar, \ + commons-codec.jar, \ commons-collections.jar, \ commons-logging.jar, \ netty.jar diff -r e318ef62411d -r db2b89cdef62 host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java --- a/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,9 +39,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import com.redhat.thermostat.backend.Backend; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.backend.BaseBackend; import com.redhat.thermostat.common.Clock; import com.redhat.thermostat.common.SystemClock; import com.redhat.thermostat.common.Version; @@ -49,7 +47,7 @@ import com.redhat.thermostat.utils.ProcDataSource; import com.redhat.thermostat.utils.SysConf; -public class HostCpuBackend extends Backend { +public class HostCpuBackend extends BaseBackend { private static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable. @@ -59,14 +57,12 @@ private boolean started; public HostCpuBackend(ScheduledExecutorService executor, CpuStatDAO cpuStatDAO, Version version) { - super(new BackendID("Host CPU Backend", HostCpuBackend.class.getName())); + super("Host CPU Backend", + "Gathers CPU statistics about a host", + "Red Hat, Inc.", + version.getVersionNumber(), true); this.executor = executor; this.cpuStats = cpuStatDAO; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers CPU statistics about a host"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - Clock clock = new SystemClock(); long ticksPerSecond = SysConf.getClockTicksPerSecond(); ProcDataSource source = new ProcDataSource(); @@ -104,16 +100,6 @@ } @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; - } - - @Override public int getOrderValue() { return ORDER_CPU_GROUP; } diff -r e318ef62411d -r db2b89cdef62 host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java --- a/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,14 +39,12 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import com.redhat.thermostat.backend.Backend; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.backend.BaseBackend; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.host.memory.common.MemoryStatDAO; import com.redhat.thermostat.utils.ProcDataSource; -public class HostMemoryBackend extends Backend { +public class HostMemoryBackend extends BaseBackend { private static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable. @@ -56,14 +54,13 @@ private boolean started; public HostMemoryBackend(ScheduledExecutorService executor, MemoryStatDAO memoryStatDAO, Version version) { - super(new BackendID("Host Memory Backend", HostMemoryBackend.class.getName())); + super("Host Memory Backend", + "Gathers memory statistics about a host", + "Red Hat, Inc.", + version.getVersionNumber(), true); this.executor = executor; this.memoryStats = memoryStatDAO; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a host"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - + ProcDataSource source = new ProcDataSource(); memoryStatBuilder = new MemoryStatBuilder(source); } @@ -95,16 +92,6 @@ } @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; - } - - @Override public int getOrderValue() { return ORDER_MEMORY_GROUP; } diff -r e318ef62411d -r db2b89cdef62 integration-tests/pom.xml --- a/integration-tests/pom.xml Fri Feb 22 16:52:26 2013 +0100 +++ b/integration-tests/pom.xml Fri Feb 22 16:53:10 2013 +0100 @@ -57,6 +57,30 @@ + + + org.apache.maven.plugins + maven-antrun-plugin + 1.6 + + + make-target-classes-dir + prepare-package + + + + + + + run + + + + org.apache.maven.plugins @@ -96,6 +120,39 @@ + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-dependency-plugin + + [2.4,) + + copy-dependencies + + + + + + + + + + + + diff -r e318ef62411d -r db2b89cdef62 integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java --- a/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/integration-tests/src/test/java/com/redhat/thermostat/itest/CliTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -37,6 +37,7 @@ package com.redhat.thermostat.itest; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -135,6 +136,39 @@ } @Test + public void testShellPrintsVersionOnStartup() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + + String stdOut = shell.getCurrentStandardOutContents(); + assertTrue(stdOut.contains("Thermostat version ")); + } + + @Test + public void versionArgumentInShellIsNotAllowed() throws Exception { + Spawn shell = spawnThermostat("shell"); + + shell.expect(SHELL_PROMPT); + shell.send("--version\n"); + + shell.expect(SHELL_PROMPT); + + String stdOut = shell.getCurrentStandardOutContents(); + String stdErr = shell.getCurrentStandardErrContents(); + + assertMatchesHelpCommandList(shell.getCurrentStandardOutContents()); + // use the Pattern.DOTALL flag (?s) so that line terminators match with + // ".*". stdOut contains the SHELL_PROMPT too. + assertTrue(stdOut.matches("(?s)^.*\nunknown command '--version'\n.*$")); + assertEquals(stdErr, ""); + + shell.send("exit\n"); + + shell.expectClose(); + } + + @Test public void testShellHelp() throws Exception { Spawn shell = spawnThermostat("help", "shell"); shell.expectClose(); @@ -152,8 +186,13 @@ @Test public void testShellUnrecognizedArgument() throws Exception { Spawn shell = spawnThermostat("shell", "--foo"); - shell.expectErr("Unrecognized option: --foo"); shell.expectClose(); + String stdOut = shell.getCurrentStandardOutContents(); + String expectedOut = "Unrecognized option: --foo\n" + + "usage: thermostat shell\n" + + " launches the Thermostat interactive shell\n" + + "thermostat shell\n\n"; + assertEquals(expectedOut, stdOut); } @Test diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BasicCommandInfo.java Fri Feb 22 16:53:10 2013 +0100 @@ -83,4 +83,8 @@ return resources; } + @Override + public String toString() { + return String.format("%s (description='%s', dependencies='%s')", name, description, resources.toString()); + } } diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSource.java Fri Feb 22 16:53:10 2013 +0100 @@ -66,8 +66,24 @@ @Override public CommandInfo getCommandInfo(String name) throws CommandInfoNotFoundException { - CommandInfo info1 = source1.getCommandInfo(name); - CommandInfo info2 = source2.getCommandInfo(name); + CommandInfo info1; + CommandInfo info2; + + try { + info1 = source1.getCommandInfo(name); + } catch (CommandInfoNotFoundException notFound) { + info1 = null; + } + try { + info2 = source2.getCommandInfo(name); + } catch (CommandInfoNotFoundException notFound) { + info2 = null; + } + + if (info1 == null && info2 == null) { + throw new CommandInfoNotFoundException(name); + } + if (info1 == null) { return info2; } if (info2 == null) { @@ -91,10 +107,9 @@ String cmdName = info.getName(); if (!result.containsKey(cmdName)) { result.put(cmdName, info); - continue; + } else { + result.put(cmdName, merge(result.get(cmdName), info)); } - - result.put(cmdName, merge(result.get(cmdName), info)); } return result.values(); diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/LauncherImpl.java Fri Feb 22 16:53:10 2013 +0100 @@ -119,7 +119,7 @@ try { if (hasNoArguments()) { runHelpCommand(); - } else if (isVersionQuery()) { + } else if (isVersionQuery(inShell)) { // We want to print the version of core // thermostat, so we use the no-arg constructor of Version Version coreVersion = new Version(); @@ -243,7 +243,14 @@ } } Options options = cmd.getOptions(); - Arguments args = parseCommandArguments(cmdArgs, options); + Arguments args = null; + try { + args = parseCommandArguments(cmdArgs, options); + } catch (CommandLineArgumentParseException e) { + out.println(e.getMessage()); + runHelpCommandFor(cmdName); + return; + } setupLogLevel(args); CommandContext ctx = setupCommandContext(cmd, args); cmd.run(ctx); @@ -329,8 +336,13 @@ return ctx; } - private boolean isVersionQuery() { - return args[0].equals(Version.VERSION_OPTION); + private boolean isVersionQuery(boolean inShell) { + // don't allow --version in the shell + if (inShell) { + return false; + } else { + return args[0].equals(Version.VERSION_OPTION); + } } static class LoggingInitializer { diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSource.java Fri Feb 22 16:53:10 2013 +0100 @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -53,19 +54,32 @@ import com.redhat.thermostat.common.cli.CommandInfoSource; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions; +import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand; +/** + * Searches for plugins under $THERMOSTAT_HOME/plugins/ and + * provides information about all commands specified by them. + *

+ * Each plugin is located under + * $THERMOSTAT_HOME/plugins/$PLUGIN_NAME/ and must have a + * plugin.xml file in the main plugin directory. + * + * @see PluginConfigurationParser how the plugin.xml file is parsed + */ public class PluginCommandInfoSource implements CommandInfoSource { - private Logger logger = LoggingUtils.getLogger(PluginCommandInfoSource.class); + private static final String PLUGIN_CONFIG_FILE = "plugin.xml"; - private Map> allInfo = new HashMap<>(); + private static final Logger logger = LoggingUtils.getLogger(PluginCommandInfoSource.class); + + private Map allNewCommands = new HashMap<>(); + private Map> additionalBundlesForExistingCommands = new HashMap<>(); public PluginCommandInfoSource(String internalJarRoot, String pluginRootDir) { this(new File(internalJarRoot), new File(pluginRootDir), new PluginConfigurationParser()); } - public PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, - PluginConfigurationParser parser) { + PluginCommandInfoSource(File internalJarRoot, File pluginRootDir, PluginConfigurationParser parser) { File[] pluginDirs = pluginRootDir.listFiles(); if (pluginDirs == null) { logger.log(Level.SEVERE, "plugin root dir " + pluginRootDir + " does not exist"); @@ -74,28 +88,28 @@ for (File pluginDir : pluginDirs) { try { - File configurationFile = new File(pluginDir, "plugin.conf"); + File configurationFile = new File(pluginDir, PLUGIN_CONFIG_FILE); PluginConfiguration pluginConfig = parser.parse(configurationFile); loadNewAndExtendedCommands(internalJarRoot, pluginDir, pluginConfig); } catch (PluginConfigurationParseException | FileNotFoundException exception) { logger.log(Level.WARNING, "unable to parse plugin configuration", exception); } } + + combineCommands(); } private void loadNewAndExtendedCommands(File coreJarRoot, File pluginDir, PluginConfiguration pluginConfig) { - List allExtensions = pluginConfig.getExtendedCommands(); - - for (CommandExtensions extension : allExtensions) { + for (CommandExtensions extension : pluginConfig.getExtendedCommands()) { String commandName = extension.getCommandName(); - List pluginBundles = extension.getAdditionalBundles(); + List pluginBundles = extension.getPluginBundles(); List dependencyBundles = extension.getDepenedencyBundles(); logger.config("plugin at " + pluginDir + " contributes " + pluginBundles.size() + " bundles to comamnd '" + commandName + "'"); - List bundlePaths = allInfo.get(commandName); + List bundlePaths = additionalBundlesForExistingCommands.get(commandName); if (bundlePaths == null) { bundlePaths = new LinkedList<>(); } @@ -107,23 +121,73 @@ bundlePaths.add(new File(coreJarRoot, bundle).toURI().toString()); } - allInfo.put(commandName, bundlePaths); + additionalBundlesForExistingCommands.put(commandName, bundlePaths); + } + + for (NewCommand command : pluginConfig.getNewCommands()) { + String commandName = command.getCommandName(); + logger.config("plugin at " + pluginDir + " contributes new command '" + commandName + "'"); + + if (allNewCommands.containsKey(commandName)) { + throw new IllegalStateException("multiple plugins are providing the command " + commandName); + } + + List bundlePaths = new LinkedList<>(); + + for (String bundle : command.getPluginBundles()) { + bundlePaths.add(new File(pluginDir, bundle).toURI().toString()); + } + for (String bundle : command.getDepenedencyBundles()) { + bundlePaths.add(new File(coreJarRoot, bundle).toURI().toString()); + } + BasicCommandInfo info = new BasicCommandInfo(commandName, + command.getDescription(), + command.getUsage(), + command.getOptions(), + bundlePaths); + + allNewCommands.put(commandName, info); + } + + } + + private void combineCommands() { + Iterator>> iter = additionalBundlesForExistingCommands.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry> entry = iter.next(); + if (allNewCommands.containsKey(entry.getKey())) { + BasicCommandInfo old = allNewCommands.get(entry.getKey()); + List updatedResources = new ArrayList<>(); + updatedResources.addAll(old.getDependencyResourceNames()); + updatedResources.addAll(entry.getValue()); + BasicCommandInfo updated = new BasicCommandInfo(old.getName(), + old.getDescription(), + old.getUsage(), + old.getOptions(), + updatedResources); + allNewCommands.put(entry.getKey(), updated); + iter.remove(); + } } } @Override public CommandInfo getCommandInfo(String name) throws CommandInfoNotFoundException { - List bundles = allInfo.get(name); - if (bundles == null) { - return null; + if (allNewCommands.containsKey(name)) { + return allNewCommands.get(name); } - return new BasicCommandInfo(name, null, null, null, bundles); + List bundles = additionalBundlesForExistingCommands.get(name); + if (bundles != null) { + return new BasicCommandInfo(name, null, null, null, bundles); + } + throw new CommandInfoNotFoundException(name); } @Override public Collection getCommandInfos() { List result = new ArrayList<>(); - for (Entry> entry : allInfo.entrySet()) { + result.addAll(allNewCommands.values()); + for (Entry> entry : additionalBundlesForExistingCommands.entrySet()) { result.add(new BasicCommandInfo(entry.getKey(), null, null, null, entry.getValue())); } return result; diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfiguration.java Fri Feb 22 16:53:10 2013 +0100 @@ -75,7 +75,7 @@ return commandName; } - public List getAdditionalBundles() { + public List getPluginBundles() { return Collections.unmodifiableList(additionalResources); } @@ -119,11 +119,11 @@ return options; } - public List getAdditionalBundles() { + public List getPluginBundles() { return Collections.unmodifiableList(additionalResources); } - public List getCoreDepenedencyBundles() { + public List getDepenedencyBundles() { return Collections.unmodifiableList(coreDeps); } } diff -r e318ef62411d -r db2b89cdef62 launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java --- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParser.java Fri Feb 22 16:53:10 2013 +0100 @@ -42,8 +42,10 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -56,18 +58,58 @@ import org.xml.sax.SAXException; import com.redhat.thermostat.common.Pair; +import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions; import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand; +/** + * Parses the configuration of a plugin as specified in an {@code File} or an + * {@code InputStream}. This configuration describes which new commands this + * plugin provides as well as additional jars to load for existing commands. + *

+ * A example configuration looks like the following: + * + *

+ * <?xml version="1.0"?>
+ * <plugin>
+ *   <commands>
+ *     <command type="extends">
+ *       <name>gui</name>
+ *       <bundles>
+ *         <bundle>hello-world-plugin-0.1-SNAPSHOT.jar</bundle>
+ *       </bundles>
+ *       <dependencies>
+ *         <dependency>thermostat-client-core-0.6.0-SNAPSHOT.jar</dependency>
+ *       </dependencies>
+ *     </command>
+ *     <command type="provides">
+ *       <name>hello</name>
+ *       <description>print hello</description>
+ *       <usage>hello</usage>
+ *       <bundles>
+ *         <bundle>hello-world-plugin-0.1-SNAPSHOT.jar</bundle>
+ *       </bundles>
+ *       <dependencies>
+ *         <dependency>thermostat-client-core-0.6.0-SNAPSHOT.jar</dependency>
+ *       </dependencies>
+ *     </command>
+ *   </commands>
+ * </plugin>
+ * 
+ *

+ * This class is thread-safe + */ public class PluginConfigurationParser { - // no state :) + private static final Logger logger = LoggingUtils.getLogger(PluginConfigurationParser.class); + + // thread safe because there is no state :) public PluginConfiguration parse(File configurationFile) throws FileNotFoundException { - return parse(new FileInputStream(configurationFile)); + return parse(configurationFile.getParentFile().getName(), new FileInputStream(configurationFile)); } - public PluginConfiguration parse(InputStream configurationStream) { + public PluginConfiguration parse(String pluginName, InputStream configurationStream) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); @@ -76,13 +118,13 @@ if (rootNode == null) { throw new PluginConfigurationParseException("no configuration found"); } - return parseRootElement(rootNode); + return parseRootElement(pluginName, rootNode); } catch (ParserConfigurationException | SAXException | IOException exception) { throw new PluginConfigurationParseException("failed to parse plugin configuration", exception); } } - private PluginConfiguration parseRootElement(Node root) { + private PluginConfiguration parseRootElement(String pluginName, Node root) { List newCommands = Collections.emptyList(); List extensions = Collections.emptyList(); @@ -92,7 +134,7 @@ for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeName().equals("commands")) { - commands = parseCommands(node); + commands = parseCommands(pluginName, node); } } } @@ -100,22 +142,31 @@ return new PluginConfiguration(commands.getFirst(), commands.getSecond()); } - private Pair, List> parseCommands(Node commandsNode) { + private Pair, List> parseCommands(String pluginName, Node commandsNode) { List newCommands = new ArrayList(); List extendedCommands = new ArrayList(); NodeList childNodes = commandsNode.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node node = childNodes.item(i); - if (node.getNodeName().equals("new")) { - newCommands.add(parseNewCommand(node)); - } else if (node.getNodeName().equals("existing")) { - extendedCommands.add(parseAdditionsToExistingCommand(node)); + if (node.getNodeName().equals("command")) { + String type = node.getAttributes().getNamedItem("type").getNodeValue(); + if (type.equals("extends")) { + CommandExtensions additions = parseAdditionsToExistingCommand(pluginName, node); + if (additions != null) { + extendedCommands.add(additions); + } + } else if (type.equals("provides")) { + NewCommand newCmd = parseNewCommand(pluginName, node); + if (newCmd != null) { + newCommands.add(newCmd); + } + } } } return new Pair<>(newCommands, extendedCommands); } - private CommandExtensions parseAdditionsToExistingCommand(Node commandNode) { + private CommandExtensions parseAdditionsToExistingCommand(String pluginName, Node commandNode) { String name = null; List bundles = new ArrayList<>(); List dependencies = new ArrayList<>(); @@ -124,33 +175,25 @@ for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeName().equals("name")) { - name = node.getTextContent(); + name = node.getTextContent().trim(); } else if (node.getNodeName().equals("bundles")) { - String[] bundleNames = node.getTextContent().split(","); - for (String bundleName : bundleNames) { - if (bundleName.trim().length() == 0) { - continue; - } - bundles.add(bundleName.trim()); - } + bundles.addAll(parseBundles(node)); } else if (node.getNodeName().equals("dependencies")) { - String[] dependencyNames = node.getTextContent().split(","); - for (String bundleName : dependencyNames) { - if (bundleName.trim().length() == 0) { - continue; - } - dependencies.add(bundleName); - } + dependencies.addAll(parseDependencies(node)); } } + if (name == null) { + logger.warning("plugin " + pluginName + " provides extensions without specifying the command"); + return null; + } return new CommandExtensions(name, bundles, dependencies); } - private NewCommand parseNewCommand(Node commandNode) { + private NewCommand parseNewCommand(String pluginName, Node commandNode) { String name = null; String usage = null; String description = null; - Options options = null; + Options options = new Options(); List bundles = new ArrayList<>(); List dependencies = new ArrayList<>(); @@ -158,37 +201,58 @@ for (int i = 0; i < nodes.getLength(); i++) { Node node = nodes.item(i); if (node.getNodeName().equals("name")) { - name = node.getTextContent(); + name = node.getTextContent().trim(); } else if (node.getNodeName().equals("usage")) { - usage = node.getTextContent(); + usage = node.getTextContent().trim(); } else if (node.getNodeName().equals("description")) { - description = node.getTextContent(); + description = node.getTextContent().trim(); } else if (node.getNodeName().equals("arguments")) { options = parseArguments(node); } else if (node.getNodeName().equals("bundles")) { - String[] bundleNames = node.getTextContent().split(","); - for (String bundleName : bundleNames) { - if (bundleName.trim().length() == 0) { - continue; - } - bundles.add(bundleName); - } + bundles.addAll(parseBundles(node)); } else if (node.getNodeName().equals("dependencies")) { - String[] dependencyNames = node.getTextContent().split(","); - for (String bundleName : dependencyNames) { - if (bundleName.trim().length() == 0) { - continue; - } - dependencies.add(bundleName); - } + dependencies.addAll(parseDependencies(node)); } } - return new NewCommand(name, usage, description, options, bundles, dependencies); + + if (name == null || usage == null || description == null) { + logger.warning("plugin " + pluginName + " provides an incomplete new command: " + + "name='" + name + "', usage='" + usage + "', description='" + description + "', options='" + options + "'"); + return null; + } else { + return new NewCommand(name, usage, description, options, bundles, dependencies); + } + } + + private Collection parseBundles(Node bundlesNode) { + List bundles = new ArrayList<>(); + NodeList nodes = bundlesNode.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node.getNodeName().equals("bundle")) { + String bundleName = node.getTextContent().trim(); + bundles.add(bundleName); + } + } + return bundles; + } + + private Collection parseDependencies(Node dependenciesNode) { + List dependencies = new ArrayList<>(); + NodeList nodes = dependenciesNode.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Node node = nodes.item(i); + if (node.getNodeName().equals("dependency")) { + String bundleName = node.getTextContent().trim(); + dependencies.add(bundleName); + } + } + return dependencies; } private Options parseArguments(Node argumentsNode) { - // need to identify a way to express arguments - return null; + // TODO need to identify a way to express arguments + return new Options(); } } diff -r e318ef62411d -r db2b89cdef62 launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BasicCommandInfoTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -48,9 +48,9 @@ @Test public void testBasics() { - final String NAME = "name"; - final String DESCRIPTION = "description"; - final String USAGE = "usage"; + final String NAME = "the_name"; + final String DESCRIPTION = "some-description"; + final String USAGE = "some-usage"; final Options OPTIONS = new Options(); final List RESOURCES = Collections.emptyList(); @@ -62,5 +62,6 @@ assertEquals(OPTIONS, info.getOptions()); assertEquals(RESOURCES, info.getDependencyResourceNames()); + assertEquals(String.format("%s (description='%s', dependencies='%s')", NAME, DESCRIPTION, RESOURCES.toString()), info.toString()); } } diff -r e318ef62411d -r db2b89cdef62 launcher/src/test/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSourceTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/CompoundCommandInfoSourceTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -0,0 +1,154 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * 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.launcher.internal; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.cli.Options; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.cli.CommandInfo; +import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; +import com.redhat.thermostat.common.cli.CommandInfoSource; + +public class CompoundCommandInfoSourceTest { + + private CommandInfoSource source1; + private CommandInfoSource source2; + private CompoundCommandInfoSource compoundSource; + + @Before + public void setUp() { + source1 = mock(CommandInfoSource.class); + source2 = mock(CommandInfoSource.class); + + compoundSource = new CompoundCommandInfoSource(source1, source2); + } + + @Test(expected = CommandInfoNotFoundException.class) + public void verifyExceptionThrownOnUnknownCommand() { + String NAME = "test-command-please-ignore"; + when(source1.getCommandInfo(NAME)).thenThrow(new CommandInfoNotFoundException(NAME)); + when(source2.getCommandInfo(NAME)).thenThrow(new CommandInfoNotFoundException(NAME)); + + compoundSource.getCommandInfo(NAME); + } + + @Test + public void verifyGetCommandInfoDelegatesToSource1() { + CommandInfo cmdInfo = mock(CommandInfo.class); + String NAME = "test-command-please-ignore"; + when(source1.getCommandInfo(NAME)).thenReturn(cmdInfo); + when(source2.getCommandInfo(NAME)).thenThrow(new CommandInfoNotFoundException(NAME)); + + CommandInfo result = compoundSource.getCommandInfo(NAME); + assertEquals(cmdInfo, result); + } + + @Test + public void verifyGetCommandInfoDelegatesToSource2() { + CommandInfo cmdInfo = mock(CommandInfo.class); + String NAME = "test-command-please-ignore"; + when(source1.getCommandInfo(NAME)).thenThrow(new CommandInfoNotFoundException(NAME)); + when(source2.getCommandInfo(NAME)).thenReturn(cmdInfo); + + CommandInfo result = compoundSource.getCommandInfo(NAME); + assertEquals(cmdInfo, result); + } + + @Test + public void verifyGetCommandInfoMergesResultFromBothSources() { + String NAME = "test-command-please-ignore"; + String DESCRIPTION = "test-description"; + String USAGE = "test-usage"; + Options OPTIONS = new Options(); + List DEPS1 = Arrays.asList("1test1", "1test2"); + List DEPS2 = Arrays.asList("2test1"); + + CommandInfo cmdInfo1 = mock(CommandInfo.class); + when(cmdInfo1.getName()).thenReturn(NAME); + when(cmdInfo1.getDescription()).thenReturn(DESCRIPTION); + when(cmdInfo1.getUsage()).thenReturn(USAGE); + when(cmdInfo1.getOptions()).thenReturn(OPTIONS); + when(cmdInfo1.getDependencyResourceNames()).thenReturn(DEPS1); + + CommandInfo cmdInfo2 = mock(CommandInfo.class); + when(cmdInfo2.getName()).thenReturn(NAME); + when(cmdInfo2.getDependencyResourceNames()).thenReturn(DEPS2); + + when(source1.getCommandInfo(NAME)).thenReturn(cmdInfo1); + when(source2.getCommandInfo(NAME)).thenReturn(cmdInfo2); + + CommandInfo result = compoundSource.getCommandInfo(NAME); + assertEquals(NAME, result.getName()); + assertEquals(DESCRIPTION, result.getDescription()); + assertEquals(USAGE, result.getUsage()); + assertEquals(OPTIONS, result.getOptions()); + + ArrayList combined = new ArrayList<>(DEPS1); + combined.addAll(DEPS2); + assertEquals(combined, result.getDependencyResourceNames()); + } + + @Test + public void verifyGetCommandInfosMergesResultsFromBothSources() { + CommandInfo cmdInfo11 = mock(CommandInfo.class); + when(cmdInfo11.getName()).thenReturn("cmd1"); + CommandInfo cmdInfo12 = mock(CommandInfo.class); + when(cmdInfo12.getName()).thenReturn("cmd2"); + + when(source1.getCommandInfos()).thenReturn(Arrays.asList(cmdInfo11, cmdInfo12)); + + CommandInfo cmdInfo21 = mock(CommandInfo.class); + when(cmdInfo21.getName()).thenReturn("cmd3"); + CommandInfo cmdInfo22 = mock(CommandInfo.class); + when(cmdInfo22.getName()).thenReturn("cmd2"); + + when(source2.getCommandInfos()).thenReturn(Arrays.asList(cmdInfo21, cmdInfo22)); + + Collection results = compoundSource.getCommandInfos(); + + } +} diff -r e318ef62411d -r db2b89cdef62 launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/LauncherImplTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,7 +39,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.isA; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -89,7 +88,6 @@ import com.redhat.thermostat.common.locale.LocaleResources; import com.redhat.thermostat.common.locale.Translate; import com.redhat.thermostat.common.tools.ApplicationState; -import com.redhat.thermostat.common.utils.OSGIUtils; import com.redhat.thermostat.launcher.BundleManager; import com.redhat.thermostat.launcher.TestCommand; import com.redhat.thermostat.launcher.internal.HelpCommand; @@ -162,8 +160,10 @@ TestCommand cmd1 = new TestCommand(name1, new TestCmd1()); CommandInfo info1 = mock(CommandInfo.class); when(info1.getName()).thenReturn(name1); + when(info1.getUsage()).thenReturn(name1 + " <--arg1 > [--arg2 ]"); Options options1 = new Options(); Option opt1 = new Option(null, "arg1", true, null); + opt1.setRequired(true); options1.addOption(opt1); Option opt2 = new Option(null, "arg2", true, null); options1.addOption(opt2); @@ -365,6 +365,42 @@ } @Test + public void testBadOption() { + String expected = "Unrecognized option: --argNotAccepted\n" + + "usage: thermostat test1 <--arg1 > [--arg2 ]\n" + + " description 1\n" + + "thermostat test1\n" + + " --arg1 \n" + + " --arg2 \n" + + " -l,--logLevel \n"; + runAndVerifyCommand(new String[] {"test1", "--arg1", "arg1value", "--argNotAccepted"}, expected, false); + } + + @Test + public void testMissingRequiredOption() { + String expected = "Missing required option: --arg1\n" + + "usage: thermostat test1 <--arg1 > [--arg2 ]\n" + + " description 1\n" + + "thermostat test1\n" + + " --arg1 \n" + + " --arg2 \n" + + " -l,--logLevel \n"; + runAndVerifyCommand(new String[] {"test1"}, expected, false); + } + + @Test + public void testOptionMissingRequiredArgument() { + String expected = "Missing argument for option: arg1\n" + + "usage: thermostat test1 <--arg1 > [--arg2 ]\n" + + " description 1\n" + + "thermostat test1\n" + + " --arg1 \n" + + " --arg2 \n" + + " -l,--logLevel \n"; + runAndVerifyCommand(new String[] {"test1", "--arg1"}, expected, false); + } + + @Test public void testCommandInfoNotFound() throws CommandInfoNotFoundException, BundleException, IOException { when(infos.getCommandInfo("foo")).thenThrow(new CommandInfoNotFoundException("foo")); doThrow(new CommandInfoNotFoundException("foo")).when(registry).addBundlesFor("foo"); diff -r e318ef62411d -r db2b89cdef62 launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginCommandInfoSourceTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -49,18 +49,22 @@ import java.util.Arrays; import java.util.List; +import org.apache.commons.cli.Options; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import com.redhat.thermostat.common.cli.CommandInfo; +import com.redhat.thermostat.common.cli.CommandInfoNotFoundException; import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions; +import com.redhat.thermostat.launcher.internal.PluginConfiguration.NewCommand; public class PluginCommandInfoSourceTest { // name paths so anything tying to use them/create them will blow up private static final String PLUGIN_ROOT = "/fake/${PLUGIN_ROOT}"; private static final String JAR_ROOT = "/fake/${JAR_ROOT}"; + private File jarRootDir; private File pluginRootDir; private PluginConfigurationParser parser; @@ -92,7 +96,7 @@ List configurationFiles = configFilesCaptor.getAllValues(); assertEquals(pluginDirs.length, configurationFiles.size()); for (int i = 0; i < pluginDirs.length; i++) { - assertEquals(new File(pluginDirs[i], "plugin.conf"), configurationFiles.get(i)); + assertEquals(new File(pluginDirs[i], "plugin.xml"), configurationFiles.get(i)); } } @@ -106,11 +110,18 @@ PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir, pluginRootDir, parser); } + @Test(expected = CommandInfoNotFoundException.class) + public void verifyMissingCommandInfo() { + PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir, pluginRootDir, parser); + + source.getCommandInfo("TEST"); + } + @Test public void verifyCommandInfoObjectsToExtendExistingCommandsAreCreated() { CommandExtensions extensions = mock(CommandExtensions.class); when(extensions.getCommandName()).thenReturn("command-name"); - when(extensions.getAdditionalBundles()).thenReturn(Arrays.asList("additional-bundle")); + when(extensions.getPluginBundles()).thenReturn(Arrays.asList("additional-bundle")); when(extensions.getDepenedencyBundles()).thenReturn(Arrays.asList("dependency-bundle")); when(parserResult.getExtendedCommands()).thenReturn(Arrays.asList(extensions)); @@ -130,4 +141,43 @@ assertTrue(info.getDependencyResourceNames().contains(expectedDep2Name)); } + @Test + public void verifyCommandInfoObjectsForNewComamndsAreCreated() { + final String NAME = "command-name"; + final String DESCRIPTION = "description of the command"; + final String USAGE = "usage"; + final Options OPTIONS = new Options(); + final String PLUGIN_BUNDLE = "plugin-bundle.jar"; + final String DEPENDENCY_BUNDLE = "dependency-bundle.jar"; + + NewCommand cmd = mock(NewCommand.class); + when(cmd.getCommandName()).thenReturn(NAME); + when(cmd.getDescription()).thenReturn(DESCRIPTION); + when(cmd.getUsage()).thenReturn(USAGE); + when(cmd.getOptions()).thenReturn(OPTIONS); + when(cmd.getPluginBundles()).thenReturn(Arrays.asList(PLUGIN_BUNDLE)); + when(cmd.getDepenedencyBundles()).thenReturn(Arrays.asList(DEPENDENCY_BUNDLE)); + + when(parserResult.getNewCommands()).thenReturn(Arrays.asList(cmd)); + + File[] pluginDirs = new File[] { new File(PLUGIN_ROOT, "plugin1") }; + when(pluginRootDir.listFiles()).thenReturn(pluginDirs); + + PluginCommandInfoSource source = new PluginCommandInfoSource(jarRootDir, pluginRootDir, parser); + + CommandInfo result = source.getCommandInfo(NAME); + + assertEquals(NAME, result.getName()); + assertEquals(DESCRIPTION, result.getDescription()); + assertEquals(USAGE, result.getUsage()); + assertEquals(OPTIONS, result.getOptions()); + + String expectedDep1Name = new File(PLUGIN_ROOT + "/plugin1/" + PLUGIN_BUNDLE).toURI().toString(); + String expectedDep2Name = new File(JAR_ROOT + "/" + DEPENDENCY_BUNDLE).toURI().toString(); + + List deps = result.getDependencyResourceNames(); + assertEquals(2, deps.size()); + assertTrue(deps.contains(expectedDep1Name)); + assertTrue(deps.contains(expectedDep2Name)); + } } diff -r e318ef62411d -r db2b89cdef62 launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java --- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/PluginConfigurationParserTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -37,6 +37,7 @@ package com.redhat.thermostat.launcher.internal; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; @@ -44,6 +45,7 @@ import java.util.Arrays; import java.util.List; +import org.apache.commons.cli.Options; import org.junit.Test; import com.redhat.thermostat.launcher.internal.PluginConfiguration.CommandExtensions; @@ -55,7 +57,7 @@ public void testEmptyConfigurationThrowsException() throws UnsupportedEncodingException { String config = "\n"; PluginConfigurationParser parser = new PluginConfigurationParser(); - parser.parse(new ByteArrayInputStream(config.getBytes("UTF-8"))); + parser.parse("test", new ByteArrayInputStream(config.getBytes("UTF-8"))); fail("should not reach here"); } @@ -66,7 +68,7 @@ "\n" + "\n" + ""; - PluginConfiguration result = parser.parse(new ByteArrayInputStream(config.getBytes("UTF-8"))); + PluginConfiguration result = parser.parse("test", new ByteArrayInputStream(config.getBytes("UTF-8"))); assertEquals(0, result.getExtendedCommands().size()); assertEquals(0, result.getNewCommands().size()); @@ -77,16 +79,22 @@ String config = "\n" + "\n" + " \n" + - " \n" + + " \n" + " test\n" + - " foo,bar,baz,\n" + - " thermostat-foo\n" + - " \n" + + " \n" + + " foo\n" + + " bar\n" + + " baz\n" + + " \n" + + " \n" + + " thermostat-foo\n" + + " \n" + + " \n" + " \n" + ""; PluginConfiguration result = new PluginConfigurationParser() - .parse(new ByteArrayInputStream(config.getBytes("UTF-8"))); + .parse("test", new ByteArrayInputStream(config.getBytes("UTF-8"))); assertEquals(0, result.getNewCommands().size()); @@ -95,7 +103,7 @@ CommandExtensions first = extensions.get(0); assertEquals("test", first.getCommandName()); - assertEquals(Arrays.asList("foo", "bar", "baz"), first.getAdditionalBundles()); + assertEquals(Arrays.asList("foo", "bar", "baz"), first.getPluginBundles()); assertEquals(Arrays.asList("thermostat-foo"), first.getDepenedencyBundles()); } @@ -104,18 +112,24 @@ String config = "\n" + "\n" + " \n" + - " \n" + + " \n" + " test\n" + " usage: test\n" + " description\n" + - " foo,bar,baz,\n" + - " thermostat-foo\n" + - " \n" + + " \n" + + " foo\n" + + " bar\n" + + " baz\n" + + " \n" + + " \n" + + " thermostat-foo\n" + + " \n" + + " \n" + " \n" + ""; PluginConfiguration result = new PluginConfigurationParser() - .parse(new ByteArrayInputStream(config.getBytes("UTF-8"))); + .parse("test", new ByteArrayInputStream(config.getBytes("UTF-8"))); List extensions = result.getExtendedCommands(); assertEquals(0, extensions.size()); @@ -127,9 +141,43 @@ assertEquals("test", newCommand.getCommandName()); assertEquals("usage: test", newCommand.getUsage()); assertEquals("description", newCommand.getDescription()); - assertEquals(null, newCommand.getOptions()); - assertEquals(Arrays.asList("foo", "bar", "baz"), newCommand.getAdditionalBundles()); - assertEquals(Arrays.asList("thermostat-foo"), newCommand.getCoreDepenedencyBundles()); + Options opts = newCommand.getOptions(); + assertTrue(opts.getOptions().isEmpty()); + assertTrue(opts.getRequiredOptions().isEmpty()); + assertEquals(Arrays.asList("foo", "bar", "baz"), newCommand.getPluginBundles()); + assertEquals(Arrays.asList("thermostat-foo"), newCommand.getDepenedencyBundles()); } + @Test + public void testSpacesAtStartAndEndAreTrimmed() throws UnsupportedEncodingException { + String config = "\n" + + "\n" + + " \n" + + " \n" + + " \ntest \n\n" + + " \n" + + " \n \t \nfoo\t \n \n\n" + + " \tbar baz\n\n" + + " buzz\n" + + " \n" + + " \n\t\n\t \t\t\n" + + " \t\t\t thermostat-foo\n\t\t\n\n" + + " \n" + + " \n" + + " \n" + + ""; + + PluginConfiguration result = new PluginConfigurationParser() + .parse("test", new ByteArrayInputStream(config.getBytes("UTF-8"))); + + assertEquals(0, result.getNewCommands().size()); + + List extensions = result.getExtendedCommands(); + assertEquals(1, extensions.size()); + + CommandExtensions first = extensions.get(0); + assertEquals("test", first.getCommandName()); + assertEquals(Arrays.asList("foo", "bar baz", "buzz"), first.getPluginBundles()); + assertEquals(Arrays.asList("thermostat-foo"), first.getDepenedencyBundles()); + } } diff -r e318ef62411d -r db2b89cdef62 numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java --- a/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -41,9 +41,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import com.redhat.thermostat.backend.Backend; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.backend.BaseBackend; import com.redhat.thermostat.common.ApplicationService; import com.redhat.thermostat.common.Timer; import com.redhat.thermostat.common.Timer.SchedulingType; @@ -53,7 +51,7 @@ import com.redhat.thermostat.numa.common.NumaNodeStat; import com.redhat.thermostat.numa.common.NumaStat; -public class NumaBackend extends Backend { +public class NumaBackend extends BaseBackend { private static final Logger log = Logger.getLogger(NumaBackend.class.getName()); @@ -66,15 +64,13 @@ private Timer timer; public NumaBackend(ApplicationService appService, NumaDAO numaDAO, NumaCollector numaCollector, Version version) { - super(new BackendID("NUMA Backend", NumaBackend.class.getName())); + super("NUMA Backend", + "Gathers NUMA statistics about a host", + "Red Hat, Inc.", + version.getVersionNumber()); this.appService = appService; this.numaDAO = numaDAO; this.numaCollector = numaCollector; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers NUMA statistics about a host"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - } @Override @@ -123,11 +119,6 @@ } @Override - public boolean attachToNewProcessByDefault() { - return false; - } - - @Override public int getOrderValue() { return ORDER_MEMORY_GROUP + 80; } diff -r e318ef62411d -r db2b89cdef62 numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java --- a/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -161,11 +161,6 @@ } @Test - public void testAttachToNewProcessByDefault() { - assertFalse(backend.attachToNewProcessByDefault()); - } - - @Test public void testOrderValue() { assertEquals(Ordered.ORDER_MEMORY_GROUP + 80, backend.getOrderValue()); } diff -r e318ef62411d -r db2b89cdef62 pom.xml --- a/pom.xml Fri Feb 22 16:52:26 2013 +0100 +++ b/pom.xml Fri Feb 22 16:53:10 2013 +0100 @@ -59,6 +59,7 @@ target 1.7 ${project.build.directory} + ${thermostat.build.directory}/${project.build.finalName} /usr/share/java 4.10 diff -r e318ef62411d -r db2b89cdef62 system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java --- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -46,9 +46,7 @@ import sun.jvmstat.monitor.MonitorException; import sun.jvmstat.monitor.MonitoredHost; -import com.redhat.thermostat.backend.Backend; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.backend.BaseBackend; import com.redhat.thermostat.common.utils.LoggingUtils; import com.redhat.thermostat.storage.dao.HostInfoDAO; import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO; @@ -56,7 +54,7 @@ import com.redhat.thermostat.storage.model.NetworkInterfaceInfo; import com.redhat.thermostat.utils.ProcDataSource; -public class SystemBackend extends Backend { +public class SystemBackend extends BaseBackend { private static final Logger logger = LoggingUtils.getLogger(SystemBackend.class); @@ -75,14 +73,13 @@ public SystemBackend(HostInfoDAO hostInfoDAO, NetworkInterfaceInfoDAO netInfoDAO, VmInfoDAO vmInfoDAO, VmStatusChangeNotifier notifier) { - super(new BackendID("System Backend", SystemBackend.class.getName())); + super("System Backend", + "Gathers basic information from the system", + "Red Hat, Inc.", + "0.5.0", true); this.hostInfos = hostInfoDAO; this.networkInterfaces = netInfoDAO; - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers basic information from the system"); - setConfigurationValue(BackendsProperties.VERSION.name(), "0.5.0"); - ProcDataSource source = new ProcDataSource(); hostInfoBuilder = new HostInfoBuilder(source); hostListener = new JvmStatHostListener(vmInfoDAO, notifier); @@ -148,16 +145,6 @@ } @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; - } - - @Override public int getOrderValue() { return ORDER_DEFAULT_GROUP; } diff -r e318ef62411d -r db2b89cdef62 vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java --- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,8 +39,6 @@ import sun.jvmstat.monitor.event.VmListener; import com.redhat.thermostat.agent.VmStatusListenerRegistrar; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; import com.redhat.thermostat.backend.VmListenerBackend; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.classstat.common.VmClassStatDAO; @@ -50,22 +48,10 @@ private final VmClassStatDAO vmClassStats; public VmClassStatBackend(VmClassStatDAO vmClassStatDAO, Version version, VmStatusListenerRegistrar registrar) { - super(new BackendID("VM Classes Backend", VmClassStatBackend.class.getName()), registrar); + super("VM Classes Backend", + "Gathers class loading statistics about a JVM", + "Red Hat, Inc.", version.getVersionNumber(), true, registrar); this.vmClassStats = vmClassStatDAO; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers class loading statistics about a JVM"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - } - - @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; } @Override diff -r e318ef62411d -r db2b89cdef62 vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java --- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -47,9 +47,7 @@ import com.redhat.thermostat.agent.VmStatusListener; import com.redhat.thermostat.agent.VmStatusListenerRegistrar; -import com.redhat.thermostat.backend.Backend; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; +import com.redhat.thermostat.backend.BaseBackend; import com.redhat.thermostat.common.Clock; import com.redhat.thermostat.common.SystemClock; import com.redhat.thermostat.common.Version; @@ -59,7 +57,7 @@ import com.redhat.thermostat.vm.cpu.common.VmCpuStatDAO; import com.redhat.thermostat.vm.cpu.common.model.VmCpuStat; -public class VmCpuBackend extends Backend implements VmStatusListener { +public class VmCpuBackend extends BaseBackend implements VmStatusListener { private static final Logger LOGGER = LoggingUtils.getLogger(VmCpuBackend.class); static final long PROC_CHECK_INTERVAL = 1000; // TODO make this configurable. @@ -74,15 +72,14 @@ public VmCpuBackend(ScheduledExecutorService executor, VmCpuStatDAO vmCpuStatDao, Version version, VmStatusListenerRegistrar registrar) { - super(new BackendID("VM CPU Backend", VmCpuBackend.class.getName())); + super("VM CPU Backend", + "Gathers CPU statistics about a JVM", + "Red Hat, Inc.", + version.getVersionNumber(), true); this.executor = executor; this.vmCpuStats = vmCpuStatDao; this.registrar = registrar; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers CPU statistics about a JVM"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - + Clock clock = new SystemClock(); long ticksPerSecond = SysConf.getClockTicksPerSecond(); ProcDataSource source = new ProcDataSource(); @@ -134,16 +131,6 @@ } @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; - } - - @Override public int getOrderValue() { return ORDER_CPU_GROUP + 50; } diff -r e318ef62411d -r db2b89cdef62 vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java --- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -39,8 +39,6 @@ import sun.jvmstat.monitor.event.VmListener; import com.redhat.thermostat.agent.VmStatusListenerRegistrar; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; import com.redhat.thermostat.backend.VmListenerBackend; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.gc.common.VmGcStatDAO; @@ -50,22 +48,10 @@ private final VmGcStatDAO vmGcStats; public VmGcBackend(VmGcStatDAO vmGcStatDAO, Version version, VmStatusListenerRegistrar registrar) { - super(new BackendID("VM GC Backend", VmGcBackend.class.getName()), registrar); + super("VM GC Backend", + "Gathers garbage collection statistics about a JVM", + "Red Hat, Inc.", version.getVersionNumber(), true, registrar); this.vmGcStats = vmGcStatDAO; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers garbage collection statistics about a JVM"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - } - - @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; } @Override diff -r e318ef62411d -r db2b89cdef62 vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java --- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Fri Feb 22 16:52:26 2013 +0100 +++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java Fri Feb 22 16:53:10 2013 +0100 @@ -37,8 +37,6 @@ package com.redhat.thermostat.vm.memory.agent.internal; import com.redhat.thermostat.agent.VmStatusListenerRegistrar; -import com.redhat.thermostat.backend.BackendID; -import com.redhat.thermostat.backend.BackendsProperties; import com.redhat.thermostat.backend.VmListenerBackend; import com.redhat.thermostat.common.Version; import com.redhat.thermostat.vm.memory.common.VmMemoryStatDAO; @@ -48,22 +46,10 @@ private VmMemoryStatDAO vmMemoryStats; public VmMemoryBackend(VmMemoryStatDAO vmMemoryStatDAO, Version version, VmStatusListenerRegistrar registrar) { - super(new BackendID("VM Memory Backend", VmMemoryBackend.class.getName()), registrar); + super("VM Memory Backend", + "Gathers memory statistics about a JVM", + "Red Hat, Inc.", version.getVersionNumber(), true, registrar); this.vmMemoryStats = vmMemoryStatDAO; - - setConfigurationValue(BackendsProperties.VENDOR.name(), "Red Hat, Inc."); - setConfigurationValue(BackendsProperties.DESCRIPTION.name(), "Gathers memory statistics about a JVM"); - setConfigurationValue(BackendsProperties.VERSION.name(), version.getVersionNumber()); - } - - @Override - public String getConfigurationValue(String key) { - return null; - } - - @Override - public boolean attachToNewProcessByDefault() { - return true; } @Override diff -r e318ef62411d -r db2b89cdef62 vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/ChangeableText.java --- a/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/ChangeableText.java Fri Feb 22 16:52:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.overview.client.swing.internal; - -import java.util.HashSet; -import java.util.Set; - -public class ChangeableText { - - private final Set listeners = new HashSet(); - private String text; - - public static interface TextListener { - public void textChanged(ChangeableText text); - } - - public ChangeableText(String text) { - this.text = text; - } - - public synchronized void setText(String text) { - if (this.text.equals(text)) { - return; - } - this.text = text; - fireChanged(); - } - - public synchronized String getText() { - return text; - } - - public synchronized void addListener(TextListener listener) { - this.listeners.add(listener); - } - - public synchronized void removeListener(TextListener listener) { - this.listeners.remove(listener); - } - - private void fireChanged() { - for (TextListener listener: listeners) { - listener.textChanged(this); - } - } - -} - diff -r e318ef62411d -r db2b89cdef62 vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/SimpleTable.java --- a/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/SimpleTable.java Fri Feb 22 16:52:26 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,293 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * 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.vm.overview.client.swing.internal; - -import java.awt.Component; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.swing.Box; -import javax.swing.JComponent; -import javax.swing.JEditorPane; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.SwingUtilities; - -import com.redhat.thermostat.client.swing.ComponentVisibleListener; -import com.redhat.thermostat.client.swing.components.LabelField; -import com.redhat.thermostat.client.swing.components.SectionHeader; -import com.redhat.thermostat.client.swing.components.ValueField; - -public class SimpleTable implements ChangeableText.TextListener { - - Map> updateMap = new HashMap>(); - - public static class Section { - private final String sectionName; - private final List tableEntries = new ArrayList(); - - public Section(String name) { - this.sectionName = name; - } - - public String getText() { - return sectionName; - } - - public void add(TableEntry entry) { - tableEntries.add(entry); - } - - public void add(Key key, List values) { - tableEntries.add(new TableEntry(key, values)); - } - - public void add(Key key, Value value) { - tableEntries.add(new TableEntry(key, value)); - } - - public TableEntry[] getEntries() { - return tableEntries.toArray(new TableEntry[0]); - } - } - - public static class TableEntry { - private final Key key; - private final List values; - - public TableEntry(String key, ChangeableText value) { - this(new Key(key), new Value(value)); - } - - public TableEntry(Key key, Value value) { - this.key = key; - this.values = new ArrayList(); - this.values.add(value); - } - - public TableEntry(Key key, List values) { - this.key = key; - this.values = new ArrayList(values); - } - - public Key getKey() { - return key; - } - - public Value[] getValues() { - return values.toArray(new Value[0]); - } - } - - public static class Key { - private final String text; - - public Key(String text) { - this.text = text; - } - - public String getText() { - return text; - } - } - - public static class Value { - private final ChangeableText text; - private final Component actualComponent; - - public Value(String text) { - this(new ChangeableText(text)); - } - - public Value(ChangeableText text) { - this.text = text; - this.actualComponent = null; - } - - public Value(Component component) { - this.actualComponent = component; - this.text = null; - } - - public Component getComponent() { - return actualComponent; - } - - public ChangeableText getChangeableText() { - return text; - } - } - - public JPanel createTable(List

sections) { - final int SECTION_TOP_GAP = 10; - final int ROW_VERTICAL_GAP = 0; - final int ROW_HORIZONTAL_GAP = 10; - - Insets sectionHeaderInsets = new Insets(SECTION_TOP_GAP, 0, 0, 0); - Insets rowInsets = new Insets(ROW_VERTICAL_GAP, ROW_HORIZONTAL_GAP, ROW_VERTICAL_GAP, ROW_HORIZONTAL_GAP); - - JPanel container = new JPanel(); - container.setLayout(new GridBagLayout()); - - GridBagConstraints keyConstraints = new GridBagConstraints(); - GridBagConstraints valueConstraints = new GridBagConstraints(); - GridBagConstraints sectionHeaderConstraints = new GridBagConstraints(); - - keyConstraints.insets = valueConstraints.insets = rowInsets; - keyConstraints.gridy = valueConstraints.gridy = 0; - keyConstraints.gridx = 0; - keyConstraints.anchor = GridBagConstraints.FIRST_LINE_END; - valueConstraints.gridx = 1; - keyConstraints.fill = valueConstraints.fill = GridBagConstraints.HORIZONTAL; - - sectionHeaderConstraints.gridx = 0; - sectionHeaderConstraints.gridwidth = GridBagConstraints.REMAINDER; - sectionHeaderConstraints.fill = GridBagConstraints.HORIZONTAL; - sectionHeaderConstraints.insets = sectionHeaderInsets; - - for (Section section : sections) { - sectionHeaderConstraints.gridy = keyConstraints.gridy = ++valueConstraints.gridy; - container.add(new SectionHeader(section.getText()), sectionHeaderConstraints); - for (TableEntry tableEntry : section.getEntries()) { - keyConstraints.gridy = ++valueConstraints.gridy; - container.add(new LabelField(tableEntry.getKey().getText()), keyConstraints); - - for (Value value : tableEntry.getValues()) { - if (value.getComponent() == null) { - ChangeableText text = value.getChangeableText(); - JComponent valueLabel = new ValueField(text.getText()); - if (updateMap.containsKey(text)) { - updateMap.get(text).add(valueLabel); - } else { - Set set = new HashSet(); - set.add(valueLabel); - updateMap.put(text, set); - } - container.add(valueLabel, valueConstraints); - } else { - container.add(value.getComponent(), valueConstraints); - } - keyConstraints.gridy = ++valueConstraints.gridy; - } - } - } - - GridBagConstraints glueConstraints = new GridBagConstraints(); - glueConstraints.gridy = keyConstraints.gridy + 1; - glueConstraints.gridx = 0; - glueConstraints.weightx = 1; - glueConstraints.weighty = 1; - glueConstraints.fill = GridBagConstraints.BOTH; - glueConstraints.gridheight = GridBagConstraints.REMAINDER; - glueConstraints.gridwidth = GridBagConstraints.REMAINDER; - Component filler = Box.createGlue(); - container.add(filler, glueConstraints); - - container.addHierarchyListener(new ComponentVisibleListener() { - @Override - public void componentShown(Component c) { - updateAllValues(); - addAllListeners(); - } - - @Override - public void componentHidden(Component c) { - removeAllListeners(); - } - }); - - return container; - } - - - private void updateAllValues() { - for (Entry> entry: updateMap.entrySet()) { - for (JComponent label: entry.getValue()) { - setText(label, entry.getKey().getText()); - } - } - } - - private static void setText(JComponent target, String text) { - if (target instanceof JLabel) { - ((JLabel)target).setText(text); - } else if (target instanceof JTextField) { - ((JTextField)target).setText(text); - } else if (target instanceof JTextArea) { - ((JTextArea)target).setText(text); - } else if (target instanceof JEditorPane) { - ((JEditorPane)target).setText(text); - } - } - - @Override - public void textChanged(final ChangeableText text) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - String newValue = text.getText(); - for (JComponent label: updateMap.get(text)) { - setText(label, newValue); - } - } - }); - } - - public void addAllListeners() { - for (ChangeableText text : updateMap.keySet()) { - text.addListener(this); - } - } - - public void removeAllListeners() { - for (ChangeableText text : updateMap.keySet()) { - text.removeListener(this); - } - } - -} - diff -r e318ef62411d -r db2b89cdef62 vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/VmOverviewPanel.java --- a/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/VmOverviewPanel.java Fri Feb 22 16:52:26 2013 +0100 +++ b/vm-overview/client-swing/src/main/java/com/redhat/thermostat/vm/overview/client/swing/internal/VmOverviewPanel.java Fri Feb 22 16:53:10 2013 +0100 @@ -37,40 +37,42 @@ package com.redhat.thermostat.vm.overview.client.swing.internal; import java.awt.Component; -import java.util.ArrayList; -import java.util.List; import javax.swing.BorderFactory; +import javax.swing.GroupLayout; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.GroupLayout.Alignment; +import javax.swing.LayoutStyle.ComponentPlacement; import com.redhat.thermostat.client.swing.ComponentVisibleListener; import com.redhat.thermostat.client.swing.SwingComponent; import com.redhat.thermostat.client.swing.components.HeaderPanel; +import com.redhat.thermostat.client.swing.components.LabelField; +import com.redhat.thermostat.client.swing.components.SectionHeader; +import com.redhat.thermostat.client.swing.components.ValueField; import com.redhat.thermostat.common.ActionListener; import com.redhat.thermostat.common.locale.Translate; import com.redhat.thermostat.vm.overview.client.core.VmOverviewView; import com.redhat.thermostat.vm.overview.client.locale.LocaleResources; -import com.redhat.thermostat.vm.overview.client.swing.internal.SimpleTable.Section; -import com.redhat.thermostat.vm.overview.client.swing.internal.SimpleTable.TableEntry; public class VmOverviewPanel extends VmOverviewView implements SwingComponent { private static final Translate translator = LocaleResources.createLocalizer(); - + private HeaderPanel visiblePanel; private JScrollPane container; - private final ChangeableText pid = new ChangeableText(""); - private final ChangeableText startTimeStamp = new ChangeableText(""); - private final ChangeableText stopTimeStamp = new ChangeableText(""); - private final ChangeableText mainClass = new ChangeableText(""); - private final ChangeableText javaCommandLine = new ChangeableText(""); - private final ChangeableText javaHome = new ChangeableText(""); - private final ChangeableText javaVersion = new ChangeableText(""); - private final ChangeableText vmNameAndVersion = new ChangeableText(""); - private final ChangeableText vmArguments = new ChangeableText(""); - + private final ValueField pid = new ValueField(""); + private final ValueField startTimeStamp = new ValueField(""); + private final ValueField stopTimeStamp = new ValueField(""); + private final ValueField mainClass = new ValueField(""); + private final ValueField javaCommandLine = new ValueField(""); + private final ValueField javaHome = new ValueField(""); + private final ValueField javaVersion = new ValueField(""); + private final ValueField vmNameAndVersion = new ValueField(""); + private final ValueField vmArguments = new ValueField(""); public VmOverviewPanel() { super(); @@ -99,49 +101,94 @@ } @Override - public void setVmPid(String pid) { - this.pid.setText(pid); + public void setVmPid(final String newPid) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + pid.setText(newPid); + } + }); } @Override - public void setVmStartTimeStamp(String timeStamp) { - this.startTimeStamp.setText(timeStamp); + public void setVmStartTimeStamp(final String newTimeStamp) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + startTimeStamp.setText(newTimeStamp); + } + }); } @Override - public void setVmStopTimeStamp(String timeStamp) { - this.stopTimeStamp.setText(timeStamp); + public void setVmStopTimeStamp(final String newTimeStamp) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + stopTimeStamp.setText(newTimeStamp); + } + }); } @Override - public void setMainClass(String mainClass) { - this.mainClass.setText(mainClass); + public void setMainClass(final String newMainClass) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainClass.setText(newMainClass); + } + }); } @Override - public void setJavaCommandLine(String javaCommandLine) { - this.javaCommandLine.setText(javaCommandLine); + public void setJavaCommandLine(final String newJavaCommandLine) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + javaCommandLine.setText(newJavaCommandLine); + } + }); } @Override - public void setJavaHome(String javaHome) { - this.javaHome.setText(javaHome); + public void setJavaHome(final String newJavaHome) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + javaHome.setText(newJavaHome); + } + }); } @Override - public void setJavaVersion(String javaVersion) { - this.javaVersion.setText(javaVersion); + public void setJavaVersion(final String newJavaVersion) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + javaVersion.setText(newJavaVersion); + } + }); } @Override - public void setVmNameAndVersion(String vmNameAndVersion) { - this.vmNameAndVersion.setText(vmNameAndVersion); + public void setVmNameAndVersion(final String newVmNameAndVersion) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + vmNameAndVersion.setText(newVmNameAndVersion); + } + }); } @Override - public void setVmArguments(String vmArguments) { - this.vmArguments.setText(vmArguments); + public void setVmArguments(final String newVmArguments) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + vmArguments.setText(newVmArguments); + } + }); } @Override @@ -154,40 +201,92 @@ visiblePanel.setHeader(translator.localize(LocaleResources.VM_INFO_TITLE)); - TableEntry entry; - List
allSections = new ArrayList
(); + SectionHeader processSection = new SectionHeader(translator.localize(LocaleResources.VM_INFO_SECTION_PROCESS)); + LabelField pidLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_PROCESS_ID)); + LabelField startTimeLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_START_TIME)); + LabelField stopTimeLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_STOP_TIME)); - Section processSection = new Section(translator.localize(LocaleResources.VM_INFO_SECTION_PROCESS)); - allSections.add(processSection); + SectionHeader javaSection = new SectionHeader(translator.localize(LocaleResources.VM_INFO_SECTION_JAVA)); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_PROCESS_ID), pid); - processSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_START_TIME), startTimeStamp); - processSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_STOP_TIME), stopTimeStamp); - processSection.add(entry); + LabelField mainClassLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_MAIN_CLASS)); + LabelField javaCommandLineLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_COMMAND_LINE)); + LabelField javaVersionLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_JAVA_VERSION)); + LabelField vmNameAndVersionLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_VM)); + LabelField vmArgumentsLabel = new LabelField(translator.localize(LocaleResources.VM_INFO_VM_ARGUMENTS)); + + JPanel table = new JPanel(); + table.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 15)); + GroupLayout gl = new GroupLayout(table); + table.setLayout(gl); - Section javaSection = new Section(translator.localize(LocaleResources.VM_INFO_SECTION_JAVA)); - allSections.add(javaSection); + gl.setHorizontalGroup(gl.createParallelGroup() + .addComponent(processSection) + .addComponent(javaSection) + .addGroup(gl.createSequentialGroup() + .addContainerGap() + .addGroup(gl.createParallelGroup(Alignment.TRAILING) + .addComponent(pidLabel) + .addComponent(startTimeLabel) + .addComponent(stopTimeLabel) + .addComponent(mainClassLabel) + .addComponent(javaCommandLineLabel) + .addComponent(javaVersionLabel) + .addComponent(vmNameAndVersionLabel) + .addComponent(vmArgumentsLabel)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup() + .addComponent(pid) + .addComponent(startTimeStamp) + .addComponent(stopTimeStamp) + .addComponent(mainClass) + .addComponent(javaCommandLine) + .addComponent(javaVersion) + .addComponent(vmNameAndVersion) + .addComponent(vmArguments)) + .addContainerGap())); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_MAIN_CLASS), mainClass); - javaSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_COMMAND_LINE), javaCommandLine); - javaSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_JAVA_VERSION), javaVersion); - javaSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_VM), vmNameAndVersion); - javaSection.add(entry); - entry = new TableEntry(translator.localize(LocaleResources.VM_INFO_VM_ARGUMENTS), vmArguments); - javaSection.add(entry); - - SimpleTable simpleTable = new SimpleTable(); - JPanel table = simpleTable.createTable(allSections); - table.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + gl.setVerticalGroup(gl.createSequentialGroup() + .addContainerGap() + .addComponent(processSection) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(pidLabel) + .addComponent(pid)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(startTimeLabel) + .addComponent(startTimeStamp)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(stopTimeLabel) + .addComponent(stopTimeStamp)) + .addPreferredGap(ComponentPlacement.UNRELATED) + .addComponent(javaSection) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(mainClassLabel) + .addComponent(mainClass)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(javaCommandLineLabel) + .addComponent(javaCommandLine)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(javaVersionLabel) + .addComponent(javaVersion)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(vmNameAndVersionLabel) + .addComponent(vmNameAndVersion)) + .addPreferredGap(ComponentPlacement.RELATED) + .addGroup(gl.createParallelGroup(Alignment.LEADING, false) + .addComponent(vmArgumentsLabel) + .addComponent(vmArguments)) + .addGap(0, 0, Short.MAX_VALUE) + .addContainerGap()); container = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); visiblePanel.setContent(container); } } - diff -r e318ef62411d -r db2b89cdef62 web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java --- a/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Fri Feb 22 16:52:26 2013 +0100 +++ b/web/server/src/main/java/com/redhat/thermostat/web/server/WebStorageEndPoint.java Fri Feb 22 16:53:10 2013 +0100 @@ -36,6 +36,7 @@ package com.redhat.thermostat.web.server; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -113,15 +114,20 @@ @Override public void init(ServletConfig config) throws ServletException { super.init(config); + logger.log(Level.INFO, "Initializing web service"); if (!isThermostatHomeSet()) { // This is the webapp and our entry point into thermostat's web // service. The launcher did not run and hence THERMOSTAT_HOME is // not set and we need to do this ourselves. - // In this case THERMOSTAT_HOME is in the WEB-INF/thermostat folder - // in order to make it inaccessible via HTTP. This is not a "real" - // THERMOSTAT_HOME. For now it only contains an ssl.properties file. - String thermostatHome = config.getServletContext().getRealPath( - "/WEB-INF/thermostat"); + String thermostatHome = config.getInitParameter("THERMOSTAT_HOME"); + File thermostatHomeFile = new File(thermostatHome); + if (!thermostatHomeFile.canRead()) { + // This is bad news. If we can't at least read THERMOSTAT_HOME + // we are bound to fail in some weird ways at some later point. + throw new RuntimeException("THERMOSTAT_HOME = " + + thermostatHome + + " is not readable or does not exist!"); + } logger.log(Level.INFO, "Setting THERMOSTAT_HOME for webapp to " + thermostatHome); System.setProperty("THERMOSTAT_HOME", thermostatHome); diff -r e318ef62411d -r db2b89cdef62 web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java --- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Fri Feb 22 16:52:26 2013 +0100 +++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Fri Feb 22 16:53:10 2013 +0100 @@ -153,6 +153,10 @@ @Before public void setUp() throws Exception { + // Set thermostat home to something so we don't set + // it in WebStorageEndPoint.init() + System.setProperty("THERMOSTAT_HOME", "does not matter"); + mockStorage = mock(Storage.class); StorageWrapper.setStorage(mockStorage); diff -r e318ef62411d -r db2b89cdef62 web/war/pom.xml --- a/web/war/pom.xml Fri Feb 22 16:52:26 2013 +0100 +++ b/web/war/pom.xml Fri Feb 22 16:53:10 2013 +0100 @@ -88,28 +88,59 @@ true + + - maven-resources-plugin - 2.5 + org.apache.maven.plugins + maven-war-plugin + 2.3 - copy-logging-properties - prepare-package + exploded-war + package - copy-resources + exploded - src/main/webapp/WEB-INF/thermostat - + + - src/../../../distribution/config - etc true + src/main/webapp - ssl.properties + **/web.xml - + + src/main/webapp + src/main/webapp/WEB-INF/web.xml + ${thermostat.web.deploy.dir} + + + + war + package + + war + + + + + + true + src/main/webapp + + **/web.xml + + + + src/main/webapp + src/main/webapp/WEB-INF/web.xml diff -r e318ef62411d -r db2b89cdef62 web/war/src/main/webapp/WEB-INF/web.xml --- a/web/war/src/main/webapp/WEB-INF/web.xml Fri Feb 22 16:52:26 2013 +0100 +++ b/web/war/src/main/webapp/WEB-INF/web.xml Fri Feb 22 16:53:10 2013 +0100 @@ -12,6 +12,11 @@ com.redhat.thermostat.storage.mongodb.MongoStorageProvider + + THERMOSTAT_HOME + ${project.build.directory}/../../../distribution/target/ + + storage.endpoint mongodb://127.0.0.1:27518