changeset 1623:b1306c7c7422

Thread monitor improvements part I review-thread: http://icedtea.classpath.org/pipermail/thermostat/2014-December/012136.html reviewed-by: omajid, jkang
author Mario Torre <neugens.limasoftware@gmail.com>
date Thu, 11 Dec 2014 11:44:42 +0100
parents 504adea43d70
children affa18455541
files client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatScrollBar.java storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/Query.java storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/WhereCriterion.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/ThreadInfo.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/Timeline.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineDimensionModel.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineFactory.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineGroupDataModel.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineInfo.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineProbe.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadTimelineView.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationServiceImpl.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java thread/client-swing/pom.xml thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/ContentPane.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/DataPane.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/Separator.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/utils/EDTHelper.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/HeaderController.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentHeader.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentLayoutManager.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RulerComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/SwingTimelineDimensionModel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/ThreadTimelineHeader.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineAdjustmentListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineBaseComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineBorder.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineCellRenderer.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineContainer.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineGroupThreadConverter.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineLabel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineRangeModelFormatter.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineViewComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineViewport.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeEvent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangedTimelineProbe.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeEvent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineDateFormatter.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineModel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/SwingTimelineScrollBarController.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineButtonUI.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineScrollBar.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineScrollBarUI.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/osgi/Activator.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo2.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo3.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineRangeModelFormatterTest.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoCategories.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoKeys.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueries.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQuery.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/StateQueries.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQuery.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadContentionSample.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadHeader.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadPojo.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueriesTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueryTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQueryTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadHeaderTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadModelPojosTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadPojoTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadStateTest.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/HarvesterHelper.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadContentionHelper.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHeaderHelper.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadStateHelper.java thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadSummaryHelper.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterHelperTest.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHeaderHelperTest.java thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadStateHelperTest.java
diffstat 95 files changed, 3774 insertions(+), 4417 deletions(-) [+]
line wrap: on
line diff
--- a/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatScrollBar.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/components/ThermostatScrollBar.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,13 +36,11 @@
 
 package com.redhat.thermostat.client.swing.components;
 
-import com.redhat.thermostat.client.swing.components.ThermostatThinScrollBar;
-
 /**
  * This widget implements standard sized scrollbars.
  */
 @SuppressWarnings("serial")
-class ThermostatScrollBar extends ThermostatThinScrollBar {
+public class ThermostatScrollBar extends ThermostatThinScrollBar {
     public ThermostatScrollBar() {
         this(ThermostatScrollBar.VERTICAL);
     }
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/Query.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/Query.java	Thu Dec 11 11:44:42 2014 +0100
@@ -63,7 +63,9 @@
                 throw new IllegalArgumentException("Already contains criteria" +
                                                    " with this id." +
                                                    " New: " + criterion +
-                                                   " Old: " + map.get(id));
+                                                   " (id = " + id + ")" +
+                                                   " Old: " + map.get(id) +
+                                                   " query id: " + getId());
             }
             map.put(id, criterion);
             criteria.add(criterion);
--- a/storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/WhereCriterion.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/storage/core/src/main/java/com/redhat/thermostat/storage/core/experimental/statement/WhereCriterion.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,6 +36,8 @@
 
 package com.redhat.thermostat.storage.core.experimental.statement;
 
+import java.util.Objects;
+
 /**
  *
  */
@@ -50,6 +52,10 @@
                           FieldDescriptor descriptor,
                           TypeMapper.Criteria criteria)
     {
+        Objects.requireNonNull(id, "id must be not null");
+        Objects.requireNonNull(criteria, "criteria must be not null");
+        Objects.requireNonNull(descriptor, "descriptor must be not null");
+
         this.descriptor = descriptor;
         this.criteria = criteria;
         this.id = id;
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/ThreadCollector.java	Thu Dec 11 11:44:42 2014 +0100
@@ -37,11 +37,10 @@
 package com.redhat.thermostat.thread.client.common.collector;
 
 import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.thread.dao.ThreadDao;
 import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadContentionSample;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -58,6 +57,11 @@
     boolean isHarvesterCollecting();
 
     /**
+     * Returns the range of all known threads probes.
+     */
+    Range<Long> getThreadRange(SessionID session);
+
+    /**
      * Returns a list of sessions recorded during sampling.
      */
     List<ThreadSession> getThreadSessions(Range<Long> range);
@@ -71,25 +75,6 @@
     List<ThreadSummary> getThreadSummary(SessionID session, Range<Long> range);
 
     /**
-     * Return the range of all {@link ThreadState} data (timestamp of first and
-     * last ThreadState entry in storage) for this virtual machine.
-     */
-    Range<Long> getThreadStateTotalTimeRange();
-
-    /**
-     * Return the range of {@link ThreadState} for the given
-     * {@link ThreadHeader} (timestamp of first and
-     * last ThreadState entry in storage).
-     */
-    Range<Long> getThreadStateRange(ThreadHeader thread);
-
-    /**
-     * Returns a list with all the {@link ThreadState} information for the
-     * given {@link ThreadHeader}, in the given range, in ascending order.
-     */
-    List<ThreadState> getThreadStates(ThreadHeader thread, Range<Long> range);
-
-    /**
      * Check for deadlocks. {@link #getLatestDeadLockData} needs to be called to
      * obtain the data.
      */
@@ -97,16 +82,9 @@
 
     /** Return the latest deadlock data */
     VmDeadLockData getLatestDeadLockData();
-    
-    /**
-     * Returns a list with all {@link ThreadHeader}s listed in the storage.
-     */
-    List<ThreadHeader> getThreads();
 
-    /**
-     * Returns the latest {@link ThreadContentionSample} available for this
-     * {@link ThreadHeader}.
-     */
-    ThreadContentionSample getLatestContentionSample(ThreadHeader thread);
+    void getThreadStates(SessionID session,
+                         ResultHandler<ThreadState> handler,
+                         Range<Long> range);
 }
 
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java	Thu Dec 11 11:44:42 2014 +0100
@@ -45,29 +45,31 @@
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.core.HostRef;
 import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
 import com.redhat.thermostat.thread.collector.HarvesterCommand;
 import com.redhat.thermostat.thread.dao.ThreadDao;
 import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadContentionSample;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import com.redhat.thermostat.thread.model.VmDeadLockData;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-
 import java.net.InetSocketAddress;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.logging.Level;
 import java.util.logging.Logger;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 
 public class ThreadMXBeanCollector implements ThreadCollector {
-    
+
+    private static final Range<Long> FULL_RANGE = new Range<>(0l, Long.MAX_VALUE);
+    private static final int ALL = -1;
+    private static final int FIRST = 1;
+
     private static final String CMD_CHANNEL_ACTION_NAME = "thread-harvester";
     private static final Logger logger = LoggingUtils.getLogger(ThreadMXBeanCollector.class);
 
@@ -99,7 +101,7 @@
         
         return harvester;
     }
-    
+
     @Override
     public boolean startHarvester() {
         
@@ -136,13 +138,14 @@
 
     @Override
     public List<ThreadSession> getThreadSessions(Range<Long> range) {
-        return threadDao.getSessions(ref, range, Integer.MAX_VALUE);
+        return threadDao.getSessions(ref, range, ALL, ThreadDao.Sort.ASCENDING);
     }
 
     @Override
     public SessionID getLastThreadSession() {
         List<ThreadSession> sessions =
-                threadDao.getSessions(ref, new Range<>(0l, Long.MAX_VALUE), 1);
+                threadDao.getSessions(ref, FULL_RANGE, FIRST,
+                                      ThreadDao.Sort.DESCENDING);
         return sessions.isEmpty() ? null : new SessionID(sessions.get(0).getSession());
     }
 
@@ -162,18 +165,41 @@
     }
 
     @Override
-    public Range<Long> getThreadStateRange(ThreadHeader thread) {
+    public Range<Long> getThreadRange(SessionID session) {
+
+        final long[] timestamps = new long[2];
+        timestamps[0] = 0l;
+        timestamps[1] = Long.MAX_VALUE;
 
-        Range<Long> result = null;
+        threadDao.getThreadStates(ref, session,
+                                  new ResultHandler<ThreadState>() {
+                                      @Override
+                                      public void onResult(ThreadState result) {
+                                          timestamps[1] = result.getTimeStamp();
+                                      }
+                                  },
+                                  FULL_RANGE, FIRST, ThreadDao.Sort.DESCENDING);
 
-        ThreadState last = threadDao.getLastThreadState(thread);
-        ThreadState first = threadDao.getFirstThreadState(thread);
+        threadDao.getThreadStates(ref, session,
+                                  new ResultHandler<ThreadState>() {
+                                      @Override
+                                      public void onResult(ThreadState result) {
+                                          timestamps[0] = result.getTimeStamp();
+                                      }
+                                  },
+                                  FULL_RANGE, FIRST, ThreadDao.Sort.ASCENDING);
 
-        if (last != null && first != null) {
-            result = new Range<>(first.getProbeStartTime(), last.getProbeEndTime());
-        }
+        return new Range<>(timestamps[0], timestamps[1]);
+    }
 
-        return result;
+    @Override
+    public void getThreadStates(SessionID session,
+                                ResultHandler<ThreadState> handler,
+                                Range<Long> range)
+    {
+        threadDao.getThreadStates(ref, session, handler, range,
+                                  ALL,
+                                  ThreadDao.Sort.ASCENDING);
     }
 
     @Override
@@ -183,15 +209,6 @@
     }
 
     @Override
-    public Range<Long> getThreadStateTotalTimeRange() {
-        return threadDao.getThreadStateTotalTimeRange(ref);
-    }
-
-    public List<ThreadState> getThreadStates(ThreadHeader thread, Range<Long> range) {
-        return threadDao.getThreadStates(thread, range);
-    }
-
-    @Override
     public VmDeadLockData getLatestDeadLockData() {
         return threadDao.loadLatestDeadLockStatus(ref);
     }
@@ -207,10 +224,10 @@
         postAndWait(harvester);
     }
 
-    @Override
-    public ThreadContentionSample getLatestContentionSample(ThreadHeader thread) {
-        return threadDao.getLatestContentionSample(thread);
-    }
+//    @Override
+//    public ThreadContentionSample getLatestContentionSample(ThreadHeader thread) {
+//        return threadDao.getLatestContentionSample(thread);
+//    }
 
     private boolean postAndWait(Request harvester) {
         final CountDownLatch latch = new CountDownLatch(1);
@@ -239,11 +256,6 @@
         return result[0];
     }
 
