changeset 2:afd6bcb26436 default tip

Update plugin for 1.1.0-SNAPSHOT
author Omair Majid <omajid@redhat.com>
date Sun, 28 Sep 2014 23:50:17 -0400
parents 5ed86f4d1723
children
files agent/pom.xml agent/src/main/java/custom/agent/Activator.java agent/src/main/java/custom/agent/CustomBackend.java common/pom.xml common/src/main/java/custom/common/Activator.java common/src/main/java/custom/common/CustomDao.java common/src/main/java/custom/common/CustomData.java common/src/main/java/custom/common/CustomRegistrar.java common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration gui/pom.xml gui/src/main/java/custom/gui/Activator.java gui/src/main/java/custom/gui/Gui.java install.sh pom.xml thermostat-plugin.xml
diffstat 16 files changed, 869 insertions(+), 44 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/pom.xml	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>custom-plugin</groupId>
+    <artifactId>custom-plugin</artifactId>
+    <version>0.2</version>
+  </parent>
+
+  <artifactId>agent</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-agent-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>custom-plugin</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Bundle-Activator>custom.agent.Activator</Bundle-Activator>
+            <Bundle-SymbolicName>custom.agent</Bundle-SymbolicName>
+            <Private-Package>custom.agent</Private-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/custom/agent/Activator.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,63 @@
+package custom.agent;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.Backend;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.storage.core.WriterID;
+
+import custom.common.CustomDao;
+
+/** Called by OSGi */
+public class Activator implements BundleActivator {
+
+    MultipleServiceTracker tracker;
+
+    /** Called when the bundle is STARTED **/
+    @Override
+    public void start(final BundleContext context) {
+        System.out.println("#### Activator called");
+        Class<?>[] classes = new Class[] {
+                WriterID.class,
+                CustomDao.class,
+        };
+        tracker = new MultipleServiceTracker(context, classes, new Action() {
+            private ServiceRegistration<Backend> reg;
+
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                CustomDao dao = (CustomDao) services.get(CustomDao.class.getName());
+                WriterID writerId = (WriterID) services.get(WriterID.class.getName());
+                // TODO Auto-generated method stub
+                CustomBackend backend = new CustomBackend(new VmStatusListenerRegistrar(context), writerId, dao);
+
+                reg = context.registerService(Backend.class, backend, null);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                // TODO Auto-generated method stub
+                reg.unregister();
+                
+            }
+            
+        });
+        tracker.open();
+    }
+
+    /** Called when the bundle is STOPPED */
+    @Override
+    public void stop(BundleContext context) {
+        tracker.close();
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/src/main/java/custom/agent/CustomBackend.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,157 @@
+package custom.agent;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import com.redhat.thermostat.agent.VmStatusListener;
+import com.redhat.thermostat.agent.VmStatusListenerRegistrar;
+import com.redhat.thermostat.backend.BaseBackend;
+import com.redhat.thermostat.storage.core.VmId;
+import com.redhat.thermostat.storage.core.WriterID;
+
+import custom.common.CustomDao;
+import custom.common.CustomData;
+
+public class CustomBackend extends BaseBackend implements VmStatusListener {
+
+    private static final String CUSTOM_PROGRAM_NAME = "Program";
+
+    private final VmStatusListenerRegistrar registrar;
+
+    private ScheduledThreadPoolExecutor executor;
+
+    private boolean started = false;
+
+    private List<Integer> customPids = new LinkedList<>();
+    private Map<Integer, String> pidToVmId = new HashMap<>();
+
+    private CustomDao dao;
+
+    private WriterID writer;
+
+    public CustomBackend(VmStatusListenerRegistrar registrar, WriterID writer, CustomDao dao) {
+        super("custom", "custom", "none", "custom plugin", true);
+        this.registrar = registrar;
+        this.writer = writer;
+        this.dao = dao;
+        
+        System.out.println("### CUSTOM BACKEND CREATED");
+    }
+
+    @Override
+    public boolean activate() {
+        if (!started) {
+            executor = new ScheduledThreadPoolExecutor(1);
+            executor.scheduleWithFixedDelay(new UpdateData(), 0, 2, TimeUnit.SECONDS);
+            registrar.register(this);
+        }
+
+        return started;
+    }
+
+    @Override
+    public boolean deactivate() {
+        if (started) {
+            executor.shutdown();
+            registrar.unregister(this);
+        }
+
+        return !started;
+    }
+
+    @Override
+    public boolean isActive() {
+        return started;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_USER_GROUP + 1000;
+    }
+
+    @Override
+    public void vmStatusChanged(Status newStatus, String vmId, int pid) {
+        switch (newStatus) {
+        case VM_STARTED:
+            /* fall through */
+        case VM_ACTIVE:
+            if (isCustomProgram(pid)) {
+                System.out.println("added reading for pid " + pid);
+                startReadingCustomPerfData(pid);
+                pidToVmId.put(pid,  vmId);
+            }
+            break;
+        case VM_STOPPED:
+            if (isReadingCustomPerfData(pid)) {
+                stopReadingCustomPerfData(pid);
+                pidToVmId.remove(pid);
+            }
+            break;
+        }
+    }
+
+    private boolean isCustomProgram(int pid) {
+        return ("java " + CUSTOM_PROGRAM_NAME).equals(getProcessOutput("ps", "-p", "" + pid, "-o", "args="));
+    }
+
+    private String getProcessOutput(String... args) {
+        try {
+            Process process = new ProcessBuilder(args).start();
+            process.waitFor();
+            return new BufferedReader(new InputStreamReader(process.getInputStream())).readLine();
+        } catch (IOException | InterruptedException ioe) {
+            return null;
+        }
+    }
+
+    private void startReadingCustomPerfData(int pid) {
+        customPids.add(pid);
+    }
+
+    private void stopReadingCustomPerfData(int pid) {
+        customPids.remove((Integer) pid);
+    }
+
+    private boolean isReadingCustomPerfData(int pid) {
+        return customPids.contains(pid);
+    }
+
+    private class UpdateData implements Runnable {
+
+        @Override
+        public void run() {
+            for (Integer pid : customPids) {
+                System.out.println("### updating for pid : " + pid);
+                readAndPostData(pid);
+            }
+        }
+
+        private void readAndPostData(Integer pid) {
+            try {
+                File metricsFile = new File(System.getProperty("java.io.tmpdir") + File.separator + System.getProperty("user.name") + File.separator
+                        + "to-process");
+                try (BufferedReader r = new BufferedReader(new FileReader(metricsFile))) {
+                    Integer metrics = Integer.valueOf(r.readLine());
+                    CustomData customData = new CustomData();
+                    customData.setAgentId(writer.getWriterID());
+                    customData.setTimeStamp(System.currentTimeMillis());
+                    customData.setVmId(pidToVmId.get(pid));
+                    customData.setCustomMetric(metrics);
+                    dao.saveCustomData(customData);
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/pom.xml	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>custom-plugin</groupId>
+    <artifactId>custom-plugin</artifactId>
+    <version>0.2</version>
+  </parent>
+
+  <artifactId>common</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-common-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-storage-core</artifactId>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+	        <Bundle-Activator>custom.common.Activator</Bundle-Activator>
+	        <Bundle-SymbolicName>custom.common</Bundle-SymbolicName>
+	        <Export-Package>custom.common</Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/custom/common/Activator.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,35 @@
+package custom.common;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.storage.core.Storage;
+
+public class Activator implements BundleActivator {
+
+    private ServiceTracker tracker;
+    
+    @Override
+    public void start(BundleContext context) throws Exception {
+        System.out.println("### Activator called");
+        tracker = new ServiceTracker(context, Storage.class, null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Storage storage = (Storage) super.addingService(reference);
+                context.registerService(CustomDao.class, new CustomDao(storage), null);
+                return storage;
+            }
+        };
+        
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        tracker.close();
+    }
+    
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/custom/common/CustomDao.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,63 @@
+package custom.common;
+
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.VmRef;
+
+public class CustomDao {
+
+    private static final Logger logger = LoggingUtils.getLogger(CustomDao.class);
+
+    private static Key<Integer> KEY_CUSTOM_METRIC = new Key<>("customMetric");
+    static Category<CustomData> CUSTOM_CATEGORY = new Category<>("custom", CustomData.class,
+            Key.TIMESTAMP, Key.AGENT_ID, Key.VM_ID, KEY_CUSTOM_METRIC);
+
+    static final String ADD = "ADD " + CUSTOM_CATEGORY.getName()
+            + " SET '" + Key.AGENT_ID.getName() + "' = ?s , "
+            + "     '" + Key.VM_ID.getName() + "' = ?s , "
+            + "     '" + Key.TIMESTAMP.getName() + "' = ?l , "
+            + "     '" + KEY_CUSTOM_METRIC.getName() + "' = ?i";
+
+    private Storage storage;
+    private final VmLatestPojoListGetter<CustomData> getter;
+
+    public CustomDao(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(CUSTOM_CATEGORY);
+        getter = new VmLatestPojoListGetter<CustomData>(storage, CUSTOM_CATEGORY);
+    }
+
+    public void saveCustomData(CustomData data) {
+        StatementDescriptor<CustomData> desc = new StatementDescriptor<>(CUSTOM_CATEGORY, ADD);
+        PreparedStatement<CustomData> prepared;
+        try {
+            prepared = storage.prepareStatement(desc);
+            prepared.setString(0, data.getAgentId());
+            prepared.setString(1, data.getVmId());
+            prepared.setLong(2, data.getTimeStamp());
+            prepared.setInt(3, data.getCustomMetric());
+            prepared.execute();
+        } catch (DescriptorParsingException e) {
+            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Executing stmt '" + desc + "' failed!", e);
+        }
+    }
+
+    public List<CustomData> getLatestCustomData(VmRef ref, long lastTimeStamp) {
+        return getter.getLatest(ref, lastTimeStamp);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/custom/common/CustomData.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,51 @@
+package custom.common;
+
+import com.redhat.thermostat.storage.core.Entity;
+import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+
+@Entity
+public class CustomData extends BasePojo implements TimeStampedPojo {
+
+    private String vmId;
+    private long timeStamp;
+    private int customMetric;
+
+    public CustomData() {
+        super(null);
+    }
+
+    @Persist
+    @Override
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    @Persist
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
+    }
+
+    @Persist
+    public String getVmId() {
+        return vmId;
+    }
+
+    @Persist
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
+    }
+
+
+    @Persist
+    public int getCustomMetric() {
+        return customMetric;
+    }
+
+    @Persist
+    public void setCustomMetric(int customMetric) {
+        this.customMetric = customMetric;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/java/custom/common/CustomRegistrar.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,46 @@
+package custom.common;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.PreparedParameter;
+import com.redhat.thermostat.storage.core.VmLatestPojoListGetter;
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.storage.core.auth.DescriptorMetadata;
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+
+public class CustomRegistrar implements StatementDescriptorRegistration, CategoryRegistration {
+
+    static final String latestDescriptor = String.format(VmLatestPojoListGetter.VM_LATEST_QUERY_FORMAT,
+            CustomDao.CUSTOM_CATEGORY.getName());
+    
+    @Override
+    public DescriptorMetadata getDescriptorMetadata(String descriptor, PreparedParameter[] params) {
+        if (descriptor.equals(CustomDao.ADD)
+                || descriptor.equals(latestDescriptor)) {
+            String agentId = (String)params[0].getValue();
+            String vmId = (String)params[1].getValue();
+            DescriptorMetadata metadata = new DescriptorMetadata(agentId, vmId);
+            return metadata;
+        } else {
+            throw new IllegalArgumentException("Unknown descriptor ->" + descriptor + "<-");
+        }
+    }
+
+    @Override
+    public Set<String> getStatementDescriptors() {
+        System.out.println("### Returning statement descriptors");
+        Set<String> descs = new HashSet<>(1);
+        descs.add(CustomDao.ADD);
+        descs.add(latestDescriptor);
+        return descs;
+    }
+
+    @Override
+    public Set<String> getCategoryNames() {
+        System.out.println("### Registered category");
+        Set<String> categories = new HashSet<>(1);
+        categories.add(CustomDao.CUSTOM_CATEGORY.getName());
+        return categories;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,1 @@
+custom.common.CustomRegistrar
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,1 @@
+custom.common.CustomRegistrar
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gui/pom.xml	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>custom-plugin</groupId>
+    <artifactId>custom-plugin</artifactId>
+    <version>0.2</version>
+  </parent>
+
+  <artifactId>gui</artifactId>
+  <packaging>bundle</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+    
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.redhat.thermostat</groupId>
+      <artifactId>thermostat-client-swing</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>custom-plugin</groupId>
+      <artifactId>common</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+	        <Bundle-Activator>custom.gui.Activator</Bundle-Activator>
+	        <Bundle-SymbolicName>custom.gui</Bundle-SymbolicName>
+	        <Private-Package>custom.gui</Private-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gui/src/main/java/custom/gui/Activator.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,55 @@
+package custom.gui;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.common.Constants;
+import com.redhat.thermostat.storage.core.VmRef;
+
+import custom.common.CustomDao;
+
+public class Activator implements BundleActivator {
+
+    ServiceTracker tracker;
+
+    @Override
+    public void start(BundleContext context) throws Exception {
+        System.out.println("### GUI Activator called");
+        
+        tracker = new ServiceTracker(context, CustomDao.class, null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                CustomDao dao = (CustomDao) super.addingService(reference);
+
+                Gui gui = new Gui(dao);
+                Dictionary<String, String> properties = new Hashtable<>();
+                properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
+                properties.put(InformationService.KEY_SERVICE_ID, Gui.SERVICE_ID);
+                ServiceRegistration<?> reg = context.registerService(InformationService.class.getName(), gui, properties);
+                
+                return dao;
+            }
+
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                super.removedService(reference, service);
+            }
+
+        };
+        tracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        // TODO Auto-generated method stub
+        tracker.close();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gui/src/main/java/custom/gui/Gui.java	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,138 @@
+package custom.gui;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.JPanel;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.data.time.FixedMillisecond;
+import org.jfree.data.time.RegularTimePeriod;
+import org.jfree.data.time.TimeSeries;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import com.redhat.thermostat.client.core.InformationService;
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.RecentTimeSeriesChartPanel;
+import com.redhat.thermostat.client.ui.RecentTimeSeriesChartController;
+import com.redhat.thermostat.common.Filter;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.storage.core.VmRef;
+
+import custom.common.CustomDao;
+import custom.common.CustomData;
+
+public class Gui implements InformationService<VmRef> {
+
+    final static String SERVICE_ID = "custom";
+
+    private CustomDao dao;
+
+    public Gui(CustomDao dao) {
+        this.dao = dao;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER_USER_GROUP + 1000;
+    }
+
+    @Override
+    public Filter<VmRef> getFilter() {
+        return new Filter<VmRef>() {
+            @Override
+            public boolean matches(VmRef toMatch) {
+                return true;
+            }
+        };
+    }
+
+    @Override
+    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
+        return new Controller(ref);
+    }
+
+    private class Controller implements InformationServiceController<VmRef>, SwingComponent {
+
+        private JPanel actualUiComponent;
+
+        private TimeSeries dataset;
+
+        private VmRef vm;
+
+        private long lastTimeStamp = Long.MIN_VALUE;
+
+        public Controller(VmRef ref) {
+            this.vm = ref;
+            actualUiComponent = new JPanel();
+
+            TimeSeriesCollection datasetCollection = new TimeSeriesCollection();
+            dataset = new TimeSeries("custom");
+            datasetCollection.addSeries(dataset);
+
+            JFreeChart chart = ChartFactory.createTimeSeriesChart(
+                    null,
+                    "Time",
+                    "Custom Metric",
+                    datasetCollection,
+                    false, false, false);
+
+            chart.getPlot().setBackgroundPaint(new Color(255, 255, 255, 0));
+            chart.getPlot().setBackgroundImageAlpha(0.0f);
+            chart.getPlot().setOutlinePaint(new Color(0, 0, 0, 0));
+
+            JPanel chartPanel = new RecentTimeSeriesChartPanel(new RecentTimeSeriesChartController(chart));
+            chartPanel.setOpaque(false);
+
+            actualUiComponent.add(chartPanel);
+
+            ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
+            executor.scheduleWithFixedDelay(new UpdateTask(), 0, 1, TimeUnit.SECONDS);
+        }
+
+        private class UpdateTask implements Runnable {
+            @Override
+            public void run() {
+                if (vm == null) {
+                    return;
+                }
+                System.out.println("timer running");
+
+                int i = 0;
+                try {
+                    for (CustomData data : dao.getLatestCustomData(vm, lastTimeStamp)) {
+                        i++;
+                        RegularTimePeriod period = new FixedMillisecond(data.getTimeStamp());
+                        dataset.add(period, data.getCustomMetric(), true);
+
+                        lastTimeStamp = Math.max(lastTimeStamp, data.getTimeStamp());
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    throw e;
+                }
+                System.out.println("timer done: inserted " + i + " data points");
+            }
+        }
+
+        @Override
+        public Component getUiComponent() {
+            return actualUiComponent;
+        }
+
+        @Override
+        public UIComponent getView() {
+            return this;
+        }
+
+        @Override
+        public LocalizedString getLocalizedName() {
+            return new LocalizedString("Custom");
+        }
+    }
+}
--- a/install.sh	Fri Feb 15 17:11:14 2013 -0500
+++ b/install.sh	Sun Sep 28 23:50:17 2014 -0400
@@ -1,24 +1,18 @@
-#!/bin/bash
+#!/bin/bash -ex
 
 #
 # Installs this project as a plugin
 #
 
-PLUGIN_NAME="hello-world-plugin"
-PLUGIN_VERSION="0.1-SNAPSHOT"
-
-#
-# $1: path of thermostat installation
-#
+PLUGIN_NAME="custom-plugin"
+PLUGIN_VERSION="0.2"
 
-if [ "$#" -lt 1 ] ; then
-    echo "Need thermostat home as first argument"
-    exit 1
-fi
-
-THERMOSTAT_HOME="$1"
+THERMOSTAT_HOME="../thermostat/distribution/target/image"
 PLUGIN_HOME="${THERMOSTAT_HOME}"/plugins/"${PLUGIN_NAME}"
 
 mkdir -p "${PLUGIN_HOME}"
-cp target/"${PLUGIN_NAME}"-"${PLUGIN_VERSION}".jar "${PLUGIN_HOME}/"
-cp plugin.xml "${PLUGIN_HOME}"/
+cp agent/target/agent-"${PLUGIN_VERSION}".jar "${PLUGIN_HOME}/"
+cp common/target/common-"${PLUGIN_VERSION}".jar "${PLUGIN_HOME}/"
+cp common/target/common-"${PLUGIN_VERSION}".jar "${THERMOSTAT_HOME}/webapp/WEB-INF/lib/"
+cp gui/target/gui-"${PLUGIN_VERSION}".jar "${PLUGIN_HOME}/"
+cp thermostat-plugin.xml "${PLUGIN_HOME}"/
--- a/pom.xml	Fri Feb 15 17:11:14 2013 -0500
+++ b/pom.xml	Sun Sep 28 23:50:17 2014 -0400
@@ -5,37 +5,78 @@
                       http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
-  <!-- The Basics -->
-  <groupId>org.classpath.icedtea.omajid</groupId>
-  <artifactId>hello-world-plugin</artifactId>
-  <version>0.1-SNAPSHOT</version>
-  <packaging>jar</packaging>
+  <groupId>custom-plugin</groupId>
+  <artifactId>custom-plugin</artifactId>
+  <version>0.2</version>
+  <packaging>pom</packaging>
 
-  <dependencies>
-    <dependency>
-      <groupId>org.osgi</groupId>
-      <artifactId>org.osgi.core</artifactId>
-      <version>4.2.0</version>
-    </dependency>
-    
-    <dependency>
-      <groupId>com.redhat.thermostat</groupId>
-      <artifactId>thermostat-client-core</artifactId>
-      <version>0.6.0-SNAPSHOT</version>
-    </dependency>
-  </dependencies>
+  <modules>
+    <module>agent</module>
+    <module>common</module>
+    <module>gui</module>
+  </modules>
 
   <build>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <configuration>
-          <archive>
-	        <manifestFile>src/main/resources/META-INF/Manifest.mf</manifestFile>
-          </archive>
-        </configuration>
-      </plugin>
-    </plugins>
+    <pluginManagement>
+      <plugins>
+        <plugin>
+          <groupId>org.apache.felix</groupId>
+          <artifactId>maven-bundle-plugin</artifactId>
+          <version>1.4.0</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-jar-plugin</artifactId>
+          <version>2.4</version>
+        </plugin>
+        <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>2.4</version>
+          <configuration>
+            <source>7</source>
+            <target>7</target>
+          </configuration>
+        </plugin>
+      </plugins>
+    </pluginManagement>
   </build>
+
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>org.osgi</groupId>
+        <artifactId>org.osgi.core</artifactId>
+        <version>4.3.1</version>
+      </dependency>
+      
+      <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-agent-core</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-common-core</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-client-core</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-client-swing</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+      </dependency>
+      <dependency>
+        <groupId>com.redhat.thermostat</groupId>
+        <artifactId>thermostat-storage-core</artifactId>
+        <version>1.1.0-SNAPSHOT</version>
+      </dependency>
+    </dependencies>
+
+  </dependencyManagement>
+
 </project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thermostat-plugin.xml	Sun Sep 28 23:50:17 2014 -0400
@@ -0,0 +1,27 @@
+<?xml version="1.0" ?>
+<plugin xmlns="http://icedtea.classpath.org/thermostat/plugins/v1.0"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://icedtea.classpath.org/thermostat/plugins/v1.0 thermsotat-plugin.xsd">
+  <extensions>
+    <extension>
+      <name>agent</name>
+      <bundles>
+        <bundle><symbolic-name>custom.agent</symbolic-name><version>0.2</version></bundle>
+        <bundle><symbolic-name>custom.common</symbolic-name><version>0.2</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.agent.core</symbolic-name><version>1.1.0-SNAPSHOT</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.common.core</symbolic-name><version>1.1.0-SNAPSHOT</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.storage.core</symbolic-name><version>1.1.0-SNAPSHOT</version></bundle>
+      </bundles>
+    </extension>
+    <extension>
+      <name>gui</name>
+      <bundles>
+        <bundle><symbolic-name>custom.common</symbolic-name><version>0.2</version></bundle>
+        <bundle><symbolic-name>custom.gui</symbolic-name><version>0.2</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.storage.core</symbolic-name><version>1.1.0-SNAPSHOT.jar</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.client.core</symbolic-name><version>1.1.0-SNAPSHOT.jar</version></bundle>
+        <bundle><symbolic-name>com.redhat.thermostat.client.swing</symbolic-name><version>1.1.0-SNAPSHOT.jar</version></bundle>
+      </bundles>
+    </extension>
+  </extensions>
+</plugin>