changeset 989:cf7a445c359b

Refactoring and cleanup of Backend Reviewed-by: omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-February/005705.html
author Jon VanAlten <jon.vanalten@redhat.com>
date Fri, 15 Feb 2013 15:16:47 -0500
parents 06cc96778e94
children ff1970bc34a4
files agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendLoadException.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java agent/core/src/main/java/com/redhat/thermostat/backend/BackendsProperties.java agent/core/src/main/java/com/redhat/thermostat/backend/BaseBackend.java agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java agent/core/src/test/java/com/redhat/thermostat/backend/BaseBackendTest.java agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java
diffstat 20 files changed, 407 insertions(+), 580 deletions(-) [+]
line wrap: on
line diff
--- a/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/main/java/com/redhat/thermostat/agent/Agent.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
+                }
             }
         }
     };
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/Backend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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.
  * <p>
- * 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<String,String> 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<String, String> configMap) throws BackendLoadException {
-        if (initialConfigurationComplete) {
-            throw new BackendLoadException(id, "The backend " + id.toString() + "may only receive initial configuration once.");
-        }
-        for (Entry<String, String> 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.
-     * 
-     * <br /><br />
-     * 
-     * 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<String, String> getConfigurationMap() {
-        return new HashMap<String, String>();
-    }
-
-    /**
-     * 
-     * @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 + "]";
-    }
 }
-
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendID.java	Wed Feb 20 12:47:21 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
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend;
-
-/**
- * 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;
-    }
-}
-
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendLoadException.java	Wed Feb 20 12:47:21 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
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend;
-
-/**
- * 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;
-    }
-}
-
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BackendRegistry.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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<Backend> {
 
-    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 {
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/BackendsProperties.java	Wed Feb 20 12:47:21 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
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.backend;
-
-/**
- * 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;
-    
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/BaseBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend;
+
+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() + "]";
+    }
+}
+
--- a/agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/main/java/com/redhat/thermostat/backend/VmListenerBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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);
--- a/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/test/java/com/redhat/thermostat/agent/AgentTest.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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<Backend> backends = new ArrayList<Backend>();
-        backends.add(backend);
         
         backendRegistry = mock(BackendRegistry.class);
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/core/src/test/java/com/redhat/thermostat/backend/BaseBackendTest.java	Fri Feb 15 15:16:47 2013 -0500
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2012, 2013 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.backend;
+
+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() {
+        @SuppressWarnings("unused")
+        Backend backend = new TestBaseBackend(null, "", "", "");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructorRejectsNullVendor() {
+        @SuppressWarnings("unused")
+        Backend backend = new TestBaseBackend("", "", null, "");
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructorRejectsNullVersion() {
+        @SuppressWarnings("unused")
+        Backend backend = new TestBaseBackend("", "", "", null);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testConstructorRejectsNullDescription() {
+        @SuppressWarnings("unused")
+        Backend backend = 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;
+        }
+        
+    }
+}
--- a/agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/agent/core/src/test/java/com/redhat/thermostat/backend/VmListenerBackendTest.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
-        }
         
     }
 
--- a/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/host-cpu/agent/src/main/java/com/redhat/thermostat/host/cpu/agent/internal/HostCpuBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
     }
--- a/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/host-memory/agent/src/main/java/com/redhat/thermostat/host/memory/agent/internal/HostMemoryBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
     }
--- a/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/numa/agent/src/main/java/com/redhat/thermostat/numa/agent/internal/NumaBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
     }
--- a/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/numa/agent/src/test/java/com/redhat/thermostat/numa/agent/internal/NumaBackendTest.java	Fri Feb 15 15:16:47 2013 -0500
@@ -161,11 +161,6 @@
     }
 
     @Test
-    public void testAttachToNewProcessByDefault() {
-        assertFalse(backend.attachToNewProcessByDefault());
-    }
-
-    @Test
     public void testOrderValue() {
         assertEquals(Ordered.ORDER_MEMORY_GROUP + 80, backend.getOrderValue());
     }
--- a/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/system-backend/src/main/java/com/redhat/thermostat/backend/system/SystemBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
     }
--- a/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/vm-classstat/agent/src/main/java/com/redhat/thermostat/vm/classstat/agent/internal/VmClassStatBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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
--- a/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/vm-cpu/agent/src/main/java/com/redhat/thermostat/vm/cpu/agent/internal/VmCpuBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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;
     }
--- a/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/vm-gc/agent/src/main/java/com/redhat/thermostat/vm/gc/agent/internal/VmGcBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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
--- a/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Wed Feb 20 12:47:21 2013 +0100
+++ b/vm-memory/agent/src/main/java/com/redhat/thermostat/vm/memory/agent/internal/VmMemoryBackend.java	Fri Feb 15 15:16:47 2013 -0500
@@ -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