-    @Override
-    public List<ThreadHeader> getThreads() {
-        return threadDao.getThreads(ref);
-    }
-
     private void enqueueRequest(Request req) throws CommandChannelException {
         ServiceReference ref = context.getServiceReference(RequestQueue.class.getName());
         if (ref == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/ThreadInfo.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.common.model.timeline;
+
+import java.util.Objects;
+
+public class ThreadInfo {
+
+    private long id;
+    private String name;
+
+    public ThreadInfo() {
+        name = "";
+        id = -1;
+    }
+
+    public ThreadInfo(ThreadInfo source) {
+        name = source.getName();
+        id = source.getId();
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public void setName(String name) {
+        Objects.requireNonNull(name);
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        ThreadInfo that = (ThreadInfo) o;
+
+        if (id != that.id) return false;
+        if (!name.equals(that.name)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = (int) (id ^ (id >>> 32));
+        result = 31 * result + name.hashCode();
+        return result;
+    }
+}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/Timeline.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,85 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.common.model.timeline;
-
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.Iterator;
-
-public class Timeline implements Iterable<TimelineInfo> {
-
-    private Deque<TimelineInfo> infos;
-
-    private String name;
-    private long id;
-
-    public Timeline(String name, long id) {
-        this.name = name;
-        this.id = id;
-        infos = new ArrayDeque<>();
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public long getId() {
-        return id;
-    }
-
-    public TimelineInfo last() {
-        return infos.peekLast();
-    }
-
-    @Override
-    public Iterator<TimelineInfo> iterator() {
-        return infos.iterator();
-    }
-
-    public TimelineInfo[] toArray() {
-        return (TimelineInfo[]) infos.toArray();
-    }
-
-    public void add(TimelineInfo info) {
-        infos.add(info);
-    }
-
-    public int size() {
-        return infos.size();
-    }
-}
-
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineDimensionModel.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.common.model.timeline;
-
-import java.beans.PropertyChangeListener;
-
-import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-
-/**
- * Model to define the size of the timeline with respect to the physical
- * dimension of the {@link ThreadTimelineView}.
- */
-public interface TimelineDimensionModel {
-    
-    /**
-     * This property defines the total length in milliseconds of the
-     * {@link ThreadTimelineView}.
-     */
-    public static final String LENGTH_PROPERTY = "LENGTH_PROPERTY";
-    
-    long getLengthInMillis();
-    
-    public void addPropertyChangeListener(String property, PropertyChangeListener listener);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineFactory.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.common.model.timeline;
+
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.thread.model.ThreadState;
+
+/**
+ *
+ */
+public class TimelineFactory {
+
+    public static TimelineProbe createTimelineProbe(ThreadState state)
+    {
+        Palette color = null;
+
+        Thread.State threadState = Thread.State.valueOf(state.getState());
+        switch (threadState) {
+        case NEW:
+            color = Palette.GREEN;
+            break;
+
+        case RUNNABLE:
+            color = Palette.ADWAITA_BLU;
+            break;
+
+        case BLOCKED:
+            color = Palette.VIOLET;
+            break;
+
+        case WAITING:
+            color = Palette.GRANITA_ORANGE;
+            break;
+
+        case TIMED_WAITING:
+            color = Palette.ADWAITA_BLU;
+            break;
+
+        case TERMINATED:
+            color = Palette.LIGHT_GRAY;
+            break;
+        }
+        return new TimelineProbe(color, state.getState(), state.getTimeStamp());
+    }
+}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineGroupDataModel.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.common.model.timeline;
-
-import com.redhat.thermostat.common.model.Range;
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-
-/**
- *
- */
-public class TimelineGroupDataModel {
-
-    public enum RangeChangeProperty {
-        TOTAL_RANGE,
-        PAGE_RANGE
-    }
-
-    private Range<Long> totalRange;
-    private Range<Long> pageRange;
-
-    private PropertyChangeSupport propertyChangeSupport;
-
-    public TimelineGroupDataModel() {
-        this(new Range<>(0L, Long.MAX_VALUE), new Range<Long>(0L, Long.MAX_VALUE));
-    }
-
-    public TimelineGroupDataModel(Range<Long> totalRange, Range<Long> pageRange) {
-        this.totalRange = totalRange;
-        this.pageRange = pageRange;
-        propertyChangeSupport = new PropertyChangeSupport(this);
-    }
-
-    public Range<Long> getTotalRange() {
-        return totalRange;
-    }
-
-    public void setTotalRange(Range<Long> totalRange) {
-        Range<Long> old = this.totalRange;
-        this.totalRange = totalRange;
-        fireRangeChangeEvent(RangeChangeProperty.TOTAL_RANGE, old, this.totalRange);
-    }
-
-    public Range<Long> getPageRange() {
-        return pageRange;
-    }
-
-    public void setPageRange(Range<Long> pageRange) {
-        Range<Long> old = this.pageRange;
-        this.pageRange = pageRange;
-        fireRangeChangeEvent(RangeChangeProperty.PAGE_RANGE, old, this.pageRange);
-    }
-
-    public void addPropertyChangeListener(RangeChangeProperty property, PropertyChangeListener listener) {
-        propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
-    }
-
-    private void fireRangeChangeEvent(RangeChangeProperty property, Range<Long> oldRange, Range<Long> newRange) {
-        propertyChangeSupport.firePropertyChange(property.name(), oldRange, newRange);
-    }
-}
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineInfo.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.common.model.timeline;
-
-import com.redhat.thermostat.client.ui.Palette;
-import com.redhat.thermostat.common.model.Range;
-
-public class TimelineInfo {
-
-    private Palette colour;
-    private Range<Long> timeStamp;
-    
-    public TimelineInfo() {
-        timeStamp = new Range<>(0l, Long.MAX_VALUE);
-        colour = Palette.BLACK; 
-    }
-    
-    public TimelineInfo(Palette colour, Range<Long> timeStamp) {
-        this.timeStamp = timeStamp;
-        this.colour = colour;
-    }
-    
-    public Palette getColor() {
-        return colour;
-    }
-    
-    public void setColor(Palette colour) {
-        this.colour = colour;
-    }
-    
-    public void setRange(Range<Long> timestamp) {
-        this.timeStamp = timestamp;
-    }
-
-    public Range<Long> getRange() {
-        return timeStamp;
-    }
-
-    @Override
-    public String toString() {
-        return "TimelineInfo [colour=" + colour + ", timeStamp=" + timeStamp + "]";
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/model/timeline/TimelineProbe.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.common.model.timeline;
+
+import com.redhat.thermostat.client.ui.Palette;
+
+public class TimelineProbe {
+
+    private String state;
+    private final long timeStamp;
+    private Palette colour;
+
+    public TimelineProbe(Palette color, String state, long timeStamp) {
+        this.colour = color;
+        this.state = state;
+        this.timeStamp = timeStamp;
+    }
+
+    public long getTimeStamp() {
+        return timeStamp;
+    }
+
+    public Palette getColor() {
+        return colour;
+    }
+
+    public String getState() {
+        return state;
+    }
+
+    @Override
+    public String toString() {
+        return "TimelineInfo [colour = " + colour + ", " + timeStamp + "]";
+    }
+}
+
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadTimelineView.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/view/ThreadTimelineView.java	Thu Dec 11 11:44:42 2014 +0100
@@ -37,74 +37,21 @@
 package com.redhat.thermostat.thread.client.common.view;
 
 import com.redhat.thermostat.client.core.views.BasicView;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
 import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.model.timeline.Timeline;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineGroupDataModel;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import java.util.List;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
 
 public abstract class ThreadTimelineView extends BasicView {
 
-    public static enum ThreadTimelineViewAction {
-        THREAD_TIMELINE_SELECTED,
-        SWITCH_TO_FOLLOW_MODE,
-        SWITCH_TO_STATIC_MODE,
-    }
-
-    public static enum TimelineSelectorState {
-        FOLLOWING,
-        STATIC,
-    }
-
-    protected final ActionNotifier<ThreadTimelineViewAction> threadTimelineNotifier;
-    
-    public ThreadTimelineView() {
-        threadTimelineNotifier = new ActionNotifier<>(this);
-    }
-    
-    public void addThreadSelectionActionListener(ActionListener<ThreadTimelineViewAction> listener) {
-        threadTimelineNotifier.addActionListener(listener);
-    }
-    
-    public void removeThreadSelectionActionListener(ActionListener<ThreadTimelineViewAction> listener) {
-        threadTimelineNotifier.removeActionListener(listener);
-    }
-
-    protected void requestFollowMode() {
-        threadTimelineNotifier.fireAction(ThreadTimelineViewAction.SWITCH_TO_FOLLOW_MODE);
-    }
+    /**
+     * Add this thread to the list of threads visible on screen
+     */
+    public abstract void addThread(ThreadInfo thread);
 
-    protected void requestStaticMode(Range<Long> pageRange) {
-        threadTimelineNotifier.fireAction(ThreadTimelineViewAction.SWITCH_TO_STATIC_MODE, pageRange);
-    }
-
-    /**
-     * Returns the {@link TimelineGroupDataModel} for this view.
-     */
-    public abstract TimelineGroupDataModel getGroupDataModel();
-
-    /**
-     * Ensures that the Timeline selector is in one of the possible states.
-     */
-    public abstract void ensureTimelineState(TimelineSelectorState following);
+    public abstract void setTotalRange(Range<Long> totalRange);
 
-    /**
-     * Update the list of all {@link ThreadHeader} currently displayed by
-     * this view.
-     */
-    public abstract void updateThreadList(List<ThreadHeader> threads);
+    public abstract void addProbe(ThreadInfo info, TimelineProbe state);
 
-    /**
-     * Display the given {@link Timeline} associated to this {@link ThreadHeader}.
-     */
-    public abstract void displayTimeline(ThreadHeader thread, Timeline threadTimeline);
-
-    /**
-     * Notify the View that all the changes may be delivered to the rendering
-     * thread.
-     */
-    public abstract void submitChanges();
+    public abstract void clear();
 }
 
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java	Thu Dec 11 11:44:42 2014 +0100
@@ -66,11 +66,13 @@
             public void actionPerformed(ActionEvent<Action> actionEvent) {
                 switch (actionEvent.getActionId()) {
                 case VISIBLE:
+                    onViewVisible();
                     timer.start();
                     break;
 
                 case HIDDEN:
                     timer.stop();
+                    onViewHidden();
                     break;
 
                 default:
@@ -79,5 +81,8 @@
             }
         });
     }
+
+    protected void onViewVisible() {}
+    protected void onViewHidden() {}
 }
 
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,9 +36,6 @@
 
 package com.redhat.thermostat.thread.client.controller.impl;
 
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.client.core.views.UIComponent;
 import com.redhat.thermostat.common.ActionEvent;
@@ -54,11 +51,12 @@
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
 import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
+import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction;
 import com.redhat.thermostat.thread.client.common.view.ThreadView;
-import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction;
 import com.redhat.thermostat.thread.client.common.view.ThreadView.ThreadAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 public class ThreadInformationController implements InformationServiceController<VmRef> {
 
@@ -70,18 +68,13 @@
 
     private ApplicationService appService;
 
-    private TimelineDimensionModel timelineDimensionModel;
-
     public ThreadInformationController(VmRef ref, ApplicationService appService,
                                        VmInfoDAO vmInfoDao,
                                        ThreadCollectorFactory collectorFactory, 
-                                       ThreadViewProvider viewFactory,
-                                       TimelineDimensionModel timelineDimensionModel)
+                                       ThreadViewProvider viewFactory)
     {
         this.appService = appService;
 
-        this.timelineDimensionModel = timelineDimensionModel;
-
         view = viewFactory.createView();
         view.setApplicationService(appService, ref.getVmId() + "-" + ref.getHostRef().getAgentId());
         
@@ -169,8 +162,9 @@
         threadTableController.initialize();
         
         CommonController threadTimeline =
-                new ThreadTimelineController(view.createThreadTimelineView(), collector,
-                                             tf.createTimer(), timelineDimensionModel);
+                new ThreadTimelineController(view.createThreadTimelineView(),
+                                             collector,
+                                             tf.createTimer());
         threadTimeline.initialize();
     }
 }
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationServiceImpl.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationServiceImpl.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,15 +36,14 @@
 
 package com.redhat.thermostat.thread.client.controller.impl;
 
-import com.redhat.thermostat.common.Filter;
 import com.redhat.thermostat.client.core.NameMatchingRefFilter;
 import com.redhat.thermostat.client.core.controllers.InformationServiceController;
 import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Filter;
 import com.redhat.thermostat.storage.core.VmRef;
 import com.redhat.thermostat.storage.dao.VmInfoDAO;
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
 import com.redhat.thermostat.thread.client.controller.ThreadInformationService;
 
 public class ThreadInformationServiceImpl implements ThreadInformationService {
@@ -56,18 +55,16 @@
     private VmInfoDAO vmInfoDao;
     private ThreadCollectorFactory collectorFactory;
     private ThreadViewProvider viewFactory;
-    private TimelineDimensionModel timelineDimensionModel;
 
-    public ThreadInformationServiceImpl(ApplicationService appService, VmInfoDAO vmInfoDao,
-                                    ThreadCollectorFactory collectorFactory,
-                                    ThreadViewProvider viewFactory,
-                                    TimelineDimensionModel timelineDimensionModel)
+    public ThreadInformationServiceImpl(ApplicationService appService,
+                                        VmInfoDAO vmInfoDao,
+                                        ThreadCollectorFactory collectorFactory,
+                                        ThreadViewProvider viewFactory)
     {
         this.service = appService;
         this.vmInfoDao = vmInfoDao;
         this.collectorFactory = collectorFactory;
         this.viewFactory = viewFactory;
-        this.timelineDimensionModel = timelineDimensionModel;
     }
     
     @Override
@@ -77,7 +74,8 @@
 
     @Override
     public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
-        return new ThreadInformationController(ref, service, vmInfoDao, collectorFactory, viewFactory, timelineDimensionModel);
+        return new ThreadInformationController(ref, service, vmInfoDao,
+                                               collectorFactory, viewFactory);
     }
 
     @Override
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Thu Dec 11 11:44:42 2014 +0100
@@ -41,13 +41,6 @@
 import com.redhat.thermostat.thread.client.common.ThreadTableBean;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
 import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
-import com.redhat.thermostat.thread.model.ThreadContentionSample;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import com.redhat.thermostat.thread.model.ThreadState;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 public class ThreadTableController extends CommonController {
     
@@ -55,7 +48,7 @@
     private ThreadCollector collector;
 
     private Range<Long> lastRangeChecked;
-    private Map<ThreadHeader, ThreadTableBean> threadStates;
+//    private Map<ThreadHeader, ThreadTableBean> threadStates;
 
     public ThreadTableController(ThreadTableView threadTableView,
                                  ThreadCollector collector,
@@ -64,7 +57,7 @@
         super(timer, threadTableView);
         timer.setAction(new ThreadTableControllerAction());
 
-        threadStates = new HashMap<>();
+//        threadStates = new HashMap<>();
         this.collector = collector;
         this.threadTableView = threadTableView;
     }
@@ -74,78 +67,78 @@
         @Override
         public void run() {
 
-            if (lastRangeChecked == null) {
-                lastRangeChecked = collector.getThreadStateTotalTimeRange();
-            } else {
-                lastRangeChecked = new Range<>(lastRangeChecked.getMax(),
-                                               System.currentTimeMillis());
-            }
-
-            List<ThreadTableBean> tableBeans = new ArrayList<>();
-
-            List<ThreadHeader> threads = collector.getThreads();
-
-            for (ThreadHeader thread : threads) {
-
-                ThreadTableBean bean = threadStates.get(thread);
-                if (bean == null) {
-                    bean = new ThreadTableBean();
-                    bean.setName(thread.getThreadName());
-                    bean.setId(thread.getThreadId());
-
-                    threadStates.put(thread, bean);
-                }
-
-                List<ThreadState> states = collector.getThreadStates(thread, lastRangeChecked);
-                for (ThreadState state : states) {
-
-                    Thread.State threadState = Thread.State.valueOf(state.getState());
-
-                    Range<Long> range = state.getRange();
-                    long currentRangeInCollection = range.getMax() - range.getMin();
-                    long currentStateInBean = getCurrentStateInBean(bean, threadState);
-
-                    currentStateInBean += currentRangeInCollection;
-                    setCurrentStateInBean(bean, threadState, currentStateInBean);
-
-                    double totalRunningTime = bean.getRunningTime()  +
-                                              bean.getMonitorTime()  +
-                                              bean.getSleepingTime() +
-                                              bean.getWaitingTime();
-
-                    if (totalRunningTime > 0) {
-                        double percent = (bean.getRunningTime() / totalRunningTime) * 100;
-                        bean.setRunningPercent(percent);
-
-                        percent = (bean.getWaitingTime() / totalRunningTime) * 100;
-                        bean.setWaitingPercent(percent);
-
-                        percent = (bean.getMonitorTime() / totalRunningTime) * 100;
-                        bean.setMonitorPercent(percent);
-
-                        percent = (bean.getSleepingTime() / totalRunningTime) * 100;
-                        bean.setSleepingPercent(percent);
-                    }
-                }
-
-                // check the latest stat regarding wait and block count
-                ThreadContentionSample sample = collector.getLatestContentionSample(thread);
-                if (sample != null) {
-                    bean.setBlockedCount(sample.getBlockedCount());
-                    bean.setWaitedCount(sample.getWaitedCount());
-                }
-
-                // finally, the time range for this thread
-                Range<Long> dataRange = collector.getThreadStateRange(thread);
-                if (dataRange != null) {
-                    bean.setStartTimeStamp(dataRange.getMin());
-                    bean.setStopTimeStamp(dataRange.getMax());
-                }
-
-                tableBeans.add(bean);
-            }
-
-            threadTableView.display(tableBeans);
+//            if (lastRangeChecked == null) {
+//                lastRangeChecked = collector.getThreadStateTotalTimeRange();
+//            } else {
+//                lastRangeChecked = new Range<>(lastRangeChecked.getMax(),
+//                                               System.currentTimeMillis());
+//            }
+//
+//            List<ThreadTableBean> tableBeans = new ArrayList<>();
+//
+//            List<ThreadHeader> threads = collector.getThreads();
+//
+//            for (ThreadHeader thread : threads) {
+//
+//                ThreadTableBean bean = threadStates.get(thread);
+//                if (bean == null) {
+//                    bean = new ThreadTableBean();
+//                    bean.setName(thread.getThreadName());
+//                    bean.setId(thread.getThreadId());
+//
+//                    threadStates.put(thread, bean);
+//                }
+//
+//                List<ThreadState> states = collector.getThreadStates(thread, lastRangeChecked);
+//                for (ThreadState state : states) {
+//
+//                    Thread.State threadState = Thread.State.valueOf(state.getState());
+//
+//                    Range<Long> range = state.getRange();
+//                    long currentRangeInCollection = range.getMax() - range.getMin();
+//                    long currentStateInBean = getCurrentStateInBean(bean, threadState);
+//
+//                    currentStateInBean += currentRangeInCollection;
+//                    setCurrentStateInBean(bean, threadState, currentStateInBean);
+//
+//                    double totalRunningTime = bean.getRunningTime()  +
+//                                              bean.getMonitorTime()  +
+//                                              bean.getSleepingTime() +
+//                                              bean.getWaitingTime();
+//
+//                    if (totalRunningTime > 0) {
+//                        double percent = (bean.getRunningTime() / totalRunningTime) * 100;
+//                        bean.setRunningPercent(percent);
+//
+//                        percent = (bean.getWaitingTime() / totalRunningTime) * 100;
+//                        bean.setWaitingPercent(percent);
+//
+//                        percent = (bean.getMonitorTime() / totalRunningTime) * 100;
+//                        bean.setMonitorPercent(percent);
+//
+//                        percent = (bean.getSleepingTime() / totalRunningTime) * 100;
+//                        bean.setSleepingPercent(percent);
+//                    }
+//                }
+//
+//                // check the latest stat regarding wait and block count
+//                ThreadContentionSample sample = collector.getLatestContentionSample(thread);
+//                if (sample != null) {
+//                    bean.setBlockedCount(sample.getBlockedCount());
+//                    bean.setWaitedCount(sample.getWaitedCount());
+//                }
+//
+//                // finally, the time range for this thread
+//                Range<Long> dataRange = collector.getThreadStateRange(thread);
+//                if (dataRange != null) {
+//                    bean.setStartTimeStamp(dataRange.getMin());
+//                    bean.setStopTimeStamp(dataRange.getMax());
+//                }
+//
+//                tableBeans.add(bean);
+//            }
+//
+//            threadTableView.display(tableBeans);
         }
     }
 
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,20 +36,19 @@
 
 package com.redhat.thermostat.thread.client.controller.impl;
 
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.chart.ChartColors;
+import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.model.timeline.Timeline;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineFactory;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
 import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView.ThreadTimelineViewAction;
-import com.redhat.thermostat.thread.model.ThreadHeader;
+import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadState;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 public class ThreadTimelineController extends CommonController {
 
@@ -57,126 +56,115 @@
 
     private ThreadTimelineView view;
     private ThreadCollector collector;
-    
-    private static final long EXTRA_TIMELINE_BUFFER = 2000;
-    
-    private final String lock = new String("ThreadTimelineController"); 
+
+    private static final String lock = new String("ThreadTimelineController");
+
+    private boolean requestClear;
 
-    private boolean followMode;
-
-    private TimelineDimensionModel timelineDimensionModel;
-    private Range<Long> pageRangeInStaticMode;
-
-    public ThreadTimelineController(ThreadTimelineView view, ThreadCollector collector, Timer timer,
-                                    TimelineDimensionModel timelineDimensionModel) {
+    public ThreadTimelineController(ThreadTimelineView view,
+                                    ThreadCollector collector,
+                                    Timer timer)
+    {
         super(timer, view);
         timer.setAction(new ThreadTimelineControllerAction());
         this.view = view;
-        this.view.addThreadSelectionActionListener(new ThreadTimelineSelectedAction());
         this.collector = collector;
-        followMode = false;
-
-        this.timelineDimensionModel = timelineDimensionModel;
     }
 
-    private class ThreadTimelineSelectedAction implements ActionListener<ThreadTimelineViewAction> {
-        
-        @Override
-        public void actionPerformed(ActionEvent<ThreadTimelineViewAction> actionEvent) {
-            switch (actionEvent.getActionId()) {
-            case THREAD_TIMELINE_SELECTED:
-                break;
-                
-            case SWITCH_TO_FOLLOW_MODE:
-                synchronized (lock) {
-                    followMode = true;
-                }
-                view.ensureTimelineState(ThreadTimelineView.TimelineSelectorState.FOLLOWING);
-                break;
-                
-            case SWITCH_TO_STATIC_MODE:
-                synchronized (lock) {                    
-                    followMode = false;
-                    pageRangeInStaticMode = (Range<Long>) actionEvent.getPayload();
-                }
-                view.ensureTimelineState(ThreadTimelineView.TimelineSelectorState.STATIC);
-                break;
-            
-            default:
-                break;
-            }
+    private class ThreadTimelineControllerAction implements Runnable {
+
+        private Range<Long> range;
+        private Range<Long> lastRange;
+        private ThreadStateResultHandler threadStateResultHandler;
+        private long lastUpdate;
+
+        private SessionID lastSession;
+
+        public ThreadTimelineControllerAction() {
+            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+            threadStateResultHandler = new ThreadStateResultHandler();
         }
-    }
-    
-    private class ThreadTimelineControllerAction implements Runnable {
+
+        private void resetState() {
+            view.clear();
+            lastRange = null;
+            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+            threadStateResultHandler.knownStates.clear();
+            threadStateResultHandler.key = new ThreadInfo();
+        }
 
         @Override
         public void run() {
-            
+
             synchronized (lock) {
 
-                long timelineLength = timelineDimensionModel.getLengthInMillis();
+                // FIXME: show only the last sessions for now, support for
+                // filtering over sessions will come later
+                SessionID session = collector.getLastThreadSession();
+                if (session == null) {
+                    // ok, no data, let's skip this round
+                    return;
+                }
 
-                Range<Long> totalRange = collector.getThreadStateTotalTimeRange();
+                if (lastSession == null ||
+                    !session.get().equals(lastSession.get()))
+                {
+                    // since we only visualise one sessions at a time and this
+                    // is a new session needs, let's clear the view
+                    resetState();
+                }
+                lastSession = session;
+
+                // get the full range of known timelines per vm
+                Range<Long> totalRange = collector.getThreadRange(session);
                 if (totalRange == null) {
-                    // that simply means we don't have data yet, let's just skip
-                    // this loop
+                    // this just means we don't have any data yet
                     return;
                 }
-                view.getGroupDataModel().setTotalRange(totalRange);
 
-                List<ThreadHeader> threads = collector.getThreads();
+                if (!totalRange.equals(lastRange)) {
+                    view.setTotalRange(totalRange);
+                }
+                lastRange = totalRange;
 
-                view.updateThreadList(threads);
+                range = new Range<>(lastUpdate, totalRange.getMax());
+                lastUpdate = totalRange.getMax();
 
-                Range<Long> pageRange = null;
-                Range<Long> visibleRange = null;
-                List<ThreadState> states = null;
-                if (followMode || pageRangeInStaticMode == null) {
-                    // get the latest info available, ensure a little of extra
-                    // buffer so that we are sure to have continuity around the
-                    // timeline edges
-                    long max = totalRange.getMax();
-                    long pageMin = max - timelineLength;
-                    pageRange = new Range<>(pageMin, totalRange.getMax());
+                collector.getThreadStates(session,
+                                          threadStateResultHandler,
+                                          range);
+                threadStateResultHandler.results = 0;
+            }
+        }
+    }
 
-                    long min = pageMin - EXTRA_TIMELINE_BUFFER;
-                    visibleRange = new Range<>(min, max);
-
-                    view.getGroupDataModel().setPageRange(pageRange);
+    private class ThreadStateResultHandler implements ResultHandler<ThreadState> {
+        private ThreadInfo key;
+        private Set<ThreadInfo> knownStates;
 
-                } else {
-                    long max = pageRangeInStaticMode.getMax() + EXTRA_TIMELINE_BUFFER;
-                    long pageMin = max - timelineLength;
-                    long min = pageMin - EXTRA_TIMELINE_BUFFER;
-                    visibleRange = new Range<>(min, max);
-                    view.getGroupDataModel().setPageRange(pageRangeInStaticMode);
-                }
+        int results;
 
-                long sampleStart = 0l;
-                if (_DEBUG_BLOCK_TIMING_) {
-                    sampleStart = System.currentTimeMillis();
-                }
+        public ThreadStateResultHandler() {
+            this.key = new ThreadInfo();
+            knownStates = new HashSet<>();
+        }
+
+        @Override
+        public void onResult(ThreadState state) {
+
+            results++;
 
-                for (ThreadHeader thread : threads) {
-                    states = collector.getThreadStates(thread, visibleRange);
-                    Timeline threadTimeline = new Timeline(thread.getThreadName(), thread.getThreadId());
-                    for (ThreadState state : states) {
-                        TimelineInfo info = new TimelineInfo();
-                        info.setColor(ChartColors.getPaletteColor(state.getState()));
-                        info.setRange(state.getRange());
-                        threadTimeline.add(info);
-                    }
-                    view.displayTimeline(thread, threadTimeline);
-                }
+            key.setName(state.getName());
+            key.setId(state.getId());
 
-                if (_DEBUG_BLOCK_TIMING_) {
-                    long sampleStop = System.currentTimeMillis();
-                    System.err.println("getThreadStates time: " + (sampleStop - sampleStart));
-                }
+            ThreadInfo info = new ThreadInfo(key);
+            if (!knownStates.contains(key)) {
+                view.addThread(info);
+                knownStates.add(info);
+            }
 
-                view.submitChanges();
-            }
+            TimelineProbe probe = TimelineFactory.createTimelineProbe(state);
+            view.addProbe(info, probe);
         }
     }
 }
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,16 +36,6 @@
 
 package com.redhat.thermostat.thread.client.controller.osgi;
 
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.Map;
-import java.util.Objects;
-
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceRegistration;
-
 import com.redhat.thermostat.client.core.InformationService;
 import com.redhat.thermostat.common.ApplicationService;
 import com.redhat.thermostat.common.Constants;
@@ -57,6 +47,13 @@
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
 import com.redhat.thermostat.thread.client.controller.ThreadInformationService;
 import com.redhat.thermostat.thread.client.controller.impl.ThreadInformationServiceImpl;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Objects;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
 
 public class Activator implements BundleActivator {
 
@@ -69,7 +66,6 @@
                 ApplicationService.class,
                 VmInfoDAO.class,
                 ThreadViewProvider.class,
-                TimelineDimensionModel.class,
         };
         
         Action action = new Action() {
@@ -82,11 +78,13 @@
                 ApplicationService applicationService = (ApplicationService) services.get(ApplicationService.class.getName());
                 VmInfoDAO vmInfoDao = Objects.requireNonNull((VmInfoDAO) services.get(VmInfoDAO.class.getName()));
                 ThreadViewProvider viewFactory = (ThreadViewProvider) services.get(ThreadViewProvider.class.getName());
-                TimelineDimensionModel timelineDimensionModel = (TimelineDimensionModel) services.get(TimelineDimensionModel.class.getName());
 
-                ThreadInformationService vmInfoService = new ThreadInformationServiceImpl(applicationService, vmInfoDao,
-                                                                                          collectorFactory, viewFactory,
-                                                                                          timelineDimensionModel);
+                ThreadInformationService vmInfoService =
+                        new ThreadInformationServiceImpl(applicationService,
+                                                         vmInfoDao,
+                                                         collectorFactory,
+                                                         viewFactory);
+
                 Dictionary<String, String> properties = new Hashtable<>();
                 properties.put(Constants.GENERIC_SERVICE_CLASSNAME, VmRef.class.getName());
                 properties.put(InformationService.KEY_SERVICE_ID, ThreadInformationService.SERVICE_ID);
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -50,7 +50,6 @@
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
 import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
 import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
 import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction;
@@ -87,13 +86,9 @@
     private ThreadTimelineView threadTimelineView;
     private ThreadCountView threadCountView;
 
-    private TimelineDimensionModel timelineDimensionModel;
-
     @Before
     public void setUp() {
 
-        timelineDimensionModel = mock(TimelineDimensionModel.class);
-
         appService = mock(ApplicationService.class);
         vmInfo = mock(VmInfo.class);
         when(vmInfo.isAlive()).thenReturn(true);
@@ -154,8 +149,7 @@
 
         controller = new ThreadInformationController(ref, appService, vmInfoDao,
                                                      collectorFactory,
-                                                     viewFactory,
-                                                     timelineDimensionModel);
+                                                     viewFactory);
     }
     
     @Test
@@ -195,8 +189,7 @@
 
         controller = new ThreadInformationController(ref, appService, vmInfoDao,
                                                      collectorFactory,
-                                                     viewFactory,
-                                                     timelineDimensionModel);
+                                                     viewFactory);
 
         verify(collector).isHarvesterCollecting();
         verify(view, times(1)).setRecording(false, false);
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,21 +38,17 @@
 
 import com.redhat.thermostat.common.Timer;
 import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.model.timeline.Timeline;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineGroupDataModel;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
 import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import java.util.ArrayList;
-import java.util.List;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadState;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -60,72 +56,109 @@
 
 public class ThreadTimelineControllerTest {
 
-    private ArgumentCaptor<Runnable> timerActionCaptor;
-    private Timer timer;
-
     private ThreadTimelineView view;
     private ThreadCollector collector;
-    private TimelineDimensionModel timelineDimensionModel;
-    private TimelineGroupDataModel groupDataModel;
-
-    private Range<Long> totalRange = new Range<>(0l, 30_000l);
+    private Timer timer;
+    private SessionID session;
 
     @Before
-    public void setUp() throws Exception {
-        timer = mock(Timer.class);
-        timerActionCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(timer).setAction(timerActionCaptor.capture());
-
+    public void setup() {
         view = mock(ThreadTimelineView.class);
         collector = mock(ThreadCollector.class);
-        timelineDimensionModel = mock(TimelineDimensionModel.class);
-        when(timelineDimensionModel.getLengthInMillis()).thenReturn(25_000l);
-        when(collector.getThreadStateTotalTimeRange()).thenReturn(totalRange);
+        timer = mock(Timer.class);
+        session = mock(SessionID.class);
+
+        when(collector.getLastThreadSession()).thenReturn(session);
+    }
+
+    @Test
+    public void verifySession() {
+        ArgumentCaptor<Runnable> captor =
+                ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
+
+        ThreadTimelineController controller =
+                new ThreadTimelineController(view, collector, timer);
+        Runnable timerAction = captor.getValue();
+
+        timerAction.run();
 
-        groupDataModel = mock(TimelineGroupDataModel.class);
-        when(view.getGroupDataModel()).thenReturn(groupDataModel);
+        verify(collector).getLastThreadSession();
+    }
+
+    @Test
+    public void verifyRange() {
+        ArgumentCaptor<Runnable> captor =
+                ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
+
+        Range<Long> range = new Range<>(0l, 10l);
+        when(collector.getThreadRange(session)).thenReturn(range);
+
+        ThreadTimelineController controller =
+                new ThreadTimelineController(view, collector, timer);
+        Runnable timerAction = captor.getValue();
+
+        timerAction.run();
+
+        verify(collector).getThreadRange(session);
+        verify(view).setTotalRange(range);
     }
 
     @Test
-    public void testTimelineController() {
-
-        Range<Long> pageRange = new Range<>(5_000l, 30_000l);
-        ArgumentCaptor<Range> pageRangeCaptor = ArgumentCaptor.forClass(Range.class);
-        doNothing().when(groupDataModel).setPageRange(pageRangeCaptor.capture());
+    public void testAllBeansAreLoaded() {
+        ArgumentCaptor<Runnable> captor =
+                ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
 
-        ThreadHeader thread1 = mock(ThreadHeader.class);
-        ThreadHeader thread2 = mock(ThreadHeader.class);
-
-        List<ThreadHeader> threads = new ArrayList<>();
+        ArgumentCaptor<ResultHandler> captor2 =
+                ArgumentCaptor.forClass(ResultHandler.class);
+        doNothing().when(collector).getThreadStates(any(SessionID.class),
+                                                    captor2.capture(),
+                                                    any(Range.class));
 
-        threads.add(thread1);
-        threads.add(thread2);
-
-        when(collector.getThreads()).thenReturn(threads);
+        Range<Long> range = new Range<>(0l, 10l);
+        when(collector.getThreadRange(session)).thenReturn(range);
 
         ThreadTimelineController controller =
-                new ThreadTimelineController(view, collector, timer,
-                                             timelineDimensionModel);
+                new ThreadTimelineController(view, collector, timer);
+        Runnable timerAction = captor.getValue();
 
-        Runnable controllerRunnable = timerActionCaptor.getValue();
+        timerAction.run();
+
+        ResultHandler handler = captor2.getValue();
 
-        controllerRunnable.run();
+        ThreadState state0 = mock(ThreadState.class);
+        when(state0.getName()).thenReturn("state0");
+        when(state0.getId()).thenReturn(0l);
+        when(state0.getState()).thenReturn("NEW");
 
-        // check that the thread list is correctly passed to the view
-        verify(view).updateThreadList(threads);
+        ThreadState state1 = mock(ThreadState.class);
+        when(state1.getName()).thenReturn("state1");
+        when(state1.getId()).thenReturn(1l);
+        when(state1.getState()).thenReturn("NEW");
 
-        // verify group model gets updated with the page and total data
-        verify(groupDataModel).setTotalRange(totalRange);
-        Range<Long> pageRangeResult = pageRangeCaptor.getValue();
-        assertEquals(pageRangeResult, pageRange);
+        ThreadState state2 = mock(ThreadState.class);
+        when(state2.getName()).thenReturn("state2");
+        when(state2.getId()).thenReturn(2l);
+        when(state2.getState()).thenReturn("NEW");
+
+        handler.onResult(state0);
+        handler.onResult(state1);
+        handler.onResult(state2);
 
-        // check that the thread state is queried for each of the thread headers
-        verify(collector).getThreadStates(eq(thread1), any(Range.class));
-        verify(collector).getThreadStates(eq(thread2), any(Range.class));
+        ThreadInfo info = new ThreadInfo();
+        info.setName("state0");
+        info.setId(0l);
+
+        verify(view).addThread(info);
 
-        verify(view).displayTimeline(eq(thread1), any(Timeline.class));
-        verify(view).displayTimeline(eq(thread2), any(Timeline.class));
+        info.setName("state1");
+        info.setId(1l);
+        verify(view).addThread(info);
 
-        verify(view).submitChanges();
+        info.setName("state2");
+        info.setId(2l);
+        verify(view).addThread(info);
     }
 }
--- a/thread/client-swing/pom.xml	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/pom.xml	Thu Dec 11 11:44:42 2014 +0100
@@ -115,8 +115,10 @@
             <Private-Package>
               com.redhat.thermostat.thread.client.swing.osgi,
               com.redhat.thermostat.thread.client.swing.impl,
-              com.redhat.thermostat.thread.client.swing.impl.timeline,
-              com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar,
+                com.redhat.thermostat.thread.client.swing.impl.timeline,
+                com.redhat.thermostat.thread.client.swing.impl.timeline.model,
+                com.redhat.thermostat.thread.client.swing.experimental.components,
+                com.redhat.thermostat.thread.client.swing.experimental.utils,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java	Thu Dec 11 11:44:42 2014 +0100
@@ -40,21 +40,18 @@
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 import com.redhat.thermostat.thread.client.common.view.ThreadView;
 import com.redhat.thermostat.thread.client.swing.impl.SwingThreadView;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
 
 public class SwingThreadViewService implements ThreadViewProvider {
     
     private UIDefaults uiDefaults;
-    private SwingTimelineDimensionModel dimensionModel;
-    
-    public SwingThreadViewService(UIDefaults uiDefaults, SwingTimelineDimensionModel dimensionModel) {
+
+    public SwingThreadViewService(UIDefaults uiDefaults) {
         this.uiDefaults = uiDefaults;
-        this.dimensionModel = dimensionModel;
     }
     
     @Override
     public ThreadView createView() {
-        return new SwingThreadView(uiDefaults, dimensionModel);
+        return new SwingThreadView(uiDefaults);
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/ContentPane.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.experimental.components;
+
+import com.redhat.thermostat.client.ui.Palette;
+import java.awt.BorderLayout;
+import javax.swing.JPanel;
+
+/**
+ * Basic panel that should be used for all custom panels in Thermostat.
+ */
+public class ContentPane extends JPanel {
+    public ContentPane() {
+        setBackground(Palette.WHITE.getColor());
+        setLayout(new BorderLayout());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/DataPane.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.experimental.components;
+
+import com.redhat.thermostat.client.swing.GraphicsUtils;
+import com.redhat.thermostat.client.ui.Palette;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.LinearGradientPaint;
+
+public class DataPane extends ContentPane {
+
+    private final Palette top;
+    private final Palette bottom;
+
+    public DataPane() {
+        this(Palette.WHITE, Palette.LIGHT_GRAY);
+    }
+
+    public DataPane(Palette top, Palette bottom) {
+        this.top = top;
+        this.bottom = bottom;
+        setLayout(new BorderLayout());
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+
+        Color top = this.top.getColor();
+        Color bottom = this.bottom.getColor();
+        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
+        final float[] fractions = {.0f, .2f, .4f, 1.f};
+        final Color[] colors = {
+                top,
+                top,
+                top,
+                bottom,
+        };
+        LinearGradientPaint paint = new LinearGradientPaint(0, 0, 0,
+                                                            getHeight(),
+                                                            fractions, colors);
+        graphics.setPaint(paint);
+        graphics.fillRect(0, 0, getWidth(), getHeight());
+        graphics.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/components/Separator.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.experimental.components;
+
+import com.redhat.thermostat.client.swing.GraphicsUtils;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.ui.Palette;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.LinearGradientPaint;
+import java.awt.Rectangle;
+import javax.swing.border.AbstractBorder;
+
+/**
+ * A configurable border that separates two components.
+ */
+public class Separator extends AbstractBorder {
+
+    public enum Type {
+        SOLID,
+        SMOOTH,
+    }
+
+    public enum Side {
+        TOP,
+        BOTTOM,
+        LEFT,
+        RIGHT,
+    }
+
+    private Side side;
+
+    private UIDefaults defaults;
+    private Type type;
+
+    public Separator(UIDefaults defaults) {
+        this(defaults, Side.TOP, Type.SOLID);
+    }
+
+    public Separator(UIDefaults defaults, Side side, Type type) {
+        this.side = side;
+        this.defaults = defaults;
+        this.type = type;
+    }
+
+    private void paintSolidBorder(Graphics2D graphics, int x, int y, int width, int height) {
+
+        Rectangle fillRect = new Rectangle(x, y, width, height);
+
+        switch (side) {
+
+        default:
+        case TOP:
+            graphics.setColor(Palette.DARK_GRAY.getColor());
+            graphics.drawLine(x, y + 1, x + width - 1, y + 1);
+            graphics.setPaint(Palette.WHITE.getColor());
+            graphics.drawLine(x, y, x + width - 1, y);
+            break;
+
+        case BOTTOM:
+            graphics.setColor(Palette.WHITE.getColor());
+            graphics.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
+            graphics.setPaint(Palette.DARK_GRAY.getColor());//defaults.getComponentBGColor());
+            graphics.drawLine(x, y + height - 2, x + width - 1, y + height - 2);
+            break;
+
+        case LEFT:
+            graphics.setColor(Palette.WHITE.getColor());
+            graphics.drawLine(x, y, x, y + height - 1);
+            graphics.setPaint(Palette.DARK_GRAY.getColor());
+            graphics.drawLine(x + 1, y, x + 1, y + height - 1);
+            break;
+
+        case RIGHT:
+            graphics.setColor(Palette.WHITE.getColor());
+            graphics.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
+            graphics.setPaint(Palette.DARK_GRAY.getColor());
+            graphics.drawLine(x + width - 2, y, x + width - 2, y + height - 1);
+            break;
+        }
+    }
+
+    private void paintSmoothBorder(Graphics2D graphics, int x, int y, int width, int height) {
+
+        final float[] fractions = {.0f, .4f, .6f, 1.f};
+        final Color[] colors = {
+                (Color) defaults.getComponentBGColor(),
+                (Color) defaults.getReferenceFieldIconColor(),
+                (Color) defaults.getReferenceFieldIconColor(),
+                (Color) defaults.getComponentBGColor(),
+        };
+
+        Rectangle gradientRect = new Rectangle(x, y, width, height);
+        Rectangle fillRect = new Rectangle(x, y, width, height);
+
+        switch (side) {
+        default:
+        case TOP:
+            gradientRect.y = 0;
+            gradientRect.height = 0;
+            fillRect.height = 1;
+            break;
+
+        case BOTTOM:
+            gradientRect.y = 0;
+            gradientRect.height = 0;
+            fillRect.y = y + height - 1;
+            fillRect.height = 1;
+            break;
+
+        case LEFT:
+            gradientRect.x = 0;
+            gradientRect.width = 0;
+            fillRect.width = 1;
+            break;
+
+        case RIGHT:
+            gradientRect.x = 0;
+            gradientRect.width = 0;
+            fillRect.x = x + width - 1;
+            fillRect.width = 1;
+            break;
+        }
+
+        LinearGradientPaint paint =
+                new LinearGradientPaint(gradientRect.x,
+                                        gradientRect.y, gradientRect.width,
+                                        gradientRect.height, fractions, colors);
+        graphics.setPaint(paint);
+        graphics.fill(fillRect);
+
+        graphics.dispose();
+    }
+
+    @Override
+    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+
+        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
+        switch (type) {
+        case SOLID:
+            paintSolidBorder(graphics, x, y, width, height);
+            break;
+
+        case SMOOTH:
+            paintSmoothBorder(graphics, x, y, width, height);
+            break;
+        }
+    }
+
+    @Override
+    public Insets getBorderInsets(Component c, Insets insets) {
+
+        insets.top = 2;
+        insets.left = 0;
+        insets.right = 0;
+        insets.bottom = 0;
+
+        return insets;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/experimental/utils/EDTHelper.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.experimental.utils;
+
+import com.redhat.thermostat.client.swing.EdtHelper;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.SwingUtilities;
+
+/**
+ *
+ */
+public class EDTHelper {
+    private static final Logger logger = LoggingUtils.getLogger(EDTHelper.class);
+    private EdtHelper delegate;
+    public EDTHelper() {
+        delegate = new EdtHelper();
+    }
+
+    public <T> T callAndWait(Callable<T> c) {
+        T result = null;
+        try {
+            result = delegate.callAndWait(c);
+
+        } catch (InvocationTargetException | InterruptedException e) {
+            logger.log(Level.WARNING, "Exception while waiting for task", e);
+        }
+        return result;
+    }
+
+    public void callLater(Runnable run) {
+        SwingUtilities.invokeLater(run);
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,130 +36,64 @@
 
 package com.redhat.thermostat.thread.client.swing.impl;
 
-import com.redhat.thermostat.client.swing.ComponentVisibleListener;
 import com.redhat.thermostat.client.swing.SwingComponent;
 import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
 import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
 import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.model.timeline.Timeline;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineGroupDataModel;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
 import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.HeaderController;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.ThreadTimelineHeader;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineCellRenderer;
+import com.redhat.thermostat.thread.client.swing.experimental.utils.EDTHelper;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.RangeComponent;
 import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineComponent;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineGroupThreadConverter;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar.SwingTimelineScrollBarController;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar.TimelineScrollBar;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-
-import java.awt.BorderLayout;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineContainer;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineViewComponent;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangedTimelineProbe;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineDateFormatter;
 import java.awt.Component;
-import java.awt.event.ComponentAdapter;
-import java.awt.event.ComponentEvent;
 import java.util.HashMap;
-import java.util.List;
 import java.util.Map;
 
-import javax.swing.DefaultListModel;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.ListSelectionModel;
-import javax.swing.SwingUtilities;
-
 public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent  {
 
-    private SwingTimelineScrollBarController scrollBarController;
-    private Map<ThreadHeader, TimelineComponent> timelineMap;
+    private final UIDefaults uiDefaults;
+    private TimelineViewComponent contentPane;
 
-    private TimelineGroupThreadConverter groupDataModel;
-    private SwingTimelineDimensionModel dimensionModel;
+    private ComponentVisibilityNotifier visibilityNotifier;
 
-    private DefaultListModel<TimelineComponent> timelineModel;
-    private JList<TimelineComponent> timelines;
+    private Map<ThreadInfo, TimelineComponent> timelines;
+
+    private EDTHelper edt;
 
-    private ThreadTimelineHeader header;
-    private JPanel contentPane;
-    private JScrollPane scrollPane;
+    public SwingThreadTimelineView(UIDefaults uiDefaults) {
+        this.uiDefaults = uiDefaults;
 
-    public SwingThreadTimelineView(UIDefaults uiDefaults,
-                                   final SwingTimelineDimensionModel dimensionModel)
-    {
-        timelineMap = new HashMap<>();
+        edt = new EDTHelper();
+        timelines = new HashMap<>();
+
+        this.contentPane = new TimelineViewComponent(uiDefaults);
+        clear();
 
-        this.dimensionModel = dimensionModel;
+        visibilityNotifier = new ComponentVisibilityNotifier();
+        visibilityNotifier.initialize(contentPane, notifier);
+    }
 
-        TimelineGroupDataModel realGDM = new TimelineGroupDataModel();
-        groupDataModel = new TimelineGroupThreadConverter(realGDM);
-
-        contentPane = new JPanel();
-        new ComponentVisibilityNotifier().initialize(contentPane, notifier);
-        contentPane.addHierarchyListener(new ComponentVisibleListener() {
+    @Override
+    public void addThread(final ThreadInfo thread) {
+        edt.callLater(new Runnable() {
             @Override
-            public void componentShown(Component component) {
-                // TODO: this should be retrieved from state properties
-                requestFollowMode();
-            }
-            
-            @Override
-            public void componentHidden(Component component) {
-                // TODO should requestFollowMode be disabled?
+            public void run() {
+                if (!timelines.containsKey(thread)) {
+                    TimelineComponent timeline =
+                            new TimelineComponent(uiDefaults, thread,
+                                                  contentPane.getModel());
+                    timeline.initComponents();
+
+                    timelines.put(thread, timeline);
+                    contentPane.addTimeline(timeline);
+                }
             }
         });
-
-        contentPane.setLayout(new BorderLayout(0, 0));
-
-        JPanel timelineBottomControls = new JPanel();
-        timelineBottomControls.setLayout(new BorderLayout(0, 0));
-
-        TimelineScrollBar scrollbar = setupTimelineScrollBar(uiDefaults);
-        timelineBottomControls.add(scrollbar, BorderLayout.NORTH);
-
-        createScrollPane();
-        contentPane.add(scrollPane, BorderLayout.CENTER);
-
-        ThreadTimelineLegendPanel timelineLegend = new ThreadTimelineLegendPanel();
-        timelineBottomControls.add(timelineLegend, BorderLayout.SOUTH);
-
-        contentPane.add(timelineBottomControls, BorderLayout.SOUTH);
-        contentPane.addComponentListener(new ComponentAdapter() {
-            @Override
-            public void componentResized(ComponentEvent e) {
-            dimensionModel.setWidth(contentPane.getWidth());
-            }
-        });
-    }
-
-    TimelineScrollBar setupTimelineScrollBar(UIDefaults uiDefaults) {
-
-        TimelineScrollBar scrollbar = new TimelineScrollBar(uiDefaults);
-        scrollBarController = new SwingTimelineScrollBarController(this, scrollbar,
-                                                                   groupDataModel,
-                                                                   dimensionModel);
-        scrollBarController.initScrollbar(this);
-        return scrollbar;
-    }
-
-    private void createScrollPane() {
-
-        timelineModel = new DefaultListModel<>();
-        timelines = new JList<>(timelineModel);
-        timelines.setCellRenderer(new TimelineCellRenderer());
-        timelines.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
-
-        scrollPane = new ThermostatScrollPane(timelines);
-        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
-
-        header = new ThreadTimelineHeader(groupDataModel, dimensionModel);
-        header.setName("TimelineRulerHeader_thread");
-
-        scrollPane.setColumnHeaderView(header);
-        groupDataModel.addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty.PAGE_RANGE,
-                                                 new HeaderController(header, timelines));
     }
 
     @Override
@@ -168,70 +102,64 @@
     }
 
     @Override
-    public void ensureTimelineState(final TimelineSelectorState following) {
-        SwingUtilities.invokeLater(new Runnable() {
+    public void setTotalRange(final Range<Long> totalRange) {
+        edt.callLater(new Runnable() {
             @Override
             public void run() {
-                scrollBarController.ensureTimelineState(following);
+                contentPane.getModel().setRange(totalRange);
             }
         });
     }
-    
+
     @Override
-    public void updateThreadList(final List<ThreadHeader> threads) {
-        SwingUtilities.invokeLater(new Runnable() {
+    public void clear() {
+        edt.callLater(new Runnable() {
             @Override
             public void run() {
-                // TODO: remove timelines that are not in the list, but
-                // still present onscreen
-                for (ThreadHeader thread : threads) {
-                    if (!timelineMap.containsKey(thread)) {
-                        TimelineComponent timeline =
-                                new TimelineComponent(groupDataModel,
-                                                      dimensionModel,
-                                                      thread.getThreadName());
-                        timelineMap.put(thread, timeline);
-                        timelineModel.addElement(timeline);
-                    }
-                }
+                timelines.clear();
+                contentPane.removeAll();
+                contentPane.initComponents();
+                contentPane.revalidate();
             }
         });
     }
 
     @Override
-    public TimelineGroupDataModel getGroupDataModel() {
-        return groupDataModel.getDataModel();
-    }
-
-    @Override
-    public void displayTimeline(final ThreadHeader thread,
-                                final Timeline threadTimeline)
-    {
-        SwingUtilities.invokeLater(new Runnable() {
+    public void addProbe(final ThreadInfo info, final TimelineProbe state) {
+        edt.callLater(new Runnable() {
             @Override
             public void run() {
-                TimelineComponent timeline = timelineMap.get(thread);
-                if (timeline != null) {
-                    timeline.setTimeline(threadTimeline);
+                TimelineComponent component = timelines.get(info);
+                TimelineContainer timelineContainer =
+                        component.getTimelineContainer();
+                RangeComponent rangeComponent =
+                        timelineContainer.getLastRangeComponent();
+
+                if (rangeComponent == null) {
+                    setRangedComponent(state, timelineContainer);
+
+                } else {
+                    RangedTimelineProbe probe = rangeComponent.getInfo();
+                    probe.setProbeEnd(state.getTimeStamp());
+                    if (!probe.getColor().equals(state.getColor())) {
+                        setRangedComponent(state, timelineContainer);
+                    }
                 }
+                timelineContainer.revalidate();
             }
         });
     }
-    @Override
-    public void submitChanges() {
-        contentPane.revalidate();
-    }
 
-
-    // rise visibility so other classes here can use those methods through us
-    @Override
-    public void requestFollowMode() {
-        super.requestFollowMode();
-    }
-
-    @Override
-    public void requestStaticMode(Range<Long> pageRange) {
-        super.requestStaticMode(pageRange);
+    private void setRangedComponent(TimelineProbe state,
+                                    TimelineContainer timelineContainer)
+    {
+        RangedTimelineProbe probe =
+                new RangedTimelineProbe(state, state.getTimeStamp());
+        RangeComponent rangeComponent = new RangeComponent(probe);
+        rangeComponent.setToolTipText(state.getState() + " - " +
+                                      TimelineDateFormatter.format(state.
+                                              getTimeStamp()));
+        timelineContainer.add(rangeComponent);
     }
 }
 
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadView.java	Thu Dec 11 11:44:42 2014 +0100
@@ -50,7 +50,6 @@
 import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
 import com.redhat.thermostat.thread.client.common.view.ThreadView;
 import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
 
 import java.awt.Component;
 import java.awt.event.ItemEvent;
@@ -86,13 +85,11 @@
     private int threadDetailsPaneID = 0;
     
     private UIDefaults uiDefaults;
-    private SwingTimelineDimensionModel dimensionModel;
-    
-    public SwingThreadView(UIDefaults uiDefaults, SwingTimelineDimensionModel dimensionModel) {
+
+    public SwingThreadView(UIDefaults uiDefaults) {
         
         this.uiDefaults = uiDefaults;
-        this.dimensionModel = dimensionModel;
-        
+
         panel = new ThreadMainPanel();
         // TODO use ComponentVisiblityNotifier instead
         // sadly, the BasicView.notifier field can not be accessed here
@@ -158,7 +155,7 @@
         topPane = new ThermostatTabbedPane();
         topPane.setName("topTabbedPane");
         
-        threadTimelineView = new SwingThreadTimelineView(uiDefaults, dimensionModel);
+        threadTimelineView = new SwingThreadTimelineView(uiDefaults);
         topPane.addTab(t.localize(LocaleResources.TIMELINE).getContents(), threadTimelineView.getUiComponent());
         
         threadCountView = new SwingThreadCountView();
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/HeaderController.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import javax.swing.JComponent;
-
-/**
- */
-public class HeaderController implements PropertyChangeListener {
-
-    private JComponent[] toRepaint;
-    public HeaderController(JComponent ... toRepaint) {
-        this.toRepaint = toRepaint;
-    }
-
-    @Override
-    public void propertyChange(PropertyChangeEvent evt) {
-        for (JComponent component : toRepaint) {
-            component.repaint();
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangedTimelineProbe;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+/**
+ *
+ */
+public class RangeComponent extends ContentPane {
+    private RangedTimelineProbe info;
+
+    public RangeComponent(RangedTimelineProbe info) {
+        this.info = info;
+    }
+
+    public RangedTimelineProbe getInfo() {
+        return info;
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+        g.setColor(info.getColor().getColor());
+        Rectangle bounds = g.getClipBounds();
+        g.fillRect(bounds.x, bounds.y, bounds.width - 1, bounds.height - 1);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentHeader.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.BorderLayout;
+import java.awt.GridLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import javax.swing.JLabel;
+
+/**
+ *
+ */
+public class RangeComponentHeader extends ContentPane {
+
+    private final TimelineModel model;
+    private final UIDefaults defaults;
+    private ContentPane controls;
+
+    public RangeComponentHeader(TimelineModel model, UIDefaults defaults) {
+        this.model = model;
+        this.defaults = defaults;
+    }
+
+    public void initComponents() {
+        controls = new ContentPane();
+        controls.setLayout(new GridLayout(1, 0, 5, 5));
+
+        JLabel zoomOut = new JLabel(new FontAwesomeIcon('\uf066', 15,
+                                                        defaults.getIconColor()));
+        zoomOut.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                double ratio = model.getMagnificationRatio();
+                model.setMagnificationRatio(ratio/2);
+            }
+        });
+        controls.add(zoomOut);
+
+        JLabel restoreZoom = new JLabel(new FontAwesomeIcon('\uf03b', 15,
+                                                        defaults.getIconColor()));
+        restoreZoom.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                model.setMagnificationRatio(TimelineModel.DEFAULT_RATIO);
+            }
+        });
+        controls.add(restoreZoom);
+
+        JLabel zoomIn = new JLabel(new FontAwesomeIcon('\uf065', 15,
+                                                       defaults.getIconColor()));
+        zoomIn.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                double ratio = model.getMagnificationRatio();
+                model.setMagnificationRatio(ratio*2);
+            }
+        });
+        controls.add(zoomIn);
+
+        add(controls, BorderLayout.EAST);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentLayoutManager.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.client.swing.components.AbstractLayout;
+import com.redhat.thermostat.common.model.LongRangeNormalizer;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangedTimelineProbe;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.Container;
+import java.awt.Dimension;
+
+/**
+ *
+ */
+class RangeComponentLayoutManager extends AbstractLayout {
+
+    private static final int STATE_COMPONENT_HEIGHT = 5;
+
+    private static final boolean DEBUG_TIMELINES = false;
+
+    @Override
+    protected void doLayout(Container parent) {
+        TimelineContainer container = (TimelineContainer) parent;
+
+        Dimension size = getRealLayoutSize(container);
+        TimelineModel model = container.getModel();
+
+        LongRangeNormalizer normalizer = new LongRangeNormalizer(model.getRange());
+
+        normalizer.setMinNormalized(0);
+        normalizer.setMaxNormalized(size.width);
+
+        if (DEBUG_TIMELINES) System.err.print(container.getName());
+
+        for (RangeComponent rangeComponent : container) {
+
+            RangedTimelineProbe info = rangeComponent.getInfo();
+            Range<Long> range = info.getRange();
+            int x = (int) normalizer.getValueNormalized(range.getMin());
+            int width = (int) normalizer.getValueNormalized(range.getMax()) - x + 1;
+
+            rangeComponent.setBounds(x, 0, width, 5);
+
+            if (DEBUG_TIMELINES) System.err.print(" [" + range.getMin() +
+                                                  " - " + range.getMax() + "]");
+        }
+
+        if (DEBUG_TIMELINES) System.err.println("");
+    }
+
+    @Override
+    public Dimension preferredLayoutSize(Container parent) {
+        return getRealLayoutSize(parent);
+    }
+
+    public Dimension getRealLayoutSize(Container parent) {
+        TimelineContainer container = (TimelineContainer) parent;
+
+        TimelineModel model = container.getModel();
+
+        Range<Long> range = model.getRange();
+
+        double length = range.getMax() - range.getMin();
+        double multiplier = model.getMagnificationRatio();
+
+        return new Dimension((int) Math.round(length * multiplier),
+                             STATE_COMPONENT_HEIGHT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RulerComponent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.client.swing.GraphicsUtils;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.thread.client.swing.experimental.components.DataPane;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+
+/**
+ *
+ */
+public class RulerComponent extends DataPane {
+
+    protected class RenderingInfo {
+        long startRendering;
+        long stopRendering;
+
+        long startMark;
+        long increment;
+    }
+
+    protected final UIDefaults uiDefaults;
+    protected TimelineModel model;
+    private boolean selected;
+
+    public RulerComponent(UIDefaults defaults, TimelineModel model) {
+
+        super(Palette.LIGHT_GRAY, Palette.WHITE);
+
+        this.uiDefaults = defaults;
+        this.model = model;
+        setFont(defaults.getDefaultFont().deriveFont(Font.PLAIN, 10.f));
+        setName("ruler");
+    }
+
+    public boolean isSelected() {
+        return selected;
+    }
+
+    public void setSelected(boolean selected) {
+        this.selected = selected;
+    }
+
+    @Override
+    public Dimension getPreferredSize() {
+        Dimension pref = super.getPreferredSize();
+        pref.height = getHeight();
+        return pref;
+    }
+
+    @Override
+    public Dimension getMinimumSize() {
+        return getPreferredSize();
+    }
+
+    @Override
+    public Dimension getSize() {
+        return getPreferredSize();
+    }
+
+    private RenderingInfo getRenderingInfo(Rectangle bounds) {
+
+        RenderingInfo info = new RenderingInfo();
+        info.stopRendering = bounds.x + bounds.width;
+        info.increment = 10;
+
+        long start = model.getRange().getMin();
+        long visibleStartTime = start + model.getScrollBarModel().getValue();
+
+        // small mark, 1 second boundary
+        long mark = visibleStartTime - (1_000 * (visibleStartTime/1_000));
+        info.startRendering = -mark;
+
+        return info;
+    }
+
+    @Override
+    protected void paintComponent(Graphics g) {
+        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
+
+        Rectangle bounds = graphics.getClipBounds();
+        if (!isSelected()) {
+            super.paintComponent(g);
+        } else {
+            graphics.setPaint(uiDefaults.getSelectedComponentBGColor());
+            graphics.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+        }
+
+        Color lines = Palette.PALE_GRAY.getColor();
+        Color tickLines = Palette.DARK_GRAY.getColor();
+
+        RenderingInfo info = getRenderingInfo(bounds);
+        int height = bounds.y + bounds.height;
+
+        boolean resetColor = false;
+        long mark = 10l;
+
+        graphics.setColor(lines);
+        for (int i = (int) info.startRendering, j = (int) info.startMark;
+             i < info.stopRendering; i += info.increment, j++)
+        {
+            if (j % mark == 0) {
+                graphics.setColor(tickLines);
+                resetColor = true;
+            }
+
+            graphics.drawLine(i, bounds.y, i, height);
+
+            if (resetColor) {
+                graphics.setColor(lines);
+                resetColor = false;
+            }
+        }
+
+        // TODO
+//        if (drawLabels) {
+//            drawLabels(graphics, info);
+//        }
+
+        graphics.dispose();
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/SwingTimelineDimensionModel.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-
-/**
- */
-public class SwingTimelineDimensionModel implements TimelineDimensionModel {
-
-    /**
-     * Default increment is 20 pixels per units.
-     * Subclasses may use different values.
-     *
-     * @see #DEFAULT_INCREMENT_IN_MILLIS
-     */
-    public static final int DEFAULT_INCREMENT_IN_PIXELS = 20;
-
-    /**
-     * Default increments is 1 second (1000 ms) per pixel unit.
-     * Subclasses may use different values.
-     *
-     * @see #DEFAULT_INCREMENT_IN_PIXELS
-     */
-    public static final long DEFAULT_INCREMENT_IN_MILLIS = 1_000;
-
-    private PropertyChangeSupport propertyChangeSupport;
-    
-    private int width;
-    private int incrementInPixels = DEFAULT_INCREMENT_IN_PIXELS;
-    private long incrementInMillis = DEFAULT_INCREMENT_IN_MILLIS;
-    
-    private volatile long length;
-    
-    public SwingTimelineDimensionModel() {
-        propertyChangeSupport = new PropertyChangeSupport(this);
-    }
-    
-    public void setWidth(int width) {
-        this.width = width;
-
-        computeLength();
-    }
-
-    public int getWidth() {
-        return width;
-    }
-
-    public void setIncrement(int pixels, long millis) {
-        this.incrementInMillis = millis;
-        this.incrementInPixels = pixels;
-
-        computeLength();
-    }
-
-    public int getIncrementInPixels() {
-        return incrementInPixels;
-    }
-
-    public long getIncrementInMillis() {
-        return incrementInMillis;
-    }
-
-    public int getLengthInPixels() {
-        return width / incrementInPixels;
-    }
-
-    private void computeLength() {
-
-        long oldLength = length;
-
-        int lengthInPixels = getLengthInPixels();
-        length = incrementInMillis * lengthInPixels;
-
-        propertyChangeSupport.firePropertyChange(LENGTH_PROPERTY, oldLength, length);
-    }
-    
-    @Override
-    public long getLengthInMillis() {
-        return length;
-    }
-    
-    @Override
-    public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
-        propertyChangeSupport.addPropertyChangeListener(property, listener);
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/ThreadTimelineHeader.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- * 
- * This file is part of Thermostat.
- * 
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- * 
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- * 
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- * 
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.ui.Palette;
-import java.awt.GradientPaint;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Rectangle;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- *
- */
-@SuppressWarnings("serial")
-public class ThreadTimelineHeader extends TimelineBaseComponent {
-
-    public ThreadTimelineHeader(TimelineGroupThreadConverter timelinePageModel,
-                                SwingTimelineDimensionModel dimensionModel)
-    {
-        super(timelinePageModel, dimensionModel);
-        setBorder(new TimelineBorder(false));
-    }
-
-    @Override
-    protected void paintComponent(Graphics g) {
-        super.paintComponent(g);
-
-        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
-
-        Rectangle bounds = graphics.getClipBounds();
-        int x = getRoundedStartingX(bounds);
-        int pixelIncrement = dimensionModel.getIncrementInPixels();
-        long increment = dimensionModel.getIncrementInMillis();
-
-        int upperBound = bounds.x + bounds.width;
-
-        Paint gradient = new GradientPaint(0, 0, Palette.WHITE.getColor(), 0, getHeight(), Palette.GRAY.getColor());
-        int height = getHeight();
-
-        DateFormat df = new SimpleDateFormat("HH:mm:ss");
-
-        long currentTime = timelinePageModel.getDataModel().getPageRange().getMin() - getRound();
-
-        for (int i = x, j = 0; i < upperBound; i += pixelIncrement, j++) {
-            if (j % 10 == 0) {
-                graphics.setColor(Palette.THERMOSTAT_BLU.getColor());
-                graphics.drawLine(i, 0, i, height);
-
-                graphics.setPaint(gradient);
-
-                String value = df.format(new Date(currentTime));
-
-                int stringWidth = (int) getFont().getStringBounds(value, graphics.getFontRenderContext()).getWidth() - 1;
-                int stringHeight = (int) getFont().getStringBounds(value, graphics.getFontRenderContext()).getHeight();
-                graphics.fillRect(i + 1, bounds.y + 5, stringWidth + 4, stringHeight + 4);
-
-                graphics.setColor(Palette.THERMOSTAT_BLU.getColor());
-                graphics.drawString(value, i + 1, bounds.y + stringHeight + 5);
-            }
-            currentTime += increment;
-        }
-
-        graphics.dispose();
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineAdjustmentListener.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import javax.swing.BoundedRangeModel;
+
+/**
+ *
+ */
+public class TimelineAdjustmentListener implements AdjustmentListener {
+
+    private TimelineModel model;
+    private boolean followMode;
+
+    public TimelineAdjustmentListener(TimelineModel model) {
+        this.model = model;
+    }
+
+    @Override
+    public void adjustmentValueChanged(AdjustmentEvent e) {
+        BoundedRangeModel scrollBarModel = model.getScrollBarModel();
+        if (scrollBarModel.getValueIsAdjusting()) {
+            followMode = false;
+            return;
+        }
+
+        int max = scrollBarModel.getMaximum();
+        int currentExtent = scrollBarModel.getValue() +
+                scrollBarModel.getExtent();
+
+        if (currentExtent == max) {
+            followMode = true;
+        }
+
+        if (followMode) {
+            int value = max - scrollBarModel.getExtent();
+            scrollBarModel.setValue(value);
+        }
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineBaseComponent.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.swing.components.GradientPanel;
-import com.redhat.thermostat.client.ui.Palette;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-
-/**
- */
-public class TimelineBaseComponent extends GradientPanel {
-
-    public static final Font FONT = new Font("SansSerif", Font.PLAIN, 10);
-
-    private boolean selected;
-
-    /**
-     * This value defines the gap between two labels, in terms of units
-     * (i.e. every TIME_GAP_BETWEEN_LABELS there will be a label displayed).
-     */
-    static final long TIME_GAP_BETWEEN_LABELS = 10;
-
-    static final int FIXED_HEIGHT = 20;
-
-    protected TimelineGroupThreadConverter timelinePageModel;
-    protected SwingTimelineDimensionModel dimensionModel;
-
-    public TimelineBaseComponent(TimelineGroupThreadConverter timelinePageModel,
-                                 SwingTimelineDimensionModel dimensionModel)
-    {
-        super(Palette.LIGHT_GRAY.getColor(), Palette.WHITE.getColor());
-        setFont(FONT);
-
-        this.timelinePageModel = timelinePageModel;
-        this.dimensionModel = dimensionModel;
-
-        this.selected = false;
-    }
-
-    protected long getRound() {
-        long min = timelinePageModel.getDataModel().getPageRange().getMin();
-        long timeIncrement = dimensionModel.getIncrementInMillis();
-        return min % (TIME_GAP_BETWEEN_LABELS * timeIncrement);
-    }
-
-    protected int getRoundedStartingX(Rectangle bounds) {
-
-        long timeIncrement = dimensionModel.getIncrementInMillis();
-        long round = getRound();
-        long roundDelta = round / timeIncrement;
-
-        int pixelIncrement = dimensionModel.getIncrementInPixels();
-        long pixelDelta = roundDelta * pixelIncrement;
-
-        return (int) (bounds.x - pixelDelta);
-    }
-
-    @Override
-    protected void paintComponent(Graphics g) {
-
-        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
-        Rectangle bounds = graphics.getClipBounds();
-
-        Color lines = Palette.GRAY.getColor();
-        Color tickLines = Palette.THERMOSTAT_BLU.getColor();
-
-        if (!isSelected()) {
-            super.paintComponent(g);
-        } else {
-            graphics.setColor(Palette.ADWAITA_BLU.getColor());
-            graphics.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
-
-            lines = Palette.DARK_GRAY.getColor();
-            tickLines = Palette.ROYAL_BLUE.getColor();
-        }
-
-        int pixelIncrement = dimensionModel.getIncrementInPixels();
-        int x = getRoundedStartingX(bounds);
-        int upperBound = bounds.x + bounds.width;
-
-        graphics.setColor(Palette.GRAY.getColor());
-
-        boolean resetColor = false;
-        for (int i = x, j = 0; i < upperBound; i += pixelIncrement, j++) {
-            if (j % 10 == 0) {
-                graphics.setColor(tickLines);
-                resetColor = true;
-            }
-
-            graphics.drawLine(i, 0, i, bounds.height);
-
-            if (resetColor) {
-                graphics.setColor(lines);
-                resetColor = false;
-            }
-        }
-
-        graphics.dispose();
-    }
-
-    @Override
-    public int getHeight() {
-        return FIXED_HEIGHT;
-    }
-
-    @Override
-    public Dimension getPreferredSize() {
-        Dimension pref = super.getPreferredSize();
-        pref.height = getHeight();
-        return pref;
-    }
-
-    public boolean isSelected() {
-        return selected;
-    }
-
-    public void setSelected(boolean selected) {
-        this.selected = selected;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineBorder.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import com.redhat.thermostat.client.swing.components.DebugBorder;
-import java.awt.Component;
-import java.awt.Graphics;
-import java.awt.Insets;
-
-/**
- */
-class TimelineBorder extends DebugBorder {
-    private boolean isOpaque;
-
-    public TimelineBorder() {
-        this(false);
-    }
-
-    public TimelineBorder(boolean opaque) {
-        this.isOpaque = opaque;
-    }
-
-    @Override
-    public boolean isBorderOpaque() {
-        return isOpaque;
-    }
-
-    @Override
-    public void paintBorder(Component c, Graphics g, int x, int y, int width,
-                            int height)
-    {
-        g.setColor(c.getForeground());
-        g.drawLine(0, c.getHeight() - 1, c.getWidth() - 1, c.getHeight() - 1);
-    }
-
-    @Override
-    public Insets getBorderInsets(Component c, Insets insets) {
-
-        insets.top = 0;
-        insets.left = 0;
-        insets.right = 0;
-        insets.bottom = 1;
-
-        return insets;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineCellRenderer.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.ListCellRenderer;
-
-@SuppressWarnings("serial")
-public class TimelineCellRenderer extends JPanel implements ListCellRenderer<TimelineComponent> {
-
-    @Override
-    public TimelineComponent getListCellRendererComponent(JList<? extends TimelineComponent> list,
-                                                          TimelineComponent value,
-                                                          int index, boolean isSelected,
-                                                          boolean cellHasFocus)
-    {
-        value.setSelected(isSelected);
-        return value;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineComponent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,59 +36,98 @@
 
 package com.redhat.thermostat.thread.client.swing.impl.timeline;
 
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.common.model.LongRangeNormalizer;
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.model.timeline.Timeline;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineInfo;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.experimental.components.Separator;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
 import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
 
-@SuppressWarnings("serial")
-public class TimelineComponent extends TimelineBaseComponent {
+/**
+ *
+ */
+public class TimelineComponent extends RulerComponent {
 
-    private static final int MIN_HEIGHT = 42;
+    private static final int MIN_HEIGHT = 50;
+    private final ThreadInfo threadInfo;
+
+    private ContentPane labelPane;
 
     private TimelineLabel label;
-    private Timeline timeline;
 
-    public TimelineComponent(TimelineGroupThreadConverter timelinePageModel,
-                             SwingTimelineDimensionModel dimensionModel,
-                             String name)
+    private ThermostatScrollPane scrollPane;
+    private TimelineContainer timelineContainer;
+
+    public TimelineComponent(UIDefaults uiDefaults, ThreadInfo threadInfo,
+                             TimelineModel model)
     {
-        // TODO: get those from color properties
-        super(timelinePageModel, dimensionModel);
+        super(uiDefaults, model);
+        this.threadInfo = threadInfo;
+    }
+
+    public void initComponents() {
+        setName(threadInfo.getName());
+
+        initModel();
 
-        setLayout(new BorderLayout());
-        label = new TimelineLabel(name);
-        add(label, BorderLayout.LINE_START);
+        initLabelPane();
+        initThreadPane();
+
+        setBorder(new Separator(uiDefaults, Separator.Side.BOTTOM,
+                                Separator.Type.SOLID));
+
+        Hover hover = new Hover();
+        addMouseListener(hover);
     }
 
-    @Override
-    protected void paintComponent(Graphics g) {
-        super.paintComponent(g);
+    private void initModel() {
+        model.getScrollBarModel().addChangeListener(new ChangeListener() {
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                repaint();
+            }
+        });
+    }
 
-        Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
+    private void initThreadPane() {
+
+        timelineContainer = new TimelineContainer(model);
+        timelineContainer.setName(threadInfo.getName());
 
-        Range<Long> pageRange = timelinePageModel.getDataModel().getPageRange();
+        scrollPane = new ThermostatScrollPane(timelineContainer);
+        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_NEVER);
 
-        int y = MIN_HEIGHT / 2;
+        add(scrollPane, BorderLayout.CENTER);
 
-        LongRangeNormalizer normalizer = new LongRangeNormalizer(pageRange, 0, getWidth());
-        if (timeline != null) {
-            for (TimelineInfo sample : timeline) {
-                graphics.setColor(sample.getColor().getColor());
-                Range<Long> sampleRange = sample.getRange();
+        scrollPane.getHorizontalScrollBar().setModel(model.getScrollBarModel());
+    }
+
+    public TimelineContainer getTimelineContainer() {
+        return timelineContainer;
+    }
+
+    private void initLabelPane() {
+        label = new TimelineLabel(uiDefaults, getName());
+        labelPane = new ContentPane();
 
-                int x0 = (int) normalizer.getValueNormalized(sampleRange.getMin());
-                int x1 = (int) normalizer.getValueNormalized(sampleRange.getMax());
+        labelPane.setOpaque(false);
+        labelPane.setLayout(new GridBagLayout());
 
-                graphics.fillRect(x0, y, x1 - x0 + 1, 5);
-            }
-        }
-        graphics.dispose();
+        GridBagConstraints gbc = new GridBagConstraints();
+        gbc.anchor = GridBagConstraints.WEST;
+        gbc.fill = GridBagConstraints.NONE;
+        gbc.weightx = 1.f;
+
+        labelPane.add(label, gbc);
+        add(labelPane, BorderLayout.NORTH);
     }
 
     @Override
@@ -96,32 +135,20 @@
         return MIN_HEIGHT;
     }
 
-    @Override
-    public Dimension getPreferredSize() {
-        Dimension pref = super.getPreferredSize();
-        pref.height = getHeight();
-        return pref;
-    }
+    private class Hover extends MouseAdapter {
+        @Override
+        public void mouseEntered(MouseEvent e) {
+            onMouseHover(true);
+        }
 
-    @Override
-    public Dimension getMinimumSize() {
-        return getPreferredSize();
-    }
+        @Override
+        public void mouseExited(MouseEvent e) {
+            onMouseHover(false);
+        }
 
-    @Override
-    public Dimension getSize() {
-        return getPreferredSize();
-    }
-
-    public void setTimeline(Timeline timeline) {
-        this.timeline = timeline;
-        if (timeline != null && timeline.size() > 0) {
-            label.setForeground(timeline.last().getColor().getColor());
+        public void onMouseHover(boolean hover) {
+            label.onMouseHover(hover);
+            repaint();
         }
     }
-
-    public Timeline getTimeline() {
-        return timeline;
-    }
 }
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineContainer.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangeChangeEvent;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangeChangeListener;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RatioChangeEvent;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RatioChangeListener;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.Component;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ *
+ */
+public class TimelineContainer extends ContentPane implements Iterable<RangeComponent> {
+
+    private List<RangeComponent> rangeComponents;
+    private TimelineModel model;
+
+    public TimelineContainer(TimelineModel model) {
+        rangeComponents  = new ArrayList<>();
+
+        setOpaque(false);
+        setLayout(new RangeComponentLayoutManager());
+
+        setModel(model);
+    }
+
+    public Component add(RangeComponent comp) {
+        rangeComponents.add(comp);
+        return super.add(comp);
+    }
+
+    @Override
+    public Iterator<RangeComponent> iterator() {
+        return rangeComponents.iterator();
+    }
+
+    public TimelineModel getModel() {
+        return model;
+    }
+
+    public void setModel(TimelineModel model) {
+        this.model = model;
+        model.addRangeChangeListener(new RangeChangeListener() {
+            @Override
+            public void rangeChanged(RangeChangeEvent event) {
+                revalidate();
+                repaint();
+            }
+        });
+        model.addRatioChangeListener(new RatioChangeListener() {
+            @Override
+            public void ratioChanged(RatioChangeEvent event) {
+                revalidate();
+                repaint();
+            }
+        });
+    }
+
+    public RangeComponent getLastRangeComponent() {
+        return rangeComponents.isEmpty() ? null :
+               rangeComponents.get(rangeComponents.size() - 1);
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineGroupThreadConverter.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineGroupDataModel;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.beans.PropertyChangeSupport;
-import javax.swing.SwingUtilities;
-
-/*
- */
-public class TimelineGroupThreadConverter implements PropertyChangeListener {
-
-    private TimelineGroupDataModel source;
-    private PropertyChangeSupport propertyChangeSupport;
-
-    public TimelineGroupThreadConverter(TimelineGroupDataModel source) {
-
-        this.source = source;
-        propertyChangeSupport = new PropertyChangeSupport(this);
-
-        source.addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty.PAGE_RANGE, this);
-        source.addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty.TOTAL_RANGE, this);
-    }
-
-    @Override
-    public void propertyChange(final PropertyChangeEvent evt) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                propertyChangeSupport.firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
-            }
-        });
-    }
-
-    public void addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty property, PropertyChangeListener listener) {
-        propertyChangeSupport.addPropertyChangeListener(property.name(), listener);
-    }
-
-    public TimelineGroupDataModel getDataModel() {
-        return source;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineLabel.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineLabel.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,73 +36,52 @@
 
 package com.redhat.thermostat.thread.client.swing.impl.timeline;
 
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.swing.components.experimental.TimelineUtils;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.swing.components.LabelField;
 import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.thread.client.swing.experimental.components.DataPane;
+import com.redhat.thermostat.thread.client.swing.experimental.components.Separator;
 import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.GradientPaint;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
+import javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
 
 /**
+ *
  */
-public class TimelineLabel extends JPanel {
+class TimelineLabel extends DataPane {
+    private LabelField nameLabel;
+    private Icon infoOn;
+    private Icon infoOff;
 
-    private String text;
+    TimelineLabel(UIDefaults defaults, String text) {
+        super(Palette.WHITE, Palette.LIGHT_GRAY);
+
+        setBorder(new Separator(defaults, Separator.Side.BOTTOM, Separator.Type.SOLID));
 
-    public TimelineLabel(String text) {
-        setOpaque(false);
-        setBorder(new TimelineBorder(true));
+        nameLabel = new LabelField(LocalizedString.EMPTY_STRING);
+        nameLabel.setFont(defaults.getDefaultFont().deriveFont(10.f));
 
-        this.text = text;
+        nameLabel.setText(text);
+        nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        nameLabel.setVerticalAlignment(SwingConstants.CENTER);
+        nameLabel.setForeground((Color) defaults.getSelectedComponentBGColor());
+        nameLabel.setBorder(new EmptyBorder(2, 2, 2, 2));
 
-        JLabel nameLabel = new JLabel(text);
-        nameLabel.setFont(TimelineUtils.FONT);
-        nameLabel.setForeground(Palette.THERMOSTAT_BLU.getColor());
+        nameLabel.setHorizontalTextPosition(SwingConstants.LEFT);
+
+        infoOn = new FontAwesomeIcon('\uf05a', 12, Palette.DARK_GRAY.getColor());
+        infoOff = new FontAwesomeIcon('\uf05a', 12, Palette.PALE_GRAY.getColor());
+
+        nameLabel.setIcon(infoOff);
 
         add(nameLabel);
     }
 
-    @Override
-    public int getHeight() {
-        return 20;
-    }
-
-    @Override
-    public Dimension getPreferredSize() {
-        Dimension dim = super.getPreferredSize();
-        dim.height = getHeight();
-        return dim;
-    }
-
-    @Override
-    public Dimension getSize() {
-        return getPreferredSize();
-    }
-
-    @Override
-    protected void paintComponent(Graphics g) {
-
-        GraphicsUtils utils = GraphicsUtils.getInstance();
-
-        Graphics2D graphics = utils.createAAGraphics(g);
-
-        Color up = utils.deriveWithAlpha(Palette.WHITE.getColor(), 200);
-        Color bottom = utils.deriveWithAlpha(Palette.GRAY.getColor(), 200);
-
-        Paint gradient = new GradientPaint(0, 0, up, 0, getHeight(), bottom);
-        graphics.setPaint(gradient);
-
-        graphics.fillRect(0, 0, getWidth(), getHeight());
-
-        graphics.dispose();
-    }
-
-    public String getText() {
-        return text;
+    public void onMouseHover(boolean hover) {
+        nameLabel.setIcon(hover ? infoOn : infoOff);
+        repaint();
     }
 }
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineRangeModelFormatter.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * Formatter class for {@link TimelineRange}
- */
-public class TimelineRangeModelFormatter {
-
-    private static DateFormat df = new SimpleDateFormat("yyyy.MM.dd, HH:mm:ss");
-
-    public static String getFormattedString(long range) {
-        if (range < 0) {
-            return " - ";
-        }
-        return df.format(new Date(range));
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineViewComponent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollBar;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangeChangeEvent;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RangeChangeListener;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RatioChangeEvent;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.RatioChangeListener;
+import com.redhat.thermostat.thread.client.swing.impl.timeline.model.TimelineModel;
+import java.awt.BorderLayout;
+import java.awt.Rectangle;
+
+/**
+ *
+ */
+public class TimelineViewComponent extends ContentPane {
+
+    private TimelineModel model;
+    private ThermostatScrollPane scrollPane;
+    private TimelineViewport viewport;
+
+    private ThermostatScrollBar scrollBar;
+    private UIDefaults uiDefaults;
+
+    public TimelineViewComponent(UIDefaults uiDefaults) {
+        this.uiDefaults = uiDefaults;
+    }
+
+    public void initComponents() {
+
+        this.model = new TimelineModel();
+
+        viewport = new TimelineViewport();
+
+        scrollPane = new ThermostatScrollPane(viewport);
+        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+        RangeComponentHeader header = new RangeComponentHeader(model, uiDefaults);
+        header.initComponents();
+        scrollPane.setColumnHeaderView(header);
+
+        add(scrollPane, BorderLayout.CENTER);
+
+        scrollBar = new ThermostatScrollBar(ThermostatScrollBar.HORIZONTAL);
+        model.setScrollBarModel(scrollBar.getModel());
+
+        scrollBar.addAdjustmentListener(new TimelineAdjustmentListener(model));
+
+        add(scrollBar, BorderLayout.SOUTH);
+        scrollBar.setEnabled(false);
+        scrollBar.setVisible(false);
+
+        model.addRatioChangeListener(new RatioChangeListener() {
+            @Override
+            public void ratioChanged(RatioChangeEvent event) {
+                checkEnableScrollbar();
+            }
+        });
+        model.addRangeChangeListener(new RangeChangeListener() {
+            @Override
+            public void rangeChanged(RangeChangeEvent event) {
+                checkEnableScrollbar();
+            }
+        });
+    }
+
+    private void checkEnableScrollbar() {
+        Range<Long> range = model.getRange();
+        Rectangle bounds = getBounds();
+        long length = bounds.x + bounds.width;
+        length = Math.round(length / model.getMagnificationRatio());
+
+        long lengthInMs = range.getMax() - range.getMin();
+        lengthInMs = Math.round(lengthInMs / model.getMagnificationRatio());
+
+        boolean shouldEnable = (length < lengthInMs);
+
+        scrollBar.setVisible(shouldEnable);
+        scrollBar.setEnabled(shouldEnable);
+    }
+
+    public void addTimeline(TimelineComponent timeline) {
+        viewport.add(timeline);
+        checkEnableScrollbar();
+
+        timeline.
+
+        revalidate();
+        repaint();
+    }
+
+    public TimelineModel getModel() {
+        return model;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineViewport.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline;
+
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import javax.swing.BoxLayout;
+import javax.swing.Scrollable;
+
+/**
+ *
+ */
+public class TimelineViewport extends ContentPane implements Scrollable {
+
+    public TimelineViewport() {
+        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+    }
+
+    @Override
+    public Dimension getPreferredScrollableViewportSize() {
+        return getPreferredSize();
+    }
+
+    @Override
+    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
+        return 1;
+    }
+
+    @Override
+    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
+        return 1;
+    }
+
+    @Override
+    public boolean getScrollableTracksViewportWidth() {
+        return true;
+    }
+
+    @Override
+    public boolean getScrollableTracksViewportHeight() {
+        return false;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeEvent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import com.redhat.thermostat.common.model.Range;
+import java.util.EventObject;
+
+/**
+ *
+ */
+public class RangeChangeEvent extends EventObject {
+
+    private final TimelineModel source;
+    private final Range<Long> range;
+
+    public RangeChangeEvent(TimelineModel source, Range<Long> range) {
+        super(source);
+        this.source = source;
+        this.range = range;
+    }
+
+    @Override
+    public TimelineModel getSource() {
+        return (TimelineModel) super.getSource();
+    }
+
+    public Range<Long> getRange() {
+        return range;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeListener.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import java.util.EventListener;
+
+/**
+ *
+ */
+public interface RangeChangeListener extends EventListener {
+
+    public void rangeChanged(RangeChangeEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangedTimelineProbe.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
+
+/**
+ *
+ */
+public class RangedTimelineProbe extends TimelineProbe {
+
+    private long probeEnd;
+
+    public RangedTimelineProbe(TimelineProbe probe, long probeStop)
+    {
+        super(probe.getColor(), probe.getState(), probe.getTimeStamp());
+        this.probeEnd = probeStop;
+    }
+
+    public void setProbeEnd(long probeEnd) {
+        this.probeEnd = probeEnd;
+    }
+
+    public Range<Long> getRange() {
+        return new Range<>(getTimeStamp(), probeEnd);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeEvent.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import java.util.EventObject;
+
+/**
+ *
+ */
+public class RatioChangeEvent extends EventObject {
+    private final TimelineModel source;
+    private final double ratio;
+
+    public RatioChangeEvent(TimelineModel source, double ratio) {
+        super(source);
+        this.source = source;
+        this.ratio = ratio;
+    }
+
+    @Override
+    public TimelineModel getSource() {
+        return (TimelineModel) super.getSource();
+    }
+
+    public double getRatio() {
+        return ratio;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeListener.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import java.util.EventListener;
+
+/**
+ *
+ */
+public interface RatioChangeListener extends EventListener {
+    public void ratioChanged(RatioChangeEvent event);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineDateFormatter.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ *
+ */
+public class TimelineDateFormatter {
+    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss.SSS");
+
+    public static String format(long timeStamp) {
+        return DATE_FORMAT.format(new Date(timeStamp));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineModel.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl.timeline.model;
+
+import com.redhat.thermostat.common.model.Range;
+import javax.swing.BoundedRangeModel;
+import javax.swing.event.EventListenerList;
+
+/**
+ *
+ */
+public class TimelineModel {
+
+    public static final double DEFAULT_RATIO = 1./100.;
+
+    private EventListenerList listenerList;
+
+    private double magnificationRatio;
+
+    private Range<Long> range;
+    private BoundedRangeModel scrollBarModel;
+
+    private TimelineModel model;
+
+    public TimelineModel() {
+        this.listenerList = new EventListenerList();
+        magnificationRatio = DEFAULT_RATIO;
+    }
+
+    public Range<Long> getRange() {
+        return range;
+    }
+
+    public void setRange(Range<Long> range) {
+        this.range = range;
+        fireRangeChangeEvent();
+    }
+
+    public void setScrollBarModel(BoundedRangeModel scrollBarModel) {
+        this.scrollBarModel = scrollBarModel;
+    }
+
+    public BoundedRangeModel getScrollBarModel() {
+        return scrollBarModel;
+    }
+
+    public void addRangeChangeListener(RangeChangeListener listener) {
+        listenerList.add(RangeChangeListener.class, listener);
+    }
+
+    public void addRatioChangeListener(RatioChangeListener listener) {
+        listenerList.add(RatioChangeListener.class, listener);
+    }
+
+    public void removeRatioChangeListener(RatioChangeListener listener) {
+        listenerList.remove(RatioChangeListener.class, listener);
+    }
+
+    public void removeRangeChangeListener(RangeChangeListener listener) {
+        listenerList.remove(RangeChangeListener.class, listener);
+    }
+
+    public double getMagnificationRatio() {
+        return magnificationRatio;
+    }
+
+    public void setMagnificationRatio(double magnificationRatio) {
+        this.magnificationRatio = magnificationRatio;
+        fireRatioChangeEvent();
+    }
+
+    private void fireRatioChangeEvent() {
+        Object[] listeners = listenerList.getListenerList();
+        RatioChangeEvent event =
+                new RatioChangeEvent(this, magnificationRatio);
+        for (int i = listeners.length - 2; i >= 0; i -= 2) {
+            if (listeners[i] == RatioChangeListener.class) {
+                ((RatioChangeListener) listeners[i + 1]).ratioChanged(event);
+            }
+        }
+    }
+
+    private void fireRangeChangeEvent() {
+        Object[] listeners = listenerList.getListenerList();
+        RangeChangeEvent event =
+                new RangeChangeEvent(this,
+                                     new Range<>(range.getMin(),
+                                                 range.getMax()));
+        for (int i = listeners.length - 2; i >= 0; i -= 2) {
+            if (listeners[i] == RangeChangeListener.class) {
+                ((RangeChangeListener) listeners[i + 1]).rangeChanged(event);
+            }
+        }
+    }
+}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/SwingTimelineScrollBarController.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar;
-
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineGroupDataModel;
-import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-import com.redhat.thermostat.thread.client.swing.impl.SwingThreadTimelineView;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.TimelineGroupThreadConverter;
-import java.awt.event.AdjustmentEvent;
-import java.awt.event.AdjustmentListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-
-public class SwingTimelineScrollBarController implements PropertyChangeListener, AdjustmentListener {
-
-    private static final int MIN_THUMB_PERCENTAGE = 2;
-
-    private boolean followMode;
-
-    private TimelineScrollBar scrollbar;
-    private TimelineGroupThreadConverter groupDataModel;
-    private SwingTimelineDimensionModel dimensionModel;
-    private SwingThreadTimelineView view;
-
-    public SwingTimelineScrollBarController(SwingThreadTimelineView view,
-                                            TimelineScrollBar scrollbar,
-                                            TimelineGroupThreadConverter groupDataModel,
-                                            SwingTimelineDimensionModel dimensionModel)
-    {
-        this.scrollbar = scrollbar;
-        this.groupDataModel = groupDataModel;
-        this.dimensionModel = dimensionModel;
-        this.view = view;
-    }
-
-    public void initScrollbar(SwingThreadTimelineView threadTimelineView) {
-        scrollbar.setEnabled(false);
-        scrollbar.addAdjustmentListener(this);
-        groupDataModel.addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty.TOTAL_RANGE, this);
-        groupDataModel.addPropertyChangeListener(TimelineGroupDataModel.RangeChangeProperty.PAGE_RANGE, this);
-    }
-
-    @Override
-    public void propertyChange(PropertyChangeEvent evt) {
-        if (scrollbar.getValueIsAdjusting()) {
-            // we won't do anything at this point
-            return;
-        }
-
-        Range<Long> pageRange = groupDataModel.getDataModel().getPageRange();
-        Range<Long> totalRange = groupDataModel.getDataModel().getTotalRange();
-
-        double pageSize = (double) (pageRange.getMax() - pageRange.getMin());
-        double totalSize = (double) (totalRange.getMax() - totalRange.getMin());
-        int width = dimensionModel.getWidth();
-
-        // no data, or not everything has been instantiated correctly yet
-        if (totalSize <= 0 || width <= 0) {
-            return;
-        }
-
-        // this happens when the whole data is less than the page size
-        if (totalSize <= pageSize) {
-            // set the whole thing 100% extent, the slider should
-            // not be enabled
-            scrollbar.setEnabled(false);
-            scrollbar.setValues(0, 100, 0, 100);
-
-        } else {
-            // ensure the scrollbar is enabled
-            scrollbar.setEnabled(true);
-
-            int amount = (int) Math.round((pageSize/totalSize) * 100);
-            if (amount < MIN_THUMB_PERCENTAGE) {
-                amount = MIN_THUMB_PERCENTAGE;
-            }
-
-            int value = scrollbar.getValue();
-            int max = scrollbar.getMaximum();
-            if (followMode || (value + amount) >= max) {
-                value = max - amount;
-            }
-            //            scrollbar.setValues(max - amount, amount, 0, 100);
-            scrollbar.setValues(value, amount, 0, 100);
-        }
-    }
-
-    public void ensureTimelineState(ThreadTimelineView.TimelineSelectorState following) {
-        switch (following) {
-            default:
-            case FOLLOWING: {
-
-                followMode = true;
-
-                int amount = scrollbar.getVisibleAmount();
-                int max = scrollbar.getMaximum();
-                int total = max + amount;
-                int currentValue = scrollbar.getValue() + amount;
-                if (currentValue <= total) {
-                    scrollbar.setValue(total);
-                }
-
-            }   break;
-
-            case STATIC:
-
-                followMode = false;
-
-                // there is really nothing else to do here, the scrollbar stays
-                // where it is, this is taken care by the range handlers
-                break;
-
-        }
-    }
-
-    @Override
-    public void adjustmentValueChanged(AdjustmentEvent e) {
-        Object source = e.getSource();
-        if (source instanceof TimelineScrollBar) {
-            TimelineScrollBar scrollBar = (TimelineScrollBar) source;
-            int value = scrollBar.getValue();
-            int visible = scrollBar.getVisibleAmount();
-            int max = scrollBar.getMaximum();
-
-            int currentExtent = value + visible;
-            if (!scrollBar.getValueIsAdjusting() && currentExtent == max) {
-                view.requestFollowMode();
-            } else {
-
-                Range<Long> totalRange = groupDataModel.getDataModel().getTotalRange();
-
-                long totalSize = totalRange.getMax() - totalRange.getMin();
-                long length = dimensionModel.getLengthInMillis();
-
-                long currentStep = Math.round((value * totalSize) / 100);
-
-                // what's the actual range based on current percentage on
-                // display?
-                long start = totalRange.getMin() + currentStep;
-                Range<Long> pageRange = new Range<>(start, start + length);
-                view.requestStaticMode(pageRange);
-            }
-        }
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineButtonUI.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar;
-
-import java.awt.Color;
-import java.awt.GradientPaint;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Rectangle;
-
-import javax.swing.AbstractButton;
-import javax.swing.ButtonModel;
-import javax.swing.Icon;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.plaf.basic.BasicButtonUI;
-
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.ui.Palette;
-
-class TimelineButtonUI extends BasicButtonUI {
-
-    @Override
-    protected void paintFocus(Graphics g, AbstractButton b, Rectangle viewRect,
-                              Rectangle textRect, Rectangle iconRect)
-    {
-        // no focus :)
-    }
-    
-    @Override
-    protected void paintButtonPressed(Graphics g, AbstractButton b) {
-        super.paintButtonPressed(g, b);
-    }
-    
-    @Override
-    public void paint(Graphics g, JComponent c) {
-    
-        JButton button = (JButton) c;
-        
-        GraphicsUtils utils = GraphicsUtils.getInstance();
-        Graphics2D graphics = utils.createAAGraphics(g);
-        Rectangle clip = graphics.getClipBounds();
-          
-        Color top = Palette.WHITE.getColor();
-        Color bottom = Palette.GRAY.getColor();
-    
-        ButtonModel model = button.getModel();
-        if (model.isPressed()) {
-            Color tmp = top;
-            top = bottom;
-            bottom = tmp;
-        }
-          
-        Paint gradient = new GradientPaint(0, 0, top, 0, button.getHeight(), bottom);
-          
-        graphics.setPaint(gradient);
-        graphics.fillRect(clip.x, clip.y, clip.width, clip.height);
-
-        Icon icon = button.getIcon();
-        if (icon != null) {
-            int x = button.getWidth() / 2 - icon.getIconWidth() / 2;
-            int y = button.getHeight() / 2 - icon.getIconHeight() / 2;
-            icon.paintIcon(button, graphics, x, y);
-        }
-
-        super.paint(g, c);
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineScrollBar.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar;
-
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.components.ThermostatThinScrollBar;
-
-@SuppressWarnings("serial")
-public class TimelineScrollBar extends ThermostatThinScrollBar {
-
-    public TimelineScrollBar(UIDefaults uiDefaults) {
-        super(TimelineScrollBar.HORIZONTAL);
-        setUI(new TimelineScrollBarUI(uiDefaults));
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/scrollbar/TimelineScrollBarUI.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline.scrollbar;
-
-import com.redhat.thermostat.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
-import com.redhat.thermostat.client.ui.Palette;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.GradientPaint;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Paint;
-import java.awt.Rectangle;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.border.EmptyBorder;
-import javax.swing.plaf.basic.BasicScrollBarUI;
-
-class TimelineScrollBarUI extends BasicScrollBarUI {
-    
-    static enum ButtonDirection {
-        INCREASE,
-        DECREASE,
-    }
-
-    private UIDefaults uiDefaults;
-    
-    public TimelineScrollBarUI(UIDefaults uiDefaults) {
-        this.uiDefaults = uiDefaults;
-    }
-
-    @SuppressWarnings("serial")
-    private class TimelineButton extends JButton {
-        
-        public TimelineButton(ButtonDirection direction) {
-            
-            switch (direction) {
-                case DECREASE:
-                    setIcon(new FontAwesomeIcon('\uf104', 16, new Color(0, 0, 0, 100)));
-                    break;
-
-                case INCREASE:
-                default:
-                    setIcon(new FontAwesomeIcon('\uf105', 16, new Color(0, 0, 0, 100)));
-                    break;
-            }
-            
-            setOpaque(false);
-            setBorder(new EmptyBorder(5, 5, 5, 5));
-
-            // those should really go somewhere in TimelineUtils or common UI
-            // properties, including the date format below
-            setForeground(Palette.EARL_GRAY.getColor());
-            setUI(new TimelineButtonUI());
-        }
-    }
-
-    @Override
-    protected void installDefaults() {
-        super.installDefaults();
-        
-        scrollBarWidth = 16;
-        incrGap = 0;
-        decrGap = 0;
-        
-        minimumThumbSize = new Dimension(50, 5);
-    }
-    
-    @Override
-    protected JButton createDecreaseButton(int orientation) {
-        return new TimelineButton(ButtonDirection.DECREASE);
-    }
-
-    @Override
-    protected JButton createIncreaseButton(int orientation) {
-        return new TimelineButton(ButtonDirection.INCREASE);
-    }
-
-    @Override
-    protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) {
-        GraphicsUtils utils = GraphicsUtils.getInstance();
-        Graphics2D graphics = utils.createAAGraphics(g);
-        graphics.setColor(Palette.GRAY.getColor());
-        
-        graphics.translate(trackBounds.x, trackBounds.y);
-        
-        int w = trackBounds.width - 1;
-        int h = trackBounds.height - 1;
-        
-        graphics.drawLine(0, h, w, h);
-        
-        for (int i = 0; i < trackBounds.width; i += 10) {
-            graphics.drawLine(i, h - 5, i, h);
-            if (i % 100 == 0) {
-                graphics.drawLine(i, 0, i, h);
-            }
-        }
-
-        graphics.setColor(Palette.EARL_GRAY.getColor());
-        graphics.drawLine(0, 0, 0, h);
-        graphics.drawLine(w, 0, w, h);
-        
-        graphics.dispose();
-    }
-
-    @Override
-    protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) {
-        if(thumbBounds.isEmpty() || !scrollbar.isEnabled())     {
-            return;
-        }
-
-        GraphicsUtils utils = GraphicsUtils.getInstance();
-        Graphics2D graphics = utils.createAAGraphics(g);
-        
-        int w = thumbBounds.width - 1;
-        int h = thumbBounds.height - 1;
-
-        graphics.translate(thumbBounds.x, thumbBounds.y);
-
-        Color top = utils.deriveWithAlpha(Palette.WHITE.getColor(), 50);
-        Color bottom = utils.deriveWithAlpha(Palette.DROID_GRAY.getColor(), 50);
-        Paint gradient = new GradientPaint(0, 0, top, 0, h, bottom);
-        
-        graphics.setPaint(gradient);
-        graphics.fillRect(0, 0, w, h);        
-        
-        graphics.setPaint(uiDefaults.getReferenceFieldIconColor());
-        
-        int y = h/2  + 1;
-        
-        graphics.drawLine(0, y, w, y);
-        graphics.drawLine(0, 0, 0, h);
-        graphics.drawLine(w, 0, w, h);
-        
-        graphics.dispose();
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/osgi/Activator.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/osgi/Activator.java	Thu Dec 11 11:44:42 2014 +0100
@@ -40,9 +40,7 @@
 import com.redhat.thermostat.common.MultipleServiceTracker;
 import com.redhat.thermostat.common.MultipleServiceTracker.Action;
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
-import com.redhat.thermostat.thread.client.common.model.timeline.TimelineDimensionModel;
 import com.redhat.thermostat.thread.client.swing.SwingThreadViewService;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -72,18 +70,12 @@
             
             @Override
             public void dependenciesAvailable(Map<String, Object> services) {
-                
-                SwingTimelineDimensionModel dimensionModel = new SwingTimelineDimensionModel();
-                
+
                 UIDefaults uiDefaults = (UIDefaults) services.get(UIDefaults.class.getName());
                 ServiceRegistration reg = context.registerService(ThreadViewProvider.class.getName(),
-                                              new SwingThreadViewService(uiDefaults, dimensionModel),
+                                              new SwingThreadViewService(uiDefaults),
                                               null);
                 regs.add(reg);
-
-                reg = context.registerService(TimelineDimensionModel.class.getName(),
-                                              dimensionModel, null);
-                regs.add(reg);
             }
         });
         
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
+import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import org.mockito.Mockito;
+
+/**
+ *
+ */
+public class SwingThreadViewDemo {
+
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+
+                UIDefaults defaults = createUIDefaults();
+
+                ContentPane contentPane = new ContentPane();
+
+                SwingThreadTimelineView view = new SwingThreadTimelineView(defaults);
+                JFrame frame = new JFrame();
+                frame.add(contentPane);
+
+                contentPane.add(view.getUiComponent());
+
+                JButton addDate = new JButton("Add Timeline Data");
+
+                addDate.addActionListener(new DataFiller(view));
+                contentPane.add(addDate, BorderLayout.SOUTH);
+
+                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                frame.setSize(new Dimension(800, 800));
+                frame.setVisible(true);
+            }
+        });
+    }
+
+    private static class DataFiller implements ActionListener {
+
+        private ThreadTimelineView view;
+        private long startTime;
+        private long lastUpdate;
+
+        private boolean threadAdded;
+        private int swap;
+        private boolean swapped;
+
+        DataFiller(ThreadTimelineView view) {
+            this.view = view;
+            startTime = System.currentTimeMillis();
+            lastUpdate = startTime;
+            swap = 0;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            lastUpdate = System.currentTimeMillis();
+            final Range<Long> range = new Range<>(startTime, lastUpdate);
+
+            SwingWorker<Void, Void> worker  = new SwingWorker<Void, Void>() {
+                @Override
+                protected Void doInBackground() throws Exception {
+
+                    //System.err.println("range: " + (range.getMax() - range.getMin()));
+
+                    view.setTotalRange(range);
+
+                    if (!threadAdded) {
+                        ThreadInfo info = new ThreadInfo();
+                        info.setName("test1");
+                        info.setId(0l);
+                        view.addThread(info);
+
+                        info = new ThreadInfo();
+                        info.setName("test2");
+                        info.setId(1l);
+                        view.addThread(info);
+
+                        threadAdded = true;
+
+                    } else {
+
+                        Palette color1 = Palette.THERMOSTAT_BLU;
+                        Palette color2 = Palette.THERMOSTAT_RED;
+
+                        if (swapped) {
+                            color1 = Palette.THERMOSTAT_RED;
+                            color2 = Palette.THERMOSTAT_BLU;
+                        }
+
+                        swap++;
+                        if (swap % 10 == 0) {
+                            swapped = !swapped;
+                            swap = 0;
+                        }
+
+                        ThreadInfo info = new ThreadInfo();
+                        info.setName("test1");
+                        info.setId(0l);
+                        TimelineProbe probe = new TimelineProbe(color1, "test1", lastUpdate);
+                        view.addProbe(info, probe);
+
+                        info = new ThreadInfo();
+                        info.setName("test2");
+                        info.setId(1l);
+                        probe = new TimelineProbe(color2, "test2", lastUpdate);
+                        view.addProbe(info, probe);
+                    }
+
+                    return null;
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    private static UIDefaults createUIDefaults() {
+        UIDefaults defaults = Mockito.mock(UIDefaults.class);
+        Mockito.when(defaults.getDefaultFont()).thenReturn(new JLabel().getFont());
+        return defaults;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo2.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
+import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Same as demo 1, except that timelines are always exactly one fixed interval
+ * apart and probes are added right away.
+ */
+public class SwingThreadViewDemo2 {
+
+    private static final int INTERVAL = 100; // ms
+
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+
+                UIDefaults defaults = createUIDefaults();
+
+                ContentPane contentPane = new ContentPane();
+
+                SwingThreadTimelineView view = new SwingThreadTimelineView(defaults);
+                JFrame frame = new JFrame();
+                frame.add(contentPane);
+
+                contentPane.add(view.getUiComponent());
+
+                JButton addDate = new JButton("Add Timeline Data");
+
+                addDate.addActionListener(new DataFiller(view));
+                contentPane.add(addDate, BorderLayout.SOUTH);
+
+                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                frame.setSize(new Dimension(800, 800));
+                frame.setVisible(true);
+            }
+        });
+    }
+
+    private static class DataFiller implements ActionListener {
+
+        private ThreadTimelineView view;
+        private long startTime;
+        private long lastUpdate;
+
+        private boolean threadAdded;
+        private int swap;
+        private boolean swapped;
+
+        private int added;
+
+        DataFiller(ThreadTimelineView view) {
+            this.view = view;
+            startTime = 0l;
+            lastUpdate = 0l;
+            swap = 0;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            lastUpdate += INTERVAL;
+            final Range<Long> range = new Range<>(startTime, lastUpdate);
+
+            SwingWorker<Void, Void> worker  = new SwingWorker<Void, Void>() {
+                @Override
+                protected Void doInBackground() throws Exception {
+
+//                    System.err.println("range: " + (range.getMax() - range.getMin()));
+
+                    view.setTotalRange(range);
+
+                    if (!threadAdded) {
+                        ThreadInfo info = new ThreadInfo();
+                        info.setName("test1");
+                        info.setId(0l);
+                        view.addThread(info);
+
+                        info = new ThreadInfo();
+                        info.setName("test2");
+                        info.setId(1l);
+                        view.addThread(info);
+
+                        threadAdded = true;
+
+                    }
+
+                    Palette color1 = Palette.THERMOSTAT_BLU;
+                    Palette color2 = Palette.THERMOSTAT_RED;
+
+                    if (swapped) {
+                        color1 = Palette.THERMOSTAT_RED;
+                        color2 = Palette.THERMOSTAT_BLU;
+                    }
+
+                    swap++;
+                    if (swap % 10 == 0) {
+                        swapped = !swapped;
+                        swap = 0;
+                    }
+
+                    ThreadInfo info = new ThreadInfo();
+                    info.setName("test1");
+                    info.setId(0l);
+                    TimelineProbe probe = new TimelineProbe(color1, "test1", lastUpdate);
+                    view.addProbe(info, probe);
+
+                    info = new ThreadInfo();
+                    info.setName("test2");
+                    info.setId(1l);
+                    probe = new TimelineProbe(color2, "test2", lastUpdate);
+                    view.addProbe(info, probe);
+
+                    added++;
+
+                   // System.err.println("probes added: " + added);
+
+                    return null;
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    private static UIDefaults createUIDefaults() {
+        UIDefaults defaults = mock(UIDefaults.class);
+        when(defaults.getDefaultFont()).thenReturn(new JLabel().getFont());
+        when(defaults.getIconColor()).thenReturn(Palette.EARL_GRAY.getColor());
+
+        return defaults;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo3.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.client.swing.impl;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.model.timeline.TimelineProbe;
+import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Same as demo 2, except that it insert 10 minutes worth of timelines.
+ * Startup time is also configurable.
+ */
+public class SwingThreadViewDemo3 {
+
+    private static final int INTERVAL = 100; // ms
+    private static final long STARTUP = 999l; // ms
+
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+
+                UIDefaults defaults = createUIDefaults();
+
+                ContentPane contentPane = new ContentPane();
+
+                SwingThreadTimelineView view = new SwingThreadTimelineView(defaults);
+                JFrame frame = new JFrame();
+                frame.add(contentPane);
+
+                contentPane.add(view.getUiComponent());
+
+                JButton addDate = new JButton("Add Timeline Data");
+
+                addDate.addActionListener(new DataFiller(view));
+                contentPane.add(addDate, BorderLayout.SOUTH);
+
+                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                frame.setSize(new Dimension(800, 800));
+                frame.setVisible(true);
+            }
+        });
+    }
+
+    private static class DataFiller implements ActionListener {
+
+        private ThreadTimelineView view;
+        private long startTime;
+        private long lastUpdate;
+
+        private boolean threadAdded;
+        private int swap;
+        private boolean swapped;
+
+        private int added;
+
+        DataFiller(ThreadTimelineView view) {
+            this.view = view;
+            startTime = STARTUP;
+            lastUpdate = STARTUP;
+            swap = 0;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            SwingWorker<Void, Void> worker  = new SwingWorker<Void, Void>() {
+                @Override
+                protected Void doInBackground() throws Exception {
+                    for (long l = 0; l < 10_000; l+=INTERVAL) {
+                        lastUpdate += INTERVAL;
+                        final Range<Long> range = new Range<>(startTime, lastUpdate);
+                        view.setTotalRange(range);
+
+                        if (!threadAdded) {
+                            ThreadInfo info = new ThreadInfo();
+                            info.setName("test1");
+                            info.setId(0l);
+                            view.addThread(info);
+
+                            info = new ThreadInfo();
+                            info.setName("test2");
+                            info.setId(1l);
+                            view.addThread(info);
+
+                            threadAdded = true;
+
+                        }
+
+                        Palette color1 = Palette.THERMOSTAT_BLU;
+                        Palette color2 = Palette.THERMOSTAT_RED;
+
+                        if (swapped) {
+                            color1 = Palette.THERMOSTAT_RED;
+                            color2 = Palette.THERMOSTAT_BLU;
+                        }
+
+                        swap++;
+                        if (swap % 10 == 0) {
+                            swapped = !swapped;
+                            swap = 0;
+                        }
+
+                        ThreadInfo info = new ThreadInfo();
+                        info.setName("test1");
+                        info.setId(0l);
+                        TimelineProbe probe = new TimelineProbe(color1, "test1", lastUpdate);
+                        view.addProbe(info, probe);
+
+                        info = new ThreadInfo();
+                        info.setName("test2");
+                        info.setId(1l);
+                        probe = new TimelineProbe(color2, "test2", lastUpdate);
+                        view.addProbe(info, probe);
+
+                        added++;
+                    }
+                    return null;
+                }
+            };
+            worker.execute();
+        }
+    }
+
+    private static UIDefaults createUIDefaults() {
+        UIDefaults defaults = mock(UIDefaults.class);
+        when(defaults.getDefaultFont()).thenReturn(new JLabel().getFont());
+        when(defaults.getIconColor()).thenReturn(Palette.EARL_GRAY.getColor());
+
+        return defaults;
+    }
+}
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -37,24 +37,14 @@
 package com.redhat.thermostat.thread.client.swing.impl;
 
 import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
 import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.ThreadTableBean;
 import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
-import com.redhat.thermostat.thread.client.common.view.ThreadTableView.ThreadSelectionAction;
-import com.redhat.thermostat.thread.client.swing.impl.timeline.SwingTimelineDimensionModel;
 import java.awt.Color;
 import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
-import java.util.concurrent.Semaphore;
 import javax.swing.JFrame;
 import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
 import org.fest.swing.annotation.GUITest;
-import org.fest.swing.data.TableCell;
 import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
 import org.fest.swing.edt.GuiActionRunner;
 import org.fest.swing.edt.GuiTask;
@@ -68,8 +58,6 @@
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -113,7 +101,7 @@
         GuiActionRunner.execute(new GuiTask() {
             @Override
             protected void executeInEDT() throws Throwable {
-                view = new SwingThreadView(defaults, new SwingTimelineDimensionModel());
+                view = new SwingThreadView(defaults);
                 frame = new JFrame();
                 frame.add(view.getUiComponent());
             }
@@ -157,45 +145,45 @@
         togglefixture.requireDisabled();
     }
 
-    @GUITest
-    @Test
-    public void verifTableViewLinksToDetailsView() throws InvocationTargetException, InterruptedException {
-        
-        final boolean listenerCalled[] = new boolean[1];
-        
-        ThreadTableBean bean1 = mock(ThreadTableBean.class);
-        when(bean1.getName()).thenReturn("mocked bean 1");
-        
-        ThreadTableBean bean2 = mock(ThreadTableBean.class);
-        when(bean2.getName()).thenReturn("mocked bean 2");
-        
-        List<ThreadTableBean> threadList = new ArrayList<>();
-        threadList.add(bean1);
-        threadList.add(bean2);
-
-        frameFixture.show();
-        
-        frameFixture.splitPane("threadMainPanelSplitPane").moveDividerTo(0);
-        frameFixture.tabbedPane("bottomTabbedPane").selectTab(0);
-        
-        final Semaphore sem = new Semaphore(1);
-        ThreadTableView tableView = view.createThreadTableView();
-        tableView.addThreadSelectionActionListener(new ActionListener<ThreadTableView.ThreadSelectionAction>() {
-            @Override
-            public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
-                listenerCalled[0] = true;
-                view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
-                sem.release();
-            }
-        });
-        
-        tableView.display(threadList);
-        
-        frameFixture.table("threadBeansTable").cell(TableCell.row(1).column(0)).doubleClick();
-        sem.acquire();
-        
-        assertTrue(listenerCalled[0]);
-        assertEquals(1, frameFixture.tabbedPane("bottomTabbedPane").target.getSelectedIndex());
-    }
+//    @GUITest
+//    @Test
+//    public void verifTableViewLinksToDetailsView() throws InvocationTargetException, InterruptedException {
+//        
+//        final boolean listenerCalled[] = new boolean[1];
+//
+//        ThreadTableBean bean1 = mock(ThreadTableBean.class);
+//        when(bean1.getName()).thenReturn("mocked bean 1");
+//
+//        ThreadTableBean bean2 = mock(ThreadTableBean.class);
+//        when(bean2.getName()).thenReturn("mocked bean 2");
+//
+//        List<ThreadTableBean> threadList = new ArrayList<>();
+//        threadList.add(bean1);
+//        threadList.add(bean2);
+//
+//        frameFixture.show();
+//
+//        frameFixture.splitPane("threadMainPanelSplitPane").moveDividerTo(0);
+//        frameFixture.tabbedPane("bottomTabbedPane").selectTab(0);
+//
+//        final Semaphore sem = new Semaphore(1);
+//        ThreadTableView tableView = view.createThreadTableView();
+//        tableView.addThreadSelectionActionListener(new ActionListener<ThreadTableView.ThreadSelectionAction>() {
+//            @Override
+//            public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
+//                listenerCalled[0] = true;
+//                view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
+//                sem.release();
+//            }
+//        });
+//
+//        tableView.display(threadList);
+//
+//        frameFixture.table("threadBeansTable").cell(TableCell.row(1).column(0)).doubleClick();
+//        sem.acquire();
+//
+//        assertTrue(listenerCalled[0]);
+//        assertEquals(1, frameFixture.tabbedPane("bottomTabbedPane").target.getSelectedIndex());
+//    }
 }
 
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineRangeModelFormatterTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.client.swing.impl.timeline;
-
-import java.util.Locale;
-import java.util.TimeZone;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class TimelineRangeModelFormatterTest {
-
-    private static Locale defaultLocale;
-    private static TimeZone defaultTimeZone;
-
-    @BeforeClass
-    public static void setUp() {
-        defaultLocale = Locale.getDefault();
-        Locale.setDefault(Locale.US);
-
-        defaultTimeZone = TimeZone.getDefault();
-        TimeZone.setDefault(TimeZone.getTimeZone("Etc/UTC"));
-    }
-
-    @AfterClass
-    public static void tearDown() {
-        TimeZone.setDefault(defaultTimeZone);
-        Locale.setDefault(defaultLocale);
-    }
-
-    @Test
-    public void testGetFormattedString() throws Exception {
-        String range = TimelineRangeModelFormatter.getFormattedString(5_000l);
-        assertEquals("1970.01.01, 00:00:05", range);
-    }
-
-    @Test
-    public void testGetFormattedStringNegative() throws Exception {
-        String range = TimelineRangeModelFormatter.getFormattedString(-1l);
-        assertEquals(" - ", range);
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Thu Dec 11 11:44:42 2014 +0100
@@ -40,10 +40,11 @@
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Key;
 import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
+import com.redhat.thermostat.thread.dao.impl.ThreadDaoKeys;
 import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadContentionSample;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -53,12 +54,10 @@
 
 public interface ThreadDao {
 
-    /*
-     * User-facing string-constants used to represent VM thread capabilities.
-     */
-    static final String CPU_TIME = "thread-cpu-time";
-    static final String CONTENTION_MONITOR = "thread-contention-monitor";
-    static final String THREAD_ALLOCATED_MEMORY = "thread-allocated-memory";
+    public static enum Sort {
+        ASCENDING,
+        DESCENDING,
+    }
 
     /*
      * vm-thread-harvesting schema
@@ -74,34 +73,6 @@
                     Arrays.<Key<?>>asList(Key.TIMESTAMP));
 
     /*
-     * vm-thread-header schema
-     */
-    static final Key<Long> THREAD_ID_KEY = new Key<Long>("threadId");
-    static final Key<String> THREAD_NAME_KEY = new Key<String>("threadName");
-    static final Key<String> THREAD_HEADER_UUID = new Key<String>("referenceID");
-    static final Category<ThreadHeader> THREAD_HEADER =
-        new Category<>("vm-thread-header", ThreadHeader.class,
-                       Arrays.<Key<?>>asList(Key.AGENT_ID, Key.VM_ID,
-                                             THREAD_NAME_KEY, THREAD_HEADER_UUID,
-                                             THREAD_ID_KEY, Key.TIMESTAMP),
-                       Arrays.<Key<?>>asList(Key.TIMESTAMP, THREAD_NAME_KEY,
-                                             THREAD_ID_KEY, THREAD_HEADER_UUID));
-                            
-    /*
-     * vm-thread-state schema
-     */
-    static final Key<String> THREAD_STATE_KEY = new Key<String>("state");
-    static final Key<Long> THREAD_PROBE_START = new Key<Long>("probeStartTime");
-    static final Key<Long> THREAD_PROBE_END = new Key<Long>("probeEndTime");
-    static final Category<ThreadState> THREAD_STATE =
-            new Category<>("vm-thread-state", ThreadState.class,
-                           Arrays.<Key<?>>asList(Key.AGENT_ID, Key.VM_ID, THREAD_STATE_KEY,
-                                            THREAD_PROBE_START, THREAD_PROBE_END,
-                                            THREAD_HEADER_UUID),
-                           Arrays.<Key<?>>asList(THREAD_PROBE_START, THREAD_PROBE_END,
-                                            THREAD_HEADER_UUID));
-
-    /*
      * vm-deadlock-data schema
      */
     static final Key<String> DEADLOCK_DESCRIPTION_KEY = new Key<>("deadLockDescription");
@@ -124,13 +95,13 @@
                                                  THREAD_CONTENTION_BLOCKED_TIME_KEY,
                                                  THREAD_CONTENTION_WAITED_COUNT_KEY,
                                                  THREAD_CONTENTION_WAITED_TIME_KEY,
-                                                 THREAD_HEADER_UUID, Key.TIMESTAMP),
+                                                 ThreadDaoKeys.THREAD_HEADER_UUID, Key.TIMESTAMP),
                            Arrays.<Key<?>>asList(Key.AGENT_ID, Key.VM_ID,
                                                  THREAD_CONTENTION_BLOCKED_COUNT_KEY,
                                                  THREAD_CONTENTION_BLOCKED_TIME_KEY,
                                                  THREAD_CONTENTION_WAITED_COUNT_KEY,
                                                  THREAD_CONTENTION_WAITED_TIME_KEY,
-                                                 THREAD_HEADER_UUID, Key.TIMESTAMP));
+                                                 ThreadDaoKeys.THREAD_HEADER_UUID, Key.TIMESTAMP));
 
     void saveSummary(ThreadSummary summary);
     List<ThreadSummary> getSummary(VmRef ref, SessionID session, Range<Long> range, int limit);
@@ -138,34 +109,13 @@
     /**
      * Returns a list of sessions registered by thread sampling.
      */
-    List<ThreadSession> getSessions(VmRef ref, Range<Long> range, int limit);
+    List<ThreadSession> getSessions(VmRef ref, Range<Long> range, int limit, Sort order);
 
     /**
      * Save the given session to the database.
      */
     void saveSession(ThreadSession session);
 
-    /**
-     * Gets the total time interval for the entire data related to
-     * {@link ThreadState}, as a {@link Range}.
-     * 
-     * <br /><br />
-     * 
-     * This {@link Range#getMin()} will return the start probe time for the
-     * oldest {@link ThreadState} entry while {@link Range#getMax()} will
-     * contains the end probe time for the most recent {@link ThreadState}
-     * entry.
-     *
-     * <br /><br />
-     *
-     * If no data is contained related to this {@link VmRef}, {@code null}
-     * will be returned.
-     *
-     * @returns a {@link Range} of timestamps (in milliseconds) or null if there
-     * is no valid data.
-     */
-    Range<Long> getThreadStateTotalTimeRange(VmRef ref);
-
     ThreadHarvestingStatus getLatestHarvestingStatus(VmRef vm);
     void saveHarvestingStatus(ThreadHarvestingStatus status);
 
@@ -177,36 +127,6 @@
      * @return the latest data or null if there is none
      */
     VmDeadLockData loadLatestDeadLockStatus(VmRef ref);
-    
-    /**
-     * Returns the list of known {@link ThreadHeader}s for the given VM.
-     * The list is sorted by timestamp.
-     */
-    List<ThreadHeader> getThreads(VmRef ref);
-
-    /**
-     * Returns the actual {@link ThreadHeader} in the storage based on th
-     * input {@link ThreadHeader} template, {@code null} otherwise.
-     */
-    ThreadHeader getThread(ThreadHeader thread);
-    
-    /**
-     * Adds the {@link ThreadHeader} into the target {@link VmRef} storage.
-     * If the target vm already has such entry the
-     */
-    void saveThread(ThreadHeader thread);
-    
-    /**
-     * Returns the last known {@link ThreadState} in the database for this
-     * {@link ThreadHeader}.
-     */
-    ThreadState getLastThreadState(ThreadHeader header);
-
-    /**
-     * Returns the first known {@link ThreadState} in the database for this
-     * {@link ThreadHeader}.
-     */
-    ThreadState getFirstThreadState(ThreadHeader thread);
 
     /**
      * Adds the given {@link ThreadState} to storage.
@@ -214,29 +134,25 @@
     void addThreadState(ThreadState thread);
 
     /**
-     * Updates the given {@link ThreadState}
-     */
-    void updateThreadState(ThreadState thread);
-
-    /**
-     * Return a list of {@link ThreadState} for the given {@link ThreadHeader}
-     * in the given {@link Range}.
-     */
-    List<ThreadState> getThreadStates(ThreadHeader thread, Range<Long> range);
-
-    /**
-     * Adds this {@link ThreadContentionSample} to the database.
-     *
-     * <br /><br />
-     *
-     * The sample must be fully created and valid, in particular, its
-     * {@link ThreadHeader} must be a valid entry in the database.
      */
     void saveContentionSample(ThreadContentionSample contentionSample);
 
     /**
-     * Returns the latest {@link ThreadContentionSample} available for this
-     * {@link ThreadHeader}.
+     */
+    ThreadContentionSample getLatestContentionSample(VmRef ref, SessionID session);
+
+
+    /**
+     * Return the oldest session registered in the database.
      */
-    ThreadContentionSample getLatestContentionSample(ThreadHeader thread);
+    ThreadSession getFirstSession(VmRef ref);
+
+    /**
+     * Return the newest session registered in the database.
+     */
+    ThreadSession getLastSession(VmRef ref);
+
+    void getThreadStates(VmRef ref, SessionID session,
+                         ResultHandler<ThreadState> handler,
+                         Range<Long> range, int limit, Sort order);
 }
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistration.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistration.java	Thu Dec 11 11:44:42 2014 +0100
@@ -56,8 +56,6 @@
 
         categories.add(ThreadDao.DEADLOCK_INFO.getName());
         categories.add(ThreadDao.THREAD_HARVESTING_STATUS.getName());
-        categories.add(ThreadDao.THREAD_HEADER.getName());
-        categories.add(ThreadDao.THREAD_STATE.getName());
         categories.add(ThreadDao.THREAD_CONTENTION_SAMPLE.getName());
 
         ThreadDaoCategories.register(categories);
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoCategories.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoCategories.java	Thu Dec 11 11:44:42 2014 +0100
@@ -39,9 +39,10 @@
 import com.redhat.thermostat.common.utils.LoggingUtils;
 import com.redhat.thermostat.storage.core.Category;
 import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.experimental.statement.CategoryBuilder;
 import com.redhat.thermostat.storage.model.Pojo;
-import com.redhat.thermostat.storage.core.experimental.statement.CategoryBuilder;
 import com.redhat.thermostat.thread.model.ThreadSession;
+import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -58,12 +59,15 @@
     public static class Categories {
         public static final String SUMMARY = "vm-thread-summary";
         public static final String SESSION = "vm-thread-session";
+        public static final String STATE = "vm-thread-state";
+
     }
 
     static final List<Class<? extends Pojo>> BEANS = new ArrayList<>();
     static {
         BEANS.add(ThreadSummary.class);
         BEANS.add(ThreadSession.class);
+        BEANS.add(ThreadState.class);
     }
 
     public static void register(Collection<String> collection) {
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Thu Dec 11 11:44:42 2014 +0100
@@ -47,20 +47,20 @@
 import com.redhat.thermostat.storage.core.StatementExecutionException;
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.model.Pojo;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.dao.impl.statement.SessionQuery;
-import com.redhat.thermostat.thread.dao.impl.statement.SummaryQuery;
 import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
 import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
-
+import com.redhat.thermostat.storage.core.experimental.statement.Id;
 import com.redhat.thermostat.storage.core.experimental.statement.Query;
 import com.redhat.thermostat.storage.core.experimental.statement.QueryValues;
 import com.redhat.thermostat.storage.core.experimental.statement.ResultHandler;
+import com.redhat.thermostat.storage.model.Pojo;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.dao.impl.statement.StateQueries;
+import com.redhat.thermostat.thread.dao.impl.statement.SessionQueries;
+import com.redhat.thermostat.thread.dao.impl.statement.SummaryQuery;
 import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadContentionSample;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -75,10 +75,9 @@
     
     private static final Logger logger = LoggingUtils.getLogger(ThreadDaoImpl.class);
 
-    static final BeanAdapter<ThreadSummary> SUMMARY = new BeanAdapterBuilder<>(ThreadSummary.class, new SummaryQuery()).build();
-    static final BeanAdapter<ThreadSession> SESSIONS = new BeanAdapterBuilder<>(ThreadSession.class, new SessionQuery()).build();
-
-    // Queries
+    static final BeanAdapter<ThreadSummary> ThreadSummaryAdapter = new BeanAdapterBuilder<>(ThreadSummary.class, new SummaryQuery()).build();
+    static final BeanAdapter<ThreadSession> ThreadSessionAdapter = new BeanAdapterBuilder<>(ThreadSession.class, SessionQueries.asList()).build();
+    static final BeanAdapter<ThreadState> ThreadStateAdapter = new BeanAdapterBuilder<>(ThreadState.class, StateQueries.asList()).build();
 
     static final String QUERY_LATEST_HARVESTING_STATUS = "QUERY "
             + THREAD_HARVESTING_STATUS.getName() + " WHERE '"
@@ -86,100 +85,24 @@
             + Key.VM_ID.getName() + "' = ?s SORT '" 
             + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
 
-    static final String QUERY_LATEST_THREAD_STATE_FOR_THREAD = "QUERY "
-            + THREAD_STATE.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
-            + THREAD_PROBE_END.getName() + "' DSC LIMIT 1";
-
-    static final String QUERY_FIRST_THREAD_STATE_FOR_THREAD = "QUERY "
-            + THREAD_STATE.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
-            + THREAD_PROBE_START.getName() + "' ASC LIMIT 1";
-
-    static final String QUERY_OLDEST_THREAD_STATE = "QUERY "
-            + THREAD_STATE.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + Key.VM_ID.getName() + "' = ?s SORT '"
-            + THREAD_PROBE_START.getName() + "' ASC LIMIT 1";
-    
-    static final String QUERY_LATEST_THREAD_STATE= "QUERY "
-            + THREAD_STATE.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + Key.VM_ID.getName() + "' = ?s SORT '"
-            + THREAD_PROBE_END.getName() + "' DSC LIMIT 1";
-    
     static final String QUERY_LATEST_DEADLOCK_INFO = "QUERY "
             + DEADLOCK_INFO.getName() + " WHERE '"
             + Key.AGENT_ID.getName() + "' = ?s AND '" 
             + Key.VM_ID.getName() + "' = ?s SORT '" 
             + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
 
-    static final String QUERY_THREAD_HEADER = "QUERY "
-            + THREAD_HEADER.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + Key.VM_ID.getName() + "' = ?s AND '"
-            + THREAD_NAME_KEY.getName() + "' = ?s AND '"
-            + THREAD_ID_KEY.getName() + "' = ?l LIMIT 1";
-    static final String QUERY_ALL_THREAD_HEADERS = "QUERY "
-            + THREAD_HEADER.getName() + " WHERE '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '"
-            + Key.VM_ID.getName() + "' = ?s SORT '"
-            + Key.TIMESTAMP.getName() + "' DSC";
-
-    static final String QUERY_THREAD_STATE_PER_THREAD = "QUERY "
-            + THREAD_STATE.getName() + " WHERE '"
-            + THREAD_HEADER_UUID.getName() + "' = ?s AND '"
-            + THREAD_PROBE_END.getName() + "' >= ?l AND '"
-            + THREAD_PROBE_START.getName() + "' <= ?l SORT '"
-            + THREAD_PROBE_START.getName() + "' ASC";
-
-    // Data modifying descriptors
-
-    // ADD vm-thread-harvesting SET 'agentId' = ?s , \
-    //                              'vmId' = ?s , \
-    //                              'timeStamp' = ?l , \
-    //                              'harvesting' = ?b
     static final String DESC_ADD_THREAD_HARVESTING_STATUS = "ADD " + THREAD_HARVESTING_STATUS.getName() +
             " SET '" + Key.AGENT_ID.getName() + "' = ?s , " +
                  "'" + Key.VM_ID.getName() + "' = ?s , " +
                  "'" + Key.TIMESTAMP.getName() + "' = ?l , " +
                  "'" + HARVESTING_STATUS_KEY.getName() + "' = ?b";
 
-    // ADD vm-deadlock-data SET 'agentId' = ?s , \
-    //                          'vmId' = ?s , \
-    //                          'timeStamp' = ?l , \
-    //                          'deadLockDescription' = ?s
     static final String DESC_ADD_THREAD_DEADLOCK_DATA = "ADD " + DEADLOCK_INFO.getName() +
             " SET '" + Key.AGENT_ID.getName() + "' = ?s , " +
                  "'" + Key.VM_ID.getName() + "' = ?s , " +
                  "'" + Key.TIMESTAMP.getName() + "' = ?l , " +
                  "'" + DEADLOCK_DESCRIPTION_KEY.getName() + "' = ?s";
 
-    static final String ADD_THREAD_HEADER =
-            "ADD " + THREAD_HEADER.getName() + " " +
-            "SET '" + Key.AGENT_ID.getName() + "' = ?s , "    +
-                "'" + Key.VM_ID.getName() + "' = ?s , "       +
-                "'" + THREAD_NAME_KEY.getName() + "' = ?s , " +
-                "'" + THREAD_ID_KEY.getName() + "' = ?l , "   +
-                "'" + Key.TIMESTAMP.getName() + "' = ?l , "   +
-                "'" + THREAD_HEADER_UUID.getName() + "' = ?s";
-
-    static final String ADD_THREAD_STATE =
-            "ADD "  + THREAD_STATE.getName() + " "               +
-            "SET '" + Key.AGENT_ID.getName() + "' = ?s , "       +
-                "'" + Key.VM_ID.getName() + "' = ?s , "          +
-                "'" + THREAD_STATE_KEY.getName() + "' = ?s , "   +
-                "'" + THREAD_PROBE_START.getName() + "' = ?l , " +
-                "'" + THREAD_PROBE_END.getName() + "' = ?l , "   +
-                "'" + THREAD_HEADER_UUID.getName() + "' = ?s";
-
-    static final String DESC_UPDATE_THREAD_STATE =
-            "UPDATE "  + THREAD_STATE.getName() + " "                 +
-            "SET '"    + THREAD_PROBE_END.getName() + "' = ?l "       +
-            "WHERE '"  + THREAD_HEADER_UUID.getName() + "' = ?s AND " +
-                  "'"  + THREAD_PROBE_START.getName() + "' = ?l";
 
     static final String ADD_CONTENTION_SAMPLE =
             "ADD "  + THREAD_CONTENTION_SAMPLE.getName() + " "               +
@@ -189,12 +112,12 @@
                     "'" + THREAD_CONTENTION_BLOCKED_TIME_KEY.getName() + "' = ?l , "  +
                     "'" + THREAD_CONTENTION_WAITED_COUNT_KEY.getName() + "' = ?l , "  +
                     "'" + THREAD_CONTENTION_WAITED_TIME_KEY.getName() + "' = ?l , "  +
-                    "'" + THREAD_HEADER_UUID.getName() + "' = ?s , " +
+                    "'" + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s , " +
                     "'" + Key.TIMESTAMP.getName() + "' = ?l";
 
     static final String GET_LATEST_CONTENTION_SAMPLE= "QUERY "
             + THREAD_CONTENTION_SAMPLE.getName() + " WHERE '"
-            + THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
+            + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
             + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
 
     private Storage storage;
@@ -205,264 +128,15 @@
         ThreadDaoCategories.register(storage);
 
         storage.registerCategory(THREAD_HARVESTING_STATUS);
-        storage.registerCategory(THREAD_HEADER);
-        storage.registerCategory(THREAD_STATE);
         storage.registerCategory(THREAD_CONTENTION_SAMPLE);
 
         storage.registerCategory(DEADLOCK_INFO);
     }
 
     @Override
-    public List<ThreadHeader> getThreads(VmRef ref) {
-        
-        List<ThreadHeader> result = null;
-        
-        StatementDescriptor<ThreadHeader> desc =
-                new StatementDescriptor<>(THREAD_HEADER, QUERY_ALL_THREAD_HEADERS);
-
-        PreparedStatement<ThreadHeader> stmt;
-        try {
-            
-            stmt = storage.prepareStatement(desc);
-            stmt.setString(0, ref.getHostRef().getAgentId());
-            stmt.setString(1, ref.getVmId());
-
-            result = getAllResults(stmt);
-            
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        }
-        
-        return result;
-    }
-    
-    @Override
-    public ThreadHeader getThread(ThreadHeader thread) {
-
-        ThreadHeader result = null;
-        
-        StatementDescriptor<ThreadHeader> desc =
-                new StatementDescriptor<>(THREAD_HEADER, QUERY_THREAD_HEADER);
-
-        PreparedStatement<ThreadHeader> prepared;
-
-        try {
-            prepared = storage.prepareStatement(desc);
-            prepared.setString(0, thread.getAgentId());
-            prepared.setString(1, thread.getVmId());
-            prepared.setString(2, thread.getThreadName());
-            prepared.setLong(3, thread.getThreadId());
-
-            result = getFirstResult(prepared);
-
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        }
-
-        return result;
-    }
-    
-    @Override
-    public void saveThread(ThreadHeader thread) {
-        StatementDescriptor<ThreadHeader> desc =
-                new StatementDescriptor<>(THREAD_HEADER, ADD_THREAD_HEADER);
-
-        PreparedStatement<ThreadHeader> prepared;
-
-        try {
-            prepared = storage.prepareStatement(desc);
-            prepared.setString(0, thread.getAgentId());
-            prepared.setString(1, thread.getVmId());
-            prepared.setString(2, thread.getThreadName());
-            prepared.setLong(3, thread.getThreadId());
-            prepared.setLong(4, thread.getTimeStamp());
-            prepared.setString(5, thread.getReferenceID());
-
-            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);
-        }
-    }
-
-    @Override
-    public ThreadState getLastThreadState(ThreadHeader header) {
-
-        ThreadState result = null;
-        StatementDescriptor<ThreadState> desc =
-                new StatementDescriptor<>(THREAD_STATE,
-                                          QUERY_LATEST_THREAD_STATE_FOR_THREAD);
-
-        PreparedStatement<ThreadState> prepared;
-        try {
-
-            prepared = storage.prepareStatement(desc);
-            String refId = header.getReferenceID();
-            if (refId == null) {
-                throw new IllegalArgumentException("header.getReferenceID() can't be null");
-            }
-
-            prepared.setString(0, header.getAgentId());
-            prepared.setString(1, header.getReferenceID());
-
-            result = getFirstResult(prepared);
-            if (result != null) {
-                result.setHeader(header);
-            }
-
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        }
-
-        return result;
-    }
-
-    public ThreadState getFirstThreadState(ThreadHeader header) {
-
-        ThreadState result = null;
-        StatementDescriptor<ThreadState> desc =
-                new StatementDescriptor<>(THREAD_STATE,
-                                          QUERY_FIRST_THREAD_STATE_FOR_THREAD);
-
-        PreparedStatement<ThreadState> prepared;
-        try {
-
-            prepared = storage.prepareStatement(desc);
-            String refId = header.getReferenceID();
-            if (refId == null) {
-                throw new IllegalArgumentException("header.getReferenceID() can't be null");
-            }
-
-            prepared.setString(0, header.getAgentId());
-            prepared.setString(1, header.getReferenceID());
-
-            result = getFirstResult(prepared);
-            if (result != null) {
-                result.setHeader(header);
-            }
-
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        }
-
-        return result;
-    }
-
-    @Override
-    public void addThreadState(ThreadState thread) {
-        StatementDescriptor<ThreadState> desc =
-                new StatementDescriptor<>(THREAD_STATE, ADD_THREAD_STATE);
-
-        PreparedStatement<ThreadState> prepared;
-        try {
-
-            prepared = storage.prepareStatement(desc);
-
-            ThreadHeader header = thread.getHeader();
-
-            prepared.setString(0, header.getAgentId());
-            prepared.setString(1, header.getVmId());
-
-            prepared.setString(2, thread.getState());
-
-            prepared.setLong(3, thread.getProbeStartTime());
-            prepared.setLong(4, thread.getProbeEndTime());
-
-            prepared.setString(5, header.getReferenceID());
-
-            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);
-        }
-    }
-
-    @Override
-    public void updateThreadState(ThreadState thread) {
-
-        StatementDescriptor<ThreadState> desc =
-                new StatementDescriptor<>(THREAD_STATE, DESC_UPDATE_THREAD_STATE);
-
-        PreparedStatement<ThreadState> prepared;
-        try {
-
-            prepared = storage.prepareStatement(desc);
-
-            ThreadHeader header = thread.getHeader();
-
-            prepared.setLong(0, thread.getProbeEndTime());
-            prepared.setString(1, header.getReferenceID());
-            prepared.setLong(2, thread.getProbeStartTime());
-
-            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);
-        }
-    }
-
-    @Override
-    public List<ThreadState> getThreadStates(ThreadHeader thread, Range<Long> range) {
-
-        List<ThreadState> result = new ArrayList<>();
-
-        StatementDescriptor<ThreadState> desc =
-                new StatementDescriptor<>(THREAD_STATE, QUERY_THREAD_STATE_PER_THREAD);
-
-        PreparedStatement<ThreadState> prepared;
-        try {
-
-            prepared = storage.prepareStatement(desc);
-
-            prepared.setString(0, thread.getReferenceID());
-            prepared.setLong(1, range.getMin());
-            prepared.setLong(2, range.getMax());
-
-            Cursor<ThreadState> cursor = prepared.executeQuery();
-            while (cursor.hasNext()) {
-                ThreadState state = cursor.next();
-                state.setHeader(thread);
-                result.add(state);
-            }
-
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Executing query '" + desc + "' failed!", e);
-        }
-        return result;
-    }
-
-    @Override
-    public Range<Long> getThreadStateTotalTimeRange(VmRef ref) {
-
-        PreparedStatement<ThreadState> stmt;
-
-        stmt = prepareQuery(THREAD_STATE, QUERY_OLDEST_THREAD_STATE, ref);
-        ThreadState oldestData = getFirstResult(stmt);
-        if (oldestData == null) {
-            return null;
-        }
-
-        long oldestTimeStamp = oldestData.getRange().getMin();
-
-        stmt = prepareQuery(THREAD_STATE, QUERY_LATEST_THREAD_STATE, ref);
-        ThreadState latestData = getFirstResult(stmt);
-        long latestTimeStamp = latestData.getRange().getMax();
-
-        return new Range<Long>(oldestTimeStamp, latestTimeStamp);
-    }
-
-    @Override
     public void saveSummary(ThreadSummary summary) {
         try {
-            SUMMARY.insert(summary, storage);
+            ThreadSummaryAdapter.insert(summary, storage);
 
         } catch (StatementExecutionException e) {
             logger.log(Level.SEVERE, "Exception saving summary: " + summary, e);
@@ -470,20 +144,59 @@
     }
 
     @Override
+    public void addThreadState(ThreadState thread) {
+        try {
+            ThreadStateAdapter.insert(thread, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception saving thread state: " + thread, e);
+        }
+    }
+
+    @Override
+    public void getThreadStates(VmRef ref, SessionID session,
+                                final ResultHandler<ThreadState> handler,
+                                Range<Long> range, int limit, Sort order)
+    {
+        Id id = order.equals(Sort.ASCENDING) ? StateQueries.getAscending :
+                                               StateQueries.getDescending;
+
+        Query<ThreadState> query = ThreadStateAdapter.getQuery(id);
+
+        QueryValues values = query.createValues();
+        values.set(StateQueries.CriteriaId.vmId, ref.getVmId());
+        values.set(StateQueries.CriteriaId.agentId, ref.getHostRef().getAgentId());
+        values.set(StateQueries.CriteriaId.sessionID, session.get());
+
+        values.set(StateQueries.CriteriaId.timeStampGEQ, range.getMin());
+        values.set(StateQueries.CriteriaId.timeStampLEQ, range.getMax());
+        values.set(StateQueries.CriteriaId.limit, limit);
+
+        try {
+            ThreadStateAdapter.query(values, handler, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception retrieving thread summary", e);
+        }
+    }
+
+    @Override
     public List<ThreadSummary> getSummary(VmRef ref, SessionID session, Range<Long> range, int limit) {
         final List<ThreadSummary> results = new ArrayList<>();
 
-        Query<ThreadSummary> query = SUMMARY.getQuery(SummaryQuery.id);
+        Query<ThreadSummary> query = ThreadSummaryAdapter.getQuery(SummaryQuery.id);
 
         QueryValues values = query.createValues();
-        values.set(SummaryQuery.CriteriaId.getVmId, ref.getVmId());
+        values.set(SummaryQuery.CriteriaId.vmId, ref.getVmId());
+        values.set(SummaryQuery.CriteriaId.agentId, ref.getHostRef().getAgentId());
         values.set(SummaryQuery.CriteriaId.sessionID, session.get());
+
         values.set(SummaryQuery.CriteriaId.timeStampGEQ, range.getMin());
         values.set(SummaryQuery.CriteriaId.timeStampLEQ, range.getMax());
         values.set(SummaryQuery.CriteriaId.limit, limit);
 
         try {
-            SUMMARY.query(values, new ResultHandler<ThreadSummary>() {
+            ThreadSummaryAdapter.query(values, new ResultHandler<ThreadSummary>() {
                 @Override
                 public void onResult(ThreadSummary result) {
                     results.add(result);
@@ -491,26 +204,47 @@
             }, storage);
 
         } catch (StatementExecutionException e) {
-            e.printStackTrace();
+            logger.log(Level.SEVERE, "Exception retrieving thread summary", e);
         }
 
         return results;
     }
 
     @Override
-    public List<ThreadSession> getSessions(VmRef ref, Range<Long> range, int limit) {
+    public ThreadSession getFirstSession(VmRef ref) {
+        Range<Long> range = new Range<>(Long.MIN_VALUE, Long.MAX_VALUE);
+        List<ThreadSession> results = getSessions(ref, range, 1, Sort.ASCENDING);
+        return results.isEmpty() ? null : results.get(0);
+    }
+
+    @Override
+    public ThreadSession getLastSession(VmRef ref) {
+        Range<Long> range = new Range<>(Long.MIN_VALUE, Long.MAX_VALUE);
+        List<ThreadSession> results = getSessions(ref, range, 1, Sort.DESCENDING);
+        return results.isEmpty() ? null : results.get(0);
+    }
+
+    @Override
+    public List<ThreadSession> getSessions(VmRef ref, Range<Long> range,
+                                           int limit, Sort order)
+    {
         final List<ThreadSession> results = new ArrayList<>();
 
-        Query<ThreadSession> query = SESSIONS.getQuery(SessionQuery.id);
+        Id id = order.equals(Sort.ASCENDING) ? SessionQueries.getRangeAscending :
+                                               SessionQueries.getRangeDescending;
+
+        Query<ThreadSession> query = ThreadSessionAdapter.getQuery(id);
 
         QueryValues values = query.createValues();
-        values.set(SessionQuery.CriteriaId.getVmId, ref.getVmId());
-        values.set(SessionQuery.CriteriaId.timeStampGEQ, range.getMin());
-        values.set(SessionQuery.CriteriaId.timeStampLEQ, range.getMax());
-        values.set(SessionQuery.CriteriaId.limit, limit);
+        values.set(SessionQueries.CriteriaId.vmId, ref.getVmId());
+        values.set(SessionQueries.CriteriaId.agentId, ref.getHostRef().getAgentId());
+
+        values.set(SessionQueries.CriteriaId.timeStampGEQ, range.getMin());
+        values.set(SessionQueries.CriteriaId.timeStampLEQ, range.getMax());
+        values.set(SessionQueries.CriteriaId.limit, limit);
 
         try {
-            SESSIONS.query(values, new ResultHandler<ThreadSession>() {
+            ThreadSessionAdapter.query(values, new ResultHandler<ThreadSession>() {
                 @Override
                 public void onResult(ThreadSession result) {
                     results.add(result);
@@ -518,7 +252,7 @@
             }, storage);
 
         } catch (StatementExecutionException e) {
-            e.printStackTrace();
+            logger.log(Level.SEVERE, "Exception retrieving thread session", e);
         }
 
         return results;
@@ -526,7 +260,7 @@
 
     public void saveSession(ThreadSession session) {
         try {
-            SESSIONS.insert(session, storage);
+            ThreadSessionAdapter.insert(session, storage);
 
         } catch (StatementExecutionException e) {
             logger.log(Level.SEVERE, "Exception saving session: " + session, e);
@@ -593,68 +327,68 @@
     @Override
     public void saveContentionSample(ThreadContentionSample contentionSample) {
 
-        StatementDescriptor<ThreadContentionSample> desc =
-                new StatementDescriptor<>(THREAD_CONTENTION_SAMPLE,
-                                          ADD_CONTENTION_SAMPLE);
-        PreparedStatement<ThreadContentionSample> prepared;
-        ThreadHeader header = contentionSample.getHeader();
-        if (header == null || header.getReferenceID() == null) {
-            throw new IllegalArgumentException("header or header.getReferenceID() can't be null");
-        }
-
-        try {
-            prepared = storage.prepareStatement(desc);
-
-            prepared.setString(0, header.getAgentId());
-            prepared.setString(1, header.getVmId());
-
-            prepared.setLong(2, contentionSample.getBlockedCount());
-            prepared.setLong(3, contentionSample.getBlockedTime());
-            prepared.setLong(4, contentionSample.getWaitedCount());
-            prepared.setLong(5, contentionSample.getWaitedTime());
-
-            prepared.setString(6, header.getReferenceID());
-
-            prepared.setLong(7, contentionSample.getTimeStamp());
-
-            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);
-        }
+//        StatementDescriptor<ThreadContentionSample> desc =
+//                new StatementDescriptor<>(THREAD_CONTENTION_SAMPLE,
+//                                          ADD_CONTENTION_SAMPLE);
+//        PreparedStatement<ThreadContentionSample> prepared;
+//        ThreadHeader header = contentionSample.getHeader();
+//        if (header == null || header.getReferenceID() == null) {
+//            throw new IllegalArgumentException("header or header.getReferenceID() can't be null");
+//        }
+//
+//        try {
+//            prepared = storage.prepareStatement(desc);
+//
+//            prepared.setString(0, header.getAgentId());
+//            prepared.setString(1, header.getVmId());
+//
+//            prepared.setLong(2, contentionSample.getBlockedCount());
+//            prepared.setLong(3, contentionSample.getBlockedTime());
+//            prepared.setLong(4, contentionSample.getWaitedCount());
+//            prepared.setLong(5, contentionSample.getWaitedTime());
+//
+//            prepared.setString(6, header.getReferenceID());
+//
+//            prepared.setLong(7, contentionSample.getTimeStamp());
+//
+//            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);
+//        }
     }
 
     @Override
-    public ThreadContentionSample getLatestContentionSample(ThreadHeader thread) {
+    public ThreadContentionSample getLatestContentionSample(VmRef ref, SessionID session) {
 
         ThreadContentionSample sample = null;
 
-        StatementDescriptor<ThreadContentionSample> desc =
-                new StatementDescriptor<>(THREAD_CONTENTION_SAMPLE,
-                                          GET_LATEST_CONTENTION_SAMPLE);
-        PreparedStatement<ThreadContentionSample> prepared;
-
-        if (thread == null || thread.getReferenceID() == null) {
-            throw new IllegalArgumentException("header or header.getReferenceID() can't be null");
-        }
-
-        try {
-            prepared = storage.prepareStatement(desc);
-
-            prepared.setString(0, thread.getReferenceID());
-            Cursor<ThreadContentionSample> cursor = prepared.executeQuery();
-            if (cursor.hasNext()) {
-                sample = cursor.next();
-                sample.setHeader(thread);
-            }
-
-        } catch (DescriptorParsingException e) {
-            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Executing stmt '" + desc + "' failed!", e);
-        }
+//        StatementDescriptor<ThreadContentionSample> desc =
+//                new StatementDescriptor<>(THREAD_CONTENTION_SAMPLE,
+//                                          GET_LATEST_CONTENTION_SAMPLE);
+//        PreparedStatement<ThreadContentionSample> prepared;
+//
+//        if (thread == null || thread.getReferenceID() == null) {
+//            throw new IllegalArgumentException("header or header.getReferenceID() can't be null");
+//        }
+//
+//        try {
+//            prepared = storage.prepareStatement(desc);
+//
+//            prepared.setString(0, thread.getReferenceID());
+//            Cursor<ThreadContentionSample> cursor = prepared.executeQuery();
+//            if (cursor.hasNext()) {
+//                sample = cursor.next();
+//                sample.setHeader(thread);
+//            }
+//
+//        } catch (DescriptorParsingException e) {
+//            logger.log(Level.SEVERE, "Preparing stmt '" + desc + "' failed!", e);
+//        } catch (StatementExecutionException e) {
+//            logger.log(Level.SEVERE, "Executing stmt '" + desc + "' failed!", e);
+//        }
 
         return sample;
     }
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistration.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistration.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,9 +38,7 @@
 
 import com.redhat.thermostat.storage.core.PreparedParameter;
 import com.redhat.thermostat.storage.core.auth.DescriptorMetadata;
-import com.redhat.thermostat.storage.core.auth.StatementDescriptorMetadataFactory;
 import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
-
 import java.util.HashSet;
 import java.util.Set;
 
@@ -53,35 +51,21 @@
         StatementDescriptorRegistration {
 
     private final Set<String> descs;
-    
+
     public ThreadDaoImplStatementDescriptorRegistration() {
         descs = new HashSet<>();
         descs.add(ThreadDaoImpl.QUERY_LATEST_DEADLOCK_INFO);
         descs.add(ThreadDaoImpl.QUERY_LATEST_HARVESTING_STATUS);
 
         // TODO: this needs to go in an helper class
-        descs.addAll(ThreadDaoImpl.SUMMARY.describeStatements());
-        descs.addAll(ThreadDaoImpl.SESSIONS.describeStatements());
+        descs.addAll(ThreadDaoImpl.ThreadSummaryAdapter.describeStatements());
+        descs.addAll(ThreadDaoImpl.ThreadSessionAdapter.describeStatements());
+        descs.addAll(ThreadDaoImpl.ThreadStateAdapter.describeStatements());
 
         descs.add(ThreadDaoImpl.DESC_ADD_THREAD_DEADLOCK_DATA);
         descs.add(ThreadDaoImpl.DESC_ADD_THREAD_HARVESTING_STATUS);
-
-        descs.add(ThreadDaoImpl.ADD_THREAD_HEADER);
-        descs.add(ThreadDaoImpl.QUERY_THREAD_HEADER);
-        descs.add(ThreadDaoImpl.QUERY_ALL_THREAD_HEADERS);
-
-        descs.add(ThreadDaoImpl.ADD_THREAD_STATE);
-        descs.add(ThreadDaoImpl.QUERY_LATEST_THREAD_STATE_FOR_THREAD);
-        descs.add(ThreadDaoImpl.QUERY_FIRST_THREAD_STATE_FOR_THREAD);
-
-        descs.add(ThreadDaoImpl.QUERY_OLDEST_THREAD_STATE);
-        descs.add(ThreadDaoImpl.QUERY_LATEST_THREAD_STATE);
-
-        descs.add(ThreadDaoImpl.QUERY_THREAD_STATE_PER_THREAD);
-
         descs.add(ThreadDaoImpl.ADD_CONTENTION_SAMPLE);
         descs.add(ThreadDaoImpl.GET_LATEST_CONTENTION_SAMPLE);
-        descs.add(ThreadDaoImpl.DESC_UPDATE_THREAD_STATE);
     }
     
     @Override
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoKeys.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoKeys.java	Thu Dec 11 11:44:42 2014 +0100
@@ -43,18 +43,5 @@
  */
 public class ThreadDaoKeys {
 
-    public static enum Keys {
-
-        vmId,
-        agentId,
-        timeStamp,
-        session,
-        currentLiveThreads,
-        currentDaemonThreads,
-
-    }
-
-    public static final Key<String> SESSION = new Key<>(Keys.session.name());
-    public static final Key<Long> LIVE_THREADS_KEY = new Key<Long>(Keys.currentLiveThreads.name());
-    public static final Key<Long> DAEMON_THREADS_KEY = new Key<Long>(Keys.currentDaemonThreads.name());
+    public static final Key<String> THREAD_HEADER_UUID = new Key<String>("referenceID");
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueries.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.dao.impl.statement;
+
+import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
+import com.redhat.thermostat.storage.core.experimental.statement.Id;
+import com.redhat.thermostat.storage.core.experimental.statement.LimitCriterion;
+import com.redhat.thermostat.storage.core.experimental.statement.Query;
+import com.redhat.thermostat.storage.core.experimental.statement.SortCriterion;
+import com.redhat.thermostat.storage.core.experimental.statement.StatementUtils;
+import com.redhat.thermostat.storage.core.experimental.statement.TypeMapper;
+import com.redhat.thermostat.storage.core.experimental.statement.WhereCriterion;
+import com.redhat.thermostat.thread.model.ThreadSession;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+public class SessionQueries {
+
+    public static final Id getRangeAscending = new Id("SessionQueries::getRangeAscending");
+    public static final Id getRangeDescending = new Id("SessionQueries::getRangeDescending");
+
+    public static class CriteriaId {
+        public static final Id vmId = new Id("vmId");
+        public static final Id agentId = new Id("agentId");
+        public static final Id timeStampGEQ = new Id("timeStampGEQ");
+        public static final Id timeStampLEQ = new Id("timeStampLEQ");
+        public static final Id limit = new Id("limit");
+    }
+
+    private static class GetDescending extends Query<ThreadSession> {
+        @Override
+        protected void describe(Criteria criteria) {
+            List<FieldDescriptor> descriptors = StatementUtils.createDescriptors(ThreadSession.class);
+            final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
+
+            criteria.add(new WhereCriterion(CriteriaId.vmId, map.get("vmId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.agentId, map.get("agentId"),
+                                            TypeMapper.Criteria.Equal));
+
+            criteria.add(new WhereCriterion(CriteriaId.timeStampGEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.GreaterEqual));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampLEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.LessEqual));
+
+            criteria.add(new SortCriterion(map.get("timeStamp"), TypeMapper.Sort.Descending));
+            criteria.add(new LimitCriterion(CriteriaId.limit));
+        }
+
+        @Override
+        public Id getId() {
+            return getRangeDescending;
+        }
+    }
+
+    private static class GetAscending extends Query<ThreadSession> {
+        @Override
+        protected void describe(Criteria criteria) {
+            List<FieldDescriptor> descriptors = StatementUtils.createDescriptors(ThreadSession.class);
+            final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
+
+            criteria.add(new WhereCriterion(CriteriaId.vmId, map.get("vmId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.agentId, map.get("agentId"),
+                                            TypeMapper.Criteria.Equal));
+
+            criteria.add(new WhereCriterion(CriteriaId.timeStampGEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.GreaterEqual));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampLEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.LessEqual));
+
+            criteria.add(new SortCriterion(map.get("timeStamp"), TypeMapper.Sort.Ascending));
+            criteria.add(new LimitCriterion(CriteriaId.limit));
+        }
+
+        @Override
+        public Id getId() {
+            return getRangeAscending;
+        }
+    }
+
+    private static final List<Query<ThreadSession>> queries = new ArrayList<>();
+    static {
+        queries.add(new GetDescending());
+        queries.add(new GetAscending());
+    }
+
+    public static List<Query<ThreadSession>> asList() {
+        return Collections.unmodifiableList(queries);
+    }
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQuery.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.dao.impl.statement;
-
-import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
-import com.redhat.thermostat.storage.core.experimental.statement.Id;
-import com.redhat.thermostat.storage.core.experimental.statement.LimitCriterion;
-import com.redhat.thermostat.storage.core.experimental.statement.Query;
-import com.redhat.thermostat.storage.core.experimental.statement.SortCriterion;
-import com.redhat.thermostat.storage.core.experimental.statement.StatementUtils;
-import com.redhat.thermostat.storage.core.experimental.statement.TypeMapper;
-import com.redhat.thermostat.storage.core.experimental.statement.WhereCriterion;
-import com.redhat.thermostat.thread.model.ThreadSession;
-import java.util.List;
-import java.util.Map;
-
-/**
- *
- */
-public class SessionQuery extends Query<ThreadSession> {
-
-    public static final Id id = new Id(ThreadSession.class.getSimpleName());
-
-    public static class CriteriaId {
-        public static final Id getVmId = new Id("0");
-        public static final Id timeStampGEQ = new Id("1");
-        public static final Id timeStampLEQ = new Id("2");
-        public static final Id limit = new Id("3");
-
-    }
-
-    @Override
-    public Id getId() {
-        return id;
-    }
-
-    @Override
-    protected void describe(Criteria criteria) {
-        List<FieldDescriptor> descriptors = StatementUtils.createDescriptors(ThreadSession.class);
-        final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
-
-        criteria.add(new WhereCriterion(CriteriaId.getVmId, map.get("vmId"),
-                                        TypeMapper.Criteria.Equal));
-        criteria.add(new WhereCriterion(CriteriaId.timeStampGEQ, map.get("timeStamp"),
-                                        TypeMapper.Criteria.GreaterEqual));
-        criteria.add(new WhereCriterion(CriteriaId.timeStampLEQ, map.get("timeStamp"),
-                                        TypeMapper.Criteria.LessEqual));
-
-        criteria.add(new SortCriterion(map.get("timeStamp"), TypeMapper.Sort.Descending));
-        criteria.add(new LimitCriterion(CriteriaId.limit));
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/StateQueries.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.dao.impl.statement;
+
+import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
+import com.redhat.thermostat.storage.core.experimental.statement.Id;
+import com.redhat.thermostat.storage.core.experimental.statement.LimitCriterion;
+import com.redhat.thermostat.storage.core.experimental.statement.Query;
+import com.redhat.thermostat.storage.core.experimental.statement.SortCriterion;
+import com.redhat.thermostat.storage.core.experimental.statement.StatementUtils;
+import com.redhat.thermostat.storage.core.experimental.statement.TypeMapper;
+import com.redhat.thermostat.storage.core.experimental.statement.WhereCriterion;
+import com.redhat.thermostat.thread.model.ThreadState;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+public class StateQueries {
+
+    public static final Id getAscending = new Id("StateQueries::getRangeAscending");
+    public static final Id getDescending = new Id("StateQueries::getRangeDescending");
+
+    public static class CriteriaId {
+        public static final Id vmId = new Id("vmId");
+        public static final Id agentId = new Id("agentId");
+        public static final Id timeStampGEQ = new Id("timeStampGEQ");
+        public static final Id timeStampLEQ = new Id("timeStampLEQ");
+        public static final Id sessionID = new Id("sessionID");
+        public static final Id limit = new Id("limit");
+    }
+
+    private static class GetDescending extends Query<ThreadState> {
+        @Override
+        protected void describe(Criteria criteria) {
+            List<FieldDescriptor> descriptors = StatementUtils.createDescriptors(ThreadState.class);
+            final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
+
+            criteria.add(new WhereCriterion(CriteriaId.vmId, map.get("vmId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.agentId, map.get("agentId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampGEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.GreaterEqual));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampLEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.LessEqual));
+            criteria.add(new WhereCriterion(CriteriaId.sessionID, map.get("session"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new SortCriterion(map.get("timeStamp"), TypeMapper.Sort.Descending));
+            criteria.add(new LimitCriterion(CriteriaId.limit));
+        }
+
+        @Override
+        public Id getId() {
+            return getDescending;
+        }
+    }
+
+    private static class GetAscending extends Query<ThreadState> {
+        @Override
+        protected void describe(Criteria criteria) {
+            List<FieldDescriptor> descriptors = StatementUtils.createDescriptors(ThreadState.class);
+            final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
+
+            criteria.add(new WhereCriterion(CriteriaId.vmId, map.get("vmId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.agentId, map.get("agentId"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampGEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.GreaterEqual));
+            criteria.add(new WhereCriterion(CriteriaId.timeStampLEQ, map.get("timeStamp"),
+                                            TypeMapper.Criteria.LessEqual));
+            criteria.add(new WhereCriterion(CriteriaId.sessionID, map.get("session"),
+                                            TypeMapper.Criteria.Equal));
+            criteria.add(new SortCriterion(map.get("timeStamp"), TypeMapper.Sort.Ascending));
+            criteria.add(new LimitCriterion(CriteriaId.limit));
+        }
+
+        @Override
+        public Id getId() {
+            return getAscending;
+        }
+    }
+
+    private static final List<Query<ThreadState>> queries = new ArrayList<>();
+    static {
+        queries.add(new GetDescending());
+        queries.add(new GetAscending());
+    }
+
+    public static List<Query<ThreadState>> asList() {
+        return Collections.unmodifiableList(queries);
+    }
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQuery.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQuery.java	Thu Dec 11 11:44:42 2014 +0100
@@ -57,11 +57,12 @@
     public static final Id id = new Id(SummaryQuery.class.getSimpleName());
 
     public static class CriteriaId {
-        public static final Id getVmId = new Id("0");
-        public static final Id timeStampGEQ = new Id("1");
-        public static final Id timeStampLEQ = new Id("2");
-        public static final Id sessionID = new Id("3");
-        public static final Id limit = new Id("4");
+        public static final Id vmId = new Id("vmId");
+        public static final Id agentId = new Id("agentId");
+        public static final Id timeStampGEQ = new Id("timeStampGEQ");
+        public static final Id timeStampLEQ = new Id("timeStampLEQ");
+        public static final Id sessionID = new Id("sessionID");
+        public static final Id limit = new Id("limit");
 
     }
 
@@ -76,7 +77,9 @@
                 (ThreadSession.class);
         final Map<String, FieldDescriptor> map = StatementUtils.createDescriptorMap(descriptors);
 
-        criteria.add(new WhereCriterion(CriteriaId.getVmId, map.get("vmId"),
+        criteria.add(new WhereCriterion(CriteriaId.vmId, map.get("vmId"),
+                                        TypeMapper.Criteria.Equal));
+        criteria.add(new WhereCriterion(CriteriaId.agentId, map.get("agentId"),
                                         TypeMapper.Criteria.Equal));
         criteria.add(new WhereCriterion(CriteriaId.sessionID, map.get("session"),
                                         TypeMapper.Criteria.Equal));
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadContentionSample.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadContentionSample.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,12 +38,13 @@
 
 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 ThreadContentionSample extends ThreadPojo implements TimeStampedPojo {
+public class ThreadContentionSample extends BasePojo implements TimeStampedPojo {
 
     private long blockedCount;
     private long waitedCount;
@@ -55,11 +56,11 @@
     private long timestamp;
 
     public ThreadContentionSample() {
-        this(null, null);
+        this(null);
     }
 
-    public ThreadContentionSample(String wID, ThreadHeader header) {
-        super(wID, header);
+    public ThreadContentionSample(String wID) {
+        super(wID);
     }
 
     @Override
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadHeader.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.model;
-
-import java.util.Objects;
-import java.util.UUID;
-
-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;
-
-/**
- * The common {@link Entity} for Thread related informations. 
- */
-@Entity
-public class ThreadHeader extends BasePojo implements TimeStampedPojo {
-
-    private long timestamp;
-    private String vmId;
-    private String name;
-    private long threadID;
-
-    private String referenceID;
-
-    public ThreadHeader() {
-        this(null);
-    }
-
-    public ThreadHeader(String writerId) {
-        this(writerId, UUID.randomUUID().toString());
-    }
-
-    public ThreadHeader(String writerId, String referenceID) {
-        super(writerId);
-        this.referenceID = referenceID;
-    }
-
-    @Persist
-    public String getReferenceID() {
-        return referenceID;
-    }
-
-    @Persist
-    public void setReferenceID(String referenceID) {
-        this.referenceID = referenceID;
-    }
-
-    @Persist
-    public void setThreadName(String threadName) {
-        this.name = threadName;
-    }
-
-    @Persist
-    public void setThreadId(long threadID) {
-        this.threadID = threadID;
-    }
-
-    @Persist
-    public String getThreadName() {
-        return name;
-    }
-    
-    @Persist
-    public long getThreadId() {
-        return threadID;
-    }
-    
-    @Persist
-    public void setVmId(String vmId) {
-        this.vmId = vmId;
-    }
-
-    @Persist
-    public String getVmId() {
-        return vmId;
-    }
-    
-    @Override
-    @Persist
-    public long getTimeStamp() {
-        return timestamp;
-    }
-    
-    @Persist
-    public void setTimeStamp(long timestamp) {
-        this.timestamp = timestamp;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = super.hashCode();
-        result = prime * result + ((name == null) ? 0 : name.hashCode());
-        result = prime * result + (int) (threadID ^ (threadID >>> 32));
-        result = prime * result + ((vmId == null) ? 0 : vmId.hashCode());
-        
-        String agentID = getAgentId();
-        result = prime * result + ((agentID == null) ? 0 : agentID.hashCode());
-        
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!super.equals(obj)) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        ThreadHeader other = (ThreadHeader) obj;
-        if (name == null) {
-            if (other.name != null) {
-                return false;
-            }
-        } else if (!name.equals(other.name)) {
-            return false;
-        }
-        if (threadID != other.threadID) {
-            return false;
-        }
-        if (vmId == null) {
-            if (other.vmId != null) {
-                return false;
-            }
-        } else if (!vmId.equals(other.vmId)) {
-            return false;
-        }
-        
-        String agentID = getAgentId();
-        String otherAgentID = getAgentId();
-        return Objects.equals(agentID, otherAgentID);
-    }
-
-    @Override
-    public String toString() {
-        return "ThreadHeader [name=" + name + ", threadID=" + threadID
-                + ", vmId=" + vmId + ", timestamp=" + timestamp + "]";
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadPojo.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.model;
-
-import com.redhat.thermostat.storage.core.Persist;
-import com.redhat.thermostat.storage.model.BasePojo;
-
-/**
- */
-public class ThreadPojo extends BasePojo {
-
-    private ThreadHeader header;
-    private String referenceID;
-    private String sessionID;
-
-    private String vmId;
-
-    public ThreadPojo() {
-        this(null, null);
-    }
-
-    public ThreadPojo(String wID, ThreadHeader header) {
-        super(wID);
-        setHeader(header);
-    }
-
-    public void setHeader(ThreadHeader header) {
-        this.header = header;
-        if (header != null) {
-            referenceID = header.getReferenceID();
-        }
-    }
-
-    @Persist
-    public void setVmId(String vmId) {
-        this.vmId = vmId;
-    }
-
-    @Persist
-    public String getVmId() {
-        return vmId;
-    }
-
-    public ThreadHeader getHeader() {
-        return header;
-    }
-
-    @Persist
-    public void setReferenceID(String referenceID) {
-        this.referenceID = referenceID;
-    }
-
-    @Persist
-    public String getReferenceID() {
-        return referenceID;
-    }
-
-    @Persist
-    public void setSessionID(String sessionID) {
-        this.sessionID = sessionID;
-    }
-
-    public String getSessionID() {
-        return sessionID;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-        if (!super.equals(o)) return false;
-
-        ThreadPojo that = (ThreadPojo) o;
-
-        if (header != null ? !header.equals(that.header) : that.header != null)
-            return false;
-        if (sessionID != null ? !sessionID.equals(that.sessionID) : that.sessionID != null)
-            return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = super.hashCode();
-        result = 31 * result + (header != null ? header.hashCode() : 0);
-        result = 31 * result + (sessionID != null ? sessionID.hashCode() : 0);
-        return result;
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,53 +36,71 @@
 
 package com.redhat.thermostat.thread.model;
 
-import com.redhat.thermostat.common.model.Range;
 import com.redhat.thermostat.storage.core.Entity;
 import com.redhat.thermostat.storage.core.Persist;
+import com.redhat.thermostat.storage.core.experimental.statement.Category;
+import com.redhat.thermostat.storage.core.experimental.statement.Indexed;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.storage.model.TimeStampedPojo;
+import com.redhat.thermostat.thread.dao.impl.ThreadDaoCategories;
 
 /**
  * Represents a single delta variation of a Thread state.
  */
+@Category(ThreadDaoCategories.Categories.STATE)
 @Entity
-public class ThreadState extends ThreadPojo {
+public class ThreadState extends BasePojo implements TimeStampedPojo {
 
-    private long probeStartTime;
-    private long probeEndTime;
-    
+    private long timeStamp;
+    private String vmId;
+    private String name;
     private String state;
+    private String session;
+    private long id;
+    private boolean suspended;
+    private boolean inNative;
 
     public ThreadState() {
-        this(null, null);
+        this(null);
     }
     
-    public ThreadState(String wID, ThreadHeader header) {
-        super(wID, header);
+    public ThreadState(String writerId) {
+        super(writerId);
+    }
+
+    @Persist
+    public String getName() {
+        return name;
     }
 
     @Persist
-    public void setProbeEndTime(long probeEndTime) {
-        this.probeEndTime = probeEndTime;
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Indexed
+    @Persist
+    public String getVmId() {
+        return vmId;
+    }
+
+    @Indexed
+    @Persist
+    public void setVmId(String vmId) {
+        this.vmId = vmId;
     }
 
     @Persist
-    public void setProbeStartTime(long probeStartTime) {
-        this.probeStartTime = probeStartTime;
-    }
-    
-    @Persist
-    public long getProbeEndTime() {
-        return probeEndTime;
+    @Override
+    public long getTimeStamp() {
+        return timeStamp;
     }
-    
+
     @Persist
-    public long getProbeStartTime() {
-        return probeStartTime;
+    public void setTimeStamp(long timeStamp) {
+        this.timeStamp = timeStamp;
     }
-    
-    public Range<Long> getRange() {
-        return new Range<Long>(probeStartTime, probeEndTime);
-    }
-    
+
     @Persist
     public String getState() {
         return state;
@@ -95,27 +113,69 @@
 
     @Override
     public String toString() {
-        ThreadHeader header = getHeader();
-        String name = null;
-        if (header != null) {
-            name = header.getThreadName();
-        }
-        return "ThreadState: [name: " + name + "state: " + getState() + ", range: " + getRange() + "]";
+        return "ThreadState: [name: " + name + " state: " + getState() +
+               ", timestamp: " + timeStamp + "]";
+    }
+
+    @Persist
+    public void setSession(String session) {
+        this.session = session;
+    }
+
+    @Persist
+    public String getSession() {
+        return session;
+    }
+
+    @Persist
+    public void setSuspended(boolean suspended) {
+        this.suspended = suspended;
+    }
+
+    @Persist
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    @Persist
+    public long getId() {
+        return id;
+    }
+
+    @Persist
+    public boolean isSuspended() {
+        return suspended;
+    }
+
+    @Persist
+    public void setInNative(boolean inNative) {
+        this.inNative = inNative;
+    }
+
+    @Persist
+    public boolean isInNative() {
+        return inNative;
     }
 
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
+        if (!super.equals(o)) return false;
 
         ThreadState that = (ThreadState) o;
 
-        if (probeEndTime != that.probeEndTime) return false;
-        if (probeStartTime != that.probeStartTime) return false;
-
-        if (!super.equals(o)) return false;
-
-        if (state != that.state) return false;
+        if (id != that.id) return false;
+        if (inNative != that.inNative) return false;
+        if (suspended != that.suspended) return false;
+        if (name != null ? !name.equals(that.name) : that.name != null)
+            return false;
+        if (session != null ? !session.equals(that.session) : that.session != null)
+            return false;
+        if (state != null ? !state.equals(that.state) : that.state != null)
+            return false;
+        if (vmId != null ? !vmId.equals(that.vmId) : that.vmId != null)
+            return false;
 
         return true;
     }
@@ -123,9 +183,13 @@
     @Override
     public int hashCode() {
         int result = super.hashCode();
-        result = 31 * result + (int) (probeStartTime ^ (probeStartTime >>> 32));
-        result = 31 * result + (int) (probeEndTime ^ (probeEndTime >>> 32));
+        result = 31 * result + (vmId != null ? vmId.hashCode() : 0);
+        result = 31 * result + (name != null ? name.hashCode() : 0);
         result = 31 * result + (state != null ? state.hashCode() : 0);
+        result = 31 * result + (session != null ? session.hashCode() : 0);
+        result = 31 * result + (int) (id ^ (id >>> 32));
+        result = 31 * result + (suspended ? 1 : 0);
+        result = 31 * result + (inNative ? 1 : 0);
         return result;
     }
 }
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistrationTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistrationTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -64,8 +64,6 @@
         assertFalse("null descriptor not allowed", categories.contains(null));
 
         assertTrue(categories.contains(ThreadDao.DEADLOCK_INFO.getName()));
-        assertTrue(categories.contains(ThreadDao.THREAD_HEADER.getName()));
-        assertTrue(categories.contains(ThreadDao.THREAD_STATE.getName()));
         assertTrue(categories.contains(ThreadDao.THREAD_HARVESTING_STATUS.getName()));
         assertTrue(categories.contains(ThreadDao.THREAD_CONTENTION_SAMPLE.getName()));
 
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -36,7 +36,6 @@
 
 package com.redhat.thermostat.thread.dao.impl;
 
-import com.redhat.thermostat.common.model.Range;
 import com.redhat.thermostat.storage.core.Cursor;
 import com.redhat.thermostat.storage.core.DescriptorParsingException;
 import com.redhat.thermostat.storage.core.HostRef;
@@ -48,22 +47,17 @@
 import com.redhat.thermostat.storage.model.Pojo;
 import com.redhat.thermostat.thread.dao.ThreadDao;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.VmDeadLockData;
-import java.util.List;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Matchers.any;
 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;
@@ -92,18 +86,6 @@
         String expectedQueryLatestHarvestingStatus = "QUERY vm-thread-harvesting WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC LIMIT 1";
         assertEquals(expectedQueryLatestHarvestingStatus, ThreadDaoImpl.QUERY_LATEST_HARVESTING_STATUS);
 
-        String expectedQueryThreadHeader = "QUERY vm-thread-header WHERE 'agentId' = ?s AND 'vmId' = ?s AND 'threadName' = ?s AND 'threadId' = ?l LIMIT 1";
-        assertEquals(expectedQueryThreadHeader, ThreadDaoImpl.QUERY_THREAD_HEADER);
-
-        String expectedQueryAllThreadHeaders = "QUERY vm-thread-header WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC";
-        assertEquals(expectedQueryAllThreadHeaders, ThreadDaoImpl.QUERY_ALL_THREAD_HEADERS);
-
-        String expectedQueryLatestThreadStateForThread = "QUERY vm-thread-state WHERE 'agentId' = ?s AND 'referenceID' = ?s SORT 'probeEndTime' DSC LIMIT 1";
-        assertEquals(expectedQueryLatestThreadStateForThread, ThreadDaoImpl.QUERY_LATEST_THREAD_STATE_FOR_THREAD);
-
-        String expectedQueryOldestThreadState = "QUERY vm-thread-state WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'probeStartTime' ASC LIMIT 1";
-        assertEquals(expectedQueryOldestThreadState, ThreadDaoImpl.QUERY_OLDEST_THREAD_STATE);
-
         String expectedQueryThreadLatestDeadlockInfo = "QUERY vm-deadlock-data WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC LIMIT 1";
         assertEquals(expectedQueryThreadLatestDeadlockInfo, ThreadDaoImpl.QUERY_LATEST_DEADLOCK_INFO);
 
@@ -119,25 +101,6 @@
                                     "'threadId' = ?l , " +
                                     "'timeStamp' = ?l , " +
                                     "'referenceID' = ?s";
-        assertEquals(addThreadInfo, ThreadDaoImpl.ADD_THREAD_HEADER);
-        String addDeadlockData = "ADD vm-deadlock-data SET 'agentId' = ?s , " +
-                                    "'vmId' = ?s , " +
-                                    "'timeStamp' = ?l , " +
-                                    "'deadLockDescription' = ?s";
-        assertEquals(addDeadlockData, ThreadDaoImpl.DESC_ADD_THREAD_DEADLOCK_DATA);
-
-        String addThreadState = "ADD vm-thread-state SET 'agentId' = ?s , " +
-                                "'vmId' = ?s , 'state' = ?s , " +
-                                "'probeStartTime' = ?l , 'probeEndTime' = ?l , " +
-                                "'referenceID' = ?s";
-        assertEquals(addThreadState, ThreadDaoImpl.ADD_THREAD_STATE);
-
-        String getThreadStatesForVM = "QUERY vm-thread-state WHERE " +
-                                      "'referenceID' = ?s AND " +
-                                      "'probeEndTime' >= ?l AND " +
-                                      "'probeStartTime' <= ?l SORT " +
-                                      "'probeStartTime' ASC";
-        assertEquals(getThreadStatesForVM, ThreadDaoImpl.QUERY_THREAD_STATE_PER_THREAD);
 
         String addContentionSample = "ADD thread-contention-sample SET " +
                 "'agentId' = ?s , 'vmId' = ?s , 'blockedCount' = ?l , " +
@@ -152,7 +115,6 @@
         String getFirstThreadState = "QUERY vm-thread-state WHERE " +
                 "'agentId' = ?s AND 'referenceID' = ?s SORT 'probeStartTime' " +
                 "ASC LIMIT 1";
-        assertEquals(getFirstThreadState, ThreadDaoImpl.QUERY_FIRST_THREAD_STATE_FOR_THREAD);
     }
     
     @Test
@@ -296,273 +258,5 @@
         verifyNoMoreInteractions(add);
     }
 
-    @Test
-    public void testSaveThread() throws DescriptorParsingException, StatementExecutionException {
-        final String THREAD_NAME = "name of a thread";
-        final long THREAD_ID = 0xcafebabe;
-        final long TIMESTAMP = 0xdeadbeef;
-        final String REF_ID = "42";
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadHeader> add = mock(PreparedStatement.class);
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(add);
-
-        // not using mocks because ThreadHeader is really a data holder (no logic)
-        ThreadHeader header = new ThreadHeader();
-        header.setAgentId(AGENT_ID);
-        header.setVmId(VM_ID);
-        header.setThreadName(THREAD_NAME);
-        header.setThreadId(THREAD_ID);
-        header.setTimeStamp(TIMESTAMP);
-        header.setReferenceID(REF_ID);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        dao.saveThread(header);
-
-        verify(add).setString(0, AGENT_ID);
-        verify(add).setString(1, VM_ID);
-        verify(add).setString(2, THREAD_NAME);
-        verify(add).setLong(3, THREAD_ID);
-        verify(add).setLong(4, TIMESTAMP);
-        verify(add).setString(5, REF_ID);
-
-        verify(add).execute();
-
-        verifyNoMoreInteractions(add);
-    }
-
-    @Test
-    public void testGetThreadNoData() throws DescriptorParsingException, StatementExecutionException {
-        final String THREAD_NAME = "name of a thread";
-        final long THREAD_ID = 0xcafebabe;
-        final long TIMESTAMP = 0xdeadbeef;
-        final String REF_ID = "42";
-
-        Cursor<ThreadHeader> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(false);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadHeader> get = mock(PreparedStatement.class);
-        when(get.executeQuery()).thenReturn(cursor);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(get);
-
-        ThreadHeader header = new ThreadHeader();
-        header.setAgentId(AGENT_ID);
-        header.setVmId(VM_ID);
-        header.setThreadName(THREAD_NAME);
-        header.setThreadId(THREAD_ID);
-        header.setTimeStamp(TIMESTAMP);
-        header.setReferenceID(REF_ID);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        ThreadHeader result = dao.getThread(header);
-        assertNull(result);
-
-        verify(get).setString(0, AGENT_ID);
-        verify(get).setString(1, VM_ID);
-        verify(get).setString(2, THREAD_NAME);
-        verify(get).setLong(3, THREAD_ID);
-
-        verify(get).executeQuery();
-
-        verifyNoMoreInteractions(get);
-    }
-
-    @Test
-    public void testGetThreadWithData() throws DescriptorParsingException, StatementExecutionException {
-        final String THREAD_NAME = "name of a thread";
-        final long THREAD_ID = 0xcafebabe;
-        final long TIMESTAMP = 0xdeadbeef;
-        final String REF_ID = "42";
-
-        ThreadHeader header = new ThreadHeader();
-        header.setAgentId(AGENT_ID);
-        header.setVmId(VM_ID);
-        header.setThreadName(THREAD_NAME);
-        header.setThreadId(THREAD_ID);
-        header.setTimeStamp(TIMESTAMP);
-        header.setReferenceID(REF_ID);
-
-        Cursor<ThreadHeader> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(header).thenReturn(null);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadHeader> get = mock(PreparedStatement.class);
-        when(get.executeQuery()).thenReturn(cursor);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(get);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        ThreadHeader result = dao.getThread(header);
-        assertNotNull(result);
-        assertEquals(header, result);
-
-        verify(get).setString(0, AGENT_ID);
-        verify(get).setString(1, VM_ID);
-        verify(get).setString(2, THREAD_NAME);
-        verify(get).setLong(3, THREAD_ID);
-
-        verify(get).executeQuery();
-
-        verifyNoMoreInteractions(get);
-    }
-
-    @Test
-    public void testGetThreads() throws DescriptorParsingException, StatementExecutionException {
-
-        ThreadHeader header0 = mock(ThreadHeader.class);
-        ThreadHeader header1 = mock(ThreadHeader.class);
-        ThreadHeader header2 = mock(ThreadHeader.class);
-
-        Cursor<ThreadHeader> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(header0).thenReturn(header1).thenReturn(header2).thenReturn(null);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadHeader> get = mock(PreparedStatement.class);
-        when(get.executeQuery()).thenReturn(cursor);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(get);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        List<ThreadHeader> result = dao.getThreads(vmRef);
-        assertNotNull(result);
-        assertEquals(result.size(), 3);
-        assertEquals(result.get(0), header0);
-        assertEquals(result.get(1), header1);
-        assertEquals(result.get(2), header2);
-
-        verify(get).setString(0, AGENT_ID);
-        verify(get).setString(1, VM_ID);
-
-        verify(get).executeQuery();
-
-        verifyNoMoreInteractions(get);
-    }
-
-    @Test
-    public void testAddThreadState() throws Exception {
-        final String VM_ID = "vm42";
-        final String AGENT_ID = "agent42";
-        final String REF_ID = "42";
-        final String STATE = "runnable";
-        final long START_TIME = 1l;
-        final long STOP_TIME = 1l;
-
-        ThreadHeader header = new ThreadHeader(AGENT_ID);
-        header.setVmId(VM_ID);
-        header.setReferenceID(REF_ID);
-
-        ThreadState template = mock(ThreadState.class);
-        when(template.getHeader()).thenReturn(header);
-        when(template.getState()).thenReturn(STATE);
-        when(template.getProbeStartTime()).thenReturn(START_TIME);
-        when(template.getProbeEndTime()).thenReturn(STOP_TIME);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadState> set = mock(PreparedStatement.class);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(set);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        dao.addThreadState(template);
-
-        verify(set).setString(0, AGENT_ID);
-        verify(set).setString(1, VM_ID);
-        verify(set).setString(2, STATE);
-        verify(set).setLong(3, START_TIME);
-        verify(set).setLong(4, STOP_TIME);
-        verify(set).setString(5, REF_ID);
-
-        verify(set).execute();
-    }
-
-    @Test
-    public void testUpdateThreadState() throws Exception {
-        final String REF_ID = "42";
-        final long START_TIME = 0l;
-        final long STOP_TIME = 1l;
-
-        ThreadHeader header = new ThreadHeader(AGENT_ID);
-        header.setVmId(VM_ID);
-        header.setReferenceID(REF_ID);
-
-        ThreadState template = mock(ThreadState.class);
-        when(template.getHeader()).thenReturn(header);
-        when(template.getProbeStartTime()).thenReturn(START_TIME);
-        when(template.getProbeEndTime()).thenReturn(STOP_TIME);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadState> update = mock(PreparedStatement.class);
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(update);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        dao.updateThreadState(template);
-
-        verify(update).setLong(0, STOP_TIME);
-        verify(update).setString(1, REF_ID);
-        verify(update).setLong(2, START_TIME);
-
-        verify(update).execute();
-    }
-
-    @Test
-    public void testGetThreadStateTotalTimeRange() throws Exception {
-
-        Range<Long> oldest = new Range(0l, 1l);
-        ThreadState oldestData = mock(ThreadState.class);
-        when(oldestData.getRange()).thenReturn(oldest);
-
-        Range<Long> latest = new Range(2l, 3l);
-        ThreadState latestData = mock(ThreadState.class);
-        when(latestData.getRange()).thenReturn(latest);
-
-        Cursor<ThreadState> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false);
-        when(cursor.next()).thenReturn(oldestData).thenReturn(latestData);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadState> get = mock(PreparedStatement.class);
-        when(get.executeQuery()).thenReturn(cursor);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(get);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        Range<Long> result = dao.getThreadStateTotalTimeRange(vmRef);
-        assertNotNull(result);
-
-        verify(get, times(2)).setString(0, AGENT_ID);
-        verify(get, times(2)).setString(1, VM_ID);
-
-        verify(get, times(2)).executeQuery();
-
-        verifyNoMoreInteractions(get);
-    }
-
-    @Test
-    public void testGetThreadStateTotalTimeRangeNoData() throws Exception {
-
-        Cursor<ThreadState> cursor = mock(Cursor.class);
-        when(cursor.hasNext()).thenReturn(false);
-
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadState> get = mock(PreparedStatement.class);
-        when(get.executeQuery()).thenReturn(cursor);
-
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(get);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        Range<Long> result = dao.getThreadStateTotalTimeRange(vmRef);
-        assertNull(result);
-
-        verify(get).setString(0, AGENT_ID);
-        verify(get).setString(1, VM_ID);
-
-        verify(get).executeQuery();
-
-        verifyNoMoreInteractions(get);
-    }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueriesTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2014 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.thread.dao.impl.statement;
+
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
+import com.redhat.thermostat.thread.model.ThreadSession;
+import java.util.Set;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class SessionQueriesTest {
+
+    @Test
+    public void testDescribe() throws Exception {
+        BeanAdapter<ThreadSession> session =
+                new BeanAdapterBuilder<>(ThreadSession.class,
+                                         SessionQueries.asList()).build();
+        Set<String> statements = session.describeStatements();
+        assertEquals(3, statements.size());
+
+        String expected = "QUERY vm-thread-session WHERE 'vmId' = ?s AND 'agentId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' ASC LIMIT ?i";
+        assertTrue(statements.contains(expected));
+
+        expected = "QUERY vm-thread-session WHERE 'vmId' = ?s AND 'agentId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
+        assertTrue(statements.contains(expected));
+    }
+}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueryTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.dao.impl.statement;
-
-import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
-import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
-import com.redhat.thermostat.thread.model.ThreadSession;
-import java.util.Set;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class SessionQueryTest {
-
-    @Test
-    public void testDescribe() throws Exception {
-        BeanAdapter<ThreadSession> session =
-                new BeanAdapterBuilder<>(ThreadSession.class,
-                                         new SessionQuery()).build();
-        Set<String> statements = session.describeStatements();
-        assertEquals(2, statements.size());
-
-        String expected = "QUERY vm-thread-session WHERE 'vmId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
-        assertTrue(statements.contains(expected));
-    }
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQueryTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SummaryQueryTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -54,7 +54,7 @@
         Set<String> statements = session.describeStatements();
         assertEquals(2, statements.size());
 
-        String expected = "QUERY vm-thread-summary WHERE 'vmId' = ?s AND 'session' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
+        String expected = "QUERY vm-thread-summary WHERE 'vmId' = ?s AND 'agentId' = ?s AND 'session' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
         assertTrue(statements.contains(expected));
     }
 }
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadHeaderTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.model;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-public class ThreadHeaderTest {
-
-    @Test
-    public void testEqualsAndHashCode() throws Exception {
-        // same thread, will they be equals?
-        ThreadHeader header1 = new ThreadHeader("1234");
-        header1.setReferenceID("vm42");
-        header1.setThreadName("main");
-        header1.setThreadId(1);
-
-        ThreadHeader header2 = new ThreadHeader("1234");
-        header2.setReferenceID("vm42");
-        header2.setThreadName("main");
-        header2.setThreadId(1);
-
-        assertEquals(header1, header2);
-        assertEquals(header1.hashCode(), header2.hashCode());
-    }
-
-    @Test
-    public void testNotEqualsAndHashCode() throws Exception {
-        // same thread, will they be equals?
-        ThreadHeader header1 = new ThreadHeader("1234");
-        header1.setReferenceID("vm42");
-        header1.setThreadName("main");
-        header1.setThreadId(2);
-
-        ThreadHeader header2 = new ThreadHeader("1234");
-        header2.setReferenceID("vm42");
-        header2.setThreadName("main");
-        header2.setThreadId(1);
-
-        assertFalse(header1.equals(header2));
-        assertFalse(header1.hashCode() == header2.hashCode());
-    }
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadModelPojosTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadModelPojosTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -47,7 +47,6 @@
     private static final Class<?>[] CLASSES_LIST = new Class[] {
         ThreadHarvestingStatus.class,
         ThreadState.class,
-        ThreadHeader.class,
         ThreadSummary.class,
         ThreadContentionSample.class,
         VmDeadLockData.class,
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadPojoTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.model;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-public class ThreadPojoTest {
-
-    @Test
-    public void testEqualsAndHashCode() throws Exception {
-
-        ThreadHeader header1 = new ThreadHeader("1234");
-        header1.setReferenceID("vm42");
-        header1.setThreadName("main");
-        header1.setThreadId(1);
-
-        ThreadPojo pojo1 = new ThreadPojo("1234", header1);
-        ThreadPojo pojo2 = new ThreadPojo("1234", header1);
-
-        assertEquals(pojo1, pojo2);
-        assertEquals(pojo1.hashCode(), pojo2.hashCode());
-
-        ThreadHeader header2 = new ThreadHeader("12344");
-        pojo2.setHeader(header2);
-
-        assertFalse(pojo1.equals(pojo2));
-        assertFalse(pojo1.hashCode() == pojo2.hashCode());
-    }
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/model/ThreadStateTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.model;
-
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-public class ThreadStateTest {
-
-    @Test
-    public void testEqualsAndHashCode() throws Exception {
-
-        ThreadHeader header1 = new ThreadHeader("1234");
-        header1.setReferenceID("vm42");
-        header1.setThreadName("main");
-        header1.setThreadId(1);
-
-        ThreadState state1 = new ThreadState("1234", header1);
-        state1.setProbeStartTime(0l);
-        state1.setProbeEndTime(1l);
-        state1.setState("NEW");
-
-        ThreadState state2 = new ThreadState("1234", header1);
-        state2.setProbeStartTime(0l);
-        state2.setProbeEndTime(1l);
-        state2.setState("NEW");
-
-        assertEquals(state1, state2);
-        assertEquals(state1.hashCode(), state2.hashCode());
-
-        ThreadHeader header2 = new ThreadHeader("12344");
-        state2.setHeader(header2);
-
-        assertFalse(state1.equals(state2));
-        assertFalse(state1.hashCode() == state2.hashCode());
-
-        state2.setHeader(header1);
-        state2.setProbeStartTime(1l);
-        state2.setProbeEndTime(1l);
-        state2.setState("NEW");
-
-        assertFalse(state1.equals(state2));
-        assertFalse(state1.hashCode() == state2.hashCode());
-    }
-}
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/HarvesterHelper.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/HarvesterHelper.java	Thu Dec 11 11:44:42 2014 +0100
@@ -39,8 +39,6 @@
 import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadContentionSample;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -55,24 +53,21 @@
     private String vmId;
 
     private ThreadSummaryHelper summaryHelper;
-    private ThreadHeaderHelper headerHelper;
     private ThreadStateHelper stateHelper;
     private ThreadContentionHelper contentionHelper;
     private ThreadSessionHelper sessionHelper;
 
     HarvesterHelper(ThreadDao threadDao, Clock clock, String vmId, WriterID writerId)
     {
-        this(threadDao, clock, vmId,
+        this(clock, vmId,
              new ThreadSummaryHelper(threadDao, writerId, vmId),
-             new ThreadHeaderHelper(threadDao, writerId, vmId),
              new ThreadStateHelper(threadDao, writerId, vmId),
              new ThreadContentionHelper(threadDao, writerId, vmId),
              new ThreadSessionHelper(threadDao, writerId, vmId, clock));
     }
 
-    HarvesterHelper(ThreadDao threadDao, Clock clock, String vmId,
+    HarvesterHelper(Clock clock, String vmId,
                     ThreadSummaryHelper summaryHelper,
-                    ThreadHeaderHelper headerHelper,
                     ThreadStateHelper stateHelper,
                     ThreadContentionHelper contentionHelper,
                     ThreadSessionHelper sessionHelper)
@@ -81,7 +76,6 @@
         this.clock = clock;
 
         this.summaryHelper = summaryHelper;
-        this.headerHelper = headerHelper;
         this.stateHelper = stateHelper;
 
         this.contentionHelper = contentionHelper;
@@ -116,21 +110,19 @@
 
             ThreadInfo beanInfo = threadInfos[i];
 
-            // common header
-            ThreadHeader header = headerHelper.createThreadHeader(beanInfo, timestamp);
-            header = headerHelper.checkAndSaveThreadHeader(header);
-
             // state information
-            ThreadState state = stateHelper.createThreadState(header, beanInfo,
-                                                              timestamp);
+            ThreadState state =
+                    stateHelper.createThreadState(beanInfo,
+                                                  session.getSessionID(),
+                                                  timestamp);
             stateHelper.saveThreadState(state);
-
-            // contention information
-            ThreadContentionSample contentionSample =
-                    contentionHelper.createThreadContentionSample(header,
-                                                                  beanInfo,
-                                                                  timestamp);
-            contentionHelper.saveContentionSample(contentionSample);
+//
+//            // contention information
+//            ThreadContentionSample contentionSample =
+//                    contentionHelper.createThreadContentionSample(header,
+//                                                                  beanInfo,
+//                                                                  timestamp);
+//            contentionHelper.saveContentionSample(contentionSample);
         }
     }
 
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadContentionHelper.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadContentionHelper.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,9 +38,6 @@
 
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadContentionSample;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import java.lang.management.ThreadInfo;
 
 /**
  */
@@ -56,23 +53,23 @@
         this.vmId = vmId;
     }
 
-    public ThreadContentionSample createThreadContentionSample(ThreadHeader header,
-                                                               ThreadInfo beanInfo,
-                                                               long timestamp)
-    {
-        String wId = writerId.getWriterID();
-
-        ThreadContentionSample sample = new ThreadContentionSample(wId, header);
-        sample.setTimeStamp(timestamp);
-        sample.setBlockedCount(beanInfo.getBlockedCount());
-        sample.setBlockedTime(beanInfo.getBlockedTime());
-        sample.setWaitedCount(beanInfo.getWaitedCount());
-        sample.setWaitedTime(beanInfo.getWaitedTime());
-
-        return sample;
-    }
-
-    public void saveContentionSample(ThreadContentionSample contentionSample) {
-        threadDao.saveContentionSample(contentionSample);
-    }
+//    public ThreadContentionSample createThreadContentionSample(ThreadHeader header,
+//                                                               ThreadInfo beanInfo,
+//                                                               long timestamp)
+//    {
+//        String wId = writerId.getWriterID();
+//
+//        ThreadContentionSample sample = new ThreadContentionSample(wId);
+//        sample.setTimeStamp(timestamp);
+//        sample.setBlockedCount(beanInfo.getBlockedCount());
+//        sample.setBlockedTime(beanInfo.getBlockedTime());
+//        sample.setWaitedCount(beanInfo.getWaitedCount());
+//        sample.setWaitedTime(beanInfo.getWaitedTime());
+//
+//        return sample;
+//    }
+//
+//    public void saveContentionSample(ThreadContentionSample contentionSample) {
+//        threadDao.saveContentionSample(contentionSample);
+//    }
 }
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadHeaderHelper.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import java.lang.management.ThreadInfo;
-
-import com.redhat.thermostat.storage.core.WriterID;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import com.redhat.thermostat.thread.model.ThreadState;
-
-/**
- */
-class ThreadHeaderHelper {
-
-    private ThreadDao threadDao;
-    private String vmId;
-    private WriterID writerId;
-
-    public ThreadHeaderHelper(ThreadDao threadDao, WriterID writerId, String vmId)
-    {
-        this.threadDao = threadDao;
-        this.vmId = vmId;
-        this.writerId = writerId;
-    }
-
-    public ThreadHeader createThreadHeader(ThreadInfo beanInfo, long timestamp)
-    {
-        String wId = writerId.getWriterID();
-
-        ThreadHeader header = new ThreadHeader(wId);
-        header.setThreadName(beanInfo.getThreadName());
-        header.setThreadId(beanInfo.getThreadId());
-        header.setTimeStamp(timestamp);
-        header.setVmId(vmId);
-
-        return header;
-    }
-
-    /**
-     * Checks if the template is in the database. If the template is not in
-     * the database, it is added and the same object is returned, otherwise
-     * the database object is returned with the correct referenceID field.
-     * The input template header itself is never updated.
-     */
-    public ThreadHeader checkAndSaveThreadHeader(ThreadHeader template) {
-        ThreadHeader inStorage = threadDao.getThread(template);
-        if (inStorage == null || !template.equals(inStorage)) {
-            threadDao.saveThread(template);
-            inStorage = template;
-        }
-        return inStorage;
-    }
-}
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadStateHelper.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadStateHelper.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,50 +38,43 @@
 
 import com.redhat.thermostat.storage.core.WriterID;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadHeader;
+import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadState;
-
 import java.lang.management.ThreadInfo;
 
 public class ThreadStateHelper {
 
     private ThreadDao threadDao;
-    private WriterID writerId;
-    private String vmId;
+    private final WriterID writerId;
+    private final String vmId;
 
-    public ThreadStateHelper(ThreadDao threadDao, WriterID writerId, String vmId) {
+    public ThreadStateHelper(ThreadDao threadDao,
+                             WriterID writerId, String vmId)
+    {
+        this.threadDao = threadDao;
         this.writerId = writerId;
         this.vmId = vmId;
-        this.threadDao = threadDao;
     }
 
-    public ThreadState createThreadState(ThreadHeader header, ThreadInfo beanInfo,
+    public ThreadState createThreadState(ThreadInfo beanInfo,
+                                         SessionID sessionID,
                                          long timestamp)
     {
-        String wId = writerId.getWriterID();
+        ThreadState state = new ThreadState(writerId.getWriterID());
 
-        ThreadState state = new ThreadState(wId, header);
         state.setState(beanInfo.getThreadState().name());
-        state.setProbeStartTime(timestamp);
-        state.setProbeEndTime(timestamp);
+        state.setTimeStamp(timestamp);
+        state.setSession(sessionID.get());
+        state.setVmId(vmId);
+        state.setName(beanInfo.getThreadName());
+        state.setId(beanInfo.getThreadId());
+        state.setSuspended(beanInfo.isSuspended());
+        state.setInNative(beanInfo.isInNative());
 
         return state;
     }
 
-    public ThreadState saveThreadState(ThreadState thread) {
-
-        ThreadHeader header = thread.getHeader();
-        ThreadState lastState = threadDao.getLastThreadState(header);
-        if (lastState == null || !lastState.getState().equals(thread.getState()))
-        {
-            threadDao.addThreadState(thread);
-            lastState = thread;
-
-        } else {
-            // update
-            lastState.setProbeEndTime(thread.getProbeEndTime());
-            threadDao.updateThreadState(lastState);
-        }
-        return lastState;
+    public void saveThreadState(ThreadState thread) {
+        threadDao.addThreadState(thread);
     }
 }
--- a/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadSummaryHelper.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/main/java/com/redhat/thermostat/thread/harvester/ThreadSummaryHelper.java	Thu Dec 11 11:44:42 2014 +0100
@@ -49,15 +49,15 @@
     private String vmId;
     private WriterID writerId;
     private ThreadDao threadDao;
-
     ThreadSummaryHelper(ThreadDao threadDao, WriterID writerId, String vmId) {
         this.vmId = vmId;
         this.writerId = writerId;
         this.threadDao = threadDao;
     }
 
-    ThreadSummary createThreadSummary(ThreadMXBean collectorBean, long timestamp, ThreadSession session) {
-
+    ThreadSummary createThreadSummary(ThreadMXBean collectorBean,
+                                      long timestamp, ThreadSession session)
+    {
         String wId = writerId.getWriterID();
 
         ThreadSummary summary = new ThreadSummary(wId);
--- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterHelperTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/HarvesterHelperTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -38,7 +38,6 @@
 
 import com.redhat.thermostat.common.Clock;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadHeader;
 import com.redhat.thermostat.thread.model.ThreadSession;
 import com.redhat.thermostat.thread.model.ThreadState;
 import com.redhat.thermostat.thread.model.ThreadSummary;
@@ -50,7 +49,6 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -66,7 +64,6 @@
     private String vmId;
 
     private ThreadSummaryHelper summaryHelper;
-    private ThreadHeaderHelper headerHelper;
     private ThreadStateHelper stateHelper;
     private ThreadContentionHelper contentionHelper;
     private ThreadSessionHelper threadSessionHelper;
@@ -76,7 +73,6 @@
     @Before
     public void setUp() {
         summaryHelper = mock(ThreadSummaryHelper.class);
-        headerHelper = mock(ThreadHeaderHelper.class);
         stateHelper = mock(ThreadStateHelper.class);
 
         contentionHelper = mock(ThreadContentionHelper.class);
@@ -104,9 +100,8 @@
                                                session)).
             thenReturn(summary);
 
-        HarvesterHelper harvester = new HarvesterHelper(threadDao, clock, vmId,
+        HarvesterHelper harvester = new HarvesterHelper(clock, vmId,
                                                         summaryHelper,
-                                                        headerHelper,
                                                         stateHelper,
                                                         contentionHelper,
                                                         threadSessionHelper);
@@ -125,9 +120,8 @@
         long[] ids = new long[] {0l, 1l, 2l};
         when(collectorBean.getAllThreadIds()).thenReturn(ids);
 
-        HarvesterHelper harvester = new HarvesterHelper(threadDao, clock, vmId,
+        HarvesterHelper harvester = new HarvesterHelper(clock, vmId,
                                                         summaryHelper,
-                                                        headerHelper,
                                                         stateHelper,
                                                         contentionHelper,
                                                         threadSessionHelper);
@@ -151,35 +145,21 @@
 
         when(collectorBean.getThreadInfo(any(long[].class), eq(true), eq(true))).thenReturn(infos);
 
-        ThreadHeader header1 = mock(ThreadHeader.class);
-        ThreadHeader header2 = mock(ThreadHeader.class);
-
-        when(headerHelper.createThreadHeader(infos[0], DEFAULT_TIMESTAMP)).thenReturn(header1);
-        when(headerHelper.createThreadHeader(infos[1], DEFAULT_TIMESTAMP)).thenReturn(header2);
-        when(headerHelper.createThreadHeader(infos[2], DEFAULT_TIMESTAMP)).thenReturn(header1);
-
-        when(headerHelper.checkAndSaveThreadHeader(header1)).thenReturn(header1);
-        when(headerHelper.checkAndSaveThreadHeader(header2)).thenReturn(header2);
-
         ThreadState state1 = mock(ThreadState.class);
         ThreadState state2 = mock(ThreadState.class);
         ThreadState state3 = mock(ThreadState.class);
 
-        when(stateHelper.createThreadState(header1, infos[0], DEFAULT_TIMESTAMP)).thenReturn(state1);
-        when(stateHelper.createThreadState(header2, infos[1], DEFAULT_TIMESTAMP)).thenReturn(state2);
-        when(stateHelper.createThreadState(header1, infos[2], DEFAULT_TIMESTAMP)).thenReturn(state3);
+        when(stateHelper.createThreadState(eq(infos[0]),eq(session.getSessionID()), eq(DEFAULT_TIMESTAMP))).thenReturn(state1);
+        when(stateHelper.createThreadState(eq(infos[1]), eq(session.getSessionID()), eq(DEFAULT_TIMESTAMP))).thenReturn(state2);
+        when(stateHelper.createThreadState(eq(infos[2]), eq(session.getSessionID()), eq(DEFAULT_TIMESTAMP))).thenReturn(state3);
 
-        HarvesterHelper harvester = new HarvesterHelper(threadDao, clock, vmId,
+        HarvesterHelper harvester = new HarvesterHelper(clock, vmId,
                                                         summaryHelper,
-                                                        headerHelper,
                                                         stateHelper,
                                                         contentionHelper,
                                                         threadSessionHelper);
         harvester.collectAndSaveThreadData(session, collectorBean);
 
-        verify(headerHelper, times(2)).checkAndSaveThreadHeader(header1);
-        verify(headerHelper).checkAndSaveThreadHeader(header2);
-
         verify(stateHelper).saveThreadState(state1);
         verify(stateHelper).saveThreadState(state2);
         verify(stateHelper).saveThreadState(state3);
--- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadHeaderHelperTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * Copyright 2012-2014 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.thread.harvester;
-
-import com.redhat.thermostat.storage.core.WriterID;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.model.ThreadHeader;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.lang.management.ThreadInfo;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.times;
-
-/*
- */
-public class ThreadHeaderHelperTest {
-
-    private static String DEFAULT_W_ID = "0xcafe";
-    private static String DEFAULT_THREAD_NAME = "Thread42";
-    private static long DEFAULT_THREAD_ID = 42;
-
-    private String vmId;
-    private WriterID writerId;
-    private ThreadDao threadDao;
-
-    private ThreadInfo info;
-
-    @Before
-    public void setUp() throws Exception {
-        vmId = "testVM";
-        writerId  = mock(WriterID.class);
-        when(writerId.getWriterID()).thenReturn(DEFAULT_W_ID);
-        
-        threadDao = mock(ThreadDao.class);
-
-        info = mock(ThreadInfo.class);
-        when(info.getThreadName()).thenReturn(DEFAULT_THREAD_NAME);
-        when(info.getThreadId()).thenReturn(DEFAULT_THREAD_ID);
-    }
-
-    @Test
-    public void testCreateThreadHeader() throws Exception {
-
-        ThreadHeaderHelper helper = new ThreadHeaderHelper(threadDao, writerId, vmId);
-        long timestamp = -1l;
-
-        ThreadHeader header = helper.createThreadHeader(info, timestamp);
-        assertNotNull(header);
-
-        verify(writerId).getWriterID();
-
-        assertEquals(header.getVmId(), vmId);
-        assertEquals(header.getAgentId(), DEFAULT_W_ID);
-        assertEquals(header.getTimeStamp(), timestamp);
-        assertEquals(header.getThreadName(), DEFAULT_THREAD_NAME);
-        assertEquals(header.getThreadId(), DEFAULT_THREAD_ID);
-    }
-
-    @Test
-    public void testSaveThreadHeader() throws Exception {
-        ThreadHeaderHelper helper = new ThreadHeaderHelper(threadDao, writerId, vmId);
-
-        ThreadHeader template = new ThreadHeader(DEFAULT_W_ID);
-        template.setReferenceID("1234");
-        template.setVmId(vmId);
-        template.setThreadName(DEFAULT_THREAD_NAME);
-        template.setThreadId(DEFAULT_THREAD_ID);
-        template.setTimeStamp(-1l);
-
-        // first time around, object not present in database
-        when(threadDao.getThread(template)).thenReturn(null);
-
-        ThreadHeader result = helper.checkAndSaveThreadHeader(template);
-        assertNotNull(result);
-
-        verify(threadDao).getThread(template);
-        verify(threadDao).saveThread(template);
-
-        assertEquals(result.getVmId(), template.getVmId());
-        assertEquals(result.getAgentId(), template.getAgentId());
-        assertEquals(result.getTimeStamp(), template.getTimeStamp());
-        assertEquals(result.getThreadName(), template.getThreadName());
-        assertEquals(result.getThreadId(), template.getThreadId());
-        assertEquals(result, template);
-    }
-
-    @Test
-    public void testSaveThreadHeader2() throws Exception {
-        ThreadHeaderHelper helper = new ThreadHeaderHelper(threadDao, writerId, vmId);
-
-        ThreadHeader template = new ThreadHeader(DEFAULT_W_ID);
-        template.setReferenceID("1234");
-        template.setVmId(vmId);
-        template.setThreadName(DEFAULT_THREAD_NAME);
-        template.setThreadId(DEFAULT_THREAD_ID);
-        template.setTimeStamp(-1l);
-
-        ThreadHeader expected = new ThreadHeader(DEFAULT_W_ID);
-        expected.setReferenceID("0000");
-        expected.setVmId(vmId);
-        expected.setThreadName(DEFAULT_THREAD_NAME);
-        expected.setThreadId(DEFAULT_THREAD_ID);
-        expected.setTimeStamp(-2l);
-
-        // second time around, object is present in database, we need
-        // to check that the two headers are actually the same, but the
-        // returned value has the correct refId and timestamp
-        when(threadDao.getThread(template)).thenReturn(expected);
-
-        ThreadHeader result = helper.checkAndSaveThreadHeader(template);
-        assertNotNull(result);
-
-        verify(threadDao).getThread(template);
-        verify(threadDao, times(0)).saveThread(template);
-
-        assertEquals(result.getVmId(), expected.getVmId());
-        assertEquals(result.getAgentId(), expected.getAgentId());
-        assertEquals(result.getTimeStamp(), expected.getTimeStamp());
-        assertEquals(result.getThreadName(), expected.getThreadName());
-        assertEquals(result.getThreadId(), expected.getThreadId());
-        assertEquals(result, expected);
-    }
-}
--- a/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadStateHelperTest.java	Thu Dec 11 01:35:29 2014 -0700
+++ b/thread/harvester/src/test/java/com/redhat/thermostat/thread/harvester/ThreadStateHelperTest.java	Thu Dec 11 11:44:42 2014 +0100
@@ -37,24 +37,17 @@
 package com.redhat.thermostat.thread.harvester;
 
 import com.redhat.thermostat.storage.core.WriterID;
-
 import com.redhat.thermostat.thread.dao.ThreadDao;
-
-import com.redhat.thermostat.thread.model.ThreadHeader;
+import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadState;
-
+import java.lang.management.ThreadInfo;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import java.lang.management.ThreadInfo;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -64,12 +57,13 @@
 
     private static String DEFAULT_W_ID = "0xcafe";
     private static String DEFAULT_HEADER_REF_ID = "42";
+    private static String DEFAULT_SESSION_ID = "This Session";
 
     private String vmId;
     private WriterID writerId;
     private ThreadDao threadDao;
 
-    private ThreadHeader header;
+    private SessionID sessionID;
 
     @Before
     public void setUp() throws Exception {
@@ -79,50 +73,33 @@
 
         threadDao = mock(ThreadDao.class);
 
-        header = mock(ThreadHeader.class);
-        when(header.getReferenceID()).thenReturn(DEFAULT_HEADER_REF_ID);
+        sessionID = mock(SessionID.class);
+        when(sessionID.get()).thenReturn(DEFAULT_SESSION_ID);
     }
 
     @Test
     public void testCreateThreadState() throws Exception {
-        ThreadStateHelper helper =
-                new ThreadStateHelper(threadDao, writerId, vmId);
+        ThreadStateHelper helper = new ThreadStateHelper(threadDao, writerId, vmId);
 
         ThreadInfo info = mock(ThreadInfo.class);
         when(info.getThreadState()).thenReturn(Thread.State.BLOCKED);
 
         long timestamp = -1l;
 
-        ThreadState state = helper.createThreadState(header, info, timestamp);
+        ThreadState state = helper.createThreadState(info, sessionID, timestamp);
         assertNotNull(state);
 
-        assertEquals(state.getProbeStartTime(), timestamp);
-        assertEquals(state.getProbeEndTime(), timestamp);
-        assertEquals(state.getState(), Thread.State.BLOCKED.name());
-        assertEquals(state.getHeader(), header);
-        assertEquals(state.getReferenceID(), DEFAULT_HEADER_REF_ID);
+        assertEquals(timestamp, state.getTimeStamp());
+        assertEquals(Thread.State.BLOCKED.name(), state.getState());
+        assertEquals(DEFAULT_SESSION_ID, state.getSession());
     }
 
     @Test
     public void testSaveThreadState() throws Exception {
-        // this test assumes there is no data in the database yet,
-        // so a database entry will be created with the template
-        // object as input
-        ThreadStateHelper helper =
-                new ThreadStateHelper(threadDao, writerId, vmId);
-
-        ThreadHeader header = mock(ThreadHeader.class);
+        ThreadStateHelper helper = new ThreadStateHelper(threadDao, writerId, vmId);
         ThreadState state = mock(ThreadState.class);
-        when(state.getState()).thenReturn(Thread.State.BLOCKED.name());
-        when(state.getHeader()).thenReturn(header);
 
-        when(threadDao.getLastThreadState(header)).thenReturn(null);
-
-        ThreadState result = helper.saveThreadState(state);
-        assertNotNull(result);
-
-        verify(threadDao).getLastThreadState(header);
-        verify(state, times(0)).getState();
+        helper.saveThreadState(state);
 
         ArgumentCaptor<ThreadState> captor =
                 ArgumentCaptor.forClass(ThreadState.class);
@@ -130,77 +107,5 @@
 
         ThreadState argumentToDao = captor.getValue();
         assertEquals(argumentToDao, state);
-        assertEquals(result, state);
-    }
-
-    @Test
-    public void testSaveThreadStateInsertNew() throws Exception {
-        // this test assumes there is already data in the database,
-        // but the data object has a different state, hence a new one
-        // will be created. This is mostly similar in behaviour to the
-        // first test, except that the dao returns a non null state
-        // object
-        ThreadStateHelper helper =
-                new ThreadStateHelper(threadDao, writerId, vmId);
-
-        ThreadHeader header = mock(ThreadHeader.class);
-        ThreadState state = mock(ThreadState.class);
-        when(state.getState()).thenReturn(Thread.State.BLOCKED.name());
-        when(state.getHeader()).thenReturn(header);
-
-        ThreadState inDao = mock(ThreadState.class);
-        when(inDao.getState()).thenReturn(Thread.State.TIMED_WAITING.name());
-        when(inDao.getHeader()).thenReturn(header);
-
-        when(threadDao.getLastThreadState(header)).thenReturn(inDao);
-
-        ThreadState result = helper.saveThreadState(state);
-        assertNotNull(result);
-
-        verify(threadDao).getLastThreadState(header);
-        verify(inDao).getState();
-
-        ArgumentCaptor<ThreadState> captor =
-                ArgumentCaptor.forClass(ThreadState.class);
-        verify(threadDao).addThreadState(captor.capture());
-
-        ThreadState argumentToDao = captor.getValue();
-        assertEquals(argumentToDao, state);
-        assertEquals(result, state);
-    }
-
-    @Test
-    public void testSaveThreadStateUpdateExisting() throws Exception {
-        // this test assumes there is already data in the database,
-        // and the object has the same state as the one passed as input,
-        // so an update will be performed
-        ThreadStateHelper helper =
-                new ThreadStateHelper(threadDao, writerId, vmId);
-
-        ThreadHeader header = mock(ThreadHeader.class);
-        ThreadState state = mock(ThreadState.class);
-        when(state.getState()).thenReturn(Thread.State.BLOCKED.name());
-        when(state.getHeader()).thenReturn(header);
-        when(state.getProbeEndTime()).thenReturn(42l);
-
-        ThreadState inDao = mock(ThreadState.class);
-        when(inDao.getState()).thenReturn(Thread.State.BLOCKED.name());
-        when(inDao.getHeader()).thenReturn(header);
-
-        when(threadDao.getLastThreadState(header)).thenReturn(inDao);
-
-        ThreadState result = helper.saveThreadState(state);
-        assertNotNull(result);
-
-        verify(threadDao).getLastThreadState(header);
-        verify(inDao).getState();
-        verify(state).getProbeEndTime();
-
-        verify(inDao).setProbeEndTime(42l);
-
-        verify(threadDao).updateThreadState(inDao);
-
-        assertEquals(result, inDao);
-        assertNotSame(result, state);
     }
 }