changeset 2273:8197dcb387ff

Code consistency: Rename packages: '*.impl.*' => '*.internal.*' Reviewed-by: neugens, omajid Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2016-April/018538.html PR2138
author Severin Gehwolf <sgehwolf@redhat.com>
date Fri, 08 Apr 2016 13:18:48 +0200
parents 9eb390ef243b
children b75660003f22
files agent/cli/pom.xml agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/locale/LocaleResources.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/Activator.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/ServiceCommand.java agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/locale/LocaleResources.java agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/impl/strings.properties agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/internal/strings.properties agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/AgentApplicationTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/locale/TranslateTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ActivatorTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ServiceCommandTest.java agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/locale/TranslateTest.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/AgentInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/BasePopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/HostInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/NetworkInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/ThreadPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/VmInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/AgentInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/BasePopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/HostInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/NetworkInfoPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/ThreadPopulator.java dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/VmInfoPopulator.java dev/data-populator/src/main/resources/META-INF/services/com.redhat.thermostat.dev.populator.CollectionPopulator dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/AgentInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/HostInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/NetworkInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/ThreadPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/VmInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/AgentInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/HostInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/NetworkInfoPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/ThreadPopulatorTest.java dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/VmInfoPopulatorTest.java distribution/packaging/fedora/0001_shared_fix_bundle_loading.patch distribution/packaging/fedora/thermostat.spec keyring/pom.xml keyring/src/main/java/com/redhat/thermostat/utils/keyring/activator/Activator.java keyring/src/main/java/com/redhat/thermostat/utils/keyring/impl/KeyringImpl.java keyring/src/main/java/com/redhat/thermostat/utils/keyring/internal/KeyringImpl.java keyring/src/main/native/libgnome-keyring/GnomeKeyringLibraryNative.c keyring/src/main/native/libgnome-keyring/Makefile keyring/src/main/native/libsecret/LibsecretKeyringLibraryNative.c keyring/src/main/native/libsecret/Makefile keyring/src/test/java/com/redhat/thermostat/utils/keyring/impl/KeyringImplTest.java keyring/src/test/java/com/redhat/thermostat/utils/keyring/internal/KeyringImplTest.java main/pom.xml main/src/main/java/com/redhat/thermostat/main/Thermostat.java main/src/main/java/com/redhat/thermostat/main/impl/FrameworkOptionsProcessor.java main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java main/src/main/java/com/redhat/thermostat/main/internal/FrameworkOptionsProcessor.java main/src/main/java/com/redhat/thermostat/main/internal/FrameworkProvider.java main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java main/src/test/java/com/redhat/thermostat/main/impl/FrameworkOptionsProcessorTest.java main/src/test/java/com/redhat/thermostat/main/impl/FrameworkProviderTest.java main/src/test/java/com/redhat/thermostat/main/impl/TestFrameworkFactory.java main/src/test/java/com/redhat/thermostat/main/internal/FrameworkOptionsProcessorTest.java main/src/test/java/com/redhat/thermostat/main/internal/FrameworkProviderTest.java main/src/test/java/com/redhat/thermostat/main/internal/TestFrameworkFactory.java main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory thread/client-common/pom.xml thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorFactoryImpl.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/collector/internal/ThreadCollectorFactoryImpl.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadMXBeanCollector.java thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/osgi/Activator.java thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorFactoryImplTest.java thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorTest.java thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadCollectorFactoryImplTest.java thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadCollectorTest.java thread/client-controllers/pom.xml 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/LocaleResources.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LockController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadCountController.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/impl/VmDeadLockController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCache.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheKey.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/CommonController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/LocaleResources.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/LockController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadCountController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationServiceImpl.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTableController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTimelineController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/VmDeadLockController.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCache.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheKey.java thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/internal/strings.properties thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResourcesTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/LockControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadCountControllerTest.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/ThreadTableControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheKeyTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/LocaleResourcesTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/LockControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadCountControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTableControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTimelineControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/VmDeadLockControllerTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheKeyTest.java thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheTest.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/impl/SwingLockView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadCountView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.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/SwingVmDeadLockView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadDetailsChart.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.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/TimelineAdjustmentListener.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/TimelineLabel.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/LegendPanel.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/internal/SwingLockView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadCountView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadDetailsView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadTableView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadTimelineView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingVmDeadLockView.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadAliveDaemonTimelinePanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadDetailsChart.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadMainPanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadTable.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RangeComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RangeComponentHeader.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RangeComponentLayoutManager.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RulerComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineAdjustmentListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineContainer.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineLabel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineViewComponent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineViewport.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/LegendPanel.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/RangeChangeEvent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/RangeChangeListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/RangedTimelineProbe.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/RatioChangeEvent.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/RatioChangeListener.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/TimelineDateFormatter.java thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/model/TimelineModel.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingLockViewTest.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/internal/SwingLockViewTest.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadViewDemo.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadViewDemo2.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadViewDemo3.java thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadViewTest.java thread/collector/pom.xml thread/collector/src/main/java/com/redhat/thermostat/thread/common/osgi/Activator.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoCategoryRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImplStatementDescriptorRegistration.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/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/dao/internal/LockInfoDaoCategoryRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImplStatementDescriptorRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDAOCategoryRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoCategories.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImpl.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplStatementDescriptorRegistration.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoKeys.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/statement/SessionQueries.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/statement/StateQueries.java thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/statement/SummaryQuery.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSession.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSummary.java thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoCategoryRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImplStatementDescriptorRegistrationTest.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/ThreadDaoCategoriesTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistrationTest.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/SummaryQueryTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoCategoryRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImplStatementDescriptorRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDAOCategoryRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoCategoriesTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplStatementDescriptorRegistrationTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/statement/SessionQueriesTest.java thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/statement/SummaryQueryTest.java
diffstat 240 files changed, 14701 insertions(+), 14670 deletions(-) [+]
line wrap: on
line diff
--- a/agent/cli/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/agent/cli/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -122,11 +122,11 @@
         <configuration>
           <instructions>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
-            <Bundle-Activator>com.redhat.thermostat.agent.cli.impl.Activator</Bundle-Activator>
+            <Bundle-Activator>com.redhat.thermostat.agent.cli.internal.Activator</Bundle-Activator>
             <Bundle-SymbolicName>com.redhat.thermostat.agent.cli</Bundle-SymbolicName>
             <Private-Package>
-              com.redhat.thermostat.agent.cli.impl,
-              com.redhat.thermostat.agent.cli.impl.locale,
+              com.redhat.thermostat.agent.cli.internal,
+              com.redhat.thermostat.agent.cli.internal.locale,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/Activator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import java.util.Map;
-
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-
-import com.redhat.thermostat.common.ExitStatus;
-import com.redhat.thermostat.common.MultipleServiceTracker;
-import com.redhat.thermostat.common.MultipleServiceTracker.Action;
-import com.redhat.thermostat.common.cli.CommandRegistry;
-import com.redhat.thermostat.common.cli.CommandRegistryImpl;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.storage.core.StorageCredentials;
-import com.redhat.thermostat.storage.core.WriterID;
-
-public class Activator implements BundleActivator {
-
-    private CommandRegistry reg;
-    private AgentApplication agentApplication;
-    private MultipleServiceTracker tracker;
-    private ServiceTracker<StorageCredentials, StorageCredentials> credsTracker;
-
-    @Override
-    public void start(final BundleContext context) throws Exception {
-        reg = new CommandRegistryImpl(context);
-        
-        Class<?>[] deps = new Class<?>[] {
-                ExitStatus.class,
-                WriterID.class,
-                SSLConfiguration.class
-        };
-        tracker = new MultipleServiceTracker(context, deps, new Action() {
-            
-            @Override
-            public void dependenciesAvailable(Map<String, Object> services) {
-                ExitStatus exitStatus = (ExitStatus) services.get(ExitStatus.class.getName());
-                WriterID writerID = (WriterID) services.get(WriterID.class.getName());
-                SSLConfiguration sslConf = (SSLConfiguration) services.get(SSLConfiguration.class.getName());
-                agentApplication = new AgentApplication(context, exitStatus, writerID, sslConf);
-                reg.registerCommand("service", new ServiceCommand(context));
-                reg.registerCommand("agent", agentApplication);
-            }
-
-            @Override
-            public void dependenciesUnavailable() {
-                agentApplication.shutdown(ExitStatus.EXIT_SUCCESS);
-                reg.unregisterCommands();
-            }
-        });
-        credsTracker = new ServiceTracker<StorageCredentials, StorageCredentials>(
-                context, StorageCredentials.class, new ServiceTrackerCustomizer<StorageCredentials, StorageCredentials>() {
-
-            @Override
-            public StorageCredentials addingService(ServiceReference<StorageCredentials> ref) {
-                StorageCredentials creds = context.getService(ref);
-                agentApplication.setStorageCredentials(creds);
-                return creds;
-            }
-
-            @Override
-            public void modifiedService(ServiceReference<StorageCredentials> ref,
-                    StorageCredentials creds) {
-                // nothing
-            }
-
-            @Override
-            public void removedService(ServiceReference<StorageCredentials> ref,
-                    StorageCredentials arg1) {
-                if (agentApplication != null) {
-                    agentApplication.setStorageCredentials(null); // remove creds
-                }
-                context.ungetService(ref);
-            }
-            
-        });
-        tracker.open();
-        credsTracker.open();
-    }
-
-    @Override
-    public void stop(BundleContext context) throws Exception {
-        if (agentApplication != null) {
-            // Bundle may be shut down *before* deps become available and
-            // app is set.
-            agentApplication.shutdown(ExitStatus.EXIT_SUCCESS);
-        }
-        reg.unregisterCommands();
-        credsTracker.close();
-        tracker.close();
-    }
-}
-
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/AgentApplication.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,393 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import java.io.IOException;
-import java.util.Map;
-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;
-import org.osgi.util.tracker.ServiceTracker;
-
-import sun.misc.Signal;
-import sun.misc.SignalHandler;
-
-import com.redhat.thermostat.agent.Agent;
-import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources;
-import com.redhat.thermostat.agent.command.ConfigurationServer;
-import com.redhat.thermostat.agent.config.AgentConfigsUtils;
-import com.redhat.thermostat.agent.config.AgentOptionParser;
-import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
-import com.redhat.thermostat.backend.BackendRegistry;
-import com.redhat.thermostat.backend.BackendService;
-import com.redhat.thermostat.common.ExitStatus;
-import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.MultipleServiceTracker;
-import com.redhat.thermostat.common.MultipleServiceTracker.Action;
-import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
-import com.redhat.thermostat.common.cli.Arguments;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.DependencyServices;
-import com.redhat.thermostat.common.tools.ApplicationState;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.config.InvalidConfigurationException;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
-import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
-import com.redhat.thermostat.storage.core.ConnectionException;
-import com.redhat.thermostat.storage.core.DbService;
-import com.redhat.thermostat.storage.core.DbServiceFactory;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.StorageCredentials;
-import com.redhat.thermostat.storage.core.WriterID;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.dao.BackendInfoDAO;
-
-@SuppressWarnings("restriction")
-public final class AgentApplication extends AbstractStateNotifyingCommand {
-
-    /**
-     * Property for turning on verbose mode. This is there so as to be able to
-     * run integration tests independent of log levels.
-     */
-    private static final String VERBOSE_MODE_PROPERTY = "thermostat.agent.verbose";
-    // Messages printed in verbose mode. Integration tests use this. Be careful
-    // when you change those!
-    private static final String VERBOSE_MODE_AGENT_STOPPED_MSG = "Agent stopped.";
-    private static final String VERBOSE_MODE_AGENT_STARTED_MSG = "Agent started.";
-
-    private static final String SIGINT_NAME = "INT";
-    private static final String SIGTERM_NAME = "TERM";
-
-    private static final Logger logger = LoggingUtils.getLogger(AgentApplication.class);
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    
-    private final BundleContext bundleContext;
-    private final ConfigurationCreator configurationCreator;
-
-    private AgentStartupConfiguration configuration;
-    private AgentOptionParser parser;
-    private DbServiceFactory dbServiceFactory;
-    @SuppressWarnings("rawtypes")
-    private ServiceTracker configServerTracker;
-    private MultipleServiceTracker depTracker;
-    private final ExitStatus exitStatus;
-    private final WriterID writerId;
-    private final SSLConfiguration sslConf;
-    private final DependencyServices depServices;
-    private CountDownLatch shutdownLatch;
-
-    private CustomSignalHandler handler;
-
-    public AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId, SSLConfiguration sslConf) {
-        this(bundleContext, exitStatus, writerId, sslConf, new DependencyServices(), new ConfigurationCreator(), new DbServiceFactory());
-    }
-
-    AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId, SSLConfiguration sslConf, DependencyServices depServices, ConfigurationCreator configurationCreator, DbServiceFactory dbServiceFactory) {
-        this.bundleContext = bundleContext;
-        this.configurationCreator = configurationCreator;
-        this.dbServiceFactory = dbServiceFactory;
-        this.exitStatus = exitStatus;
-        this.writerId = writerId;
-        this.sslConf = sslConf;
-        this.depServices = depServices;
-    }
-    
-    private void parseArguments(Arguments args) throws InvalidConfigurationException {
-        parser = new AgentOptionParser(configuration, args);
-        parser.parse();
-    }
-    
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private void runAgent(CommandContext ctx) throws CommandException {
-        long startTime = System.currentTimeMillis();
-        configuration.setStartTime(startTime);
-        
-        StorageCredentials creds = getServiceOrExit(StorageCredentials.class);
-        final DbService dbService = dbServiceFactory.createDbService(
-                configuration.getDBConnectionString(), creds, sslConf);
-        
-        shutdownLatch = new CountDownLatch(1);
-        
-        configServerTracker = new ServiceTracker(bundleContext, ConfigurationServer.class.getName(), null) {
-            @Override
-            public Object addingService(ServiceReference reference) {
-                final ConfigurationServer configServer = (ConfigurationServer) super.addingService(reference);
-                String [] host = configuration.getConfigListenAddress().split(":");
-
-                try {
-                    configServer.startListening(host[0], Integer.valueOf(host[1]));
-
-                    ConnectionListener connectionListener = new ConnectionListener() {
-                        @Override
-                        public void changed(ConnectionStatus newStatus) {
-                            switch (newStatus) {
-                            case DISCONNECTED:
-                                logger.warning("Unexpected disconnect event.");
-                                break;
-                            case CONNECTING:
-                                logger.fine("Connecting to storage.");
-                                break;
-                            case CONNECTED:
-                                logger.fine("Connected to storage");
-                                handleConnected(configServer);
-                                break;
-                            case FAILED_TO_CONNECT:
-                                // ConnectionException will be thrown
-                                break;
-                            default:
-                                logger.warning("Unfamiliar ConnectionStatus value: " + newStatus.toString());
-                            }
-                        }
-                    };
-
-                    dbService.addConnectionListener(connectionListener);
-                    logger.fine("Connecting to storage...");
-                
-                    dbService.connect();
-                } catch (IOException e) {
-                    logger.log(Level.SEVERE, e.getMessage());
-                    // log stack trace as info only
-                    logger.log(Level.INFO, e.getMessage(), e);
-                    shutdown(ExitStatus.EXIT_ERROR);
-                } catch (ConnectionException e) {
-                    logger.log(Level.SEVERE, "Could not connect to storage (" + e.getMessage() + ")");
-                    // log stack trace as info only
-                    logger.log(Level.INFO, "Could not connect to storage", e);
-                    shutdown(ExitStatus.EXIT_ERROR);
-                }
-                
-                return configServer;
-            }
-            
-            @Override
-            public void removedService(ServiceReference reference, Object service) {
-                if (shutdownLatch.getCount() > 0) {
-                    // Lost config server while still running
-                    logger.warning("ConfigurationServer unexpectedly became unavailable");
-                }
-                // Stop listening on command channel
-                ConfigurationServer server = (ConfigurationServer) service;
-                server.stopListening();
-                super.removedService(reference, service);
-            }
-        };
-        configServerTracker.open();
-        
-        try {
-            // Wait for either SIGINT or SIGTERM
-            shutdownLatch.await();
-            logger.fine("terminating agent cmd");
-        } catch (InterruptedException e) {
-            // Ensure proper shutdown if interrupted
-            handler.handle(new Signal(SIGINT_NAME));
-            return;
-        }
-    }
-
-    private StorageCredentials getServiceOrExit(Class<StorageCredentials> clazz) throws CommandException {
-        StorageCredentials creds = depServices.getService(clazz);
-        if (creds == null) {
-            throw new CommandException(t.localize(LocaleResources.STORAGE_CREDS_UNAVAILABLE));
-        }
-        return creds;
-    }
-    
-    void setStorageCredentials(StorageCredentials creds) {
-        if (creds == null) {
-            depServices.removeService(StorageCredentials.class);
-        } else {
-            depServices.addService(StorageCredentials.class, creds);
-        }
-    }
-
-    @Override
-    public void run(CommandContext ctx) throws CommandException {
-        configuration = configurationCreator.create();
-
-        parseArguments(ctx.getArguments());
-        if (!parser.isHelp()) {
-            runAgent(ctx);
-        }
-    }
-    
-    public void shutdown(int shutDownStatus) {
-        // Exit application
-        if (shutdownLatch != null) {
-            shutdownLatch.countDown();
-        }
-        
-        if (depTracker != null) {
-            depTracker.close();
-        }
-        if (configServerTracker != null) {
-            configServerTracker.close();
-        }
-        this.exitStatus.setExitStatus(shutDownStatus);
-        if (shutDownStatus == ExitStatus.EXIT_SUCCESS) {
-            getNotifier().fireAction(ApplicationState.STOP);
-        } else {
-            getNotifier().fireAction(ApplicationState.FAIL);
-        }
-    }
-    
-    private class CustomSignalHandler implements SignalHandler {
-        
-        private Agent agent;
-        private ConfigurationServer configServer;
-
-        public CustomSignalHandler(Agent agent, ConfigurationServer configServer) {
-            this.agent = agent;
-            this.configServer = configServer;
-        }
-        
-        @Override
-        public void handle(Signal arg0) {
-            configServer.stopListening();
-            try {
-                agent.stop();
-            } catch (Exception ex) {
-                // We don't want any exception to hold back the signal handler, otherwise
-                // there will be no way to actually stop Thermostat.
-                ex.printStackTrace();
-            }
-            logger.fine("Agent stopped.");
-            // Hook for integration tests. Print a well known message to stdout
-            // if verbose mode is turned on via the system property.
-            if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
-                System.out.println(VERBOSE_MODE_AGENT_STOPPED_MSG);
-            }
-            shutdown(ExitStatus.EXIT_SUCCESS);
-        }
-        
-    }
-
-    Agent startAgent(final Storage storage, AgentInfoDAO agentInfoDAO, BackendInfoDAO backendInfoDAO, MXBeanConnectionPool pool) {
-        BackendRegistry backendRegistry = null;
-        try {
-            backendRegistry = new BackendRegistry(bundleContext);
-            
-        } catch (Exception e) {
-            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e);
-            shutdown(ExitStatus.EXIT_ERROR);
-            // Since this would throw NPE's down the line if we continue in this
-            // method, let's fail right and early :)
-            throw new RuntimeException(e);
-        }
-
-        final Agent agent = new Agent(backendRegistry, configuration, storage, agentInfoDAO, backendInfoDAO, writerId, pool);
-        try {
-            logger.fine("Starting agent.");
-            agent.start();
-            
-            bundleContext.registerService(BackendService.class, new BackendService(), null);
-            
-        } catch (LaunchException le) {
-            logger.log(Level.SEVERE,
-                    "Agent could not start, probably because a configured backend could not be activated.",
-                    le);
-            shutdown(ExitStatus.EXIT_ERROR);
-        }
-        logger.fine("Agent started.");
-        // Hook for integration tests. Print a well known message to stdout
-        // if verbose mode is turned on via the system property.
-        if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
-            System.out.println(VERBOSE_MODE_AGENT_STARTED_MSG);
-        }
-
-        logger.info("Agent id: " + agent.getId());
-        getNotifier().fireAction(ApplicationState.START, agent.getId());
-        return agent;
-    }
-    
-    private void handleConnected(final ConfigurationServer configServer) {
-        Class<?>[] deps = new Class<?>[] {
-                Storage.class,
-                AgentInfoDAO.class,
-                BackendInfoDAO.class,
-                MXBeanConnectionPool.class
-        };
-        depTracker = new MultipleServiceTracker(bundleContext, deps, new Action() {
-
-            @Override
-            public void dependenciesAvailable(Map<String, Object> services) {
-                Storage storage = (Storage) services.get(Storage.class.getName());
-                AgentInfoDAO agentInfoDAO = (AgentInfoDAO) services
-                        .get(AgentInfoDAO.class.getName());
-                BackendInfoDAO backendInfoDAO = (BackendInfoDAO) services
-                        .get(BackendInfoDAO.class.getName());
-                MXBeanConnectionPool pool = (MXBeanConnectionPool) services
-                        .get(MXBeanConnectionPool.class.getName());
-
-                Agent agent = startAgent(storage, agentInfoDAO, backendInfoDAO, pool);
-                handler = new CustomSignalHandler(agent, configServer);
-                Signal.handle(new Signal(SIGINT_NAME), handler);
-                Signal.handle(new Signal(SIGTERM_NAME), handler);
-            }
-
-            @Override
-            public void dependenciesUnavailable() {
-                if (shutdownLatch.getCount() > 0) {
-                    // In the rare case we lose one of our deps, gracefully shutdown
-                    logger.severe("Storage unexpectedly became unavailable");
-                    shutdown(ExitStatus.EXIT_ERROR);
-                }
-            }
-            
-        });
-        depTracker.open();
-    }
-
-    static class ConfigurationCreator {
-        public AgentStartupConfiguration create() throws InvalidConfigurationException {
-            return AgentConfigsUtils.createAgentConfigs();
-        }
-    }
-
-    @Override
-    public boolean isStorageRequired() {
-        return false;
-    }
-    
-}
-
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/ServiceCommand.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,202 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-
-import com.redhat.thermostat.agent.cli.impl.locale.LocaleResources;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.Console;
-import com.redhat.thermostat.common.tools.ApplicationState;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.launcher.Launcher;
-import com.redhat.thermostat.shared.locale.Translate;
-
-/**
- * Simple service that allows starting Agent and DB Backend
- * in a single step.
- */
-public class ServiceCommand extends AbstractStateNotifyingCommand implements ActionListener<ApplicationState> {
-    
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-    private static final Logger logger = LoggingUtils.getLogger(ServiceCommand.class);
-
-    private List<ActionListener<ApplicationState>> listeners;
-    private Semaphore agentBarrier = new Semaphore(0);
-    private BundleContext context;
-    private Launcher launcher;
-    private boolean storageFailed = false;
-    private boolean agentStarted = false;
-    private CommandContext cmdCtx;
-
-    public ServiceCommand(BundleContext context) {
-        this.context = context;
-        listeners = new ArrayList<>();
-        listeners.add(this);
-    }
-
-    @Override
-    public void run(CommandContext ctx) throws CommandException {
-        cmdCtx = ctx;
-        ServiceReference launcherRef = context.getServiceReference(Launcher.class);
-        requireNonNull(launcherRef, translator.localize(LocaleResources.LAUNCHER_UNAVAILABLE));
-        launcher = (Launcher) context.getService(launcherRef);
-        String[] storageStartArgs = new String[] { "storage", "--start" };
-        launcher.run(storageStartArgs, listeners, false);
-        agentBarrier.acquireUninterruptibly();
-        
-        if (storageFailed) {
-            storageFailed = false;
-            context.ungetService(launcherRef);
-            getNotifier().fireAction(ApplicationState.FAIL);
-            throw new CommandException(translator.localize(LocaleResources.SERVICE_FAILED_TO_START_DB));
-        }
-        
-        String[] storageStopArgs = new String[] { "storage", "--stop" };
-        launcher.run(storageStopArgs, false);
-
-        if (agentStarted) {
-            getNotifier().fireAction(ApplicationState.STOP);
-        }
-
-        context.ungetService(launcherRef);
-        cmdCtx = null;
-    }
-
-    @Override
-    public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-        if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) {
-            AbstractStateNotifyingCommand storage = (AbstractStateNotifyingCommand) actionEvent.getSource();
-            // Implementation detail: there is a single StorageCommand instance registered
-            // as an OSGi service.  We remove ourselves as listener so that we don't get
-            // notified in the case that the command is invoked by some other means later.
-            storage.getNotifier().removeActionListener(this);
-            
-            try {
-                switch (actionEvent.getActionId()) {
-                case START:
-                    // Payload is connection URL
-                    Object payload = actionEvent.getPayload();
-                    if (payload == null || !(payload instanceof String)) {
-                        getNotifier().fireAction(ApplicationState.FAIL);
-                        throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE));
-                    }
-                    String dbUrl = (String) payload;
-                    String[] agentArgs =  new String[] {"agent", "-d", dbUrl};
-                    logger.fine("starting agent now...");
-                    listeners.clear();
-                    listeners.add(new AgentStartedListener(cmdCtx.getConsole()));
-                    launcher.run(agentArgs, listeners, false);
-                    break;
-                case FAIL:
-                    storageFailed = true;
-                    // Payload is exception
-                    payload = actionEvent.getPayload();
-                    if (payload == null || !(payload instanceof Exception)) {
-                        getNotifier().fireAction(ApplicationState.FAIL);
-                        throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE));
-                    }
-                    Exception ex = (Exception) payload;
-                    cmdCtx.getConsole().getError().println(ex.getMessage());
-                    logger.log(Level.WARNING, ex.getMessage(), ex);
-                    break;
-                }
-            } catch (CommandException e) {
-                cmdCtx.getConsole().getError().println(e.getMessage());
-            } finally {
-                agentBarrier.release();
-            }
-        }
-    }
-
-    @Override
-    public boolean isStorageRequired() {
-        return false;
-    }
-    
-    private class AgentStartedListener implements ActionListener<ApplicationState> {
-
-        private final Console console;
-
-        private AgentStartedListener(Console console) {
-            this.console = console;
-        }
-
-        @Override
-        public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-            if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) {
-                AbstractStateNotifyingCommand agent = (AbstractStateNotifyingCommand) actionEvent.getSource();
-                // Implementation detail: there is a single AgentCommand instance registered
-                // as an OSGi service. We remove ourselves as listener so that we don't get
-                // notified in the case that the command is invoked by some other means later.
-                agent.getNotifier().removeActionListener(this);
-
-                ApplicationState state = actionEvent.getActionId();
-                // propagate the Agent ActionEvent
-                switch (state) {
-                case START:
-                    agentStarted = true;
-                    logger.fine("Agent started via service. Agent ID was: " + actionEvent.getPayload());
-                    getNotifier().fireAction(ApplicationState.START, actionEvent.getPayload());
-                    break;
-                case FAIL:
-                    console.getError().println(translator.localize(LocaleResources.STARTING_AGENT_FAILED).getContents());
-                    getNotifier().fireAction(ApplicationState.FAIL, actionEvent.getPayload());
-                    break;
-                case STOP:
-                    getNotifier().fireAction(ApplicationState.STOP);
-                    break;
-                default:
-                    throw new AssertionError("Unexpected state " + state);
-                }
-            }
-        }
-    }
-
-}
-
--- a/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/impl/locale/LocaleResources.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl.locale;
-
-import com.redhat.thermostat.shared.locale.Translate;
-
-public enum LocaleResources {
-
-    SERVICE_FAILED_TO_START_DB,
-    LAUNCHER_UNAVAILABLE,
-    UNEXPECTED_RESULT_STORAGE,
-    STARTING_AGENT_FAILED,
-    STORAGE_CREDS_UNAVAILABLE,
-    ;
-
-    static final String RESOURCE_BUNDLE = "com.redhat.thermostat.agent.cli.impl.strings";
-
-    public static Translate<LocaleResources> createLocalizer() {
-        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/Activator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+import com.redhat.thermostat.common.ExitStatus;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.common.cli.CommandRegistry;
+import com.redhat.thermostat.common.cli.CommandRegistryImpl;
+import com.redhat.thermostat.shared.config.SSLConfiguration;
+import com.redhat.thermostat.storage.core.StorageCredentials;
+import com.redhat.thermostat.storage.core.WriterID;
+
+public class Activator implements BundleActivator {
+
+    private CommandRegistry reg;
+    private AgentApplication agentApplication;
+    private MultipleServiceTracker tracker;
+    private ServiceTracker<StorageCredentials, StorageCredentials> credsTracker;
+
+    @Override
+    public void start(final BundleContext context) throws Exception {
+        reg = new CommandRegistryImpl(context);
+        
+        Class<?>[] deps = new Class<?>[] {
+                ExitStatus.class,
+                WriterID.class,
+                SSLConfiguration.class
+        };
+        tracker = new MultipleServiceTracker(context, deps, new Action() {
+            
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                ExitStatus exitStatus = (ExitStatus) services.get(ExitStatus.class.getName());
+                WriterID writerID = (WriterID) services.get(WriterID.class.getName());
+                SSLConfiguration sslConf = (SSLConfiguration) services.get(SSLConfiguration.class.getName());
+                agentApplication = new AgentApplication(context, exitStatus, writerID, sslConf);
+                reg.registerCommand("service", new ServiceCommand(context));
+                reg.registerCommand("agent", agentApplication);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                agentApplication.shutdown(ExitStatus.EXIT_SUCCESS);
+                reg.unregisterCommands();
+            }
+        });
+        credsTracker = new ServiceTracker<StorageCredentials, StorageCredentials>(
+                context, StorageCredentials.class, new ServiceTrackerCustomizer<StorageCredentials, StorageCredentials>() {
+
+            @Override
+            public StorageCredentials addingService(ServiceReference<StorageCredentials> ref) {
+                StorageCredentials creds = context.getService(ref);
+                agentApplication.setStorageCredentials(creds);
+                return creds;
+            }
+
+            @Override
+            public void modifiedService(ServiceReference<StorageCredentials> ref,
+                    StorageCredentials creds) {
+                // nothing
+            }
+
+            @Override
+            public void removedService(ServiceReference<StorageCredentials> ref,
+                    StorageCredentials arg1) {
+                if (agentApplication != null) {
+                    agentApplication.setStorageCredentials(null); // remove creds
+                }
+                context.ungetService(ref);
+            }
+            
+        });
+        tracker.open();
+        credsTracker.open();
+    }
+
+    @Override
+    public void stop(BundleContext context) throws Exception {
+        if (agentApplication != null) {
+            // Bundle may be shut down *before* deps become available and
+            // app is set.
+            agentApplication.shutdown(ExitStatus.EXIT_SUCCESS);
+        }
+        reg.unregisterCommands();
+        credsTracker.close();
+        tracker.close();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/AgentApplication.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import java.io.IOException;
+import java.util.Map;
+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;
+import org.osgi.util.tracker.ServiceTracker;
+
+import sun.misc.Signal;
+import sun.misc.SignalHandler;
+
+import com.redhat.thermostat.agent.Agent;
+import com.redhat.thermostat.agent.cli.internal.locale.LocaleResources;
+import com.redhat.thermostat.agent.command.ConfigurationServer;
+import com.redhat.thermostat.agent.config.AgentConfigsUtils;
+import com.redhat.thermostat.agent.config.AgentOptionParser;
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
+import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.backend.BackendService;
+import com.redhat.thermostat.common.ExitStatus;
+import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.MultipleServiceTracker;
+import com.redhat.thermostat.common.MultipleServiceTracker.Action;
+import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.DependencyServices;
+import com.redhat.thermostat.common.tools.ApplicationState;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.config.InvalidConfigurationException;
+import com.redhat.thermostat.shared.config.SSLConfiguration;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
+import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
+import com.redhat.thermostat.storage.core.ConnectionException;
+import com.redhat.thermostat.storage.core.DbService;
+import com.redhat.thermostat.storage.core.DbServiceFactory;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.StorageCredentials;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.BackendInfoDAO;
+
+@SuppressWarnings("restriction")
+public final class AgentApplication extends AbstractStateNotifyingCommand {
+
+    /**
+     * Property for turning on verbose mode. This is there so as to be able to
+     * run integration tests independent of log levels.
+     */
+    private static final String VERBOSE_MODE_PROPERTY = "thermostat.agent.verbose";
+    // Messages printed in verbose mode. Integration tests use this. Be careful
+    // when you change those!
+    private static final String VERBOSE_MODE_AGENT_STOPPED_MSG = "Agent stopped.";
+    private static final String VERBOSE_MODE_AGENT_STARTED_MSG = "Agent started.";
+
+    private static final String SIGINT_NAME = "INT";
+    private static final String SIGTERM_NAME = "TERM";
+
+    private static final Logger logger = LoggingUtils.getLogger(AgentApplication.class);
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    
+    private final BundleContext bundleContext;
+    private final ConfigurationCreator configurationCreator;
+
+    private AgentStartupConfiguration configuration;
+    private AgentOptionParser parser;
+    private DbServiceFactory dbServiceFactory;
+    @SuppressWarnings("rawtypes")
+    private ServiceTracker configServerTracker;
+    private MultipleServiceTracker depTracker;
+    private final ExitStatus exitStatus;
+    private final WriterID writerId;
+    private final SSLConfiguration sslConf;
+    private final DependencyServices depServices;
+    private CountDownLatch shutdownLatch;
+
+    private CustomSignalHandler handler;
+
+    public AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId, SSLConfiguration sslConf) {
+        this(bundleContext, exitStatus, writerId, sslConf, new DependencyServices(), new ConfigurationCreator(), new DbServiceFactory());
+    }
+
+    AgentApplication(BundleContext bundleContext, ExitStatus exitStatus, WriterID writerId, SSLConfiguration sslConf, DependencyServices depServices, ConfigurationCreator configurationCreator, DbServiceFactory dbServiceFactory) {
+        this.bundleContext = bundleContext;
+        this.configurationCreator = configurationCreator;
+        this.dbServiceFactory = dbServiceFactory;
+        this.exitStatus = exitStatus;
+        this.writerId = writerId;
+        this.sslConf = sslConf;
+        this.depServices = depServices;
+    }
+    
+    private void parseArguments(Arguments args) throws InvalidConfigurationException {
+        parser = new AgentOptionParser(configuration, args);
+        parser.parse();
+    }
+    
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    private void runAgent(CommandContext ctx) throws CommandException {
+        long startTime = System.currentTimeMillis();
+        configuration.setStartTime(startTime);
+        
+        StorageCredentials creds = getServiceOrExit(StorageCredentials.class);
+        final DbService dbService = dbServiceFactory.createDbService(
+                configuration.getDBConnectionString(), creds, sslConf);
+        
+        shutdownLatch = new CountDownLatch(1);
+        
+        configServerTracker = new ServiceTracker(bundleContext, ConfigurationServer.class.getName(), null) {
+            @Override
+            public Object addingService(ServiceReference reference) {
+                final ConfigurationServer configServer = (ConfigurationServer) super.addingService(reference);
+                String [] host = configuration.getConfigListenAddress().split(":");
+
+                try {
+                    configServer.startListening(host[0], Integer.valueOf(host[1]));
+
+                    ConnectionListener connectionListener = new ConnectionListener() {
+                        @Override
+                        public void changed(ConnectionStatus newStatus) {
+                            switch (newStatus) {
+                            case DISCONNECTED:
+                                logger.warning("Unexpected disconnect event.");
+                                break;
+                            case CONNECTING:
+                                logger.fine("Connecting to storage.");
+                                break;
+                            case CONNECTED:
+                                logger.fine("Connected to storage");
+                                handleConnected(configServer);
+                                break;
+                            case FAILED_TO_CONNECT:
+                                // ConnectionException will be thrown
+                                break;
+                            default:
+                                logger.warning("Unfamiliar ConnectionStatus value: " + newStatus.toString());
+                            }
+                        }
+                    };
+
+                    dbService.addConnectionListener(connectionListener);
+                    logger.fine("Connecting to storage...");
+                
+                    dbService.connect();
+                } catch (IOException e) {
+                    logger.log(Level.SEVERE, e.getMessage());
+                    // log stack trace as info only
+                    logger.log(Level.INFO, e.getMessage(), e);
+                    shutdown(ExitStatus.EXIT_ERROR);
+                } catch (ConnectionException e) {
+                    logger.log(Level.SEVERE, "Could not connect to storage (" + e.getMessage() + ")");
+                    // log stack trace as info only
+                    logger.log(Level.INFO, "Could not connect to storage", e);
+                    shutdown(ExitStatus.EXIT_ERROR);
+                }
+                
+                return configServer;
+            }
+            
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                if (shutdownLatch.getCount() > 0) {
+                    // Lost config server while still running
+                    logger.warning("ConfigurationServer unexpectedly became unavailable");
+                }
+                // Stop listening on command channel
+                ConfigurationServer server = (ConfigurationServer) service;
+                server.stopListening();
+                super.removedService(reference, service);
+            }
+        };
+        configServerTracker.open();
+        
+        try {
+            // Wait for either SIGINT or SIGTERM
+            shutdownLatch.await();
+            logger.fine("terminating agent cmd");
+        } catch (InterruptedException e) {
+            // Ensure proper shutdown if interrupted
+            handler.handle(new Signal(SIGINT_NAME));
+            return;
+        }
+    }
+
+    private StorageCredentials getServiceOrExit(Class<StorageCredentials> clazz) throws CommandException {
+        StorageCredentials creds = depServices.getService(clazz);
+        if (creds == null) {
+            throw new CommandException(t.localize(LocaleResources.STORAGE_CREDS_UNAVAILABLE));
+        }
+        return creds;
+    }
+    
+    void setStorageCredentials(StorageCredentials creds) {
+        if (creds == null) {
+            depServices.removeService(StorageCredentials.class);
+        } else {
+            depServices.addService(StorageCredentials.class, creds);
+        }
+    }
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        configuration = configurationCreator.create();
+
+        parseArguments(ctx.getArguments());
+        if (!parser.isHelp()) {
+            runAgent(ctx);
+        }
+    }
+    
+    public void shutdown(int shutDownStatus) {
+        // Exit application
+        if (shutdownLatch != null) {
+            shutdownLatch.countDown();
+        }
+        
+        if (depTracker != null) {
+            depTracker.close();
+        }
+        if (configServerTracker != null) {
+            configServerTracker.close();
+        }
+        this.exitStatus.setExitStatus(shutDownStatus);
+        if (shutDownStatus == ExitStatus.EXIT_SUCCESS) {
+            getNotifier().fireAction(ApplicationState.STOP);
+        } else {
+            getNotifier().fireAction(ApplicationState.FAIL);
+        }
+    }
+    
+    private class CustomSignalHandler implements SignalHandler {
+        
+        private Agent agent;
+        private ConfigurationServer configServer;
+
+        public CustomSignalHandler(Agent agent, ConfigurationServer configServer) {
+            this.agent = agent;
+            this.configServer = configServer;
+        }
+        
+        @Override
+        public void handle(Signal arg0) {
+            configServer.stopListening();
+            try {
+                agent.stop();
+            } catch (Exception ex) {
+                // We don't want any exception to hold back the signal handler, otherwise
+                // there will be no way to actually stop Thermostat.
+                ex.printStackTrace();
+            }
+            logger.fine("Agent stopped.");
+            // Hook for integration tests. Print a well known message to stdout
+            // if verbose mode is turned on via the system property.
+            if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
+                System.out.println(VERBOSE_MODE_AGENT_STOPPED_MSG);
+            }
+            shutdown(ExitStatus.EXIT_SUCCESS);
+        }
+        
+    }
+
+    Agent startAgent(final Storage storage, AgentInfoDAO agentInfoDAO, BackendInfoDAO backendInfoDAO, MXBeanConnectionPool pool) {
+        BackendRegistry backendRegistry = null;
+        try {
+            backendRegistry = new BackendRegistry(bundleContext);
+            
+        } catch (Exception e) {
+            logger.log(Level.SEVERE, "Could not get BackendRegistry instance.", e);
+            shutdown(ExitStatus.EXIT_ERROR);
+            // Since this would throw NPE's down the line if we continue in this
+            // method, let's fail right and early :)
+            throw new RuntimeException(e);
+        }
+
+        final Agent agent = new Agent(backendRegistry, configuration, storage, agentInfoDAO, backendInfoDAO, writerId, pool);
+        try {
+            logger.fine("Starting agent.");
+            agent.start();
+            
+            bundleContext.registerService(BackendService.class, new BackendService(), null);
+            
+        } catch (LaunchException le) {
+            logger.log(Level.SEVERE,
+                    "Agent could not start, probably because a configured backend could not be activated.",
+                    le);
+            shutdown(ExitStatus.EXIT_ERROR);
+        }
+        logger.fine("Agent started.");
+        // Hook for integration tests. Print a well known message to stdout
+        // if verbose mode is turned on via the system property.
+        if (Boolean.getBoolean(VERBOSE_MODE_PROPERTY)) {
+            System.out.println(VERBOSE_MODE_AGENT_STARTED_MSG);
+        }
+
+        logger.info("Agent id: " + agent.getId());
+        getNotifier().fireAction(ApplicationState.START, agent.getId());
+        return agent;
+    }
+    
+    private void handleConnected(final ConfigurationServer configServer) {
+        Class<?>[] deps = new Class<?>[] {
+                Storage.class,
+                AgentInfoDAO.class,
+                BackendInfoDAO.class,
+                MXBeanConnectionPool.class
+        };
+        depTracker = new MultipleServiceTracker(bundleContext, deps, new Action() {
+
+            @Override
+            public void dependenciesAvailable(Map<String, Object> services) {
+                Storage storage = (Storage) services.get(Storage.class.getName());
+                AgentInfoDAO agentInfoDAO = (AgentInfoDAO) services
+                        .get(AgentInfoDAO.class.getName());
+                BackendInfoDAO backendInfoDAO = (BackendInfoDAO) services
+                        .get(BackendInfoDAO.class.getName());
+                MXBeanConnectionPool pool = (MXBeanConnectionPool) services
+                        .get(MXBeanConnectionPool.class.getName());
+
+                Agent agent = startAgent(storage, agentInfoDAO, backendInfoDAO, pool);
+                handler = new CustomSignalHandler(agent, configServer);
+                Signal.handle(new Signal(SIGINT_NAME), handler);
+                Signal.handle(new Signal(SIGTERM_NAME), handler);
+            }
+
+            @Override
+            public void dependenciesUnavailable() {
+                if (shutdownLatch.getCount() > 0) {
+                    // In the rare case we lose one of our deps, gracefully shutdown
+                    logger.severe("Storage unexpectedly became unavailable");
+                    shutdown(ExitStatus.EXIT_ERROR);
+                }
+            }
+            
+        });
+        depTracker.open();
+    }
+
+    static class ConfigurationCreator {
+        public AgentStartupConfiguration create() throws InvalidConfigurationException {
+            return AgentConfigsUtils.createAgentConfigs();
+        }
+    }
+
+    @Override
+    public boolean isStorageRequired() {
+        return false;
+    }
+    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/ServiceCommand.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+import com.redhat.thermostat.agent.cli.internal.locale.LocaleResources;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.Console;
+import com.redhat.thermostat.common.tools.ApplicationState;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.launcher.Launcher;
+import com.redhat.thermostat.shared.locale.Translate;
+
+/**
+ * Simple service that allows starting Agent and DB Backend
+ * in a single step.
+ */
+public class ServiceCommand extends AbstractStateNotifyingCommand implements ActionListener<ApplicationState> {
+    
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+    private static final Logger logger = LoggingUtils.getLogger(ServiceCommand.class);
+
+    private List<ActionListener<ApplicationState>> listeners;
+    private Semaphore agentBarrier = new Semaphore(0);
+    private BundleContext context;
+    private Launcher launcher;
+    private boolean storageFailed = false;
+    private boolean agentStarted = false;
+    private CommandContext cmdCtx;
+
+    public ServiceCommand(BundleContext context) {
+        this.context = context;
+        listeners = new ArrayList<>();
+        listeners.add(this);
+    }
+
+    @Override
+    public void run(CommandContext ctx) throws CommandException {
+        cmdCtx = ctx;
+        ServiceReference launcherRef = context.getServiceReference(Launcher.class);
+        requireNonNull(launcherRef, translator.localize(LocaleResources.LAUNCHER_UNAVAILABLE));
+        launcher = (Launcher) context.getService(launcherRef);
+        String[] storageStartArgs = new String[] { "storage", "--start" };
+        launcher.run(storageStartArgs, listeners, false);
+        agentBarrier.acquireUninterruptibly();
+        
+        if (storageFailed) {
+            storageFailed = false;
+            context.ungetService(launcherRef);
+            getNotifier().fireAction(ApplicationState.FAIL);
+            throw new CommandException(translator.localize(LocaleResources.SERVICE_FAILED_TO_START_DB));
+        }
+        
+        String[] storageStopArgs = new String[] { "storage", "--stop" };
+        launcher.run(storageStopArgs, false);
+
+        if (agentStarted) {
+            getNotifier().fireAction(ApplicationState.STOP);
+        }
+
+        context.ungetService(launcherRef);
+        cmdCtx = null;
+    }
+
+    @Override
+    public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+        if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) {
+            AbstractStateNotifyingCommand storage = (AbstractStateNotifyingCommand) actionEvent.getSource();
+            // Implementation detail: there is a single StorageCommand instance registered
+            // as an OSGi service.  We remove ourselves as listener so that we don't get
+            // notified in the case that the command is invoked by some other means later.
+            storage.getNotifier().removeActionListener(this);
+            
+            try {
+                switch (actionEvent.getActionId()) {
+                case START:
+                    // Payload is connection URL
+                    Object payload = actionEvent.getPayload();
+                    if (payload == null || !(payload instanceof String)) {
+                        getNotifier().fireAction(ApplicationState.FAIL);
+                        throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE));
+                    }
+                    String dbUrl = (String) payload;
+                    String[] agentArgs =  new String[] {"agent", "-d", dbUrl};
+                    logger.fine("starting agent now...");
+                    listeners.clear();
+                    listeners.add(new AgentStartedListener(cmdCtx.getConsole()));
+                    launcher.run(agentArgs, listeners, false);
+                    break;
+                case FAIL:
+                    storageFailed = true;
+                    // Payload is exception
+                    payload = actionEvent.getPayload();
+                    if (payload == null || !(payload instanceof Exception)) {
+                        getNotifier().fireAction(ApplicationState.FAIL);
+                        throw new CommandException(translator.localize(LocaleResources.UNEXPECTED_RESULT_STORAGE));
+                    }
+                    Exception ex = (Exception) payload;
+                    cmdCtx.getConsole().getError().println(ex.getMessage());
+                    logger.log(Level.WARNING, ex.getMessage(), ex);
+                    break;
+                }
+            } catch (CommandException e) {
+                cmdCtx.getConsole().getError().println(e.getMessage());
+            } finally {
+                agentBarrier.release();
+            }
+        }
+    }
+
+    @Override
+    public boolean isStorageRequired() {
+        return false;
+    }
+    
+    private class AgentStartedListener implements ActionListener<ApplicationState> {
+
+        private final Console console;
+
+        private AgentStartedListener(Console console) {
+            this.console = console;
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+            if (actionEvent.getSource() instanceof AbstractStateNotifyingCommand) {
+                AbstractStateNotifyingCommand agent = (AbstractStateNotifyingCommand) actionEvent.getSource();
+                // Implementation detail: there is a single AgentCommand instance registered
+                // as an OSGi service. We remove ourselves as listener so that we don't get
+                // notified in the case that the command is invoked by some other means later.
+                agent.getNotifier().removeActionListener(this);
+
+                ApplicationState state = actionEvent.getActionId();
+                // propagate the Agent ActionEvent
+                switch (state) {
+                case START:
+                    agentStarted = true;
+                    logger.fine("Agent started via service. Agent ID was: " + actionEvent.getPayload());
+                    getNotifier().fireAction(ApplicationState.START, actionEvent.getPayload());
+                    break;
+                case FAIL:
+                    console.getError().println(translator.localize(LocaleResources.STARTING_AGENT_FAILED).getContents());
+                    getNotifier().fireAction(ApplicationState.FAIL, actionEvent.getPayload());
+                    break;
+                case STOP:
+                    getNotifier().fireAction(ApplicationState.STOP);
+                    break;
+                default:
+                    throw new AssertionError("Unexpected state " + state);
+                }
+            }
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/main/java/com/redhat/thermostat/agent/cli/internal/locale/LocaleResources.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal.locale;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources {
+
+    SERVICE_FAILED_TO_START_DB,
+    LAUNCHER_UNAVAILABLE,
+    UNEXPECTED_RESULT_STORAGE,
+    STARTING_AGENT_FAILED,
+    STORAGE_CREDS_UNAVAILABLE,
+    ;
+
+    static final String RESOURCE_BUNDLE = "com.redhat.thermostat.agent.cli.internal.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+
+}
+
--- a/agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/impl/strings.properties	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-SERVICE_FAILED_TO_START_DB = Service failed to start due to error starting storage.
-LAUNCHER_UNAVAILABLE = Launcher is not available
-UNEXPECTED_RESULT_STORAGE = Unexpected result from storage.
-STARTING_AGENT_FAILED = Thermostat agent failed to start. See logs for details.
-STORAGE_CREDS_UNAVAILABLE = StorageCredentials are not available
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/main/resources/com/redhat/thermostat/agent/cli/internal/strings.properties	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,5 @@
+SERVICE_FAILED_TO_START_DB = Service failed to start due to error starting storage.
+LAUNCHER_UNAVAILABLE = Launcher is not available
+UNEXPECTED_RESULT_STORAGE = Unexpected result from storage.
+STARTING_AGENT_FAILED = Thermostat agent failed to start. See logs for details.
+STORAGE_CREDS_UNAVAILABLE = StorageCredentials are not available
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ActivatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.common.ExitStatus;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.storage.core.WriterID;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-public class ActivatorTest {
-
-    @Test
-    public void verifyActivatorRegistersCommands() throws Exception {        
-        StubBundleContext bundleContext = new StubBundleContext();
-
-        ExitStatus exitStatus = mock(ExitStatus.class);
-        WriterID writerID = mock(WriterID.class);
-        bundleContext.registerService(WriterID.class, writerID, null);
-        bundleContext.registerService(ExitStatus.class, exitStatus, null);
-        bundleContext.registerService(SSLConfiguration.class, mock(SSLConfiguration.class), null);
-        
-        Activator activator = new Activator();
-
-        assertEquals(0, bundleContext.getServiceListeners().size());
-        
-        activator.start(bundleContext);
-        
-        assertEquals(4, bundleContext.getServiceListeners().size());
-        
-        assertCommandIsRegistered(bundleContext, "agent", AgentApplication.class);
-        assertCommandIsRegistered(bundleContext, "service", ServiceCommand.class);
-
-        activator.stop(bundleContext);
-
-        assertEquals(0, bundleContext.getServiceListeners().size());
-        assertEquals(3, bundleContext.getAllServices().size());
-    }
-}
-
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/AgentApplicationTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,292 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.powermock.api.mockito.PowerMockito.whenNew;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.redhat.thermostat.agent.Agent;
-import com.redhat.thermostat.agent.cli.impl.AgentApplication.ConfigurationCreator;
-import com.redhat.thermostat.agent.command.ConfigurationServer;
-import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
-import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
-import com.redhat.thermostat.backend.BackendRegistry;
-import com.redhat.thermostat.common.ExitStatus;
-import com.redhat.thermostat.common.LaunchException;
-import com.redhat.thermostat.common.cli.Arguments;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.DependencyServices;
-import com.redhat.thermostat.shared.config.InvalidConfigurationException;
-import com.redhat.thermostat.shared.config.SSLConfiguration;
-import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
-import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
-import com.redhat.thermostat.storage.core.ConnectionException;
-import com.redhat.thermostat.storage.core.DbService;
-import com.redhat.thermostat.storage.core.DbServiceFactory;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.StorageCredentials;
-import com.redhat.thermostat.storage.core.WriterID;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.dao.BackendInfoDAO;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-@RunWith(PowerMockRunner.class)
-public class AgentApplicationTest {
-
-    private static final String COMMAND_CHANNLE_BIND_HOST = "test";
-    private static final int COMMAND_CHANNEL_BIND_PORT = 10101;
-
-    private StubBundleContext context;
-
-    private ConfigurationServer configServer;
-    private DbService dbService;
-    private ConfigurationCreator configCreator;
-    private ExitStatus exitStatus;
-    private DbServiceFactory dbServiceFactory;
-    private WriterID writerId;
-    
-    @Before
-    public void setUp() throws InvalidConfigurationException {
-        
-        context = new StubBundleContext();
-        
-        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
-        when(config.getDBConnectionString()).thenReturn("test string; please ignore");
-        when(config.getConfigListenAddress()).thenReturn(COMMAND_CHANNLE_BIND_HOST + ":" + COMMAND_CHANNEL_BIND_PORT);
-
-        configCreator = mock(ConfigurationCreator.class);
-        when(configCreator.create()).thenReturn(config);
-
-        Storage storage = mock(Storage.class);
-        context.registerService(Storage.class, storage, null);
-        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
-        context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null);
-        BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class);
-        context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null);
-        configServer = mock(ConfigurationServer.class);
-        context.registerService(ConfigurationServer.class.getName(), configServer, null);
-        dbServiceFactory = mock(DbServiceFactory.class);
-        dbService = mock(DbService.class);
-        writerId = mock(WriterID.class);
-        when(dbServiceFactory.createDbService(anyString(), any(StorageCredentials.class), any(SSLConfiguration.class))).thenReturn(dbService);
-
-        exitStatus = mock(ExitStatus.class);
-    }
-
-    @After
-    public void tearDown() {
-        context = null;
-        configServer = null;
-        dbService = null;
-        configCreator = null;
-        dbServiceFactory = null;
-        exitStatus = null;
-    }
-
-    @Test
-    public void testAgentStartup() throws CommandException, InterruptedException {
-        final AgentApplication agent = new AgentApplication(context, exitStatus, writerId, mock(SSLConfiguration.class), new DependencyServices(), configCreator, dbServiceFactory);
-        agent.setStorageCredentials(mock(StorageCredentials.class));
-        final CountDownLatch latch = new CountDownLatch(1);
-        final CommandException[] ce = new CommandException[1];
-        final long timeoutMillis = 5000L;
-        
-        startAgentRunThread(timeoutMillis, agent, ce, latch);
-        
-        boolean ret = latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
-        if (ce[0] != null) {
-            throw ce[0];
-        }
-        if (!ret) {
-            fail("Timeout expired!");
-        }
-        
-    }
-    
-    @Test
-    public void testAgentStartupConnectFailure() throws CommandException, InterruptedException {
-        final AgentApplication agent = new AgentApplication(context, exitStatus, writerId, mock(SSLConfiguration.class), new DependencyServices(), configCreator, dbServiceFactory);
-        agent.setStorageCredentials(mock(StorageCredentials.class));
-        
-        Arguments args = mock(Arguments.class);
-        final CommandContext commandContext = mock(CommandContext.class);
-        when(commandContext.getArguments()).thenReturn(args);
-        
-        // Throw a ConnectionException when we try to connect to storage
-        doThrow(new ConnectionException()).when(dbService).connect();
-        
-        agent.run(commandContext);
-        
-        // Ensure we shut down command channel server
-        verify(configServer).stopListening();
-    }
-    
-    /*
-     * Having the PrepareForTest annotation on method level does not seem to
-     * deadlock the test, which seems to be more or less reliably reproducible
-     * if this annotation is at class level instead. Steps to reproduce the
-     * deadlock is:
-     * 1. Attach the PrepareForTest annotation to the class (over the test
-     *    method)
-     * 2. Run the test multiple times. 5-20 times seemed sufficient for me to
-     *    make the deadlock show up. This deadlock does not seem to happen
-     *    otherwise (can run up to 30 times head-to-head without deadlock).
-     *    
-     */
-    @PrepareForTest({ AgentApplication.class })
-    @SuppressWarnings("unchecked")
-    @Test
-    public void verifyBackendRegistryProblemsSetsExitStatus() throws Exception {
-        whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class)
-                .withArguments(any(BundleContext.class))
-                .thenThrow(InvalidSyntaxException.class);
-        final AgentApplication agent = new AgentApplication(context,
-                exitStatus, writerId,  mock(SSLConfiguration.class),
-                mock(DependencyServices.class), configCreator, dbServiceFactory);
-        try {
-            agent.startAgent(null, null, null, null);
-        } catch (RuntimeException e) {
-            assertEquals(InvalidSyntaxException.class, e.getCause().getClass());
-        }
-        verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR);
-    }
-    
-    @PrepareForTest({ AgentApplication.class })
-    @Test
-    public void verifyAgentLaunchExceptionSetsExitStatus() throws Exception {
-        whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class)
-                .withArguments(any(BundleContext.class))
-                .thenReturn(mock(BackendRegistry.class));
-        Agent mockAgent = mock(Agent.class);
-        whenNew(Agent.class).withParameterTypes(BackendRegistry.class,
-                AgentStartupConfiguration.class, Storage.class,
-                AgentInfoDAO.class, BackendInfoDAO.class, WriterID.class, 
-                MXBeanConnectionPool.class).withArguments(
-                any(BackendRegistry.class),
-                any(AgentStartupConfiguration.class), any(Storage.class),
-                any(AgentInfoDAO.class), any(BackendInfoDAO.class), any(WriterID.class), 
-                any(MXBeanConnectionPool.class)).thenReturn(mockAgent);
-        doThrow(LaunchException.class).when(mockAgent).start();
-        final AgentApplication agent = new AgentApplication(context,
-                exitStatus, writerId,  mock(SSLConfiguration.class),
-                mock(DependencyServices.class), configCreator, dbServiceFactory);
-        try {
-            agent.startAgent(null, null, null, null);
-        } catch (RuntimeException e) {
-            fail("Should not have thrown RuntimeException");
-        }
-        verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR);
-    }
-
-    private void startAgentRunThread(final long timoutMillis, final AgentApplication agent, final CommandException[] ce, final CountDownLatch latch) {
-        Arguments args = mock(Arguments.class);
-        final CommandContext commandContext = mock(CommandContext.class);
-        when(commandContext.getArguments()).thenReturn(args);
-        
-        // Immediately switch to CONNECTED state on dbService.connect
-        final ArgumentCaptor<ConnectionListener> listenerCaptor = ArgumentCaptor.forClass(ConnectionListener.class);
-        doNothing().when(dbService).addConnectionListener(listenerCaptor.capture());
-        
-        doAnswer(new Answer<Void>() {
-
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                ConnectionListener listener = listenerCaptor.getValue();
-                listener.changed(ConnectionStatus.CONNECTED);
-                return null;
-            }
-            
-        }).when(dbService).connect();
-
-        // Run agent in a new thread so we can timeout on failure
-        Thread t = new Thread(new Runnable() {
-            
-            @Override
-            public void run() {
-                // Finish when config server starts listening
-                try {
-                    doAnswer(new Answer<Void>() {
-
-                        @Override
-                        public Void answer(InvocationOnMock invocation) throws Throwable {
-                            latch.countDown();
-                            return null;
-                        }
-                    }).when(configServer).startListening(COMMAND_CHANNLE_BIND_HOST, COMMAND_CHANNEL_BIND_PORT);
-                } catch (IOException e1) {
-                    fail("a mock should not throw an exception");
-                }
-                
-                try {
-                    agent.run(commandContext);
-                } catch (CommandException e) {
-                    ce[0] = e;
-                }
-            }
-        });
-        
-        t.start();
-    }
-
-}
-
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/ServiceCommandTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,412 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl;
-
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.util.Collection;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ActionNotifier;
-import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
-import com.redhat.thermostat.common.cli.CommandContext;
-import com.redhat.thermostat.common.cli.CommandException;
-import com.redhat.thermostat.common.cli.Console;
-import com.redhat.thermostat.common.tools.ApplicationState;
-import com.redhat.thermostat.launcher.Launcher;
-import com.redhat.thermostat.testutils.StubBundleContext;
-
-public class ServiceCommandTest {
-
-    private ByteArrayOutputStream stdErrOut;
-    private Launcher mockLauncher;
-    private ServiceCommand serviceCommand;
-    private CommandContext mockCommandContext;
-
-    private static ActionEvent<ApplicationState> mockActionEvent;
-    private static Collection<ActionListener<ApplicationState>> listeners;
-
-    private static final String[] STORAGE_START_ARGS = { "storage", "--start" };
-    private static final String[] STORAGE_STOP_ARGS = { "storage", "--stop" };
-    private static final String[] AGENT_ARGS = {"agent", "-d", "Test String"};
-    private static final String AGENT_ID = "Test ID";
-
-    @SuppressWarnings("unchecked")
-    @Before
-    public void setUp() {
-        StubBundleContext bundleContext = new StubBundleContext();
-        mockLauncher = mock(Launcher.class);
-        bundleContext.registerService(Launcher.class, mockLauncher, null);
-        serviceCommand = new ServiceCommand(bundleContext);
-        
-        AbstractStateNotifyingCommand mockStorageCommand = mock(AbstractStateNotifyingCommand.class);
-        mockActionEvent = mock(ActionEvent.class);
-        when(mockActionEvent.getSource()).thenReturn(mockStorageCommand);
-        mockCommandContext = mock(CommandContext.class);
-        Console console = mock(Console.class);
-        stdErrOut = new ByteArrayOutputStream();
-        PrintStream err = new PrintStream(stdErrOut);
-        when(console.getError()).thenReturn(err);
-        when(mockCommandContext.getConsole()).thenReturn(console);
-        
-        ActionNotifier<ApplicationState> mockNotifier = mock(ActionNotifier.class);
-        when(mockStorageCommand.getNotifier()).thenReturn(mockNotifier);
-        when(mockActionEvent.getPayload()).thenReturn(new String("Test String"));
-    }
-
-    @After
-    public void tearDown() {
-        listeners = null;
-        mockLauncher = null;
-        serviceCommand = null;
-        mockActionEvent = null;
-        mockCommandContext = null;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test(timeout=1000)
-    public void testRunOnce() throws CommandException, InterruptedException {
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-                
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
-                
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
-                when(mockActionEvent.getPayload()).thenReturn(AGENT_ID);
-
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-
-        final boolean[] result = new boolean[2];
-        final String[] agentIdFound = new String[1];
-        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
-            @SuppressWarnings("incomplete-switch")
-            @Override
-            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                    case FAIL:
-                        result[0] = false;
-                        break;
-                    case START:
-                        result[0] = true;
-                        agentIdFound[0] = (String) actionEvent.getPayload();
-                        break;
-                    case STOP:
-                        result[1] = true;
-                        break;
-                }
-            }
-        });
-
-        boolean exTriggered = false;
-        try {
-            serviceCommand.run(mockCommandContext);
-        } catch (CommandException e) { 
-            exTriggered = true;
-        }
-        Assert.assertFalse(exTriggered);
-        Assert.assertTrue("Agent expected to fire START event", result[0]);
-        Assert.assertTrue("Agent expected to fire STOP event", result[1]);
-        Assert.assertEquals("Payload does not contain AgentId matching the agent started", agentIdFound[0], AGENT_ID);
-
-        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
-        verify(mockLauncher, times(1)).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockActionEvent, times(2)).getActionId();
-    }
-
-    @Test(timeout=1000)
-    public void testStorageStartUnknownPath()  throws CommandException {
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
-                // Return a null payload in order to trigger unknown path
-                when(mockActionEvent.getPayload()).thenReturn(null);
-
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-
-        final boolean[] result = new boolean[1];
-        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
-            @SuppressWarnings("incomplete-switch")
-            @Override
-            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                    case FAIL:
-                        result[0] = true;
-                        break;
-                    case START:
-                        result[0] = false;
-                        break;
-                    case STOP:
-                        result[0] = false;
-                        break;
-                }
-            }
-        });
-
-        boolean exTriggered = false;
-        try {
-            serviceCommand.run(mockCommandContext);
-        } catch (CommandException e) {
-            exTriggered = true;
-        }
-        Assert.assertFalse(exTriggered);
-        Assert.assertEquals("Unexpected result from storage.\n", stdErrOut.toString());
-        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
-
-        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
-        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockActionEvent, times(1)).getActionId();
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test(timeout=1000)
-    public void testStorageFailStart() throws CommandException, InterruptedException {
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-                
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
-                when(mockActionEvent.getPayload()).thenReturn(new Exception("Test Exception"));
-                
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-
-        final boolean[] result = new boolean[1];
-        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
-            @SuppressWarnings("incomplete-switch")
-            @Override
-            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                    case FAIL:
-                        result[0] = true;
-                        break;
-                    case START:
-                        result[0] = false;
-                        break;
-                    case STOP:
-                        result[0] = false;
-                        break;
-                }
-            }
-        });
-
-        boolean exTriggered = false;
-        try {
-            serviceCommand.run(mockCommandContext);
-        } catch (CommandException e) {
-            exTriggered = true;
-        }
-        Assert.assertTrue(exTriggered);
-        Assert.assertEquals("Test Exception\n", stdErrOut.toString());
-        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
-
-        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockLauncher, never()).run(eq(STORAGE_STOP_ARGS), anyBoolean());
-        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockActionEvent, times(1)).getActionId();
-    }
-
-    @Test(timeout=1000)
-    public void testStorageFailStartUnknownPath()  throws CommandException {
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
-                // Return a null payload in order to trigger unknown path
-                when(mockActionEvent.getPayload()).thenReturn(null);
-
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-
-        final boolean[] result = new boolean[1];
-        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
-            @SuppressWarnings("incomplete-switch")
-            @Override
-            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                    case FAIL:
-                        result[0] = true;
-                        break;
-                    case START:
-                        result[0] = false;
-                        break;
-                    case STOP:
-                        result[0] = false;
-                        break;
-                }
-            }
-        });
-
-        boolean exTriggered = false;
-        try {
-            serviceCommand.run(mockCommandContext);
-        } catch (CommandException e) {
-            exTriggered = true;
-        }
-        Assert.assertTrue(exTriggered);
-        Assert.assertEquals("Unexpected result from storage.\n", stdErrOut.toString());
-        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
-
-        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockLauncher, never()).run(eq(STORAGE_STOP_ARGS), anyBoolean());
-        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockActionEvent, times(1)).getActionId();
-    }
-    
-    @Test
-    public void testAgentStartFail()  throws CommandException {
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
-
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        doAnswer(new Answer<Void>() {
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Object[] args = invocation.getArguments();
-                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
-
-                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
-
-                for(ActionListener<ApplicationState> listener : listeners) {
-                    listener.actionPerformed(mockActionEvent);
-                }
-                return null;
-            }
-        }).when(mockLauncher).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-
-        final boolean[] result = new boolean[1];
-        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
-            @SuppressWarnings("incomplete-switch")
-            @Override
-            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                    case FAIL:
-                        result[0] = true;
-                        break;
-                    case START:
-                        result[0] = false;
-                        break;
-                    case STOP:
-                        result[0] = false;
-                        break;
-                }
-            }
-        });
-
-        boolean exTriggered = false;
-        try {
-            serviceCommand.run(mockCommandContext);
-        } catch (CommandException e) {
-            exTriggered = true;
-        }
-        Assert.assertFalse(exTriggered);
-        Assert.assertEquals("Thermostat agent failed to start. See logs for details.\n", stdErrOut.toString());
-        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
-
-        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
-        verify(mockLauncher, times(1)).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
-        verify(mockActionEvent, times(2)).getActionId();
-    }
-
-}
-
--- a/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/impl/locale/TranslateTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.agent.cli.impl.locale;
-
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Properties;
-
-import org.junit.After;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-public class TranslateTest {
-
-    private Locale lang;
-
-    @Before
-    public void setUp() {
-        this.lang = Locale.getDefault();
-        Locale.setDefault(Locale.US);
-    }
-
-    @After
-    public void tearDown() {
-        Locale.setDefault(lang);
-    }
-
-    @Test
-    public void testLocalizedStringsArePresent() throws IOException {
-
-        String stringsResource = "/" + LocaleResources.RESOURCE_BUNDLE.replace(".", "/") + ".properties";
-
-        Properties props = new Properties();
-        props.load(getClass().getResourceAsStream(stringsResource));
-
-        Assert.assertEquals(LocaleResources.values().length, props.values().size());
-        for (LocaleResources resource : LocaleResources.values()) {
-            Assert.assertTrue("missing property from resource bound file: " + resource,
-                              props.containsKey(resource.name()));
-        }
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ActivatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import static com.redhat.thermostat.testutils.Asserts.assertCommandIsRegistered;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ExitStatus;
+import com.redhat.thermostat.shared.config.SSLConfiguration;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.testutils.StubBundleContext;
+
+public class ActivatorTest {
+
+    @Test
+    public void verifyActivatorRegistersCommands() throws Exception {        
+        StubBundleContext bundleContext = new StubBundleContext();
+
+        ExitStatus exitStatus = mock(ExitStatus.class);
+        WriterID writerID = mock(WriterID.class);
+        bundleContext.registerService(WriterID.class, writerID, null);
+        bundleContext.registerService(ExitStatus.class, exitStatus, null);
+        bundleContext.registerService(SSLConfiguration.class, mock(SSLConfiguration.class), null);
+        
+        Activator activator = new Activator();
+
+        assertEquals(0, bundleContext.getServiceListeners().size());
+        
+        activator.start(bundleContext);
+        
+        assertEquals(4, bundleContext.getServiceListeners().size());
+        
+        assertCommandIsRegistered(bundleContext, "agent", AgentApplication.class);
+        assertCommandIsRegistered(bundleContext, "service", ServiceCommand.class);
+
+        activator.stop(bundleContext);
+
+        assertEquals(0, bundleContext.getServiceListeners().size());
+        assertEquals(3, bundleContext.getAllServices().size());
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/AgentApplicationTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.whenNew;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.redhat.thermostat.agent.Agent;
+import com.redhat.thermostat.agent.cli.internal.AgentApplication.ConfigurationCreator;
+import com.redhat.thermostat.agent.command.ConfigurationServer;
+import com.redhat.thermostat.agent.config.AgentStartupConfiguration;
+import com.redhat.thermostat.agent.utils.management.MXBeanConnectionPool;
+import com.redhat.thermostat.backend.BackendRegistry;
+import com.redhat.thermostat.common.ExitStatus;
+import com.redhat.thermostat.common.LaunchException;
+import com.redhat.thermostat.common.cli.Arguments;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.DependencyServices;
+import com.redhat.thermostat.shared.config.InvalidConfigurationException;
+import com.redhat.thermostat.shared.config.SSLConfiguration;
+import com.redhat.thermostat.storage.core.Connection.ConnectionListener;
+import com.redhat.thermostat.storage.core.Connection.ConnectionStatus;
+import com.redhat.thermostat.storage.core.ConnectionException;
+import com.redhat.thermostat.storage.core.DbService;
+import com.redhat.thermostat.storage.core.DbServiceFactory;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.StorageCredentials;
+import com.redhat.thermostat.storage.core.WriterID;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.BackendInfoDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+
+@RunWith(PowerMockRunner.class)
+public class AgentApplicationTest {
+
+    private static final String COMMAND_CHANNLE_BIND_HOST = "test";
+    private static final int COMMAND_CHANNEL_BIND_PORT = 10101;
+
+    private StubBundleContext context;
+
+    private ConfigurationServer configServer;
+    private DbService dbService;
+    private ConfigurationCreator configCreator;
+    private ExitStatus exitStatus;
+    private DbServiceFactory dbServiceFactory;
+    private WriterID writerId;
+    
+    @Before
+    public void setUp() throws InvalidConfigurationException {
+        
+        context = new StubBundleContext();
+        
+        AgentStartupConfiguration config = mock(AgentStartupConfiguration.class);
+        when(config.getDBConnectionString()).thenReturn("test string; please ignore");
+        when(config.getConfigListenAddress()).thenReturn(COMMAND_CHANNLE_BIND_HOST + ":" + COMMAND_CHANNEL_BIND_PORT);
+
+        configCreator = mock(ConfigurationCreator.class);
+        when(configCreator.create()).thenReturn(config);
+
+        Storage storage = mock(Storage.class);
+        context.registerService(Storage.class, storage, null);
+        AgentInfoDAO agentInfoDAO = mock(AgentInfoDAO.class);
+        context.registerService(AgentInfoDAO.class.getName(), agentInfoDAO, null);
+        BackendInfoDAO backendInfoDAO = mock(BackendInfoDAO.class);
+        context.registerService(BackendInfoDAO.class.getName(), backendInfoDAO, null);
+        configServer = mock(ConfigurationServer.class);
+        context.registerService(ConfigurationServer.class.getName(), configServer, null);
+        dbServiceFactory = mock(DbServiceFactory.class);
+        dbService = mock(DbService.class);
+        writerId = mock(WriterID.class);
+        when(dbServiceFactory.createDbService(anyString(), any(StorageCredentials.class), any(SSLConfiguration.class))).thenReturn(dbService);
+
+        exitStatus = mock(ExitStatus.class);
+    }
+
+    @After
+    public void tearDown() {
+        context = null;
+        configServer = null;
+        dbService = null;
+        configCreator = null;
+        dbServiceFactory = null;
+        exitStatus = null;
+    }
+
+    @Test
+    public void testAgentStartup() throws CommandException, InterruptedException {
+        final AgentApplication agent = new AgentApplication(context, exitStatus, writerId, mock(SSLConfiguration.class), new DependencyServices(), configCreator, dbServiceFactory);
+        agent.setStorageCredentials(mock(StorageCredentials.class));
+        final CountDownLatch latch = new CountDownLatch(1);
+        final CommandException[] ce = new CommandException[1];
+        final long timeoutMillis = 5000L;
+        
+        startAgentRunThread(timeoutMillis, agent, ce, latch);
+        
+        boolean ret = latch.await(timeoutMillis, TimeUnit.MILLISECONDS);
+        if (ce[0] != null) {
+            throw ce[0];
+        }
+        if (!ret) {
+            fail("Timeout expired!");
+        }
+        
+    }
+    
+    @Test
+    public void testAgentStartupConnectFailure() throws CommandException, InterruptedException {
+        final AgentApplication agent = new AgentApplication(context, exitStatus, writerId, mock(SSLConfiguration.class), new DependencyServices(), configCreator, dbServiceFactory);
+        agent.setStorageCredentials(mock(StorageCredentials.class));
+        
+        Arguments args = mock(Arguments.class);
+        final CommandContext commandContext = mock(CommandContext.class);
+        when(commandContext.getArguments()).thenReturn(args);
+        
+        // Throw a ConnectionException when we try to connect to storage
+        doThrow(new ConnectionException()).when(dbService).connect();
+        
+        agent.run(commandContext);
+        
+        // Ensure we shut down command channel server
+        verify(configServer).stopListening();
+    }
+    
+    /*
+     * Having the PrepareForTest annotation on method level does not seem to
+     * deadlock the test, which seems to be more or less reliably reproducible
+     * if this annotation is at class level instead. Steps to reproduce the
+     * deadlock is:
+     * 1. Attach the PrepareForTest annotation to the class (over the test
+     *    method)
+     * 2. Run the test multiple times. 5-20 times seemed sufficient for me to
+     *    make the deadlock show up. This deadlock does not seem to happen
+     *    otherwise (can run up to 30 times head-to-head without deadlock).
+     *    
+     */
+    @PrepareForTest({ AgentApplication.class })
+    @SuppressWarnings("unchecked")
+    @Test
+    public void verifyBackendRegistryProblemsSetsExitStatus() throws Exception {
+        whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class)
+                .withArguments(any(BundleContext.class))
+                .thenThrow(InvalidSyntaxException.class);
+        final AgentApplication agent = new AgentApplication(context,
+                exitStatus, writerId,  mock(SSLConfiguration.class),
+                mock(DependencyServices.class), configCreator, dbServiceFactory);
+        try {
+            agent.startAgent(null, null, null, null);
+        } catch (RuntimeException e) {
+            assertEquals(InvalidSyntaxException.class, e.getCause().getClass());
+        }
+        verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR);
+    }
+    
+    @PrepareForTest({ AgentApplication.class })
+    @Test
+    public void verifyAgentLaunchExceptionSetsExitStatus() throws Exception {
+        whenNew(BackendRegistry.class).withParameterTypes(BundleContext.class)
+                .withArguments(any(BundleContext.class))
+                .thenReturn(mock(BackendRegistry.class));
+        Agent mockAgent = mock(Agent.class);
+        whenNew(Agent.class).withParameterTypes(BackendRegistry.class,
+                AgentStartupConfiguration.class, Storage.class,
+                AgentInfoDAO.class, BackendInfoDAO.class, WriterID.class, 
+                MXBeanConnectionPool.class).withArguments(
+                any(BackendRegistry.class),
+                any(AgentStartupConfiguration.class), any(Storage.class),
+                any(AgentInfoDAO.class), any(BackendInfoDAO.class), any(WriterID.class), 
+                any(MXBeanConnectionPool.class)).thenReturn(mockAgent);
+        doThrow(LaunchException.class).when(mockAgent).start();
+        final AgentApplication agent = new AgentApplication(context,
+                exitStatus, writerId,  mock(SSLConfiguration.class),
+                mock(DependencyServices.class), configCreator, dbServiceFactory);
+        try {
+            agent.startAgent(null, null, null, null);
+        } catch (RuntimeException e) {
+            fail("Should not have thrown RuntimeException");
+        }
+        verify(exitStatus).setExitStatus(ExitStatus.EXIT_ERROR);
+    }
+
+    private void startAgentRunThread(final long timoutMillis, final AgentApplication agent, final CommandException[] ce, final CountDownLatch latch) {
+        Arguments args = mock(Arguments.class);
+        final CommandContext commandContext = mock(CommandContext.class);
+        when(commandContext.getArguments()).thenReturn(args);
+        
+        // Immediately switch to CONNECTED state on dbService.connect
+        final ArgumentCaptor<ConnectionListener> listenerCaptor = ArgumentCaptor.forClass(ConnectionListener.class);
+        doNothing().when(dbService).addConnectionListener(listenerCaptor.capture());
+        
+        doAnswer(new Answer<Void>() {
+
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                ConnectionListener listener = listenerCaptor.getValue();
+                listener.changed(ConnectionStatus.CONNECTED);
+                return null;
+            }
+            
+        }).when(dbService).connect();
+
+        // Run agent in a new thread so we can timeout on failure
+        Thread t = new Thread(new Runnable() {
+            
+            @Override
+            public void run() {
+                // Finish when config server starts listening
+                try {
+                    doAnswer(new Answer<Void>() {
+
+                        @Override
+                        public Void answer(InvocationOnMock invocation) throws Throwable {
+                            latch.countDown();
+                            return null;
+                        }
+                    }).when(configServer).startListening(COMMAND_CHANNLE_BIND_HOST, COMMAND_CHANNEL_BIND_PORT);
+                } catch (IOException e1) {
+                    fail("a mock should not throw an exception");
+                }
+                
+                try {
+                    agent.run(commandContext);
+                } catch (CommandException e) {
+                    ce[0] = e;
+                }
+            }
+        });
+        
+        t.start();
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/ServiceCommandTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal;
+
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.util.Collection;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ActionNotifier;
+import com.redhat.thermostat.common.cli.AbstractStateNotifyingCommand;
+import com.redhat.thermostat.common.cli.CommandContext;
+import com.redhat.thermostat.common.cli.CommandException;
+import com.redhat.thermostat.common.cli.Console;
+import com.redhat.thermostat.common.tools.ApplicationState;
+import com.redhat.thermostat.launcher.Launcher;
+import com.redhat.thermostat.testutils.StubBundleContext;
+
+public class ServiceCommandTest {
+
+    private ByteArrayOutputStream stdErrOut;
+    private Launcher mockLauncher;
+    private ServiceCommand serviceCommand;
+    private CommandContext mockCommandContext;
+
+    private static ActionEvent<ApplicationState> mockActionEvent;
+    private static Collection<ActionListener<ApplicationState>> listeners;
+
+    private static final String[] STORAGE_START_ARGS = { "storage", "--start" };
+    private static final String[] STORAGE_STOP_ARGS = { "storage", "--stop" };
+    private static final String[] AGENT_ARGS = {"agent", "-d", "Test String"};
+    private static final String AGENT_ID = "Test ID";
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        StubBundleContext bundleContext = new StubBundleContext();
+        mockLauncher = mock(Launcher.class);
+        bundleContext.registerService(Launcher.class, mockLauncher, null);
+        serviceCommand = new ServiceCommand(bundleContext);
+        
+        AbstractStateNotifyingCommand mockStorageCommand = mock(AbstractStateNotifyingCommand.class);
+        mockActionEvent = mock(ActionEvent.class);
+        when(mockActionEvent.getSource()).thenReturn(mockStorageCommand);
+        mockCommandContext = mock(CommandContext.class);
+        Console console = mock(Console.class);
+        stdErrOut = new ByteArrayOutputStream();
+        PrintStream err = new PrintStream(stdErrOut);
+        when(console.getError()).thenReturn(err);
+        when(mockCommandContext.getConsole()).thenReturn(console);
+        
+        ActionNotifier<ApplicationState> mockNotifier = mock(ActionNotifier.class);
+        when(mockStorageCommand.getNotifier()).thenReturn(mockNotifier);
+        when(mockActionEvent.getPayload()).thenReturn(new String("Test String"));
+    }
+
+    @After
+    public void tearDown() {
+        listeners = null;
+        mockLauncher = null;
+        serviceCommand = null;
+        mockActionEvent = null;
+        mockCommandContext = null;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(timeout=1000)
+    public void testRunOnce() throws CommandException, InterruptedException {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+                
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
+                
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
+                when(mockActionEvent.getPayload()).thenReturn(AGENT_ID);
+
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+
+        final boolean[] result = new boolean[2];
+        final String[] agentIdFound = new String[1];
+        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
+            @SuppressWarnings("incomplete-switch")
+            @Override
+            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case FAIL:
+                        result[0] = false;
+                        break;
+                    case START:
+                        result[0] = true;
+                        agentIdFound[0] = (String) actionEvent.getPayload();
+                        break;
+                    case STOP:
+                        result[1] = true;
+                        break;
+                }
+            }
+        });
+
+        boolean exTriggered = false;
+        try {
+            serviceCommand.run(mockCommandContext);
+        } catch (CommandException e) { 
+            exTriggered = true;
+        }
+        Assert.assertFalse(exTriggered);
+        Assert.assertTrue("Agent expected to fire START event", result[0]);
+        Assert.assertTrue("Agent expected to fire STOP event", result[1]);
+        Assert.assertEquals("Payload does not contain AgentId matching the agent started", agentIdFound[0], AGENT_ID);
+
+        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
+        verify(mockLauncher, times(1)).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockActionEvent, times(2)).getActionId();
+    }
+
+    @Test(timeout=1000)
+    public void testStorageStartUnknownPath()  throws CommandException {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
+                // Return a null payload in order to trigger unknown path
+                when(mockActionEvent.getPayload()).thenReturn(null);
+
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+
+        final boolean[] result = new boolean[1];
+        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
+            @SuppressWarnings("incomplete-switch")
+            @Override
+            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case FAIL:
+                        result[0] = true;
+                        break;
+                    case START:
+                        result[0] = false;
+                        break;
+                    case STOP:
+                        result[0] = false;
+                        break;
+                }
+            }
+        });
+
+        boolean exTriggered = false;
+        try {
+            serviceCommand.run(mockCommandContext);
+        } catch (CommandException e) {
+            exTriggered = true;
+        }
+        Assert.assertFalse(exTriggered);
+        Assert.assertEquals("Unexpected result from storage.\n", stdErrOut.toString());
+        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
+
+        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
+        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockActionEvent, times(1)).getActionId();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test(timeout=1000)
+    public void testStorageFailStart() throws CommandException, InterruptedException {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+                
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
+                when(mockActionEvent.getPayload()).thenReturn(new Exception("Test Exception"));
+                
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+
+        final boolean[] result = new boolean[1];
+        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
+            @SuppressWarnings("incomplete-switch")
+            @Override
+            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case FAIL:
+                        result[0] = true;
+                        break;
+                    case START:
+                        result[0] = false;
+                        break;
+                    case STOP:
+                        result[0] = false;
+                        break;
+                }
+            }
+        });
+
+        boolean exTriggered = false;
+        try {
+            serviceCommand.run(mockCommandContext);
+        } catch (CommandException e) {
+            exTriggered = true;
+        }
+        Assert.assertTrue(exTriggered);
+        Assert.assertEquals("Test Exception\n", stdErrOut.toString());
+        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
+
+        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockLauncher, never()).run(eq(STORAGE_STOP_ARGS), anyBoolean());
+        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockActionEvent, times(1)).getActionId();
+    }
+
+    @Test(timeout=1000)
+    public void testStorageFailStartUnknownPath()  throws CommandException {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
+                // Return a null payload in order to trigger unknown path
+                when(mockActionEvent.getPayload()).thenReturn(null);
+
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+
+        final boolean[] result = new boolean[1];
+        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
+            @SuppressWarnings("incomplete-switch")
+            @Override
+            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case FAIL:
+                        result[0] = true;
+                        break;
+                    case START:
+                        result[0] = false;
+                        break;
+                    case STOP:
+                        result[0] = false;
+                        break;
+                }
+            }
+        });
+
+        boolean exTriggered = false;
+        try {
+            serviceCommand.run(mockCommandContext);
+        } catch (CommandException e) {
+            exTriggered = true;
+        }
+        Assert.assertTrue(exTriggered);
+        Assert.assertEquals("Unexpected result from storage.\n", stdErrOut.toString());
+        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
+
+        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockLauncher, never()).run(eq(STORAGE_STOP_ARGS), anyBoolean());
+        verify(mockLauncher, never()).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockActionEvent, times(1)).getActionId();
+    }
+    
+    @Test
+    public void testAgentStartFail()  throws CommandException {
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.START);
+
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        doAnswer(new Answer<Void>() {
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                listeners = (Collection<ActionListener<ApplicationState>>)args[1];
+
+                when(mockActionEvent.getActionId()).thenReturn(ApplicationState.FAIL);
+
+                for(ActionListener<ApplicationState> listener : listeners) {
+                    listener.actionPerformed(mockActionEvent);
+                }
+                return null;
+            }
+        }).when(mockLauncher).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+
+        final boolean[] result = new boolean[1];
+        serviceCommand.getNotifier().addActionListener(new ActionListener<ApplicationState>() {
+            @SuppressWarnings("incomplete-switch")
+            @Override
+            public void actionPerformed(ActionEvent<ApplicationState> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                    case FAIL:
+                        result[0] = true;
+                        break;
+                    case START:
+                        result[0] = false;
+                        break;
+                    case STOP:
+                        result[0] = false;
+                        break;
+                }
+            }
+        });
+
+        boolean exTriggered = false;
+        try {
+            serviceCommand.run(mockCommandContext);
+        } catch (CommandException e) {
+            exTriggered = true;
+        }
+        Assert.assertFalse(exTriggered);
+        Assert.assertEquals("Thermostat agent failed to start. See logs for details.\n", stdErrOut.toString());
+        Assert.assertTrue("Agent expected to fire FAIL event", result[0]);
+
+        verify(mockLauncher, times(1)).run(eq(STORAGE_START_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockLauncher, times(1)).run(eq(STORAGE_STOP_ARGS), anyBoolean());
+        verify(mockLauncher, times(1)).run(eq(AGENT_ARGS), isA(Collection.class), anyBoolean());
+        verify(mockActionEvent, times(2)).getActionId();
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/agent/cli/src/test/java/com/redhat/thermostat/agent/cli/internal/locale/TranslateTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.agent.cli.internal.locale;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.Properties;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TranslateTest {
+
+    private Locale lang;
+
+    @Before
+    public void setUp() {
+        this.lang = Locale.getDefault();
+        Locale.setDefault(Locale.US);
+    }
+
+    @After
+    public void tearDown() {
+        Locale.setDefault(lang);
+    }
+
+    @Test
+    public void testLocalizedStringsArePresent() throws IOException {
+
+        String stringsResource = "/" + LocaleResources.RESOURCE_BUNDLE.replace(".", "/") + ".properties";
+
+        Properties props = new Properties();
+        props.load(getClass().getResourceAsStream(stringsResource));
+
+        Assert.assertEquals(LocaleResources.values().length, props.values().size());
+        for (LocaleResources resource : LocaleResources.values()) {
+            Assert.assertTrue("missing property from resource bound file: " + resource,
+                              props.containsKey(resource.name()));
+        }
+    }
+}
+
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/AgentInfoPopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.internal.dao.AgentInfoDAOImpl;
-import com.redhat.thermostat.storage.model.AgentInformation;
-
-public class AgentInfoPopulator extends BasePopulator {
-    
-    private final AgentInfoDAO dao;
-    
-    public AgentInfoPopulator() {
-        this(null);
-    }
-    
-    // for testing
-    AgentInfoPopulator(AgentInfoDAO dao) {
-        this.dao = dao;
-    }
-
-    @Override
-    public SharedState addPojos(Storage storage, ConfigItem item, SharedState state) {
-        // Default to all alive, if unset
-        int aliveItems = item.getAliveItems() == ConfigItem.UNSET ? item.getNumber() : item.getAliveItems();
-        List<String> processedRecords = new ArrayList<>(); 
-        AgentInfoDAO agentInfoDao = getDao(storage);
-        long currentTime = System.currentTimeMillis();
-        long countBefore = agentInfoDao.getCount();
-        System.out.println("Populating "+ item.getNumber() + " " + item.getName() + " records");
-        for (int i = 0; i < item.getNumber(); i++) {
-            AgentInformation agentInfo = new AgentInformation();
-            String agentId = UUID.randomUUID().toString();
-            processedRecords.add(agentId);
-            agentInfo.setAgentId(agentId);
-            agentInfo.setAlive(getAliveValue(aliveItems, i));
-            agentInfo.setConfigListenAddress(String.format("127.0.0.1:%d", i));
-            agentInfo.setStartTime(currentTime);
-            agentInfoDao.addAgentInformation(agentInfo);
-            reportProgress(item, i);
-        }
-        doWaitUntilCount(agentInfoDao, countBefore + item.getNumber());
-        state.addProcessedRecords("agentId", new ProcessedRecords<>(processedRecords));
-        // FIXME: Why does HostInfoDAOImpl need AgentInfoDAO? See HostInfoPopulator
-        state.addProperty("agent-info-dao", agentInfoDao);
-        return state;
-    }
-
-    private boolean getAliveValue(int aliveItems, int i) {
-        if (i < aliveItems) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    private AgentInfoDAO getDao(Storage storage) {
-        if (dao == null) {
-            return new AgentInfoDAOImpl(storage);
-        } else {
-            return dao;
-        }
-    }
-
-    @Override
-    public String getHandledCollection() {
-        return AgentInfoDAO.CATEGORY.getName();
-    }
-
-}
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/BasePopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import com.redhat.thermostat.dev.populator.CollectionPopulator;
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.storage.core.Countable;
-
-public abstract class BasePopulator implements CollectionPopulator {
-
-    protected void doWaitUntilCount(Countable countable, long expectedCount) {
-        System.out.print("Waiting for storage items to arrive at backend...");
-        long currCount = countable.getCount();
-        while (currCount != expectedCount) {
-            try {
-                Thread.sleep(200);
-                System.out.print("."); // report some progress
-            } catch (InterruptedException e) {
-                // ignore
-            }
-            currCount = countable.getCount();
-        }
-        System.out.println(" Done.");
-    }
-    
-    protected void reportProgress(ConfigItem item, int currCount) {
-        if (currCount > 0 && currCount % 10 == 0) {
-            System.out.println("Submitted " + currCount + " " + item.getName() + " records to storage.");
-        }
-    }
-}
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/HostInfoPopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import java.util.List;
-import java.util.Objects;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.dao.HostInfoDAO;
-import com.redhat.thermostat.storage.internal.dao.HostInfoDAOImpl;
-import com.redhat.thermostat.storage.model.HostInfo;
-
-public class HostInfoPopulator extends BasePopulator {
-    
-    private static final String[] HOSTS_FORMAT = new String[] {
-            "vm-host-%06d", "prometheus-%06d", "saturn-%06d"
-    };
-    private static final String DOMAIN_FORMAT = "domain-%06d";
-    private static final String HOSTNAME_SUFFIX = "example.com";
-    private static final long MB = 1024 * 1024;
-    private final HostInfoDAO dao;
-    
-    public HostInfoPopulator() {
-        this(null);
-    }
-
-    HostInfoPopulator(HostInfoDAO dao) {
-        this.dao = dao;
-    }
-
-    @Override
-    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
-        // FIXME: We need the same instance of the AgentInfoDAO due to the register-category quirk.
-        AgentInfoDAO agentInfoDAO = Objects.requireNonNull((AgentInfoDAO)relState.getProperty("agent-info-dao"));
-        HostInfoDAO hostDAO = getDao(storage, agentInfoDAO);
-        List<String> agentIds = relState.getProcessedRecordsFor("agentId").getAll();
-        int totalItems = agentIds.size();
-        int currCount = 0;
-        long countBefore = hostDAO.getCount();
-        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
-        // There is a 1-to-1 correspondance between host-info and agents
-        for (int i = agentIds.size() - 1; i >= 0; i--) {
-            String agentId = agentIds.get(i);
-            HostInfo info = new HostInfo();
-            info.setAgentId(agentId);
-            info.setCpuCount(getRandomInt(50 + i));
-            info.setCpuModel("x86_64, data populator");
-            info.setHostname(getRandomHostName(i));
-            info.setOsKernel("4.2.3 data populator");
-            info.setOsName("Linux Random Flavor");
-            int memory = getRandomInt(500 + i);
-            info.setTotalMemory(memory * MB);
-            hostDAO.putHostInfo(info);
-            reportProgress(item, currCount);
-            currCount++;
-        }
-        doWaitUntilCount(hostDAO, countBefore + totalItems);
-        return relState;
-    }
-
-    private String getRandomHostName(int i) {
-        int index = getRandomInt(HOSTS_FORMAT.length);
-        String hostFormat = HOSTS_FORMAT[index] + "." + DOMAIN_FORMAT + "." + HOSTNAME_SUFFIX;
-        return String.format(hostFormat, i, i);
-    }
-
-    private int getRandomInt(int i) {
-        return (int)(Math.random() * i);
-    }
-
-    @Override
-    public String getHandledCollection() {
-        return HostInfoDAO.hostInfoCategory.getName();
-    }
-    
-    // hook for testing
-    private HostInfoDAO getDao(Storage storage, AgentInfoDAO agentInfoDAO) {
-        if (dao == null) {
-            return new HostInfoDAOImpl(storage, agentInfoDAO);
-        } else {
-            return dao;
-        }
-    }
-
-}
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/NetworkInfoPopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import java.util.List;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.CategoryAdapter;
-import com.redhat.thermostat.storage.core.Countable;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.DescriptorParsingException;
-import com.redhat.thermostat.storage.core.PreparedStatement;
-import com.redhat.thermostat.storage.core.StatementDescriptor;
-import com.redhat.thermostat.storage.core.StatementExecutionException;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO;
-import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl;
-import com.redhat.thermostat.storage.model.AggregateCount;
-import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
-
-public class NetworkInfoPopulator extends BasePopulator {
-    
-    static final String[] IPV4_FORMATS = new String[] {
-            "192.168.0.%d", "10.33.4.%d", "89.15.93.%d" 
-    };
-    static final String[] IPV6_FORMATS = new String[] {
-            "e8:b1:fc:d2:e3:%s%%%s", "fe80::56ee:75ff:fe35:%s%%%s",
-            "0:0:0:0:0:0:0:%s%%%s"
-    };
-    private final String[] IFACE_NAMES = new String[] {
-            "lo", "enp0s25", "tun0", "virbr0", "wlp4s0"
-    };
-    private static int IPv6Mod = Integer.parseInt("ffff", 16);
-    private static int IPv4Mod = 255;
-    
-    private final NetworkInterfaceDAOCountable dao;
-    
-    public NetworkInfoPopulator() {
-        this(null);
-    }
-    
-    NetworkInfoPopulator(NetworkInterfaceDAOCountable dao) {
-        this.dao = dao;
-    }
-
-    @Override
-    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
-        ProcessedRecords<String> procAgents = relState.getProcessedRecordsFor("agentId");
-        NetworkInterfaceDAOCountable dao = getDao(storage);
-        List<String> allAgents = procAgents.getAll();
-        long countBefore = dao.getCount();
-        int totalItems = item.getNumber() * allAgents.size();
-        // populate network info records per agentID
-        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
-        int currVal = 0;
-        for (String agentId: allAgents) {
-            for (int i = 0; i < item.getNumber(); i++) {
-                NetworkInterfaceInfo info = new NetworkInterfaceInfo();
-                info.setAgentId(agentId);
-                String name = getRandomInterfaceName(i);
-                info.setInterfaceName(name);
-                info.setIp6Addr(getRandomIpv6Address(i, name));
-                info.setIp4Addr(getRandomIpv4Addrees(i));
-                dao.putNetworkInterfaceInfo(info);
-                reportProgress(item, currVal);
-                currVal++;
-            }
-        }
-        doWaitUntilCount(dao, countBefore + totalItems);
-        return relState;
-    }
-
-    private String getRandomIpv4Addrees(int i) {
-        int idx = getRandomInt(IPV4_FORMATS.length);
-        return String.format(IPV4_FORMATS[idx], getIpv4Octet(i));
-    }
-
-    private int getRandomInt(int upperBound) {
-        return (int)(Math.random() * upperBound);
-    }
-
-    private String getRandomIpv6Address(int i, String name) {
-        int idx = getRandomInt(IPV6_FORMATS.length);
-        return String.format(IPV6_FORMATS[idx], getIpv6Hextet(i), name);
-    }
-    
-    // package-private for testing
-    String getIpv6Hextet(int i) {
-        int remainder = i % IPv6Mod;
-        if (remainder == 0) {
-            // 0 isn't really an IP, make it 1 instead.
-            remainder = 1;
-        }
-        return Integer.toHexString(remainder);
-    }
-    
-    // package-private for testing
-    int getIpv4Octet(int i) {
-        int remainder = i % IPv4Mod;
-        if (remainder == 0) {
-            // 0 is network address, switch to IP
-            remainder = 1;
-        }
-        return remainder;
-    }
-
-    private String getRandomInterfaceName(int i) {
-        int idx = getRandomInt(IFACE_NAMES.length);
-        return IFACE_NAMES[idx] + i;
-    }
-
-    private NetworkInterfaceDAOCountable getDao(Storage storage) {
-        if (this.dao == null) {
-            return new NetworkInterfaceDAOCountable(storage);
-        }
-        return dao;
-    }
-
-    @Override
-    public String getHandledCollection() {
-        return NetworkInterfaceInfoDAO.networkInfoCategory.getName();
-    }
-    
-    static class NetworkInterfaceDAOCountable extends NetworkInterfaceInfoDAOImpl implements Countable {
-
-        private final Category<AggregateCount> aggregateCategory;
-        private final Storage storage;
-        
-        NetworkInterfaceDAOCountable(Storage storage) {
-            super(storage);
-            CategoryAdapter<NetworkInterfaceInfo, AggregateCount> adapter = new CategoryAdapter<>(networkInfoCategory);
-            aggregateCategory = adapter.getAdapted(AggregateCount.class);
-            storage.registerCategory(aggregateCategory);
-            this.storage = storage;
-        }
-
-        @Override
-        public long getCount() {
-            String descriptor = "QUERY-COUNT " + networkInfoCategory.getName();
-            try {
-                PreparedStatement<AggregateCount> stmt = storage.prepareStatement(new StatementDescriptor<>(aggregateCategory, descriptor));
-                Cursor<AggregateCount> cursor = stmt.executeQuery();
-                AggregateCount count = cursor.next();
-                return count.getCount();
-            } catch (DescriptorParsingException | StatementExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-        
-    }
-
-}
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/ThreadPopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import com.redhat.thermostat.common.Pair;
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.CategoryAdapter;
-import com.redhat.thermostat.storage.core.Countable;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.DescriptorParsingException;
-import com.redhat.thermostat.storage.core.PreparedStatement;
-import com.redhat.thermostat.storage.core.StatementDescriptor;
-import com.redhat.thermostat.storage.core.StatementExecutionException;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.AggregateCount;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.dao.impl.ThreadDaoImpl;
-import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-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;
-
-public class ThreadPopulator extends BasePopulator {
-
-    /**
-     * Package-private and static for testing.
-     */
-    static final int NUM_SAMPLES = 10;
-
-    public final String SESSION = "session";
-    private final int NUM_THREADS = 3;
-    private final long SEED = 5;
-    Random generator = new Random(SEED);
-    private static final String[] THREAD_NAMES = new String[] {
-            "Spencer", "Sheldon", "Alice", "Bob", "Julie", "Phoebe", "Alpha", "Beta", "Gamma",
-            "Theta", "Zeta",
-    };
-    static final String DEADLOCK_DESC_FORMAT = "" +
-            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7 owned by \"%s\" Id=%d\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
-            "\t...\n\n" +
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
-            "\n\n" +
-            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e owned by \"%s\" Id=%d\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n" + 
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" + 
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
-            "\t...\n\n" + 
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
-            "\n\n" +
-            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2 owned by \"%s\" Id=%d\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" + 
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" + 
-            "\t...\n\n" +
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n\n\n";
-            
-    
-    private ThreadDaoCountable dao;
-
-    public ThreadPopulator() {
-        this(null);
-    }
-
-    ThreadPopulator(ThreadDaoCountable dao) {
-        this.dao = dao;
-    }
-
-    // Testing hook
-    private void createDao(Storage storage) {
-        if (dao == null) {
-            dao = new ThreadDaoCountable(storage);
-        }
-    }
-
-    @Override
-    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
-        createDao(storage);
-        // creates records per agent *and* vm
-        List<String> agentIds = relState.getProcessedRecordsFor("agentId").getAll();
-        List<String> vmIds = relState.getProcessedRecordsFor("vmId").getAll();
-        int perVmNumber = item.getNumber();
-        int totalCount = perVmNumber * agentIds.size() * vmIds.size();
-        int currCount = 0;
-        long countBefore = dao.getCount();
-        System.out.println("Populating "+ totalCount  + " " + item.getName() + " records");
-        long currTime = System.currentTimeMillis();
-        for (String agentId: agentIds) {
-            for (String vmId: vmIds) {
-                for (int i = 0; i < perVmNumber; i++) {
-                    ThreadSummary summary = new ThreadSummary();
-                    summary.setAgentId(agentId);
-                    summary.setVmId(vmId);
-                    int liveThreads = generator.nextInt(NUM_THREADS);
-                    summary.setCurrentLiveThreads(liveThreads);
-                    int daemonThreads = NUM_THREADS - liveThreads;
-                    summary.setCurrentDaemonThreads(daemonThreads);
-                    long timeStamp = currTime + i * 1000;
-                    summary.setTimeStamp(timeStamp);
-                    dao.saveSummary(summary);
-
-                    ThreadSession session = new ThreadSession();
-                    session.setSession(SESSION);
-                    session.setTimeStamp(timeStamp);
-                    session.setAgentId(agentId);
-                    session.setVmId(vmId);
-                    dao.saveSession(session);
-
-                    final Thread.State[] states = Thread.State.values();
-                    String state = states[generator.nextInt(states.length)].toString();
-                    String name = THREAD_NAMES[generator.nextInt(THREAD_NAMES.length)];
-
-                    for(int j = 0; j < NUM_SAMPLES; j++) {
-                        ThreadState threadState = new ThreadState();
-                        threadState.setTimeStamp(currTime + j * 1000);
-                        threadState.setVmId(vmId);
-                        threadState.setAgentId(agentId);
-                        threadState.setState(state);
-                        threadState.setId(i);
-                        threadState.setName(name);
-                        threadState.setSession(SESSION);
-                        dao.addThreadState(threadState);
-                    }
-
-                    ThreadHarvestingStatus status = new ThreadHarvestingStatus(agentId);
-                    status.setVmId(vmId);
-                    status.setTimeStamp(timeStamp);
-                    status.setHarvesting(generator.nextBoolean());
-                    dao.saveHarvestingStatus(status);
-
-                    VmDeadLockData data = new VmDeadLockData();
-                    data.setAgentId(agentId);
-                    data.setVmId(vmId);
-                    data.setTimeStamp(currTime + i * 10);
-                    data.setDeadLockDescription(getDeadlockedDescription(currCount));
-                    dao.saveDeadLockStatus(data);
-                    currCount++;
-                    reportProgress(item, currCount);
-                }
-            }
-        }
-        doWaitUntilCount(dao, countBefore + totalCount);
-        return relState;
-    }
-
-    private String getDeadlockedDescription(int currCount) {
-        return String.format(DEADLOCK_DESC_FORMAT, getFormatStringArgs(currCount));
-    }
-
-    Object[] getFormatStringArgs(int currCount) {
-        List<Pair<String, Integer>> nameIdMapping = new ArrayList<>();
-        int[] randomIds = getIdValues(currCount, NUM_THREADS);
-        int randomEntry = getRandomInt(THREAD_NAMES.length);
-        for (int i = 0, idx; i < NUM_THREADS; i++) {
-            idx = (randomEntry + i) % THREAD_NAMES.length;
-            String name = THREAD_NAMES[idx];
-            int tid = randomIds[i];
-            nameIdMapping.add(new Pair<>(name, tid));
-        }
-        Object[] formatArgs = new Object[NUM_THREADS * 2 * 2];
-        formatArgs[0] = nameIdMapping.get(0).getFirst();
-        formatArgs[1] = nameIdMapping.get(0).getSecond();
-        for (int i = 2, j = 1; i < formatArgs.length - 2; i+= 4, j++) {
-            formatArgs[i] = nameIdMapping.get(j).getFirst();
-            formatArgs[i + 1] = nameIdMapping.get(j).getSecond();
-            formatArgs[i + 2] = nameIdMapping.get(j).getFirst();
-            formatArgs[i + 3] = nameIdMapping.get(j).getSecond();
-        }
-        formatArgs[formatArgs.length - 2] = nameIdMapping.get(0).getFirst();
-        formatArgs[formatArgs.length - 1] = nameIdMapping.get(0).getSecond();
-        return formatArgs;
-    }
-
-    private int getRandomInt(int len) {
-        return (int)(Math.random() * len);
-    }
-
-    private int[] getIdValues(int currCount, int length) {
-        int[] values = new int[length];
-        for (int i = 0; i < values.length; i++) {
-            values[i] = currCount + i;
-        }
-        return values;
-    }
-
-    @Override
-    public String getHandledCollection() {
-        return ThreadDao.THREAD_HARVESTING_STATUS.getName();
-    }
-
-    static class ThreadDaoCountable extends ThreadDaoImpl implements Countable {
-
-        private final Category<AggregateCount> aggregateCategory;
-        private final Storage storage;
-
-        public ThreadDaoCountable(Storage storage) {
-            super(storage);
-            CategoryAdapter<VmDeadLockData, AggregateCount> adapter = new CategoryAdapter<>(DEADLOCK_INFO);
-            aggregateCategory = adapter.getAdapted(AggregateCount.class);
-            storage.registerCategory(aggregateCategory);
-            this.storage = storage;
-        }
-
-        @Override
-        public long getCount() {
-            String descriptor = "QUERY-COUNT " + DEADLOCK_INFO.getName();
-            try {
-                PreparedStatement<AggregateCount> stmt = storage.prepareStatement(new StatementDescriptor<>(aggregateCategory, descriptor));
-                Cursor<AggregateCount> cursor = stmt.executeQuery();
-                AggregateCount count = cursor.next();
-                return count.getCount();
-            } catch (DescriptorParsingException | StatementExecutionException e) {
-                throw new RuntimeException(e);
-            }
-        }
-
-    }
-}
--- a/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/impl/VmInfoPopulator.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.storage.internal.dao.VmInfoDAOImpl;
-import com.redhat.thermostat.storage.model.VmInfo;
-
-public class VmInfoPopulator extends BasePopulator {
-
-    private final VmInfoDAO dao;
-    
-    public VmInfoPopulator() {
-        this(null);
-    }
-    
-    VmInfoPopulator(VmInfoDAO dao) {
-        this.dao = dao;
-    }
-
-    @Override
-    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
-        // Default to all alive, if unset
-        int aliveItems = item.getAliveItems() == ConfigItem.UNSET ? item.getNumber() : item.getAliveItems();
-        ProcessedRecords<String> procAgents = relState.getProcessedRecordsFor("agentId");
-        List<String> vmIds = new ArrayList<>();
-        VmInfoDAO dao = getDao(storage);
-        List<String> allAgents = procAgents.getAll();
-        long countBefore = dao.getCount();
-        int totalItems = item.getNumber() * allAgents.size();
-        // populate VM records per agentID
-        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
-        int currVal = 0;
-        for (String agentId: allAgents) {
-            for (int i = 0; i < item.getNumber(); i++) {
-                VmInfo info = new VmInfo();
-                info.setAgentId(agentId);
-                info.setJavaCommandLine("java DataPopulatorProducedVM --vm " + i  + " --version");
-                info.setJavaHome("/opt/foo/bar/java-1.8.0-openjdk/jre");
-                info.setJavaVersion("1.8.0");
-                info.setMainClass("com.redhat.thermostat.TestDataPopulator");
-                info.setLoadedNativeLibraries(new String[] { "glibc.so", "something.so" });
-                info.setProperties(getFakeJavaProperties(i, agentId));
-                StartStopTimeStamp ts = getFakeStartStopTimeStamp(i, aliveItems);
-                info.setStartTimeStamp(ts.startTS);
-                info.setStopTimeStamp(ts.stopTS);
-                info.setUsername("vm-user-" + i);
-                info.setUid(i);
-                info.setVmArguments("-XX:+UseG1Gc");
-                String vmId = UUID.randomUUID().toString();
-                info.setVmId(vmId);
-                vmIds.add(vmId);
-                info.setVmInfo("OpenJDK 8");
-                info.setVmPid(34 + i);
-                info.setVmVersion("hs 25");
-                info.setVmName("Hotspot");
-                dao.putVmInfo(info);
-                reportProgress(item, currVal);
-                currVal++;
-            }
-        }
-        doWaitUntilCount(dao, countBefore + totalItems);
-        relState.addProcessedRecords("vmId", new ProcessedRecords<>(vmIds));
-        return relState;
-    }
-    
-    private StartStopTimeStamp getFakeStartStopTimeStamp(int num, int aliveItems) {
-        long currTime = System.currentTimeMillis();
-        if (num < aliveItems) {
-            // create alive timestamp pair
-            long startTs = Long.MAX_VALUE;
-            long stopTs = currTime;
-            return new StartStopTimeStamp(startTs, stopTs);
-        }
-        // create dead timestamp pair
-        return new StartStopTimeStamp(currTime - (1000 + num), currTime - (500 + num));
-    }
-
-    private Map<String, String> getFakeJavaProperties(int i, String agentId) {
-        Map<String, String> props = new HashMap<String, String>();
-        props.put("foo", "bar");
-        props.put("vm#", Integer.toString(i));
-        props.put("agentId", agentId);
-        return props;
-    }
-
-    // hook for testing
-    private VmInfoDAO getDao(Storage storage) {
-        if (dao == null) {
-            return new VmInfoDAOImpl(storage);
-        } else {
-            return dao;
-        }
-    }
-
-    @Override
-    public String getHandledCollection() {
-        return VmInfoDAO.vmInfoCategory.getName();
-    }
-    
-    private static class StartStopTimeStamp {
-        private final long startTS;
-        private final long stopTS;
-        
-        private StartStopTimeStamp(long start, long stop) {
-            this.startTS = start;
-            this.stopTS = stop;
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/AgentInfoPopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.internal.dao.AgentInfoDAOImpl;
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+public class AgentInfoPopulator extends BasePopulator {
+    
+    private final AgentInfoDAO dao;
+    
+    public AgentInfoPopulator() {
+        this(null);
+    }
+    
+    // for testing
+    AgentInfoPopulator(AgentInfoDAO dao) {
+        this.dao = dao;
+    }
+
+    @Override
+    public SharedState addPojos(Storage storage, ConfigItem item, SharedState state) {
+        // Default to all alive, if unset
+        int aliveItems = item.getAliveItems() == ConfigItem.UNSET ? item.getNumber() : item.getAliveItems();
+        List<String> processedRecords = new ArrayList<>(); 
+        AgentInfoDAO agentInfoDao = getDao(storage);
+        long currentTime = System.currentTimeMillis();
+        long countBefore = agentInfoDao.getCount();
+        System.out.println("Populating "+ item.getNumber() + " " + item.getName() + " records");
+        for (int i = 0; i < item.getNumber(); i++) {
+            AgentInformation agentInfo = new AgentInformation();
+            String agentId = UUID.randomUUID().toString();
+            processedRecords.add(agentId);
+            agentInfo.setAgentId(agentId);
+            agentInfo.setAlive(getAliveValue(aliveItems, i));
+            agentInfo.setConfigListenAddress(String.format("127.0.0.1:%d", i));
+            agentInfo.setStartTime(currentTime);
+            agentInfoDao.addAgentInformation(agentInfo);
+            reportProgress(item, i);
+        }
+        doWaitUntilCount(agentInfoDao, countBefore + item.getNumber());
+        state.addProcessedRecords("agentId", new ProcessedRecords<>(processedRecords));
+        // FIXME: Why does HostInfoDAOImpl need AgentInfoDAO? See HostInfoPopulator
+        state.addProperty("agent-info-dao", agentInfoDao);
+        return state;
+    }
+
+    private boolean getAliveValue(int aliveItems, int i) {
+        if (i < aliveItems) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private AgentInfoDAO getDao(Storage storage) {
+        if (dao == null) {
+            return new AgentInfoDAOImpl(storage);
+        } else {
+            return dao;
+        }
+    }
+
+    @Override
+    public String getHandledCollection() {
+        return AgentInfoDAO.CATEGORY.getName();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/BasePopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import com.redhat.thermostat.dev.populator.CollectionPopulator;
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.storage.core.Countable;
+
+public abstract class BasePopulator implements CollectionPopulator {
+
+    protected void doWaitUntilCount(Countable countable, long expectedCount) {
+        System.out.print("Waiting for storage items to arrive at backend...");
+        long currCount = countable.getCount();
+        while (currCount != expectedCount) {
+            try {
+                Thread.sleep(200);
+                System.out.print("."); // report some progress
+            } catch (InterruptedException e) {
+                // ignore
+            }
+            currCount = countable.getCount();
+        }
+        System.out.println(" Done.");
+    }
+    
+    protected void reportProgress(ConfigItem item, int currCount) {
+        if (currCount > 0 && currCount % 10 == 0) {
+            System.out.println("Submitted " + currCount + " " + item.getName() + " records to storage.");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/HostInfoPopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import java.util.List;
+import java.util.Objects;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.internal.dao.HostInfoDAOImpl;
+import com.redhat.thermostat.storage.model.HostInfo;
+
+public class HostInfoPopulator extends BasePopulator {
+    
+    private static final String[] HOSTS_FORMAT = new String[] {
+            "vm-host-%06d", "prometheus-%06d", "saturn-%06d"
+    };
+    private static final String DOMAIN_FORMAT = "domain-%06d";
+    private static final String HOSTNAME_SUFFIX = "example.com";
+    private static final long MB = 1024 * 1024;
+    private final HostInfoDAO dao;
+    
+    public HostInfoPopulator() {
+        this(null);
+    }
+
+    HostInfoPopulator(HostInfoDAO dao) {
+        this.dao = dao;
+    }
+
+    @Override
+    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
+        // FIXME: We need the same instance of the AgentInfoDAO due to the register-category quirk.
+        AgentInfoDAO agentInfoDAO = Objects.requireNonNull((AgentInfoDAO)relState.getProperty("agent-info-dao"));
+        HostInfoDAO hostDAO = getDao(storage, agentInfoDAO);
+        List<String> agentIds = relState.getProcessedRecordsFor("agentId").getAll();
+        int totalItems = agentIds.size();
+        int currCount = 0;
+        long countBefore = hostDAO.getCount();
+        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
+        // There is a 1-to-1 correspondance between host-info and agents
+        for (int i = agentIds.size() - 1; i >= 0; i--) {
+            String agentId = agentIds.get(i);
+            HostInfo info = new HostInfo();
+            info.setAgentId(agentId);
+            info.setCpuCount(getRandomInt(50 + i));
+            info.setCpuModel("x86_64, data populator");
+            info.setHostname(getRandomHostName(i));
+            info.setOsKernel("4.2.3 data populator");
+            info.setOsName("Linux Random Flavor");
+            int memory = getRandomInt(500 + i);
+            info.setTotalMemory(memory * MB);
+            hostDAO.putHostInfo(info);
+            reportProgress(item, currCount);
+            currCount++;
+        }
+        doWaitUntilCount(hostDAO, countBefore + totalItems);
+        return relState;
+    }
+
+    private String getRandomHostName(int i) {
+        int index = getRandomInt(HOSTS_FORMAT.length);
+        String hostFormat = HOSTS_FORMAT[index] + "." + DOMAIN_FORMAT + "." + HOSTNAME_SUFFIX;
+        return String.format(hostFormat, i, i);
+    }
+
+    private int getRandomInt(int i) {
+        return (int)(Math.random() * i);
+    }
+
+    @Override
+    public String getHandledCollection() {
+        return HostInfoDAO.hostInfoCategory.getName();
+    }
+    
+    // hook for testing
+    private HostInfoDAO getDao(Storage storage, AgentInfoDAO agentInfoDAO) {
+        if (dao == null) {
+            return new HostInfoDAOImpl(storage, agentInfoDAO);
+        } else {
+            return dao;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/NetworkInfoPopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import java.util.List;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.CategoryAdapter;
+import com.redhat.thermostat.storage.core.Countable;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.NetworkInterfaceInfoDAO;
+import com.redhat.thermostat.storage.internal.dao.NetworkInterfaceInfoDAOImpl;
+import com.redhat.thermostat.storage.model.AggregateCount;
+import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
+
+public class NetworkInfoPopulator extends BasePopulator {
+    
+    static final String[] IPV4_FORMATS = new String[] {
+            "192.168.0.%d", "10.33.4.%d", "89.15.93.%d" 
+    };
+    static final String[] IPV6_FORMATS = new String[] {
+            "e8:b1:fc:d2:e3:%s%%%s", "fe80::56ee:75ff:fe35:%s%%%s",
+            "0:0:0:0:0:0:0:%s%%%s"
+    };
+    private final String[] IFACE_NAMES = new String[] {
+            "lo", "enp0s25", "tun0", "virbr0", "wlp4s0"
+    };
+    private static int IPv6Mod = Integer.parseInt("ffff", 16);
+    private static int IPv4Mod = 255;
+    
+    private final NetworkInterfaceDAOCountable dao;
+    
+    public NetworkInfoPopulator() {
+        this(null);
+    }
+    
+    NetworkInfoPopulator(NetworkInterfaceDAOCountable dao) {
+        this.dao = dao;
+    }
+
+    @Override
+    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
+        ProcessedRecords<String> procAgents = relState.getProcessedRecordsFor("agentId");
+        NetworkInterfaceDAOCountable dao = getDao(storage);
+        List<String> allAgents = procAgents.getAll();
+        long countBefore = dao.getCount();
+        int totalItems = item.getNumber() * allAgents.size();
+        // populate network info records per agentID
+        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
+        int currVal = 0;
+        for (String agentId: allAgents) {
+            for (int i = 0; i < item.getNumber(); i++) {
+                NetworkInterfaceInfo info = new NetworkInterfaceInfo();
+                info.setAgentId(agentId);
+                String name = getRandomInterfaceName(i);
+                info.setInterfaceName(name);
+                info.setIp6Addr(getRandomIpv6Address(i, name));
+                info.setIp4Addr(getRandomIpv4Addrees(i));
+                dao.putNetworkInterfaceInfo(info);
+                reportProgress(item, currVal);
+                currVal++;
+            }
+        }
+        doWaitUntilCount(dao, countBefore + totalItems);
+        return relState;
+    }
+
+    private String getRandomIpv4Addrees(int i) {
+        int idx = getRandomInt(IPV4_FORMATS.length);
+        return String.format(IPV4_FORMATS[idx], getIpv4Octet(i));
+    }
+
+    private int getRandomInt(int upperBound) {
+        return (int)(Math.random() * upperBound);
+    }
+
+    private String getRandomIpv6Address(int i, String name) {
+        int idx = getRandomInt(IPV6_FORMATS.length);
+        return String.format(IPV6_FORMATS[idx], getIpv6Hextet(i), name);
+    }
+    
+    // package-private for testing
+    String getIpv6Hextet(int i) {
+        int remainder = i % IPv6Mod;
+        if (remainder == 0) {
+            // 0 isn't really an IP, make it 1 instead.
+            remainder = 1;
+        }
+        return Integer.toHexString(remainder);
+    }
+    
+    // package-private for testing
+    int getIpv4Octet(int i) {
+        int remainder = i % IPv4Mod;
+        if (remainder == 0) {
+            // 0 is network address, switch to IP
+            remainder = 1;
+        }
+        return remainder;
+    }
+
+    private String getRandomInterfaceName(int i) {
+        int idx = getRandomInt(IFACE_NAMES.length);
+        return IFACE_NAMES[idx] + i;
+    }
+
+    private NetworkInterfaceDAOCountable getDao(Storage storage) {
+        if (this.dao == null) {
+            return new NetworkInterfaceDAOCountable(storage);
+        }
+        return dao;
+    }
+
+    @Override
+    public String getHandledCollection() {
+        return NetworkInterfaceInfoDAO.networkInfoCategory.getName();
+    }
+    
+    static class NetworkInterfaceDAOCountable extends NetworkInterfaceInfoDAOImpl implements Countable {
+
+        private final Category<AggregateCount> aggregateCategory;
+        private final Storage storage;
+        
+        NetworkInterfaceDAOCountable(Storage storage) {
+            super(storage);
+            CategoryAdapter<NetworkInterfaceInfo, AggregateCount> adapter = new CategoryAdapter<>(networkInfoCategory);
+            aggregateCategory = adapter.getAdapted(AggregateCount.class);
+            storage.registerCategory(aggregateCategory);
+            this.storage = storage;
+        }
+
+        @Override
+        public long getCount() {
+            String descriptor = "QUERY-COUNT " + networkInfoCategory.getName();
+            try {
+                PreparedStatement<AggregateCount> stmt = storage.prepareStatement(new StatementDescriptor<>(aggregateCategory, descriptor));
+                Cursor<AggregateCount> cursor = stmt.executeQuery();
+                AggregateCount count = cursor.next();
+                return count.getCount();
+            } catch (DescriptorParsingException | StatementExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/ThreadPopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import com.redhat.thermostat.common.Pair;
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.CategoryAdapter;
+import com.redhat.thermostat.storage.core.Countable;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.AggregateCount;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+import com.redhat.thermostat.thread.dao.internal.ThreadDaoImpl;
+import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
+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;
+
+public class ThreadPopulator extends BasePopulator {
+
+    /**
+     * Package-private and static for testing.
+     */
+    static final int NUM_SAMPLES = 10;
+
+    public final String SESSION = "session";
+    private final int NUM_THREADS = 3;
+    private final long SEED = 5;
+    Random generator = new Random(SEED);
+    private static final String[] THREAD_NAMES = new String[] {
+            "Spencer", "Sheldon", "Alice", "Bob", "Julie", "Phoebe", "Alpha", "Beta", "Gamma",
+            "Theta", "Zeta",
+    };
+    static final String DEADLOCK_DESC_FORMAT = "" +
+            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7 owned by \"%s\" Id=%d\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
+            "\t...\n\n" +
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
+            "\n\n" +
+            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e owned by \"%s\" Id=%d\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n" + 
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" + 
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
+            "\t...\n\n" + 
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
+            "\n\n" +
+            "\"%s\" Id=%d WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2 owned by \"%s\" Id=%d\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" + 
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" + 
+            "\t...\n\n" +
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n\n\n";
+            
+    
+    private ThreadDaoCountable dao;
+
+    public ThreadPopulator() {
+        this(null);
+    }
+
+    ThreadPopulator(ThreadDaoCountable dao) {
+        this.dao = dao;
+    }
+
+    // Testing hook
+    private void createDao(Storage storage) {
+        if (dao == null) {
+            dao = new ThreadDaoCountable(storage);
+        }
+    }
+
+    @Override
+    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
+        createDao(storage);
+        // creates records per agent *and* vm
+        List<String> agentIds = relState.getProcessedRecordsFor("agentId").getAll();
+        List<String> vmIds = relState.getProcessedRecordsFor("vmId").getAll();
+        int perVmNumber = item.getNumber();
+        int totalCount = perVmNumber * agentIds.size() * vmIds.size();
+        int currCount = 0;
+        long countBefore = dao.getCount();
+        System.out.println("Populating "+ totalCount  + " " + item.getName() + " records");
+        long currTime = System.currentTimeMillis();
+        for (String agentId: agentIds) {
+            for (String vmId: vmIds) {
+                for (int i = 0; i < perVmNumber; i++) {
+                    ThreadSummary summary = new ThreadSummary();
+                    summary.setAgentId(agentId);
+                    summary.setVmId(vmId);
+                    int liveThreads = generator.nextInt(NUM_THREADS);
+                    summary.setCurrentLiveThreads(liveThreads);
+                    int daemonThreads = NUM_THREADS - liveThreads;
+                    summary.setCurrentDaemonThreads(daemonThreads);
+                    long timeStamp = currTime + i * 1000;
+                    summary.setTimeStamp(timeStamp);
+                    dao.saveSummary(summary);
+
+                    ThreadSession session = new ThreadSession();
+                    session.setSession(SESSION);
+                    session.setTimeStamp(timeStamp);
+                    session.setAgentId(agentId);
+                    session.setVmId(vmId);
+                    dao.saveSession(session);
+
+                    final Thread.State[] states = Thread.State.values();
+                    String state = states[generator.nextInt(states.length)].toString();
+                    String name = THREAD_NAMES[generator.nextInt(THREAD_NAMES.length)];
+
+                    for(int j = 0; j < NUM_SAMPLES; j++) {
+                        ThreadState threadState = new ThreadState();
+                        threadState.setTimeStamp(currTime + j * 1000);
+                        threadState.setVmId(vmId);
+                        threadState.setAgentId(agentId);
+                        threadState.setState(state);
+                        threadState.setId(i);
+                        threadState.setName(name);
+                        threadState.setSession(SESSION);
+                        dao.addThreadState(threadState);
+                    }
+
+                    ThreadHarvestingStatus status = new ThreadHarvestingStatus(agentId);
+                    status.setVmId(vmId);
+                    status.setTimeStamp(timeStamp);
+                    status.setHarvesting(generator.nextBoolean());
+                    dao.saveHarvestingStatus(status);
+
+                    VmDeadLockData data = new VmDeadLockData();
+                    data.setAgentId(agentId);
+                    data.setVmId(vmId);
+                    data.setTimeStamp(currTime + i * 10);
+                    data.setDeadLockDescription(getDeadlockedDescription(currCount));
+                    dao.saveDeadLockStatus(data);
+                    currCount++;
+                    reportProgress(item, currCount);
+                }
+            }
+        }
+        doWaitUntilCount(dao, countBefore + totalCount);
+        return relState;
+    }
+
+    private String getDeadlockedDescription(int currCount) {
+        return String.format(DEADLOCK_DESC_FORMAT, getFormatStringArgs(currCount));
+    }
+
+    Object[] getFormatStringArgs(int currCount) {
+        List<Pair<String, Integer>> nameIdMapping = new ArrayList<>();
+        int[] randomIds = getIdValues(currCount, NUM_THREADS);
+        int randomEntry = getRandomInt(THREAD_NAMES.length);
+        for (int i = 0, idx; i < NUM_THREADS; i++) {
+            idx = (randomEntry + i) % THREAD_NAMES.length;
+            String name = THREAD_NAMES[idx];
+            int tid = randomIds[i];
+            nameIdMapping.add(new Pair<>(name, tid));
+        }
+        Object[] formatArgs = new Object[NUM_THREADS * 2 * 2];
+        formatArgs[0] = nameIdMapping.get(0).getFirst();
+        formatArgs[1] = nameIdMapping.get(0).getSecond();
+        for (int i = 2, j = 1; i < formatArgs.length - 2; i+= 4, j++) {
+            formatArgs[i] = nameIdMapping.get(j).getFirst();
+            formatArgs[i + 1] = nameIdMapping.get(j).getSecond();
+            formatArgs[i + 2] = nameIdMapping.get(j).getFirst();
+            formatArgs[i + 3] = nameIdMapping.get(j).getSecond();
+        }
+        formatArgs[formatArgs.length - 2] = nameIdMapping.get(0).getFirst();
+        formatArgs[formatArgs.length - 1] = nameIdMapping.get(0).getSecond();
+        return formatArgs;
+    }
+
+    private int getRandomInt(int len) {
+        return (int)(Math.random() * len);
+    }
+
+    private int[] getIdValues(int currCount, int length) {
+        int[] values = new int[length];
+        for (int i = 0; i < values.length; i++) {
+            values[i] = currCount + i;
+        }
+        return values;
+    }
+
+    @Override
+    public String getHandledCollection() {
+        return ThreadDao.THREAD_HARVESTING_STATUS.getName();
+    }
+
+    static class ThreadDaoCountable extends ThreadDaoImpl implements Countable {
+
+        private final Category<AggregateCount> aggregateCategory;
+        private final Storage storage;
+
+        public ThreadDaoCountable(Storage storage) {
+            super(storage);
+            CategoryAdapter<VmDeadLockData, AggregateCount> adapter = new CategoryAdapter<>(DEADLOCK_INFO);
+            aggregateCategory = adapter.getAdapted(AggregateCount.class);
+            storage.registerCategory(aggregateCategory);
+            this.storage = storage;
+        }
+
+        @Override
+        public long getCount() {
+            String descriptor = "QUERY-COUNT " + DEADLOCK_INFO.getName();
+            try {
+                PreparedStatement<AggregateCount> stmt = storage.prepareStatement(new StatementDescriptor<>(aggregateCategory, descriptor));
+                Cursor<AggregateCount> cursor = stmt.executeQuery();
+                AggregateCount count = cursor.next();
+                return count.getCount();
+            } catch (DescriptorParsingException | StatementExecutionException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/main/java/com/redhat/thermostat/dev/populator/internal/VmInfoPopulator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.internal.dao.VmInfoDAOImpl;
+import com.redhat.thermostat.storage.model.VmInfo;
+
+public class VmInfoPopulator extends BasePopulator {
+
+    private final VmInfoDAO dao;
+    
+    public VmInfoPopulator() {
+        this(null);
+    }
+    
+    VmInfoPopulator(VmInfoDAO dao) {
+        this.dao = dao;
+    }
+
+    @Override
+    public SharedState addPojos(Storage storage, ConfigItem item, SharedState relState) {
+        // Default to all alive, if unset
+        int aliveItems = item.getAliveItems() == ConfigItem.UNSET ? item.getNumber() : item.getAliveItems();
+        ProcessedRecords<String> procAgents = relState.getProcessedRecordsFor("agentId");
+        List<String> vmIds = new ArrayList<>();
+        VmInfoDAO dao = getDao(storage);
+        List<String> allAgents = procAgents.getAll();
+        long countBefore = dao.getCount();
+        int totalItems = item.getNumber() * allAgents.size();
+        // populate VM records per agentID
+        System.out.println("Populating "+ totalItems  + " " + item.getName() + " records");
+        int currVal = 0;
+        for (String agentId: allAgents) {
+            for (int i = 0; i < item.getNumber(); i++) {
+                VmInfo info = new VmInfo();
+                info.setAgentId(agentId);
+                info.setJavaCommandLine("java DataPopulatorProducedVM --vm " + i  + " --version");
+                info.setJavaHome("/opt/foo/bar/java-1.8.0-openjdk/jre");
+                info.setJavaVersion("1.8.0");
+                info.setMainClass("com.redhat.thermostat.TestDataPopulator");
+                info.setLoadedNativeLibraries(new String[] { "glibc.so", "something.so" });
+                info.setProperties(getFakeJavaProperties(i, agentId));
+                StartStopTimeStamp ts = getFakeStartStopTimeStamp(i, aliveItems);
+                info.setStartTimeStamp(ts.startTS);
+                info.setStopTimeStamp(ts.stopTS);
+                info.setUsername("vm-user-" + i);
+                info.setUid(i);
+                info.setVmArguments("-XX:+UseG1Gc");
+                String vmId = UUID.randomUUID().toString();
+                info.setVmId(vmId);
+                vmIds.add(vmId);
+                info.setVmInfo("OpenJDK 8");
+                info.setVmPid(34 + i);
+                info.setVmVersion("hs 25");
+                info.setVmName("Hotspot");
+                dao.putVmInfo(info);
+                reportProgress(item, currVal);
+                currVal++;
+            }
+        }
+        doWaitUntilCount(dao, countBefore + totalItems);
+        relState.addProcessedRecords("vmId", new ProcessedRecords<>(vmIds));
+        return relState;
+    }
+    
+    private StartStopTimeStamp getFakeStartStopTimeStamp(int num, int aliveItems) {
+        long currTime = System.currentTimeMillis();
+        if (num < aliveItems) {
+            // create alive timestamp pair
+            long startTs = Long.MAX_VALUE;
+            long stopTs = currTime;
+            return new StartStopTimeStamp(startTs, stopTs);
+        }
+        // create dead timestamp pair
+        return new StartStopTimeStamp(currTime - (1000 + num), currTime - (500 + num));
+    }
+
+    private Map<String, String> getFakeJavaProperties(int i, String agentId) {
+        Map<String, String> props = new HashMap<String, String>();
+        props.put("foo", "bar");
+        props.put("vm#", Integer.toString(i));
+        props.put("agentId", agentId);
+        return props;
+    }
+
+    // hook for testing
+    private VmInfoDAO getDao(Storage storage) {
+        if (dao == null) {
+            return new VmInfoDAOImpl(storage);
+        } else {
+            return dao;
+        }
+    }
+
+    @Override
+    public String getHandledCollection() {
+        return VmInfoDAO.vmInfoCategory.getName();
+    }
+    
+    private static class StartStopTimeStamp {
+        private final long startTS;
+        private final long stopTS;
+        
+        private StartStopTimeStamp(long start, long stop) {
+            this.startTS = start;
+            this.stopTS = stop;
+        }
+    }
+}
--- a/dev/data-populator/src/main/resources/META-INF/services/com.redhat.thermostat.dev.populator.CollectionPopulator	Wed Apr 06 14:52:26 2016 +0200
+++ b/dev/data-populator/src/main/resources/META-INF/services/com.redhat.thermostat.dev.populator.CollectionPopulator	Fri Apr 08 13:18:48 2016 +0200
@@ -1,5 +1,5 @@
-com.redhat.thermostat.dev.populator.impl.ThreadPopulator
-com.redhat.thermostat.dev.populator.impl.AgentInfoPopulator
-com.redhat.thermostat.dev.populator.impl.VmInfoPopulator
-com.redhat.thermostat.dev.populator.impl.HostInfoPopulator
-com.redhat.thermostat.dev.populator.impl.NetworkInfoPopulator
+com.redhat.thermostat.dev.populator.internal.ThreadPopulator
+com.redhat.thermostat.dev.populator.internal.AgentInfoPopulator
+com.redhat.thermostat.dev.populator.internal.VmInfoPopulator
+com.redhat.thermostat.dev.populator.internal.HostInfoPopulator
+com.redhat.thermostat.dev.populator.internal.NetworkInfoPopulator
--- a/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/AgentInfoPopulatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-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.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.model.AgentInformation;
-
-public class AgentInfoPopulatorTest {
-
-    @Test
-    public void canHandleAgentInfoCollection() {
-        AgentInfoPopulator putter = new AgentInfoPopulator();
-        assertEquals("agent-config", putter.getHandledCollection());
-    }
-    
-    @Test
-    public void canAddPojos() {
-        int totalRecords = 23;
-        AgentInfoDAO dao = mock(AgentInfoDAO.class);
-        // Initially return 0, then return the expected count
-        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
-        AgentInfoPopulator putter = new AgentInfoPopulator(dao);
-        ConfigItem item = new ConfigItem(totalRecords, 3, "agent-config");
-        SharedState state = new SharedState();
-        state = putter.addPojos(mock(Storage.class), item, state);
-        ArgumentCaptor<AgentInformation> agentInfoCaptor = ArgumentCaptor.forClass(AgentInformation.class);
-        verify(dao, times(23)).addAgentInformation(agentInfoCaptor.capture());
-        List<AgentInformation> agentInfos = agentInfoCaptor.getAllValues();
-        List<AgentInformation> filtered = getAliveAgentInfos(agentInfos);
-        assertEquals(3, filtered.size());
-        ProcessedRecords<String> recs = state.getProcessedRecordsFor("agentId");
-        assertEquals(23, recs.getAll().size());
-        ProcessedRecords<String> notExisting = state.getProcessedRecordsFor("foo-bar");
-        assertNull(notExisting);
-        // FIXME: HostInfo populator needs AgentInfoDAO so we tag the dao
-        //        to the shared state. This test asserts that it does so.
-        AgentInfoDAO stateDAO = (AgentInfoDAO)state.getProperty("agent-info-dao");
-        assertNotNull(stateDAO);
-        assertSame(stateDAO, dao);
-    }
-
-    private List<AgentInformation> getAliveAgentInfos(List<AgentInformation> agentInfos) {
-        List<AgentInformation> aliveInfos = new ArrayList<>();
-        for (AgentInformation info: agentInfos) {
-            if (info.isAlive()) {
-                aliveInfos.add(info);
-            }
-        }
-        return aliveInfos;
-    }
-}
--- a/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/HostInfoPopulatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,98 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import static org.junit.Assert.assertEquals;
-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;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.storage.dao.HostInfoDAO;
-import com.redhat.thermostat.storage.model.HostInfo;
-
-public class HostInfoPopulatorTest {
-
-    @Test
-    public void handlesCorrectCollection() {
-        HostInfoPopulator populator = new HostInfoPopulator();
-        assertEquals("host-info", populator.getHandledCollection());
-    }
-    
-    @Test
-    public void canPopulateToStorage() {
-        String[] agents = new String[] {
-                "host-agent1", "host-agent2", "host-agent3", "host-agent4"
-        };
-        List<String> agentIds = Arrays.asList(agents);
-        SharedState state = mock(SharedState.class);
-        @SuppressWarnings("unchecked")
-        ProcessedRecords<String> procRecs = mock(ProcessedRecords.class);
-        when(procRecs.getAll()).thenReturn(agentIds);
-        when(state.getProcessedRecordsFor(eq("agentId"))).thenReturn(procRecs);
-        when(state.getProperty(eq("agent-info-dao"))).thenReturn(mock(AgentInfoDAO.class));
-        int totalRecords = agents.length;
-        HostInfoDAO dao = mock(HostInfoDAO.class);
-        // Initially return 0, then return the expected count
-        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
-        
-        ConfigItem config = new ConfigItem(1, ConfigItem.UNSET, "host-info");
-        HostInfoPopulator populator = new HostInfoPopulator(dao);
-        populator.addPojos(mock(Storage.class), config, state);
-        ArgumentCaptor<HostInfo> captor = ArgumentCaptor.forClass(HostInfo.class);
-        verify(dao, times(totalRecords)).putHostInfo(captor.capture());
-        List<HostInfo> values = captor.getAllValues();
-        // ensure no memory values are negative
-        for (HostInfo info: values) {
-            if (info.getTotalMemory() < 0) {
-                throw new AssertionError("Invalid memory value: " + info.getTotalMemory() + " < 0");
-            }
-        }
-    }
-}
--- a/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/NetworkInfoPopulatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import static org.junit.Assert.assertEquals;
-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.when;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.dev.populator.impl.NetworkInfoPopulator.NetworkInterfaceDAOCountable;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
-
-public class NetworkInfoPopulatorTest {
-
-    @Test
-    public void canHandleCorrectCollection() {
-        NetworkInfoPopulator populator = new NetworkInfoPopulator();
-        assertEquals("network-info", populator.getHandledCollection());
-    }
-    
-    @Test
-    public void testPopulation() {
-        NetworkInterfaceDAOCountable dao = mock(NetworkInterfaceDAOCountable.class);
-        NetworkInfoPopulator populator = new NetworkInfoPopulator(dao);
-        
-        String[] agentIds = new String[] {
-                "testAgent1", "testAgent2", "testAgent3", "testAgent4"
-        };
-        int perAgentItems = 100;
-        int totalRecords = perAgentItems * agentIds.length;
-        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
-        List<String> agents = Arrays.asList(agentIds);
-        SharedState state = new SharedState();
-        state.addProcessedRecords("agentId", new ProcessedRecords<>(agents));
-        ConfigItem config = new ConfigItem(perAgentItems, ConfigItem.UNSET, "network-info");
-        populator.addPojos(mock(Storage.class), config, state);
-        ArgumentCaptor<NetworkInterfaceInfo> captor = ArgumentCaptor.forClass(NetworkInterfaceInfo.class);
-        verify(dao, times(totalRecords)).putNetworkInterfaceInfo(captor.capture());
-        List<NetworkInterfaceInfo> list = captor.getAllValues();
-        // expected agentId + iface name to be unique (since REPLACE otherwise replaces some random values)
-        Set<String> uniqueSet = new HashSet<>();
-        for (NetworkInterfaceInfo info: list) {
-            String agentId = info.getAgentId();
-            String ifaceName = info.getInterfaceName();
-            String key = agentId + ifaceName;
-            if (uniqueSet.contains(key)) {
-                throw new AssertionError("Expected interface names to be unique per agent");
-            } else {
-                uniqueSet.add(key);
-            }
-        }
-    }
-    
-    @Test
-    public void iPv6HextetRollOver() {
-        String ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("fffe", 16));
-        assertEquals("fffe", ipv6);
-        ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("ffff", 16));
-        assertEquals("1", ipv6);
-        ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("eee1", 16));
-        assertEquals("eee1", ipv6);
-    }
-    
-    @Test
-    public void iPv4OctetsRollOver() {
-        int octet = new NetworkInfoPopulator().getIpv4Octet(255);
-        assertEquals(1, octet);
-        octet = new NetworkInfoPopulator().getIpv4Octet(256);
-        assertEquals(1, octet);
-        octet = new NetworkInfoPopulator().getIpv4Octet(114);
-        assertEquals(114, octet);
-    }
-    
-    @Test
-    public void stringFormatIpv6Template() {
-        String formattedString = String.format(NetworkInfoPopulator.IPV6_FORMATS[1], "ff", "scope");
-        assertEquals("fe80::56ee:75ff:fe35:ff%scope", formattedString);
-    }
-}
--- a/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/ThreadPopulatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,289 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.dev.populator.impl.ThreadPopulator.ThreadDaoCountable;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.BasePojo;
-import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-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;
-
-public class ThreadPopulatorTest {
-    
-    private static final String EXPECTED_DESCRIPTION = "" +
-            "\"Mallory\" Id=12 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7 owned by \"Alice\" Id=10\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
-            "\t...\n\n" +
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2" +
-            "\n\n\n" +
-            "\"Alice\" Id=10 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e owned by \"Bob\" Id=11\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n" + 
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" + 
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
-            "\t...\n\n" + 
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
-            "\n\n" +
-            "\"Bob\" Id=11 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2 owned by \"Mallory\" Id=12\n" +
-            "\tat sun.misc.Unsafe.park(Native Method)\n" +
-            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
-            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" + 
-            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" + 
-            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
-            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
-            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" + 
-            "\t...\n\n" +
-            "\tNumber of locked synchronizers = 1\n" +
-            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n\n\n";
-
-    private static final String[] AGENTS = new String[] {
-            "fooAgent1", "fooBarAgent", "testAgent", "someAgent"
-    };
-    private static final String[] VMS = new String[] {
-            "vm1", "vm2", "vm3", "vm4", "vm5"
-    };
-
-    private ThreadDaoCountable countableDao;
-    
-    @Test
-    public void testFormatDeadlockDesc() {
-        Object[] stringFormatArgs = new Object[] {
-                "Mallory", 12,
-                "Alice", 10,
-                "Alice", 10,
-                "Bob", 11,
-                "Bob", 11,
-                "Mallory", 12
-        };
-        assertEquals(EXPECTED_DESCRIPTION, String.format(ThreadPopulator.DEADLOCK_DESC_FORMAT, stringFormatArgs));
-    }
-    
-    @Test
-    public void canGetRandomThreadNames() {
-        Object[] args = new ThreadPopulator().getFormatStringArgs(2);
-        assertEquals(12, args.length);
-        Map<Integer, Integer> tids = new HashMap<>();
-        Map<String, Integer> threadNames = new HashMap<>();
-        for (int i = 0; i < args.length; i +=2) {
-            Object arg1 = args[i];
-            Object arg2 = args[i + 1];
-            assertEquals(String.class, arg1.getClass());
-            assertEquals(Integer.class, arg2.getClass());
-            if (tids.containsKey(arg2)) {
-                Integer value = tids.get(arg2);
-                value += 1;
-                tids.put((Integer)arg2, value);
-            } else {
-                tids.put((Integer)arg2, 1);
-            }
-            if (threadNames.containsKey(arg1)) {
-                Integer value = threadNames.get(arg1);
-                value += 1;
-                threadNames.put((String)arg1, value);
-            } else {
-                threadNames.put((String)arg1, 1);
-            }
-        }
-        for (String key : threadNames.keySet()) {
-            int value = threadNames.get(key);
-            assertEquals(2, value);
-        }
-        for (Integer key : tids.keySet()) {
-            int value = tids.get(key);
-            assertEquals(2, value);
-        }
-    }
-
-    @Test
-    public void canHandleAppropriateCollection() {
-        ThreadPopulator populator = new ThreadPopulator();
-        assertEquals("vm-thread-harvesting", populator.getHandledCollection());
-    }
-
-    @Test
-    public void canAddThreadData() {
-        int perVmCount = 123;
-        int totalCount = perVmCount * AGENTS.length * VMS.length;
-        setUp(perVmCount, totalCount);
-
-        assertThreadSummaries(totalCount, perVmCount);
-        assertThreadSessions(totalCount, perVmCount);
-        assertThreadStates(totalCount, perVmCount);
-        assertThreadHarvestingStatus(totalCount, perVmCount);
-        assertDeadlockInfo(perVmCount, totalCount);
-    }
-
-    private void setUp(int perVmCount, int totalCount) {
-        ConfigItem config = new ConfigItem(perVmCount, ConfigItem.UNSET, "vm-deadlock-data");
-        countableDao = mock(ThreadDaoCountable.class);
-        when(countableDao.getCount()).thenReturn(0L).thenReturn((long)totalCount);
-        ThreadPopulator populator = new ThreadPopulator(countableDao);
-        SharedState state = mock(SharedState.class);
-        when(state.getProcessedRecordsFor("agentId")).thenReturn(new ProcessedRecords<>(Arrays.asList(AGENTS)));
-        when(state.getProcessedRecordsFor("vmId")).thenReturn(new ProcessedRecords<>(Arrays.asList(VMS)));
-        SharedState retval = populator.addPojos(mock(Storage.class), config, state);
-        assertSame(retval, state);
-    }
-
-    private void assertThreadSummaries(int totalCount, int perVmCount) {
-        ArgumentCaptor<ThreadSummary> summaryCaptor = ArgumentCaptor.forClass(ThreadSummary.class);
-        verify(countableDao, times(totalCount)).saveSummary(summaryCaptor.capture());
-        List<ThreadSummary> savedValues = summaryCaptor.getAllValues();
-        assertEquals(totalCount, savedValues.size());
-        checkInstances(savedValues, VMS.length * perVmCount);
-    }
-
-    private void assertThreadSessions(int totalCount, int perVmCount) {
-        ArgumentCaptor<ThreadSession> sessionCaptor = ArgumentCaptor.forClass(ThreadSession.class);
-        verify(countableDao, times(totalCount)).saveSession(sessionCaptor.capture());
-        List<ThreadSession> savedValues = sessionCaptor.getAllValues();
-        assertEquals(totalCount, savedValues.size());
-        checkInstances(savedValues, VMS.length * perVmCount);
-    }
-
-    private void assertThreadStates(int totalCount, int perVmCount) {
-        ArgumentCaptor<ThreadState> stateCaptor = ArgumentCaptor.forClass(ThreadState.class);
-        verify(countableDao, times(totalCount * ThreadPopulator.NUM_SAMPLES)).
-                addThreadState(stateCaptor.capture());
-        List<ThreadState> savedValues = stateCaptor.getAllValues();
-        assertEquals(totalCount * ThreadPopulator.NUM_SAMPLES, savedValues.size());
-        checkInstances(savedValues, VMS.length * perVmCount * ThreadPopulator.NUM_SAMPLES);
-    }
-
-    private void assertThreadHarvestingStatus(int totalCount, int perVmCount) {
-        ArgumentCaptor<ThreadHarvestingStatus> statusCaptor =
-                ArgumentCaptor.forClass(ThreadHarvestingStatus.class);
-        verify(countableDao, times(totalCount)).saveHarvestingStatus(statusCaptor.capture());
-        List<ThreadHarvestingStatus> savedValues = statusCaptor.getAllValues();
-        checkInstances(savedValues, VMS.length * perVmCount);
-    }
-
-    private void checkInstances(List<? extends BasePojo> savedValues, int expected) {
-        List<String> agentIds = new ArrayList<>();
-        for (int i = 0; i < savedValues.size(); i++) {
-            agentIds.add(savedValues.get(i).getAgentId());
-        }
-
-        for (String agentId : AGENTS) {
-            int numInstances = Collections.frequency(agentIds, agentId);
-            assertEquals(expected, numInstances);
-        }
-    }
-
-    private void assertDeadlockInfo(int perVmCount, int totalCount) {
-        ArgumentCaptor<VmDeadLockData> deadlockInfoCaptor = ArgumentCaptor.forClass(VmDeadLockData.class);
-        verify(countableDao, times(totalCount)).saveDeadLockStatus(deadlockInfoCaptor.capture());
-        List<VmDeadLockData> savedValues = deadlockInfoCaptor.getAllValues();
-        assertEquals(totalCount, savedValues.size());
-        List<VmDeadLockData> perVmIdAgentDeadlockData = getFilteredByVmId(savedValues, "vm3");
-        assertEquals(perVmCount * AGENTS.length, perVmIdAgentDeadlockData.size());
-        List<VmDeadLockData> perAgentIdDeadlockData = getFilteredByAgentId(savedValues, "fooAgent1");
-        verifyVmIdsDifferent(perAgentIdDeadlockData);
-    }
-
-    private List<VmDeadLockData> getFilteredByAgentId(List<VmDeadLockData> perVmDeadlockData, String agentId) {
-        List<VmDeadLockData> filteredValues = new ArrayList<>();
-        for (VmDeadLockData data: perVmDeadlockData) {
-            if (data.getAgentId().equals(agentId)) {
-                filteredValues.add(data);
-            }
-        }
-        return filteredValues;
-    }
-
-    private void verifyVmIdsDifferent(List<VmDeadLockData> perVmDeadlockData) {
-        Set<String> vmIds = new HashSet<>();
-        for (VmDeadLockData data: perVmDeadlockData) {
-            if (vmIds.contains(data.getVmId())) {
-                throw new AssertionError("Duplicate VM ID per agent. Duplicate was: " + data.getVmId());
-            }
-            vmIds.add(data.getAgentId());
-        }
-    }
-
-    private List<VmDeadLockData> getFilteredByVmId(List<VmDeadLockData> savedValues, String vmId) {
-        List<VmDeadLockData> filteredValues = new ArrayList<>();
-        for (VmDeadLockData data: savedValues) {
-            if (data.getVmId().equals(vmId)) {
-                filteredValues.add(data);
-            }
-        }
-        return filteredValues;
-    }
-}
--- a/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/impl/VmInfoPopulatorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-/*
- * Copyright 2012-2016 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.dev.populator.impl;
-
-import static org.junit.Assert.assertEquals;
-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;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.dev.populator.config.ConfigItem;
-import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
-import com.redhat.thermostat.dev.populator.dependencies.SharedState;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.storage.model.VmInfo;
-
-public class VmInfoPopulatorTest {
-
-    @Test
-    public void canHandleCorrectCollection() {
-        VmInfoPopulator populator = new VmInfoPopulator();
-        assertEquals("vm-info", populator.getHandledCollection());
-    }
-    
-    @SuppressWarnings("unchecked")
-    @Test
-    public void canPopulateData() {
-        VmInfoDAO mockDAO = mock(VmInfoDAO.class);
-        SharedState state = mock(SharedState.class);
-        ProcessedRecords<String> procRecs = mock(ProcessedRecords.class);
-        String[] agents = new String[] {
-                "foo-agent1", "foo-agent2", "bar-agent3"
-        };
-        List<String> agentIdList = Arrays.asList(agents);
-        when(procRecs.getAll()).thenReturn(agentIdList);
-        when(state.getProcessedRecordsFor(eq("agentId"))).thenReturn(procRecs);
-        int perAgentVms = 44;
-        int subsetAlive = 23;
-        ConfigItem config = new ConfigItem(perAgentVms, subsetAlive, "vm-info");
-        
-        // total records inserted will be 3 * 44. 44 records per agentId
-        int totalRecords = perAgentVms * agents.length;
-        // Initially return 0, then return the expected count
-        when(mockDAO.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
-        VmInfoPopulator populator = new VmInfoPopulator(mockDAO);
-        populator.addPojos(mock(Storage.class), config, state);
-        ArgumentCaptor<VmInfo> captor = ArgumentCaptor.forClass(VmInfo.class);
-        verify(mockDAO, times(totalRecords)).putVmInfo(captor.capture());
-        List<VmInfo> allInfos = captor.getAllValues();
-        assertEquals(totalRecords, allInfos.size());
-        List<VmInfo> aliveInfos = getAliveVmInfos(allInfos);
-        int totalAliveRecs = subsetAlive * agents.length;
-        assertEquals(totalAliveRecs, aliveInfos.size());
-        @SuppressWarnings("rawtypes")
-        ArgumentCaptor<ProcessedRecords> procsVms = ArgumentCaptor.forClass(ProcessedRecords.class);
-        verify(state).addProcessedRecords(eq("vmId"), procsVms.capture());
-        ProcessedRecords<String> processedVms = procsVms.getValue();
-        assertEquals("Excected vmIds added to shared state", totalRecords, processedVms.getAll().size());
-    }
-    
-    @SuppressWarnings("deprecation")
-    private List<VmInfo> getAliveVmInfos(List<VmInfo> allInfos) {
-        List<VmInfo> aliveInfos = new ArrayList<>();
-        for (VmInfo info: allInfos) {
-            if (info.isAlive()) {
-                aliveInfos.add(info);
-            }
-        }
-        return aliveInfos;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/AgentInfoPopulatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+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.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.model.AgentInformation;
+
+public class AgentInfoPopulatorTest {
+
+    @Test
+    public void canHandleAgentInfoCollection() {
+        AgentInfoPopulator putter = new AgentInfoPopulator();
+        assertEquals("agent-config", putter.getHandledCollection());
+    }
+    
+    @Test
+    public void canAddPojos() {
+        int totalRecords = 23;
+        AgentInfoDAO dao = mock(AgentInfoDAO.class);
+        // Initially return 0, then return the expected count
+        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
+        AgentInfoPopulator putter = new AgentInfoPopulator(dao);
+        ConfigItem item = new ConfigItem(totalRecords, 3, "agent-config");
+        SharedState state = new SharedState();
+        state = putter.addPojos(mock(Storage.class), item, state);
+        ArgumentCaptor<AgentInformation> agentInfoCaptor = ArgumentCaptor.forClass(AgentInformation.class);
+        verify(dao, times(23)).addAgentInformation(agentInfoCaptor.capture());
+        List<AgentInformation> agentInfos = agentInfoCaptor.getAllValues();
+        List<AgentInformation> filtered = getAliveAgentInfos(agentInfos);
+        assertEquals(3, filtered.size());
+        ProcessedRecords<String> recs = state.getProcessedRecordsFor("agentId");
+        assertEquals(23, recs.getAll().size());
+        ProcessedRecords<String> notExisting = state.getProcessedRecordsFor("foo-bar");
+        assertNull(notExisting);
+        // FIXME: HostInfo populator needs AgentInfoDAO so we tag the dao
+        //        to the shared state. This test asserts that it does so.
+        AgentInfoDAO stateDAO = (AgentInfoDAO)state.getProperty("agent-info-dao");
+        assertNotNull(stateDAO);
+        assertSame(stateDAO, dao);
+    }
+
+    private List<AgentInformation> getAliveAgentInfos(List<AgentInformation> agentInfos) {
+        List<AgentInformation> aliveInfos = new ArrayList<>();
+        for (AgentInformation info: agentInfos) {
+            if (info.isAlive()) {
+                aliveInfos.add(info);
+            }
+        }
+        return aliveInfos;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/HostInfoPopulatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import static org.junit.Assert.assertEquals;
+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;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.storage.dao.HostInfoDAO;
+import com.redhat.thermostat.storage.model.HostInfo;
+
+public class HostInfoPopulatorTest {
+
+    @Test
+    public void handlesCorrectCollection() {
+        HostInfoPopulator populator = new HostInfoPopulator();
+        assertEquals("host-info", populator.getHandledCollection());
+    }
+    
+    @Test
+    public void canPopulateToStorage() {
+        String[] agents = new String[] {
+                "host-agent1", "host-agent2", "host-agent3", "host-agent4"
+        };
+        List<String> agentIds = Arrays.asList(agents);
+        SharedState state = mock(SharedState.class);
+        @SuppressWarnings("unchecked")
+        ProcessedRecords<String> procRecs = mock(ProcessedRecords.class);
+        when(procRecs.getAll()).thenReturn(agentIds);
+        when(state.getProcessedRecordsFor(eq("agentId"))).thenReturn(procRecs);
+        when(state.getProperty(eq("agent-info-dao"))).thenReturn(mock(AgentInfoDAO.class));
+        int totalRecords = agents.length;
+        HostInfoDAO dao = mock(HostInfoDAO.class);
+        // Initially return 0, then return the expected count
+        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
+        
+        ConfigItem config = new ConfigItem(1, ConfigItem.UNSET, "host-info");
+        HostInfoPopulator populator = new HostInfoPopulator(dao);
+        populator.addPojos(mock(Storage.class), config, state);
+        ArgumentCaptor<HostInfo> captor = ArgumentCaptor.forClass(HostInfo.class);
+        verify(dao, times(totalRecords)).putHostInfo(captor.capture());
+        List<HostInfo> values = captor.getAllValues();
+        // ensure no memory values are negative
+        for (HostInfo info: values) {
+            if (info.getTotalMemory() < 0) {
+                throw new AssertionError("Invalid memory value: " + info.getTotalMemory() + " < 0");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/NetworkInfoPopulatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.dev.populator.internal.NetworkInfoPopulator.NetworkInterfaceDAOCountable;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.NetworkInterfaceInfo;
+
+public class NetworkInfoPopulatorTest {
+
+    @Test
+    public void canHandleCorrectCollection() {
+        NetworkInfoPopulator populator = new NetworkInfoPopulator();
+        assertEquals("network-info", populator.getHandledCollection());
+    }
+    
+    @Test
+    public void testPopulation() {
+        NetworkInterfaceDAOCountable dao = mock(NetworkInterfaceDAOCountable.class);
+        NetworkInfoPopulator populator = new NetworkInfoPopulator(dao);
+        
+        String[] agentIds = new String[] {
+                "testAgent1", "testAgent2", "testAgent3", "testAgent4"
+        };
+        int perAgentItems = 100;
+        int totalRecords = perAgentItems * agentIds.length;
+        when(dao.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
+        List<String> agents = Arrays.asList(agentIds);
+        SharedState state = new SharedState();
+        state.addProcessedRecords("agentId", new ProcessedRecords<>(agents));
+        ConfigItem config = new ConfigItem(perAgentItems, ConfigItem.UNSET, "network-info");
+        populator.addPojos(mock(Storage.class), config, state);
+        ArgumentCaptor<NetworkInterfaceInfo> captor = ArgumentCaptor.forClass(NetworkInterfaceInfo.class);
+        verify(dao, times(totalRecords)).putNetworkInterfaceInfo(captor.capture());
+        List<NetworkInterfaceInfo> list = captor.getAllValues();
+        // expected agentId + iface name to be unique (since REPLACE otherwise replaces some random values)
+        Set<String> uniqueSet = new HashSet<>();
+        for (NetworkInterfaceInfo info: list) {
+            String agentId = info.getAgentId();
+            String ifaceName = info.getInterfaceName();
+            String key = agentId + ifaceName;
+            if (uniqueSet.contains(key)) {
+                throw new AssertionError("Expected interface names to be unique per agent");
+            } else {
+                uniqueSet.add(key);
+            }
+        }
+    }
+    
+    @Test
+    public void iPv6HextetRollOver() {
+        String ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("fffe", 16));
+        assertEquals("fffe", ipv6);
+        ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("ffff", 16));
+        assertEquals("1", ipv6);
+        ipv6 = new NetworkInfoPopulator().getIpv6Hextet(Integer.parseInt("eee1", 16));
+        assertEquals("eee1", ipv6);
+    }
+    
+    @Test
+    public void iPv4OctetsRollOver() {
+        int octet = new NetworkInfoPopulator().getIpv4Octet(255);
+        assertEquals(1, octet);
+        octet = new NetworkInfoPopulator().getIpv4Octet(256);
+        assertEquals(1, octet);
+        octet = new NetworkInfoPopulator().getIpv4Octet(114);
+        assertEquals(114, octet);
+    }
+    
+    @Test
+    public void stringFormatIpv6Template() {
+        String formattedString = String.format(NetworkInfoPopulator.IPV6_FORMATS[1], "ff", "scope");
+        assertEquals("fe80::56ee:75ff:fe35:ff%scope", formattedString);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/ThreadPopulatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.dev.populator.internal.ThreadPopulator.ThreadDaoCountable;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.model.BasePojo;
+import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
+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;
+
+public class ThreadPopulatorTest {
+    
+    private static final String EXPECTED_DESCRIPTION = "" +
+            "\"Mallory\" Id=12 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7 owned by \"Alice\" Id=10\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
+            "\t...\n\n" +
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2" +
+            "\n\n\n" +
+            "\"Alice\" Id=10 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e owned by \"Bob\" Id=11\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n" + 
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" + 
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" +
+            "\t...\n\n" + 
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@52de95c7\n" +
+            "\n\n" +
+            "\"Bob\" Id=11 WAITING on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2 owned by \"Mallory\" Id=12\n" +
+            "\tat sun.misc.Unsafe.park(Native Method)\n" +
+            "\t-  waiting on java.util.concurrent.locks.ReentrantLock$NonfairSync@441634c2\n" +
+            "\tat java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)\n" +
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)\n" + 
+            "\tat java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)\n" + 
+            "\tat java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)\n" +
+            "\tat java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)\n" +
+            "\tat com.redhat.thermostat.tests.DeadLock$Philosopher.run(DeadLock.java:57)\n" + 
+            "\t...\n\n" +
+            "\tNumber of locked synchronizers = 1\n" +
+            "\t- java.util.concurrent.locks.ReentrantLock$NonfairSync@105ff84e\n\n\n";
+
+    private static final String[] AGENTS = new String[] {
+            "fooAgent1", "fooBarAgent", "testAgent", "someAgent"
+    };
+    private static final String[] VMS = new String[] {
+            "vm1", "vm2", "vm3", "vm4", "vm5"
+    };
+
+    private ThreadDaoCountable countableDao;
+    
+    @Test
+    public void testFormatDeadlockDesc() {
+        Object[] stringFormatArgs = new Object[] {
+                "Mallory", 12,
+                "Alice", 10,
+                "Alice", 10,
+                "Bob", 11,
+                "Bob", 11,
+                "Mallory", 12
+        };
+        assertEquals(EXPECTED_DESCRIPTION, String.format(ThreadPopulator.DEADLOCK_DESC_FORMAT, stringFormatArgs));
+    }
+    
+    @Test
+    public void canGetRandomThreadNames() {
+        Object[] args = new ThreadPopulator().getFormatStringArgs(2);
+        assertEquals(12, args.length);
+        Map<Integer, Integer> tids = new HashMap<>();
+        Map<String, Integer> threadNames = new HashMap<>();
+        for (int i = 0; i < args.length; i +=2) {
+            Object arg1 = args[i];
+            Object arg2 = args[i + 1];
+            assertEquals(String.class, arg1.getClass());
+            assertEquals(Integer.class, arg2.getClass());
+            if (tids.containsKey(arg2)) {
+                Integer value = tids.get(arg2);
+                value += 1;
+                tids.put((Integer)arg2, value);
+            } else {
+                tids.put((Integer)arg2, 1);
+            }
+            if (threadNames.containsKey(arg1)) {
+                Integer value = threadNames.get(arg1);
+                value += 1;
+                threadNames.put((String)arg1, value);
+            } else {
+                threadNames.put((String)arg1, 1);
+            }
+        }
+        for (String key : threadNames.keySet()) {
+            int value = threadNames.get(key);
+            assertEquals(2, value);
+        }
+        for (Integer key : tids.keySet()) {
+            int value = tids.get(key);
+            assertEquals(2, value);
+        }
+    }
+
+    @Test
+    public void canHandleAppropriateCollection() {
+        ThreadPopulator populator = new ThreadPopulator();
+        assertEquals("vm-thread-harvesting", populator.getHandledCollection());
+    }
+
+    @Test
+    public void canAddThreadData() {
+        int perVmCount = 123;
+        int totalCount = perVmCount * AGENTS.length * VMS.length;
+        setUp(perVmCount, totalCount);
+
+        assertThreadSummaries(totalCount, perVmCount);
+        assertThreadSessions(totalCount, perVmCount);
+        assertThreadStates(totalCount, perVmCount);
+        assertThreadHarvestingStatus(totalCount, perVmCount);
+        assertDeadlockInfo(perVmCount, totalCount);
+    }
+
+    private void setUp(int perVmCount, int totalCount) {
+        ConfigItem config = new ConfigItem(perVmCount, ConfigItem.UNSET, "vm-deadlock-data");
+        countableDao = mock(ThreadDaoCountable.class);
+        when(countableDao.getCount()).thenReturn(0L).thenReturn((long)totalCount);
+        ThreadPopulator populator = new ThreadPopulator(countableDao);
+        SharedState state = mock(SharedState.class);
+        when(state.getProcessedRecordsFor("agentId")).thenReturn(new ProcessedRecords<>(Arrays.asList(AGENTS)));
+        when(state.getProcessedRecordsFor("vmId")).thenReturn(new ProcessedRecords<>(Arrays.asList(VMS)));
+        SharedState retval = populator.addPojos(mock(Storage.class), config, state);
+        assertSame(retval, state);
+    }
+
+    private void assertThreadSummaries(int totalCount, int perVmCount) {
+        ArgumentCaptor<ThreadSummary> summaryCaptor = ArgumentCaptor.forClass(ThreadSummary.class);
+        verify(countableDao, times(totalCount)).saveSummary(summaryCaptor.capture());
+        List<ThreadSummary> savedValues = summaryCaptor.getAllValues();
+        assertEquals(totalCount, savedValues.size());
+        checkInstances(savedValues, VMS.length * perVmCount);
+    }
+
+    private void assertThreadSessions(int totalCount, int perVmCount) {
+        ArgumentCaptor<ThreadSession> sessionCaptor = ArgumentCaptor.forClass(ThreadSession.class);
+        verify(countableDao, times(totalCount)).saveSession(sessionCaptor.capture());
+        List<ThreadSession> savedValues = sessionCaptor.getAllValues();
+        assertEquals(totalCount, savedValues.size());
+        checkInstances(savedValues, VMS.length * perVmCount);
+    }
+
+    private void assertThreadStates(int totalCount, int perVmCount) {
+        ArgumentCaptor<ThreadState> stateCaptor = ArgumentCaptor.forClass(ThreadState.class);
+        verify(countableDao, times(totalCount * ThreadPopulator.NUM_SAMPLES)).
+                addThreadState(stateCaptor.capture());
+        List<ThreadState> savedValues = stateCaptor.getAllValues();
+        assertEquals(totalCount * ThreadPopulator.NUM_SAMPLES, savedValues.size());
+        checkInstances(savedValues, VMS.length * perVmCount * ThreadPopulator.NUM_SAMPLES);
+    }
+
+    private void assertThreadHarvestingStatus(int totalCount, int perVmCount) {
+        ArgumentCaptor<ThreadHarvestingStatus> statusCaptor =
+                ArgumentCaptor.forClass(ThreadHarvestingStatus.class);
+        verify(countableDao, times(totalCount)).saveHarvestingStatus(statusCaptor.capture());
+        List<ThreadHarvestingStatus> savedValues = statusCaptor.getAllValues();
+        checkInstances(savedValues, VMS.length * perVmCount);
+    }
+
+    private void checkInstances(List<? extends BasePojo> savedValues, int expected) {
+        List<String> agentIds = new ArrayList<>();
+        for (int i = 0; i < savedValues.size(); i++) {
+            agentIds.add(savedValues.get(i).getAgentId());
+        }
+
+        for (String agentId : AGENTS) {
+            int numInstances = Collections.frequency(agentIds, agentId);
+            assertEquals(expected, numInstances);
+        }
+    }
+
+    private void assertDeadlockInfo(int perVmCount, int totalCount) {
+        ArgumentCaptor<VmDeadLockData> deadlockInfoCaptor = ArgumentCaptor.forClass(VmDeadLockData.class);
+        verify(countableDao, times(totalCount)).saveDeadLockStatus(deadlockInfoCaptor.capture());
+        List<VmDeadLockData> savedValues = deadlockInfoCaptor.getAllValues();
+        assertEquals(totalCount, savedValues.size());
+        List<VmDeadLockData> perVmIdAgentDeadlockData = getFilteredByVmId(savedValues, "vm3");
+        assertEquals(perVmCount * AGENTS.length, perVmIdAgentDeadlockData.size());
+        List<VmDeadLockData> perAgentIdDeadlockData = getFilteredByAgentId(savedValues, "fooAgent1");
+        verifyVmIdsDifferent(perAgentIdDeadlockData);
+    }
+
+    private List<VmDeadLockData> getFilteredByAgentId(List<VmDeadLockData> perVmDeadlockData, String agentId) {
+        List<VmDeadLockData> filteredValues = new ArrayList<>();
+        for (VmDeadLockData data: perVmDeadlockData) {
+            if (data.getAgentId().equals(agentId)) {
+                filteredValues.add(data);
+            }
+        }
+        return filteredValues;
+    }
+
+    private void verifyVmIdsDifferent(List<VmDeadLockData> perVmDeadlockData) {
+        Set<String> vmIds = new HashSet<>();
+        for (VmDeadLockData data: perVmDeadlockData) {
+            if (vmIds.contains(data.getVmId())) {
+                throw new AssertionError("Duplicate VM ID per agent. Duplicate was: " + data.getVmId());
+            }
+            vmIds.add(data.getAgentId());
+        }
+    }
+
+    private List<VmDeadLockData> getFilteredByVmId(List<VmDeadLockData> savedValues, String vmId) {
+        List<VmDeadLockData> filteredValues = new ArrayList<>();
+        for (VmDeadLockData data: savedValues) {
+            if (data.getVmId().equals(vmId)) {
+                filteredValues.add(data);
+            }
+        }
+        return filteredValues;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dev/data-populator/src/test/java/com/redhat/thermostat/dev/populator/internal/VmInfoPopulatorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012-2016 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.dev.populator.internal;
+
+import static org.junit.Assert.assertEquals;
+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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.dev.populator.config.ConfigItem;
+import com.redhat.thermostat.dev.populator.dependencies.ProcessedRecords;
+import com.redhat.thermostat.dev.populator.dependencies.SharedState;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+
+public class VmInfoPopulatorTest {
+
+    @Test
+    public void canHandleCorrectCollection() {
+        VmInfoPopulator populator = new VmInfoPopulator();
+        assertEquals("vm-info", populator.getHandledCollection());
+    }
+    
+    @SuppressWarnings("unchecked")
+    @Test
+    public void canPopulateData() {
+        VmInfoDAO mockDAO = mock(VmInfoDAO.class);
+        SharedState state = mock(SharedState.class);
+        ProcessedRecords<String> procRecs = mock(ProcessedRecords.class);
+        String[] agents = new String[] {
+                "foo-agent1", "foo-agent2", "bar-agent3"
+        };
+        List<String> agentIdList = Arrays.asList(agents);
+        when(procRecs.getAll()).thenReturn(agentIdList);
+        when(state.getProcessedRecordsFor(eq("agentId"))).thenReturn(procRecs);
+        int perAgentVms = 44;
+        int subsetAlive = 23;
+        ConfigItem config = new ConfigItem(perAgentVms, subsetAlive, "vm-info");
+        
+        // total records inserted will be 3 * 44. 44 records per agentId
+        int totalRecords = perAgentVms * agents.length;
+        // Initially return 0, then return the expected count
+        when(mockDAO.getCount()).thenReturn(0L).thenReturn((long)totalRecords);
+        VmInfoPopulator populator = new VmInfoPopulator(mockDAO);
+        populator.addPojos(mock(Storage.class), config, state);
+        ArgumentCaptor<VmInfo> captor = ArgumentCaptor.forClass(VmInfo.class);
+        verify(mockDAO, times(totalRecords)).putVmInfo(captor.capture());
+        List<VmInfo> allInfos = captor.getAllValues();
+        assertEquals(totalRecords, allInfos.size());
+        List<VmInfo> aliveInfos = getAliveVmInfos(allInfos);
+        int totalAliveRecs = subsetAlive * agents.length;
+        assertEquals(totalAliveRecs, aliveInfos.size());
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<ProcessedRecords> procsVms = ArgumentCaptor.forClass(ProcessedRecords.class);
+        verify(state).addProcessedRecords(eq("vmId"), procsVms.capture());
+        ProcessedRecords<String> processedVms = procsVms.getValue();
+        assertEquals("Excected vmIds added to shared state", totalRecords, processedVms.getAll().size());
+    }
+    
+    @SuppressWarnings("deprecation")
+    private List<VmInfo> getAliveVmInfos(List<VmInfo> allInfos) {
+        List<VmInfo> aliveInfos = new ArrayList<>();
+        for (VmInfo info: allInfos) {
+            if (info.isAlive()) {
+                aliveInfos.add(info);
+            }
+        }
+        return aliveInfos;
+    }
+}
--- a/distribution/packaging/fedora/0001_shared_fix_bundle_loading.patch	Wed Apr 06 14:52:26 2016 +0200
+++ b/distribution/packaging/fedora/0001_shared_fix_bundle_loading.patch	Fri Apr 08 13:18:48 2016 +0200
@@ -180,9 +180,9 @@
        </bundles>
      </command>
    </commands>
-diff --git a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties b/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties
---- a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties
-+++ b/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties
+diff --git a/main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties b/main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties
+--- a/main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties
++++ b/main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties
 @@ -6,4 +6,8 @@
          thermostat-launcher-${project.version}.jar, \
          jline-${jline.version}.jar, \
--- a/distribution/packaging/fedora/thermostat.spec	Wed Apr 06 14:52:26 2016 +0200
+++ b/distribution/packaging/fedora/thermostat.spec	Fri Apr 08 13:18:48 2016 +0200
@@ -686,7 +686,7 @@
         -d target/classes \
            src/main/java/com/redhat/thermostat/utils/keyring/Keyring.java \
            src/main/java/com/redhat/thermostat/utils/keyring/KeyringException.java \
-           src/main/java/com/redhat/thermostat/utils/keyring/impl/KeyringImpl.java
+           src/main/java/com/redhat/thermostat/utils/keyring/internal/KeyringImpl.java
   autoreconf --install
   ./configure
   make all
--- a/keyring/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -60,7 +60,7 @@
 	        <Bundle-SymbolicName>com.redhat.thermostat.keyring</Bundle-SymbolicName>
 	        <Private-Package>
 	          com.redhat.thermostat.utils.keyring.activator,
-	          com.redhat.thermostat.utils.keyring.impl,
+	          com.redhat.thermostat.utils.keyring.internal,
 	        </Private-Package>
             <Export-Package>com.redhat.thermostat.utils.keyring</Export-Package>
             <Bundle-Vendor>Red Hat, Inc.</Bundle-Vendor>
--- a/keyring/src/main/java/com/redhat/thermostat/utils/keyring/activator/Activator.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/src/main/java/com/redhat/thermostat/utils/keyring/activator/Activator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -40,7 +40,7 @@
 import org.osgi.framework.BundleContext;
 
 import com.redhat.thermostat.utils.keyring.Keyring;
-import com.redhat.thermostat.utils.keyring.impl.KeyringImpl;
+import com.redhat.thermostat.utils.keyring.internal.KeyringImpl;
 
 public class Activator implements BundleActivator {
 
--- a/keyring/src/main/java/com/redhat/thermostat/utils/keyring/impl/KeyringImpl.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,105 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.utils.keyring.impl;
-
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-
-import com.redhat.thermostat.shared.config.NativeLibraryResolver;
-import com.redhat.thermostat.utils.keyring.Keyring;
-import com.redhat.thermostat.utils.keyring.KeyringException;
-
-public class KeyringImpl implements Keyring {
-
-    private static String DESC_PREFIX = "Thermostat auth info for: ";
-
-    static {
-        String lib = NativeLibraryResolver.getAbsoluteLibraryPath("GnomeKeyringWrapper");
-        System.load(lib);
-    }
-
-    @Override
-    public synchronized void savePassword(String url, String username, char[] password) {
-        byte[] pwBytes = null;
-        boolean success = false;
-        try {
-            String desc = DESC_PREFIX + username + "@" + url;
-            if (password == null) {
-                pwBytes = new byte[]{};
-            } else {
-                pwBytes = Charset.defaultCharset().encode(CharBuffer.wrap(password)).array();
-            }
-            success = gnomeKeyringWrapperSavePasswordNative(url, username, pwBytes, desc);
-        } finally {
-            if (pwBytes != null) {
-                Arrays.fill(pwBytes, (byte) 0);
-            }
-        }
-        if (!success) {
-            throw new KeyringException("Couldn't save password.");
-        }
-    }
-
-    @Override
-    public synchronized char[] getPassword(String url, String username) {
-        byte[] pwBytes = gnomeKeyringWrapperGetPasswordNative(url, username);
-        char[] password = null;
-        if (pwBytes != null) {
-            try {
-                password = Charset.defaultCharset().decode(ByteBuffer.wrap(pwBytes)).array();
-            } finally {
-                Arrays.fill(pwBytes, (byte) 0);
-            }
-        }
-        return password;
-    }
-
-    @Override
-    public synchronized void clearPassword(String url, String username) {
-        if (!gnomeKeyringWrapperClearPasswordNative(url, username)) {
-            throw new RuntimeException("Couldn't clear password.");
-        }
-        
-    }
-
-    private static native boolean gnomeKeyringWrapperSavePasswordNative(String url, String userName, byte[] password, String description);
-    private static native byte[] gnomeKeyringWrapperGetPasswordNative(String url, String userName);
-    private static native boolean gnomeKeyringWrapperClearPasswordNative(String url, String userName);
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/keyring/src/main/java/com/redhat/thermostat/utils/keyring/internal/KeyringImpl.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.utils.keyring.internal;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import com.redhat.thermostat.shared.config.NativeLibraryResolver;
+import com.redhat.thermostat.utils.keyring.Keyring;
+import com.redhat.thermostat.utils.keyring.KeyringException;
+
+public class KeyringImpl implements Keyring {
+
+    private static String DESC_PREFIX = "Thermostat auth info for: ";
+
+    static {
+        String lib = NativeLibraryResolver.getAbsoluteLibraryPath("GnomeKeyringWrapper");
+        System.load(lib);
+    }
+
+    @Override
+    public synchronized void savePassword(String url, String username, char[] password) {
+        byte[] pwBytes = null;
+        boolean success = false;
+        try {
+            String desc = DESC_PREFIX + username + "@" + url;
+            if (password == null) {
+                pwBytes = new byte[]{};
+            } else {
+                pwBytes = Charset.defaultCharset().encode(CharBuffer.wrap(password)).array();
+            }
+            success = gnomeKeyringWrapperSavePasswordNative(url, username, pwBytes, desc);
+        } finally {
+            if (pwBytes != null) {
+                Arrays.fill(pwBytes, (byte) 0);
+            }
+        }
+        if (!success) {
+            throw new KeyringException("Couldn't save password.");
+        }
+    }
+
+    @Override
+    public synchronized char[] getPassword(String url, String username) {
+        byte[] pwBytes = gnomeKeyringWrapperGetPasswordNative(url, username);
+        char[] password = null;
+        if (pwBytes != null) {
+            try {
+                password = Charset.defaultCharset().decode(ByteBuffer.wrap(pwBytes)).array();
+            } finally {
+                Arrays.fill(pwBytes, (byte) 0);
+            }
+        }
+        return password;
+    }
+
+    @Override
+    public synchronized void clearPassword(String url, String username) {
+        if (!gnomeKeyringWrapperClearPasswordNative(url, username)) {
+            throw new RuntimeException("Couldn't clear password.");
+        }
+        
+    }
+
+    private static native boolean gnomeKeyringWrapperSavePasswordNative(String url, String userName, byte[] password, String description);
+    private static native byte[] gnomeKeyringWrapperGetPasswordNative(String url, String userName);
+    private static native boolean gnomeKeyringWrapperClearPasswordNative(String url, String userName);
+}
+
--- a/keyring/src/main/native/libgnome-keyring/GnomeKeyringLibraryNative.c	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/src/main/native/libgnome-keyring/GnomeKeyringLibraryNative.c	Fri Apr 08 13:18:48 2016 +0200
@@ -34,7 +34,7 @@
  * to do so, delete this exception statement from your version.
  */
 
-#include "com_redhat_thermostat_utils_keyring_impl_KeyringImpl.h"
+#include "com_redhat_thermostat_utils_keyring_internal_KeyringImpl.h"
 
 #include <jni.h>
 #include <glib.h>
@@ -58,7 +58,7 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperSavePasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperSavePasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNativeClass, jstring jurl, jstring juserName, jbyteArray jpassword, jstring jdescription)
 {
     int passIndex;
@@ -133,7 +133,7 @@
 }
 
 JNIEXPORT jbyteArray JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperGetPasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperGetPasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNative, jstring jurl, jstring juserName)
 {
 	const char *url = (*env)->GetStringUTFChars(env, jurl, NULL);
@@ -175,7 +175,7 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperClearPasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperClearPasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNative, jstring jurl, jstring juserName)
 {
     const char *url = (*env)->GetStringUTFChars(env, jurl, NULL);
--- a/keyring/src/main/native/libgnome-keyring/Makefile	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/src/main/native/libgnome-keyring/Makefile	Fri Apr 08 13:18:48 2016 +0200
@@ -18,7 +18,7 @@
 MYLDFLAGS  += `pkg-config --libs gnome-keyring-1`
 
 .PHONY:
-JNI_LIST = com.redhat.thermostat.utils.keyring.impl.KeyringImpl
+JNI_LIST = com.redhat.thermostat.utils.keyring.internal.KeyringImpl
 
 $(JNI_LIST):
 	$(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST)
--- a/keyring/src/main/native/libsecret/LibsecretKeyringLibraryNative.c	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/src/main/native/libsecret/LibsecretKeyringLibraryNative.c	Fri Apr 08 13:18:48 2016 +0200
@@ -34,7 +34,7 @@
  * to do so, delete this exception statement from your version.
  */
 
-#include "com_redhat_thermostat_utils_keyring_impl_KeyringImpl.h"
+#include "com_redhat_thermostat_utils_keyring_internal_KeyringImpl.h"
 
 #include <jni.h>
 #include <glib.h>
@@ -58,7 +58,7 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperSavePasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperSavePasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNativeClass, jstring jurl, jstring juserName, jbyteArray jpassword, jstring jdescription)
 {
     int passIndex;
@@ -141,7 +141,7 @@
 }
 
 JNIEXPORT jbyteArray JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperGetPasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperGetPasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNative, jstring jurl, jstring juserName)
 {
     const char *url = (*env)->GetStringUTFChars(env, jurl, NULL);
@@ -188,7 +188,7 @@
 }
 
 JNIEXPORT jboolean JNICALL
-Java_com_redhat_thermostat_utils_keyring_impl_KeyringImpl_gnomeKeyringWrapperClearPasswordNative
+Java_com_redhat_thermostat_utils_keyring_internal_KeyringImpl_gnomeKeyringWrapperClearPasswordNative
   (JNIEnv *env, jclass GnomeKeyringLibraryNative, jstring jurl, jstring juserName)
 {
     const char *url = (*env)->GetStringUTFChars(env, jurl, NULL);
--- a/keyring/src/main/native/libsecret/Makefile	Wed Apr 06 14:52:26 2016 +0200
+++ b/keyring/src/main/native/libsecret/Makefile	Fri Apr 08 13:18:48 2016 +0200
@@ -18,7 +18,7 @@
 MYLDFLAGS  += `pkg-config --libs libsecret-1`
 
 .PHONY:
-JNI_LIST = com.redhat.thermostat.utils.keyring.impl.KeyringImpl
+JNI_LIST = com.redhat.thermostat.utils.keyring.internal.KeyringImpl
 
 $(JNI_LIST):
 	$(JAVAH) -force -classpath $(CLASSPATH) -d $(TARGET_DIR) $(JNI_LIST)
--- a/keyring/src/test/java/com/redhat/thermostat/utils/keyring/impl/KeyringImplTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
-/*
- * Copyright 2012-2016 Red Hat, Inc.
- *
- * This file is part of Thermostat.
- *
- * Thermostat is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * Thermostat is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Thermostat; see the file COPYING.  If not see
- * <http://www.gnu.org/licenses/>.
- *
- * Linking this code with other modules is making a combined work
- * based on this code.  Thus, the terms and conditions of the GNU
- * General Public License cover the whole combination.
- *
- * As a special exception, the copyright holders of this code give
- * you permission to link this code with independent modules to
- * produce an executable, regardless of the license terms of these
- * independent modules, and to copy and distribute the resulting
- * executable under terms of your choice, provided that you also
- * meet, for each linked independent module, the terms and conditions
- * of the license of that module.  An independent module is a module
- * which is not derived from or based on this code.  If you modify
- * this code, you may extend this exception to your version of the
- * library, but you are not obligated to do so.  If you do not wish
- * to do so, delete this exception statement from your version.
- */
-
-package com.redhat.thermostat.utils.keyring.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
-import org.junit.Ignore;
-import org.junit.Test;
-
-public class KeyringImplTest {
-
-    @Ignore // FIXME Hangs waiting for user to unlock keyring.
-    @Test
-    public void verifySavedPasswordIsRetrieveable() {
-        String url = "www.example1.com";
-        String user = "mike";
-        char[] pw = new char[] {'1', '2', '3'};
-        KeyringImpl k = new KeyringImpl();
-        k.savePassword(url, user, pw);
-        assertEquals(new String(pw), new String(k.getPassword(url, user)));
-
-        // Cleanup
-        k.clearPassword(url, user);
-    }
-
-    @Ignore // FIXME Hangs waiting for user to unlock keyring.
-    @Test
-    public void verifySavePasswordReplacesExisting() {
-        String url = "www.example2.com";
-        String user = "mike";
-        char[] pw1 = new char[] {'1', '2', '3'};
-        char[] pw2 = new char[] {'4', '5', '6'};
-        KeyringImpl k = new KeyringImpl();
-        k.savePassword(url, user, pw1);
-        k.savePassword(url, user, pw2);
-
-        assertEquals(new String(pw2), new String(k.getPassword(url, user)));
-
-        // Cleanup
-        k.clearPassword(url, user);
-    }
-
-    @Ignore // FIXME Hangs waiting for user to unlock keyring.
-    @Test
-    public void verifyClearedPasswordIsCleared() {
-        String url = "www.example3.com";
-        String user = "mike";
-        char[] pw = new char[] {'1', '2', '3'};
-        KeyringImpl k = new KeyringImpl();
-        k.savePassword(url, user, pw);
-        assertNotNull(k.getPassword(url, user));
-        k.clearPassword(url, user);
-        assertNull(k.getPassword(url, user));
-    }
-
-    @Ignore // FIXME Hangs waiting for user to unlock keyring.
-    @Test
-    public void multipleServersSameUser() {
-        String url1 = "www.example4.com";
-        String url2 = "www.fake.com";
-        String user = "mike";
-        char[] pw1 = new char[] {'1', '2', '3'};
-        char[] pw2 = new char[] {'4', '5', '6'};
-        KeyringImpl k = new KeyringImpl();
-        k.savePassword(url1, user, pw1);
-        k.savePassword(url2, user, pw2);
-
-        assertEquals(new String(pw1), new String(k.getPassword(url1, user)));
-        assertEquals(new String(pw2), new String(k.getPassword(url2, user)));
-
-        // Cleanup
-        k.clearPassword(url1, user);
-        k.clearPassword(url2, user);
-    }
-
-    @Ignore // FIXME Hangs waiting for user to unlock keyring.
-    @Test
-    public void multipleUsersSameServer() {
-        String url = "www.example5.com";
-        String user1 = "mike";
-        String user2 = "mary";
-        char[] pw1 = new char[] {'1', '2', '3'};
-        char[] pw2 = new char[] {'4', '5', '6'};
-        KeyringImpl k = new KeyringImpl();
-        k.savePassword(url, user1, pw1);
-        k.savePassword(url, user2, pw2);
-
-        assertEquals(new String(pw1), new String(k.getPassword(url, user1)));
-        assertEquals(new String(pw2), new String(k.getPassword(url, user2)));
-
-        // Cleanup
-        k.clearPassword(url, user1);
-        k.clearPassword(url, user2);
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/keyring/src/test/java/com/redhat/thermostat/utils/keyring/internal/KeyringImplTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2012-2016 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.utils.keyring.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class KeyringImplTest {
+
+    @Ignore // FIXME Hangs waiting for user to unlock keyring.
+    @Test
+    public void verifySavedPasswordIsRetrieveable() {
+        String url = "www.example1.com";
+        String user = "mike";
+        char[] pw = new char[] {'1', '2', '3'};
+        KeyringImpl k = new KeyringImpl();
+        k.savePassword(url, user, pw);
+        assertEquals(new String(pw), new String(k.getPassword(url, user)));
+
+        // Cleanup
+        k.clearPassword(url, user);
+    }
+
+    @Ignore // FIXME Hangs waiting for user to unlock keyring.
+    @Test
+    public void verifySavePasswordReplacesExisting() {
+        String url = "www.example2.com";
+        String user = "mike";
+        char[] pw1 = new char[] {'1', '2', '3'};
+        char[] pw2 = new char[] {'4', '5', '6'};
+        KeyringImpl k = new KeyringImpl();
+        k.savePassword(url, user, pw1);
+        k.savePassword(url, user, pw2);
+
+        assertEquals(new String(pw2), new String(k.getPassword(url, user)));
+
+        // Cleanup
+        k.clearPassword(url, user);
+    }
+
+    @Ignore // FIXME Hangs waiting for user to unlock keyring.
+    @Test
+    public void verifyClearedPasswordIsCleared() {
+        String url = "www.example3.com";
+        String user = "mike";
+        char[] pw = new char[] {'1', '2', '3'};
+        KeyringImpl k = new KeyringImpl();
+        k.savePassword(url, user, pw);
+        assertNotNull(k.getPassword(url, user));
+        k.clearPassword(url, user);
+        assertNull(k.getPassword(url, user));
+    }
+
+    @Ignore // FIXME Hangs waiting for user to unlock keyring.
+    @Test
+    public void multipleServersSameUser() {
+        String url1 = "www.example4.com";
+        String url2 = "www.fake.com";
+        String user = "mike";
+        char[] pw1 = new char[] {'1', '2', '3'};
+        char[] pw2 = new char[] {'4', '5', '6'};
+        KeyringImpl k = new KeyringImpl();
+        k.savePassword(url1, user, pw1);
+        k.savePassword(url2, user, pw2);
+
+        assertEquals(new String(pw1), new String(k.getPassword(url1, user)));
+        assertEquals(new String(pw2), new String(k.getPassword(url2, user)));
+
+        // Cleanup
+        k.clearPassword(url1, user);
+        k.clearPassword(url2, user);
+    }
+
+    @Ignore // FIXME Hangs waiting for user to unlock keyring.
+    @Test
+    public void multipleUsersSameServer() {
+        String url = "www.example5.com";
+        String user1 = "mike";
+        String user2 = "mary";
+        char[] pw1 = new char[] {'1', '2', '3'};
+        char[] pw2 = new char[] {'4', '5', '6'};
+        KeyringImpl k = new KeyringImpl();
+        k.savePassword(url, user1, pw1);
+        k.savePassword(url, user2, pw2);
+
+        assertEquals(new String(pw1), new String(k.getPassword(url, user1)));
+        assertEquals(new String(pw2), new String(k.getPassword(url, user2)));
+
+        // Cleanup
+        k.clearPassword(url, user1);
+        k.clearPassword(url, user2);
+    }
+
+}
+
--- a/main/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/main/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -127,7 +127,7 @@
               com.redhat.thermostat.main,
             </Export-Package>
             <Private-Package>
-              com.redhat.thermostat.main.impl
+              com.redhat.thermostat.main.internal
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/main/src/main/java/com/redhat/thermostat/main/Thermostat.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/main/src/main/java/com/redhat/thermostat/main/Thermostat.java	Fri Apr 08 13:18:48 2016 +0200
@@ -36,8 +36,8 @@
 
 package com.redhat.thermostat.main;
 
-import com.redhat.thermostat.main.impl.FrameworkOptionsProcessor;
-import com.redhat.thermostat.main.impl.FrameworkProvider;
+import com.redhat.thermostat.main.internal.FrameworkOptionsProcessor;
+import com.redhat.thermostat.main.internal.FrameworkProvider;
 import com.redhat.thermostat.shared.config.CommonPaths;
 import com.redhat.thermostat.shared.config.internal.CommonPathsImpl;
 
--- a/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkOptionsProcessor.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012-2016 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.main.impl;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import com.redhat.thermostat.launcher.FrameworkOptions;
-
-/**
- * Thermostat options for the OSGi framework and relevant debug output.
- *
- */
-public class FrameworkOptionsProcessor {
-
-    private final Map<FrameworkOptions, String> globalOptions;
-    private final String[] otherOptions;
-
-    public FrameworkOptionsProcessor(String[] args) {
-        this.globalOptions = new HashMap<>();
-        initializeDefaultGlobalOptions();
-        this.otherOptions = processGlobalOptions(args);
-    }
-
-    private void initializeDefaultGlobalOptions() {
-        // set up default boot delegation to allow the vm-profiler to work
-        // correctly by default
-        globalOptions.put(FrameworkOptions.BOOT_DELEGATION,
-                "com.redhat.thermostat.vm.profiler.agent.jvm," +
-                "com.redhat.thermostat.vm.profiler.agent.asm," +
-                "com.redhat.thermostat.vm.profiler.agent.asm.commons");
-    }
-
-    private String[] processGlobalOptions(String[] args) {
-        List<String> toProcess = new ArrayList<>(Arrays.asList(args));
-        Iterator<String> iter = toProcess.iterator();
-        while (iter.hasNext()) {
-            String arg = iter.next();
-            if (FrameworkOptions.PRINT_OSGI_INFO.getOptString().equals(arg)) {
-                globalOptions.put(FrameworkOptions.PRINT_OSGI_INFO,
-                        Boolean.TRUE.toString());
-                iter.remove();
-            }
-            if (FrameworkOptions.IGNORE_BUNDLE_VERSIONS.getOptString().equals(arg)) {
-                globalOptions.put(FrameworkOptions.IGNORE_BUNDLE_VERSIONS,
-                        Boolean.TRUE.toString());
-                iter.remove();
-            }
-            if (arg.startsWith(FrameworkOptions.BOOT_DELEGATION.getOptString() + "=")) {
-                int startIndex = (FrameworkOptions.BOOT_DELEGATION.getOptString() + "=")
-                        .length();
-                String bootDelegation = arg.substring(startIndex);
-                if ("".equals(bootDelegation)) {
-                    throw new RuntimeException(
-                            "Unexpected string used with boot delegation: '"
-                                    + bootDelegation + "'");
-                }
-                globalOptions.put(FrameworkOptions.BOOT_DELEGATION, bootDelegation);
-                iter.remove();
-            }
-        }
-        return toProcess.toArray(new String[] {});
-    }
-
-    public String[] getOtherOptions() {
-        return otherOptions;
-    }
-
-    public boolean printOsgiInfo() {
-        return globalOptions.containsKey(FrameworkOptions.PRINT_OSGI_INFO);
-    }
-
-    public boolean ignoreBundleVersions() {
-        return globalOptions.containsKey(FrameworkOptions.IGNORE_BUNDLE_VERSIONS);
-    }
-
-    public String bootDelegationValue() {
-        return globalOptions.get(FrameworkOptions.BOOT_DELEGATION);
-    }
-
-}
--- a/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-/*
- * Copyright 2012-2016 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.main.impl;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Method;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.ServiceLoader;
-
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-import org.osgi.util.tracker.ServiceTracker;
-
-import com.redhat.thermostat.launcher.BundleManager;
-import com.redhat.thermostat.launcher.Launcher;
-import com.redhat.thermostat.shared.config.CommonPaths;
-
-public class FrameworkProvider {
-
-    private static final String DEBUG_PREFIX = "FrameworkProvider: ";
-    private static final String PROPS_FILE = "/com/redhat/thermostat/main/impl/bootstrapbundles.properties";
-    private static final String BUNDLELIST = "bundles";
-
-    private final CommonPaths paths;
-    private final FrameworkOptionsProcessor frameworkOptionsProcessor;
-    
-    // The framework cache location; Must not be shared between apps!
-    private Path osgiCacheStorage;
-    
-
-    public FrameworkProvider(CommonPaths paths, FrameworkOptionsProcessor options) {
-        this.paths = paths;
-        this.frameworkOptionsProcessor = options;
-    }
-
-    // This is our ticket into OSGi land. Unfortunately, we to use a bit of reflection here.
-    // The launcher and bundleloader are instantiated from within their bundles, ie loaded
-    // by the bundle classloader.
-    public void start(String[] args) {
-        try {
-            Framework framework = makeFramework();
-            prepareFramework(framework);
-            loadBootstrapBundles(framework);
-            setLoaderVerbosity(framework);
-            setIgnoreBundleVersions(framework);
-            runLauncher(framework, args);
-        } catch (InterruptedException | BundleException | IOException e) {
-            throw new RuntimeException("Could not start framework.", e);
-        }
-    }
-
-    private String getOSGiPublicPackages() throws FileNotFoundException, IOException {
-        File osgiBundleDefinitions = new File(paths.getSystemConfigurationDirectory(), "osgi-export.properties");
-
-        Properties bundles = new Properties();
-        bundles.load(new FileInputStream(osgiBundleDefinitions));
-
-        StringBuilder publicPackages = new StringBuilder();
-        /*
-         * Packages the launcher requires
-         */
-        //publicPackages.append("com.redhat.thermostat.common.services");
-        boolean firstPackage = true;
-        for (Object bundle : bundles.keySet()) {
-            if (!firstPackage) {
-                publicPackages.append(",\n");
-            }
-            firstPackage = false;
-            publicPackages.append(bundle);
-            String bundleVersion = (String) bundles.get(bundle);
-            if (!bundleVersion.isEmpty()) {
-                publicPackages.append("; version=").append(bundleVersion);
-            }
-        }
-
-        return publicPackages.toString();
-    }
-
-    private void prepareFramework(final Framework framework) throws BundleException, IOException {
-        framework.init();
-        framework.start();
-        if (frameworkOptionsProcessor.printOsgiInfo()) {
-            System.out.println(DEBUG_PREFIX + "OSGi framework has started.");
-        }
-
-        Runtime.getRuntime().addShutdownHook(new Thread() {
-            @Override
-            public void run() {
-                try {
-                    framework.stop();
-                    framework.waitForStop(0);
-                    if (frameworkOptionsProcessor.printOsgiInfo()) {
-                        System.out.println(DEBUG_PREFIX + "OSGi framework has shut down.");
-                    }
-                    recursivelyDeleteDirectory(osgiCacheStorage.toFile());
-                    if (frameworkOptionsProcessor.printOsgiInfo()) {
-                        System.out.println(DEBUG_PREFIX + "Removed OSGi cache directory: "
-                                + osgiCacheStorage.toFile().getAbsolutePath());
-                    }
-                } catch (Exception e) {
-                    throw new RuntimeException("Error shutting down framework.", e);
-                }
-            }
-        });
-
-    }
-
-    private void recursivelyDeleteDirectory(File directory) {
-        for (File file: directory.listFiles()) {
-            if (file.isDirectory()) {
-                recursivelyDeleteDirectory(file);
-            }
-            file.delete();
-        }
-        directory.delete();
-    }
-    
-    
-    private Framework makeFramework() throws FileNotFoundException, IOException {
-        File osgiCacheDir = new File(paths.getUserCacheDirectory(), "osgi-cache");
-        if (!osgiCacheDir.isDirectory() && !osgiCacheDir.mkdirs()) {
-            throw new RuntimeException("Unable to create " + osgiCacheDir);
-        }
-
-        // Create temporary directory which will be used as cache for OSGi bundles. See
-        // http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/Constants.html#FRAMEWORK_STORAGE
-        // for details about what this location is used for.
-        // 
-        // Agent, swing gui client application must not use the same location as this tricks the framework
-        // into thinking that some bundles are installed and loaded when that might not actually be the case.
-        // Note that we do not specify the org.osgi.framework.storage.clean property and the default is to NOT
-        // clean the cache, which is when we might run into trouble if this location is shared. This
-        // temp directory will be deleted on VM shutdown.
-        // 
-        // This fixes Thermostat BZ 1110.
-        osgiCacheStorage = Files.createTempDirectory(osgiCacheDir.toPath(), null);
-        if (frameworkOptionsProcessor.printOsgiInfo()) {
-            System.out.println(DEBUG_PREFIX + "OSGi cache location: "
-                    + osgiCacheStorage.toFile().getAbsolutePath());
-        }
-        
-        ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class,
-                getClass().getClassLoader());
-        Map<String, String> bundleConfigurations = new HashMap<String, String>();
-        String extraPackages = getOSGiPublicPackages();
-        bundleConfigurations.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, extraPackages);
-        bundleConfigurations.put(Constants.FRAMEWORK_STORAGE, osgiCacheStorage.toFile().getAbsolutePath());
-        if (frameworkOptionsProcessor.bootDelegationValue() != null) {
-            if (frameworkOptionsProcessor.printOsgiInfo()) {
-                System.out.println("Boot delegation: " + frameworkOptionsProcessor.bootDelegationValue());
-            }
-            bundleConfigurations.put(Constants.FRAMEWORK_BOOTDELEGATION, frameworkOptionsProcessor.bootDelegationValue());
-        }
-        Iterator<FrameworkFactory> factories = loader.iterator();
-        if (factories.hasNext()) {
-            // we just want the first found
-            return factories.next().newFramework(bundleConfigurations);
-        } else {
-            throw new InternalError("ServiceLoader cannot find a FrameworkFactory!");
-        }   
-    }
-
-    private void loadBootstrapBundles(Framework framework) throws BundleException, InterruptedException {
-        Properties bootstrapProps = new Properties();
-        // the properties file should be in the same package as this class
-        InputStream res = getClass().getResourceAsStream(PROPS_FILE);
-        if (res != null) {
-            try {
-                bootstrapProps.load(res);
-            } catch (IOException e) {
-                throw new RuntimeException("Could not load bootstrap bundle properties.", e);
-            }
-        }
-        String[] bundles = bootstrapProps.getProperty(BUNDLELIST).split(",");
-        List<String> locations = new ArrayList<>();
-        for (String bundle : bundles) {
-            String trimmed = bundle.trim();
-            if (trimmed != null && trimmed.length() > 0) {
-                String location = actualLocation(trimmed);
-                locations.add(location);
-            }
-        }
-        BundleManager.preLoadBundles(framework, locations, frameworkOptionsProcessor.printOsgiInfo());
-    }
-
-    private void setLoaderVerbosity(Framework framework) throws InterruptedException {
-        Object loader = getService(framework, BundleManager.class.getName());
-        callVoidReflectedMethod(loader, "setPrintOSGiInfo", frameworkOptionsProcessor.printOsgiInfo());
-    }
-
-    private void setIgnoreBundleVersions(Framework framework) throws InterruptedException {
-        Object loader = getService(framework, BundleManager.class.getName());
-        callVoidReflectedMethod(loader, "setIgnoreBundleVersions", frameworkOptionsProcessor.ignoreBundleVersions());
-    }
-
-    private void runLauncher(Framework framework, String[] args) throws InterruptedException {
-        Object launcher = getService(framework, Launcher.class.getName());
-        callVoidReflectedMethod(launcher, "run", args, false);
-    }
-
-    private Object getService(Framework framework, String name) throws InterruptedException {
-        Object service = null;
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        ServiceTracker tracker = new ServiceTracker(framework.getBundleContext(), name, null);
-        tracker.open();
-        service = tracker.waitForService(0);
-        tracker.close();
-        return service;
-    }
-
-    /**
-     * Call {@code object}.{@code name} with {@code args} as the arguments. The
-     * return value is ignored. The types of the method arguments must exactly
-     * match the types of the supplied arguments, but primitives are used unboxed.
-     */
-    private void callVoidReflectedMethod(Object object, String name, Object... args) {
-        Class<?> clazz = object.getClass();
-        Class<?>[] classes = new Class<?>[args.length];
-        for (int i = 0; i < args.length; i++) {
-            classes[i] = preferPrimitiveClass(args[i].getClass());
-        }
-
-        try {
-            Method m = clazz.getMethod(name, classes);
-            m.invoke(object, args);
-        } catch (ReflectiveOperationException e) {
-            throw new AssertionError("Unable to call '" + name + "' method on object " + object, e);
-        }
-    }
-
-    private static <T> Class<T> preferPrimitiveClass(Class<T> boxedPrimitive) {
-        HashMap<Class<?>, Class<?>> map = new HashMap<>();
-        map.put(Byte.class, byte.class);
-        map.put(Short.class, short.class);
-        map.put(Integer.class, int.class);
-        map.put(Long.class, long.class);
-        map.put(Float.class, float.class);
-        map.put(Double.class, double.class);
-        map.put(Boolean.class, boolean.class);
-        map.put(Character.class, char.class);
-
-        if (map.containsKey(boxedPrimitive)) {
-            return (Class<T>) map.get(boxedPrimitive);
-        } else  {
-            return boxedPrimitive;
-        }
-    }
-
-    // Resolve symlinks completely; This makes the behaviour of this class
-    // consistent with BundleManagerImpl and avoid problems where two different
-    // files across bootstrap and later code provide the same bundle symbolic
-    // name version
-    private String actualLocation(String resourceName) {
-        File file = new File(paths.getSystemLibRoot(), resourceName);
-        try {
-            return file.getCanonicalFile().toURI().toString();
-        } catch (IOException e) {
-            // okay, lets not canonicalize the path
-            return file.toURI().toString();
-        }
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/main/java/com/redhat/thermostat/main/internal/FrameworkOptionsProcessor.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012-2016 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.main.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.redhat.thermostat.launcher.FrameworkOptions;
+
+/**
+ * Thermostat options for the OSGi framework and relevant debug output.
+ *
+ */
+public class FrameworkOptionsProcessor {
+
+    private final Map<FrameworkOptions, String> globalOptions;
+    private final String[] otherOptions;
+
+    public FrameworkOptionsProcessor(String[] args) {
+        this.globalOptions = new HashMap<>();
+        initializeDefaultGlobalOptions();
+        this.otherOptions = processGlobalOptions(args);
+    }
+
+    private void initializeDefaultGlobalOptions() {
+        // set up default boot delegation to allow the vm-profiler to work
+        // correctly by default
+        globalOptions.put(FrameworkOptions.BOOT_DELEGATION,
+                "com.redhat.thermostat.vm.profiler.agent.jvm," +
+                "com.redhat.thermostat.vm.profiler.agent.asm," +
+                "com.redhat.thermostat.vm.profiler.agent.asm.commons");
+    }
+
+    private String[] processGlobalOptions(String[] args) {
+        List<String> toProcess = new ArrayList<>(Arrays.asList(args));
+        Iterator<String> iter = toProcess.iterator();
+        while (iter.hasNext()) {
+            String arg = iter.next();
+            if (FrameworkOptions.PRINT_OSGI_INFO.getOptString().equals(arg)) {
+                globalOptions.put(FrameworkOptions.PRINT_OSGI_INFO,
+                        Boolean.TRUE.toString());
+                iter.remove();
+            }
+            if (FrameworkOptions.IGNORE_BUNDLE_VERSIONS.getOptString().equals(arg)) {
+                globalOptions.put(FrameworkOptions.IGNORE_BUNDLE_VERSIONS,
+                        Boolean.TRUE.toString());
+                iter.remove();
+            }
+            if (arg.startsWith(FrameworkOptions.BOOT_DELEGATION.getOptString() + "=")) {
+                int startIndex = (FrameworkOptions.BOOT_DELEGATION.getOptString() + "=")
+                        .length();
+                String bootDelegation = arg.substring(startIndex);
+                if ("".equals(bootDelegation)) {
+                    throw new RuntimeException(
+                            "Unexpected string used with boot delegation: '"
+                                    + bootDelegation + "'");
+                }
+                globalOptions.put(FrameworkOptions.BOOT_DELEGATION, bootDelegation);
+                iter.remove();
+            }
+        }
+        return toProcess.toArray(new String[] {});
+    }
+
+    public String[] getOtherOptions() {
+        return otherOptions;
+    }
+
+    public boolean printOsgiInfo() {
+        return globalOptions.containsKey(FrameworkOptions.PRINT_OSGI_INFO);
+    }
+
+    public boolean ignoreBundleVersions() {
+        return globalOptions.containsKey(FrameworkOptions.IGNORE_BUNDLE_VERSIONS);
+    }
+
+    public String bootDelegationValue() {
+        return globalOptions.get(FrameworkOptions.BOOT_DELEGATION);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/main/java/com/redhat/thermostat/main/internal/FrameworkProvider.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,311 @@
+/*
+ * Copyright 2012-2016 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.main.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import org.osgi.util.tracker.ServiceTracker;
+
+import com.redhat.thermostat.launcher.BundleManager;
+import com.redhat.thermostat.launcher.Launcher;
+import com.redhat.thermostat.shared.config.CommonPaths;
+
+public class FrameworkProvider {
+
+    private static final String DEBUG_PREFIX = "FrameworkProvider: ";
+    private static final String PROPS_FILE = "/com/redhat/thermostat/main/internal/bootstrapbundles.properties";
+    private static final String BUNDLELIST = "bundles";
+
+    private final CommonPaths paths;
+    private final FrameworkOptionsProcessor frameworkOptionsProcessor;
+    
+    // The framework cache location; Must not be shared between apps!
+    private Path osgiCacheStorage;
+    
+
+    public FrameworkProvider(CommonPaths paths, FrameworkOptionsProcessor options) {
+        this.paths = paths;
+        this.frameworkOptionsProcessor = options;
+    }
+
+    // This is our ticket into OSGi land. Unfortunately, we to use a bit of reflection here.
+    // The launcher and bundleloader are instantiated from within their bundles, ie loaded
+    // by the bundle classloader.
+    public void start(String[] args) {
+        try {
+            Framework framework = makeFramework();
+            prepareFramework(framework);
+            loadBootstrapBundles(framework);
+            setLoaderVerbosity(framework);
+            setIgnoreBundleVersions(framework);
+            runLauncher(framework, args);
+        } catch (InterruptedException | BundleException | IOException e) {
+            throw new RuntimeException("Could not start framework.", e);
+        }
+    }
+
+    private String getOSGiPublicPackages() throws FileNotFoundException, IOException {
+        File osgiBundleDefinitions = new File(paths.getSystemConfigurationDirectory(), "osgi-export.properties");
+
+        Properties bundles = new Properties();
+        bundles.load(new FileInputStream(osgiBundleDefinitions));
+
+        StringBuilder publicPackages = new StringBuilder();
+        /*
+         * Packages the launcher requires
+         */
+        //publicPackages.append("com.redhat.thermostat.common.services");
+        boolean firstPackage = true;
+        for (Object bundle : bundles.keySet()) {
+            if (!firstPackage) {
+                publicPackages.append(",\n");
+            }
+            firstPackage = false;
+            publicPackages.append(bundle);
+            String bundleVersion = (String) bundles.get(bundle);
+            if (!bundleVersion.isEmpty()) {
+                publicPackages.append("; version=").append(bundleVersion);
+            }
+        }
+
+        return publicPackages.toString();
+    }
+
+    private void prepareFramework(final Framework framework) throws BundleException, IOException {
+        framework.init();
+        framework.start();
+        if (frameworkOptionsProcessor.printOsgiInfo()) {
+            System.out.println(DEBUG_PREFIX + "OSGi framework has started.");
+        }
+
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            @Override
+            public void run() {
+                try {
+                    framework.stop();
+                    framework.waitForStop(0);
+                    if (frameworkOptionsProcessor.printOsgiInfo()) {
+                        System.out.println(DEBUG_PREFIX + "OSGi framework has shut down.");
+                    }
+                    recursivelyDeleteDirectory(osgiCacheStorage.toFile());
+                    if (frameworkOptionsProcessor.printOsgiInfo()) {
+                        System.out.println(DEBUG_PREFIX + "Removed OSGi cache directory: "
+                                + osgiCacheStorage.toFile().getAbsolutePath());
+                    }
+                } catch (Exception e) {
+                    throw new RuntimeException("Error shutting down framework.", e);
+                }
+            }
+        });
+
+    }
+
+    private void recursivelyDeleteDirectory(File directory) {
+        for (File file: directory.listFiles()) {
+            if (file.isDirectory()) {
+                recursivelyDeleteDirectory(file);
+            }
+            file.delete();
+        }
+        directory.delete();
+    }
+    
+    
+    private Framework makeFramework() throws FileNotFoundException, IOException {
+        File osgiCacheDir = new File(paths.getUserCacheDirectory(), "osgi-cache");
+        if (!osgiCacheDir.isDirectory() && !osgiCacheDir.mkdirs()) {
+            throw new RuntimeException("Unable to create " + osgiCacheDir);
+        }
+
+        // Create temporary directory which will be used as cache for OSGi bundles. See
+        // http://www.osgi.org/javadoc/r4v43/core/org/osgi/framework/Constants.html#FRAMEWORK_STORAGE
+        // for details about what this location is used for.
+        // 
+        // Agent, swing gui client application must not use the same location as this tricks the framework
+        // into thinking that some bundles are installed and loaded when that might not actually be the case.
+        // Note that we do not specify the org.osgi.framework.storage.clean property and the default is to NOT
+        // clean the cache, which is when we might run into trouble if this location is shared. This
+        // temp directory will be deleted on VM shutdown.
+        // 
+        // This fixes Thermostat BZ 1110.
+        osgiCacheStorage = Files.createTempDirectory(osgiCacheDir.toPath(), null);
+        if (frameworkOptionsProcessor.printOsgiInfo()) {
+            System.out.println(DEBUG_PREFIX + "OSGi cache location: "
+                    + osgiCacheStorage.toFile().getAbsolutePath());
+        }
+        
+        ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class,
+                getClass().getClassLoader());
+        Map<String, String> bundleConfigurations = new HashMap<String, String>();
+        String extraPackages = getOSGiPublicPackages();
+        bundleConfigurations.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, extraPackages);
+        bundleConfigurations.put(Constants.FRAMEWORK_STORAGE, osgiCacheStorage.toFile().getAbsolutePath());
+        if (frameworkOptionsProcessor.bootDelegationValue() != null) {
+            if (frameworkOptionsProcessor.printOsgiInfo()) {
+                System.out.println("Boot delegation: " + frameworkOptionsProcessor.bootDelegationValue());
+            }
+            bundleConfigurations.put(Constants.FRAMEWORK_BOOTDELEGATION, frameworkOptionsProcessor.bootDelegationValue());
+        }
+        Iterator<FrameworkFactory> factories = loader.iterator();
+        if (factories.hasNext()) {
+            // we just want the first found
+            return factories.next().newFramework(bundleConfigurations);
+        } else {
+            throw new InternalError("ServiceLoader cannot find a FrameworkFactory!");
+        }   
+    }
+
+    private void loadBootstrapBundles(Framework framework) throws BundleException, InterruptedException {
+        Properties bootstrapProps = new Properties();
+        // the properties file should be in the same package as this class
+        InputStream res = getClass().getResourceAsStream(PROPS_FILE);
+        if (res != null) {
+            try {
+                bootstrapProps.load(res);
+            } catch (IOException e) {
+                throw new RuntimeException("Could not load bootstrap bundle properties.", e);
+            }
+        }
+        String[] bundles = bootstrapProps.getProperty(BUNDLELIST).split(",");
+        List<String> locations = new ArrayList<>();
+        for (String bundle : bundles) {
+            String trimmed = bundle.trim();
+            if (trimmed != null && trimmed.length() > 0) {
+                String location = actualLocation(trimmed);
+                locations.add(location);
+            }
+        }
+        BundleManager.preLoadBundles(framework, locations, frameworkOptionsProcessor.printOsgiInfo());
+    }
+
+    private void setLoaderVerbosity(Framework framework) throws InterruptedException {
+        Object loader = getService(framework, BundleManager.class.getName());
+        callVoidReflectedMethod(loader, "setPrintOSGiInfo", frameworkOptionsProcessor.printOsgiInfo());
+    }
+
+    private void setIgnoreBundleVersions(Framework framework) throws InterruptedException {
+        Object loader = getService(framework, BundleManager.class.getName());
+        callVoidReflectedMethod(loader, "setIgnoreBundleVersions", frameworkOptionsProcessor.ignoreBundleVersions());
+    }
+
+    private void runLauncher(Framework framework, String[] args) throws InterruptedException {
+        Object launcher = getService(framework, Launcher.class.getName());
+        callVoidReflectedMethod(launcher, "run", args, false);
+    }
+
+    private Object getService(Framework framework, String name) throws InterruptedException {
+        Object service = null;
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        ServiceTracker tracker = new ServiceTracker(framework.getBundleContext(), name, null);
+        tracker.open();
+        service = tracker.waitForService(0);
+        tracker.close();
+        return service;
+    }
+
+    /**
+     * Call {@code object}.{@code name} with {@code args} as the arguments. The
+     * return value is ignored. The types of the method arguments must exactly
+     * match the types of the supplied arguments, but primitives are used unboxed.
+     */
+    private void callVoidReflectedMethod(Object object, String name, Object... args) {
+        Class<?> clazz = object.getClass();
+        Class<?>[] classes = new Class<?>[args.length];
+        for (int i = 0; i < args.length; i++) {
+            classes[i] = preferPrimitiveClass(args[i].getClass());
+        }
+
+        try {
+            Method m = clazz.getMethod(name, classes);
+            m.invoke(object, args);
+        } catch (ReflectiveOperationException e) {
+            throw new AssertionError("Unable to call '" + name + "' method on object " + object, e);
+        }
+    }
+
+    private static <T> Class<T> preferPrimitiveClass(Class<T> boxedPrimitive) {
+        HashMap<Class<?>, Class<?>> map = new HashMap<>();
+        map.put(Byte.class, byte.class);
+        map.put(Short.class, short.class);
+        map.put(Integer.class, int.class);
+        map.put(Long.class, long.class);
+        map.put(Float.class, float.class);
+        map.put(Double.class, double.class);
+        map.put(Boolean.class, boolean.class);
+        map.put(Character.class, char.class);
+
+        if (map.containsKey(boxedPrimitive)) {
+            return (Class<T>) map.get(boxedPrimitive);
+        } else  {
+            return boxedPrimitive;
+        }
+    }
+
+    // Resolve symlinks completely; This makes the behaviour of this class
+    // consistent with BundleManagerImpl and avoid problems where two different
+    // files across bootstrap and later code provide the same bundle symbolic
+    // name version
+    private String actualLocation(String resourceName) {
+        File file = new File(paths.getSystemLibRoot(), resourceName);
+        try {
+            return file.getCanonicalFile().toURI().toString();
+        } catch (IOException e) {
+            // okay, lets not canonicalize the path
+            return file.toURI().toString();
+        }
+    }
+
+}
+
--- a/main/src/main/resources/com/redhat/thermostat/main/impl/bootstrapbundles.properties	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-bundles=thermostat-shared-config-${project.version}.jar, \
-        thermostat-keyring-${project.version}.jar, \
-        thermostat-storage-core-${project.version}.jar, \
-        thermostat-common-core-${project.version}.jar, \
-        thermostat-plugin-validator-${project.version}.jar, \
-        thermostat-launcher-${project.version}.jar, \
-        jline-${jline.version}.jar, \
-        commons-cli-${commons-cli.version}.jar, \
-        org.apache.felix.scr-${felix.scr.version}.jar, \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/main/resources/com/redhat/thermostat/main/internal/bootstrapbundles.properties	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,9 @@
+bundles=thermostat-shared-config-${project.version}.jar, \
+        thermostat-keyring-${project.version}.jar, \
+        thermostat-storage-core-${project.version}.jar, \
+        thermostat-common-core-${project.version}.jar, \
+        thermostat-plugin-validator-${project.version}.jar, \
+        thermostat-launcher-${project.version}.jar, \
+        jline-${jline.version}.jar, \
+        commons-cli-${commons-cli.version}.jar, \
+        org.apache.felix.scr-${felix.scr.version}.jar, \
--- a/main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/main/src/test/java/com/redhat/thermostat/main/ThermostatTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -49,8 +49,8 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 
-import com.redhat.thermostat.main.impl.FrameworkOptionsProcessor;
-import com.redhat.thermostat.main.impl.FrameworkProvider;
+import com.redhat.thermostat.main.internal.FrameworkOptionsProcessor;
+import com.redhat.thermostat.main.internal.FrameworkProvider;
 import com.redhat.thermostat.shared.config.CommonPaths;
 
 public class ThermostatTest {
--- a/main/src/test/java/com/redhat/thermostat/main/impl/FrameworkOptionsProcessorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/*
- * Copyright 2012-2016 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.main.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-
-import org.junit.Test;
-
-public class FrameworkOptionsProcessorTest {
-
-    @Test
-    public void verifyDefaults() {
-        String[] args = new String[] { "help" };
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
-        assertFalse(opts.printOsgiInfo());
-        assertFalse(opts.ignoreBundleVersions());
-        assertTrue(opts.bootDelegationValue().contains("com.redhat.thermostat.vm.profiler"));
-        assertEquals(Arrays.asList("help"), Arrays.asList(opts.getOtherOptions()));
-    }
-
-    @Test
-    public void canGetPrintOsgiInfo() {
-        String[] args = new String[] { "--print-osgi-info", "help" };
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
-        assertTrue(opts.printOsgiInfo());
-        assertEquals(Arrays.asList("help"),
-                Arrays.asList(opts.getOtherOptions()));
-    }
-
-    @Test
-    public void canGetBootDelegation() {
-        String[] args = new String[] { "--boot-delegation=foo", "help" };
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
-        assertEquals("foo", opts.bootDelegationValue());
-        assertEquals(Arrays.asList("help"),
-                Arrays.asList(opts.getOtherOptions()));
-    }
-
-    @Test
-    public void canGetIgnoreBundleVersion() {
-        String[] args = new String[] { "--ignore-bundle-versions", "help" };
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
-        assertTrue(opts.ignoreBundleVersions());
-        assertEquals(Arrays.asList("help"),
-                Arrays.asList(opts.getOtherOptions()));
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void emtpyStringForBootDelegationThrowsException() {
-        String[] args = new String[] { "--boot-delegation=", "help" };
-        // This is expected to fail since the boot delegation value is an empty
-        // string.
-        new FrameworkOptionsProcessor(args);
-    }
-}
--- a/main/src/test/java/com/redhat/thermostat/main/impl/FrameworkProviderTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,272 +0,0 @@
-/*
- * Copyright 2012-2016 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.main.impl;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.Constants;
-import org.osgi.framework.launch.Framework;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import com.redhat.thermostat.launcher.BundleInformation;
-import com.redhat.thermostat.launcher.BundleManager;
-import com.redhat.thermostat.launcher.Launcher;
-import com.redhat.thermostat.shared.config.CommonPaths;
-import com.redhat.thermostat.shared.config.internal.CommonPathsImpl;
-
-@RunWith(PowerMockRunner.class)
-@PrepareForTest({FrameworkProvider.class})
-public class FrameworkProviderTest {
-
-    private static final String THERMOSTAT_HOME_PROPERTY = "THERMOSTAT_HOME";
-    private static final String USER_THERMOSTAT_HOME_PROPERTY = "USER_THERMOSTAT_HOME";
-
-    private BundleContext mockContext;
-
-    private Framework framework;
-
-    private FakeBundleManager bundleManager;
-    private Launcher launcher;
-    private CommonPaths paths;
-    private String savedHome, savedUserHome;
-
-    static class FakeBundleManager extends BundleManager /* extends BundleManagerImpl */ {
-
-        private boolean printOSGiInfo;
-        private boolean ignoreBundleVersion;
-
-        @Override
-        public void loadBundlesByName(List<BundleInformation> bundles) throws BundleException, IOException {
-            // do nothing
-        }
-
-        @Override
-        public CommonPaths getCommonPaths() {
-            return null;
-        }
-
-        // @Override
-        public void setPrintOSGiInfo(boolean print) {
-            this.printOSGiInfo = print;
-        }
-
-        // @Override
-        public void setIgnoreBundleVersions(boolean ignore) {
-            this.ignoreBundleVersion = ignore;
-        }
-
-    }
-
-    @SuppressWarnings("rawtypes")
-    @Before
-    public void setUp() throws Exception {
-        Path tempDir;
-        tempDir = Files.createTempDirectory("FrameworkProviderTest");
-        tempDir.toFile().deleteOnExit();
-        savedHome = System.setProperty(THERMOSTAT_HOME_PROPERTY, tempDir.toString());
-        savedUserHome = System.setProperty(USER_THERMOSTAT_HOME_PROPERTY, tempDir.toString());
-        paths = new CommonPathsImpl();
-
-        File tempEtc = new File(tempDir.toFile(), "etc");
-        tempEtc.mkdirs();
-        tempEtc.deleteOnExit();
-
-        File tempProps = new File(tempEtc, "osgi-export.properties");
-        tempProps.createNewFile();
-        tempProps.deleteOnExit();
-
-        File tempBundleProps = new File(tempEtc, "bundles.properties");
-        tempBundleProps.createNewFile();
-        tempBundleProps.deleteOnExit();
-
-        File tempLibs = new File(tempDir.toFile(), "libs");
-        tempLibs.mkdirs();
-        tempLibs.deleteOnExit();
-
-        mockContext = mock(BundleContext.class);
-
-        framework = mock(Framework.class);
-        TestFrameworkFactory.setFramework(framework);
-        when(framework.getBundleContext()).thenReturn(mockContext);
-
-        Bundle mockBundle = mock(Bundle.class);
-        when(mockContext.installBundle(any(String.class))).thenReturn(mockBundle);
-        when(mockBundle.getHeaders()).thenReturn(new Hashtable<String, String>());
-        ServiceTracker registryTracker = mock(ServiceTracker.class);
-        PowerMockito
-                .whenNew(ServiceTracker.class)
-                .withParameterTypes(BundleContext.class, String.class,
-                        ServiceTrackerCustomizer.class)
-                .withArguments(any(BundleContext.class),
-                        eq(BundleManager.class.getName()), any(ServiceTrackerCustomizer.class))
-                .thenReturn(registryTracker);
-        bundleManager = new FakeBundleManager();
-        when(registryTracker.waitForService(0)).thenReturn(bundleManager);
-        ServiceTracker launcherTracker = mock(ServiceTracker.class);
-        launcher = mock(Launcher.class);
-        PowerMockito
-                .whenNew(ServiceTracker.class)
-                .withParameterTypes(BundleContext.class, String.class,
-                        ServiceTrackerCustomizer.class)
-                .withArguments(any(BundleContext.class),
-                        eq(Launcher.class.getName()),
-                        any(ServiceTrackerCustomizer.class))
-                .thenReturn(launcherTracker);
-        when(launcherTracker.waitForService(0))
-                .thenReturn(launcher);
-
-        Path osgiDir;
-
-        osgiDir = tempDir.resolve("osgi-cache");
-        osgiDir.toFile().mkdirs();
-        osgiDir.toFile().deleteOnExit();
-        assertTrue(osgiDir.toFile().exists());
-    }
-
-    @After
-    public void clearSystemProperties() {
-        if (savedHome == null) {
-            System.clearProperty(THERMOSTAT_HOME_PROPERTY);
-        } else {
-            System.setProperty(THERMOSTAT_HOME_PROPERTY, savedHome);
-            savedHome = null;
-        }
-        if (savedUserHome == null) {
-            System.clearProperty(USER_THERMOSTAT_HOME_PROPERTY);
-        } else {
-            System.setProperty(USER_THERMOSTAT_HOME_PROPERTY, savedUserHome);
-            savedUserHome = null;
-        }
-        paths = null;
-    }
-
-    @Test
-    public void testStartRunsOSGiFramework() throws Exception {
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(new String[] {});
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        verify(framework).init();
-        verify(framework).start();
-    }
-
-    @Test
-    public void testStartRunsLauncher() throws Exception {
-        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(new String[] {});
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        verify(launcher).run(new String[] {}, false);
-    }
-
-    @Test
-    public void testPrintOSGiInfoParameterIsPassedToBundleManager() {
-        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
-        when(opts.printOsgiInfo()).thenReturn(true);
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        assertEquals(true, bundleManager.printOSGiInfo);
-    }
-
-    @Test
-    public void testIgnoreBundleVersionsParameterIsPassedToBundleManager() {
-        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
-        when(opts.ignoreBundleVersions()).thenReturn(true);
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        assertEquals(true, bundleManager.ignoreBundleVersion);
-    }
-
-    @Test
-    public void testNullBootDelegationIsNotSetInConfiguration() {
-        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
-        when(opts.bootDelegationValue()).thenReturn(null);
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        Map<String, String> config = TestFrameworkFactory.getConfig();
-        assertFalse(config.containsKey(Constants.FRAMEWORK_BOOTDELEGATION));
-    }
-
-    @Test
-    public void testPackagesListedInBootDelegationArePassedToFramework() {
-        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
-        when(opts.bootDelegationValue()).thenReturn("foo");
-        FrameworkProvider provider = new FrameworkProvider(paths, opts);
-
-        provider.start(new String[] {});
-
-        Map<String, String> config = TestFrameworkFactory.getConfig();
-        assertEquals("foo", config.get(Constants.FRAMEWORK_BOOTDELEGATION));
-    }
-
-}
--- a/main/src/test/java/com/redhat/thermostat/main/impl/TestFrameworkFactory.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Copyright 2012-2016 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.main.impl;
-
-import java.util.Map;
-
-import org.osgi.framework.launch.Framework;
-import org.osgi.framework.launch.FrameworkFactory;
-
-/** Registered using ServiceLoader */
-public class TestFrameworkFactory implements FrameworkFactory {
-
-    private static Framework framework;
-    private static Map<String, String> config;
-
-    public static void setFramework(Framework framework) {
-        TestFrameworkFactory.framework = framework;
-    }
-
-    public static Map<String,String> getConfig() {
-        return config;
-    }
-
-    // NOTE: For some unknown reason, this doesn't compile when declared
-    // as newFramework(Map<String,String> config). At least not in Maven.
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    @Override
-    public Framework newFramework(Map config) {
-        TestFrameworkFactory.config = config;
-        return framework;
-    }
-
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/test/java/com/redhat/thermostat/main/internal/FrameworkOptionsProcessorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2012-2016 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.main.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.main.internal.FrameworkOptionsProcessor;
+
+public class FrameworkOptionsProcessorTest {
+
+    @Test
+    public void verifyDefaults() {
+        String[] args = new String[] { "help" };
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
+        assertFalse(opts.printOsgiInfo());
+        assertFalse(opts.ignoreBundleVersions());
+        assertTrue(opts.bootDelegationValue().contains("com.redhat.thermostat.vm.profiler"));
+        assertEquals(Arrays.asList("help"), Arrays.asList(opts.getOtherOptions()));
+    }
+
+    @Test
+    public void canGetPrintOsgiInfo() {
+        String[] args = new String[] { "--print-osgi-info", "help" };
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
+        assertTrue(opts.printOsgiInfo());
+        assertEquals(Arrays.asList("help"),
+                Arrays.asList(opts.getOtherOptions()));
+    }
+
+    @Test
+    public void canGetBootDelegation() {
+        String[] args = new String[] { "--boot-delegation=foo", "help" };
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
+        assertEquals("foo", opts.bootDelegationValue());
+        assertEquals(Arrays.asList("help"),
+                Arrays.asList(opts.getOtherOptions()));
+    }
+
+    @Test
+    public void canGetIgnoreBundleVersion() {
+        String[] args = new String[] { "--ignore-bundle-versions", "help" };
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(args);
+        assertTrue(opts.ignoreBundleVersions());
+        assertEquals(Arrays.asList("help"),
+                Arrays.asList(opts.getOtherOptions()));
+    }
+
+    @Test(expected = RuntimeException.class)
+    public void emtpyStringForBootDelegationThrowsException() {
+        String[] args = new String[] { "--boot-delegation=", "help" };
+        // This is expected to fail since the boot delegation value is an empty
+        // string.
+        new FrameworkOptionsProcessor(args);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/test/java/com/redhat/thermostat/main/internal/FrameworkProviderTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2012-2016 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.main.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import com.redhat.thermostat.launcher.BundleInformation;
+import com.redhat.thermostat.launcher.BundleManager;
+import com.redhat.thermostat.launcher.Launcher;
+import com.redhat.thermostat.main.internal.FrameworkOptionsProcessor;
+import com.redhat.thermostat.main.internal.FrameworkProvider;
+import com.redhat.thermostat.shared.config.CommonPaths;
+import com.redhat.thermostat.shared.config.internal.CommonPathsImpl;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({FrameworkProvider.class})
+public class FrameworkProviderTest {
+
+    private static final String THERMOSTAT_HOME_PROPERTY = "THERMOSTAT_HOME";
+    private static final String USER_THERMOSTAT_HOME_PROPERTY = "USER_THERMOSTAT_HOME";
+
+    private BundleContext mockContext;
+
+    private Framework framework;
+
+    private FakeBundleManager bundleManager;
+    private Launcher launcher;
+    private CommonPaths paths;
+    private String savedHome, savedUserHome;
+
+    static class FakeBundleManager extends BundleManager /* extends BundleManagerImpl */ {
+
+        private boolean printOSGiInfo;
+        private boolean ignoreBundleVersion;
+
+        @Override
+        public void loadBundlesByName(List<BundleInformation> bundles) throws BundleException, IOException {
+            // do nothing
+        }
+
+        @Override
+        public CommonPaths getCommonPaths() {
+            return null;
+        }
+
+        // @Override
+        public void setPrintOSGiInfo(boolean print) {
+            this.printOSGiInfo = print;
+        }
+
+        // @Override
+        public void setIgnoreBundleVersions(boolean ignore) {
+            this.ignoreBundleVersion = ignore;
+        }
+
+    }
+
+    @SuppressWarnings("rawtypes")
+    @Before
+    public void setUp() throws Exception {
+        Path tempDir;
+        tempDir = Files.createTempDirectory("FrameworkProviderTest");
+        tempDir.toFile().deleteOnExit();
+        savedHome = System.setProperty(THERMOSTAT_HOME_PROPERTY, tempDir.toString());
+        savedUserHome = System.setProperty(USER_THERMOSTAT_HOME_PROPERTY, tempDir.toString());
+        paths = new CommonPathsImpl();
+
+        File tempEtc = new File(tempDir.toFile(), "etc");
+        tempEtc.mkdirs();
+        tempEtc.deleteOnExit();
+
+        File tempProps = new File(tempEtc, "osgi-export.properties");
+        tempProps.createNewFile();
+        tempProps.deleteOnExit();
+
+        File tempBundleProps = new File(tempEtc, "bundles.properties");
+        tempBundleProps.createNewFile();
+        tempBundleProps.deleteOnExit();
+
+        File tempLibs = new File(tempDir.toFile(), "libs");
+        tempLibs.mkdirs();
+        tempLibs.deleteOnExit();
+
+        mockContext = mock(BundleContext.class);
+
+        framework = mock(Framework.class);
+        TestFrameworkFactory.setFramework(framework);
+        when(framework.getBundleContext()).thenReturn(mockContext);
+
+        Bundle mockBundle = mock(Bundle.class);
+        when(mockContext.installBundle(any(String.class))).thenReturn(mockBundle);
+        when(mockBundle.getHeaders()).thenReturn(new Hashtable<String, String>());
+        ServiceTracker registryTracker = mock(ServiceTracker.class);
+        PowerMockito
+                .whenNew(ServiceTracker.class)
+                .withParameterTypes(BundleContext.class, String.class,
+                        ServiceTrackerCustomizer.class)
+                .withArguments(any(BundleContext.class),
+                        eq(BundleManager.class.getName()), any(ServiceTrackerCustomizer.class))
+                .thenReturn(registryTracker);
+        bundleManager = new FakeBundleManager();
+        when(registryTracker.waitForService(0)).thenReturn(bundleManager);
+        ServiceTracker launcherTracker = mock(ServiceTracker.class);
+        launcher = mock(Launcher.class);
+        PowerMockito
+                .whenNew(ServiceTracker.class)
+                .withParameterTypes(BundleContext.class, String.class,
+                        ServiceTrackerCustomizer.class)
+                .withArguments(any(BundleContext.class),
+                        eq(Launcher.class.getName()),
+                        any(ServiceTrackerCustomizer.class))
+                .thenReturn(launcherTracker);
+        when(launcherTracker.waitForService(0))
+                .thenReturn(launcher);
+
+        Path osgiDir;
+
+        osgiDir = tempDir.resolve("osgi-cache");
+        osgiDir.toFile().mkdirs();
+        osgiDir.toFile().deleteOnExit();
+        assertTrue(osgiDir.toFile().exists());
+    }
+
+    @After
+    public void clearSystemProperties() {
+        if (savedHome == null) {
+            System.clearProperty(THERMOSTAT_HOME_PROPERTY);
+        } else {
+            System.setProperty(THERMOSTAT_HOME_PROPERTY, savedHome);
+            savedHome = null;
+        }
+        if (savedUserHome == null) {
+            System.clearProperty(USER_THERMOSTAT_HOME_PROPERTY);
+        } else {
+            System.setProperty(USER_THERMOSTAT_HOME_PROPERTY, savedUserHome);
+            savedUserHome = null;
+        }
+        paths = null;
+    }
+
+    @Test
+    public void testStartRunsOSGiFramework() throws Exception {
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(new String[] {});
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        verify(framework).init();
+        verify(framework).start();
+    }
+
+    @Test
+    public void testStartRunsLauncher() throws Exception {
+        FrameworkOptionsProcessor opts = new FrameworkOptionsProcessor(new String[] {});
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        verify(launcher).run(new String[] {}, false);
+    }
+
+    @Test
+    public void testPrintOSGiInfoParameterIsPassedToBundleManager() {
+        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
+        when(opts.printOsgiInfo()).thenReturn(true);
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        assertEquals(true, bundleManager.printOSGiInfo);
+    }
+
+    @Test
+    public void testIgnoreBundleVersionsParameterIsPassedToBundleManager() {
+        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
+        when(opts.ignoreBundleVersions()).thenReturn(true);
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        assertEquals(true, bundleManager.ignoreBundleVersion);
+    }
+
+    @Test
+    public void testNullBootDelegationIsNotSetInConfiguration() {
+        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
+        when(opts.bootDelegationValue()).thenReturn(null);
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        Map<String, String> config = TestFrameworkFactory.getConfig();
+        assertFalse(config.containsKey(Constants.FRAMEWORK_BOOTDELEGATION));
+    }
+
+    @Test
+    public void testPackagesListedInBootDelegationArePassedToFramework() {
+        FrameworkOptionsProcessor opts = mock(FrameworkOptionsProcessor.class);
+        when(opts.bootDelegationValue()).thenReturn("foo");
+        FrameworkProvider provider = new FrameworkProvider(paths, opts);
+
+        provider.start(new String[] {});
+
+        Map<String, String> config = TestFrameworkFactory.getConfig();
+        assertEquals("foo", config.get(Constants.FRAMEWORK_BOOTDELEGATION));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/src/test/java/com/redhat/thermostat/main/internal/TestFrameworkFactory.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012-2016 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.main.internal;
+
+import java.util.Map;
+
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+
+/** Registered using ServiceLoader */
+public class TestFrameworkFactory implements FrameworkFactory {
+
+    private static Framework framework;
+    private static Map<String, String> config;
+
+    public static void setFramework(Framework framework) {
+        TestFrameworkFactory.framework = framework;
+    }
+
+    public static Map<String,String> getConfig() {
+        return config;
+    }
+
+    // NOTE: For some unknown reason, this doesn't compile when declared
+    // as newFramework(Map<String,String> config). At least not in Maven.
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    @Override
+    public Framework newFramework(Map config) {
+        TestFrameworkFactory.config = config;
+        return framework;
+    }
+
+}
+
--- a/main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory	Wed Apr 06 14:52:26 2016 +0200
+++ b/main/src/test/resources/META-INF/services/org.osgi.framework.launch.FrameworkFactory	Fri Apr 08 13:18:48 2016 +0200
@@ -1,5 +1,5 @@
 #
-# Copyright 2012-2014 Red Hat, Inc.
+# Copyright 2012-2016 Red Hat, Inc.
 #
 # This file is part of Thermostat.
 #
@@ -34,5 +34,5 @@
 # to do so, delete this exception statement from your version.
 #
 
-com.redhat.thermostat.main.impl.TestFrameworkFactory
+com.redhat.thermostat.main.internal.TestFrameworkFactory
 
--- a/thread/client-common/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-common/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -124,7 +124,7 @@
               com.redhat.thermostat.thread.client.common.collector,
             </Export-Package>
             <Private-Package>
-              com.redhat.thermostat.thread.client.common.collector.impl,
+              com.redhat.thermostat.thread.client.common.collector.internal,
               com.redhat.thermostat.thread.client.common.osgi,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorFactoryImpl.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright 2012-2016 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.collector.impl;
-
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-public class ThreadCollectorFactoryImpl implements ThreadCollectorFactory {
-
-    private BundleContext context;
-    private AgentInfoDAO agentDao;
-    private ThreadDao threadDao;
-    
-    public ThreadCollectorFactoryImpl() {
-        this(FrameworkUtil.getBundle(ThreadCollectorFactoryImpl.class).getBundleContext());
-    }
-    
-    ThreadCollectorFactoryImpl(BundleContext context) {
-        this.context = context;
-    }
-    
-    public void setAgentDao(AgentInfoDAO agentDao) {
-        this.agentDao = agentDao;
-    }
-
-    public void setThreadDao(ThreadDao threadDao) {
-        this.threadDao = threadDao;
-    }
-    
-    @Override
-    public synchronized ThreadCollector getCollector(VmRef reference) {
-        // TODO set the values when the agent/thread dao changes
-        ThreadMXBeanCollector result = new ThreadMXBeanCollector(context, reference);
-        result.setAgentInfoDao(agentDao);
-        result.setThreadDao(threadDao);
-        return result;
-    }
-}
-
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadMXBeanCollector.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,278 +0,0 @@
-/*
- * Copyright 2012-2016 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.collector.impl;
-
-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;
-
-import com.redhat.thermostat.client.command.RequestQueue;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.Request.RequestType;
-import com.redhat.thermostat.common.command.RequestResponseListener;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.core.AgentId;
-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.ThreadHarvestingStatus;
-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;
-
-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);
-
-    private AgentInfoDAO agentDao;
-    private ThreadDao threadDao;
-    private BundleContext context;
-    private VmRef ref;
-
-    public ThreadMXBeanCollector(BundleContext context, VmRef ref) {
-        this.context = context;
-        this.ref = ref;
-    }
-
-    @Override
-    public void setThreadDao(ThreadDao threadDao) {
-        this.threadDao = threadDao;
-    }
-
-    @Override
-    public void setAgentInfoDao(AgentInfoDAO agentDao) {
-        this.agentDao = agentDao;
-    }
-
-    Request createRequest() {
-        AgentId targetId = new AgentId(ref.getHostRef().getAgentId());
-        
-        InetSocketAddress target = agentDao.getAgentInformation(targetId).getRequestQueueAddress();
-        Request harvester = new Request(RequestType.RESPONSE_EXPECTED, target);
-
-        harvester.setReceiver(HarvesterCommand.RECEIVER);
-        
-        return harvester;
-    }
-
-    @Override
-    public boolean startHarvester() {
-        
-        Request harvester = createRequest();
-        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
-        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
-        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
-        harvester.setParameter(HarvesterCommand.VM_PID.name(), String.valueOf(ref.getPid()));
-        
-        return postAndWait(harvester);
-
-    }
-
-    @Override
-    public boolean stopHarvester() {
-        
-        Request harvester = createRequest();
-        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
-        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
-        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
-
-        boolean result = postAndWait(harvester);
-        return result;
-    }
-
-    @Override
-    public boolean isHarvesterCollecting() {
-        ThreadHarvestingStatus status = threadDao.getLatestHarvestingStatus(ref);
-        if (status == null) {
-            return false;
-        }
-        return status.isHarvesting();
-    }
-
-    @Override
-    public List<ThreadSession> getThreadSessions(Range<Long> range) {
-        return threadDao.getSessions(ref, range, ALL, ThreadDao.Sort.ASCENDING);
-    }
-
-    @Override
-    public SessionID getLastThreadSession() {
-        List<ThreadSession> sessions =
-                threadDao.getSessions(ref, FULL_RANGE, FIRST,
-                                      ThreadDao.Sort.DESCENDING);
-        return sessions.isEmpty() ? null : new SessionID(sessions.get(0).getSession());
-    }
-
-    @Override
-    public ThreadSummary getLatestThreadSummary() {
-        List<ThreadSummary> summaries = threadDao.getSummary(ref, FULL_RANGE, FIRST);
-        ThreadSummary summary = null;
-        if (summaries.isEmpty()) {
-            // default to all 0
-            summary = new ThreadSummary();
-        } else {
-            summary = summaries.get(0);
-        }
-
-        return summary;
-    }
-
-    @Override
-    public Range<Long> getThreadRange(SessionID session) {
-
-        final long[] timestamps = new long[2];
-        timestamps[0] = 0l;
-        timestamps[1] = Long.MAX_VALUE;
-
-        threadDao.getThreadStates(ref, session,
-                                  new ResultHandler<ThreadState>() {
-                                      @Override
-                                      public boolean onResult(ThreadState result) {
-                                          timestamps[1] = result.getTimeStamp();
-                                          return false;
-                                      }
-                                  },
-                                  FULL_RANGE, FIRST, ThreadDao.Sort.DESCENDING);
-
-        threadDao.getThreadStates(ref, session,
-                                  new ResultHandler<ThreadState>() {
-                                      @Override
-                                      public boolean onResult(ThreadState result) {
-                                          timestamps[0] = result.getTimeStamp();
-                                          return false;
-                                      }
-                                  },
-                                  FULL_RANGE, FIRST, ThreadDao.Sort.ASCENDING);
-
-        return new Range<>(timestamps[0], timestamps[1]);
-    }
-
-    @Override
-    public void getThreadStates(SessionID session,
-                                ResultHandler<ThreadState> handler,
-                                Range<Long> range)
-    {
-        threadDao.getThreadStates(ref, session, handler, range,
-                                  ALL,
-                                  ThreadDao.Sort.ASCENDING);
-    }
-
-    @Override
-    public List<ThreadSummary> getThreadSummary(Range<Long> range) {
-        List<ThreadSummary> summary = threadDao.getSummary(ref, range, Integer.MAX_VALUE);
-        return summary;
-    }
-
-    @Override
-    public VmDeadLockData getLatestDeadLockData() {
-        return threadDao.loadLatestDeadLockStatus(ref);
-    }
-
-    @Override
-    public void requestDeadLockCheck() {
-        Request harvester = createRequest();
-        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
-        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.FIND_DEADLOCKS.name());
-        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
-        harvester.setParameter(HarvesterCommand.VM_PID.name(), String.valueOf(ref.getPid()));
-
-        postAndWait(harvester);
-    }
-
-    private boolean postAndWait(Request harvester) {
-        final CountDownLatch latch = new CountDownLatch(1);
-        final boolean[] result = new boolean[1];
-
-        harvester.addListener(new RequestResponseListener() {
-            @Override
-            public void fireComplete(Request request, Response response) {
-                switch (response.getType()) {
-                case OK:
-                    result[0] = true;
-                    break;
-                default:
-                    break;
-                }
-                latch.countDown();
-            }
-        });
-
-        try {
-            enqueueRequest(harvester);
-            latch.await();
-        } catch (CommandChannelException e) {
-            logger.log(Level.WARNING, "Failed to enqueue request", e);
-        } catch (InterruptedException ignore) {}
-        return result[0];
-    }
-
-    private void enqueueRequest(Request req) throws CommandChannelException {
-        ServiceReference ref = context.getServiceReference(RequestQueue.class.getName());
-        if (ref == null) {
-            throw new CommandChannelException("Cannot access command channel");
-        }
-        RequestQueue queue = (RequestQueue) context.getService(ref);
-        queue.putRequest(req);
-        context.ungetService(ref);
-    }
-
-    @SuppressWarnings("serial")
-    private class CommandChannelException extends Exception {
-
-        public CommandChannelException(String message) {
-            super(message);
-        }
-
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadCollectorFactoryImpl.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012-2016 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.collector.internal;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadCollectorFactoryImpl implements ThreadCollectorFactory {
+
+    private BundleContext context;
+    private AgentInfoDAO agentDao;
+    private ThreadDao threadDao;
+    
+    public ThreadCollectorFactoryImpl() {
+        this(FrameworkUtil.getBundle(ThreadCollectorFactoryImpl.class).getBundleContext());
+    }
+    
+    ThreadCollectorFactoryImpl(BundleContext context) {
+        this.context = context;
+    }
+    
+    public void setAgentDao(AgentInfoDAO agentDao) {
+        this.agentDao = agentDao;
+    }
+
+    public void setThreadDao(ThreadDao threadDao) {
+        this.threadDao = threadDao;
+    }
+    
+    @Override
+    public synchronized ThreadCollector getCollector(VmRef reference) {
+        // TODO set the values when the agent/thread dao changes
+        ThreadMXBeanCollector result = new ThreadMXBeanCollector(context, reference);
+        result.setAgentInfoDao(agentDao);
+        result.setThreadDao(threadDao);
+        return result;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadMXBeanCollector.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2012-2016 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.collector.internal;
+
+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;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.Request.RequestType;
+import com.redhat.thermostat.common.command.RequestResponseListener;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.AgentId;
+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.ThreadHarvestingStatus;
+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;
+
+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);
+
+    private AgentInfoDAO agentDao;
+    private ThreadDao threadDao;
+    private BundleContext context;
+    private VmRef ref;
+
+    public ThreadMXBeanCollector(BundleContext context, VmRef ref) {
+        this.context = context;
+        this.ref = ref;
+    }
+
+    @Override
+    public void setThreadDao(ThreadDao threadDao) {
+        this.threadDao = threadDao;
+    }
+
+    @Override
+    public void setAgentInfoDao(AgentInfoDAO agentDao) {
+        this.agentDao = agentDao;
+    }
+
+    Request createRequest() {
+        AgentId targetId = new AgentId(ref.getHostRef().getAgentId());
+        
+        InetSocketAddress target = agentDao.getAgentInformation(targetId).getRequestQueueAddress();
+        Request harvester = new Request(RequestType.RESPONSE_EXPECTED, target);
+
+        harvester.setReceiver(HarvesterCommand.RECEIVER);
+        
+        return harvester;
+    }
+
+    @Override
+    public boolean startHarvester() {
+        
+        Request harvester = createRequest();
+        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
+        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
+        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
+        harvester.setParameter(HarvesterCommand.VM_PID.name(), String.valueOf(ref.getPid()));
+        
+        return postAndWait(harvester);
+
+    }
+
+    @Override
+    public boolean stopHarvester() {
+        
+        Request harvester = createRequest();
+        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
+        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
+        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
+
+        boolean result = postAndWait(harvester);
+        return result;
+    }
+
+    @Override
+    public boolean isHarvesterCollecting() {
+        ThreadHarvestingStatus status = threadDao.getLatestHarvestingStatus(ref);
+        if (status == null) {
+            return false;
+        }
+        return status.isHarvesting();
+    }
+
+    @Override
+    public List<ThreadSession> getThreadSessions(Range<Long> range) {
+        return threadDao.getSessions(ref, range, ALL, ThreadDao.Sort.ASCENDING);
+    }
+
+    @Override
+    public SessionID getLastThreadSession() {
+        List<ThreadSession> sessions =
+                threadDao.getSessions(ref, FULL_RANGE, FIRST,
+                                      ThreadDao.Sort.DESCENDING);
+        return sessions.isEmpty() ? null : new SessionID(sessions.get(0).getSession());
+    }
+
+    @Override
+    public ThreadSummary getLatestThreadSummary() {
+        List<ThreadSummary> summaries = threadDao.getSummary(ref, FULL_RANGE, FIRST);
+        ThreadSummary summary = null;
+        if (summaries.isEmpty()) {
+            // default to all 0
+            summary = new ThreadSummary();
+        } else {
+            summary = summaries.get(0);
+        }
+
+        return summary;
+    }
+
+    @Override
+    public Range<Long> getThreadRange(SessionID session) {
+
+        final long[] timestamps = new long[2];
+        timestamps[0] = 0l;
+        timestamps[1] = Long.MAX_VALUE;
+
+        threadDao.getThreadStates(ref, session,
+                                  new ResultHandler<ThreadState>() {
+                                      @Override
+                                      public boolean onResult(ThreadState result) {
+                                          timestamps[1] = result.getTimeStamp();
+                                          return false;
+                                      }
+                                  },
+                                  FULL_RANGE, FIRST, ThreadDao.Sort.DESCENDING);
+
+        threadDao.getThreadStates(ref, session,
+                                  new ResultHandler<ThreadState>() {
+                                      @Override
+                                      public boolean onResult(ThreadState result) {
+                                          timestamps[0] = result.getTimeStamp();
+                                          return false;
+                                      }
+                                  },
+                                  FULL_RANGE, FIRST, ThreadDao.Sort.ASCENDING);
+
+        return new Range<>(timestamps[0], timestamps[1]);
+    }
+
+    @Override
+    public void getThreadStates(SessionID session,
+                                ResultHandler<ThreadState> handler,
+                                Range<Long> range)
+    {
+        threadDao.getThreadStates(ref, session, handler, range,
+                                  ALL,
+                                  ThreadDao.Sort.ASCENDING);
+    }
+
+    @Override
+    public List<ThreadSummary> getThreadSummary(Range<Long> range) {
+        List<ThreadSummary> summary = threadDao.getSummary(ref, range, Integer.MAX_VALUE);
+        return summary;
+    }
+
+    @Override
+    public VmDeadLockData getLatestDeadLockData() {
+        return threadDao.loadLatestDeadLockStatus(ref);
+    }
+
+    @Override
+    public void requestDeadLockCheck() {
+        Request harvester = createRequest();
+        harvester.setParameter(Request.ACTION, CMD_CHANNEL_ACTION_NAME);
+        harvester.setParameter(HarvesterCommand.class.getName(), HarvesterCommand.FIND_DEADLOCKS.name());
+        harvester.setParameter(HarvesterCommand.VM_ID.name(), ref.getVmId());
+        harvester.setParameter(HarvesterCommand.VM_PID.name(), String.valueOf(ref.getPid()));
+
+        postAndWait(harvester);
+    }
+
+    private boolean postAndWait(Request harvester) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        final boolean[] result = new boolean[1];
+
+        harvester.addListener(new RequestResponseListener() {
+            @Override
+            public void fireComplete(Request request, Response response) {
+                switch (response.getType()) {
+                case OK:
+                    result[0] = true;
+                    break;
+                default:
+                    break;
+                }
+                latch.countDown();
+            }
+        });
+
+        try {
+            enqueueRequest(harvester);
+            latch.await();
+        } catch (CommandChannelException e) {
+            logger.log(Level.WARNING, "Failed to enqueue request", e);
+        } catch (InterruptedException ignore) {}
+        return result[0];
+    }
+
+    private void enqueueRequest(Request req) throws CommandChannelException {
+        ServiceReference ref = context.getServiceReference(RequestQueue.class.getName());
+        if (ref == null) {
+            throw new CommandChannelException("Cannot access command channel");
+        }
+        RequestQueue queue = (RequestQueue) context.getService(ref);
+        queue.putRequest(req);
+        context.ungetService(ref);
+    }
+
+    @SuppressWarnings("serial")
+    private class CommandChannelException extends Exception {
+
+        public CommandChannelException(String message) {
+            super(message);
+        }
+
+    }
+}
+
--- a/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/osgi/Activator.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-common/src/main/java/com/redhat/thermostat/thread/client/common/osgi/Activator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -43,7 +43,7 @@
 
 import com.redhat.thermostat.storage.dao.AgentInfoDAO;
 import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
-import com.redhat.thermostat.thread.client.common.collector.impl.ThreadCollectorFactoryImpl;
+import com.redhat.thermostat.thread.client.common.collector.internal.ThreadCollectorFactoryImpl;
 import com.redhat.thermostat.thread.dao.ThreadDao;
 
 public class Activator implements BundleActivator {
--- a/thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorFactoryImplTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2016 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.collector.impl;
-
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.mock;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.testutils.StubBundleContext;
-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.collector.impl.ThreadCollectorFactoryImpl;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-public class ThreadCollectorFactoryImplTest {
-
-    @Test
-    public void testThreadCollectorFactory() {
-        StubBundleContext context = new StubBundleContext();
-        VmRef reference = mock(VmRef.class);
-
-        ThreadCollectorFactory factory = new ThreadCollectorFactoryImpl(context);
-        ThreadCollector collector = factory.getCollector(reference);
-        assertNotNull(collector);
-    }
-
-    @Test
-    public void testThreadCollectorFactoryWithAgentAndThreadDaos() {
-        StubBundleContext context = new StubBundleContext();
-        AgentInfoDAO agentDao = mock(AgentInfoDAO.class);
-        ThreadDao threadDao = mock(ThreadDao.class);
-        VmRef reference = mock(VmRef.class);
-
-        ThreadCollectorFactory factory = new ThreadCollectorFactoryImpl(context);
-        factory.setAgentDao(agentDao);
-        factory.setThreadDao(threadDao);
-        ThreadCollector collector = factory.getCollector(reference);
-        assertNotNull(collector);
-    }
-}
-
--- a/thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/impl/ThreadCollectorTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,223 +0,0 @@
-/*
- * Copyright 2012-2016 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.collector.impl;
-
-import com.redhat.thermostat.client.command.RequestQueue;
-import com.redhat.thermostat.common.command.Request;
-import com.redhat.thermostat.common.command.RequestResponseListener;
-import com.redhat.thermostat.common.command.Response;
-import com.redhat.thermostat.common.command.Response.ResponseType;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.AgentInfoDAO;
-import com.redhat.thermostat.testutils.StubBundleContext;
-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.ThreadHarvestingStatus;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class ThreadCollectorTest {
-    
-    private StubBundleContext context;
-    private ThreadDao threadDao;
-    private VmRef reference;
-    private Request request;
-    private AgentInfoDAO agentDao;
-
-    @Before
-    public void setup() {
-        context = new StubBundleContext();
-        request = mock(Request.class);
-        agentDao = mock(AgentInfoDAO.class);
-        threadDao = mock(ThreadDao.class);
-        reference = mock(VmRef.class);
-        when(reference.getVmId()).thenReturn("00101010");
-        
-        final Response response = mock(Response.class);
-        when(response.getType()).thenReturn(ResponseType.OK);
-        
-        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
-        doNothing().when(request).addListener(captor.capture());
-    }
-
-    @Test
-    public void testHarvesterCollecting() {
-        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
-        when(status.isHarvesting()).thenReturn(true);
-        ThreadCollector collector = new ThreadMXBeanCollector(context, reference);
-        when(threadDao.getLatestHarvestingStatus(reference)).thenReturn(status);
-
-        collector.setThreadDao(threadDao);
-
-        assertTrue(collector.isHarvesterCollecting());
-    }
-    
-    @Test
-    public void testStart() {
-        final RequestQueue requestQueue = mock(RequestQueue.class);
-        context.registerService(RequestQueue.class, requestQueue, null);
-        
-        final Response response = mock(Response.class);
-        when(response.getType()).thenReturn(ResponseType.OK);
-        
-        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
-        doNothing().when(request).addListener(captor.capture());
-        
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Request req = (Request) invocation.getArguments()[0];
-                assertSame(request, req);
-                
-                RequestResponseListener listener = captor.getValue();
-                listener.fireComplete(request, response);
-                
-                return null;
-            }
-
-        }).when(requestQueue).putRequest(request);
-        
-        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
-            @Override
-            Request createRequest() {
-                return request;
-            }
-        };
-        collector.setAgentInfoDao(agentDao);
-        collector.setThreadDao(threadDao);
-        
-        collector.startHarvester();
-        
-        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
-        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
-        
-        verify(requestQueue).putRequest(request);
-    }
-    
-    @Test
-    public void testStartNoRequestQueue() {
-        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
-            @Override
-            Request createRequest() {
-                return request;
-            }
-        };
-        collector.setAgentInfoDao(agentDao);
-        collector.setThreadDao(threadDao);
-        
-        boolean result = collector.startHarvester();
-        
-        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
-        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
-        
-        assertFalse(result);
-    }
-
-    @Test
-    public void testStop() {
-        final RequestQueue requestQueue = mock(RequestQueue.class);
-        context.registerService(RequestQueue.class, requestQueue, null);
-        
-        final Response response = mock(Response.class);
-        when(response.getType()).thenReturn(ResponseType.OK);
-        
-        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
-        doNothing().when(request).addListener(captor.capture());
-        
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                Request req = (Request) invocation.getArguments()[0];
-                assertSame(request, req);
-                
-                RequestResponseListener listener = captor.getValue();
-                listener.fireComplete(request, response);
-                
-                return null;
-            }
-
-        }).when(requestQueue).putRequest(request);
-        
-        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
-            @Override
-            Request createRequest() {
-                return request;
-            }
-        };
-        collector.setAgentInfoDao(agentDao);
-        collector.setThreadDao(threadDao);
-        collector.stopHarvester();
-        
-        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
-        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
-        
-        verify(requestQueue).putRequest(request);
-    }
-    
-    @Test
-    public void testStopNoRequestQueue() {
-        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
-            @Override
-            Request createRequest() {
-                return request;
-            }
-        };
-        collector.setAgentInfoDao(agentDao);
-        collector.setThreadDao(threadDao);
-        
-        boolean result = collector.stopHarvester();
-        
-        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
-        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
-        
-        assertFalse(result);
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadCollectorFactoryImplTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012-2016 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.collector.internal;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollectorFactory;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadCollectorFactoryImplTest {
+
+    @Test
+    public void testThreadCollectorFactory() {
+        StubBundleContext context = new StubBundleContext();
+        VmRef reference = mock(VmRef.class);
+
+        ThreadCollectorFactory factory = new ThreadCollectorFactoryImpl(context);
+        ThreadCollector collector = factory.getCollector(reference);
+        assertNotNull(collector);
+    }
+
+    @Test
+    public void testThreadCollectorFactoryWithAgentAndThreadDaos() {
+        StubBundleContext context = new StubBundleContext();
+        AgentInfoDAO agentDao = mock(AgentInfoDAO.class);
+        ThreadDao threadDao = mock(ThreadDao.class);
+        VmRef reference = mock(VmRef.class);
+
+        ThreadCollectorFactory factory = new ThreadCollectorFactoryImpl(context);
+        factory.setAgentDao(agentDao);
+        factory.setThreadDao(threadDao);
+        ThreadCollector collector = factory.getCollector(reference);
+        assertNotNull(collector);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-common/src/test/java/com/redhat/thermostat/thread/client/common/collector/internal/ThreadCollectorTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2012-2016 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.collector.internal;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import com.redhat.thermostat.client.command.RequestQueue;
+import com.redhat.thermostat.common.command.Request;
+import com.redhat.thermostat.common.command.RequestResponseListener;
+import com.redhat.thermostat.common.command.Response;
+import com.redhat.thermostat.common.command.Response.ResponseType;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.AgentInfoDAO;
+import com.redhat.thermostat.testutils.StubBundleContext;
+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.ThreadHarvestingStatus;
+
+public class ThreadCollectorTest {
+    
+    private StubBundleContext context;
+    private ThreadDao threadDao;
+    private VmRef reference;
+    private Request request;
+    private AgentInfoDAO agentDao;
+
+    @Before
+    public void setup() {
+        context = new StubBundleContext();
+        request = mock(Request.class);
+        agentDao = mock(AgentInfoDAO.class);
+        threadDao = mock(ThreadDao.class);
+        reference = mock(VmRef.class);
+        when(reference.getVmId()).thenReturn("00101010");
+        
+        final Response response = mock(Response.class);
+        when(response.getType()).thenReturn(ResponseType.OK);
+        
+        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
+        doNothing().when(request).addListener(captor.capture());
+    }
+
+    @Test
+    public void testHarvesterCollecting() {
+        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
+        when(status.isHarvesting()).thenReturn(true);
+        ThreadCollector collector = new ThreadMXBeanCollector(context, reference);
+        when(threadDao.getLatestHarvestingStatus(reference)).thenReturn(status);
+
+        collector.setThreadDao(threadDao);
+
+        assertTrue(collector.isHarvesterCollecting());
+    }
+    
+    @Test
+    public void testStart() {
+        final RequestQueue requestQueue = mock(RequestQueue.class);
+        context.registerService(RequestQueue.class, requestQueue, null);
+        
+        final Response response = mock(Response.class);
+        when(response.getType()).thenReturn(ResponseType.OK);
+        
+        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
+        doNothing().when(request).addListener(captor.capture());
+        
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Request req = (Request) invocation.getArguments()[0];
+                assertSame(request, req);
+                
+                RequestResponseListener listener = captor.getValue();
+                listener.fireComplete(request, response);
+                
+                return null;
+            }
+
+        }).when(requestQueue).putRequest(request);
+        
+        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
+            @Override
+            Request createRequest() {
+                return request;
+            }
+        };
+        collector.setAgentInfoDao(agentDao);
+        collector.setThreadDao(threadDao);
+        
+        collector.startHarvester();
+        
+        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
+        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
+        
+        verify(requestQueue).putRequest(request);
+    }
+    
+    @Test
+    public void testStartNoRequestQueue() {
+        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
+            @Override
+            Request createRequest() {
+                return request;
+            }
+        };
+        collector.setAgentInfoDao(agentDao);
+        collector.setThreadDao(threadDao);
+        
+        boolean result = collector.startHarvester();
+        
+        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.START.name());
+        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
+        
+        assertFalse(result);
+    }
+
+    @Test
+    public void testStop() {
+        final RequestQueue requestQueue = mock(RequestQueue.class);
+        context.registerService(RequestQueue.class, requestQueue, null);
+        
+        final Response response = mock(Response.class);
+        when(response.getType()).thenReturn(ResponseType.OK);
+        
+        final ArgumentCaptor<RequestResponseListener> captor = ArgumentCaptor.forClass(RequestResponseListener.class);
+        doNothing().when(request).addListener(captor.capture());
+        
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Request req = (Request) invocation.getArguments()[0];
+                assertSame(request, req);
+                
+                RequestResponseListener listener = captor.getValue();
+                listener.fireComplete(request, response);
+                
+                return null;
+            }
+
+        }).when(requestQueue).putRequest(request);
+        
+        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
+            @Override
+            Request createRequest() {
+                return request;
+            }
+        };
+        collector.setAgentInfoDao(agentDao);
+        collector.setThreadDao(threadDao);
+        collector.stopHarvester();
+        
+        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
+        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
+        
+        verify(requestQueue).putRequest(request);
+    }
+    
+    @Test
+    public void testStopNoRequestQueue() {
+        ThreadCollector collector = new ThreadMXBeanCollector(context, reference) {
+            @Override
+            Request createRequest() {
+                return request;
+            }
+        };
+        collector.setAgentInfoDao(agentDao);
+        collector.setThreadDao(threadDao);
+        
+        boolean result = collector.stopHarvester();
+        
+        verify(request).setParameter(HarvesterCommand.class.getName(), HarvesterCommand.STOP.name());
+        verify(request).setParameter(HarvesterCommand.VM_ID.name(), "00101010");
+        
+        assertFalse(result);
+    }
+}
+
--- a/thread/client-controllers/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-controllers/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -131,8 +131,8 @@
             </Export-Package>
             <Private-Package>
               com.redhat.thermostat.thread.client.controller.osgi,
-              com.redhat.thermostat.thread.client.controller.impl,
-              com.redhat.thermostat.thread.client.controller.impl.cache,
+              com.redhat.thermostat.thread.client.controller.internal,
+              com.redhat.thermostat.thread.client.controller.internal.cache,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/CommonController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.client.core.views.BasicView;
-import com.redhat.thermostat.client.core.views.BasicView.Action;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.Timer.SchedulingType;
-import com.redhat.thermostat.thread.model.SessionID;
-
-import java.util.concurrent.TimeUnit;
-
-public abstract class CommonController {
-    
-    protected Timer timer;
-    protected BasicView view;
-
-    protected volatile SessionID session;
-
-    public CommonController(Timer timer, BasicView view) {
-        this.view = view;
-        this.timer = timer;
-    }
-    
-    void initialize() {
-        timer.setInitialDelay(0);
-        timer.setDelay(1000);
-        timer.setTimeUnit(TimeUnit.MILLISECONDS);
-        timer.setSchedulingType(SchedulingType.FIXED_RATE);
-        
-        view.addActionListener(new ActionListener<Action>() {
-            @Override
-            public void actionPerformed(ActionEvent<Action> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                case VISIBLE:
-                    onViewVisible();
-                    timer.start();
-                    break;
-
-                case HIDDEN:
-                    timer.stop();
-                    onViewHidden();
-                    break;
-
-                default:
-                    break;
-                }
-            }
-        });
-    }
-
-    protected void onViewVisible() {}
-    protected void onViewHidden() {}
-
-    public void setSession(SessionID session) {
-        this.session = session;
-    }
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResources.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.shared.locale.Translate;
-
-public enum LocaleResources {
-
-    CONTROLLER_NAME,
-
-    WARNING_CANNOT_DISABLE,
-    WARNING_CANNOT_ENABLE,
-
-    CHECKING_FOR_DEADLOCKS,
-    NO_DEADLOCK_DETECTED,
-
-    STARTING_MONITORING,
-    STOPPING_MONITORING,
-
-    LOADING_SESSION_LIST,
-    ;
-
-    static final String RESOURCE_BUNDLE = "com.redhat.thermostat.thread.client.controller.impl.strings";
-
-    public static Translate<LocaleResources> createLocalizer() {
-        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
-    }
-
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/LockController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.thread.client.common.view.LockView;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-import com.redhat.thermostat.thread.model.LockInfo;
-
-public class LockController extends CommonController {
-
-    private LockInfoDao dao;
-    private VmRef vm;
-
-    public LockController(LockView view, Timer timer, LockInfoDao lockInfoDao, VmRef vm) {
-        super(timer, view);
-        this.dao = lockInfoDao;
-        this.vm = vm;
-        timer.setAction(new LockInfoUpdateAction());
-    }
-
-    class LockInfoUpdateAction implements Runnable {
-        @Override
-        public void run() {
-            LockInfo result = dao.getLatestLockInfo(vm);
-            if (result != null) {
-                ((LockView) view).setLatestLockData(result);
-            }
-        }
-    }
-}
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadCountController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,112 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
-import com.redhat.thermostat.thread.model.ThreadSummary;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.TimeUnit;
-
-class ThreadCountController extends CommonController {
-
-    private LivingDaemonThreadDifferenceChart model;
-    private ThreadCollector collector;
-    
-    public ThreadCountController(ThreadCountView view, ThreadCollector collector, Timer timer) {
-        super(timer, view);
-        
-        this.collector = collector;
-        model = new LivingDaemonThreadDifferenceChart("Living Threads vs. Daemon Threads", "time", "threads",
-                                                      "Living Threads", "Daemon Threads");
-        model.setMaximumItemCount(3600);
-
-        timer.setAction(new ThreadInformationDataCollector());
-    }
-    
-    class ThreadInformationDataCollector implements Runnable {
-
-        private void updateLastSession() {
-
-            ThreadCountView view = (ThreadCountView) ThreadCountController.this.view;
-
-            // load the very latest thread summary
-            ThreadSummary latestSummary = collector.getLatestThreadSummary();
-            Objects.requireNonNull(latestSummary);
-
-            if (latestSummary.getTimeStamp() != 0) {
-                view.setLiveThreads(Long.toString(latestSummary.getCurrentLiveThreads()));
-                view.setDaemonThreads(Long.toString(latestSummary.getCurrentDaemonThreads()));
-            }
-        }
-
-        private void updateChart() {
-
-            ThreadCountView view = (ThreadCountView) ThreadCountController.this.view;
-
-            long now = System.currentTimeMillis();
-            long lastHour = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
-
-            Range<Long> range = new Range<>(lastHour, now);
-
-            boolean updateModel = false;
-            List<ThreadSummary> summaries = collector.getThreadSummary(range);
-            if (summaries.size() != 0) {
-                for (ThreadSummary summary : summaries) {
-                    model.addData(summary.getTimeStamp(), summary.getCurrentLiveThreads(), summary.getCurrentDaemonThreads());
-                }
-                updateModel = true;
-            }
-
-            if (updateModel) {
-                view.updateLivingDaemonTimeline(model);
-            }
-        }
-
-        @Override
-        public void run() {
-            updateLastSession();
-            updateChart();
-        }
-    }    
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,297 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.client.core.controllers.InformationServiceController;
-import com.redhat.thermostat.client.core.progress.ProgressHandle;
-import com.redhat.thermostat.client.core.progress.ProgressNotifier;
-import com.redhat.thermostat.client.core.views.UIComponent;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.locale.LocalizedString;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.thread.client.common.ThreadTableBean;
-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.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.ThreadView.ThreadAction;
-import com.redhat.thermostat.thread.client.controller.impl.cache.AppCache;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadSession;
-
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-public class ThreadInformationController implements InformationServiceController<VmRef> {
-
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-
-    private VmRef ref;
-    private VmInfoDAO vmInfoDAO;
-    private LockInfoDao lockInfoDao;
-
-    private static final Logger logger = LoggingUtils.getLogger(ThreadInformationController.class);
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    
-    private ThreadView view;
-    private ThreadCollector collector;
-
-    private ApplicationService appService;
-
-    private AppCache cache;
-    private ProgressNotifier notifier;
-
-    private CommonController threadTimeline;
-    private CommonController threadTableController;
-    private CommonController threadCountController;
-    private VmDeadLockController deadLockController;
-    private CommonController lockTableController;
-
-    public ThreadInformationController(VmRef ref, ApplicationService appService,
-                                       VmInfoDAO vmInfoDao,
-                                       LockInfoDao lockInfoDao,
-                                       ThreadCollectorFactory collectorFactory, 
-                                       ThreadViewProvider viewFactory,
-                                       ProgressNotifier notifier)
-    {
-        this.appService = appService;
-        this.ref = ref;
-        this.notifier = notifier;
-        this.vmInfoDAO = vmInfoDao;
-        this.lockInfoDao = lockInfoDao;
-
-        collector = collectorFactory.getCollector(ref);
-
-        view = viewFactory.createView();
-        view.setApplicationService(appService, ref.getVmId() + "-" + ref.getHostRef().getAgentId());
-
-        initControllers();
-        
-        view.setRecording(isRecording() ? ThreadView.MonitoringState.STARTED : ThreadView.MonitoringState.STOPPED, false);
-        view.addThreadActionListener(new ThreadActionListener());
-
-        view.setEnableRecordingControl(vmInfoDao.getVmInfo(ref).isAlive());
-    }
-
-    private boolean isRecording() {
-        
-        return collector.isHarvesterCollecting();
-    }
-    
-    @Override
-    public LocalizedString getLocalizedName() {
-        return t.localize(LocaleResources.CONTROLLER_NAME);
-    }
-
-    @Override
-    public UIComponent getView() {
-        return view;
-    }
-    
-    private class ThreadActionListener implements ActionListener<ThreadAction> {
-
-        @Override
-        public void actionPerformed(ActionEvent<ThreadAction> actionEvent) {
-            switch (actionEvent.getActionId()) {
-            case START_LIVE_RECORDING:
-                view.setRecording(ThreadView.MonitoringState.STARTING, false);
-                startHarvester();
-                view.setRecording(ThreadView.MonitoringState.STARTED, false);
-                break;
-            
-            case STOP_LIVE_RECORDING:
-                view.setRecording(ThreadView.MonitoringState.STOPPING, false);
-                stopHarvester();
-                view.setRecording(ThreadView.MonitoringState.STOPPED, false);
-                break;
-
-            case REQUEST_DISPLAY_RECORDED_SESSIONS:
-                loadSessionsList();
-                break;
-
-            case REQUEST_LOAD_SESSION: {
-                ThreadSession session = (ThreadSession) actionEvent.getPayload();
-                if (session != null) {
-                    threadTimeline.setSession(session.getSessionID());
-                    threadTableController.setSession(session.getSessionID());
-                }
-            } break;
-
-            default:
-                logger.log(Level.WARNING, "unknown action: " + actionEvent.getActionId());
-                break;
-            }
-        }
-
-        private class SessionComparator implements Comparator<ThreadSession> {
-            @Override
-            public int compare(ThreadSession o1, ThreadSession o2) {
-                // TODO: descending order only for now, we should allow the users
-                // to sort this via the UI though
-                int result = Long.compare(o1.getTimeStamp(), o2.getTimeStamp());
-                return -result;
-            }
-        }
-
-        private void loadSessionsList() {
-            submitTask(new Runnable() {
-                @Override
-                public void run() {
-                    Range<Long> range = new Range<>(0L, System.currentTimeMillis());
-                    List<ThreadSession> threadSessions = collector.getThreadSessions(range);
-
-                    Collections.sort(threadSessions, new SessionComparator());
-                    view.displayTimelineSessionList(threadSessions);
-
-                }
-            }, translator.localize(LocaleResources.LOADING_SESSION_LIST));
-        }
-
-        private void startHarvester() {
-            submitTask(new Runnable() {
-                @Override
-                public void run() {
-                    boolean result = collector.startHarvester();
-                    if (!result) {
-                        view.displayWarning(t.localize(LocaleResources.WARNING_CANNOT_ENABLE));
-                        view.setEnableRecordingControl(false);
-                        view.setRecording(ThreadView.MonitoringState.DISABLED, false);
-                    }
-                }
-            }, translator.localize(LocaleResources.STARTING_MONITORING));
-        }
-
-        private void stopHarvester() {
-            submitTask(new Runnable() {
-                @Override
-                public void run() {
-                    boolean result = collector.stopHarvester();
-                    if (!result) {
-                        view.displayWarning(t.localize(LocaleResources.WARNING_CANNOT_DISABLE));
-                        view.setEnableRecordingControl(false);
-                        view.setRecording(ThreadView.MonitoringState.DISABLED, false);
-                    }
-                }
-            }, translator.localize(LocaleResources.STOPPING_MONITORING));
-        }
-
-        private void submitTask(final Runnable task, final LocalizedString taskName) {
-            appService.getApplicationExecutor().execute(new Runnable() {
-                @Override
-                public void run() {
-                    final ProgressHandle handle = new ProgressHandle(taskName);
-                    handle.setTask(taskName);
-                    handle.setIndeterminate(true);
-                    notifier.register(handle);
-                    handle.runTask(task);
-                }
-            });
-        }
-    }
-    
-    private class ThreadSelectionActionListener implements ActionListener<ThreadSelectionAction> {
-        @Override
-        public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
-            view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
-        }
-    }
-
-    void ___injectControllersForTesting(ThreadTimelineController timeline,
-                                        ThreadTableController table,
-                                        ThreadCountController count,
-                                        LockController lock,
-                                        VmDeadLockController deadLock)
-    {
-        this.threadTableController = table;
-        this.threadCountController = count;
-        this.deadLockController = deadLock;
-        this.threadTimeline = timeline;
-        this.lockTableController = lock;
-    }
-
-    void ___injectCollectorForTesting(ThreadCollector collector)   {
-        this.collector = collector;
-    }
-
-    private void initControllers() {
-        TimerFactory tf = appService.getTimerFactory();
-
-        deadLockController =
-                new VmDeadLockController(vmInfoDAO, ref, view.createDeadLockView(), collector, tf.createTimer(),
-                        appService.getApplicationExecutor(), notifier);
-        deadLockController.initialize();
-
-        ThreadTableView threadTableView = view.createThreadTableView();
-        threadTableView.addThreadSelectionActionListener(new ThreadSelectionActionListener());
-        
-        threadCountController =
-                new ThreadCountController(view.createThreadCountView(), collector, tf.createTimer());
-        threadCountController.initialize();
-
-        threadTableController =
-                new ThreadTableController(threadTableView, collector, tf.createTimer());
-        threadTableController.initialize();
-        
-        threadTimeline = new ThreadTimelineController(view.createThreadTimelineView(),
-                                                      collector,
-                                                      tf.createTimer());
-        threadTimeline.initialize();
-        SessionID lastThreadSession = collector.getLastThreadSession();
-        threadTimeline.setSession(lastThreadSession);
-
-        lockTableController =
-                new LockController(view.createLockView(),
-                                   tf.createTimer(),
-                                   lockInfoDao,
-                                   ref);
-        lockTableController.initialize();
-    }
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationServiceImpl.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.client.core.NameMatchingRefFilter;
-import com.redhat.thermostat.client.core.controllers.InformationServiceController;
-import com.redhat.thermostat.client.core.progress.ProgressNotifier;
-import com.redhat.thermostat.common.ApplicationCache;
-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.controller.ThreadInformationService;
-import com.redhat.thermostat.thread.client.controller.impl.cache.AppCache;
-import com.redhat.thermostat.thread.client.controller.impl.cache.AppCacheKey;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-
-public class ThreadInformationServiceImpl implements ThreadInformationService {
-    
-    private static final int ORDER = ORDER_THREAD_GROUP;
-    
-    private Filter<VmRef> filter = new NameMatchingRefFilter<>();
-    private ApplicationService service;
-    private VmInfoDAO vmInfoDao;
-    private LockInfoDao lockInfoDao;
-    private ThreadCollectorFactory collectorFactory;
-    private ThreadViewProvider viewFactory;
-    private ProgressNotifier notifier;
-
-    public ThreadInformationServiceImpl(ApplicationService appService,
-                                        VmInfoDAO vmInfoDao,
-                                        LockInfoDao lockInfoDao,
-                                        ThreadCollectorFactory collectorFactory,
-                                        ThreadViewProvider viewFactory, ProgressNotifier notifier)
-    {
-        this.service = appService;
-        this.vmInfoDao = vmInfoDao;
-        this.lockInfoDao = lockInfoDao;
-        this.collectorFactory = collectorFactory;
-        this.viewFactory = viewFactory;
-        this.notifier = notifier;
-    }
-    
-    @Override
-    public Filter<VmRef> getFilter() {
-        return filter;
-    }
-
-    private AppCache getCache(VmRef ref) {
-        ApplicationCache applicationCache = service.getApplicationCache();
-        AppCache cache = (AppCache) applicationCache.getAttribute(ref.getVmId());
-        if (cache == null) {
-            AppCacheKey mainKey = new AppCacheKey(ref.getVmId(), ThreadInformationController.class);
-            cache = new AppCache(mainKey, applicationCache);
-            applicationCache.addAttribute(ref.getVmId(), cache);
-        }
-        return cache;
-    }
-
-    @Override
-    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
-        AppCache cache = getCache(ref);
-        AppCacheKey key = new AppCacheKey(ref.getVmId(), ThreadInformationController.class);
-        ThreadInformationController controller = cache.retrieve(key);
-        if (controller == null) {
-            controller = new ThreadInformationController(ref, service,
-                                                         vmInfoDao,
-                                                         lockInfoDao,
-                                                         collectorFactory,
-                                                         viewFactory,
-                                                         notifier);
-            cache.save(key, controller);
-        }
-        return controller;
-    }
-
-    @Override
-    public int getOrderValue() {
-        return ORDER;
-    }
-
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-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.ThreadTableBean;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
-import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
-import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadState;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-public class ThreadTableController extends CommonController {
-    
-    private ThreadTableView threadTableView;
-    private ThreadCollector collector;
-
-    private volatile boolean stopLooping;
-
-    public ThreadTableController(ThreadTableView threadTableView,
-                                 ThreadCollector collector,
-                                 Timer timer)
-    {
-        super(timer, threadTableView);
-        this.threadTableView = threadTableView;
-        this.collector = collector;
-        timer.setAction(new ThreadTableControllerAction());
-    }
-
-    @Override
-    protected void onViewVisible() {
-        stopLooping = false;
-    }
-
-    @Override
-    protected void onViewHidden() {
-        stopLooping = true;
-    }
-
-    private class ThreadTableControllerAction implements Runnable {
-
-        private ThreadResultHandler handler;
-        private Range<Long> range;
-        private SessionID lastSession;
-        private long lastUpdate;
-
-        public ThreadTableControllerAction() {
-            handler = new ThreadResultHandler();
-            resetState();
-        }
-
-        private void resetState() {
-            handler.threadStates.clear();
-            threadTableView.clear();
-            range = null;
-            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
-        }
-
-        @Override
-        public void run() {
-            SessionID session = ThreadTableController.this.session;
-            if (session == null) {
-                // no session selected, but let's try to default to the last
-                // available
-                session = collector.getLastThreadSession();
-                if (session == null) {
-                    // ok, really no data, let's skip this round
-                    return;
-                }
-            }
-
-            if (lastSession == null ||
-                    !session.get().equals(lastSession.get())) {
-                // since we only visualise one session at a time and this is
-                // a new session, 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) {
-                // this just means we don't have any data yet
-                return;
-            }
-
-            range = new Range<>(lastUpdate, totalRange.getMax());
-            lastUpdate = totalRange.getMax();
-
-            collector.getThreadStates(session,
-                                      handler,
-                                      range);
-            threadTableView.submitChanges();
-        }
-    }
-
-    private class ThreadResultHandler implements ResultHandler<ThreadState> {
-        private Map<ThreadInfo, ThreadTableBean> threadStates;
-
-        public ThreadResultHandler() {
-            this.threadStates = new HashMap<>();
-        }
-
-        @Override
-        public boolean onResult(ThreadState thread) {
-
-            ThreadInfo key = new ThreadInfo();
-            key.setName(thread.getName());
-            key.setId(thread.getId());
-
-            ThreadTableBean bean = threadStates.get(key);
-            if (bean == null) {
-                bean = new ThreadTableBean();
-                bean.setName(thread.getName());
-                bean.setId(thread.getId());
-                bean.setStartTimeStamp(thread.getTimeStamp());
-                threadStates.put(key, bean);
-            }
-
-            setCurrentStateTime(bean, thread);
-
-            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);
-            }
-
-            bean.setBlockedCount(thread.getBlockedCount());
-            bean.setWaitedCount(thread.getWaitedCount());
-
-            bean.setStopTimeStamp(thread.getTimeStamp());
-
-            threadTableView.display(bean);
-
-            boolean _stopLooping = stopLooping;
-            return !_stopLooping;
-        }
-    }
-
-    void setCurrentStateTime(ThreadTableBean bean, ThreadState thread) {
-        Thread.State threadState = Thread.State.valueOf(thread.getState());
-
-        switch (threadState) {
-            case RUNNABLE:
-                bean.setRunningTime(bean.getRunningTime() + 1);
-                break;
-
-            case BLOCKED:
-                bean.setMonitorTime(bean.getMonitorTime() + 1);
-                break;
-
-            case TIMED_WAITING:
-                bean.setSleepingTime(bean.getSleepingTime() + 1);
-                break;
-
-            case WAITING:
-                bean.setWaitingTime(bean.getWaitingTime() + 1);
-                break;
-
-            case NEW:
-            case TERMINATED:
-            default:
-                break;
-        }
-    }
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-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.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.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadState;
-
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
-
-public class ThreadTimelineController extends CommonController {
-
-    private static final boolean _DEBUG_BLOCK_TIMING_ = false;
-
-    private ThreadTimelineView view;
-    private ThreadCollector collector;
-
-    private volatile boolean stopLooping;
-
-    public ThreadTimelineController(ThreadTimelineView view,
-                                    ThreadCollector collector,
-                                    Timer timer)
-    {
-        super(timer, view);
-        this.view = view;
-        this.collector = collector;
-
-        timer.setAction(new ThreadTimelineControllerAction());
-    }
-
-    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 void resetState() {
-            view.clear();
-            lastRange = null;
-            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
-            threadStateResultHandler.knownStates.clear();
-            threadStateResultHandler.key = new ThreadInfo();
-        }
-
-        @Override
-        public void run() {
-            SessionID session = ThreadTimelineController.this.session;
-            if (session == null) {
-                // no session selected, but let's try to default to the last
-                // available
-                session = collector.getLastThreadSession();
-                if (session == null) {
-                    // ok, really no data, let's skip this round
-                    return;
-                }
-            }
-
-            if (lastSession == null ||
-                !session.get().equals(lastSession.get()))
-            {
-                // since we only visualise one session at a time and this is
-                // a new session, 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) {
-                // this just means we don't have any data yet
-                return;
-            }
-
-            if (!totalRange.equals(lastRange)) {
-                view.setTotalRange(totalRange);
-            }
-            lastRange = totalRange;
-
-            range = new Range<>(lastUpdate, totalRange.getMax());
-            lastUpdate = totalRange.getMax();
-
-            collector.getThreadStates(session,
-                                      threadStateResultHandler,
-                                      range);
-        }
-    }
-
-    @Override
-    protected void onViewVisible() {
-        stopLooping = false;
-    }
-
-    @Override
-    protected void onViewHidden() {
-        stopLooping = true;
-    }
-
-    private class ThreadStateResultHandler implements ResultHandler<ThreadState> {
-        private ThreadInfo key;
-        private Set<ThreadInfo> knownStates;
-
-        public ThreadStateResultHandler() {
-            this.key = new ThreadInfo();
-            knownStates = new HashSet<>();
-        }
-
-        @Override
-        public boolean onResult(ThreadState state) {
-
-            key.setName(state.getName());
-            key.setId(state.getId());
-
-            ThreadInfo info = new ThreadInfo(key);
-            if (!knownStates.contains(key)) {
-                view.addThread(info);
-                knownStates.add(info);
-            }
-
-            TimelineProbe probe = TimelineFactory.createTimelineProbe(state);
-            view.addProbe(info, probe);
-
-            boolean _stopLooping = stopLooping;
-            return !_stopLooping;
-        }
-    }
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockController.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Objects;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.client.core.progress.ProgressHandle;
-import com.redhat.thermostat.client.core.progress.ProgressNotifier;
-import com.redhat.thermostat.client.core.views.BasicView.Action;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.Timer.SchedulingType;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.shared.locale.LocalizedString;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.thread.client.common.DeadlockParser;
-import com.redhat.thermostat.thread.client.common.DeadlockParser.Information;
-import com.redhat.thermostat.thread.client.common.DeadlockParser.ParseException;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
-import com.redhat.thermostat.thread.model.VmDeadLockData;
-
-public class VmDeadLockController {
-
-    private static final Logger logger = LoggingUtils.getLogger(VmDeadLockController.class);
-    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
-
-    private static final String NO_DEADLOCK = translate.localize(LocaleResources.NO_DEADLOCK_DETECTED).getContents();
-
-    private VmInfoDAO vmInfoDAO;
-    private VmRef vmRef;
-
-    private VmDeadLockView view;
-    private ThreadCollector collector;
-    private Timer timer;
-    private ExecutorService executor;
-    private ProgressNotifier notifier;
-
-    private final AtomicReference<String> descriptionRef =  new AtomicReference<>("");
-    private String previousDeadlockData = null;
-
-
-    public VmDeadLockController(VmInfoDAO vmInfoDAO, VmRef vmRef, VmDeadLockView view, ThreadCollector collector, Timer timer,
-                                ExecutorService executor, ProgressNotifier notifier) {
-        this.vmInfoDAO = vmInfoDAO;
-        this.vmRef = vmRef;
-        this.view = view;
-        this.collector = collector;
-        this.timer = timer;
-        this.executor = executor;
-        this.notifier = Objects.requireNonNull(notifier);
-    }
-
-    public void initialize() {
-        view.addVmDeadLockViewActionListener(new ActionListener<VmDeadLockViewAction>() {
-            @Override
-            public void actionPerformed(ActionEvent<VmDeadLockViewAction> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                case CHECK_FOR_DEADLOCK:
-                    executor.execute(new Runnable() {
-                        @Override
-                        public void run() {
-                            LocalizedString message = translate.localize(LocaleResources.CHECKING_FOR_DEADLOCKS);
-                            ProgressHandle handle = new ProgressHandle(message);
-                            handle.setIndeterminate(true);
-
-                            notifier.register(handle);
-
-                            handle.runTask(new Runnable() {
-                                @Override
-                                public void run() {
-                                    checkForDeadLock();
-                                    updateViewIfNeeded();
-                                }
-                            });
-                        }
-                    });
-                    break;
-                default:
-                    break;
-                }
-            }
-        });
-
-        timer.setAction(new Runnable() {
-            @Override
-            public void run() {
-                checkStorageForDeadLockData();
-                updateViewIfNeeded();
-            }
-        });
-        timer.setDelay(5);
-        timer.setInitialDelay(0);
-        timer.setTimeUnit(TimeUnit.SECONDS);
-        timer.setSchedulingType(SchedulingType.FIXED_DELAY);
-
-        view.addActionListener(new ActionListener<Action>() {
-            @Override
-            public void actionPerformed(ActionEvent<Action> actionEvent) {
-                switch (actionEvent.getActionId()) {
-                case HIDDEN:
-                    timer.stop();
-                    break;
-                case VISIBLE:
-                    timer.start();
-                    break;
-                }
-            }
-        });
-
-        view.setCheckDeadlockControlEnabled(vmInfoDAO.getVmInfo(vmRef).isAlive());
-    }
-
-    private void checkForDeadLock() {
-        askAgentToCheckForDeadLock();
-        checkStorageForDeadLockData();
-    }
-
-    private void askAgentToCheckForDeadLock() {
-        collector.requestDeadLockCheck();
-    }
-
-    private void checkStorageForDeadLockData() {
-        VmDeadLockData data = collector.getLatestDeadLockData();
-        if (data == null) {
-            // no deadlock data; so don't update anything
-            return;
-        }
-
-        String description = data.getDeadLockDescription();
-        if (description.equals(VmDeadLockData.NO_DEADLOCK)) {
-            description = NO_DEADLOCK;
-        }
-        this.descriptionRef.set(description);
-
-    }
-
-    private void updateViewIfNeeded() {
-        String rawDeadlockData = descriptionRef.get();
-
-        if (!rawDeadlockData.equals(previousDeadlockData)) {
-            Information parsed = null;
-
-            if (!rawDeadlockData.equals(NO_DEADLOCK)) {
-                try {
-                    parsed = new DeadlockParser().parse(new BufferedReader(new StringReader(rawDeadlockData)));
-                } catch (IOException | ParseException e) {
-                    logger.log(Level.FINE, "Failed to parse deadlock data. Visualizations might not show up correctly.", e);
-                }
-            }
-
-            view.setDeadLockInformation(parsed, rawDeadlockData);
-            previousDeadlockData = rawDeadlockData;
-        }
-    }
-
-}
-
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCache.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl.cache;
-
-import com.redhat.thermostat.common.ApplicationCache;
-import java.util.HashMap;
-
-/**
- *
- */
-public class AppCache {
-    private static final String LOCK = new String("AppCacheLock");
-
-    private AppCacheKey root;
-    private ApplicationCache cache;
-
-    public AppCache(AppCacheKey root, ApplicationCache cache) {
-        this.root = root;
-        this.cache = cache;
-    }
-
-    public <E> void save(AppCacheKey key, E value) {
-        synchronized (LOCK) {
-            HashMap<AppCacheKey, E> localCache = getLocalCache();
-            localCache.put(key, value);
-            saveLocalCache(localCache);
-        }
-    }
-
-    public <E> E retrieve(AppCacheKey key) {
-        synchronized (LOCK) {
-            HashMap<AppCacheKey, E> localCache = getLocalCache();
-            return localCache.get(key);
-        }
-    }
-
-    private <E> HashMap<AppCacheKey, E> getLocalCache() {
-        HashMap<AppCacheKey, E> localCache =
-                (HashMap<AppCacheKey, E>) cache.getAttribute(root);
-
-        if (localCache == null) {
-            localCache = new HashMap<>();
-            saveLocalCache(localCache);
-        }
-
-        return localCache;
-    }
-
-    private <E> void saveLocalCache(HashMap<AppCacheKey, E> localCache) {
-        cache.addAttribute(root, localCache);
-    }
-}
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheKey.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl.cache;
-
-import java.util.Objects;
-
-/**
- *
- */
-public class AppCacheKey {
-    private String UUID;
-    private String targetClass;
-
-    public AppCacheKey(Class<?> hostClass, Class<?> targetClass) {
-        this(hostClass.getSimpleName(), targetClass.getSimpleName());
-    }
-
-    public AppCacheKey(String UUID, Class<?> targetClass) {
-        this(UUID, targetClass.getSimpleName());
-    }
-
-    private AppCacheKey(String UUID, String targetClass) {
-        this.UUID = UUID;
-        this.targetClass = targetClass;
-        Objects.requireNonNull(UUID);
-        Objects.requireNonNull(targetClass);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        AppCacheKey that = (AppCacheKey) o;
-
-        if (!UUID.equals(that.UUID)) return false;
-        if (!targetClass.equals(that.targetClass)) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = UUID.hashCode();
-        result = 31 * result + targetClass.hashCode();
-        return result;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/CommonController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.thread.model.SessionID;
+
+import java.util.concurrent.TimeUnit;
+
+public abstract class CommonController {
+    
+    protected Timer timer;
+    protected BasicView view;
+
+    protected volatile SessionID session;
+
+    public CommonController(Timer timer, BasicView view) {
+        this.view = view;
+        this.timer = timer;
+    }
+    
+    void initialize() {
+        timer.setInitialDelay(0);
+        timer.setDelay(1000);
+        timer.setTimeUnit(TimeUnit.MILLISECONDS);
+        timer.setSchedulingType(SchedulingType.FIXED_RATE);
+        
+        view.addActionListener(new ActionListener<Action>() {
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case VISIBLE:
+                    onViewVisible();
+                    timer.start();
+                    break;
+
+                case HIDDEN:
+                    timer.stop();
+                    onViewHidden();
+                    break;
+
+                default:
+                    break;
+                }
+            }
+        });
+    }
+
+    protected void onViewVisible() {}
+    protected void onViewHidden() {}
+
+    public void setSession(SessionID session) {
+        this.session = session;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/LocaleResources.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.shared.locale.Translate;
+
+public enum LocaleResources {
+
+    CONTROLLER_NAME,
+
+    WARNING_CANNOT_DISABLE,
+    WARNING_CANNOT_ENABLE,
+
+    CHECKING_FOR_DEADLOCKS,
+    NO_DEADLOCK_DETECTED,
+
+    STARTING_MONITORING,
+    STOPPING_MONITORING,
+
+    LOADING_SESSION_LIST,
+    ;
+
+    static final String RESOURCE_BUNDLE = "com.redhat.thermostat.thread.client.controller.internal.strings";
+
+    public static Translate<LocaleResources> createLocalizer() {
+        return new Translate<>(RESOURCE_BUNDLE, LocaleResources.class);
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/LockController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.thread.client.common.view.LockView;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+import com.redhat.thermostat.thread.model.LockInfo;
+
+public class LockController extends CommonController {
+
+    private LockInfoDao dao;
+    private VmRef vm;
+
+    public LockController(LockView view, Timer timer, LockInfoDao lockInfoDao, VmRef vm) {
+        super(timer, view);
+        this.dao = lockInfoDao;
+        this.vm = vm;
+        timer.setAction(new LockInfoUpdateAction());
+    }
+
+    class LockInfoUpdateAction implements Runnable {
+        @Override
+        public void run() {
+            LockInfo result = dao.getLatestLockInfo(vm);
+            if (result != null) {
+                ((LockView) view).setLatestLockData(result);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadCountController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+class ThreadCountController extends CommonController {
+
+    private LivingDaemonThreadDifferenceChart model;
+    private ThreadCollector collector;
+    
+    public ThreadCountController(ThreadCountView view, ThreadCollector collector, Timer timer) {
+        super(timer, view);
+        
+        this.collector = collector;
+        model = new LivingDaemonThreadDifferenceChart("Living Threads vs. Daemon Threads", "time", "threads",
+                                                      "Living Threads", "Daemon Threads");
+        model.setMaximumItemCount(3600);
+
+        timer.setAction(new ThreadInformationDataCollector());
+    }
+    
+    class ThreadInformationDataCollector implements Runnable {
+
+        private void updateLastSession() {
+
+            ThreadCountView view = (ThreadCountView) ThreadCountController.this.view;
+
+            // load the very latest thread summary
+            ThreadSummary latestSummary = collector.getLatestThreadSummary();
+            Objects.requireNonNull(latestSummary);
+
+            if (latestSummary.getTimeStamp() != 0) {
+                view.setLiveThreads(Long.toString(latestSummary.getCurrentLiveThreads()));
+                view.setDaemonThreads(Long.toString(latestSummary.getCurrentDaemonThreads()));
+            }
+        }
+
+        private void updateChart() {
+
+            ThreadCountView view = (ThreadCountView) ThreadCountController.this.view;
+
+            long now = System.currentTimeMillis();
+            long lastHour = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+
+            Range<Long> range = new Range<>(lastHour, now);
+
+            boolean updateModel = false;
+            List<ThreadSummary> summaries = collector.getThreadSummary(range);
+            if (summaries.size() != 0) {
+                for (ThreadSummary summary : summaries) {
+                    model.addData(summary.getTimeStamp(), summary.getCurrentLiveThreads(), summary.getCurrentDaemonThreads());
+                }
+                updateModel = true;
+            }
+
+            if (updateModel) {
+                view.updateLivingDaemonTimeline(model);
+            }
+        }
+
+        @Override
+        public void run() {
+            updateLastSession();
+            updateChart();
+        }
+    }    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.progress.ProgressHandle;
+import com.redhat.thermostat.client.core.progress.ProgressNotifier;
+import com.redhat.thermostat.client.core.views.UIComponent;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+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.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.ThreadView.ThreadAction;
+import com.redhat.thermostat.thread.client.controller.internal.cache.AppCache;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadSession;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ThreadInformationController implements InformationServiceController<VmRef> {
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private VmRef ref;
+    private VmInfoDAO vmInfoDAO;
+    private LockInfoDao lockInfoDao;
+
+    private static final Logger logger = LoggingUtils.getLogger(ThreadInformationController.class);
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    
+    private ThreadView view;
+    private ThreadCollector collector;
+
+    private ApplicationService appService;
+
+    private AppCache cache;
+    private ProgressNotifier notifier;
+
+    private CommonController threadTimeline;
+    private CommonController threadTableController;
+    private CommonController threadCountController;
+    private VmDeadLockController deadLockController;
+    private CommonController lockTableController;
+
+    public ThreadInformationController(VmRef ref, ApplicationService appService,
+                                       VmInfoDAO vmInfoDao,
+                                       LockInfoDao lockInfoDao,
+                                       ThreadCollectorFactory collectorFactory, 
+                                       ThreadViewProvider viewFactory,
+                                       ProgressNotifier notifier)
+    {
+        this.appService = appService;
+        this.ref = ref;
+        this.notifier = notifier;
+        this.vmInfoDAO = vmInfoDao;
+        this.lockInfoDao = lockInfoDao;
+
+        collector = collectorFactory.getCollector(ref);
+
+        view = viewFactory.createView();
+        view.setApplicationService(appService, ref.getVmId() + "-" + ref.getHostRef().getAgentId());
+
+        initControllers();
+        
+        view.setRecording(isRecording() ? ThreadView.MonitoringState.STARTED : ThreadView.MonitoringState.STOPPED, false);
+        view.addThreadActionListener(new ThreadActionListener());
+
+        view.setEnableRecordingControl(vmInfoDao.getVmInfo(ref).isAlive());
+    }
+
+    private boolean isRecording() {
+        
+        return collector.isHarvesterCollecting();
+    }
+    
+    @Override
+    public LocalizedString getLocalizedName() {
+        return t.localize(LocaleResources.CONTROLLER_NAME);
+    }
+
+    @Override
+    public UIComponent getView() {
+        return view;
+    }
+    
+    private class ThreadActionListener implements ActionListener<ThreadAction> {
+
+        @Override
+        public void actionPerformed(ActionEvent<ThreadAction> actionEvent) {
+            switch (actionEvent.getActionId()) {
+            case START_LIVE_RECORDING:
+                view.setRecording(ThreadView.MonitoringState.STARTING, false);
+                startHarvester();
+                view.setRecording(ThreadView.MonitoringState.STARTED, false);
+                break;
+            
+            case STOP_LIVE_RECORDING:
+                view.setRecording(ThreadView.MonitoringState.STOPPING, false);
+                stopHarvester();
+                view.setRecording(ThreadView.MonitoringState.STOPPED, false);
+                break;
+
+            case REQUEST_DISPLAY_RECORDED_SESSIONS:
+                loadSessionsList();
+                break;
+
+            case REQUEST_LOAD_SESSION: {
+                ThreadSession session = (ThreadSession) actionEvent.getPayload();
+                if (session != null) {
+                    threadTimeline.setSession(session.getSessionID());
+                    threadTableController.setSession(session.getSessionID());
+                }
+            } break;
+
+            default:
+                logger.log(Level.WARNING, "unknown action: " + actionEvent.getActionId());
+                break;
+            }
+        }
+
+        private class SessionComparator implements Comparator<ThreadSession> {
+            @Override
+            public int compare(ThreadSession o1, ThreadSession o2) {
+                // TODO: descending order only for now, we should allow the users
+                // to sort this via the UI though
+                int result = Long.compare(o1.getTimeStamp(), o2.getTimeStamp());
+                return -result;
+            }
+        }
+
+        private void loadSessionsList() {
+            submitTask(new Runnable() {
+                @Override
+                public void run() {
+                    Range<Long> range = new Range<>(0L, System.currentTimeMillis());
+                    List<ThreadSession> threadSessions = collector.getThreadSessions(range);
+
+                    Collections.sort(threadSessions, new SessionComparator());
+                    view.displayTimelineSessionList(threadSessions);
+
+                }
+            }, translator.localize(LocaleResources.LOADING_SESSION_LIST));
+        }
+
+        private void startHarvester() {
+            submitTask(new Runnable() {
+                @Override
+                public void run() {
+                    boolean result = collector.startHarvester();
+                    if (!result) {
+                        view.displayWarning(t.localize(LocaleResources.WARNING_CANNOT_ENABLE));
+                        view.setEnableRecordingControl(false);
+                        view.setRecording(ThreadView.MonitoringState.DISABLED, false);
+                    }
+                }
+            }, translator.localize(LocaleResources.STARTING_MONITORING));
+        }
+
+        private void stopHarvester() {
+            submitTask(new Runnable() {
+                @Override
+                public void run() {
+                    boolean result = collector.stopHarvester();
+                    if (!result) {
+                        view.displayWarning(t.localize(LocaleResources.WARNING_CANNOT_DISABLE));
+                        view.setEnableRecordingControl(false);
+                        view.setRecording(ThreadView.MonitoringState.DISABLED, false);
+                    }
+                }
+            }, translator.localize(LocaleResources.STOPPING_MONITORING));
+        }
+
+        private void submitTask(final Runnable task, final LocalizedString taskName) {
+            appService.getApplicationExecutor().execute(new Runnable() {
+                @Override
+                public void run() {
+                    final ProgressHandle handle = new ProgressHandle(taskName);
+                    handle.setTask(taskName);
+                    handle.setIndeterminate(true);
+                    notifier.register(handle);
+                    handle.runTask(task);
+                }
+            });
+        }
+    }
+    
+    private class ThreadSelectionActionListener implements ActionListener<ThreadSelectionAction> {
+        @Override
+        public void actionPerformed(ActionEvent<ThreadSelectionAction> actionEvent) {
+            view.displayThreadDetails((ThreadTableBean) actionEvent.getPayload());
+        }
+    }
+
+    void ___injectControllersForTesting(ThreadTimelineController timeline,
+                                        ThreadTableController table,
+                                        ThreadCountController count,
+                                        LockController lock,
+                                        VmDeadLockController deadLock)
+    {
+        this.threadTableController = table;
+        this.threadCountController = count;
+        this.deadLockController = deadLock;
+        this.threadTimeline = timeline;
+        this.lockTableController = lock;
+    }
+
+    void ___injectCollectorForTesting(ThreadCollector collector)   {
+        this.collector = collector;
+    }
+
+    private void initControllers() {
+        TimerFactory tf = appService.getTimerFactory();
+
+        deadLockController =
+                new VmDeadLockController(vmInfoDAO, ref, view.createDeadLockView(), collector, tf.createTimer(),
+                        appService.getApplicationExecutor(), notifier);
+        deadLockController.initialize();
+
+        ThreadTableView threadTableView = view.createThreadTableView();
+        threadTableView.addThreadSelectionActionListener(new ThreadSelectionActionListener());
+        
+        threadCountController =
+                new ThreadCountController(view.createThreadCountView(), collector, tf.createTimer());
+        threadCountController.initialize();
+
+        threadTableController =
+                new ThreadTableController(threadTableView, collector, tf.createTimer());
+        threadTableController.initialize();
+        
+        threadTimeline = new ThreadTimelineController(view.createThreadTimelineView(),
+                                                      collector,
+                                                      tf.createTimer());
+        threadTimeline.initialize();
+        SessionID lastThreadSession = collector.getLastThreadSession();
+        threadTimeline.setSession(lastThreadSession);
+
+        lockTableController =
+                new LockController(view.createLockView(),
+                                   tf.createTimer(),
+                                   lockInfoDao,
+                                   ref);
+        lockTableController.initialize();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationServiceImpl.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.client.core.NameMatchingRefFilter;
+import com.redhat.thermostat.client.core.controllers.InformationServiceController;
+import com.redhat.thermostat.client.core.progress.ProgressNotifier;
+import com.redhat.thermostat.common.ApplicationCache;
+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.controller.ThreadInformationService;
+import com.redhat.thermostat.thread.client.controller.internal.cache.AppCache;
+import com.redhat.thermostat.thread.client.controller.internal.cache.AppCacheKey;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+
+public class ThreadInformationServiceImpl implements ThreadInformationService {
+    
+    private static final int ORDER = ORDER_THREAD_GROUP;
+    
+    private Filter<VmRef> filter = new NameMatchingRefFilter<>();
+    private ApplicationService service;
+    private VmInfoDAO vmInfoDao;
+    private LockInfoDao lockInfoDao;
+    private ThreadCollectorFactory collectorFactory;
+    private ThreadViewProvider viewFactory;
+    private ProgressNotifier notifier;
+
+    public ThreadInformationServiceImpl(ApplicationService appService,
+                                        VmInfoDAO vmInfoDao,
+                                        LockInfoDao lockInfoDao,
+                                        ThreadCollectorFactory collectorFactory,
+                                        ThreadViewProvider viewFactory, ProgressNotifier notifier)
+    {
+        this.service = appService;
+        this.vmInfoDao = vmInfoDao;
+        this.lockInfoDao = lockInfoDao;
+        this.collectorFactory = collectorFactory;
+        this.viewFactory = viewFactory;
+        this.notifier = notifier;
+    }
+    
+    @Override
+    public Filter<VmRef> getFilter() {
+        return filter;
+    }
+
+    private AppCache getCache(VmRef ref) {
+        ApplicationCache applicationCache = service.getApplicationCache();
+        AppCache cache = (AppCache) applicationCache.getAttribute(ref.getVmId());
+        if (cache == null) {
+            AppCacheKey mainKey = new AppCacheKey(ref.getVmId(), ThreadInformationController.class);
+            cache = new AppCache(mainKey, applicationCache);
+            applicationCache.addAttribute(ref.getVmId(), cache);
+        }
+        return cache;
+    }
+
+    @Override
+    public InformationServiceController<VmRef> getInformationServiceController(VmRef ref) {
+        AppCache cache = getCache(ref);
+        AppCacheKey key = new AppCacheKey(ref.getVmId(), ThreadInformationController.class);
+        ThreadInformationController controller = cache.retrieve(key);
+        if (controller == null) {
+            controller = new ThreadInformationController(ref, service,
+                                                         vmInfoDao,
+                                                         lockInfoDao,
+                                                         collectorFactory,
+                                                         viewFactory,
+                                                         notifier);
+            cache.save(key, controller);
+        }
+        return controller;
+    }
+
+    @Override
+    public int getOrderValue() {
+        return ORDER;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTableController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+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.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.model.timeline.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadState;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class ThreadTableController extends CommonController {
+    
+    private ThreadTableView threadTableView;
+    private ThreadCollector collector;
+
+    private volatile boolean stopLooping;
+
+    public ThreadTableController(ThreadTableView threadTableView,
+                                 ThreadCollector collector,
+                                 Timer timer)
+    {
+        super(timer, threadTableView);
+        this.threadTableView = threadTableView;
+        this.collector = collector;
+        timer.setAction(new ThreadTableControllerAction());
+    }
+
+    @Override
+    protected void onViewVisible() {
+        stopLooping = false;
+    }
+
+    @Override
+    protected void onViewHidden() {
+        stopLooping = true;
+    }
+
+    private class ThreadTableControllerAction implements Runnable {
+
+        private ThreadResultHandler handler;
+        private Range<Long> range;
+        private SessionID lastSession;
+        private long lastUpdate;
+
+        public ThreadTableControllerAction() {
+            handler = new ThreadResultHandler();
+            resetState();
+        }
+
+        private void resetState() {
+            handler.threadStates.clear();
+            threadTableView.clear();
+            range = null;
+            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+        }
+
+        @Override
+        public void run() {
+            SessionID session = ThreadTableController.this.session;
+            if (session == null) {
+                // no session selected, but let's try to default to the last
+                // available
+                session = collector.getLastThreadSession();
+                if (session == null) {
+                    // ok, really no data, let's skip this round
+                    return;
+                }
+            }
+
+            if (lastSession == null ||
+                    !session.get().equals(lastSession.get())) {
+                // since we only visualise one session at a time and this is
+                // a new session, 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) {
+                // this just means we don't have any data yet
+                return;
+            }
+
+            range = new Range<>(lastUpdate, totalRange.getMax());
+            lastUpdate = totalRange.getMax();
+
+            collector.getThreadStates(session,
+                                      handler,
+                                      range);
+            threadTableView.submitChanges();
+        }
+    }
+
+    private class ThreadResultHandler implements ResultHandler<ThreadState> {
+        private Map<ThreadInfo, ThreadTableBean> threadStates;
+
+        public ThreadResultHandler() {
+            this.threadStates = new HashMap<>();
+        }
+
+        @Override
+        public boolean onResult(ThreadState thread) {
+
+            ThreadInfo key = new ThreadInfo();
+            key.setName(thread.getName());
+            key.setId(thread.getId());
+
+            ThreadTableBean bean = threadStates.get(key);
+            if (bean == null) {
+                bean = new ThreadTableBean();
+                bean.setName(thread.getName());
+                bean.setId(thread.getId());
+                bean.setStartTimeStamp(thread.getTimeStamp());
+                threadStates.put(key, bean);
+            }
+
+            setCurrentStateTime(bean, thread);
+
+            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);
+            }
+
+            bean.setBlockedCount(thread.getBlockedCount());
+            bean.setWaitedCount(thread.getWaitedCount());
+
+            bean.setStopTimeStamp(thread.getTimeStamp());
+
+            threadTableView.display(bean);
+
+            boolean _stopLooping = stopLooping;
+            return !_stopLooping;
+        }
+    }
+
+    void setCurrentStateTime(ThreadTableBean bean, ThreadState thread) {
+        Thread.State threadState = Thread.State.valueOf(thread.getState());
+
+        switch (threadState) {
+            case RUNNABLE:
+                bean.setRunningTime(bean.getRunningTime() + 1);
+                break;
+
+            case BLOCKED:
+                bean.setMonitorTime(bean.getMonitorTime() + 1);
+                break;
+
+            case TIMED_WAITING:
+                bean.setSleepingTime(bean.getSleepingTime() + 1);
+                break;
+
+            case WAITING:
+                bean.setWaitingTime(bean.getWaitingTime() + 1);
+                break;
+
+            case NEW:
+            case TERMINATED:
+            default:
+                break;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTimelineController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+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.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.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadState;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+public class ThreadTimelineController extends CommonController {
+
+    private static final boolean _DEBUG_BLOCK_TIMING_ = false;
+
+    private ThreadTimelineView view;
+    private ThreadCollector collector;
+
+    private volatile boolean stopLooping;
+
+    public ThreadTimelineController(ThreadTimelineView view,
+                                    ThreadCollector collector,
+                                    Timer timer)
+    {
+        super(timer, view);
+        this.view = view;
+        this.collector = collector;
+
+        timer.setAction(new ThreadTimelineControllerAction());
+    }
+
+    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 void resetState() {
+            view.clear();
+            lastRange = null;
+            lastUpdate = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1);
+            threadStateResultHandler.knownStates.clear();
+            threadStateResultHandler.key = new ThreadInfo();
+        }
+
+        @Override
+        public void run() {
+            SessionID session = ThreadTimelineController.this.session;
+            if (session == null) {
+                // no session selected, but let's try to default to the last
+                // available
+                session = collector.getLastThreadSession();
+                if (session == null) {
+                    // ok, really no data, let's skip this round
+                    return;
+                }
+            }
+
+            if (lastSession == null ||
+                !session.get().equals(lastSession.get()))
+            {
+                // since we only visualise one session at a time and this is
+                // a new session, 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) {
+                // this just means we don't have any data yet
+                return;
+            }
+
+            if (!totalRange.equals(lastRange)) {
+                view.setTotalRange(totalRange);
+            }
+            lastRange = totalRange;
+
+            range = new Range<>(lastUpdate, totalRange.getMax());
+            lastUpdate = totalRange.getMax();
+
+            collector.getThreadStates(session,
+                                      threadStateResultHandler,
+                                      range);
+        }
+    }
+
+    @Override
+    protected void onViewVisible() {
+        stopLooping = false;
+    }
+
+    @Override
+    protected void onViewHidden() {
+        stopLooping = true;
+    }
+
+    private class ThreadStateResultHandler implements ResultHandler<ThreadState> {
+        private ThreadInfo key;
+        private Set<ThreadInfo> knownStates;
+
+        public ThreadStateResultHandler() {
+            this.key = new ThreadInfo();
+            knownStates = new HashSet<>();
+        }
+
+        @Override
+        public boolean onResult(ThreadState state) {
+
+            key.setName(state.getName());
+            key.setId(state.getId());
+
+            ThreadInfo info = new ThreadInfo(key);
+            if (!knownStates.contains(key)) {
+                view.addThread(info);
+                knownStates.add(info);
+            }
+
+            TimelineProbe probe = TimelineFactory.createTimelineProbe(state);
+            view.addProbe(info, probe);
+
+            boolean _stopLooping = stopLooping;
+            return !_stopLooping;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/VmDeadLockController.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.client.core.progress.ProgressHandle;
+import com.redhat.thermostat.client.core.progress.ProgressNotifier;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.thread.client.common.DeadlockParser;
+import com.redhat.thermostat.thread.client.common.DeadlockParser.Information;
+import com.redhat.thermostat.thread.client.common.DeadlockParser.ParseException;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
+
+public class VmDeadLockController {
+
+    private static final Logger logger = LoggingUtils.getLogger(VmDeadLockController.class);
+    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
+
+    private static final String NO_DEADLOCK = translate.localize(LocaleResources.NO_DEADLOCK_DETECTED).getContents();
+
+    private VmInfoDAO vmInfoDAO;
+    private VmRef vmRef;
+
+    private VmDeadLockView view;
+    private ThreadCollector collector;
+    private Timer timer;
+    private ExecutorService executor;
+    private ProgressNotifier notifier;
+
+    private final AtomicReference<String> descriptionRef =  new AtomicReference<>("");
+    private String previousDeadlockData = null;
+
+
+    public VmDeadLockController(VmInfoDAO vmInfoDAO, VmRef vmRef, VmDeadLockView view, ThreadCollector collector, Timer timer,
+                                ExecutorService executor, ProgressNotifier notifier) {
+        this.vmInfoDAO = vmInfoDAO;
+        this.vmRef = vmRef;
+        this.view = view;
+        this.collector = collector;
+        this.timer = timer;
+        this.executor = executor;
+        this.notifier = Objects.requireNonNull(notifier);
+    }
+
+    public void initialize() {
+        view.addVmDeadLockViewActionListener(new ActionListener<VmDeadLockViewAction>() {
+            @Override
+            public void actionPerformed(ActionEvent<VmDeadLockViewAction> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case CHECK_FOR_DEADLOCK:
+                    executor.execute(new Runnable() {
+                        @Override
+                        public void run() {
+                            LocalizedString message = translate.localize(LocaleResources.CHECKING_FOR_DEADLOCKS);
+                            ProgressHandle handle = new ProgressHandle(message);
+                            handle.setIndeterminate(true);
+
+                            notifier.register(handle);
+
+                            handle.runTask(new Runnable() {
+                                @Override
+                                public void run() {
+                                    checkForDeadLock();
+                                    updateViewIfNeeded();
+                                }
+                            });
+                        }
+                    });
+                    break;
+                default:
+                    break;
+                }
+            }
+        });
+
+        timer.setAction(new Runnable() {
+            @Override
+            public void run() {
+                checkStorageForDeadLockData();
+                updateViewIfNeeded();
+            }
+        });
+        timer.setDelay(5);
+        timer.setInitialDelay(0);
+        timer.setTimeUnit(TimeUnit.SECONDS);
+        timer.setSchedulingType(SchedulingType.FIXED_DELAY);
+
+        view.addActionListener(new ActionListener<Action>() {
+            @Override
+            public void actionPerformed(ActionEvent<Action> actionEvent) {
+                switch (actionEvent.getActionId()) {
+                case HIDDEN:
+                    timer.stop();
+                    break;
+                case VISIBLE:
+                    timer.start();
+                    break;
+                }
+            }
+        });
+
+        view.setCheckDeadlockControlEnabled(vmInfoDAO.getVmInfo(vmRef).isAlive());
+    }
+
+    private void checkForDeadLock() {
+        askAgentToCheckForDeadLock();
+        checkStorageForDeadLockData();
+    }
+
+    private void askAgentToCheckForDeadLock() {
+        collector.requestDeadLockCheck();
+    }
+
+    private void checkStorageForDeadLockData() {
+        VmDeadLockData data = collector.getLatestDeadLockData();
+        if (data == null) {
+            // no deadlock data; so don't update anything
+            return;
+        }
+
+        String description = data.getDeadLockDescription();
+        if (description.equals(VmDeadLockData.NO_DEADLOCK)) {
+            description = NO_DEADLOCK;
+        }
+        this.descriptionRef.set(description);
+
+    }
+
+    private void updateViewIfNeeded() {
+        String rawDeadlockData = descriptionRef.get();
+
+        if (!rawDeadlockData.equals(previousDeadlockData)) {
+            Information parsed = null;
+
+            if (!rawDeadlockData.equals(NO_DEADLOCK)) {
+                try {
+                    parsed = new DeadlockParser().parse(new BufferedReader(new StringReader(rawDeadlockData)));
+                } catch (IOException | ParseException e) {
+                    logger.log(Level.FINE, "Failed to parse deadlock data. Visualizations might not show up correctly.", e);
+                }
+            }
+
+            view.setDeadLockInformation(parsed, rawDeadlockData);
+            previousDeadlockData = rawDeadlockData;
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCache.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2016 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.controller.internal.cache;
+
+import com.redhat.thermostat.common.ApplicationCache;
+import java.util.HashMap;
+
+/**
+ *
+ */
+public class AppCache {
+    private static final String LOCK = new String("AppCacheLock");
+
+    private AppCacheKey root;
+    private ApplicationCache cache;
+
+    public AppCache(AppCacheKey root, ApplicationCache cache) {
+        this.root = root;
+        this.cache = cache;
+    }
+
+    public <E> void save(AppCacheKey key, E value) {
+        synchronized (LOCK) {
+            HashMap<AppCacheKey, E> localCache = getLocalCache();
+            localCache.put(key, value);
+            saveLocalCache(localCache);
+        }
+    }
+
+    public <E> E retrieve(AppCacheKey key) {
+        synchronized (LOCK) {
+            HashMap<AppCacheKey, E> localCache = getLocalCache();
+            return localCache.get(key);
+        }
+    }
+
+    private <E> HashMap<AppCacheKey, E> getLocalCache() {
+        HashMap<AppCacheKey, E> localCache =
+                (HashMap<AppCacheKey, E>) cache.getAttribute(root);
+
+        if (localCache == null) {
+            localCache = new HashMap<>();
+            saveLocalCache(localCache);
+        }
+
+        return localCache;
+    }
+
+    private <E> void saveLocalCache(HashMap<AppCacheKey, E> localCache) {
+        cache.addAttribute(root, localCache);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheKey.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2016 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.controller.internal.cache;
+
+import java.util.Objects;
+
+/**
+ *
+ */
+public class AppCacheKey {
+    private String UUID;
+    private String targetClass;
+
+    public AppCacheKey(Class<?> hostClass, Class<?> targetClass) {
+        this(hostClass.getSimpleName(), targetClass.getSimpleName());
+    }
+
+    public AppCacheKey(String UUID, Class<?> targetClass) {
+        this(UUID, targetClass.getSimpleName());
+    }
+
+    private AppCacheKey(String UUID, String targetClass) {
+        this.UUID = UUID;
+        this.targetClass = targetClass;
+        Objects.requireNonNull(UUID);
+        Objects.requireNonNull(targetClass);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AppCacheKey that = (AppCacheKey) o;
+
+        if (!UUID.equals(that.UUID)) return false;
+        if (!targetClass.equals(that.targetClass)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = UUID.hashCode();
+        result = 31 * result + targetClass.hashCode();
+        return result;
+    }
+}
--- a/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-controllers/src/main/java/com/redhat/thermostat/thread/client/controller/osgi/Activator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -47,7 +47,7 @@
 import com.redhat.thermostat.thread.client.common.ThreadViewProvider;
 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 com.redhat.thermostat.thread.client.controller.internal.ThreadInformationServiceImpl;
 import com.redhat.thermostat.thread.dao.LockInfoDao;
 
 import java.util.Dictionary;
--- a/thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/impl/strings.properties	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-CONTROLLER_NAME = Threads
-WARNING_CANNOT_ENABLE = Cannot enable Thread recording
-WARNING_CANNOT_DISABLE = Cannot disable Thread recording
-CHECKING_FOR_DEADLOCKS = Checking for deadlocks\u2026
-NO_DEADLOCK_DETECTED = No Deadlocks Detected.
-STARTING_MONITORING = Starting thread monitoring\u2026
-STOPPING_MONITORING = Stopping thread monitoring\u2026
-LOADING_SESSION_LIST = Retrieving the list of recorded thread timeline sessions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/main/resources/com/redhat/thermostat/thread/client/controller/internal/strings.properties	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,8 @@
+CONTROLLER_NAME = Threads
+WARNING_CANNOT_ENABLE = Cannot enable Thread recording
+WARNING_CANNOT_DISABLE = Cannot disable Thread recording
+CHECKING_FOR_DEADLOCKS = Checking for deadlocks\u2026
+NO_DEADLOCK_DETECTED = No Deadlocks Detected.
+STARTING_MONITORING = Starting thread monitoring\u2026
+STOPPING_MONITORING = Stopping thread monitoring\u2026
+LOADING_SESSION_LIST = Retrieving the list of recorded thread timeline sessions
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/LocaleResourcesTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
-
-public class LocaleResourcesTest  extends AbstractLocaleResourcesTest<LocaleResources> {
-
-    @Override
-    protected Class<LocaleResources> getEnumClass() {
-        return LocaleResources.class;
-    }
-
-    @Override
-    protected String getResourceBundle() {
-        return LocaleResources.RESOURCE_BUNDLE;
-    }
-
-}
-
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/LockControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.client.core.views.BasicView;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.thread.client.common.view.LockView;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-import com.redhat.thermostat.thread.model.LockInfo;
-
-public class LockControllerTest {
-
-    private LockView view;
-    private Timer timer;
-    private LockInfoDao dao;
-    private VmRef vm;
-
-    @Before
-    public void setup() {
-        view = mock(LockView.class);
-        timer = mock(Timer.class);
-        dao = mock(LockInfoDao.class);
-        vm = mock(VmRef.class);
-    }
-
-    @Test
-    public void verifyVisibilityEnablesAndDisablesTimer() {
-        LockController controller = new LockController(view, timer, dao, vm);
-        controller.initialize();
-
-        ArgumentCaptor<ActionListener> actionListenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        verify(view).addActionListener(actionListenerCaptor.capture());
-
-        ActionListener listener = actionListenerCaptor.getValue();
-        listener.actionPerformed(new ActionEvent(view, BasicView.Action.VISIBLE));
-
-        verify(timer).start();
-
-        listener.actionPerformed(new ActionEvent(view, BasicView.Action.HIDDEN));
-
-        verify(timer).stop();
-    }
-
-    @Test
-    public void verifyViewIsNotUpdatedOnNoData() {
-        when(dao.getLatestLockInfo(vm)).thenReturn(null);
-
-        LockController controller = new LockController(view, timer, dao, vm);
-        controller.initialize();
-
-        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        verify(timer).setAction(captor.capture());
-        Runnable updater = captor.getValue();
-
-        verify(view, never()).setLatestLockData(isA(LockInfo.class));
-
-        updater.run();
-
-        verify(view, never()).setLatestLockData(isA(LockInfo.class));
-    }
-
-    @Test
-    public void verifyViewIsUpdatedWithData() {
-        LockInfo lockInfo = new LockInfo();
-        when(dao.getLatestLockInfo(vm)).thenReturn(lockInfo);
-
-        LockController controller = new LockController(view, timer, dao, vm);
-        controller.initialize();
-
-        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        verify(timer).setAction(captor.capture());
-        Runnable updater = captor.getValue();
-
-        verify(view, never()).setLatestLockData(isA(LockInfo.class));
-
-        updater.run();
-
-        verify(view).setLatestLockData(lockInfo);
-    }
-}
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadCountControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,172 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.client.core.views.BasicView;
-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.LivingDaemonThreadDifferenceChart;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
-import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadSession;
-import com.redhat.thermostat.thread.model.ThreadSummary;
-
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.List;
-
-import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
-
-import org.jfree.chart.JFreeChart;
-import org.jfree.data.xy.XYDataset;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-// this is not a GUI test, but testGetThreadInformation uses AWT under the hood 
-@RunWith(CacioFESTRunner.class)
-public class ThreadCountControllerTest {
-    
-    private Timer timer;
-    private Runnable threadAction;
-    private ThreadCountView view;
-    private ThreadCollector collector;
-    
-    private ActionListener<ThreadCountView.Action> actionListener;
-
-    @Before
-    public void setUp() {
-        timer = mock(Timer.class);
-        view = mock(ThreadCountView.class);
-        collector = mock(ThreadCollector.class);
-    }
-    
-    @Test
-    public void testGetThreadInformation() {
-        
-        ArgumentCaptor<LivingDaemonThreadDifferenceChart> modelCaptor =
-                ArgumentCaptor.forClass(LivingDaemonThreadDifferenceChart.class);
-        
-        
-        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(timer).setAction(captor.capture());
-
-        ThreadCountController controller = new ThreadCountController(view, collector, timer);
-        controller.initialize();
-
-        ThreadSummary summary = mock(ThreadSummary.class);
-        when(summary.getTimeStamp()).thenReturn(5l);
-        when(summary.getCurrentLiveThreads()).thenReturn(42l);
-        when(summary.getCurrentDaemonThreads()).thenReturn(2l);
-
-        ThreadSummary summary0 = mock(ThreadSummary.class);
-        when(summary0.getTimeStamp()).thenReturn(2l);
-        when(summary0.getCurrentLiveThreads()).thenReturn(43l);
-        when(summary0.getCurrentDaemonThreads()).thenReturn(1l);
-        
-        List<ThreadSummary> summaries = new ArrayList<>();
-        summaries.add(summary);
-        summaries.add(summary0);
-
-        SessionID lastSession = mock(SessionID.class);
-        when(collector.getLastThreadSession()).thenReturn(lastSession);
-
-        when(collector.getLatestThreadSummary()).thenReturn(summary);
-
-        List<ThreadSession> sessions = new ArrayList<>();
-        when(collector.getThreadSessions(isA(Range.class))).thenReturn(sessions);
-        when(collector.getThreadSummary(isA(Range.class))).thenReturn(summaries);
-
-        threadAction = captor.getValue();
-        threadAction.run();
-
-        verify(collector).getLatestThreadSummary();
-
-        verify(view).setLiveThreads("42");
-        verify(view).setDaemonThreads("2");
-        
-        verify(view).updateLivingDaemonTimeline(modelCaptor.capture());
-        LivingDaemonThreadDifferenceChart model = modelCaptor.getValue();
-        
-        assertNotNull(model);
-        
-        JFreeChart chart = model.createChart(100, Color.BLACK);
-        XYDataset dataSet = chart.getXYPlot().getDataset();
-        assertEquals(2, dataSet.getSeriesCount());
-        
-        // total and living
-        assertEquals(2l, dataSet.getX(0, 0));
-        assertEquals(5l, dataSet.getX(0, 1));
-
-        // the actual numbers
-        assertEquals(43.0, dataSet.getY(0, 0));
-        assertEquals(42.0, dataSet.getY(0, 1));
-        assertEquals(1.0, dataSet.getY(1, 0));        
-        assertEquals(2.0, dataSet.getY(1, 1));
-    }
-    
-    @Test
-    public void testTimerStartAndStop() {
-        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
-
-        ThreadCountController controller = new ThreadCountController(view, collector, timer);
-        controller.initialize();
-                
-        actionListener = viewArgumentCaptor.getValue();
-        actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.VISIBLE));
-        
-        verify(timer).start();
-        
-        actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.HIDDEN));
-
-        verify(timer).stop();        
-    }
-}
-
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadInformationControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,350 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import com.redhat.thermostat.client.core.progress.ProgressNotifier;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.ApplicationCache;
-import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.TimerFactory;
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.storage.core.HostRef;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.storage.model.VmInfo;
-import com.redhat.thermostat.thread.client.common.ThreadTableBean;
-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.view.LockView;
-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;
-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.dao.LockInfoDao;
-import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadSession;
-import junit.framework.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class ThreadInformationControllerTest {
-
-    private ThreadView view;
-
-    private ActionListener<ThreadTableView.ThreadSelectionAction> threadTableActionListener;
-
-    private ThreadViewProvider viewFactory;
-    private ThreadInformationController controller;
-    
-    private ApplicationService appService;
-    private ExecutorService appExecutor;
-
-    private VmInfo vmInfo;
-    private VmInfoDAO vmInfoDao;
-    private LockInfoDao lockInfoDao;
-
-    private ThreadTableView threadTableView;
-    private VmDeadLockView deadLockView;
-    private ThreadTimelineView threadTimelineView;
-    private ThreadCountView threadCountView;
-    private LockView lockView;
-
-    @Before
-    public void setUp() {
-
-        appService = mock(ApplicationService.class);
-        vmInfo = mock(VmInfo.class);
-        when(vmInfo.isAlive()).thenReturn(true);
-        vmInfoDao = mock(VmInfoDAO.class);
-        when(vmInfoDao.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo);
-        lockInfoDao = mock(LockInfoDao.class);
-        setUpTimers();
-        setupCache();
-        setupExecutor();
-        setUpView();
-    }
-
-    private void setUpView() {
-        deadLockView = mock(VmDeadLockView.class);
-        threadTableView = mock(ThreadTableView.class);
-        threadTimelineView = mock(ThreadTimelineView.class);
-        threadCountView = mock(ThreadCountView.class);
-        lockView = mock(LockView.class);
-        
-        view = mock(ThreadView.class);
-        viewFactory = mock(ThreadViewProvider.class);
-        when(viewFactory.createView()).thenReturn(view);
-        
-        when(view.createDeadLockView()).thenReturn(deadLockView);
-        when(view.createThreadTableView()).thenReturn(threadTableView);
-        when(view.createThreadTimelineView()).thenReturn(threadTimelineView);
-        when(view.createThreadCountView()).thenReturn(threadCountView);
-        when(view.createLockView()).thenReturn(lockView);
-
-    }
-    
-    private void setUpTimers() {
-        Timer timer = mock(Timer.class);
-
-        TimerFactory timerFactory = mock(TimerFactory.class);
-        when(timerFactory.createTimer()).thenReturn(timer);
-        when(appService.getTimerFactory()).thenReturn(timerFactory);
-    }
-    
-    private void setupCache() {
-        ApplicationCache cache = mock(ApplicationCache.class);
-        when(appService.getApplicationCache()).thenReturn(cache);
-    }
-
-    private void setupExecutor() {
-        appExecutor = mock(ExecutorService.class);
-        when(appService.getApplicationExecutor()).thenReturn(appExecutor);
-    }
-
-    private void setUpListeners() {        
-        doNothing().when(view).addActionListener(any(ActionListener.class));
-        
-        ArgumentCaptor<ActionListener> threadTableViewCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(threadTableView).addThreadSelectionActionListener(threadTableViewCaptor.capture());
-        
-        createController();
-        
-        threadTableActionListener = threadTableViewCaptor.getValue();
-    }
-    
-    private void createController() {
-
-        VmRef ref = mock(VmRef.class);
-        HostRef agent = mock(HostRef.class);
-        when(ref.getHostRef()).thenReturn(agent);
-        when(agent.getAgentId()).thenReturn("0xcafe");
-
-        ThreadCollectorFactory collectorFactory = mock(ThreadCollectorFactory.class);
-        ThreadCollector collector = mock(ThreadCollector.class);
-        when(collectorFactory.getCollector(ref)).thenReturn(collector);
-
-        ProgressNotifier notifier = mock(ProgressNotifier.class);
-
-        controller = new ThreadInformationController(ref, appService, vmInfoDao, lockInfoDao,
-                                                     collectorFactory,
-                                                     viewFactory, notifier);
-    }
-    
-    @Test
-    public void verifyViewCreateSubViewCalled() {
-        
-        createController();
-        
-        verify(view).createThreadTableView();
-        verify(view).createDeadLockView();
-        verify(view).createThreadTimelineView();
-        verify(view).createThreadCountView();
-    }
-    
-    @Test
-    public void verifyLiveRecording() {
-        
-        ActionListener<ThreadView.ThreadAction> threadActionListener;
-        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
-
-        VmRef ref = mock(VmRef.class);
-        when(ref.getVmId()).thenReturn("42");
-        HostRef agent = mock(HostRef.class);
-        when(ref.getHostRef()).thenReturn(agent);
-        when(agent.getAgentId()).thenReturn("0xcafe");
-
-        ThreadCollector collector = mock(ThreadCollector.class);
-        when(collector.isHarvesterCollecting()).thenReturn(false).thenReturn(true);
-        when(collector.startHarvester()).thenReturn(true);
-        when(collector.stopHarvester()).thenReturn(true).thenReturn(false);
-
-        ThreadCollectorFactory collectorFactory = mock(ThreadCollectorFactory.class);
-        when(collectorFactory.getCollector(ref)).thenReturn(collector);
-
-        ProgressNotifier notifier = mock(ProgressNotifier.class);
-
-        ArgumentCaptor<Runnable> longRunningTaskCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(appExecutor).execute(longRunningTaskCaptor.capture());
-
-        controller = new ThreadInformationController(ref, appService,
-                                                     vmInfoDao, lockInfoDao,
-                                                     collectorFactory,
-                                                     viewFactory, notifier);
-
-        verify(collector).isHarvesterCollecting();
-        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPED, false);
-
-        // each action event posts a task to the executor.
-        // make sure the task is posted and execute it manually in tests to see its effects.
-
-        threadActionListener = viewArgumentCaptor.getValue();
-        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.START_LIVE_RECORDING));
-
-        verify(appExecutor, times(1)).execute(isA(Runnable.class));
-        longRunningTaskCaptor.getValue().run();
-
-        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTING, false);
-        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTED, false);
-        verify(collector).startHarvester();
-
-        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING));
-
-        verify(appExecutor, times(2)).execute(isA(Runnable.class));
-        longRunningTaskCaptor.getValue().run();
-
-        verify(collector).stopHarvester();
-        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPING, false);
-        verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPED, false);
-
-        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING));
-
-        verify(appExecutor, times(3)).execute(isA(Runnable.class));
-        longRunningTaskCaptor.getValue().run();
-
-        verify(collector, times(2)).stopHarvester();
-        verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPING, false);
-        verify(view, times(3)).setRecording(ThreadView.MonitoringState.STOPPED, false);
-    }
-
-    @Test
-    public void verifyRecordingControlDisabledForDeadVms() {
-        when(vmInfo.isAlive()).thenReturn(false);
-
-        createController();
-
-        verify(view).setEnableRecordingControl(false);
-    }
-    
-    @Test
-    public void verifyTableViewLinksToDetailsView() {
-        setUpListeners();
-
-        ThreadTableBean bean = mock(ThreadTableBean.class);
-
-        ActionEvent<ThreadSelectionAction> event =
-                new ActionEvent<>(threadTableView, ThreadSelectionAction.SHOW_THREAD_DETAILS);
-        event.setPayload(bean);
-        
-        threadTableActionListener.actionPerformed(event);
-        verify(view).displayThreadDetails(bean);
-    }
-
-    @Test
-    public void verifySessionListDisplays() {
-
-        ThreadCollector collector = mock(ThreadCollector.class);
-        List<ThreadSession> list = new ArrayList<>();
-        list.add(mock(ThreadSession.class));
-        list.add(mock(ThreadSession.class));
-
-        when(collector.getThreadSessions(any(Range.class))).thenReturn(list);
-
-        ActionListener<ThreadView.ThreadAction> threadActionListener;
-        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
-
-        ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(appExecutor).execute(taskCaptor.capture());
-
-        createController();
-
-        controller.___injectCollectorForTesting(collector);
-
-        threadActionListener = viewArgumentCaptor.getValue();
-        ActionEvent<ThreadView.ThreadAction> action = new ActionEvent<>(view, ThreadView.ThreadAction.REQUEST_DISPLAY_RECORDED_SESSIONS);
-        threadActionListener.actionPerformed(action);
-
-        Runnable runnable = taskCaptor.getValue();
-        runnable.run();
-
-        ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class);
-        verify(view).displayTimelineSessionList(listCaptor.capture());
-
-        Assert.assertTrue(listCaptor.getValue().equals(list));
-    }
-
-    @Test
-    public void verifySessionLoads() {
-
-        ThreadTimelineController timeline = mock(ThreadTimelineController.class);
-        ThreadTableController table = mock(ThreadTableController.class);
-        ThreadCountController count = mock(ThreadCountController.class);
-        LockController lock = mock(LockController.class);
-        VmDeadLockController deadLock = mock(VmDeadLockController.class);
-
-        ThreadSession session = mock(ThreadSession.class);
-        SessionID id = mock(SessionID.class);
-        when(session.getSessionID()).thenReturn(id);
-
-        ActionListener<ThreadView.ThreadAction> threadActionListener;
-        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
-
-        createController();
-
-        controller.___injectControllersForTesting(timeline, table, count, lock, deadLock);
-
-        threadActionListener = viewArgumentCaptor.getValue();
-        ActionEvent<ThreadView.ThreadAction> action = new ActionEvent<>(view, ThreadView.ThreadAction.REQUEST_LOAD_SESSION);
-        action.setPayload(session);
-
-        threadActionListener.actionPerformed(action);
-
-        verify(timeline).setSession(id);
-        verify(table).setSession(id);
-    }
-}
-
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTableControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
-
-public class ThreadTableControllerTest {
-
-    private ThreadTableView view;
-    private ThreadCollector collector;
-    
-    private Timer timer;
-    
-    private ActionListener<ThreadTableView.Action> actionListener;
-    ArgumentCaptor<Runnable> timerActionCaptor;
-    
-    @Before
-    public void setUp() {
-        collector = mock(ThreadCollector.class);
-        
-        timer = mock(Timer.class);
-
-        view = mock(ThreadTableView.class);
-        
-        setUpTimers();
-    }
-    
-    private void setUpTimers() {
-        timer = mock(Timer.class);
-        timerActionCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(timer).setAction(timerActionCaptor.capture());
-    }
-    
-    @Test
-    public void testStartThreadTableController() {
-        
-        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
-        doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
-        
-        ThreadTableController controller = new ThreadTableController(view, collector, timer);
-        controller.initialize();
-
-        actionListener = viewArgumentCaptor.getValue();
-        actionListener.actionPerformed(new ActionEvent<>(view, ThreadTableView.Action.VISIBLE));
-        
-        verify(timer).start();
-        
-        actionListener.actionPerformed(new ActionEvent<>(view, ThreadTableView.Action.HIDDEN));
-
-        verify(timer).stop();
-    }
-}
-
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/ThreadTimelineControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,189 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-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.ThreadInfo;
-import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
-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.mockito.Matchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class ThreadTimelineControllerTest {
-
-    private ThreadTimelineView view;
-    private ThreadCollector collector;
-    private Timer timer;
-    private SessionID session;
-
-    @Before
-    public void setup() {
-        view = mock(ThreadTimelineView.class);
-        collector = mock(ThreadCollector.class);
-        timer = mock(Timer.class);
-        session = mock(SessionID.class);
-        when(session.get()).thenReturn("42");
-        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();
-
-        verify(collector).getLastThreadSession();
-        verify(collector).getThreadRange(session);
-    }
-
-    @Test
-    public void verifySessionIfSet() {
-        ArgumentCaptor<Runnable> captor =
-                ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(timer).setAction(captor.capture());
-
-        SessionID newSession = mock(SessionID.class);
-        when(newSession.get()).thenReturn("0");
-
-        ThreadTimelineController controller =
-                new ThreadTimelineController(view, collector, timer);
-        Runnable timerAction = captor.getValue();
-
-        timerAction.run();
-
-        verify(collector).getThreadRange(session);
-
-        controller.setSession(newSession);
-
-        timerAction.run();
-
-        verify(collector).getThreadRange(newSession);
-    }
-
-    @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 testAllBeansAreLoaded() {
-        ArgumentCaptor<Runnable> captor =
-                ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(timer).setAction(captor.capture());
-
-        ArgumentCaptor<ResultHandler> captor2 =
-                ArgumentCaptor.forClass(ResultHandler.class);
-        doNothing().when(collector).getThreadStates(any(SessionID.class),
-                                                    captor2.capture(),
-                                                    any(Range.class));
-
-        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();
-
-        ResultHandler handler = captor2.getValue();
-
-        ThreadState state0 = mock(ThreadState.class);
-        when(state0.getName()).thenReturn("state0");
-        when(state0.getId()).thenReturn(0l);
-        when(state0.getState()).thenReturn("NEW");
-
-        ThreadState state1 = mock(ThreadState.class);
-        when(state1.getName()).thenReturn("state1");
-        when(state1.getId()).thenReturn(1l);
-        when(state1.getState()).thenReturn("NEW");
-
-        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);
-
-        ThreadInfo info = new ThreadInfo();
-        info.setName("state0");
-        info.setId(0l);
-
-        verify(view).addThread(info);
-
-        info.setName("state1");
-        info.setId(1l);
-        verify(view).addThread(info);
-
-        info.setName("state2");
-        info.setId(2l);
-        verify(view).addThread(info);
-    }
-}
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/VmDeadLockControllerTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl;
-
-import static org.mockito.Matchers.isA;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.TimeUnit;
-
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.dao.VmInfoDAO;
-import com.redhat.thermostat.storage.model.VmInfo;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import com.redhat.thermostat.client.core.progress.ProgressNotifier;
-import com.redhat.thermostat.client.core.views.BasicView;
-import com.redhat.thermostat.client.core.views.BasicView.Action;
-import com.redhat.thermostat.common.ActionEvent;
-import com.redhat.thermostat.common.ActionListener;
-import com.redhat.thermostat.common.Timer;
-import com.redhat.thermostat.common.Timer.SchedulingType;
-import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
-import com.redhat.thermostat.thread.model.VmDeadLockData;
-
-public class VmDeadLockControllerTest {
-
-    private VmInfoDAO vmInfoDao;
-    private VmInfo vmInfo;
-    private VmRef vmRef;
-    private Timer timer;
-    private VmDeadLockView view;
-    private ThreadCollector collector;
-    private ExecutorService executor;
-    private ProgressNotifier notifier;
-
-    private VmDeadLockController controller;
-
-    @Before
-    public void setUp() {
-        vmInfoDao = mock(VmInfoDAO.class);
-
-        vmInfo = mock(VmInfo.class);
-        when(vmInfoDao.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo);
-        when(vmInfo.isAlive()).thenReturn(true);
-
-        vmRef = mock(VmRef.class);
-
-        timer = mock(Timer.class);
-
-        view = mock(VmDeadLockView.class);
-
-        collector = mock(ThreadCollector.class);
-
-        executor = mock(ExecutorService.class);
-
-        notifier = mock(ProgressNotifier.class);
-
-        controller = new VmDeadLockController(vmInfoDao, vmRef, view, collector, timer, executor, notifier);
-    }
-
-    @Test
-    public void verifyInitilizeRegistersActionListener() {
-        controller.initialize();
-
-        verify(view).addVmDeadLockViewActionListener(isA(ActionListener.class));
-    }
-
-    @Test
-    public void verifyRealDeadLockDataIsDisplayedOnViewAction() {
-        final String DESCRIPTION = "foo bar";
-        VmDeadLockData data = new VmDeadLockData("foo-agent");
-        data.setDeadLockDescription(DESCRIPTION);
-
-        ArgumentCaptor<Runnable> executionCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(executor).execute(executionCaptor.capture());
-
-        controller.initialize();
-
-        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
-        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
-
-        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
-
-        when(collector.getLatestDeadLockData()).thenReturn(data);
-
-        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
-
-        Runnable deferredTask = executionCaptor.getValue();
-        deferredTask.run();
-
-        verify(collector).requestDeadLockCheck();
-        verify(view).setDeadLockInformation(null, DESCRIPTION);
-    }
-
-    @Test
-    public void verifyNoDeadLockDataIsDisplayedOnViewAction() {
-        VmDeadLockData data = new VmDeadLockData("foo-agent");
-        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
-
-        ArgumentCaptor<Runnable> executionCaptor = ArgumentCaptor.forClass(Runnable.class);
-        doNothing().when(executor).execute(executionCaptor.capture());
-
-        controller.initialize();
-
-        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
-        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
-
-        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
-
-        when(collector.getLatestDeadLockData()).thenReturn(data);
-
-        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
-
-        Runnable deferredTask = executionCaptor.getValue();
-        deferredTask.run();
-
-        verify(collector).requestDeadLockCheck();
-        verify(view).setDeadLockInformation(null, "No Deadlocks Detected.");
-    }
-
-    @Test
-    public void verifyInitializeSetsUpTimer() {
-        controller.initialize();
-
-        verify(timer).setAction(isA(Runnable.class));
-        verify(timer).setDelay(5);
-        verify(timer).setInitialDelay(0);
-        verify(timer).setSchedulingType(SchedulingType.FIXED_DELAY);
-        verify(timer).setTimeUnit(TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void verifyTimerIsEnabledWhenViewIsVisible() {
-        controller.initialize();
-
-        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
-
-        verify(view).addActionListener(listenerCaptor.capture());
-
-        ActionListener<BasicView.Action> visibilityListener = listenerCaptor.getValue();
-
-        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.VISIBLE));
-
-        verify(timer).start();
-
-        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.HIDDEN));
-
-        verify(timer).stop();
-    }
-
-    @Test
-    public void verifyTimerActionRefreshesView() {
-        doThrow(new AssertionError()).when(collector).requestDeadLockCheck();
-
-        VmDeadLockData data = new VmDeadLockData("foo-agent");
-        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
-        controller.initialize();
-
-        when(collector.getLatestDeadLockData()).thenReturn(data);
-
-        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        verify(timer).setAction(runnableCaptor.capture());
-
-        Runnable action = runnableCaptor.getValue();
-
-        action.run();
-
-        verify(view).setDeadLockInformation(null, "No Deadlocks Detected.");
-    }
-
-    @Test
-    public void verifyTimerActionHandlesNoDataCorrectly() {
-        doThrow(new AssertionError()).when(collector).requestDeadLockCheck();
-
-        controller.initialize();
-
-        when(collector.getLatestDeadLockData()).thenReturn(null);
-
-        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
-        verify(timer).setAction(runnableCaptor.capture());
-
-        Runnable action = runnableCaptor.getValue();
-
-        action.run();
-
-        // pass if no exceptions thrown
-    }
-
-    @Test
-    public void verifyDeadlockControlEnabledWhenVmAlive() {
-        controller.initialize();
-        verify(view).setCheckDeadlockControlEnabled(true);
-    }
-
-    @Test
-    public void verifyDeadlockControlDisabledWhenVmDead() {
-        when(vmInfo.isAlive()).thenReturn(false);
-        controller.initialize();
-        verify(view).setCheckDeadlockControlEnabled(false);
-    }
-}
-
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheKeyTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl.cache;
-
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class AppCacheKeyTest {
-    @Test
-    public void testEquals() throws Exception {
-
-        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
-
-        assertFalse(key1.equals(key2));
-
-        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        assertEquals(key1, key2);
-
-        key1 = new AppCacheKey("test1", AppCacheKey.class);
-        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        assertFalse(key1.equals(key2));
-    }
-
-    @Test
-    public void testHashCode() throws Exception {
-        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
-
-        assertFalse(key1.hashCode() == key2.hashCode());
-        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        assertEquals(key1.hashCode(), key2.hashCode());
-    }
-
-    @Test
-    public void testInHashMap() {
-        Map<AppCacheKey, String> table = new HashMap<>();
-        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        table.put(key1, "1");
-
-        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
-        table.put(key2, "1");
-
-        assertTrue(table.containsKey(key1));
-        assertTrue(table.containsKey(key2));
-
-        assertEquals(table.get(key1), "1");
-        assertEquals(table.get(key2), "1");
-
-        key1 = new AppCacheKey("test1", AppCacheKey.class);
-        table.put(key1, "1");
-
-        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
-        table.put(key2, "2");
-
-        assertTrue(table.containsKey(key1));
-        assertTrue(table.containsKey(key2));
-
-        assertEquals(table.get(key1), "1");
-        assertEquals(table.get(key2), "2");
-    }
-}
--- a/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/impl/cache/AppCacheTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * Copyright 2012-2016 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.controller.impl.cache;
-
-import com.redhat.thermostat.common.ApplicationCache;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class AppCacheTest {
-    private ApplicationCache applicationCache;
-    private AppCacheKey key;
-
-    @Before
-    public void setUp() {
-        this.key = new AppCacheKey("root", AppCache.class);
-        applicationCache = new ApplicationCache();
-    }
-
-    @Test
-    public void testCache() throws Exception {
-        AppCache cache = new AppCache(key, applicationCache);
-
-        AppCacheKey key0 = new AppCacheKey("some-value-under-root-key", String.class);
-        cache.save(key0, "some-value-under-root-value");
-
-        String value = cache.retrieve(key0);
-        assertEquals(value, "some-value-under-root-value");
-
-        Object someValue = new Object();
-        AppCacheKey key1 = new AppCacheKey("some-other-value-under-root-key", Object.class);
-        cache.save(key1, someValue);
-
-        Object value2 = cache.retrieve(key1);
-        assertEquals(value2, someValue);
-
-        cache.save(key1, "some-value-under-root-value");
-        value = cache.retrieve(key1);
-        assertEquals(value, "some-value-under-root-value");
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/LocaleResourcesTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import com.redhat.thermostat.testutils.AbstractLocaleResourcesTest;
+
+public class LocaleResourcesTest  extends AbstractLocaleResourcesTest<LocaleResources> {
+
+    @Override
+    protected Class<LocaleResources> getEnumClass() {
+        return LocaleResources.class;
+    }
+
+    @Override
+    protected String getResourceBundle() {
+        return LocaleResources.RESOURCE_BUNDLE;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/LockControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.thread.client.common.view.LockView;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+import com.redhat.thermostat.thread.model.LockInfo;
+
+public class LockControllerTest {
+
+    private LockView view;
+    private Timer timer;
+    private LockInfoDao dao;
+    private VmRef vm;
+
+    @Before
+    public void setup() {
+        view = mock(LockView.class);
+        timer = mock(Timer.class);
+        dao = mock(LockInfoDao.class);
+        vm = mock(VmRef.class);
+    }
+
+    @Test
+    public void verifyVisibilityEnablesAndDisablesTimer() {
+        LockController controller = new LockController(view, timer, dao, vm);
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> actionListenerCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addActionListener(actionListenerCaptor.capture());
+
+        ActionListener listener = actionListenerCaptor.getValue();
+        listener.actionPerformed(new ActionEvent(view, BasicView.Action.VISIBLE));
+
+        verify(timer).start();
+
+        listener.actionPerformed(new ActionEvent(view, BasicView.Action.HIDDEN));
+
+        verify(timer).stop();
+    }
+
+    @Test
+    public void verifyViewIsNotUpdatedOnNoData() {
+        when(dao.getLatestLockInfo(vm)).thenReturn(null);
+
+        LockController controller = new LockController(view, timer, dao, vm);
+        controller.initialize();
+
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(captor.capture());
+        Runnable updater = captor.getValue();
+
+        verify(view, never()).setLatestLockData(isA(LockInfo.class));
+
+        updater.run();
+
+        verify(view, never()).setLatestLockData(isA(LockInfo.class));
+    }
+
+    @Test
+    public void verifyViewIsUpdatedWithData() {
+        LockInfo lockInfo = new LockInfo();
+        when(dao.getLatestLockInfo(vm)).thenReturn(lockInfo);
+
+        LockController controller = new LockController(view, timer, dao, vm);
+        controller.initialize();
+
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(captor.capture());
+        Runnable updater = captor.getValue();
+
+        verify(view, never()).setLatestLockData(isA(LockInfo.class));
+
+        updater.run();
+
+        verify(view).setLatestLockData(lockInfo);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadCountControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.data.xy.XYDataset;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.core.views.BasicView;
+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.LivingDaemonThreadDifferenceChart;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadSession;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+// this is not a GUI test, but testGetThreadInformation uses AWT under the hood 
+@RunWith(CacioFESTRunner.class)
+public class ThreadCountControllerTest {
+    
+    private Timer timer;
+    private Runnable threadAction;
+    private ThreadCountView view;
+    private ThreadCollector collector;
+    
+    private ActionListener<ThreadCountView.Action> actionListener;
+
+    @Before
+    public void setUp() {
+        timer = mock(Timer.class);
+        view = mock(ThreadCountView.class);
+        collector = mock(ThreadCollector.class);
+    }
+    
+    @Test
+    public void testGetThreadInformation() {
+        
+        ArgumentCaptor<LivingDaemonThreadDifferenceChart> modelCaptor =
+                ArgumentCaptor.forClass(LivingDaemonThreadDifferenceChart.class);
+        
+        
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
+
+        ThreadCountController controller = new ThreadCountController(view, collector, timer);
+        controller.initialize();
+
+        ThreadSummary summary = mock(ThreadSummary.class);
+        when(summary.getTimeStamp()).thenReturn(5l);
+        when(summary.getCurrentLiveThreads()).thenReturn(42l);
+        when(summary.getCurrentDaemonThreads()).thenReturn(2l);
+
+        ThreadSummary summary0 = mock(ThreadSummary.class);
+        when(summary0.getTimeStamp()).thenReturn(2l);
+        when(summary0.getCurrentLiveThreads()).thenReturn(43l);
+        when(summary0.getCurrentDaemonThreads()).thenReturn(1l);
+        
+        List<ThreadSummary> summaries = new ArrayList<>();
+        summaries.add(summary);
+        summaries.add(summary0);
+
+        SessionID lastSession = mock(SessionID.class);
+        when(collector.getLastThreadSession()).thenReturn(lastSession);
+
+        when(collector.getLatestThreadSummary()).thenReturn(summary);
+
+        List<ThreadSession> sessions = new ArrayList<>();
+        when(collector.getThreadSessions(isA(Range.class))).thenReturn(sessions);
+        when(collector.getThreadSummary(isA(Range.class))).thenReturn(summaries);
+
+        threadAction = captor.getValue();
+        threadAction.run();
+
+        verify(collector).getLatestThreadSummary();
+
+        verify(view).setLiveThreads("42");
+        verify(view).setDaemonThreads("2");
+        
+        verify(view).updateLivingDaemonTimeline(modelCaptor.capture());
+        LivingDaemonThreadDifferenceChart model = modelCaptor.getValue();
+        
+        assertNotNull(model);
+        
+        JFreeChart chart = model.createChart(100, Color.BLACK);
+        XYDataset dataSet = chart.getXYPlot().getDataset();
+        assertEquals(2, dataSet.getSeriesCount());
+        
+        // total and living
+        assertEquals(2l, dataSet.getX(0, 0));
+        assertEquals(5l, dataSet.getX(0, 1));
+
+        // the actual numbers
+        assertEquals(43.0, dataSet.getY(0, 0));
+        assertEquals(42.0, dataSet.getY(0, 1));
+        assertEquals(1.0, dataSet.getY(1, 0));        
+        assertEquals(2.0, dataSet.getY(1, 1));
+    }
+    
+    @Test
+    public void testTimerStartAndStop() {
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
+
+        ThreadCountController controller = new ThreadCountController(view, collector, timer);
+        controller.initialize();
+                
+        actionListener = viewArgumentCaptor.getValue();
+        actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.VISIBLE));
+        
+        verify(timer).start();
+        
+        actionListener.actionPerformed(new ActionEvent<>(view, BasicView.Action.HIDDEN));
+
+        verify(timer).stop();        
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadInformationControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.core.progress.ProgressNotifier;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.ApplicationCache;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.TimerFactory;
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+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.view.LockView;
+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;
+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.dao.LockInfoDao;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadSession;
+
+import junit.framework.Assert;
+
+public class ThreadInformationControllerTest {
+
+    private ThreadView view;
+
+    private ActionListener<ThreadTableView.ThreadSelectionAction> threadTableActionListener;
+
+    private ThreadViewProvider viewFactory;
+    private ThreadInformationController controller;
+    
+    private ApplicationService appService;
+    private ExecutorService appExecutor;
+
+    private VmInfo vmInfo;
+    private VmInfoDAO vmInfoDao;
+    private LockInfoDao lockInfoDao;
+
+    private ThreadTableView threadTableView;
+    private VmDeadLockView deadLockView;
+    private ThreadTimelineView threadTimelineView;
+    private ThreadCountView threadCountView;
+    private LockView lockView;
+
+    @Before
+    public void setUp() {
+
+        appService = mock(ApplicationService.class);
+        vmInfo = mock(VmInfo.class);
+        when(vmInfo.isAlive()).thenReturn(true);
+        vmInfoDao = mock(VmInfoDAO.class);
+        when(vmInfoDao.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo);
+        lockInfoDao = mock(LockInfoDao.class);
+        setUpTimers();
+        setupCache();
+        setupExecutor();
+        setUpView();
+    }
+
+    private void setUpView() {
+        deadLockView = mock(VmDeadLockView.class);
+        threadTableView = mock(ThreadTableView.class);
+        threadTimelineView = mock(ThreadTimelineView.class);
+        threadCountView = mock(ThreadCountView.class);
+        lockView = mock(LockView.class);
+        
+        view = mock(ThreadView.class);
+        viewFactory = mock(ThreadViewProvider.class);
+        when(viewFactory.createView()).thenReturn(view);
+        
+        when(view.createDeadLockView()).thenReturn(deadLockView);
+        when(view.createThreadTableView()).thenReturn(threadTableView);
+        when(view.createThreadTimelineView()).thenReturn(threadTimelineView);
+        when(view.createThreadCountView()).thenReturn(threadCountView);
+        when(view.createLockView()).thenReturn(lockView);
+
+    }
+    
+    private void setUpTimers() {
+        Timer timer = mock(Timer.class);
+
+        TimerFactory timerFactory = mock(TimerFactory.class);
+        when(timerFactory.createTimer()).thenReturn(timer);
+        when(appService.getTimerFactory()).thenReturn(timerFactory);
+    }
+    
+    private void setupCache() {
+        ApplicationCache cache = mock(ApplicationCache.class);
+        when(appService.getApplicationCache()).thenReturn(cache);
+    }
+
+    private void setupExecutor() {
+        appExecutor = mock(ExecutorService.class);
+        when(appService.getApplicationExecutor()).thenReturn(appExecutor);
+    }
+
+    private void setUpListeners() {        
+        doNothing().when(view).addActionListener(any(ActionListener.class));
+        
+        ArgumentCaptor<ActionListener> threadTableViewCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(threadTableView).addThreadSelectionActionListener(threadTableViewCaptor.capture());
+        
+        createController();
+        
+        threadTableActionListener = threadTableViewCaptor.getValue();
+    }
+    
+    private void createController() {
+
+        VmRef ref = mock(VmRef.class);
+        HostRef agent = mock(HostRef.class);
+        when(ref.getHostRef()).thenReturn(agent);
+        when(agent.getAgentId()).thenReturn("0xcafe");
+
+        ThreadCollectorFactory collectorFactory = mock(ThreadCollectorFactory.class);
+        ThreadCollector collector = mock(ThreadCollector.class);
+        when(collectorFactory.getCollector(ref)).thenReturn(collector);
+
+        ProgressNotifier notifier = mock(ProgressNotifier.class);
+
+        controller = new ThreadInformationController(ref, appService, vmInfoDao, lockInfoDao,
+                                                     collectorFactory,
+                                                     viewFactory, notifier);
+    }
+    
+    @Test
+    public void verifyViewCreateSubViewCalled() {
+        
+        createController();
+        
+        verify(view).createThreadTableView();
+        verify(view).createDeadLockView();
+        verify(view).createThreadTimelineView();
+        verify(view).createThreadCountView();
+    }
+    
+    @Test
+    public void verifyLiveRecording() {
+        
+        ActionListener<ThreadView.ThreadAction> threadActionListener;
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
+
+        VmRef ref = mock(VmRef.class);
+        when(ref.getVmId()).thenReturn("42");
+        HostRef agent = mock(HostRef.class);
+        when(ref.getHostRef()).thenReturn(agent);
+        when(agent.getAgentId()).thenReturn("0xcafe");
+
+        ThreadCollector collector = mock(ThreadCollector.class);
+        when(collector.isHarvesterCollecting()).thenReturn(false).thenReturn(true);
+        when(collector.startHarvester()).thenReturn(true);
+        when(collector.stopHarvester()).thenReturn(true).thenReturn(false);
+
+        ThreadCollectorFactory collectorFactory = mock(ThreadCollectorFactory.class);
+        when(collectorFactory.getCollector(ref)).thenReturn(collector);
+
+        ProgressNotifier notifier = mock(ProgressNotifier.class);
+
+        ArgumentCaptor<Runnable> longRunningTaskCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(appExecutor).execute(longRunningTaskCaptor.capture());
+
+        controller = new ThreadInformationController(ref, appService,
+                                                     vmInfoDao, lockInfoDao,
+                                                     collectorFactory,
+                                                     viewFactory, notifier);
+
+        verify(collector).isHarvesterCollecting();
+        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPED, false);
+
+        // each action event posts a task to the executor.
+        // make sure the task is posted and execute it manually in tests to see its effects.
+
+        threadActionListener = viewArgumentCaptor.getValue();
+        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.START_LIVE_RECORDING));
+
+        verify(appExecutor, times(1)).execute(isA(Runnable.class));
+        longRunningTaskCaptor.getValue().run();
+
+        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTING, false);
+        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STARTED, false);
+        verify(collector).startHarvester();
+
+        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING));
+
+        verify(appExecutor, times(2)).execute(isA(Runnable.class));
+        longRunningTaskCaptor.getValue().run();
+
+        verify(collector).stopHarvester();
+        verify(view, times(1)).setRecording(ThreadView.MonitoringState.STOPPING, false);
+        verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPED, false);
+
+        threadActionListener.actionPerformed(new ActionEvent<>(view, ThreadView.ThreadAction.STOP_LIVE_RECORDING));
+
+        verify(appExecutor, times(3)).execute(isA(Runnable.class));
+        longRunningTaskCaptor.getValue().run();
+
+        verify(collector, times(2)).stopHarvester();
+        verify(view, times(2)).setRecording(ThreadView.MonitoringState.STOPPING, false);
+        verify(view, times(3)).setRecording(ThreadView.MonitoringState.STOPPED, false);
+    }
+
+    @Test
+    public void verifyRecordingControlDisabledForDeadVms() {
+        when(vmInfo.isAlive()).thenReturn(false);
+
+        createController();
+
+        verify(view).setEnableRecordingControl(false);
+    }
+    
+    @Test
+    public void verifyTableViewLinksToDetailsView() {
+        setUpListeners();
+
+        ThreadTableBean bean = mock(ThreadTableBean.class);
+
+        ActionEvent<ThreadSelectionAction> event =
+                new ActionEvent<>(threadTableView, ThreadSelectionAction.SHOW_THREAD_DETAILS);
+        event.setPayload(bean);
+        
+        threadTableActionListener.actionPerformed(event);
+        verify(view).displayThreadDetails(bean);
+    }
+
+    @Test
+    public void verifySessionListDisplays() {
+
+        ThreadCollector collector = mock(ThreadCollector.class);
+        List<ThreadSession> list = new ArrayList<>();
+        list.add(mock(ThreadSession.class));
+        list.add(mock(ThreadSession.class));
+
+        when(collector.getThreadSessions(any(Range.class))).thenReturn(list);
+
+        ActionListener<ThreadView.ThreadAction> threadActionListener;
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
+
+        ArgumentCaptor<Runnable> taskCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(appExecutor).execute(taskCaptor.capture());
+
+        createController();
+
+        controller.___injectCollectorForTesting(collector);
+
+        threadActionListener = viewArgumentCaptor.getValue();
+        ActionEvent<ThreadView.ThreadAction> action = new ActionEvent<>(view, ThreadView.ThreadAction.REQUEST_DISPLAY_RECORDED_SESSIONS);
+        threadActionListener.actionPerformed(action);
+
+        Runnable runnable = taskCaptor.getValue();
+        runnable.run();
+
+        ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class);
+        verify(view).displayTimelineSessionList(listCaptor.capture());
+
+        Assert.assertTrue(listCaptor.getValue().equals(list));
+    }
+
+    @Test
+    public void verifySessionLoads() {
+
+        ThreadTimelineController timeline = mock(ThreadTimelineController.class);
+        ThreadTableController table = mock(ThreadTableController.class);
+        ThreadCountController count = mock(ThreadCountController.class);
+        LockController lock = mock(LockController.class);
+        VmDeadLockController deadLock = mock(VmDeadLockController.class);
+
+        ThreadSession session = mock(ThreadSession.class);
+        SessionID id = mock(SessionID.class);
+        when(session.getSessionID()).thenReturn(id);
+
+        ActionListener<ThreadView.ThreadAction> threadActionListener;
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addThreadActionListener(viewArgumentCaptor.capture());
+
+        createController();
+
+        controller.___injectControllersForTesting(timeline, table, count, lock, deadLock);
+
+        threadActionListener = viewArgumentCaptor.getValue();
+        ActionEvent<ThreadView.ThreadAction> action = new ActionEvent<>(view, ThreadView.ThreadAction.REQUEST_LOAD_SESSION);
+        action.setPayload(session);
+
+        threadActionListener.actionPerformed(action);
+
+        verify(timeline).setSession(id);
+        verify(table).setSession(id);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTableControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.ThreadTableView;
+
+public class ThreadTableControllerTest {
+
+    private ThreadTableView view;
+    private ThreadCollector collector;
+    
+    private Timer timer;
+    
+    private ActionListener<ThreadTableView.Action> actionListener;
+    ArgumentCaptor<Runnable> timerActionCaptor;
+    
+    @Before
+    public void setUp() {
+        collector = mock(ThreadCollector.class);
+        
+        timer = mock(Timer.class);
+
+        view = mock(ThreadTableView.class);
+        
+        setUpTimers();
+    }
+    
+    private void setUpTimers() {
+        timer = mock(Timer.class);
+        timerActionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(timerActionCaptor.capture());
+    }
+    
+    @Test
+    public void testStartThreadTableController() {
+        
+        ArgumentCaptor<ActionListener> viewArgumentCaptor = ArgumentCaptor.forClass(ActionListener.class);
+        doNothing().when(view).addActionListener(viewArgumentCaptor.capture());
+        
+        ThreadTableController controller = new ThreadTableController(view, collector, timer);
+        controller.initialize();
+
+        actionListener = viewArgumentCaptor.getValue();
+        actionListener.actionPerformed(new ActionEvent<>(view, ThreadTableView.Action.VISIBLE));
+        
+        verify(timer).start();
+        
+        actionListener.actionPerformed(new ActionEvent<>(view, ThreadTableView.Action.HIDDEN));
+
+        verify(timer).stop();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/ThreadTimelineControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+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.ThreadInfo;
+import com.redhat.thermostat.thread.client.common.view.ThreadTimelineView;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadState;
+
+public class ThreadTimelineControllerTest {
+
+    private ThreadTimelineView view;
+    private ThreadCollector collector;
+    private Timer timer;
+    private SessionID session;
+
+    @Before
+    public void setup() {
+        view = mock(ThreadTimelineView.class);
+        collector = mock(ThreadCollector.class);
+        timer = mock(Timer.class);
+        session = mock(SessionID.class);
+        when(session.get()).thenReturn("42");
+        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();
+
+        verify(collector).getLastThreadSession();
+        verify(collector).getThreadRange(session);
+    }
+
+    @Test
+    public void verifySessionIfSet() {
+        ArgumentCaptor<Runnable> captor =
+                ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
+
+        SessionID newSession = mock(SessionID.class);
+        when(newSession.get()).thenReturn("0");
+
+        ThreadTimelineController controller =
+                new ThreadTimelineController(view, collector, timer);
+        Runnable timerAction = captor.getValue();
+
+        timerAction.run();
+
+        verify(collector).getThreadRange(session);
+
+        controller.setSession(newSession);
+
+        timerAction.run();
+
+        verify(collector).getThreadRange(newSession);
+    }
+
+    @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 testAllBeansAreLoaded() {
+        ArgumentCaptor<Runnable> captor =
+                ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(timer).setAction(captor.capture());
+
+        ArgumentCaptor<ResultHandler> captor2 =
+                ArgumentCaptor.forClass(ResultHandler.class);
+        doNothing().when(collector).getThreadStates(any(SessionID.class),
+                                                    captor2.capture(),
+                                                    any(Range.class));
+
+        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();
+
+        ResultHandler handler = captor2.getValue();
+
+        ThreadState state0 = mock(ThreadState.class);
+        when(state0.getName()).thenReturn("state0");
+        when(state0.getId()).thenReturn(0l);
+        when(state0.getState()).thenReturn("NEW");
+
+        ThreadState state1 = mock(ThreadState.class);
+        when(state1.getName()).thenReturn("state1");
+        when(state1.getId()).thenReturn(1l);
+        when(state1.getState()).thenReturn("NEW");
+
+        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);
+
+        ThreadInfo info = new ThreadInfo();
+        info.setName("state0");
+        info.setId(0l);
+
+        verify(view).addThread(info);
+
+        info.setName("state1");
+        info.setId(1l);
+        verify(view).addThread(info);
+
+        info.setName("state2");
+        info.setId(2l);
+        verify(view).addThread(info);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/VmDeadLockControllerTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2012-2016 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.controller.internal;
+
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+
+import com.redhat.thermostat.client.core.progress.ProgressNotifier;
+import com.redhat.thermostat.client.core.views.BasicView;
+import com.redhat.thermostat.client.core.views.BasicView.Action;
+import com.redhat.thermostat.common.ActionEvent;
+import com.redhat.thermostat.common.ActionListener;
+import com.redhat.thermostat.common.Timer;
+import com.redhat.thermostat.common.Timer.SchedulingType;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.dao.VmInfoDAO;
+import com.redhat.thermostat.storage.model.VmInfo;
+import com.redhat.thermostat.thread.client.common.collector.ThreadCollector;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView.VmDeadLockViewAction;
+import com.redhat.thermostat.thread.model.VmDeadLockData;
+
+public class VmDeadLockControllerTest {
+
+    private VmInfoDAO vmInfoDao;
+    private VmInfo vmInfo;
+    private VmRef vmRef;
+    private Timer timer;
+    private VmDeadLockView view;
+    private ThreadCollector collector;
+    private ExecutorService executor;
+    private ProgressNotifier notifier;
+
+    private VmDeadLockController controller;
+
+    @Before
+    public void setUp() {
+        vmInfoDao = mock(VmInfoDAO.class);
+
+        vmInfo = mock(VmInfo.class);
+        when(vmInfoDao.getVmInfo(isA(VmRef.class))).thenReturn(vmInfo);
+        when(vmInfo.isAlive()).thenReturn(true);
+
+        vmRef = mock(VmRef.class);
+
+        timer = mock(Timer.class);
+
+        view = mock(VmDeadLockView.class);
+
+        collector = mock(ThreadCollector.class);
+
+        executor = mock(ExecutorService.class);
+
+        notifier = mock(ProgressNotifier.class);
+
+        controller = new VmDeadLockController(vmInfoDao, vmRef, view, collector, timer, executor, notifier);
+    }
+
+    @Test
+    public void verifyInitilizeRegistersActionListener() {
+        controller.initialize();
+
+        verify(view).addVmDeadLockViewActionListener(isA(ActionListener.class));
+    }
+
+    @Test
+    public void verifyRealDeadLockDataIsDisplayedOnViewAction() {
+        final String DESCRIPTION = "foo bar";
+        VmDeadLockData data = new VmDeadLockData("foo-agent");
+        data.setDeadLockDescription(DESCRIPTION);
+
+        ArgumentCaptor<Runnable> executionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(executor).execute(executionCaptor.capture());
+
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
+
+        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
+
+        Runnable deferredTask = executionCaptor.getValue();
+        deferredTask.run();
+
+        verify(collector).requestDeadLockCheck();
+        verify(view).setDeadLockInformation(null, DESCRIPTION);
+    }
+
+    @Test
+    public void verifyNoDeadLockDataIsDisplayedOnViewAction() {
+        VmDeadLockData data = new VmDeadLockData("foo-agent");
+        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
+
+        ArgumentCaptor<Runnable> executionCaptor = ArgumentCaptor.forClass(Runnable.class);
+        doNothing().when(executor).execute(executionCaptor.capture());
+
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+        verify(view).addVmDeadLockViewActionListener(listenerCaptor.capture());
+
+        ActionListener<VmDeadLockViewAction> listener = (ActionListener<VmDeadLockViewAction>) listenerCaptor.getValue();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        listener.actionPerformed(new ActionEvent<VmDeadLockViewAction>(view, VmDeadLockViewAction.CHECK_FOR_DEADLOCK));
+
+        Runnable deferredTask = executionCaptor.getValue();
+        deferredTask.run();
+
+        verify(collector).requestDeadLockCheck();
+        verify(view).setDeadLockInformation(null, "No Deadlocks Detected.");
+    }
+
+    @Test
+    public void verifyInitializeSetsUpTimer() {
+        controller.initialize();
+
+        verify(timer).setAction(isA(Runnable.class));
+        verify(timer).setDelay(5);
+        verify(timer).setInitialDelay(0);
+        verify(timer).setSchedulingType(SchedulingType.FIXED_DELAY);
+        verify(timer).setTimeUnit(TimeUnit.SECONDS);
+    }
+
+    @Test
+    public void verifyTimerIsEnabledWhenViewIsVisible() {
+        controller.initialize();
+
+        ArgumentCaptor<ActionListener> listenerCaptor = (ArgumentCaptor<ActionListener>) ArgumentCaptor.forClass(ActionListener.class);
+
+        verify(view).addActionListener(listenerCaptor.capture());
+
+        ActionListener<BasicView.Action> visibilityListener = listenerCaptor.getValue();
+
+        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.VISIBLE));
+
+        verify(timer).start();
+
+        visibilityListener.actionPerformed(new ActionEvent<BasicView.Action>(view, Action.HIDDEN));
+
+        verify(timer).stop();
+    }
+
+    @Test
+    public void verifyTimerActionRefreshesView() {
+        doThrow(new AssertionError()).when(collector).requestDeadLockCheck();
+
+        VmDeadLockData data = new VmDeadLockData("foo-agent");
+        data.setDeadLockDescription(VmDeadLockData.NO_DEADLOCK);
+        controller.initialize();
+
+        when(collector.getLatestDeadLockData()).thenReturn(data);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(runnableCaptor.capture());
+
+        Runnable action = runnableCaptor.getValue();
+
+        action.run();
+
+        verify(view).setDeadLockInformation(null, "No Deadlocks Detected.");
+    }
+
+    @Test
+    public void verifyTimerActionHandlesNoDataCorrectly() {
+        doThrow(new AssertionError()).when(collector).requestDeadLockCheck();
+
+        controller.initialize();
+
+        when(collector.getLatestDeadLockData()).thenReturn(null);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(timer).setAction(runnableCaptor.capture());
+
+        Runnable action = runnableCaptor.getValue();
+
+        action.run();
+
+        // pass if no exceptions thrown
+    }
+
+    @Test
+    public void verifyDeadlockControlEnabledWhenVmAlive() {
+        controller.initialize();
+        verify(view).setCheckDeadlockControlEnabled(true);
+    }
+
+    @Test
+    public void verifyDeadlockControlDisabledWhenVmDead() {
+        when(vmInfo.isAlive()).thenReturn(false);
+        controller.initialize();
+        verify(view).setCheckDeadlockControlEnabled(false);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheKeyTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2012-2016 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.controller.internal.cache;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class AppCacheKeyTest {
+    @Test
+    public void testEquals() throws Exception {
+
+        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
+
+        assertFalse(key1.equals(key2));
+
+        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        assertEquals(key1, key2);
+
+        key1 = new AppCacheKey("test1", AppCacheKey.class);
+        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        assertFalse(key1.equals(key2));
+    }
+
+    @Test
+    public void testHashCode() throws Exception {
+        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
+
+        assertFalse(key1.hashCode() == key2.hashCode());
+        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        assertEquals(key1.hashCode(), key2.hashCode());
+    }
+
+    @Test
+    public void testInHashMap() {
+        Map<AppCacheKey, String> table = new HashMap<>();
+        AppCacheKey key1 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        table.put(key1, "1");
+
+        AppCacheKey key2 = new AppCacheKey("test2", AppCacheKeyTest.class);
+        table.put(key2, "1");
+
+        assertTrue(table.containsKey(key1));
+        assertTrue(table.containsKey(key2));
+
+        assertEquals(table.get(key1), "1");
+        assertEquals(table.get(key2), "1");
+
+        key1 = new AppCacheKey("test1", AppCacheKey.class);
+        table.put(key1, "1");
+
+        key2 = new AppCacheKey("test1", AppCacheKeyTest.class);
+        table.put(key2, "2");
+
+        assertTrue(table.containsKey(key1));
+        assertTrue(table.containsKey(key2));
+
+        assertEquals(table.get(key1), "1");
+        assertEquals(table.get(key2), "2");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-controllers/src/test/java/com/redhat/thermostat/thread/client/controller/internal/cache/AppCacheTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012-2016 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.controller.internal.cache;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.redhat.thermostat.common.ApplicationCache;
+
+public class AppCacheTest {
+    private ApplicationCache applicationCache;
+    private AppCacheKey key;
+
+    @Before
+    public void setUp() {
+        this.key = new AppCacheKey("root", AppCache.class);
+        applicationCache = new ApplicationCache();
+    }
+
+    @Test
+    public void testCache() throws Exception {
+        AppCache cache = new AppCache(key, applicationCache);
+
+        AppCacheKey key0 = new AppCacheKey("some-value-under-root-key", String.class);
+        cache.save(key0, "some-value-under-root-value");
+
+        String value = cache.retrieve(key0);
+        assertEquals(value, "some-value-under-root-value");
+
+        Object someValue = new Object();
+        AppCacheKey key1 = new AppCacheKey("some-other-value-under-root-key", Object.class);
+        cache.save(key1, someValue);
+
+        Object value2 = cache.retrieve(key1);
+        assertEquals(value2, someValue);
+
+        cache.save(key1, "some-value-under-root-value");
+        value = cache.retrieve(key1);
+        assertEquals(value, "some-value-under-root-value");
+    }
+}
--- a/thread/client-swing/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-swing/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -119,9 +119,9 @@
             </Export-Package>
             <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.model,
+              com.redhat.thermostat.thread.client.swing.internal,
+                com.redhat.thermostat.thread.client.swing.internal.timeline,
+                com.redhat.thermostat.thread.client.swing.internal.timeline.model,
                 com.redhat.thermostat.thread.client.swing.experimental.components,
                 com.redhat.thermostat.thread.client.swing.experimental.utils,
             </Private-Package>
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/SwingThreadViewService.java	Fri Apr 08 13:18:48 2016 +0200
@@ -39,7 +39,7 @@
 import com.redhat.thermostat.client.swing.UIDefaults;
 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.internal.SwingThreadView;
 
 public class SwingThreadViewService implements ThreadViewProvider {
     
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingLockView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,145 +0,0 @@
-/*
- * Copyright 2012-2016 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 java.awt.BorderLayout;
-import java.awt.Component;
-
-import javax.swing.JPanel;
-import javax.swing.SwingUtilities;
-import javax.swing.table.DefaultTableModel;
-
-import com.redhat.thermostat.client.swing.NonEditableTableModel;
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
-import com.redhat.thermostat.client.swing.components.ThermostatTable;
-import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.client.common.view.LockView;
-import com.redhat.thermostat.thread.model.LockInfo;
-
-public class SwingLockView extends LockView implements SwingComponent {
-
-    protected static final String TABLE_NAME = "locks-table";
-
-    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
-
-    private static final int VALUE_COLUMN = 1;
-
-    private JPanel topPanel;
-    private ThermostatTable table;
-    private DefaultTableModel model;
-
-    public SwingLockView() {
-        topPanel = new JPanel();
-        topPanel.setLayout(new BorderLayout());
-
-        model = new NonEditableTableModel(18, 2);
-        Object[][] dataVector = new Object[][] {
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_CONTENDED_LOCK_ATTEMPS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_DEFLATIONS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_EMPTY_NOTIFICATIONS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_FAILED_SPINS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_FUTILE_WAKEUPS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_INFLATIONS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_EXTANT).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_IN_CIRCULATION).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_SCAVENGED).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_NOTIFICATIONS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PARKS).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PRIVATE_A).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PRIVATE_B).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_ENTER).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_EXIT).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_NOTIFY).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_NOTIFY_ALL).getContents(), 0 },
-            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SUCCESSFUL_SPINS).getContents(), 0 },
-        };
-
-        Object[] columnIdentifiers = new Object[] {
-                translator.localize(LocaleResources.LOCK_COLUMN_NAME).getContents(),
-                translator.localize(LocaleResources.LOCK_COLUMN_VALUE).getContents()};
-        model.setDataVector(dataVector, columnIdentifiers);
-
-        table = new ThermostatTable(model);
-        table.setName(TABLE_NAME);
-
-        ThermostatScrollPane scrollPane = new ThermostatScrollPane(table);
-        topPanel.add(scrollPane, BorderLayout.CENTER);
-
-        new ComponentVisibilityNotifier().initialize(topPanel, notifier);
-    }
-
-    @Override
-    public void setLatestLockData(final LockInfo data) {
-        SwingUtilities.invokeLater(new Runnable() {
-            private int row = 0;
-            @Override
-            public void run() {
-                updateModel(data.getContendedLockAttempts());
-                updateModel(data.getDeflations());
-                updateModel(data.getEmptyNotifications());
-                updateModel(data.getFailedSpins());
-                updateModel(data.getFutileWakeups());
-                updateModel(data.getInflations());
-                updateModel(data.getMonExtant());
-                updateModel(data.getMonInCirculation());
-                updateModel(data.getMonScavenged());
-                updateModel(data.getNotifications());
-                updateModel(data.getParks());
-                updateModel(data.getPrivateA());
-                updateModel(data.getPrivateB());
-                updateModel(data.getSlowEnter());
-                updateModel(data.getSlowExit());
-                updateModel(data.getSlowNotify());
-                updateModel(data.getSlowNotifyAll());
-                updateModel(data.getSuccessfulSpins());
-            }
-
-            private void updateModel(long number) {
-                model.setValueAt(number, row, VALUE_COLUMN);
-                row++;
-            }
-        });
-    }
-    @Override
-    public Component getUiComponent() {
-        return topPanel;
-    }
-
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadCountView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +0,0 @@
-/*
- * Copyright 2012-2016 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.ComponentVisibleListener;
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.components.ChartPanel;
-import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
-import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
-
-import javax.swing.JPanel;
-import javax.swing.SwingUtilities;
-import javax.swing.SwingWorker;
-import java.awt.Component;
-
-public class SwingThreadCountView extends ThreadCountView implements SwingComponent {
-    
-    private ThreadAliveDaemonTimelinePanel timelinePanel;
-
-    public SwingThreadCountView() {
-        timelinePanel = new ThreadAliveDaemonTimelinePanel();
-        timelinePanel.addHierarchyListener(new ComponentVisibleListener() {
-            @Override
-            public void componentShown(Component component) {
-                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
-                    @Override
-                    protected Void doInBackground() throws Exception {
-                        SwingThreadCountView.this.notify(Action.VISIBLE);
-                        return null;
-                    }
-                };
-                worker.execute();
-            }
-
-            @Override
-            public void componentHidden(Component component) {
-                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
-                    @Override
-                    protected Void doInBackground() throws Exception {
-                        SwingThreadCountView.this.notify(Action.HIDDEN);
-                        return null;
-                    }
-                };
-                worker.execute();
-            }
-        });
-    }
-    
-    public void setLiveThreads(final String liveThreads) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                timelinePanel.getLiveThreads().setText(liveThreads);
-            }
-        });
-    };
-    
-    @Override
-    public void updateLivingDaemonTimeline(final LivingDaemonThreadDifferenceChart model)
-    {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                JPanel pane = timelinePanel.getTimelinePanel();
-                pane.removeAll();
-                
-                ChartPanel charts = new ChartPanel(model.createChart(pane.getWidth(), pane.getBackground()));
-                charts.setName("threadChartPanel");
-                pane.add(charts);
-                pane.revalidate();
-                pane.repaint();
-            }
-        });
-    }
-    
-    @Override
-    public void setDaemonThreads(final String daemonThreads) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                timelinePanel.getDaemonThreads().setText(daemonThreads);
-            }
-        });
-    }
-    
-    @Override
-    public Component getUiComponent() {
-        return timelinePanel;
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadDetailsView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * Copyright 2012-2016 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 java.awt.BorderLayout;
-import java.awt.Component;
-
-import javax.swing.ImageIcon;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.components.ChartPanel;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.ThreadTableBean;
-import com.redhat.thermostat.thread.client.common.chart.ThreadDeatailsPieChart;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.client.common.view.ThreadDetailsView;
-
-public class SwingThreadDetailsView extends ThreadDetailsView implements SwingComponent {
-
-    private JPanel details;
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-
-    SwingThreadDetailsView() {
-        details = new JPanel();
-        details.setLayout(new BorderLayout(0, 0));
-        
-        JLabel lblNewLabel = new JLabel(t.localize(LocaleResources.THREAD_DETAILS_EMTPY).getContents());
-        lblNewLabel.setIcon(new ImageIcon(getEmptyDetailsIcon().getData().array()));
-        details.add(lblNewLabel);
-    }
-    
-    @Override
-    public Component getUiComponent() {
-        return details;
-    }
-
-    @Override
-    public void setDetails(ThreadTableBean thread) {
-        details.removeAll();
-        
-        ThreadDetailsChart threadChart = new ThreadDetailsChart();
-        
-        ChartPanel threadSummary = new ChartPanel(new ThreadDeatailsPieChart(thread).createChart());
-        threadChart.add(threadSummary);
-        
-        details.add(threadChart);
-        details.repaint();
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTableView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/*
- * Copyright 2012-2016 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.NonEditableTableModel;
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.components.ThermostatTable;
-import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
-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 java.awt.Component;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.swing.SwingUtilities;
-import javax.swing.SwingWorker;
-import javax.swing.event.TableModelEvent;
-import javax.swing.event.TableModelListener;
-
-public class SwingThreadTableView extends ThreadTableView implements SwingComponent {
-
-    private boolean tableRepacked = false; 
-    
-    private int currentSelection = -1;
-    
-    private ThermostatTable table;
-    private ThreadTable tablePanel;
-
-    private Map<ThreadTableBean, Integer> beans;
-
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    
-    public SwingThreadTableView() {
-
-        beans = new HashMap<>();
-        tablePanel = new ThreadTable();
-        new ComponentVisibilityNotifier().initialize(tablePanel, notifier);
-        
-        table = new ThermostatTable(new ThreadViewTableModel());
-        table.setName("threadBeansTable");
-        table.getModel().addTableModelListener(new TableModelListener() {
-            @Override
-            public void tableChanged(TableModelEvent e) {
-                // NOTE: The fireTableDataChanged executes this listener
-                // before the internal listener, this means this update will
-                // be overridden since the default listener resets the model.
-                // So, although we are in the EDT, we need to ensure that
-                // we schedule this operation for later, rather than do it
-                // right away... isn't Swing fun?
-                SwingUtilities.invokeLater(new Runnable() {
-                    @Override
-                    public void run() {
-                        if (currentSelection != -1) {
-                            table.setRowSelectionInterval(currentSelection, currentSelection);
-                        }
-                    }
-                });
-            }
-        });
-        tablePanel.add(table.wrap());
-        
-        table.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                if (e.getClickCount() == 2) {
-                    ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
-                    int selectedRow = table.getSelectedRow();
-                    if (selectedRow != -1) {
-                        selectedRow = table.convertRowIndexToModel(selectedRow);
-                        final ThreadTableBean bean = model.infos.get(selectedRow);
-                        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
-                            protected Void doInBackground() throws Exception {
-                                threadTableNotifier.fireAction(ThreadSelectionAction.SHOW_THREAD_DETAILS, bean);
-                                return null;
-                            }
-                        };
-                        worker.execute();
-                    }
-                }
-            }
-        });
-    }
-
-    @Override
-    public void clear() {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
-                model.setRowCount(0);
-                beans.clear();
-            }
-        });
-    }
-
-    @Override
-    public Component getUiComponent() {
-        return tablePanel;
-    }
-    
-    @Override
-    public void display(final ThreadTableBean tableBean) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                
-                // reset the selection for the next iteration
-                // everything is happening in one thread, so there's no fear
-                currentSelection = -1;
-                
-                ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
-                int selectedRow = table.getSelectedRow();
-                
-                ThreadTableBean info = null;
-                if (selectedRow != -1) {
-                    info = model.infos.get(selectedRow);
-                }
-
-                // update the infos
-                Integer beanIndex = beans.get(tableBean);
-                if (beanIndex == null) {
-                    beanIndex = Integer.valueOf(model.infos.size());
-                    beans.put(tableBean, beanIndex);
-                    model.infos.add(tableBean);
-                }
-
-                if (info != null) {
-                    int index = 0;
-                    for (ThreadTableBean inModel : model.infos) {
-                        if (info.equals(inModel)) {
-                            currentSelection = index;
-                            break;
-                        }
-                        index++;
-                    }
-                }
-                
-                // just repack once, or the user will see the table moving around
-                if (!tableRepacked) {
-                    table.repackCells();
-                    tableRepacked = true;
-                }
-            }
-        });
-    }
-
-    @Override
-    public void submitChanges() {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                ThreadViewTableModel model =
-                        (ThreadViewTableModel) table.getModel();
-                model.fireTableDataChanged();
-            }
-        });
-    }
-
-    @SuppressWarnings("serial")
-    private class ThreadViewTableModel extends NonEditableTableModel {
-
-        private String [] columns = {
-                t.localize(LocaleResources.NAME).getContents(),
-                t.localize(LocaleResources.ID).getContents(),
-                t.localize(LocaleResources.FIRST_SEEN).getContents(),
-                t.localize(LocaleResources.LAST_SEEN).getContents(),
-                t.localize(LocaleResources.WAIT_COUNT).getContents(),
-                t.localize(LocaleResources.BLOCK_COUNT).getContents(),
-                t.localize(LocaleResources.RUNNING).getContents(),
-                t.localize(LocaleResources.WAITING).getContents(),
-                t.localize(LocaleResources.SLEEPING).getContents(),
-                t.localize(LocaleResources.MONITOR).getContents(), //, "Heap", "CPU Time", "User CPU Time"
-        };
-
-        private List<ThreadTableBean> infos;
-        public ThreadViewTableModel() {
-            this.infos = new ArrayList<>();
-        }
-    
-        @Override
-        public String getColumnName(int column) {
-            return columns[column];
-        }
-        
-        @Override
-        public int getColumnCount() {
-            return columns.length;
-        }
-        
-        @Override
-        public int getRowCount() {
-            if (infos == null) {
-                return 0;
-            }
-            return infos.size();
-        }
-        
-        @Override
-        public Class<?> getColumnClass(int column) {
-            switch (column) {
-            case 0:
-            case 2:
-            case 3:
-            case 6:
-            case 7:
-            case 8:
-            case 9:
-                return String.class;
-            default:
-                return Long.class;
-            }
-        }
-        
-        @Override
-        public Object getValueAt(int row, int column) {
-
-            DecimalFormat format = new DecimalFormat("###.00");
-
-            Object result = null;
-            
-            ThreadTableBean info = infos.get(row);
-            switch (column) {
-            case 0:
-                result = info.getName();
-                break;
-            case 1:
-                result = info.getId();
-                break;
-            case 2:
-                result = new Date(info.getStartTimeStamp()).toString();
-                break;
-            case 3:
-                if (info.getStopTimeStamp() != 0) {
-                    result = new Date(info.getStopTimeStamp()).toString();
-                } else {
-                    result = "-";
-                }
-                break;
-            case 4:
-                result = info.getWaitedCount();
-                break;
-            case 5:
-                result = info.getBlockedCount();
-                break;
-            case 6:
-                result = format.format(info.getRunningPercent());
-                break;
-            case 7:
-                result = format.format(info.getWaitingPercent());
-                break;
-            case 8:
-                result = format.format(info.getSleepingPercent());
-                break;
-            case 9:
-                result = format.format(info.getMonitorPercent());
-                break;
-             default:
-                 result = "n/a";
-                 break;
-            }
-            return result;
-        }
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadTimelineView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,167 +0,0 @@
-/*
- * Copyright 2012-2016 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.SwingComponent;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
-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.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.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.util.HashMap;
-import java.util.Map;
-
-public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent  {
-
-    private final UIDefaults uiDefaults;
-    private TimelineViewComponent contentPane;
-
-    private ComponentVisibilityNotifier visibilityNotifier;
-
-    private Map<ThreadInfo, TimelineComponent> timelines;
-
-    private EDTHelper edt;
-
-    public SwingThreadTimelineView(UIDefaults uiDefaults) {
-        this.uiDefaults = uiDefaults;
-
-        edt = new EDTHelper();
-        timelines = new HashMap<>();
-
-        this.contentPane = new TimelineViewComponent(uiDefaults);
-        clear();
-
-        visibilityNotifier = new ComponentVisibilityNotifier();
-        visibilityNotifier.initialize(contentPane, notifier);
-    }
-
-    @Override
-    public void addThread(final ThreadInfo thread) {
-        edt.callLater(new Runnable() {
-            @Override
-            public void run() {
-                if (!timelines.containsKey(thread)) {
-                    TimelineComponent timeline =
-                            new TimelineComponent(uiDefaults, thread,
-                                                  contentPane.getModel());
-                    timeline.initComponents();
-
-                    timelines.put(thread, timeline);
-                    contentPane.addTimeline(timeline);
-                }
-            }
-        });
-    }
-
-    @Override
-    public Component getUiComponent() {
-        return contentPane;
-    }
-
-    @Override
-    public void setTotalRange(final Range<Long> totalRange) {
-        edt.callLater(new Runnable() {
-            @Override
-            public void run() {
-                contentPane.getModel().setRange(totalRange);
-            }
-        });
-    }
-
-    @Override
-    public void clear() {
-        edt.callLater(new Runnable() {
-            @Override
-            public void run() {
-                timelines.clear();
-                contentPane.removeAll();
-                contentPane.initComponents();
-                contentPane.revalidate();
-                contentPane.repaint();
-            }
-        });
-    }
-
-    @Override
-    public void addProbe(final ThreadInfo info, final TimelineProbe state) {
-        edt.callLater(new Runnable() {
-            @Override
-            public void run() {
-                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();
-                timelineContainer.repaint();
-            }
-        });
-    }
-
-    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	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,334 +0,0 @@
-/*
- * Copyright 2012-2016 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.ComponentVisibleListener;
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.components.ThermostatTabbedPane;
-import com.redhat.thermostat.common.ApplicationService;
-import com.redhat.thermostat.shared.locale.LocalizedString;
-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.LockView;
-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.ThreadTimelineView;
-import com.redhat.thermostat.thread.client.common.view.ThreadView;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
-import com.redhat.thermostat.thread.model.ThreadSession;
-
-import javax.swing.JOptionPane;
-import javax.swing.JSplitPane;
-import javax.swing.JTabbedPane;
-import javax.swing.SwingUtilities;
-import javax.swing.SwingWorker;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-import java.awt.Component;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.util.List;
-
-public class SwingThreadView extends ThreadView implements SwingComponent {
-    
-    private String DIVIDER_LOCATION_KEY;
-    
-    private ThreadMainPanel panel;
-    
-    private SwingThreadCountView threadCountView;
-    private SwingThreadTableView threadTableView;
-    private SwingVmDeadLockView vmDeadLockView;
-    private SwingThreadTimelineView threadTimelineView;
-    private SwingThreadDetailsView threadDetailsView;
-    private SwingLockView lockView;
-
-    private JTabbedPane topPane;
-    private JTabbedPane bottomPane;
-
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-
-    private boolean skipNotification = false;
-    
-    private int threadDetailsPaneID = 0;
-    
-    private UIDefaults uiDefaults;
-    private boolean viewControlsEnabled = true;
-
-
-    public SwingThreadView(UIDefaults uiDefaults) {
-        
-        this.uiDefaults = uiDefaults;
-
-        panel = new ThreadMainPanel(uiDefaults);
-        // TODO use ComponentVisiblityNotifier instead
-        // sadly, the BasicView.notifier field can not be accessed here
-        panel.addHierarchyListener(new ComponentVisibleListener() {
-
-            @Override
-            public void componentShown(Component component) {
-                SwingThreadView.this.notify(Action.VISIBLE);
-                restoreDivider();
-            }
-
-            @Override
-            public void componentHidden(Component component) {
-                SwingThreadView.this.notify(Action.HIDDEN);
-            }
-        });
-        
-        panel.getSplitPane().addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,
-                new PropertyChangeListener() {
-                    @Override
-                    public void propertyChange(PropertyChangeEvent evt) {
-                        JSplitPane sourceSplitPane = (JSplitPane) evt.getSource();
-                        saveDivider(sourceSplitPane.getDividerLocation());
-                    }
-                });
-
-        panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.START_RECORDING).getContents());
-        panel.getRecordingToggleButton().addItemListener(new ItemListener()
-        {
-            @Override
-            public void itemStateChanged(ItemEvent e) {
-                
-                ThreadAction action = null;                
-                if (e.getStateChange() == ItemEvent.SELECTED) {
-                    action = ThreadAction.START_LIVE_RECORDING;
-                    panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.STOP_RECORDING).getContents());
-                } else {
-                    action = ThreadAction.STOP_LIVE_RECORDING;
-                    panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.START_RECORDING).getContents());
-                }
-                
-                if (skipNotification) return;
-                
-                final ThreadAction toNotify = action;
-                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
-                    @Override
-                    protected Void doInBackground() throws Exception {
-                        notifier.fireAction(toNotify);
-                        return null;
-                    }
-                };
-                worker.execute();
-            }
-        });
-
-        panel.getShowRecordedSessionsButton().setToolTipText(t.localize(LocaleResources.RECORDING_LIST_HINT).getContents());
-        panel.getShowRecordedSessionsButton().addItemListener(new ItemListener() {
-            @Override
-            public void itemStateChanged(ItemEvent e) {
-                if (e.getStateChange() != ItemEvent.SELECTED) {
-                    panel.toggleOverlayPanel(false);
-                    return;
-                }
-
-                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
-                    @Override
-                    protected Void doInBackground() throws Exception {
-                        notifier.fireAction(ThreadAction.REQUEST_DISPLAY_RECORDED_SESSIONS);
-                        return null;
-                    }
-                };
-                worker.execute();
-            }
-        });
-
-        panel.getSessionsPanel().addListSelectionListener(new ListSelectionListener() {
-            @Override
-            public void valueChanged(ListSelectionEvent e) {
-                notifier.fireAction(ThreadAction.REQUEST_LOAD_SESSION,
-                                    panel.getSessionsPanel().getSelectedValue());
-            }
-        });
-        setupTopPane();
-        setupBottomPane();
-    }
-    
-    private void setupTopPane() {
-        topPane = new ThermostatTabbedPane();
-        topPane.setName("topTabbedPane");
-        
-        threadCountView = new SwingThreadCountView();
-        Component comp = threadCountView.getUiComponent();
-        comp.setName("count");
-        topPane.addTab(t.localize(LocaleResources.THREAD_COUNT).getContents(), comp);
-        
-        threadTimelineView = new SwingThreadTimelineView(uiDefaults);
-        comp = threadTimelineView.getUiComponent();
-        comp.setName("timeline");
-        topPane.addTab(t.localize(LocaleResources.TIMELINE).getContents(), comp);
-        
-        lockView = new SwingLockView();
-        comp = lockView.getUiComponent();
-        comp.setName("lock");
-        topPane.addTab(t.localize(LocaleResources.LOCKS).getContents(), comp);
-
-        panel.getSplitPane().setTopComponent(topPane);
-    }
-    
-    private void setupBottomPane() {
-        bottomPane = new ThermostatTabbedPane();
-        bottomPane.setName("bottomTabbedPane");
-        
-        threadTableView = new SwingThreadTableView();
-        bottomPane.addTab(t.localize(LocaleResources.TABLE).getContents(), threadTableView.getUiComponent());
-        
-        threadDetailsView = new SwingThreadDetailsView();
-        bottomPane.addTab(t.localize(LocaleResources.DETAILS).getContents(), threadDetailsView.getUiComponent());
-        threadDetailsPaneID = 1;
-
-        vmDeadLockView = new SwingVmDeadLockView();
-        bottomPane.addTab(t.localize(LocaleResources.VM_DEADLOCK).getContents(), vmDeadLockView.getUiComponent());
-
-        panel.getSplitPane().setBottomComponent(bottomPane);
-    }
-    
-    @Override
-    public Component getUiComponent() {
-        return panel;
-    }
-    
-    @Override
-    public void setApplicationService(ApplicationService appService, String uniqueId) {
-        super.setApplicationService(appService, uniqueId);
-        DIVIDER_LOCATION_KEY = "divider." + uniqueId;
-    }
-
-    @Override
-    public void setEnableRecordingControl(final boolean enable) {
-        this.viewControlsEnabled = enable;
-        if (!enable) {
-            setRecording(MonitoringState.DISABLED, false);
-        }
-    }
-    
-    @Override
-    public void setRecording(final MonitoringState monitoringState, final boolean notify) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                if (!notify) skipNotification = true;
-                if (!viewControlsEnabled) {
-                    panel.getRecordingToggleButton().setToggleActionState(MonitoringState.DISABLED);
-                } else {
-                    panel.getRecordingToggleButton().setToggleActionState(monitoringState);
-                }
-                if (!notify) skipNotification = false;
-            }
-        });
-    }
-
-    @Override
-    public VmDeadLockView createDeadLockView() {
-        return vmDeadLockView;
-    }
-    
-    @Override
-    public ThreadTableView createThreadTableView() {
-        return threadTableView;
-    }
-    
-    @Override
-    public void displayWarning(final LocalizedString warning) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                JOptionPane.showMessageDialog(panel.getParent(), warning.getContents(), "", JOptionPane.WARNING_MESSAGE);
-            }
-        });
-    }
-    
-    private void restoreDivider() {
-        int location = (int) ((double) (panel.getSplitPane().getHeight() - panel.getSplitPane().getDividerSize()) * 0.80);
-        if (appService != null) {
-            Object _location = appService.getApplicationCache().getAttribute(DIVIDER_LOCATION_KEY);
-            if (_location != null) {
-                location = (Integer) _location;
-            }
-        }
-        panel.getSplitPane().setDividerLocation(location);
-    }
-    
-    private void saveDivider(int location) {
-        if (appService != null) {
-            appService.getApplicationCache().addAttribute(DIVIDER_LOCATION_KEY, location);
-        }
-    }
-    
-    @Override
-    public void displayThreadDetails(final ThreadTableBean thread) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                bottomPane.setSelectedIndex(threadDetailsPaneID);
-                threadDetailsView.setDetails(thread);
-            }
-        });
-    }
-    
-    @Override
-    public ThreadTimelineView createThreadTimelineView() {
-        return threadTimelineView;
-    }
-
-    @Override
-    public ThreadCountView createThreadCountView() {
-        return threadCountView;
-    }
-
-    @Override
-    public LockView createLockView() {
-        return lockView;
-    }
-
-    @Override
-    public void displayTimelineSessionList(final List<ThreadSession> threadSessions) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                panel.setOverlayContent(threadSessions);
-                panel.toggleOverlayPanel(true);
-            }
-        });
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/SwingVmDeadLockView.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,328 +0,0 @@
-/*
- * Copyright 2012-2016 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 java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.FontMetrics;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-import javax.swing.JButton;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JSplitPane;
-import javax.swing.SwingUtilities;
-
-import com.mxgraph.layout.mxCircleLayout;
-import com.mxgraph.layout.mxEdgeLabelLayout;
-import com.mxgraph.layout.mxGraphLayout;
-import com.mxgraph.model.mxCell;
-import com.mxgraph.swing.mxGraphComponent;
-import com.mxgraph.util.mxConstants;
-import com.mxgraph.view.mxGraph;
-import com.redhat.thermostat.client.swing.SwingComponent;
-import com.redhat.thermostat.client.swing.components.ThermostatScrollBar;
-import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
-import com.redhat.thermostat.client.swing.components.ThermostatTextArea;
-import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
-import com.redhat.thermostat.common.utils.StringUtils;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.DeadlockParser;
-import com.redhat.thermostat.thread.client.common.DeadlockParser.Information;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
-
-public class SwingVmDeadLockView extends VmDeadLockView implements SwingComponent {
-
-    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
-
-    private final JPanel actualComponent = new JPanel();
-    private final JButton checkForDeadlockButton;
-
-    private final JSplitPane deadlockTextAndVisualization = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
-
-    private final JPanel graphical = new JPanel();
-    private final ThermostatTextArea description = new ThermostatTextArea();
-    /**
-     * Whether to set the divider's location. Do this only once to set a sane
-     * initial value but don't change anything after and allow the user to tweak
-     * this as appropriate.
-     */
-    private boolean dividerLocationSet = false;
-
-    public SwingVmDeadLockView() {
-        actualComponent.setLayout(new GridBagLayout());
-
-        GridBagConstraints c = new GridBagConstraints();
-        c.gridy = 0;
-        c.anchor = GridBagConstraints.LINE_END;
-        checkForDeadlockButton = new JButton(translate.localize(LocaleResources.CHECK_FOR_DEADLOCKS).getContents());
-        checkForDeadlockButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed(ActionEvent e) {
-                deadLockNotifier.fireAction(VmDeadLockViewAction.CHECK_FOR_DEADLOCK);
-            }
-        });
-
-        actualComponent.add(checkForDeadlockButton, c);
-
-        c.anchor = GridBagConstraints.LINE_START;
-        c.gridy++;
-        c.fill = GridBagConstraints.BOTH;
-        c.weightx = 1;
-        c.weighty = 1;
-
-        description.setEditable(false);
-
-        JScrollPane scrollPane = new ThermostatScrollPane(description);
-
-        graphical.setLayout(new BorderLayout());
-
-        deadlockTextAndVisualization.setLeftComponent(scrollPane);
-        deadlockTextAndVisualization.setRightComponent(graphical);
-
-        actualComponent.add(deadlockTextAndVisualization, c);
-
-        new ComponentVisibilityNotifier().initialize(actualComponent, notifier);
-    }
-
-    @Override
-    public void setDeadLockInformation(final Information parsed, final String rawText) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-
-                graphical.removeAll();
-
-                if (!dividerLocationSet) {
-                    // 0.7 is chosen empirically to show a bit more of the text than the gui
-                    deadlockTextAndVisualization.setDividerLocation(0.7);
-                    deadlockTextAndVisualization.revalidate();
-                    dividerLocationSet = true;
-                }
-
-                if (parsed != null) {
-                    FontMetrics metrics = graphical.getGraphics().getFontMetrics();
-                    graphical.add(createGraph(parsed, metrics), BorderLayout.CENTER);
-                }
-
-                graphical.revalidate();
-                graphical.repaint();
-
-                description.setText(rawText);
-            }
-        });
-    }
-
-    private mxGraphComponent createGraph(Information info, FontMetrics fontMetrics) {
-
-        final mxGraph graph = new mxGraph() {
-
-            /* Show tooltips for vertices and edges */
-            @Override
-            public String getToolTipForCell(Object source) {
-                mxCell cell = ((mxCell) source);
-
-                if (cell.getValue() instanceof GraphItem) {
-                    return ((GraphItem) cell.getValue()).getTooltip();
-                } else {
-                    return super.getToolTipForCell(cell);
-                }
-            }
-
-
-            /* Prevent modifying the contents of edges or vertices */
-            @Override
-            public boolean isCellEditable(Object cell) {
-                return false;
-            }
-
-            /* Prevent moving edges away from the vertices */
-            @Override
-            public boolean isCellSelectable(Object cell) {
-                return !model.isEdge(cell);
-            }
-
-        };
-
-        Object parent = graph.getDefaultParent();
-
-        addDeadlockToGraph(info, graph, parent, fontMetrics);
-
-        graph.setAutoSizeCells(true);
-        graph.setCellsResizable(true);
-
-        final mxGraphComponent graphComponent = new mxGraphComponent(graph);
-        graphComponent.setTextAntiAlias(true);
-        graphComponent.setToolTips(true);
-        graphComponent.setConnectable(false);
-
-        graphComponent.setHorizontalScrollBar(new ThermostatScrollBar(ThermostatScrollBar.HORIZONTAL));
-        graphComponent.setVerticalScrollBar(new ThermostatScrollBar(ThermostatScrollBar.VERTICAL));
-
-        Map<String, Object> style = graph.getStylesheet().getDefaultVertexStyle();
-        style.put(mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
-        graph.getStylesheet().setDefaultVertexStyle(style);
-
-        mxGraphLayout layout = new mxCircleLayout(graph);
-        layout.execute(graph.getDefaultParent());
-
-        layout = new mxEdgeLabelLayout(graph);
-        layout.execute(graph.getDefaultParent());
-
-        return graphComponent;
-    }
-
-    @Override
-    public void setCheckDeadlockControlEnabled(final boolean enabled) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                SwingVmDeadLockView.this.checkForDeadlockButton.setEnabled(enabled);
-            }
-        });
-    }
-
-    private static void addDeadlockToGraph(Information info, mxGraph graph, Object parent, FontMetrics metrics) {
-        graph.getModel().beginUpdate(); // batch updates
-        try {
-            Map<String, Object> idToCell = new HashMap<>();
-            Map<String, String> idToLabel = new HashMap<>();
-
-            for (DeadlockParser.Thread thread : info.threads) {
-                String label = getThreadLabel(thread);
-                String tooltip = getThreadTooltip(thread);
-                idToLabel.put(thread.id, label);
-                GraphItem node = new GraphItem(label, tooltip);
-                final int PADDING = 20;
-                int width = metrics.stringWidth(label) + PADDING;
-                int height = metrics.getHeight() + PADDING;
-                Object threadNode = graph.insertVertex(parent, thread.id, node, 0, 0, width, height);
-                idToCell.put(thread.id, threadNode);
-            }
-
-            for (DeadlockParser.Thread thread : info.threads) {
-                String label = translate.localize(LocaleResources.DEADLOCK_WAITING_ON).getContents();
-                String tooltip = getEdgeTooltip(thread, idToLabel);
-                GraphItem edge = new GraphItem(label, tooltip);
-                graph.insertEdge(parent, thread.waitingOn.name, edge, idToCell.get(thread.id), idToCell.get(thread.waitingOn.ownerId));
-            }
-
-        }  finally {
-           graph.getModel().endUpdate();
-        }
-    }
-
-    private static String getThreadLabel(DeadlockParser.Thread thread) {
-        return translate.localize(LocaleResources.DEADLOCK_THREAD_NAME, thread.name, thread.id).getContents();
-    }
-
-    private static String getThreadTooltip(DeadlockParser.Thread thread) {
-        return translate.localize(
-                LocaleResources.DEADLOCK_THREAD_TOOLTIP,
-                StringUtils.htmlEscape(thread.waitingOn.name),
-                stackTraceToHtmlString(thread.stackTrace))
-            .getContents();
-    }
-
-    private static String stackTraceToHtmlString(List<String> items) {
-        StringBuilder result = new StringBuilder();
-        for (String item : items) {
-            result.append(StringUtils.htmlEscape(item)).append("<br/>");
-        }
-        return result.toString();
-    }
-
-    private static String getEdgeTooltip(DeadlockParser.Thread thread, Map<String, String> idToLabel) {
-        return translate.localize(
-                LocaleResources.DEADLOCK_EDGE_TOOLTIP,
-                idToLabel.get(thread.id),
-                idToLabel.get(thread.waitingOn.ownerId))
-            .getContents();
-    }
-
-    @Override
-    public Component getUiComponent() {
-        return actualComponent;
-    }
-
-    static class GraphItem implements Serializable {
-
-        private final String label;
-        private final String tooltip;
-
-        public GraphItem(String label, String tooltip) {
-            this.label = label;
-            this.tooltip = tooltip;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(label, tooltip);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            GraphItem other = (GraphItem) obj;
-            return Objects.equals(label, other.label) && Objects.equals(tooltip, other.tooltip);
-        }
-
-        @Override
-        public String toString() {
-            return label;
-        }
-
-        public String getTooltip() {
-            return tooltip;
-        }
-    }
-
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadAliveDaemonTimelinePanel.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-/*
- * Copyright 2012-2016 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.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-
-import javax.swing.BoxLayout;
-import javax.swing.GroupLayout;
-import javax.swing.GroupLayout.Alignment;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.LayoutStyle.ComponentPlacement;
-import javax.swing.SwingConstants;
-
-@SuppressWarnings("serial")
-class ThreadAliveDaemonTimelinePanel extends JPanel {
-
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    
-    private JLabel liveThreads;
-    private JLabel daemonThreads;
-    private JPanel timelinePanel;
-    
-    /**
-     * Create the panel.
-     */
-    public ThreadAliveDaemonTimelinePanel() {
-        JPanel runningPanel = new JPanel();
-
-        timelinePanel = new JPanel();
-        timelinePanel.setOpaque(false);
-        timelinePanel.setLayout(new BoxLayout(timelinePanel, BoxLayout.X_AXIS));
-        
-        GroupLayout groupLayout = new GroupLayout(this);
-        groupLayout.setHorizontalGroup(
-            groupLayout.createParallelGroup(Alignment.TRAILING)
-                .addComponent(runningPanel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE)
-                .addComponent(timelinePanel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE)
-        );
-        groupLayout.setVerticalGroup(
-            groupLayout.createParallelGroup(Alignment.LEADING)
-                .addGroup(groupLayout.createSequentialGroup()
-                    .addComponent(runningPanel, GroupLayout.PREFERRED_SIZE, 41, GroupLayout.PREFERRED_SIZE)
-                    .addPreferredGap(ComponentPlacement.RELATED)
-                    .addComponent(timelinePanel, GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE))
-        );
-        
-        JLabel liveThreadsLabel = new JLabel(t.localize(LocaleResources.LIVE_THREADS).getContents() + ":");
-        
-        JLabel daemonThreadsLabel = new JLabel(t.localize(LocaleResources.DAEMON_THREADS).getContents() + ":");
-        
-        liveThreads = new JLabel("-");
-        liveThreads.setHorizontalAlignment(SwingConstants.RIGHT);
-        
-        daemonThreads = new JLabel("-");
-        daemonThreads.setHorizontalAlignment(SwingConstants.RIGHT);
-        GroupLayout gl_runningPanel = new GroupLayout(runningPanel);
-        gl_runningPanel.setHorizontalGroup(
-            gl_runningPanel.createParallelGroup(Alignment.LEADING)
-                .addGroup(gl_runningPanel.createSequentialGroup()
-                    .addComponent(liveThreadsLabel)
-                    .addGap(18)
-                    .addComponent(liveThreads, GroupLayout.PREFERRED_SIZE, 85, GroupLayout.PREFERRED_SIZE)
-                    .addGap(18)
-                    .addComponent(daemonThreadsLabel)
-                    .addPreferredGap(ComponentPlacement.RELATED)
-                    .addComponent(daemonThreads, GroupLayout.PREFERRED_SIZE, 49, GroupLayout.PREFERRED_SIZE)
-                    .addContainerGap(54, Short.MAX_VALUE))
-        );
-        gl_runningPanel.setVerticalGroup(
-            gl_runningPanel.createParallelGroup(Alignment.TRAILING)
-                .addGroup(Alignment.LEADING, gl_runningPanel.createSequentialGroup()
-                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.BASELINE)
-                        .addComponent(liveThreadsLabel)
-                        .addComponent(liveThreads)
-                        .addComponent(daemonThreads)
-                        .addComponent(daemonThreadsLabel))
-                    .addContainerGap(26, Short.MAX_VALUE))
-        );
-        runningPanel.setLayout(gl_runningPanel);
-        setLayout(groupLayout);
-
-    }
-    
-    public JLabel getLiveThreads() {
-        return liveThreads;
-    }
-    
-    public JLabel getDaemonThreads() {
-        return daemonThreads;
-    }
-    
-    public JPanel getTimelinePanel() {
-        return timelinePanel;
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadDetailsChart.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright 2012-2016 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 javax.swing.BoxLayout;
-import javax.swing.JPanel;
-
-@SuppressWarnings("serial")
-public class ThreadDetailsChart extends JPanel {
-    
-    public ThreadDetailsChart() {
-        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadMainPanel.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,269 +0,0 @@
-/*
- * Copyright 2012-2016 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.IconResource;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.client.swing.components.ActionToggleButton;
-import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
-import com.redhat.thermostat.client.swing.components.HeaderPanel;
-import com.redhat.thermostat.client.swing.components.Icon;
-import com.redhat.thermostat.client.swing.components.OverlayPanel;
-import com.redhat.thermostat.client.swing.components.ShadowLabel;
-import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
-import com.redhat.thermostat.client.swing.components.ThermostatThinScrollBar;
-import com.redhat.thermostat.client.ui.Palette;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.model.ThreadSession;
-
-import javax.swing.BoxLayout;
-import javax.swing.DefaultListModel;
-import javax.swing.GroupLayout;
-import javax.swing.GroupLayout.Alignment;
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JSplitPane;
-import javax.swing.ListCellRenderer;
-import javax.swing.ListModel;
-import javax.swing.ListSelectionModel;
-import javax.swing.OverlayLayout;
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Point;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.util.Date;
-import java.util.List;
-
-@SuppressWarnings("serial")
-class ThreadMainPanel extends JPanel {
-
-    private static final Icon START_ICON = IconResource.SAMPLE.getIcon();
-    private final Icon stopIcon;
-
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    private JSplitPane splitPane;
-    
-    private ActionToggleButton toggleButton;
-    private ActionToggleButton showRecordedSessionsButton;
-
-    private OverlayPanel overlay;
-
-    private UIDefaults uiDefaults;
-    private ThreadSessionList sessionsPanel;
-    private DefaultListModel<ThreadSession> sessionsModel;
-
-    @Override
-    public boolean isOptimizedDrawingEnabled() {
-        return false;
-    }
-
-    public ThreadMainPanel(UIDefaults uiDefaults) {
-        this.uiDefaults = uiDefaults;
-
-        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
-
-        HeaderPanel headerPanel = new HeaderPanel();
-        headerPanel.setHeader(t.localize(LocaleResources.THREAD_CONTROL_PANEL));
-
-        stopIcon = new FontAwesomeIcon('\uf28e', START_ICON.getIconHeight(), uiDefaults.getIconColor());
-
-        toggleButton = new ActionToggleButton(START_ICON, stopIcon, t.localize(LocaleResources.THREAD_MONITOR_SWITCH));
-        toggleButton.setName("recordButton");
-        headerPanel.addToolBarButton(toggleButton);
-
-        Icon listSessionsIcon = IconResource.HISTORY.getIcon();
-        showRecordedSessionsButton = new ActionToggleButton(listSessionsIcon, t.localize(LocaleResources.THREAD_MONITOR_DISPLAY_SESSIONS));
-        showRecordedSessionsButton.setName("showRecordedSessionsButton");
-        headerPanel.addToolBarButton(showRecordedSessionsButton);
-
-        overlay = new OverlayPanel(t.localize(LocaleResources.RECORDING_LIST), true, true);
-        overlay.setName("threadOverlayPanel");
-        overlay.addCloseEventListener(new OverlayPanel.CloseEventListener() {
-            @Override
-            public void closeRequested(OverlayPanel.CloseEvent event) {
-                showRecordedSessionsButton.doClick();
-            }
-        });
-
-        JPanel stack = new JPanel();
-        stack.setName("threadStackPanel");
-        stack.setOpaque(true);
-        stack.setLayout(new OverlayLayout(stack));
-
-        splitPane = new JSplitPane();
-        splitPane.setName("threadMainPanelSplitPane");
-        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
-        splitPane.setOneTouchExpandable(true);
-
-        JPanel content = new JPanel();
-        GroupLayout gl_content = new GroupLayout(content);
-        gl_content.setHorizontalGroup(
-            gl_content.createParallelGroup(Alignment.TRAILING)
-                .addGroup(Alignment.LEADING, gl_content.createSequentialGroup()
-                    .addContainerGap()
-                    .addComponent(splitPane, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
-                    .addContainerGap())
-        );
-        gl_content.setVerticalGroup(
-            gl_content.createParallelGroup(Alignment.TRAILING)
-                .addGroup(Alignment.LEADING, gl_content.createSequentialGroup()
-                    .addContainerGap()
-                    .addComponent(splitPane, 0, 240, Short.MAX_VALUE)
-                    .addContainerGap())
-        );
-
-        content.setLayout(gl_content);
-
-        stack.add(overlay);
-        stack.add(content);
-        stack.setOpaque(false);
-
-        headerPanel.setContent(stack);
-
-        add(headerPanel);
-
-        sessionsModel = new DefaultListModel<>();
-
-        sessionsPanel = new ThreadSessionList(sessionsModel);
-        sessionsPanel.setSelectionMode(JList.VERTICAL);
-        sessionsPanel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
-        sessionsPanel.setOpaque(false);
-        sessionsPanel.setCellRenderer(new ThreadSessionRenderer());
-        sessionsPanel.addMouseMotionListener(new MouseAdapter() {
-            @Override
-            public void mouseMoved(MouseEvent e) {
-                Point where = new Point(e.getX(), e.getY());
-                int index = sessionsPanel.locationToIndex(where);
-                int hoveredIndex = sessionsPanel.getHoveredIndex();
-                if (index != hoveredIndex) {
-                    sessionsPanel.setHoveredIndex(index);
-                    sessionsPanel.repaint();
-                }
-            }
-        });
-        ThermostatScrollPane scrollPane = new ThermostatScrollPane(sessionsPanel);
-        scrollPane.setVerticalScrollBar(new ThermostatThinScrollBar(ThermostatThinScrollBar.VERTICAL));
-        overlay.add(scrollPane);
-    }
-
-    public ThreadSessionList getSessionsPanel() {
-        return sessionsPanel;
-    }
-
-    public JSplitPane getSplitPane() {
-        return splitPane;
-    }
-    
-    public ActionToggleButton getRecordingToggleButton() {
-        return toggleButton;
-    }
-
-    public ActionToggleButton getShowRecordedSessionsButton() {
-        return showRecordedSessionsButton;
-    }
-
-    public void toggleOverlayPanel(boolean visible) {
-        overlay.setOverlayVisible(visible);
-    }
-
-    public class ThreadSessionRenderer implements ListCellRenderer<ThreadSession> {
-
-        @Override
-        public Component getListCellRendererComponent(JList<? extends ThreadSession> list,
-                                                      ThreadSession value, int index,
-                                                      boolean isSelected,
-                                                      boolean cellHasFocus)
-        {
-            JPanel panel = new JPanel();
-            panel.setLayout(new BorderLayout());
-            panel.setOpaque(false);
-            ShadowLabel label = new ShadowLabel();
-            label.setText("[" + new Date(value.getTimeStamp()) +"]");
-            label.setOpaque(false);
-
-            if (isSelected || cellHasFocus) {
-                panel.setOpaque(true);
-                panel.setBackground((Color) uiDefaults.getSelectedComponentBGColor());
-                label.setForeground((Color) uiDefaults.getSelectedComponentFGColor());
-
-            } else if ((sessionsPanel.getHoveredIndex() == index)) {
-                panel.setOpaque(true);
-                panel.setBackground(Palette.ELEGANT_CYAN.getColor());
-                label.setForeground((Color) uiDefaults.getSelectedComponentFGColor());
-
-            } else {
-                label.setForeground((Color) uiDefaults.getComponentFGColor());
-            }
-
-            panel.add(label);
-            return panel;
-        }
-    }
-
-    public void setOverlayContent(List<ThreadSession> threadSessions) {
-        sessionsModel.clear();
-
-        for (ThreadSession session : threadSessions) {
-            sessionsModel.addElement(session);
-        }
-        sessionsPanel.setHoveredIndex(-1);
-
-        overlay.revalidate();
-        overlay.repaint();
-
-    }
-
-    public class ThreadSessionList extends JList<ThreadSession> {
-        private int hoveredIndex;
-        public ThreadSessionList(ListModel<ThreadSession> dataModel) {
-            super(dataModel);
-            hoveredIndex = -1;
-        }
-
-        public int getHoveredIndex() {
-            return hoveredIndex;
-        }
-
-        public void setHoveredIndex(int hoveredIndex) {
-            this.hoveredIndex = hoveredIndex;
-        }
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/ThreadTable.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * Copyright 2012-2016 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 javax.swing.JPanel;
-import javax.swing.BoxLayout;
-
-@SuppressWarnings("serial")
-public class ThreadTable extends JPanel {
-    /**
-     * Create the panel.
-     */
-    public ThreadTable() {
-        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-        setOpaque(false);
-    }
-
-    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);
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentHeader.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/*
- * Copyright 2012-2016 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.client.swing.components.Icon;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
-import com.redhat.thermostat.thread.client.swing.experimental.components.DataPane;
-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 static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-
-    private final TimelineModel model;
-    private final UIDefaults defaults;
-    private DataPane controls;
-
-    private JLabel zoomOut;
-    private JLabel restoreZoom;
-    private JLabel zoomIn;
-
-    public RangeComponentHeader(TimelineModel model, UIDefaults defaults) {
-        this.model = model;
-        this.defaults = defaults;
-    }
-
-    public void initComponents() {
-        controls = new DataPane();
-        controls.setLayout(new GridLayout(1, 0, 5, 5));
-
-        Icon baseIcon = new FontAwesomeIcon('\uf066', 15, defaults.getIconColor());
-        Icon hoverIcon = new FontAwesomeIcon('\uf066', 15,
-                                             defaults.getSelectedComponentBGColor());
-        zoomOut = new JLabel(baseIcon);
-        zoomOut.setToolTipText(t.localize(LocaleResources.ZOOM_OUT).getContents());
-        zoomOut.addMouseListener(new Hover(zoomOut, baseIcon, hoverIcon));
-        zoomOut.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                double ratio = model.getMagnificationRatio();
-                model.setMagnificationRatio(ratio/2);
-            }
-        });
-        controls.add(zoomOut);
-
-        baseIcon = new FontAwesomeIcon('\uf03b', 15, defaults.getIconColor());
-        hoverIcon = new FontAwesomeIcon('\uf03b', 15,
-                                        defaults.getSelectedComponentBGColor());
-        restoreZoom = new JLabel(baseIcon);
-        restoreZoom.setToolTipText(t.localize(LocaleResources.RESTORE_ZOOM).getContents());
-        restoreZoom.addMouseListener(new Hover(restoreZoom, baseIcon, hoverIcon));
-        restoreZoom.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                model.setMagnificationRatio(TimelineModel.DEFAULT_RATIO);
-            }
-        });
-        controls.add(restoreZoom);
-
-        baseIcon = new FontAwesomeIcon('\uf065', 15, defaults.getIconColor());
-        hoverIcon = new FontAwesomeIcon('\uf065', 15,
-                                        defaults.getSelectedComponentBGColor());
-        zoomIn = new JLabel(baseIcon);
-        zoomIn.setToolTipText(t.localize(LocaleResources.ZOOM_IN).getContents());
-        zoomIn.addMouseListener(new Hover(zoomIn, baseIcon, hoverIcon));
-        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);
-    }
-
-    public void setControlsEnabled(boolean b) {
-        zoomIn.setEnabled(b);
-        zoomOut.setEnabled(b);
-        restoreZoom.setEnabled(b);
-    }
-
-    private class Hover extends MouseAdapter {
-
-        private final JLabel label;
-        private final Icon baseIcon;
-        private final Icon hoverIcon;
-
-        public Hover(JLabel label, Icon baseIcon, Icon hoverIcon) {
-
-            this.label = label;
-            this.baseIcon = baseIcon;
-            this.hoverIcon = hoverIcon;
-        }
-
-        @Override
-        public void mouseEntered(MouseEvent e) {
-            onMouseHover(true);
-        }
-
-        @Override
-        public void mouseExited(MouseEvent e) {
-            onMouseHover(false);
-        }
-
-        public void onMouseHover(boolean hover) {
-            label.setIcon(hover ? hoverIcon : baseIcon);
-            repaint();
-        }
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RangeComponentLayoutManager.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,104 +0,0 @@
-/*
- * Copyright 2012-2016 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);
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/RulerComponent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-/*
- * Copyright 2012-2016 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/TimelineAdjustmentListener.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * Copyright 2012-2016 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/TimelineComponent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +0,0 @@
-/*
- * Copyright 2012-2016 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.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.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;
-
-/**
- *
- */
-public class TimelineComponent extends RulerComponent {
-
-    private static final int MIN_HEIGHT = 50;
-    private final ThreadInfo threadInfo;
-
-    private ContentPane labelPane;
-
-    private TimelineLabel label;
-
-    private ThermostatScrollPane scrollPane;
-    private TimelineContainer timelineContainer;
-
-    public TimelineComponent(UIDefaults uiDefaults, ThreadInfo threadInfo,
-                             TimelineModel model)
-    {
-        super(uiDefaults, model);
-        this.threadInfo = threadInfo;
-    }
-
-    public void initComponents() {
-        setName(threadInfo.getName());
-
-        initModel();
-
-        initLabelPane();
-        initThreadPane();
-
-        setBorder(new Separator(uiDefaults, Separator.Side.BOTTOM,
-                                Separator.Type.SOLID));
-
-        Hover hover = new Hover();
-        addMouseListener(hover);
-    }
-
-    private void initModel() {
-        model.getScrollBarModel().addChangeListener(new ChangeListener() {
-            @Override
-            public void stateChanged(ChangeEvent e) {
-                repaint();
-            }
-        });
-    }
-
-    private void initThreadPane() {
-
-        timelineContainer = new TimelineContainer(model);
-        timelineContainer.setName(threadInfo.getName());
-
-        scrollPane = new ThermostatScrollPane(timelineContainer);
-        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_NEVER);
-
-        add(scrollPane, BorderLayout.CENTER);
-
-        scrollPane.getHorizontalScrollBar().setModel(model.getScrollBarModel());
-    }
-
-    public TimelineContainer getTimelineContainer() {
-        return timelineContainer;
-    }
-
-    private void initLabelPane() {
-        label = new TimelineLabel(uiDefaults, getName());
-        labelPane = new ContentPane();
-
-        labelPane.setOpaque(false);
-        labelPane.setLayout(new GridBagLayout());
-
-        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
-    public int getHeight() {
-        return MIN_HEIGHT;
-    }
-
-    private class Hover extends MouseAdapter {
-        @Override
-        public void mouseEntered(MouseEvent e) {
-            onMouseHover(true);
-        }
-
-        @Override
-        public void mouseExited(MouseEvent e) {
-            onMouseHover(false);
-        }
-
-        public void onMouseHover(boolean hover) {
-            label.onMouseHover(hover);
-            repaint();
-        }
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineContainer.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-/*
- * Copyright 2012-2016 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/TimelineLabel.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright 2012-2016 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.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 javax.swing.SwingConstants;
-import javax.swing.border.EmptyBorder;
-
-/**
- *
- */
-class TimelineLabel extends DataPane {
-    private LabelField nameLabel;
-    private Icon infoOn;
-    private Icon infoOff;
-
-    TimelineLabel(UIDefaults defaults, String text) {
-        super(Palette.WHITE, Palette.LIGHT_GRAY);
-
-        setBorder(new Separator(defaults, Separator.Side.BOTTOM, Separator.Type.SOLID));
-
-        nameLabel = new LabelField(LocalizedString.EMPTY_STRING);
-        nameLabel.setFont(defaults.getDefaultFont().deriveFont(10.f));
-
-        nameLabel.setText(text);
-        nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
-        nameLabel.setVerticalAlignment(SwingConstants.CENTER);
-        nameLabel.setForeground((Color) defaults.getSelectedComponentBGColor());
-
-        // make same small space around the label, a bit higher left and right
-        // to account for the missing icon
-        nameLabel.setBorder(new EmptyBorder(2, 4, 2, 4));
-
-        nameLabel.setHorizontalTextPosition(SwingConstants.LEFT);
-
-        // FIXME && TODO: comment this out for now, the functionality this is
-        // meant for is not yet implemented in the controller
-//        infoOn = new FontAwesomeIcon('\uf05a', 12, Palette.DARK_GRAY.getColor());
-//        infoOff = new FontAwesomeIcon('\uf05a', 12, Palette.PALE_GRAY.getColor());
-//
-//        nameLabel.setIcon(infoOff);
-
-        add(nameLabel);
-    }
-
-    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/TimelineViewComponent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-/*
- * Copyright 2012-2016 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.LegendPanel;
-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;
-    private RangeComponentHeader header;
-
-    public TimelineViewComponent(UIDefaults uiDefaults) {
-        this.uiDefaults = uiDefaults;
-    }
-
-    public void initComponents() {
-
-        ContentPane contentPane = new ContentPane();
-
-        this.model = new TimelineModel();
-
-        viewport = new TimelineViewport();
-
-        scrollPane = new ThermostatScrollPane(viewport);
-        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
-        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
-
-        header = new RangeComponentHeader(model, uiDefaults);
-        header.initComponents();
-        scrollPane.setColumnHeaderView(header);
-
-        contentPane.add(scrollPane, BorderLayout.CENTER);
-
-        scrollBar = new ThermostatScrollBar(ThermostatScrollBar.HORIZONTAL);
-        model.setScrollBarModel(scrollBar.getModel());
-
-        scrollBar.addAdjustmentListener(new TimelineAdjustmentListener(model));
-
-        contentPane.add(scrollBar, BorderLayout.SOUTH);
-        scrollBar.setEnabled(false);
-        scrollBar.setVisible(false);
-
-        header.setControlsEnabled(false);
-
-        add(contentPane, BorderLayout.CENTER);
-
-        LegendPanel legend = new LegendPanel(uiDefaults);
-        add(legend, BorderLayout.SOUTH);
-
-        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();
-        // no data, so no scrolling
-        if (range == null) {
-            scrollBar.setVisible(false);
-            scrollBar.setEnabled(false);
-            return;
-        }
-
-        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);
-        header.setControlsEnabled(true);
-        checkEnableScrollbar();
-        revalidate();
-        repaint();
-    }
-
-    public TimelineModel getModel() {
-        return model;
-    }
-}
-
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/TimelineViewport.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/LegendPanel.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,101 +0,0 @@
-/*
- * Copyright 2012-2016 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.client.swing.GraphicsUtils;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.thread.client.common.chart.ChartColors;
-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.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import javax.swing.Icon;
-import javax.swing.JLabel;
-import javax.swing.SwingConstants;
-
-/**
- *
- */
-public class LegendPanel extends DataPane {
-
-    public LegendPanel(UIDefaults defaults) {
-        setBorder(new Separator(defaults, Separator.Side.TOP.TOP, Separator.Type.SOLID));
-        setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 6));
-        setPreferredSize(new Dimension(getWidth(), 30));
-
-        for (Thread.State state : Thread.State.values()) {
-
-            Color color = ChartColors.getColor(state);
-            // no chart is black, it's just the default colour
-            if (!color.equals(Color.BLACK)) {
-                JLabel label =  new JLabel(new ColorIcon(color), SwingConstants.LEFT);
-                label.setText(state.toString());
-                add(label);
-            }
-        }
-    }
-
-    private class ColorIcon implements Icon {
-
-        private Color color;
-        private ColorIcon(Color color) {
-            this.color = color;
-        }
-
-        @Override
-        public int getIconHeight() {
-            return 12;
-        }
-
-        @Override
-        public int getIconWidth() {
-            return 12;
-        }
-
-        @Override
-        public void paintIcon(Component c, Graphics g, int x, int y) {
-            Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
-            graphics.setColor(color);
-            graphics.fillRect(x, y, getIconWidth(), getIconHeight());
-            graphics.dispose();
-        }
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeEvent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangeChangeListener.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright 2012-2016 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);
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RangedTimelineProbe.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012-2016 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);
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeEvent.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/RatioChangeListener.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-/*
- * Copyright 2012-2016 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);
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineDateFormatter.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright 2012-2016 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));
-    }
-}
--- a/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/impl/timeline/model/TimelineModel.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/*
- * Copyright 2012-2016 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);
-            }
-        }
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingLockView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.table.DefaultTableModel;
+
+import com.redhat.thermostat.client.swing.NonEditableTableModel;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.client.swing.components.ThermostatTable;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.common.view.LockView;
+import com.redhat.thermostat.thread.model.LockInfo;
+
+public class SwingLockView extends LockView implements SwingComponent {
+
+    protected static final String TABLE_NAME = "locks-table";
+
+    private static final Translate<LocaleResources> translator = LocaleResources.createLocalizer();
+
+    private static final int VALUE_COLUMN = 1;
+
+    private JPanel topPanel;
+    private ThermostatTable table;
+    private DefaultTableModel model;
+
+    public SwingLockView() {
+        topPanel = new JPanel();
+        topPanel.setLayout(new BorderLayout());
+
+        model = new NonEditableTableModel(18, 2);
+        Object[][] dataVector = new Object[][] {
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_CONTENDED_LOCK_ATTEMPS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_DEFLATIONS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_EMPTY_NOTIFICATIONS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_FAILED_SPINS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_FUTILE_WAKEUPS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_INFLATIONS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_EXTANT).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_IN_CIRCULATION).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_MON_SCAVENGED).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_NOTIFICATIONS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PARKS).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PRIVATE_A).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_PRIVATE_B).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_ENTER).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_EXIT).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_NOTIFY).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SLOW_NOTIFY_ALL).getContents(), 0 },
+            new Object[] { translator.localize(LocaleResources.LOCK_DESCRIPTION_SUCCESSFUL_SPINS).getContents(), 0 },
+        };
+
+        Object[] columnIdentifiers = new Object[] {
+                translator.localize(LocaleResources.LOCK_COLUMN_NAME).getContents(),
+                translator.localize(LocaleResources.LOCK_COLUMN_VALUE).getContents()};
+        model.setDataVector(dataVector, columnIdentifiers);
+
+        table = new ThermostatTable(model);
+        table.setName(TABLE_NAME);
+
+        ThermostatScrollPane scrollPane = new ThermostatScrollPane(table);
+        topPanel.add(scrollPane, BorderLayout.CENTER);
+
+        new ComponentVisibilityNotifier().initialize(topPanel, notifier);
+    }
+
+    @Override
+    public void setLatestLockData(final LockInfo data) {
+        SwingUtilities.invokeLater(new Runnable() {
+            private int row = 0;
+            @Override
+            public void run() {
+                updateModel(data.getContendedLockAttempts());
+                updateModel(data.getDeflations());
+                updateModel(data.getEmptyNotifications());
+                updateModel(data.getFailedSpins());
+                updateModel(data.getFutileWakeups());
+                updateModel(data.getInflations());
+                updateModel(data.getMonExtant());
+                updateModel(data.getMonInCirculation());
+                updateModel(data.getMonScavenged());
+                updateModel(data.getNotifications());
+                updateModel(data.getParks());
+                updateModel(data.getPrivateA());
+                updateModel(data.getPrivateB());
+                updateModel(data.getSlowEnter());
+                updateModel(data.getSlowExit());
+                updateModel(data.getSlowNotify());
+                updateModel(data.getSlowNotifyAll());
+                updateModel(data.getSuccessfulSpins());
+            }
+
+            private void updateModel(long number) {
+                model.setValueAt(number, row, VALUE_COLUMN);
+                row++;
+            }
+        });
+    }
+    @Override
+    public Component getUiComponent() {
+        return topPanel;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadCountView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.client.swing.ComponentVisibleListener;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ChartPanel;
+import com.redhat.thermostat.thread.client.common.chart.LivingDaemonThreadDifferenceChart;
+import com.redhat.thermostat.thread.client.common.view.ThreadCountView;
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import java.awt.Component;
+
+public class SwingThreadCountView extends ThreadCountView implements SwingComponent {
+    
+    private ThreadAliveDaemonTimelinePanel timelinePanel;
+
+    public SwingThreadCountView() {
+        timelinePanel = new ThreadAliveDaemonTimelinePanel();
+        timelinePanel.addHierarchyListener(new ComponentVisibleListener() {
+            @Override
+            public void componentShown(Component component) {
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        SwingThreadCountView.this.notify(Action.VISIBLE);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+
+            @Override
+            public void componentHidden(Component component) {
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        SwingThreadCountView.this.notify(Action.HIDDEN);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+        });
+    }
+    
+    public void setLiveThreads(final String liveThreads) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                timelinePanel.getLiveThreads().setText(liveThreads);
+            }
+        });
+    };
+    
+    @Override
+    public void updateLivingDaemonTimeline(final LivingDaemonThreadDifferenceChart model)
+    {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                JPanel pane = timelinePanel.getTimelinePanel();
+                pane.removeAll();
+                
+                ChartPanel charts = new ChartPanel(model.createChart(pane.getWidth(), pane.getBackground()));
+                charts.setName("threadChartPanel");
+                pane.add(charts);
+                pane.revalidate();
+                pane.repaint();
+            }
+        });
+    }
+    
+    @Override
+    public void setDaemonThreads(final String daemonThreads) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                timelinePanel.getDaemonThreads().setText(daemonThreads);
+            }
+        });
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return timelinePanel;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadDetailsView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ChartPanel;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.ThreadTableBean;
+import com.redhat.thermostat.thread.client.common.chart.ThreadDeatailsPieChart;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.common.view.ThreadDetailsView;
+
+public class SwingThreadDetailsView extends ThreadDetailsView implements SwingComponent {
+
+    private JPanel details;
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    SwingThreadDetailsView() {
+        details = new JPanel();
+        details.setLayout(new BorderLayout(0, 0));
+        
+        JLabel lblNewLabel = new JLabel(t.localize(LocaleResources.THREAD_DETAILS_EMTPY).getContents());
+        lblNewLabel.setIcon(new ImageIcon(getEmptyDetailsIcon().getData().array()));
+        details.add(lblNewLabel);
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return details;
+    }
+
+    @Override
+    public void setDetails(ThreadTableBean thread) {
+        details.removeAll();
+        
+        ThreadDetailsChart threadChart = new ThreadDetailsChart();
+        
+        ChartPanel threadSummary = new ChartPanel(new ThreadDeatailsPieChart(thread).createChart());
+        threadChart.add(threadSummary);
+        
+        details.add(threadChart);
+        details.repaint();
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadTableView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.client.swing.NonEditableTableModel;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ThermostatTable;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+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 java.awt.Component;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+
+public class SwingThreadTableView extends ThreadTableView implements SwingComponent {
+
+    private boolean tableRepacked = false; 
+    
+    private int currentSelection = -1;
+    
+    private ThermostatTable table;
+    private ThreadTable tablePanel;
+
+    private Map<ThreadTableBean, Integer> beans;
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    
+    public SwingThreadTableView() {
+
+        beans = new HashMap<>();
+        tablePanel = new ThreadTable();
+        new ComponentVisibilityNotifier().initialize(tablePanel, notifier);
+        
+        table = new ThermostatTable(new ThreadViewTableModel());
+        table.setName("threadBeansTable");
+        table.getModel().addTableModelListener(new TableModelListener() {
+            @Override
+            public void tableChanged(TableModelEvent e) {
+                // NOTE: The fireTableDataChanged executes this listener
+                // before the internal listener, this means this update will
+                // be overridden since the default listener resets the model.
+                // So, although we are in the EDT, we need to ensure that
+                // we schedule this operation for later, rather than do it
+                // right away... isn't Swing fun?
+                SwingUtilities.invokeLater(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (currentSelection != -1) {
+                            table.setRowSelectionInterval(currentSelection, currentSelection);
+                        }
+                    }
+                });
+            }
+        });
+        tablePanel.add(table.wrap());
+        
+        table.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() == 2) {
+                    ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
+                    int selectedRow = table.getSelectedRow();
+                    if (selectedRow != -1) {
+                        selectedRow = table.convertRowIndexToModel(selectedRow);
+                        final ThreadTableBean bean = model.infos.get(selectedRow);
+                        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                            protected Void doInBackground() throws Exception {
+                                threadTableNotifier.fireAction(ThreadSelectionAction.SHOW_THREAD_DETAILS, bean);
+                                return null;
+                            }
+                        };
+                        worker.execute();
+                    }
+                }
+            }
+        });
+    }
+
+    @Override
+    public void clear() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
+                model.setRowCount(0);
+                beans.clear();
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return tablePanel;
+    }
+    
+    @Override
+    public void display(final ThreadTableBean tableBean) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                
+                // reset the selection for the next iteration
+                // everything is happening in one thread, so there's no fear
+                currentSelection = -1;
+                
+                ThreadViewTableModel model = (ThreadViewTableModel) table.getModel();
+                int selectedRow = table.getSelectedRow();
+                
+                ThreadTableBean info = null;
+                if (selectedRow != -1) {
+                    info = model.infos.get(selectedRow);
+                }
+
+                // update the infos
+                Integer beanIndex = beans.get(tableBean);
+                if (beanIndex == null) {
+                    beanIndex = Integer.valueOf(model.infos.size());
+                    beans.put(tableBean, beanIndex);
+                    model.infos.add(tableBean);
+                }
+
+                if (info != null) {
+                    int index = 0;
+                    for (ThreadTableBean inModel : model.infos) {
+                        if (info.equals(inModel)) {
+                            currentSelection = index;
+                            break;
+                        }
+                        index++;
+                    }
+                }
+                
+                // just repack once, or the user will see the table moving around
+                if (!tableRepacked) {
+                    table.repackCells();
+                    tableRepacked = true;
+                }
+            }
+        });
+    }
+
+    @Override
+    public void submitChanges() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                ThreadViewTableModel model =
+                        (ThreadViewTableModel) table.getModel();
+                model.fireTableDataChanged();
+            }
+        });
+    }
+
+    @SuppressWarnings("serial")
+    private class ThreadViewTableModel extends NonEditableTableModel {
+
+        private String [] columns = {
+                t.localize(LocaleResources.NAME).getContents(),
+                t.localize(LocaleResources.ID).getContents(),
+                t.localize(LocaleResources.FIRST_SEEN).getContents(),
+                t.localize(LocaleResources.LAST_SEEN).getContents(),
+                t.localize(LocaleResources.WAIT_COUNT).getContents(),
+                t.localize(LocaleResources.BLOCK_COUNT).getContents(),
+                t.localize(LocaleResources.RUNNING).getContents(),
+                t.localize(LocaleResources.WAITING).getContents(),
+                t.localize(LocaleResources.SLEEPING).getContents(),
+                t.localize(LocaleResources.MONITOR).getContents(), //, "Heap", "CPU Time", "User CPU Time"
+        };
+
+        private List<ThreadTableBean> infos;
+        public ThreadViewTableModel() {
+            this.infos = new ArrayList<>();
+        }
+    
+        @Override
+        public String getColumnName(int column) {
+            return columns[column];
+        }
+        
+        @Override
+        public int getColumnCount() {
+            return columns.length;
+        }
+        
+        @Override
+        public int getRowCount() {
+            if (infos == null) {
+                return 0;
+            }
+            return infos.size();
+        }
+        
+        @Override
+        public Class<?> getColumnClass(int column) {
+            switch (column) {
+            case 0:
+            case 2:
+            case 3:
+            case 6:
+            case 7:
+            case 8:
+            case 9:
+                return String.class;
+            default:
+                return Long.class;
+            }
+        }
+        
+        @Override
+        public Object getValueAt(int row, int column) {
+
+            DecimalFormat format = new DecimalFormat("###.00");
+
+            Object result = null;
+            
+            ThreadTableBean info = infos.get(row);
+            switch (column) {
+            case 0:
+                result = info.getName();
+                break;
+            case 1:
+                result = info.getId();
+                break;
+            case 2:
+                result = new Date(info.getStartTimeStamp()).toString();
+                break;
+            case 3:
+                if (info.getStopTimeStamp() != 0) {
+                    result = new Date(info.getStopTimeStamp()).toString();
+                } else {
+                    result = "-";
+                }
+                break;
+            case 4:
+                result = info.getWaitedCount();
+                break;
+            case 5:
+                result = info.getBlockedCount();
+                break;
+            case 6:
+                result = format.format(info.getRunningPercent());
+                break;
+            case 7:
+                result = format.format(info.getWaitingPercent());
+                break;
+            case 8:
+                result = format.format(info.getSleepingPercent());
+                break;
+            case 9:
+                result = format.format(info.getMonitorPercent());
+                break;
+             default:
+                 result = "n/a";
+                 break;
+            }
+            return result;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadTimelineView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+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.utils.EDTHelper;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.RangeComponent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.TimelineComponent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.TimelineContainer;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.TimelineViewComponent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RangedTimelineProbe;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.TimelineDateFormatter;
+
+import java.awt.Component;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SwingThreadTimelineView extends ThreadTimelineView implements SwingComponent  {
+
+    private final UIDefaults uiDefaults;
+    private TimelineViewComponent contentPane;
+
+    private ComponentVisibilityNotifier visibilityNotifier;
+
+    private Map<ThreadInfo, TimelineComponent> timelines;
+
+    private EDTHelper edt;
+
+    public SwingThreadTimelineView(UIDefaults uiDefaults) {
+        this.uiDefaults = uiDefaults;
+
+        edt = new EDTHelper();
+        timelines = new HashMap<>();
+
+        this.contentPane = new TimelineViewComponent(uiDefaults);
+        clear();
+
+        visibilityNotifier = new ComponentVisibilityNotifier();
+        visibilityNotifier.initialize(contentPane, notifier);
+    }
+
+    @Override
+    public void addThread(final ThreadInfo thread) {
+        edt.callLater(new Runnable() {
+            @Override
+            public void run() {
+                if (!timelines.containsKey(thread)) {
+                    TimelineComponent timeline =
+                            new TimelineComponent(uiDefaults, thread,
+                                                  contentPane.getModel());
+                    timeline.initComponents();
+
+                    timelines.put(thread, timeline);
+                    contentPane.addTimeline(timeline);
+                }
+            }
+        });
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return contentPane;
+    }
+
+    @Override
+    public void setTotalRange(final Range<Long> totalRange) {
+        edt.callLater(new Runnable() {
+            @Override
+            public void run() {
+                contentPane.getModel().setRange(totalRange);
+            }
+        });
+    }
+
+    @Override
+    public void clear() {
+        edt.callLater(new Runnable() {
+            @Override
+            public void run() {
+                timelines.clear();
+                contentPane.removeAll();
+                contentPane.initComponents();
+                contentPane.revalidate();
+                contentPane.repaint();
+            }
+        });
+    }
+
+    @Override
+    public void addProbe(final ThreadInfo info, final TimelineProbe state) {
+        edt.callLater(new Runnable() {
+            @Override
+            public void run() {
+                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();
+                timelineContainer.repaint();
+            }
+        });
+    }
+
+    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);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+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.ThermostatTabbedPane;
+import com.redhat.thermostat.common.ApplicationService;
+import com.redhat.thermostat.shared.locale.LocalizedString;
+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.LockView;
+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.ThreadTimelineView;
+import com.redhat.thermostat.thread.client.common.view.ThreadView;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+import com.redhat.thermostat.thread.model.ThreadSession;
+
+import javax.swing.JOptionPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.SwingUtilities;
+import javax.swing.SwingWorker;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import java.awt.Component;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+
+public class SwingThreadView extends ThreadView implements SwingComponent {
+    
+    private String DIVIDER_LOCATION_KEY;
+    
+    private ThreadMainPanel panel;
+    
+    private SwingThreadCountView threadCountView;
+    private SwingThreadTableView threadTableView;
+    private SwingVmDeadLockView vmDeadLockView;
+    private SwingThreadTimelineView threadTimelineView;
+    private SwingThreadDetailsView threadDetailsView;
+    private SwingLockView lockView;
+
+    private JTabbedPane topPane;
+    private JTabbedPane bottomPane;
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private boolean skipNotification = false;
+    
+    private int threadDetailsPaneID = 0;
+    
+    private UIDefaults uiDefaults;
+    private boolean viewControlsEnabled = true;
+
+
+    public SwingThreadView(UIDefaults uiDefaults) {
+        
+        this.uiDefaults = uiDefaults;
+
+        panel = new ThreadMainPanel(uiDefaults);
+        // TODO use ComponentVisiblityNotifier instead
+        // sadly, the BasicView.notifier field can not be accessed here
+        panel.addHierarchyListener(new ComponentVisibleListener() {
+
+            @Override
+            public void componentShown(Component component) {
+                SwingThreadView.this.notify(Action.VISIBLE);
+                restoreDivider();
+            }
+
+            @Override
+            public void componentHidden(Component component) {
+                SwingThreadView.this.notify(Action.HIDDEN);
+            }
+        });
+        
+        panel.getSplitPane().addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY,
+                new PropertyChangeListener() {
+                    @Override
+                    public void propertyChange(PropertyChangeEvent evt) {
+                        JSplitPane sourceSplitPane = (JSplitPane) evt.getSource();
+                        saveDivider(sourceSplitPane.getDividerLocation());
+                    }
+                });
+
+        panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.START_RECORDING).getContents());
+        panel.getRecordingToggleButton().addItemListener(new ItemListener()
+        {
+            @Override
+            public void itemStateChanged(ItemEvent e) {
+                
+                ThreadAction action = null;                
+                if (e.getStateChange() == ItemEvent.SELECTED) {
+                    action = ThreadAction.START_LIVE_RECORDING;
+                    panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.STOP_RECORDING).getContents());
+                } else {
+                    action = ThreadAction.STOP_LIVE_RECORDING;
+                    panel.getRecordingToggleButton().setToolTipText(t.localize(LocaleResources.START_RECORDING).getContents());
+                }
+                
+                if (skipNotification) return;
+                
+                final ThreadAction toNotify = action;
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        notifier.fireAction(toNotify);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+        });
+
+        panel.getShowRecordedSessionsButton().setToolTipText(t.localize(LocaleResources.RECORDING_LIST_HINT).getContents());
+        panel.getShowRecordedSessionsButton().addItemListener(new ItemListener() {
+            @Override
+            public void itemStateChanged(ItemEvent e) {
+                if (e.getStateChange() != ItemEvent.SELECTED) {
+                    panel.toggleOverlayPanel(false);
+                    return;
+                }
+
+                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
+                    @Override
+                    protected Void doInBackground() throws Exception {
+                        notifier.fireAction(ThreadAction.REQUEST_DISPLAY_RECORDED_SESSIONS);
+                        return null;
+                    }
+                };
+                worker.execute();
+            }
+        });
+
+        panel.getSessionsPanel().addListSelectionListener(new ListSelectionListener() {
+            @Override
+            public void valueChanged(ListSelectionEvent e) {
+                notifier.fireAction(ThreadAction.REQUEST_LOAD_SESSION,
+                                    panel.getSessionsPanel().getSelectedValue());
+            }
+        });
+        setupTopPane();
+        setupBottomPane();
+    }
+    
+    private void setupTopPane() {
+        topPane = new ThermostatTabbedPane();
+        topPane.setName("topTabbedPane");
+        
+        threadCountView = new SwingThreadCountView();
+        Component comp = threadCountView.getUiComponent();
+        comp.setName("count");
+        topPane.addTab(t.localize(LocaleResources.THREAD_COUNT).getContents(), comp);
+        
+        threadTimelineView = new SwingThreadTimelineView(uiDefaults);
+        comp = threadTimelineView.getUiComponent();
+        comp.setName("timeline");
+        topPane.addTab(t.localize(LocaleResources.TIMELINE).getContents(), comp);
+        
+        lockView = new SwingLockView();
+        comp = lockView.getUiComponent();
+        comp.setName("lock");
+        topPane.addTab(t.localize(LocaleResources.LOCKS).getContents(), comp);
+
+        panel.getSplitPane().setTopComponent(topPane);
+    }
+    
+    private void setupBottomPane() {
+        bottomPane = new ThermostatTabbedPane();
+        bottomPane.setName("bottomTabbedPane");
+        
+        threadTableView = new SwingThreadTableView();
+        bottomPane.addTab(t.localize(LocaleResources.TABLE).getContents(), threadTableView.getUiComponent());
+        
+        threadDetailsView = new SwingThreadDetailsView();
+        bottomPane.addTab(t.localize(LocaleResources.DETAILS).getContents(), threadDetailsView.getUiComponent());
+        threadDetailsPaneID = 1;
+
+        vmDeadLockView = new SwingVmDeadLockView();
+        bottomPane.addTab(t.localize(LocaleResources.VM_DEADLOCK).getContents(), vmDeadLockView.getUiComponent());
+
+        panel.getSplitPane().setBottomComponent(bottomPane);
+    }
+    
+    @Override
+    public Component getUiComponent() {
+        return panel;
+    }
+    
+    @Override
+    public void setApplicationService(ApplicationService appService, String uniqueId) {
+        super.setApplicationService(appService, uniqueId);
+        DIVIDER_LOCATION_KEY = "divider." + uniqueId;
+    }
+
+    @Override
+    public void setEnableRecordingControl(final boolean enable) {
+        this.viewControlsEnabled = enable;
+        if (!enable) {
+            setRecording(MonitoringState.DISABLED, false);
+        }
+    }
+    
+    @Override
+    public void setRecording(final MonitoringState monitoringState, final boolean notify) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                if (!notify) skipNotification = true;
+                if (!viewControlsEnabled) {
+                    panel.getRecordingToggleButton().setToggleActionState(MonitoringState.DISABLED);
+                } else {
+                    panel.getRecordingToggleButton().setToggleActionState(monitoringState);
+                }
+                if (!notify) skipNotification = false;
+            }
+        });
+    }
+
+    @Override
+    public VmDeadLockView createDeadLockView() {
+        return vmDeadLockView;
+    }
+    
+    @Override
+    public ThreadTableView createThreadTableView() {
+        return threadTableView;
+    }
+    
+    @Override
+    public void displayWarning(final LocalizedString warning) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                JOptionPane.showMessageDialog(panel.getParent(), warning.getContents(), "", JOptionPane.WARNING_MESSAGE);
+            }
+        });
+    }
+    
+    private void restoreDivider() {
+        int location = (int) ((double) (panel.getSplitPane().getHeight() - panel.getSplitPane().getDividerSize()) * 0.80);
+        if (appService != null) {
+            Object _location = appService.getApplicationCache().getAttribute(DIVIDER_LOCATION_KEY);
+            if (_location != null) {
+                location = (Integer) _location;
+            }
+        }
+        panel.getSplitPane().setDividerLocation(location);
+    }
+    
+    private void saveDivider(int location) {
+        if (appService != null) {
+            appService.getApplicationCache().addAttribute(DIVIDER_LOCATION_KEY, location);
+        }
+    }
+    
+    @Override
+    public void displayThreadDetails(final ThreadTableBean thread) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                bottomPane.setSelectedIndex(threadDetailsPaneID);
+                threadDetailsView.setDetails(thread);
+            }
+        });
+    }
+    
+    @Override
+    public ThreadTimelineView createThreadTimelineView() {
+        return threadTimelineView;
+    }
+
+    @Override
+    public ThreadCountView createThreadCountView() {
+        return threadCountView;
+    }
+
+    @Override
+    public LockView createLockView() {
+        return lockView;
+    }
+
+    @Override
+    public void displayTimelineSessionList(final List<ThreadSession> threadSessions) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                panel.setOverlayContent(threadSessions);
+                panel.toggleOverlayPanel(true);
+            }
+        });
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/SwingVmDeadLockView.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.FontMetrics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.SwingUtilities;
+
+import com.mxgraph.layout.mxCircleLayout;
+import com.mxgraph.layout.mxEdgeLabelLayout;
+import com.mxgraph.layout.mxGraphLayout;
+import com.mxgraph.model.mxCell;
+import com.mxgraph.swing.mxGraphComponent;
+import com.mxgraph.util.mxConstants;
+import com.mxgraph.view.mxGraph;
+import com.redhat.thermostat.client.swing.SwingComponent;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollBar;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.client.swing.components.ThermostatTextArea;
+import com.redhat.thermostat.client.swing.experimental.ComponentVisibilityNotifier;
+import com.redhat.thermostat.common.utils.StringUtils;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.DeadlockParser;
+import com.redhat.thermostat.thread.client.common.DeadlockParser.Information;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.common.view.VmDeadLockView;
+
+public class SwingVmDeadLockView extends VmDeadLockView implements SwingComponent {
+
+    private static final Translate<LocaleResources> translate = LocaleResources.createLocalizer();
+
+    private final JPanel actualComponent = new JPanel();
+    private final JButton checkForDeadlockButton;
+
+    private final JSplitPane deadlockTextAndVisualization = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+
+    private final JPanel graphical = new JPanel();
+    private final ThermostatTextArea description = new ThermostatTextArea();
+    /**
+     * Whether to set the divider's location. Do this only once to set a sane
+     * initial value but don't change anything after and allow the user to tweak
+     * this as appropriate.
+     */
+    private boolean dividerLocationSet = false;
+
+    public SwingVmDeadLockView() {
+        actualComponent.setLayout(new GridBagLayout());
+
+        GridBagConstraints c = new GridBagConstraints();
+        c.gridy = 0;
+        c.anchor = GridBagConstraints.LINE_END;
+        checkForDeadlockButton = new JButton(translate.localize(LocaleResources.CHECK_FOR_DEADLOCKS).getContents());
+        checkForDeadlockButton.addActionListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                deadLockNotifier.fireAction(VmDeadLockViewAction.CHECK_FOR_DEADLOCK);
+            }
+        });
+
+        actualComponent.add(checkForDeadlockButton, c);
+
+        c.anchor = GridBagConstraints.LINE_START;
+        c.gridy++;
+        c.fill = GridBagConstraints.BOTH;
+        c.weightx = 1;
+        c.weighty = 1;
+
+        description.setEditable(false);
+
+        JScrollPane scrollPane = new ThermostatScrollPane(description);
+
+        graphical.setLayout(new BorderLayout());
+
+        deadlockTextAndVisualization.setLeftComponent(scrollPane);
+        deadlockTextAndVisualization.setRightComponent(graphical);
+
+        actualComponent.add(deadlockTextAndVisualization, c);
+
+        new ComponentVisibilityNotifier().initialize(actualComponent, notifier);
+    }
+
+    @Override
+    public void setDeadLockInformation(final Information parsed, final String rawText) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+
+                graphical.removeAll();
+
+                if (!dividerLocationSet) {
+                    // 0.7 is chosen empirically to show a bit more of the text than the gui
+                    deadlockTextAndVisualization.setDividerLocation(0.7);
+                    deadlockTextAndVisualization.revalidate();
+                    dividerLocationSet = true;
+                }
+
+                if (parsed != null) {
+                    FontMetrics metrics = graphical.getGraphics().getFontMetrics();
+                    graphical.add(createGraph(parsed, metrics), BorderLayout.CENTER);
+                }
+
+                graphical.revalidate();
+                graphical.repaint();
+
+                description.setText(rawText);
+            }
+        });
+    }
+
+    private mxGraphComponent createGraph(Information info, FontMetrics fontMetrics) {
+
+        final mxGraph graph = new mxGraph() {
+
+            /* Show tooltips for vertices and edges */
+            @Override
+            public String getToolTipForCell(Object source) {
+                mxCell cell = ((mxCell) source);
+
+                if (cell.getValue() instanceof GraphItem) {
+                    return ((GraphItem) cell.getValue()).getTooltip();
+                } else {
+                    return super.getToolTipForCell(cell);
+                }
+            }
+
+
+            /* Prevent modifying the contents of edges or vertices */
+            @Override
+            public boolean isCellEditable(Object cell) {
+                return false;
+            }
+
+            /* Prevent moving edges away from the vertices */
+            @Override
+            public boolean isCellSelectable(Object cell) {
+                return !model.isEdge(cell);
+            }
+
+        };
+
+        Object parent = graph.getDefaultParent();
+
+        addDeadlockToGraph(info, graph, parent, fontMetrics);
+
+        graph.setAutoSizeCells(true);
+        graph.setCellsResizable(true);
+
+        final mxGraphComponent graphComponent = new mxGraphComponent(graph);
+        graphComponent.setTextAntiAlias(true);
+        graphComponent.setToolTips(true);
+        graphComponent.setConnectable(false);
+
+        graphComponent.setHorizontalScrollBar(new ThermostatScrollBar(ThermostatScrollBar.HORIZONTAL));
+        graphComponent.setVerticalScrollBar(new ThermostatScrollBar(ThermostatScrollBar.VERTICAL));
+
+        Map<String, Object> style = graph.getStylesheet().getDefaultVertexStyle();
+        style.put(mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER);
+        graph.getStylesheet().setDefaultVertexStyle(style);
+
+        mxGraphLayout layout = new mxCircleLayout(graph);
+        layout.execute(graph.getDefaultParent());
+
+        layout = new mxEdgeLabelLayout(graph);
+        layout.execute(graph.getDefaultParent());
+
+        return graphComponent;
+    }
+
+    @Override
+    public void setCheckDeadlockControlEnabled(final boolean enabled) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                SwingVmDeadLockView.this.checkForDeadlockButton.setEnabled(enabled);
+            }
+        });
+    }
+
+    private static void addDeadlockToGraph(Information info, mxGraph graph, Object parent, FontMetrics metrics) {
+        graph.getModel().beginUpdate(); // batch updates
+        try {
+            Map<String, Object> idToCell = new HashMap<>();
+            Map<String, String> idToLabel = new HashMap<>();
+
+            for (DeadlockParser.Thread thread : info.threads) {
+                String label = getThreadLabel(thread);
+                String tooltip = getThreadTooltip(thread);
+                idToLabel.put(thread.id, label);
+                GraphItem node = new GraphItem(label, tooltip);
+                final int PADDING = 20;
+                int width = metrics.stringWidth(label) + PADDING;
+                int height = metrics.getHeight() + PADDING;
+                Object threadNode = graph.insertVertex(parent, thread.id, node, 0, 0, width, height);
+                idToCell.put(thread.id, threadNode);
+            }
+
+            for (DeadlockParser.Thread thread : info.threads) {
+                String label = translate.localize(LocaleResources.DEADLOCK_WAITING_ON).getContents();
+                String tooltip = getEdgeTooltip(thread, idToLabel);
+                GraphItem edge = new GraphItem(label, tooltip);
+                graph.insertEdge(parent, thread.waitingOn.name, edge, idToCell.get(thread.id), idToCell.get(thread.waitingOn.ownerId));
+            }
+
+        }  finally {
+           graph.getModel().endUpdate();
+        }
+    }
+
+    private static String getThreadLabel(DeadlockParser.Thread thread) {
+        return translate.localize(LocaleResources.DEADLOCK_THREAD_NAME, thread.name, thread.id).getContents();
+    }
+
+    private static String getThreadTooltip(DeadlockParser.Thread thread) {
+        return translate.localize(
+                LocaleResources.DEADLOCK_THREAD_TOOLTIP,
+                StringUtils.htmlEscape(thread.waitingOn.name),
+                stackTraceToHtmlString(thread.stackTrace))
+            .getContents();
+    }
+
+    private static String stackTraceToHtmlString(List<String> items) {
+        StringBuilder result = new StringBuilder();
+        for (String item : items) {
+            result.append(StringUtils.htmlEscape(item)).append("<br/>");
+        }
+        return result.toString();
+    }
+
+    private static String getEdgeTooltip(DeadlockParser.Thread thread, Map<String, String> idToLabel) {
+        return translate.localize(
+                LocaleResources.DEADLOCK_EDGE_TOOLTIP,
+                idToLabel.get(thread.id),
+                idToLabel.get(thread.waitingOn.ownerId))
+            .getContents();
+    }
+
+    @Override
+    public Component getUiComponent() {
+        return actualComponent;
+    }
+
+    static class GraphItem implements Serializable {
+
+        private final String label;
+        private final String tooltip;
+
+        public GraphItem(String label, String tooltip) {
+            this.label = label;
+            this.tooltip = tooltip;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(label, tooltip);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            GraphItem other = (GraphItem) obj;
+            return Objects.equals(label, other.label) && Objects.equals(tooltip, other.tooltip);
+        }
+
+        @Override
+        public String toString() {
+            return label;
+        }
+
+        public String getTooltip() {
+            return tooltip;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadAliveDaemonTimelinePanel.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+
+import javax.swing.BoxLayout;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.LayoutStyle.ComponentPlacement;
+import javax.swing.SwingConstants;
+
+@SuppressWarnings("serial")
+class ThreadAliveDaemonTimelinePanel extends JPanel {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    
+    private JLabel liveThreads;
+    private JLabel daemonThreads;
+    private JPanel timelinePanel;
+    
+    /**
+     * Create the panel.
+     */
+    public ThreadAliveDaemonTimelinePanel() {
+        JPanel runningPanel = new JPanel();
+
+        timelinePanel = new JPanel();
+        timelinePanel.setOpaque(false);
+        timelinePanel.setLayout(new BoxLayout(timelinePanel, BoxLayout.X_AXIS));
+        
+        GroupLayout groupLayout = new GroupLayout(this);
+        groupLayout.setHorizontalGroup(
+            groupLayout.createParallelGroup(Alignment.TRAILING)
+                .addComponent(runningPanel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE)
+                .addComponent(timelinePanel, Alignment.LEADING, GroupLayout.DEFAULT_SIZE, 577, Short.MAX_VALUE)
+        );
+        groupLayout.setVerticalGroup(
+            groupLayout.createParallelGroup(Alignment.LEADING)
+                .addGroup(groupLayout.createSequentialGroup()
+                    .addComponent(runningPanel, GroupLayout.PREFERRED_SIZE, 41, GroupLayout.PREFERRED_SIZE)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(timelinePanel, GroupLayout.DEFAULT_SIZE, 254, Short.MAX_VALUE))
+        );
+        
+        JLabel liveThreadsLabel = new JLabel(t.localize(LocaleResources.LIVE_THREADS).getContents() + ":");
+        
+        JLabel daemonThreadsLabel = new JLabel(t.localize(LocaleResources.DAEMON_THREADS).getContents() + ":");
+        
+        liveThreads = new JLabel("-");
+        liveThreads.setHorizontalAlignment(SwingConstants.RIGHT);
+        
+        daemonThreads = new JLabel("-");
+        daemonThreads.setHorizontalAlignment(SwingConstants.RIGHT);
+        GroupLayout gl_runningPanel = new GroupLayout(runningPanel);
+        gl_runningPanel.setHorizontalGroup(
+            gl_runningPanel.createParallelGroup(Alignment.LEADING)
+                .addGroup(gl_runningPanel.createSequentialGroup()
+                    .addComponent(liveThreadsLabel)
+                    .addGap(18)
+                    .addComponent(liveThreads, GroupLayout.PREFERRED_SIZE, 85, GroupLayout.PREFERRED_SIZE)
+                    .addGap(18)
+                    .addComponent(daemonThreadsLabel)
+                    .addPreferredGap(ComponentPlacement.RELATED)
+                    .addComponent(daemonThreads, GroupLayout.PREFERRED_SIZE, 49, GroupLayout.PREFERRED_SIZE)
+                    .addContainerGap(54, Short.MAX_VALUE))
+        );
+        gl_runningPanel.setVerticalGroup(
+            gl_runningPanel.createParallelGroup(Alignment.TRAILING)
+                .addGroup(Alignment.LEADING, gl_runningPanel.createSequentialGroup()
+                    .addGroup(gl_runningPanel.createParallelGroup(Alignment.BASELINE)
+                        .addComponent(liveThreadsLabel)
+                        .addComponent(liveThreads)
+                        .addComponent(daemonThreads)
+                        .addComponent(daemonThreadsLabel))
+                    .addContainerGap(26, Short.MAX_VALUE))
+        );
+        runningPanel.setLayout(gl_runningPanel);
+        setLayout(groupLayout);
+
+    }
+    
+    public JLabel getLiveThreads() {
+        return liveThreads;
+    }
+    
+    public JLabel getDaemonThreads() {
+        return daemonThreads;
+    }
+    
+    public JPanel getTimelinePanel() {
+        return timelinePanel;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadDetailsChart.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import javax.swing.BoxLayout;
+import javax.swing.JPanel;
+
+@SuppressWarnings("serial")
+public class ThreadDetailsChart extends JPanel {
+    
+    public ThreadDetailsChart() {
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadMainPanel.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.client.swing.IconResource;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.client.swing.components.ActionToggleButton;
+import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+import com.redhat.thermostat.client.swing.components.HeaderPanel;
+import com.redhat.thermostat.client.swing.components.Icon;
+import com.redhat.thermostat.client.swing.components.OverlayPanel;
+import com.redhat.thermostat.client.swing.components.ShadowLabel;
+import com.redhat.thermostat.client.swing.components.ThermostatScrollPane;
+import com.redhat.thermostat.client.swing.components.ThermostatThinScrollBar;
+import com.redhat.thermostat.client.ui.Palette;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.model.ThreadSession;
+
+import javax.swing.BoxLayout;
+import javax.swing.DefaultListModel;
+import javax.swing.GroupLayout;
+import javax.swing.GroupLayout.Alignment;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JSplitPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.OverlayLayout;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Date;
+import java.util.List;
+
+@SuppressWarnings("serial")
+class ThreadMainPanel extends JPanel {
+
+    private static final Icon START_ICON = IconResource.SAMPLE.getIcon();
+    private final Icon stopIcon;
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    private JSplitPane splitPane;
+    
+    private ActionToggleButton toggleButton;
+    private ActionToggleButton showRecordedSessionsButton;
+
+    private OverlayPanel overlay;
+
+    private UIDefaults uiDefaults;
+    private ThreadSessionList sessionsPanel;
+    private DefaultListModel<ThreadSession> sessionsModel;
+
+    @Override
+    public boolean isOptimizedDrawingEnabled() {
+        return false;
+    }
+
+    public ThreadMainPanel(UIDefaults uiDefaults) {
+        this.uiDefaults = uiDefaults;
+
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+        HeaderPanel headerPanel = new HeaderPanel();
+        headerPanel.setHeader(t.localize(LocaleResources.THREAD_CONTROL_PANEL));
+
+        stopIcon = new FontAwesomeIcon('\uf28e', START_ICON.getIconHeight(), uiDefaults.getIconColor());
+
+        toggleButton = new ActionToggleButton(START_ICON, stopIcon, t.localize(LocaleResources.THREAD_MONITOR_SWITCH));
+        toggleButton.setName("recordButton");
+        headerPanel.addToolBarButton(toggleButton);
+
+        Icon listSessionsIcon = IconResource.HISTORY.getIcon();
+        showRecordedSessionsButton = new ActionToggleButton(listSessionsIcon, t.localize(LocaleResources.THREAD_MONITOR_DISPLAY_SESSIONS));
+        showRecordedSessionsButton.setName("showRecordedSessionsButton");
+        headerPanel.addToolBarButton(showRecordedSessionsButton);
+
+        overlay = new OverlayPanel(t.localize(LocaleResources.RECORDING_LIST), true, true);
+        overlay.setName("threadOverlayPanel");
+        overlay.addCloseEventListener(new OverlayPanel.CloseEventListener() {
+            @Override
+            public void closeRequested(OverlayPanel.CloseEvent event) {
+                showRecordedSessionsButton.doClick();
+            }
+        });
+
+        JPanel stack = new JPanel();
+        stack.setName("threadStackPanel");
+        stack.setOpaque(true);
+        stack.setLayout(new OverlayLayout(stack));
+
+        splitPane = new JSplitPane();
+        splitPane.setName("threadMainPanelSplitPane");
+        splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+        splitPane.setOneTouchExpandable(true);
+
+        JPanel content = new JPanel();
+        GroupLayout gl_content = new GroupLayout(content);
+        gl_content.setHorizontalGroup(
+            gl_content.createParallelGroup(Alignment.TRAILING)
+                .addGroup(Alignment.LEADING, gl_content.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(splitPane, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+                    .addContainerGap())
+        );
+        gl_content.setVerticalGroup(
+            gl_content.createParallelGroup(Alignment.TRAILING)
+                .addGroup(Alignment.LEADING, gl_content.createSequentialGroup()
+                    .addContainerGap()
+                    .addComponent(splitPane, 0, 240, Short.MAX_VALUE)
+                    .addContainerGap())
+        );
+
+        content.setLayout(gl_content);
+
+        stack.add(overlay);
+        stack.add(content);
+        stack.setOpaque(false);
+
+        headerPanel.setContent(stack);
+
+        add(headerPanel);
+
+        sessionsModel = new DefaultListModel<>();
+
+        sessionsPanel = new ThreadSessionList(sessionsModel);
+        sessionsPanel.setSelectionMode(JList.VERTICAL);
+        sessionsPanel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+        sessionsPanel.setOpaque(false);
+        sessionsPanel.setCellRenderer(new ThreadSessionRenderer());
+        sessionsPanel.addMouseMotionListener(new MouseAdapter() {
+            @Override
+            public void mouseMoved(MouseEvent e) {
+                Point where = new Point(e.getX(), e.getY());
+                int index = sessionsPanel.locationToIndex(where);
+                int hoveredIndex = sessionsPanel.getHoveredIndex();
+                if (index != hoveredIndex) {
+                    sessionsPanel.setHoveredIndex(index);
+                    sessionsPanel.repaint();
+                }
+            }
+        });
+        ThermostatScrollPane scrollPane = new ThermostatScrollPane(sessionsPanel);
+        scrollPane.setVerticalScrollBar(new ThermostatThinScrollBar(ThermostatThinScrollBar.VERTICAL));
+        overlay.add(scrollPane);
+    }
+
+    public ThreadSessionList getSessionsPanel() {
+        return sessionsPanel;
+    }
+
+    public JSplitPane getSplitPane() {
+        return splitPane;
+    }
+    
+    public ActionToggleButton getRecordingToggleButton() {
+        return toggleButton;
+    }
+
+    public ActionToggleButton getShowRecordedSessionsButton() {
+        return showRecordedSessionsButton;
+    }
+
+    public void toggleOverlayPanel(boolean visible) {
+        overlay.setOverlayVisible(visible);
+    }
+
+    public class ThreadSessionRenderer implements ListCellRenderer<ThreadSession> {
+
+        @Override
+        public Component getListCellRendererComponent(JList<? extends ThreadSession> list,
+                                                      ThreadSession value, int index,
+                                                      boolean isSelected,
+                                                      boolean cellHasFocus)
+        {
+            JPanel panel = new JPanel();
+            panel.setLayout(new BorderLayout());
+            panel.setOpaque(false);
+            ShadowLabel label = new ShadowLabel();
+            label.setText("[" + new Date(value.getTimeStamp()) +"]");
+            label.setOpaque(false);
+
+            if (isSelected || cellHasFocus) {
+                panel.setOpaque(true);
+                panel.setBackground((Color) uiDefaults.getSelectedComponentBGColor());
+                label.setForeground((Color) uiDefaults.getSelectedComponentFGColor());
+
+            } else if ((sessionsPanel.getHoveredIndex() == index)) {
+                panel.setOpaque(true);
+                panel.setBackground(Palette.ELEGANT_CYAN.getColor());
+                label.setForeground((Color) uiDefaults.getSelectedComponentFGColor());
+
+            } else {
+                label.setForeground((Color) uiDefaults.getComponentFGColor());
+            }
+
+            panel.add(label);
+            return panel;
+        }
+    }
+
+    public void setOverlayContent(List<ThreadSession> threadSessions) {
+        sessionsModel.clear();
+
+        for (ThreadSession session : threadSessions) {
+            sessionsModel.addElement(session);
+        }
+        sessionsPanel.setHoveredIndex(-1);
+
+        overlay.revalidate();
+        overlay.repaint();
+
+    }
+
+    public class ThreadSessionList extends JList<ThreadSession> {
+        private int hoveredIndex;
+        public ThreadSessionList(ListModel<ThreadSession> dataModel) {
+            super(dataModel);
+            hoveredIndex = -1;
+        }
+
+        public int getHoveredIndex() {
+            return hoveredIndex;
+        }
+
+        public void setHoveredIndex(int hoveredIndex) {
+            this.hoveredIndex = hoveredIndex;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/ThreadTable.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import javax.swing.JPanel;
+import javax.swing.BoxLayout;
+
+@SuppressWarnings("serial")
+public class ThreadTable extends JPanel {
+    /**
+     * Create the panel.
+     */
+    public ThreadTable() {
+        setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RangeComponent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.internal.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;
+        setOpaque(false);
+    }
+
+    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/internal/timeline/RangeComponentHeader.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+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.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.experimental.components.DataPane;
+import com.redhat.thermostat.thread.client.swing.internal.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 static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+
+    private final TimelineModel model;
+    private final UIDefaults defaults;
+    private DataPane controls;
+
+    private JLabel zoomOut;
+    private JLabel restoreZoom;
+    private JLabel zoomIn;
+
+    public RangeComponentHeader(TimelineModel model, UIDefaults defaults) {
+        this.model = model;
+        this.defaults = defaults;
+    }
+
+    public void initComponents() {
+        controls = new DataPane();
+        controls.setLayout(new GridLayout(1, 0, 5, 5));
+
+        Icon baseIcon = new FontAwesomeIcon('\uf066', 15, defaults.getIconColor());
+        Icon hoverIcon = new FontAwesomeIcon('\uf066', 15,
+                                             defaults.getSelectedComponentBGColor());
+        zoomOut = new JLabel(baseIcon);
+        zoomOut.setToolTipText(t.localize(LocaleResources.ZOOM_OUT).getContents());
+        zoomOut.addMouseListener(new Hover(zoomOut, baseIcon, hoverIcon));
+        zoomOut.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                double ratio = model.getMagnificationRatio();
+                model.setMagnificationRatio(ratio/2);
+            }
+        });
+        controls.add(zoomOut);
+
+        baseIcon = new FontAwesomeIcon('\uf03b', 15, defaults.getIconColor());
+        hoverIcon = new FontAwesomeIcon('\uf03b', 15,
+                                        defaults.getSelectedComponentBGColor());
+        restoreZoom = new JLabel(baseIcon);
+        restoreZoom.setToolTipText(t.localize(LocaleResources.RESTORE_ZOOM).getContents());
+        restoreZoom.addMouseListener(new Hover(restoreZoom, baseIcon, hoverIcon));
+        restoreZoom.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                model.setMagnificationRatio(TimelineModel.DEFAULT_RATIO);
+            }
+        });
+        controls.add(restoreZoom);
+
+        baseIcon = new FontAwesomeIcon('\uf065', 15, defaults.getIconColor());
+        hoverIcon = new FontAwesomeIcon('\uf065', 15,
+                                        defaults.getSelectedComponentBGColor());
+        zoomIn = new JLabel(baseIcon);
+        zoomIn.setToolTipText(t.localize(LocaleResources.ZOOM_IN).getContents());
+        zoomIn.addMouseListener(new Hover(zoomIn, baseIcon, hoverIcon));
+        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);
+    }
+
+    public void setControlsEnabled(boolean b) {
+        zoomIn.setEnabled(b);
+        zoomOut.setEnabled(b);
+        restoreZoom.setEnabled(b);
+    }
+
+    private class Hover extends MouseAdapter {
+
+        private final JLabel label;
+        private final Icon baseIcon;
+        private final Icon hoverIcon;
+
+        public Hover(JLabel label, Icon baseIcon, Icon hoverIcon) {
+
+            this.label = label;
+            this.baseIcon = baseIcon;
+            this.hoverIcon = hoverIcon;
+        }
+
+        @Override
+        public void mouseEntered(MouseEvent e) {
+            onMouseHover(true);
+        }
+
+        @Override
+        public void mouseExited(MouseEvent e) {
+            onMouseHover(false);
+        }
+
+        public void onMouseHover(boolean hover) {
+            label.setIcon(hover ? hoverIcon : baseIcon);
+            repaint();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/RangeComponentLayoutManager.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2012-2016 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.internal.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.internal.timeline.model.RangedTimelineProbe;
+import com.redhat.thermostat.thread.client.swing.internal.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/internal/timeline/RulerComponent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012-2016 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.internal.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.internal.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();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineAdjustmentListener.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import javax.swing.BoundedRangeModel;
+
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.TimelineModel;
+
+/**
+ *
+ */
+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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineComponent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+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.internal.timeline.model.TimelineModel;
+
+import java.awt.BorderLayout;
+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;
+
+/**
+ *
+ */
+public class TimelineComponent extends RulerComponent {
+
+    private static final int MIN_HEIGHT = 50;
+    private final ThreadInfo threadInfo;
+
+    private ContentPane labelPane;
+
+    private TimelineLabel label;
+
+    private ThermostatScrollPane scrollPane;
+    private TimelineContainer timelineContainer;
+
+    public TimelineComponent(UIDefaults uiDefaults, ThreadInfo threadInfo,
+                             TimelineModel model)
+    {
+        super(uiDefaults, model);
+        this.threadInfo = threadInfo;
+    }
+
+    public void initComponents() {
+        setName(threadInfo.getName());
+
+        initModel();
+
+        initLabelPane();
+        initThreadPane();
+
+        setBorder(new Separator(uiDefaults, Separator.Side.BOTTOM,
+                                Separator.Type.SOLID));
+
+        Hover hover = new Hover();
+        addMouseListener(hover);
+    }
+
+    private void initModel() {
+        model.getScrollBarModel().addChangeListener(new ChangeListener() {
+            @Override
+            public void stateChanged(ChangeEvent e) {
+                repaint();
+            }
+        });
+    }
+
+    private void initThreadPane() {
+
+        timelineContainer = new TimelineContainer(model);
+        timelineContainer.setName(threadInfo.getName());
+
+        scrollPane = new ThermostatScrollPane(timelineContainer);
+        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_NEVER);
+
+        add(scrollPane, BorderLayout.CENTER);
+
+        scrollPane.getHorizontalScrollBar().setModel(model.getScrollBarModel());
+    }
+
+    public TimelineContainer getTimelineContainer() {
+        return timelineContainer;
+    }
+
+    private void initLabelPane() {
+        label = new TimelineLabel(uiDefaults, getName());
+        labelPane = new ContentPane();
+
+        labelPane.setOpaque(false);
+        labelPane.setLayout(new GridBagLayout());
+
+        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
+    public int getHeight() {
+        return MIN_HEIGHT;
+    }
+
+    private class Hover extends MouseAdapter {
+        @Override
+        public void mouseEntered(MouseEvent e) {
+            onMouseHover(true);
+        }
+
+        @Override
+        public void mouseExited(MouseEvent e) {
+            onMouseHover(false);
+        }
+
+        public void onMouseHover(boolean hover) {
+            label.onMouseHover(hover);
+            repaint();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineContainer.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+import com.redhat.thermostat.thread.client.swing.experimental.components.ContentPane;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RangeChangeEvent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RangeChangeListener;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RatioChangeEvent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RatioChangeListener;
+import com.redhat.thermostat.thread.client.swing.internal.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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineLabel.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline;
+
+import com.redhat.thermostat.client.swing.UIDefaults;
+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 javax.swing.SwingConstants;
+import javax.swing.border.EmptyBorder;
+
+/**
+ *
+ */
+class TimelineLabel extends DataPane {
+    private LabelField nameLabel;
+    private Icon infoOn;
+    private Icon infoOff;
+
+    TimelineLabel(UIDefaults defaults, String text) {
+        super(Palette.WHITE, Palette.LIGHT_GRAY);
+
+        setBorder(new Separator(defaults, Separator.Side.BOTTOM, Separator.Type.SOLID));
+
+        nameLabel = new LabelField(LocalizedString.EMPTY_STRING);
+        nameLabel.setFont(defaults.getDefaultFont().deriveFont(10.f));
+
+        nameLabel.setText(text);
+        nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
+        nameLabel.setVerticalAlignment(SwingConstants.CENTER);
+        nameLabel.setForeground((Color) defaults.getSelectedComponentBGColor());
+
+        // make same small space around the label, a bit higher left and right
+        // to account for the missing icon
+        nameLabel.setBorder(new EmptyBorder(2, 4, 2, 4));
+
+        nameLabel.setHorizontalTextPosition(SwingConstants.LEFT);
+
+        // FIXME && TODO: comment this out for now, the functionality this is
+        // meant for is not yet implemented in the controller
+//        infoOn = new FontAwesomeIcon('\uf05a', 12, Palette.DARK_GRAY.getColor());
+//        infoOff = new FontAwesomeIcon('\uf05a', 12, Palette.PALE_GRAY.getColor());
+//
+//        nameLabel.setIcon(infoOff);
+
+        add(nameLabel);
+    }
+
+    public void onMouseHover(boolean hover) {
+//        nameLabel.setIcon(hover ? infoOn : infoOff);
+        repaint();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/main/java/com/redhat/thermostat/thread/client/swing/internal/timeline/TimelineViewComponent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2012-2016 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.internal.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.internal.timeline.model.LegendPanel;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RangeChangeEvent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RangeChangeListener;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RatioChangeEvent;
+import com.redhat.thermostat.thread.client.swing.internal.timeline.model.RatioChangeListener;
+import com.redhat.thermostat.thread.client.swing.internal.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;
+    private RangeComponentHeader header;
+
+    public TimelineViewComponent(UIDefaults uiDefaults) {
+        this.uiDefaults = uiDefaults;
+    }
+
+    public void initComponents() {
+
+        ContentPane contentPane = new ContentPane();
+
+        this.model = new TimelineModel();
+
+        viewport = new TimelineViewport();
+
+        scrollPane = new ThermostatScrollPane(viewport);
+        scrollPane.setVerticalScrollBarPolicy(ThermostatScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
+        scrollPane.setHorizontalScrollBarPolicy(ThermostatScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+
+        header = new RangeComponentHeader(model, uiDefaults);
+        header.initComponents();
+        scrollPane.setColumnHeaderView(header);
+
+        contentPane.add(scrollPane, BorderLayout.CENTER);
+
+        scrollBar = new ThermostatScrollBar(ThermostatScrollBar.HORIZONTAL);
+        model.setScrollBarModel(scrollBar.getModel());
+
+        scrollBar.addAdjustmentListener(new TimelineAdjustmentListener(model));
+
+        contentPane.add(scrollBar, BorderLayout.SOUTH);
+        scrollBar.setEnabled(false);
+        scrollBar.setVisible(false);
+
+        header.setControlsEnabled(false);
+
+        add(contentPane, BorderLayout.CENTER);
+
+        LegendPanel legend = new LegendPanel(uiDefaults);
+        add(legend, BorderLayout.SOUTH);
+
+        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();
+        // no data, so no scrolling
+        if (range == null) {
+            scrollBar.setVisible(false);
+            scrollBar.setEnabled(false);
+            return;
+        }
+
+        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);
+        header.setControlsEnabled(true);
+        checkEnableScrollbar();
+        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/internal/timeline/TimelineViewport.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/LegendPanel.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2012-2016 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.internal.timeline.model;
+
+import com.redhat.thermostat.client.swing.GraphicsUtils;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.thread.client.common.chart.ChartColors;
+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.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import javax.swing.Icon;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+
+/**
+ *
+ */
+public class LegendPanel extends DataPane {
+
+    public LegendPanel(UIDefaults defaults) {
+        setBorder(new Separator(defaults, Separator.Side.TOP.TOP, Separator.Type.SOLID));
+        setLayout(new FlowLayout(FlowLayout.RIGHT, 10, 6));
+        setPreferredSize(new Dimension(getWidth(), 30));
+
+        for (Thread.State state : Thread.State.values()) {
+
+            Color color = ChartColors.getColor(state);
+            // no chart is black, it's just the default colour
+            if (!color.equals(Color.BLACK)) {
+                JLabel label =  new JLabel(new ColorIcon(color), SwingConstants.LEFT);
+                label.setText(state.toString());
+                add(label);
+            }
+        }
+    }
+
+    private class ColorIcon implements Icon {
+
+        private Color color;
+        private ColorIcon(Color color) {
+            this.color = color;
+        }
+
+        @Override
+        public int getIconHeight() {
+            return 12;
+        }
+
+        @Override
+        public int getIconWidth() {
+            return 12;
+        }
+
+        @Override
+        public void paintIcon(Component c, Graphics g, int x, int y) {
+            Graphics2D graphics = GraphicsUtils.getInstance().createAAGraphics(g);
+            graphics.setColor(color);
+            graphics.fillRect(x, y, getIconWidth(), getIconHeight());
+            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/internal/timeline/model/RangeChangeEvent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/RangeChangeListener.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/RangedTimelineProbe.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/RatioChangeEvent.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/RatioChangeListener.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/TimelineDateFormatter.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2012-2016 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.internal.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/internal/timeline/model/TimelineModel.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2012-2016 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.internal.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/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingLockViewTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,148 +0,0 @@
-/*
- * Copyright 2012-2016 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 static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import javax.swing.JFrame;
-import javax.swing.SwingUtilities;
-
-import org.fest.swing.annotation.GUITest;
-import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
-import org.fest.swing.edt.GuiActionRunner;
-import org.fest.swing.edt.GuiTask;
-import org.fest.swing.fixture.FrameFixture;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-
-import com.redhat.thermostat.annotations.internal.CacioTest;
-import com.redhat.thermostat.thread.model.LockInfo;
-
-import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
-
-@Category(CacioTest.class)
-@RunWith(CacioFESTRunner.class)
-public class SwingLockViewTest {
-
-    private JFrame frame;
-    private FrameFixture frameFixture;
-    private SwingLockView view;
-
-    @BeforeClass
-    public static void setupOnce() {
-        FailOnThreadViolationRepaintManager.install();
-    }
-
-    @Before
-    public void setup() {
-        GuiActionRunner.execute(new GuiTask() {
-
-            @Override
-            protected void executeInEDT() throws Throwable {
-                frame = new JFrame();
-                view = new SwingLockView();
-                frame.add(view.getUiComponent());
-            }
-        });
-        frameFixture = new FrameFixture(frame);
-    }
-
-    @After
-    public void tearDown() {
-        frameFixture.cleanUp();
-        frameFixture = null;
-    }
-
-    @GUITest
-    @Category(GUITest.class)
-    @Test
-    public void verifyInitialValues() {
-        frameFixture.show();
-
-        String[][] contents = frameFixture.table(SwingLockView.TABLE_NAME).contents();
-        for (int i = 0; i < 18; i++) {
-            String label = contents[i][0];
-            String value = contents[i][1];
-            assertFalse(label.contains("LocalizedString"));
-            assertEquals("0", value);
-        }
-    }
-
-    @GUITest
-    @Category(GUITest.class)
-    @Test
-    public void verifyTableIsUpdated() {
-        frameFixture.show();
-
-        LockInfo lockData = new LockInfo(System.currentTimeMillis(), "writer", "vm",
-                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
-
-        // incidentally, this also checks for correct handling of calls from non EDT
-        view.setLatestLockData(lockData);
-
-        String[][] contents = frameFixture.table(SwingLockView.TABLE_NAME).contents();
-        for (int i = 0; i < 18; i++) {
-            String value = contents[i][1];
-            assertEquals(String.valueOf(i + 1), value);
-        }
-    }
-
-    public static void main(String[] args) {
-        SwingUtilities.invokeLater(new Runnable() {
-            @Override
-            public void run() {
-                JFrame mainWindow = new JFrame("mainWindow");
-
-                SwingLockView view = new SwingLockView();
-                mainWindow.add(view.getUiComponent());
-
-                LockInfo lockData = new LockInfo(System.currentTimeMillis(), "writer", "vm",
-                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
-
-                view.setLatestLockData(lockData);
-                mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
-                mainWindow.pack();
-                mainWindow.setVisible(true);
-            }
-        });
-    }
-}
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import static org.mockito.Mockito.when;
-
-/**
- *
- */
-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);
-        when(defaults.getDefaultFont()).thenReturn(new JLabel().getFont());
-        when(defaults.getIconColor()).thenReturn(Palette.EARL_GRAY.getColor());
-        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.getColor());
-
-        return defaults;
-    }
-}
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo2.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/*
- * Copyright 2012-2016 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());
-        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.getColor());
-
-        return defaults;
-    }
-}
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewDemo3.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,182 +0,0 @@
-/*
- * Copyright 2012-2016 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());
-        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.getColor());
-
-        return defaults;
-    }
-}
--- a/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/impl/SwingThreadViewTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,213 +0,0 @@
-/*
- * Copyright 2012-2016 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.annotations.internal.CacioTest;
-import com.redhat.thermostat.client.swing.UIDefaults;
-import com.redhat.thermostat.shared.locale.Translate;
-import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
-
-import java.awt.Color;
-import java.awt.Component;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Locale;
-
-import javax.swing.JFrame;
-
-import com.redhat.thermostat.thread.client.common.view.ThreadView;
-import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
-
-import org.fest.swing.annotation.GUITest;
-import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
-import org.fest.swing.edt.GuiActionRunner;
-import org.fest.swing.edt.GuiTask;
-import org.fest.swing.fixture.FrameFixture;
-import org.fest.swing.fixture.JTabbedPaneFixture;
-import org.fest.swing.fixture.JToggleButtonFixture;
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.experimental.categories.Category;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-@Category(CacioTest.class)
-@RunWith(CacioFESTRunner.class)
-public class SwingThreadViewTest {
-
-    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
-    
-    private SwingThreadView view;
-    
-    private JFrame frame;
-    private FrameFixture frameFixture;
-    
-    private static Locale locale;
-
-    private static UIDefaults defaults;
-
-    @BeforeClass
-    public static void setUpOnce() {
-        defaults = mock(UIDefaults.class);
-        when(defaults.getReferenceFieldIconColor()).thenReturn(Color.BLACK);
-
-        FailOnThreadViolationRepaintManager.install();
-        locale = Locale.getDefault();
-        Locale.setDefault(Locale.US);
-    }
-    
-    @AfterClass
-    public static void tearDownOnce() {
-        Locale.setDefault(locale);
-    }
-    
-    @After
-    public void tearDown() {
-        frameFixture.cleanUp();
-        frameFixture = null;
-    }
-    
-    @Before
-    public void setUp() {
-        GuiActionRunner.execute(new GuiTask() {
-            @Override
-            protected void executeInEDT() throws Throwable {
-                view = new SwingThreadView(defaults);
-                frame = new JFrame();
-                frame.add(view.getUiComponent());
-            }
-        });
-        frameFixture = new FrameFixture(frame);
-    }
-
-    @Category(GUITest.class)
-    @GUITest
-    @Test
-    public void verifyThreadCountIsFirstTab() {
-        frameFixture.show();
-
-        JTabbedPaneFixture tabPane = frameFixture.tabbedPane("topTabbedPane");
-        Component comp = tabPane.component().getComponentAt(0);
-        assertEquals("count", comp.getName());
-        
-        comp = tabPane.component().getComponentAt(1);
-        assertEquals("timeline", comp.getName());
-    }
-    
-    @GUITest
-    @Test
-    public void verifyMonitorLabelChange() throws InvocationTargetException, InterruptedException {
-        frameFixture.show();
-
-        JToggleButtonFixture togglefixture = frameFixture.toggleButton("recordButton");
-        
-        togglefixture.requireToolTip(t.localize(LocaleResources.START_RECORDING).getContents());
-
-        togglefixture.click();
-
-        togglefixture.requireToolTip(t.localize(LocaleResources.STOP_RECORDING).getContents());
-        
-        // now try "programmatically"
-        
-        view.setRecording(ThreadView.MonitoringState.STARTED, true);
-        
-        togglefixture.requireToolTip(t.localize(LocaleResources.STOP_RECORDING).getContents());
-    
-        view.setRecording(ThreadView.MonitoringState.STOPPED, false);
-        
-        togglefixture.requireToolTip(t.localize(LocaleResources.START_RECORDING).getContents());
-    }
-
-    @Category(GUITest.class)
-    @GUITest
-    @Test
-    public void verifyToggleButtonIsDisabled() {
-        frameFixture.show();
-
-        view.setEnableRecordingControl(false);
-
-        JToggleButtonFixture togglefixture = frameFixture.toggleButton("recordButton");
-        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());
-//    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingLockViewTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.fest.swing.fixture.FrameFixture;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.annotations.internal.CacioTest;
+import com.redhat.thermostat.thread.model.LockInfo;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+@Category(CacioTest.class)
+@RunWith(CacioFESTRunner.class)
+public class SwingLockViewTest {
+
+    private JFrame frame;
+    private FrameFixture frameFixture;
+    private SwingLockView view;
+
+    @BeforeClass
+    public static void setupOnce() {
+        FailOnThreadViolationRepaintManager.install();
+    }
+
+    @Before
+    public void setup() {
+        GuiActionRunner.execute(new GuiTask() {
+
+            @Override
+            protected void executeInEDT() throws Throwable {
+                frame = new JFrame();
+                view = new SwingLockView();
+                frame.add(view.getUiComponent());
+            }
+        });
+        frameFixture = new FrameFixture(frame);
+    }
+
+    @After
+    public void tearDown() {
+        frameFixture.cleanUp();
+        frameFixture = null;
+    }
+
+    @GUITest
+    @Category(GUITest.class)
+    @Test
+    public void verifyInitialValues() {
+        frameFixture.show();
+
+        String[][] contents = frameFixture.table(SwingLockView.TABLE_NAME).contents();
+        for (int i = 0; i < 18; i++) {
+            String label = contents[i][0];
+            String value = contents[i][1];
+            assertFalse(label.contains("LocalizedString"));
+            assertEquals("0", value);
+        }
+    }
+
+    @GUITest
+    @Category(GUITest.class)
+    @Test
+    public void verifyTableIsUpdated() {
+        frameFixture.show();
+
+        LockInfo lockData = new LockInfo(System.currentTimeMillis(), "writer", "vm",
+                1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
+
+        // incidentally, this also checks for correct handling of calls from non EDT
+        view.setLatestLockData(lockData);
+
+        String[][] contents = frameFixture.table(SwingLockView.TABLE_NAME).contents();
+        for (int i = 0; i < 18; i++) {
+            String value = contents[i][1];
+            assertEquals(String.valueOf(i + 1), value);
+        }
+    }
+
+    public static void main(String[] args) {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                JFrame mainWindow = new JFrame("mainWindow");
+
+                SwingLockView view = new SwingLockView();
+                mainWindow.add(view.getUiComponent());
+
+                LockInfo lockData = new LockInfo(System.currentTimeMillis(), "writer", "vm",
+                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
+
+                view.setLatestLockData(lockData);
+                mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                mainWindow.pack();
+                mainWindow.setVisible(true);
+            }
+        });
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/client-swing/src/test/java/com/redhat/thermostat/thread/client/swing/internal/SwingThreadViewDemo.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.mockito.Mockito.when;
+
+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;
+
+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;
+
+/**
+ *
+ */
+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);
+        when(defaults.getDefaultFont()).thenReturn(new JLabel().getFont());
+        when(defaults.getIconColor()).thenReturn(Palette.EARL_GRAY.getColor());
+        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.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/internal/SwingThreadViewDemo2.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+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 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;
+
+/**
+ * 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());
+        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.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/internal/SwingThreadViewDemo3.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+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 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;
+
+/**
+ * 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());
+        when(defaults.getSelectedComponentBGColor()).thenReturn(Palette.ADWAITA_BLU.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/internal/SwingThreadViewTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+import javax.swing.JFrame;
+
+import org.fest.swing.annotation.GUITest;
+import org.fest.swing.edt.FailOnThreadViolationRepaintManager;
+import org.fest.swing.edt.GuiActionRunner;
+import org.fest.swing.edt.GuiTask;
+import org.fest.swing.fixture.FrameFixture;
+import org.fest.swing.fixture.JTabbedPaneFixture;
+import org.fest.swing.fixture.JToggleButtonFixture;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import com.redhat.thermostat.annotations.internal.CacioTest;
+import com.redhat.thermostat.client.swing.UIDefaults;
+import com.redhat.thermostat.shared.locale.Translate;
+import com.redhat.thermostat.thread.client.common.locale.LocaleResources;
+import com.redhat.thermostat.thread.client.common.view.ThreadView;
+
+import net.java.openjdk.cacio.ctc.junit.CacioFESTRunner;
+
+@Category(CacioTest.class)
+@RunWith(CacioFESTRunner.class)
+public class SwingThreadViewTest {
+
+    private static final Translate<LocaleResources> t = LocaleResources.createLocalizer();
+    
+    private SwingThreadView view;
+    
+    private JFrame frame;
+    private FrameFixture frameFixture;
+    
+    private static Locale locale;
+
+    private static UIDefaults defaults;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        defaults = mock(UIDefaults.class);
+        when(defaults.getReferenceFieldIconColor()).thenReturn(Color.BLACK);
+
+        FailOnThreadViolationRepaintManager.install();
+        locale = Locale.getDefault();
+        Locale.setDefault(Locale.US);
+    }
+    
+    @AfterClass
+    public static void tearDownOnce() {
+        Locale.setDefault(locale);
+    }
+    
+    @After
+    public void tearDown() {
+        frameFixture.cleanUp();
+        frameFixture = null;
+    }
+    
+    @Before
+    public void setUp() {
+        GuiActionRunner.execute(new GuiTask() {
+            @Override
+            protected void executeInEDT() throws Throwable {
+                view = new SwingThreadView(defaults);
+                frame = new JFrame();
+                frame.add(view.getUiComponent());
+            }
+        });
+        frameFixture = new FrameFixture(frame);
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void verifyThreadCountIsFirstTab() {
+        frameFixture.show();
+
+        JTabbedPaneFixture tabPane = frameFixture.tabbedPane("topTabbedPane");
+        Component comp = tabPane.component().getComponentAt(0);
+        assertEquals("count", comp.getName());
+        
+        comp = tabPane.component().getComponentAt(1);
+        assertEquals("timeline", comp.getName());
+    }
+    
+    @GUITest
+    @Test
+    public void verifyMonitorLabelChange() throws InvocationTargetException, InterruptedException {
+        frameFixture.show();
+
+        JToggleButtonFixture togglefixture = frameFixture.toggleButton("recordButton");
+        
+        togglefixture.requireToolTip(t.localize(LocaleResources.START_RECORDING).getContents());
+
+        togglefixture.click();
+
+        togglefixture.requireToolTip(t.localize(LocaleResources.STOP_RECORDING).getContents());
+        
+        // now try "programmatically"
+        
+        view.setRecording(ThreadView.MonitoringState.STARTED, true);
+        
+        togglefixture.requireToolTip(t.localize(LocaleResources.STOP_RECORDING).getContents());
+    
+        view.setRecording(ThreadView.MonitoringState.STOPPED, false);
+        
+        togglefixture.requireToolTip(t.localize(LocaleResources.START_RECORDING).getContents());
+    }
+
+    @Category(GUITest.class)
+    @GUITest
+    @Test
+    public void verifyToggleButtonIsDisabled() {
+        frameFixture.show();
+
+        view.setEnableRecordingControl(false);
+
+        JToggleButtonFixture togglefixture = frameFixture.toggleButton("recordButton");
+        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());
+//    }
+}
+
--- a/thread/collector/pom.xml	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/pom.xml	Fri Apr 08 13:18:48 2016 +0200
@@ -120,8 +120,8 @@
             </Export-Package>
             <Private-Package>
               com.redhat.thermostat.thread.common.osgi,
-              com.redhat.thermostat.thread.dao.impl,
-              com.redhat.thermostat.thread.dao.impl.statement,
+              com.redhat.thermostat.thread.dao.internal,
+              com.redhat.thermostat.thread.dao.internal.statement,
             </Private-Package>
             <!-- Do not autogenerate uses clauses in Manifests -->
             <_nouses>true</_nouses>
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/common/osgi/Activator.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/common/osgi/Activator.java	Fri Apr 08 13:18:48 2016 +0200
@@ -48,8 +48,8 @@
 import com.redhat.thermostat.storage.core.Storage;
 import com.redhat.thermostat.thread.dao.LockInfoDao;
 import com.redhat.thermostat.thread.dao.ThreadDao;
-import com.redhat.thermostat.thread.dao.impl.LockInfoDaoImpl;
-import com.redhat.thermostat.thread.dao.impl.ThreadDaoImpl;
+import com.redhat.thermostat.thread.dao.internal.LockInfoDaoImpl;
+import com.redhat.thermostat.thread.dao.internal.ThreadDaoImpl;
 
 public class Activator implements BundleActivator {
 
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/ThreadDao.java	Fri Apr 08 13:18:48 2016 +0200
@@ -41,7 +41,7 @@
 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.dao.internal.ThreadDaoKeys;
 import com.redhat.thermostat.thread.model.SessionID;
 import com.redhat.thermostat.thread.model.ThreadContentionSample;
 import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoCategoryRegistration.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-
-public class LockInfoDaoCategoryRegistration implements CategoryRegistration {
-
-    @Override
-    public Set<String> getCategoryNames() {
-        Set<String> set = new HashSet<>();
-        set.add(LockInfoDao.LOCK_INFO_CATEGORY.getName());
-        return set;
-    }
-
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImpl.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import java.util.List;
-import java.util.logging.Logger;
-
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.PreparedStatement;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
-import com.redhat.thermostat.storage.dao.AbstractDao;
-import com.redhat.thermostat.storage.dao.AbstractDaoStatement;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-import com.redhat.thermostat.thread.model.LockInfo;
-
-public class LockInfoDaoImpl extends AbstractDao implements LockInfoDao {
-
-    private static final Logger logger = LoggingUtils.getLogger(LockInfoDaoImpl.class);
-
-    static final String ADD_LOCK_INFO = "ADD " + LOCK_INFO_CATEGORY.getName() +
-            " SET '" + Key.AGENT_ID.getName() + "' = ?s , " +
-                 "'" + Key.VM_ID.getName() + "' = ?s , " +
-                 "'" + Key.TIMESTAMP.getName() + "' = ?l , " +
-                 "'" + CONTENDED_LOCK_ATTEMPTS_KEY.getName() + "' = ?l , " +
-                 "'" + DEFLATIONS_KEY.getName() + "' = ?l , " +
-                 "'" + EMPTY_NOTIFICATIONS_KEY.getName() + "' = ?l , " +
-                 "'" + FAILED_SPINS_KEY.getName() + "' = ?l , " +
-                 "'" + FUTILE_WAKEUPS_KEY.getName() + "' = ?l , " +
-                 "'" + INFLATIONS_KEY.getName() + "' = ?l , " +
-                 "'" + MONITORS_EXTANT_KEY.getName() + "' = ?l , " +
-                 "'" + MONITORS_IN_CIRCULATION_KEY.getName() + "' = ?l , " +
-                 "'" + MONITORS_SCAVENGED_KEY.getName() + "' = ?l , " +
-                 "'" + NOTIFICATIONS_KEY.getName() + "' = ?l , " +
-                 "'" + PARKS_KEY.getName() + "' = ?l , " +
-                 "'" + PRIVATE_A_KEY.getName() + "' = ?l , " +
-                 "'" + PRIVATE_B_KEY.getName() + "' = ?l , " +
-                 "'" + SLOW_ENTER_KEY.getName() + "' = ?l , " +
-                 "'" + SLOW_EXIT_KEY.getName() + "' = ?l , " +
-                 "'" + SLOW_NOTIFY_KEY.getName() + "' = ?l , " +
-                 "'" + SLOW_NOTIFY_ALL_KEY.getName() + "' = ?l , " +
-                 "'" + SUCCESSFUL_SPINS_KEY.getName() + "' = ?l";
-
-    private final Storage storage;
-
-    private final VmBoundaryPojoGetter<LockInfo> boundaryGetter;
-
-    private VmTimeIntervalPojoListGetter<LockInfo> intervalGetter;
-
-    public LockInfoDaoImpl(Storage storage) {
-        this.storage = storage;
-        storage.registerCategory(LOCK_INFO_CATEGORY);
-
-        boundaryGetter = new VmBoundaryPojoGetter<>(storage, LOCK_INFO_CATEGORY);
-        intervalGetter = new VmTimeIntervalPojoListGetter<>(storage, LOCK_INFO_CATEGORY);
-    }
-
-    @Override
-    public void saveLockInfo(final LockInfo info) {
-        executeStatement(new AbstractDaoStatement<LockInfo>(storage, LOCK_INFO_CATEGORY, ADD_LOCK_INFO) {
-            @Override
-            public PreparedStatement<LockInfo> customize(PreparedStatement<LockInfo> preparedStatement) {
-                preparedStatement.setString(0, info.getAgentId());
-                preparedStatement.setString(1, info.getVmId());
-                preparedStatement.setLong(2, info.getTimeStamp());
-                preparedStatement.setLong(3, info.getContendedLockAttempts());
-                preparedStatement.setLong(4, info.getDeflations());
-                preparedStatement.setLong(5, info.getEmptyNotifications());
-                preparedStatement.setLong(6, info.getFailedSpins());
-                preparedStatement.setLong(7, info.getFutileWakeups());
-                preparedStatement.setLong(8, info.getInflations());
-                preparedStatement.setLong(9, info.getMonExtant());
-                preparedStatement.setLong(10, info.getMonInCirculation());
-                preparedStatement.setLong(11, info.getMonScavenged());
-                preparedStatement.setLong(12, info.getNotifications());
-                preparedStatement.setLong(13, info.getParks());
-                preparedStatement.setLong(14, info.getPrivateA());
-                preparedStatement.setLong(15, info.getPrivateB());
-                preparedStatement.setLong(16, info.getSlowEnter());
-                preparedStatement.setLong(17, info.getSlowExit());
-                preparedStatement.setLong(18, info.getSlowNotify());
-                preparedStatement.setLong(19, info.getSlowNotifyAll());
-                preparedStatement.setLong(20, info.getSuccessfulSpins());
-
-                return preparedStatement;
-            }
-        });
-    }
-
-    @Override
-    public List<LockInfo> getLockInfo(VmRef ref, Range<Long> range) {
-        return intervalGetter.getLatest(ref, range.getMin(), range.getMax());
-    }
-
-    @Override
-    public LockInfo getLatestLockInfo(VmRef ref) {
-        return boundaryGetter.getNewestStat(ref);
-    }
-
-    @Override
-    protected Logger getLogger() {
-        return logger;
-    }
-
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImplStatementDescriptorRegistration.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,70 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
-import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
-import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
-import com.redhat.thermostat.thread.dao.LockInfoDao;
-
-public class LockInfoDaoImplStatementDescriptorRegistration
-    implements StatementDescriptorRegistration {
-
-    @Override
-    public Set<String> getStatementDescriptors() {
-        Set<String> set = new HashSet<>();
-
-        set.add(LockInfoDaoImpl.ADD_LOCK_INFO);
-
-        String latestStatDescriptor = String.format(
-                VmBoundaryPojoGetter.DESC_NEWEST_VM_STAT,
-                LockInfoDao.LOCK_INFO_CATEGORY.getName());
-
-        set.add(latestStatDescriptor);
-
-        String rangeStatDescriptor = String.format(
-                VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT,
-                LockInfoDao.LOCK_INFO_CATEGORY.getName());
-        set.add(rangeStatDescriptor);
-
-        return set;
-    }
-
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistration.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-/**
- * Registers the category used by this maven module. The web storage
- * endpoint only allows categories to be registered which it knows of
- * ahead of time.
- *
- */
-public class ThreadDAOCategoryRegistration implements CategoryRegistration {
-
-    @Override
-    public Set<String> getCategoryNames() {
-        Set<String> categories = new HashSet<>();
-
-        categories.add(ThreadDao.DEADLOCK_INFO.getName());
-        categories.add(ThreadDao.THREAD_HARVESTING_STATUS.getName());
-        categories.add(ThreadDao.THREAD_CONTENTION_SAMPLE.getName());
-
-        ThreadDaoCategories.register(categories);
-
-        return categories;
-    }
-}
-
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoCategories.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-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.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;
-import java.util.List;
-import java.util.logging.Logger;
-
-/**
- *
- */
-public class ThreadDaoCategories {
-
-    private static final Logger logger = LoggingUtils.getLogger(ThreadDaoCategories.class);
-
-    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) {
-        for (Class<? extends Pojo> beanClass: BEANS) {
-            Category<? extends Pojo> category = new CategoryBuilder(beanClass).build();
-            collection.add(category.getName());
-        }
-    }
-
-    public static void register(Storage storage) {
-        for (Class<? extends Pojo> beanClass: BEANS) {
-            Category<? extends Pojo> category = new CategoryBuilder(beanClass).build();
-            storage.registerCategory(category);
-        }
-    }
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImpl.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,355 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import com.redhat.thermostat.common.model.Range;
-import com.redhat.thermostat.common.utils.LoggingUtils;
-import com.redhat.thermostat.storage.core.Category;
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.DescriptorParsingException;
-import com.redhat.thermostat.storage.core.Key;
-import com.redhat.thermostat.storage.core.PreparedStatement;
-import com.redhat.thermostat.storage.core.StatementDescriptor;
-import com.redhat.thermostat.storage.core.StatementExecutionException;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.VmRef;
-import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
-import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
-import com.redhat.thermostat.storage.core.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.SessionQueries;
-import com.redhat.thermostat.thread.dao.impl.statement.StateQueries;
-import com.redhat.thermostat.thread.dao.impl.statement.SummaryQuery;
-import com.redhat.thermostat.thread.model.SessionID;
-import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
-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 java.util.ArrayList;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import static com.redhat.thermostat.common.utils.IteratorUtils.head;
-
-public class ThreadDaoImpl implements ThreadDao {
-    
-    private static final Logger logger = LoggingUtils.getLogger(ThreadDaoImpl.class);
-
-    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 '"
-            + Key.AGENT_ID.getName() + "' = ?s AND '" 
-            + Key.VM_ID.getName() + "' = ?s SORT '" 
-            + Key.TIMESTAMP.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 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";
-
-    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_CONTENTION_SAMPLE =
-            "ADD "  + THREAD_CONTENTION_SAMPLE.getName() + " "               +
-                    "SET '" + Key.AGENT_ID.getName() + "' = ?s , "       +
-                    "'" + Key.VM_ID.getName() + "' = ?s , "          +
-                    "'" + THREAD_CONTENTION_BLOCKED_COUNT_KEY.getName() + "' = ?l , " +
-                    "'" + THREAD_CONTENTION_BLOCKED_TIME_KEY.getName() + "' = ?l , "  +
-                    "'" + THREAD_CONTENTION_WAITED_COUNT_KEY.getName() + "' = ?l , "  +
-                    "'" + THREAD_CONTENTION_WAITED_TIME_KEY.getName() + "' = ?l , "  +
-                    "'" + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s , " +
-                    "'" + Key.TIMESTAMP.getName() + "' = ?l";
-
-    static final String GET_LATEST_CONTENTION_SAMPLE= "QUERY "
-            + THREAD_CONTENTION_SAMPLE.getName() + " WHERE '"
-            + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
-            + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
-
-    private Storage storage;
-    
-    public ThreadDaoImpl(Storage storage) {
-        this.storage = storage;
-
-        ThreadDaoCategories.register(storage);
-
-        storage.registerCategory(THREAD_HARVESTING_STATUS);
-        storage.registerCategory(THREAD_CONTENTION_SAMPLE);
-
-        storage.registerCategory(DEADLOCK_INFO);
-    }
-
-    @Override
-    public void saveSummary(ThreadSummary summary) {
-        try {
-            ThreadSummaryAdapter.insert(summary, storage);
-
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Exception saving summary: " + summary, e);
-        }
-    }
-
-    @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, Range<Long> range, int limit) {
-        final List<ThreadSummary> results = new ArrayList<>();
-
-        Query<ThreadSummary> query = ThreadSummaryAdapter.getQuery(SummaryQuery.id);
-
-        QueryValues values = query.createValues();
-        values.set(SummaryQuery.CriteriaId.vmId, ref.getVmId());
-        values.set(SummaryQuery.CriteriaId.agentId, ref.getHostRef().getAgentId());
-
-        values.set(SummaryQuery.CriteriaId.timeStampGEQ, range.getMin());
-        values.set(SummaryQuery.CriteriaId.timeStampLEQ, range.getMax());
-        values.set(SummaryQuery.CriteriaId.limit, limit);
-
-        try {
-            ThreadSummaryAdapter.query(values, new ResultHandler<ThreadSummary>() {
-                @Override
-                public boolean onResult(ThreadSummary result) {
-                    results.add(result);
-                    return true;
-                }
-            }, storage);
-
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Exception retrieving thread summary", e);
-        }
-
-        return results;
-    }
-
-    @Override
-    public List<ThreadSession> getSessions(VmRef ref, Range<Long> range,
-                                           int limit, Sort order)
-    {
-        final List<ThreadSession> results = new ArrayList<>();
-
-        Id id = order.equals(Sort.ASCENDING) ? SessionQueries.getRangeAscending :
-                                               SessionQueries.getRangeDescending;
-
-        Query<ThreadSession> query = ThreadSessionAdapter.getQuery(id);
-
-        QueryValues values = query.createValues();
-        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 {
-            ThreadSessionAdapter.query(values, new ResultHandler<ThreadSession>() {
-                @Override
-                public boolean onResult(ThreadSession result) {
-                    results.add(result);
-                    return true;
-                }
-            }, storage);
-
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Exception retrieving thread session", e);
-        }
-
-        return results;
-    }
-
-    public void saveSession(ThreadSession session) {
-        try {
-            ThreadSessionAdapter.insert(session, storage);
-
-        } catch (StatementExecutionException e) {
-            logger.log(Level.SEVERE, "Exception saving session: " + session, e);
-        }
-    }
-
-    @Override
-    public void saveHarvestingStatus(ThreadHarvestingStatus status) {
-        StatementDescriptor<ThreadHarvestingStatus> desc = new StatementDescriptor<>(THREAD_HARVESTING_STATUS, DESC_ADD_THREAD_HARVESTING_STATUS);
-        PreparedStatement<ThreadHarvestingStatus> prepared;
-        try {
-            prepared = storage.prepareStatement(desc);
-            prepared.setString(0, status.getAgentId());
-            prepared.setString(1, status.getVmId());
-            prepared.setLong(2, status.getTimeStamp());
-            prepared.setBoolean(3, status.isHarvesting());
-            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 ThreadHarvestingStatus getLatestHarvestingStatus(VmRef vm) {
-        PreparedStatement<ThreadHarvestingStatus> stmt = prepareQuery(THREAD_HARVESTING_STATUS, 
-                QUERY_LATEST_HARVESTING_STATUS, vm);
-        if (stmt == null) {
-            return null;
-        }
-        
-        return getFirstResult(stmt);
-    }
-
-    @Override
-    public VmDeadLockData loadLatestDeadLockStatus(VmRef ref) {
-        PreparedStatement<VmDeadLockData> stmt = prepareQuery(DEADLOCK_INFO, QUERY_LATEST_DEADLOCK_INFO, ref);
-        if (stmt == null) {
-            return null;
-        }
-        
-        return getFirstResult(stmt);
-    }
-
-    @Override
-    public void saveDeadLockStatus(VmDeadLockData deadLockInfo) {
-        StatementDescriptor<VmDeadLockData> desc = new StatementDescriptor<>(DEADLOCK_INFO, DESC_ADD_THREAD_DEADLOCK_DATA);
-        PreparedStatement<VmDeadLockData> prepared;
-        try {
-            prepared = storage.prepareStatement(desc);
-            prepared.setString(0, deadLockInfo.getAgentId());
-            prepared.setString(1, deadLockInfo.getVmId());
-            prepared.setLong(2, deadLockInfo.getTimeStamp());
-            prepared.setString(3, deadLockInfo.getDeadLockDescription());
-            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);
-        }
-    }
-
-
-    /**************************************************************************/
-
-    private <T extends Pojo> PreparedStatement<T> prepareQuery(Category<T> category, String query, VmRef ref) {
-        return prepareQuery(category, query, ref, null, null);
-    }
-    
-    private <T extends Pojo> PreparedStatement<T> prepareQuery(Category<T> category, String query, VmRef ref, Long since, Long to) {
-        StatementDescriptor<T> desc = new StatementDescriptor<>(category, query);
-        PreparedStatement<T> stmt = null;
-        try {
-            stmt = storage.prepareStatement(desc);
-            stmt.setString(0, ref.getHostRef().getAgentId());
-            stmt.setString(1, ref.getVmId());
-            // assume: the format of the query is such that 2nd and 3rd arguments (if any) are longs
-            if (since != null) {
-                stmt.setLong(2, since);
-            }
-            if (to != null) {
-                stmt.setLong(3, to);
-            }
-        } catch (DescriptorParsingException e) {
-            // should not happen, but if it *does* happen, at least log it
-            logger.log(Level.SEVERE, "Preparing query '" + desc + "' failed!", e);
-        }
-        return stmt;
-    }
-
-    private <T extends Pojo> T getFirstResult(PreparedStatement<T> stmt) {
-        Cursor<T> cursor;
-        try {
-            cursor = stmt.executeQuery();
-        } catch (StatementExecutionException e) {
-            // should not happen, but if it *does* happen, at least log it
-            logger.log(Level.SEVERE, "Executing query '" + stmt + "' failed!", e);
-            return null;
-        }
-
-        return head(cursor);
-    }
-}
-
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistration.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
-
-/**
- * Registers prepared queries issued by this maven module via
- * via {@link ThreadDaoImpl}.
- *
- */
-public class ThreadDaoImplStatementDescriptorRegistration implements
-        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.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_CONTENTION_SAMPLE);
-        descs.add(ThreadDaoImpl.GET_LATEST_CONTENTION_SAMPLE);
-    }
-    
-    @Override
-    public Set<String> getStatementDescriptors() {
-        return descs;
-    }
-
-}
-
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoKeys.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import com.redhat.thermostat.storage.core.Key;
-
-/**
- *
- */
-public class ThreadDaoKeys {
-
-    public static final Key<String> THREAD_HEADER_UUID = new Key<String>("referenceID");
-}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueries.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * Copyright 2012-2016 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 java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import com.redhat.thermostat.storage.core.Id;
-import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
-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;
-
-/**
- *
- */
-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/StateQueries.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,131 +0,0 @@
-/*
- * Copyright 2012-2016 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.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	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * Copyright 2012-2016 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.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 com.redhat.thermostat.thread.model.ThreadSummary;
-import java.util.List;
-import java.util.Map;
-
-/**
- *
- */
-public class SummaryQuery extends Query<ThreadSummary> {
-
-    public static final Id id = new Id(SummaryQuery.class.getSimpleName());
-
-    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");
-    }
-
-    @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.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));
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoCategoryRegistration.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+
+public class LockInfoDaoCategoryRegistration implements CategoryRegistration {
+
+    @Override
+    public Set<String> getCategoryNames() {
+        Set<String> set = new HashSet<>();
+        set.add(LockInfoDao.LOCK_INFO_CATEGORY.getName());
+        return set;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImpl.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.util.List;
+import java.util.logging.Logger;
+
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
+import com.redhat.thermostat.storage.dao.AbstractDao;
+import com.redhat.thermostat.storage.dao.AbstractDaoStatement;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+import com.redhat.thermostat.thread.model.LockInfo;
+
+public class LockInfoDaoImpl extends AbstractDao implements LockInfoDao {
+
+    private static final Logger logger = LoggingUtils.getLogger(LockInfoDaoImpl.class);
+
+    static final String ADD_LOCK_INFO = "ADD " + LOCK_INFO_CATEGORY.getName() +
+            " SET '" + Key.AGENT_ID.getName() + "' = ?s , " +
+                 "'" + Key.VM_ID.getName() + "' = ?s , " +
+                 "'" + Key.TIMESTAMP.getName() + "' = ?l , " +
+                 "'" + CONTENDED_LOCK_ATTEMPTS_KEY.getName() + "' = ?l , " +
+                 "'" + DEFLATIONS_KEY.getName() + "' = ?l , " +
+                 "'" + EMPTY_NOTIFICATIONS_KEY.getName() + "' = ?l , " +
+                 "'" + FAILED_SPINS_KEY.getName() + "' = ?l , " +
+                 "'" + FUTILE_WAKEUPS_KEY.getName() + "' = ?l , " +
+                 "'" + INFLATIONS_KEY.getName() + "' = ?l , " +
+                 "'" + MONITORS_EXTANT_KEY.getName() + "' = ?l , " +
+                 "'" + MONITORS_IN_CIRCULATION_KEY.getName() + "' = ?l , " +
+                 "'" + MONITORS_SCAVENGED_KEY.getName() + "' = ?l , " +
+                 "'" + NOTIFICATIONS_KEY.getName() + "' = ?l , " +
+                 "'" + PARKS_KEY.getName() + "' = ?l , " +
+                 "'" + PRIVATE_A_KEY.getName() + "' = ?l , " +
+                 "'" + PRIVATE_B_KEY.getName() + "' = ?l , " +
+                 "'" + SLOW_ENTER_KEY.getName() + "' = ?l , " +
+                 "'" + SLOW_EXIT_KEY.getName() + "' = ?l , " +
+                 "'" + SLOW_NOTIFY_KEY.getName() + "' = ?l , " +
+                 "'" + SLOW_NOTIFY_ALL_KEY.getName() + "' = ?l , " +
+                 "'" + SUCCESSFUL_SPINS_KEY.getName() + "' = ?l";
+
+    private final Storage storage;
+
+    private final VmBoundaryPojoGetter<LockInfo> boundaryGetter;
+
+    private VmTimeIntervalPojoListGetter<LockInfo> intervalGetter;
+
+    public LockInfoDaoImpl(Storage storage) {
+        this.storage = storage;
+        storage.registerCategory(LOCK_INFO_CATEGORY);
+
+        boundaryGetter = new VmBoundaryPojoGetter<>(storage, LOCK_INFO_CATEGORY);
+        intervalGetter = new VmTimeIntervalPojoListGetter<>(storage, LOCK_INFO_CATEGORY);
+    }
+
+    @Override
+    public void saveLockInfo(final LockInfo info) {
+        executeStatement(new AbstractDaoStatement<LockInfo>(storage, LOCK_INFO_CATEGORY, ADD_LOCK_INFO) {
+            @Override
+            public PreparedStatement<LockInfo> customize(PreparedStatement<LockInfo> preparedStatement) {
+                preparedStatement.setString(0, info.getAgentId());
+                preparedStatement.setString(1, info.getVmId());
+                preparedStatement.setLong(2, info.getTimeStamp());
+                preparedStatement.setLong(3, info.getContendedLockAttempts());
+                preparedStatement.setLong(4, info.getDeflations());
+                preparedStatement.setLong(5, info.getEmptyNotifications());
+                preparedStatement.setLong(6, info.getFailedSpins());
+                preparedStatement.setLong(7, info.getFutileWakeups());
+                preparedStatement.setLong(8, info.getInflations());
+                preparedStatement.setLong(9, info.getMonExtant());
+                preparedStatement.setLong(10, info.getMonInCirculation());
+                preparedStatement.setLong(11, info.getMonScavenged());
+                preparedStatement.setLong(12, info.getNotifications());
+                preparedStatement.setLong(13, info.getParks());
+                preparedStatement.setLong(14, info.getPrivateA());
+                preparedStatement.setLong(15, info.getPrivateB());
+                preparedStatement.setLong(16, info.getSlowEnter());
+                preparedStatement.setLong(17, info.getSlowExit());
+                preparedStatement.setLong(18, info.getSlowNotify());
+                preparedStatement.setLong(19, info.getSlowNotifyAll());
+                preparedStatement.setLong(20, info.getSuccessfulSpins());
+
+                return preparedStatement;
+            }
+        });
+    }
+
+    @Override
+    public List<LockInfo> getLockInfo(VmRef ref, Range<Long> range) {
+        return intervalGetter.getLatest(ref, range.getMin(), range.getMax());
+    }
+
+    @Override
+    public LockInfo getLatestLockInfo(VmRef ref) {
+        return boundaryGetter.getNewestStat(ref);
+    }
+
+    @Override
+    protected Logger getLogger() {
+        return logger;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImplStatementDescriptorRegistration.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.VmBoundaryPojoGetter;
+import com.redhat.thermostat.storage.core.VmTimeIntervalPojoListGetter;
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.thread.dao.LockInfoDao;
+
+public class LockInfoDaoImplStatementDescriptorRegistration
+    implements StatementDescriptorRegistration {
+
+    @Override
+    public Set<String> getStatementDescriptors() {
+        Set<String> set = new HashSet<>();
+
+        set.add(LockInfoDaoImpl.ADD_LOCK_INFO);
+
+        String latestStatDescriptor = String.format(
+                VmBoundaryPojoGetter.DESC_NEWEST_VM_STAT,
+                LockInfoDao.LOCK_INFO_CATEGORY.getName());
+
+        set.add(latestStatDescriptor);
+
+        String rangeStatDescriptor = String.format(
+                VmTimeIntervalPojoListGetter.VM_INTERVAL_QUERY_FORMAT,
+                LockInfoDao.LOCK_INFO_CATEGORY.getName());
+        set.add(rangeStatDescriptor);
+
+        return set;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDAOCategoryRegistration.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+/**
+ * Registers the category used by this maven module. The web storage
+ * endpoint only allows categories to be registered which it knows of
+ * ahead of time.
+ *
+ */
+public class ThreadDAOCategoryRegistration implements CategoryRegistration {
+
+    @Override
+    public Set<String> getCategoryNames() {
+        Set<String> categories = new HashSet<>();
+
+        categories.add(ThreadDao.DEADLOCK_INFO.getName());
+        categories.add(ThreadDao.THREAD_HARVESTING_STATUS.getName());
+        categories.add(ThreadDao.THREAD_CONTENTION_SAMPLE.getName());
+
+        ThreadDaoCategories.register(categories);
+
+        return categories;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoCategories.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+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.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;
+import java.util.List;
+import java.util.logging.Logger;
+
+/**
+ *
+ */
+public class ThreadDaoCategories {
+
+    private static final Logger logger = LoggingUtils.getLogger(ThreadDaoCategories.class);
+
+    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) {
+        for (Class<? extends Pojo> beanClass: BEANS) {
+            Category<? extends Pojo> category = new CategoryBuilder(beanClass).build();
+            collection.add(category.getName());
+        }
+    }
+
+    public static void register(Storage storage) {
+        for (Class<? extends Pojo> beanClass: BEANS) {
+            Category<? extends Pojo> category = new CategoryBuilder(beanClass).build();
+            storage.registerCategory(category);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImpl.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.common.model.Range;
+import com.redhat.thermostat.common.utils.LoggingUtils;
+import com.redhat.thermostat.storage.core.Category;
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.Key;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmRef;
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
+import com.redhat.thermostat.storage.core.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.internal.statement.SessionQueries;
+import com.redhat.thermostat.thread.dao.internal.statement.StateQueries;
+import com.redhat.thermostat.thread.dao.internal.statement.SummaryQuery;
+import com.redhat.thermostat.thread.model.SessionID;
+import com.redhat.thermostat.thread.model.ThreadHarvestingStatus;
+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 java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import static com.redhat.thermostat.common.utils.IteratorUtils.head;
+
+public class ThreadDaoImpl implements ThreadDao {
+    
+    private static final Logger logger = LoggingUtils.getLogger(ThreadDaoImpl.class);
+
+    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 '"
+            + Key.AGENT_ID.getName() + "' = ?s AND '" 
+            + Key.VM_ID.getName() + "' = ?s SORT '" 
+            + Key.TIMESTAMP.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 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";
+
+    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_CONTENTION_SAMPLE =
+            "ADD "  + THREAD_CONTENTION_SAMPLE.getName() + " "               +
+                    "SET '" + Key.AGENT_ID.getName() + "' = ?s , "       +
+                    "'" + Key.VM_ID.getName() + "' = ?s , "          +
+                    "'" + THREAD_CONTENTION_BLOCKED_COUNT_KEY.getName() + "' = ?l , " +
+                    "'" + THREAD_CONTENTION_BLOCKED_TIME_KEY.getName() + "' = ?l , "  +
+                    "'" + THREAD_CONTENTION_WAITED_COUNT_KEY.getName() + "' = ?l , "  +
+                    "'" + THREAD_CONTENTION_WAITED_TIME_KEY.getName() + "' = ?l , "  +
+                    "'" + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s , " +
+                    "'" + Key.TIMESTAMP.getName() + "' = ?l";
+
+    static final String GET_LATEST_CONTENTION_SAMPLE= "QUERY "
+            + THREAD_CONTENTION_SAMPLE.getName() + " WHERE '"
+            + ThreadDaoKeys.THREAD_HEADER_UUID.getName() + "' = ?s SORT '"
+            + Key.TIMESTAMP.getName() + "' DSC LIMIT 1";
+
+    private Storage storage;
+    
+    public ThreadDaoImpl(Storage storage) {
+        this.storage = storage;
+
+        ThreadDaoCategories.register(storage);
+
+        storage.registerCategory(THREAD_HARVESTING_STATUS);
+        storage.registerCategory(THREAD_CONTENTION_SAMPLE);
+
+        storage.registerCategory(DEADLOCK_INFO);
+    }
+
+    @Override
+    public void saveSummary(ThreadSummary summary) {
+        try {
+            ThreadSummaryAdapter.insert(summary, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception saving summary: " + summary, e);
+        }
+    }
+
+    @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, Range<Long> range, int limit) {
+        final List<ThreadSummary> results = new ArrayList<>();
+
+        Query<ThreadSummary> query = ThreadSummaryAdapter.getQuery(SummaryQuery.id);
+
+        QueryValues values = query.createValues();
+        values.set(SummaryQuery.CriteriaId.vmId, ref.getVmId());
+        values.set(SummaryQuery.CriteriaId.agentId, ref.getHostRef().getAgentId());
+
+        values.set(SummaryQuery.CriteriaId.timeStampGEQ, range.getMin());
+        values.set(SummaryQuery.CriteriaId.timeStampLEQ, range.getMax());
+        values.set(SummaryQuery.CriteriaId.limit, limit);
+
+        try {
+            ThreadSummaryAdapter.query(values, new ResultHandler<ThreadSummary>() {
+                @Override
+                public boolean onResult(ThreadSummary result) {
+                    results.add(result);
+                    return true;
+                }
+            }, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception retrieving thread summary", e);
+        }
+
+        return results;
+    }
+
+    @Override
+    public List<ThreadSession> getSessions(VmRef ref, Range<Long> range,
+                                           int limit, Sort order)
+    {
+        final List<ThreadSession> results = new ArrayList<>();
+
+        Id id = order.equals(Sort.ASCENDING) ? SessionQueries.getRangeAscending :
+                                               SessionQueries.getRangeDescending;
+
+        Query<ThreadSession> query = ThreadSessionAdapter.getQuery(id);
+
+        QueryValues values = query.createValues();
+        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 {
+            ThreadSessionAdapter.query(values, new ResultHandler<ThreadSession>() {
+                @Override
+                public boolean onResult(ThreadSession result) {
+                    results.add(result);
+                    return true;
+                }
+            }, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception retrieving thread session", e);
+        }
+
+        return results;
+    }
+
+    public void saveSession(ThreadSession session) {
+        try {
+            ThreadSessionAdapter.insert(session, storage);
+
+        } catch (StatementExecutionException e) {
+            logger.log(Level.SEVERE, "Exception saving session: " + session, e);
+        }
+    }
+
+    @Override
+    public void saveHarvestingStatus(ThreadHarvestingStatus status) {
+        StatementDescriptor<ThreadHarvestingStatus> desc = new StatementDescriptor<>(THREAD_HARVESTING_STATUS, DESC_ADD_THREAD_HARVESTING_STATUS);
+        PreparedStatement<ThreadHarvestingStatus> prepared;
+        try {
+            prepared = storage.prepareStatement(desc);
+            prepared.setString(0, status.getAgentId());
+            prepared.setString(1, status.getVmId());
+            prepared.setLong(2, status.getTimeStamp());
+            prepared.setBoolean(3, status.isHarvesting());
+            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 ThreadHarvestingStatus getLatestHarvestingStatus(VmRef vm) {
+        PreparedStatement<ThreadHarvestingStatus> stmt = prepareQuery(THREAD_HARVESTING_STATUS, 
+                QUERY_LATEST_HARVESTING_STATUS, vm);
+        if (stmt == null) {
+            return null;
+        }
+        
+        return getFirstResult(stmt);
+    }
+
+    @Override
+    public VmDeadLockData loadLatestDeadLockStatus(VmRef ref) {
+        PreparedStatement<VmDeadLockData> stmt = prepareQuery(DEADLOCK_INFO, QUERY_LATEST_DEADLOCK_INFO, ref);
+        if (stmt == null) {
+            return null;
+        }
+        
+        return getFirstResult(stmt);
+    }
+
+    @Override
+    public void saveDeadLockStatus(VmDeadLockData deadLockInfo) {
+        StatementDescriptor<VmDeadLockData> desc = new StatementDescriptor<>(DEADLOCK_INFO, DESC_ADD_THREAD_DEADLOCK_DATA);
+        PreparedStatement<VmDeadLockData> prepared;
+        try {
+            prepared = storage.prepareStatement(desc);
+            prepared.setString(0, deadLockInfo.getAgentId());
+            prepared.setString(1, deadLockInfo.getVmId());
+            prepared.setLong(2, deadLockInfo.getTimeStamp());
+            prepared.setString(3, deadLockInfo.getDeadLockDescription());
+            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);
+        }
+    }
+
+
+    /**************************************************************************/
+
+    private <T extends Pojo> PreparedStatement<T> prepareQuery(Category<T> category, String query, VmRef ref) {
+        return prepareQuery(category, query, ref, null, null);
+    }
+    
+    private <T extends Pojo> PreparedStatement<T> prepareQuery(Category<T> category, String query, VmRef ref, Long since, Long to) {
+        StatementDescriptor<T> desc = new StatementDescriptor<>(category, query);
+        PreparedStatement<T> stmt = null;
+        try {
+            stmt = storage.prepareStatement(desc);
+            stmt.setString(0, ref.getHostRef().getAgentId());
+            stmt.setString(1, ref.getVmId());
+            // assume: the format of the query is such that 2nd and 3rd arguments (if any) are longs
+            if (since != null) {
+                stmt.setLong(2, since);
+            }
+            if (to != null) {
+                stmt.setLong(3, to);
+            }
+        } catch (DescriptorParsingException e) {
+            // should not happen, but if it *does* happen, at least log it
+            logger.log(Level.SEVERE, "Preparing query '" + desc + "' failed!", e);
+        }
+        return stmt;
+    }
+
+    private <T extends Pojo> T getFirstResult(PreparedStatement<T> stmt) {
+        Cursor<T> cursor;
+        try {
+            cursor = stmt.executeQuery();
+        } catch (StatementExecutionException e) {
+            // should not happen, but if it *does* happen, at least log it
+            logger.log(Level.SEVERE, "Executing query '" + stmt + "' failed!", e);
+            return null;
+        }
+
+        return head(cursor);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplStatementDescriptorRegistration.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+
+/**
+ * Registers prepared queries issued by this maven module via
+ * via {@link ThreadDaoImpl}.
+ *
+ */
+public class ThreadDaoImplStatementDescriptorRegistration implements
+        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.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_CONTENTION_SAMPLE);
+        descs.add(ThreadDaoImpl.GET_LATEST_CONTENTION_SAMPLE);
+    }
+    
+    @Override
+    public Set<String> getStatementDescriptors() {
+        return descs;
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoKeys.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import com.redhat.thermostat.storage.core.Key;
+
+/**
+ *
+ */
+public class ThreadDaoKeys {
+
+    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/internal/statement/SessionQueries.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012-2016 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.internal.statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.redhat.thermostat.storage.core.Id;
+import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
+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;
+
+/**
+ *
+ */
+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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/statement/StateQueries.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2012-2016 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.internal.statement;
+
+import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
+import com.redhat.thermostat.storage.core.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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/dao/internal/statement/SummaryQuery.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2016 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.internal.statement;
+
+import com.redhat.thermostat.storage.core.experimental.statement.FieldDescriptor;
+import com.redhat.thermostat.storage.core.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 com.redhat.thermostat.thread.model.ThreadSummary;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ */
+public class SummaryQuery extends Query<ThreadSummary> {
+
+    public static final Id id = new Id(SummaryQuery.class.getSimpleName());
+
+    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");
+    }
+
+    @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.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));
+    }
+}
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSession.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSession.java	Fri Apr 08 13:18:48 2016 +0200
@@ -40,7 +40,7 @@
 import com.redhat.thermostat.storage.core.Persist;
 import com.redhat.thermostat.storage.model.BasePojo;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
-import com.redhat.thermostat.thread.dao.impl.ThreadDaoCategories;
+import com.redhat.thermostat.thread.dao.internal.ThreadDaoCategories;
 import com.redhat.thermostat.storage.core.experimental.statement.Category;
 import com.redhat.thermostat.storage.core.experimental.statement.Indexed;
 
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadState.java	Fri Apr 08 13:18:48 2016 +0200
@@ -42,7 +42,7 @@
 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;
+import com.redhat.thermostat.thread.dao.internal.ThreadDaoCategories;
 
 /**
  * Represents a single delta variation of a Thread state.
--- a/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSummary.java	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/java/com/redhat/thermostat/thread/model/ThreadSummary.java	Fri Apr 08 13:18:48 2016 +0200
@@ -40,7 +40,7 @@
 import com.redhat.thermostat.storage.core.Persist;
 import com.redhat.thermostat.storage.model.BasePojo;
 import com.redhat.thermostat.storage.model.TimeStampedPojo;
-import com.redhat.thermostat.thread.dao.impl.ThreadDaoCategories;
+import com.redhat.thermostat.thread.dao.internal.ThreadDaoCategories;
 import com.redhat.thermostat.storage.core.experimental.statement.Category;
 import com.redhat.thermostat.storage.core.experimental.statement.Indexed;
 
--- a/thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.CategoryRegistration	Fri Apr 08 13:18:48 2016 +0200
@@ -1,2 +1,2 @@
-com.redhat.thermostat.thread.dao.impl.ThreadDAOCategoryRegistration
-com.redhat.thermostat.thread.dao.impl.LockInfoDaoCategoryRegistration
+com.redhat.thermostat.thread.dao.internal.ThreadDAOCategoryRegistration
+com.redhat.thermostat.thread.dao.internal.LockInfoDaoCategoryRegistration
--- a/thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration	Wed Apr 06 14:52:26 2016 +0200
+++ b/thread/collector/src/main/resources/META-INF/services/com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration	Fri Apr 08 13:18:48 2016 +0200
@@ -1,2 +1,2 @@
-com.redhat.thermostat.thread.dao.impl.ThreadDaoImplStatementDescriptorRegistration
-com.redhat.thermostat.thread.dao.impl.LockInfoDaoImplStatementDescriptorRegistration
+com.redhat.thermostat.thread.dao.internal.ThreadDaoImplStatementDescriptorRegistration
+com.redhat.thermostat.thread.dao.internal.LockInfoDaoImplStatementDescriptorRegistration
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoCategoryRegistrationTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
-import com.redhat.thermostat.testutils.ServiceLoaderTest;
-
-public class LockInfoDaoCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
-
-    public LockInfoDaoCategoryRegistrationTest() {
-        super(CategoryRegistration.class, STORAGE_SERVICES + 1, /* from TheadDao */
-                LockInfoDaoCategoryRegistration.class);
-    }
-
-    @Test
-    public void verifyKnownsCategory() {
-        Set<String> names =
-                new LockInfoDaoCategoryRegistration().getCategoryNames();
-        assertEquals(1, names.size());
-        assertTrue(names.contains("vm-thread-lock"));
-    }
-
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/LockInfoDaoImplStatementDescriptorRegistrationTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
-import com.redhat.thermostat.testutils.ServiceLoaderTest;
-
-public class LockInfoDaoImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
-
-    public LockInfoDaoImplStatementDescriptorRegistrationTest() {
-        super(StatementDescriptorRegistration.class, STORAGE_SERVICES + 1 /* from thread dao */, LockInfoDaoImplStatementDescriptorRegistration.class);
-    }
-
-    @Test
-    public void verifyExportsStatements() {
-        Set<String> statements =
-                new LockInfoDaoImplStatementDescriptorRegistration().getStatementDescriptors();
-
-        assertEquals(3, statements.size());
-    }
-
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDAOCategoryRegistrationTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.HashSet;
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
-import com.redhat.thermostat.testutils.ServiceLoaderTest;
-import com.redhat.thermostat.thread.dao.ThreadDao;
-
-public class ThreadDAOCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
-
-    private static final int EXPECTED_CATEGORIES = 6;
-
-    public ThreadDAOCategoryRegistrationTest() {
-        super(CategoryRegistration.class, STORAGE_SERVICES + 1 /* from lock dao */,
-                ThreadDAOCategoryRegistration.class);
-    }
-
-    @Test
-    public void registersAllCategories() {
-        ThreadDAOCategoryRegistration reg = new ThreadDAOCategoryRegistration();
-        Set<String> categories = reg.getCategoryNames();
-        assertEquals(EXPECTED_CATEGORIES, categories.size());
-
-        assertFalse("null descriptor not allowed", categories.contains(null));
-
-        assertTrue(categories.contains(ThreadDao.DEADLOCK_INFO.getName()));
-        assertTrue(categories.contains(ThreadDao.THREAD_HARVESTING_STATUS.getName()));
-        assertTrue(categories.contains(ThreadDao.THREAD_CONTENTION_SAMPLE.getName()));
-
-        Set<String> sourceCategories = new HashSet<>();
-        ThreadDaoCategories.register(sourceCategories);
-
-        for (String category : sourceCategories) {
-            assertTrue(categories.contains(category));
-        }
-    }
-
-}
-
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoCategoriesTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.model.Pojo;
-import com.redhat.thermostat.storage.core.experimental.statement.Category;
-import com.redhat.thermostat.storage.core.experimental.statement.CategoryBuilder;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class ThreadDaoCategoriesTest {
-
-    @Test
-    public void testRegister() throws Exception {
-        Set<String> set = new HashSet<>();
-        ThreadDaoCategories.register(set);
-
-        assertEquals(ThreadDaoCategories.BEANS.size(), set.size());
-        for (Class<? extends Pojo> categoryClass : ThreadDaoCategories.BEANS) {
-            String name = categoryClass.getAnnotation(Category.class).value();
-            assertTrue(set.contains(name));
-        }
-    }
-
-    @Test
-    public void testRegisterInStorage() throws Exception {
-        Storage storage = mock(Storage.class);
-
-        List<com.redhat.thermostat.storage.core.Category<?>> categories =
-                new ArrayList<>();
-        for (Class<? extends Pojo> categoryClass : ThreadDaoCategories.BEANS) {
-            categories.add(new CategoryBuilder(categoryClass).build());
-        }
-
-        ThreadDaoCategories.register(storage);
-        for (com.redhat.thermostat.storage.core.Category<?> category : categories) {
-            verify(storage).registerCategory(category);
-        }
-    }
-}
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplStatementDescriptorRegistrationTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import java.util.Set;
-
-import org.junit.Test;
-
-import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
-import com.redhat.thermostat.testutils.ServiceLoaderTest;
-
-public class ThreadDaoImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
-
-    public ThreadDaoImplStatementDescriptorRegistrationTest() {
-        super(StatementDescriptorRegistration.class, STORAGE_SERVICES + 1 /* from lock dao */, ThreadDaoImplStatementDescriptorRegistration.class);
-    }
-
-    @Test
-    public void registersAllDescriptors() {
-        ThreadDaoImplStatementDescriptorRegistration reg = new ThreadDaoImplStatementDescriptorRegistration();
-        Set<String> descriptors = reg.getStatementDescriptors();
-        assertEquals(14, descriptors.size());
-        assertFalse("null statement not allowed", descriptors.contains(null));
-    }
-
-}
-
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/ThreadDaoImplTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,262 +0,0 @@
-/*
- * Copyright 2012-2016 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;
-
-import com.redhat.thermostat.storage.core.Cursor;
-import com.redhat.thermostat.storage.core.DescriptorParsingException;
-import com.redhat.thermostat.storage.core.HostRef;
-import com.redhat.thermostat.storage.core.PreparedStatement;
-import com.redhat.thermostat.storage.core.StatementDescriptor;
-import com.redhat.thermostat.storage.core.StatementExecutionException;
-import com.redhat.thermostat.storage.core.Storage;
-import com.redhat.thermostat.storage.core.VmRef;
-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.VmDeadLockData;
-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.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-public class ThreadDaoImplTest {
-
-    private static final String AGENT_ID = "0xcafe";
-    private static final String VM_ID = "VM42";
-
-    private VmRef vmRef;
-    private HostRef hostRef;
-
-    @Before
-    public void setUp() {
-        hostRef = mock(HostRef.class);
-        when(hostRef.getAgentId()).thenReturn(AGENT_ID);
-
-        vmRef = mock(VmRef.class);
-        when(vmRef.getHostRef()).thenReturn(hostRef);
-        when(vmRef.getVmId()).thenReturn(VM_ID);
-    }
-
-    @Test
-    public void preparedQueryDescriptorsAreSane() {
-
-        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 expectedQueryThreadLatestDeadlockInfo = "QUERY vm-deadlock-data WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC LIMIT 1";
-        assertEquals(expectedQueryThreadLatestDeadlockInfo, ThreadDaoImpl.QUERY_LATEST_DEADLOCK_INFO);
-
-        String addThreadHarvesting = "ADD vm-thread-harvesting SET 'agentId' = ?s , " +
-                                                    "'vmId' = ?s , " +
-                                                    "'timeStamp' = ?l , " +
-                                                    "'harvesting' = ?b";
-        assertEquals(addThreadHarvesting, ThreadDaoImpl.DESC_ADD_THREAD_HARVESTING_STATUS);
-
-        String addThreadInfo = "ADD vm-thread-header SET 'agentId' = ?s , " +
-                                    "'vmId' = ?s , " +
-                                    "'threadName' = ?s , " +
-                                    "'threadId' = ?l , " +
-                                    "'timeStamp' = ?l , " +
-                                    "'referenceID' = ?s";
-
-        String addContentionSample = "ADD thread-contention-sample SET " +
-                "'agentId' = ?s , 'vmId' = ?s , 'blockedCount' = ?l , " +
-                "'blockedTime' = ?l , 'waitedCount' = ?l , " +
-                "'waitedTime' = ?l , 'referenceID' = ?s , 'timeStamp' = ?l";
-        assertEquals(addContentionSample, ThreadDaoImpl.ADD_CONTENTION_SAMPLE);
-
-        String getLatestContentionSample = "QUERY thread-contention-sample " +
-                "WHERE 'referenceID' = ?s SORT 'timeStamp' DSC LIMIT 1";
-        assertEquals(getLatestContentionSample, ThreadDaoImpl.GET_LATEST_CONTENTION_SAMPLE);
-
-        String getFirstThreadState = "QUERY vm-thread-state WHERE " +
-                "'agentId' = ?s AND 'referenceID' = ?s SORT 'probeStartTime' " +
-                "ASC LIMIT 1";
-    }
-    
-    @Test
-    public void testThreadDaoCategoryRegistration() {
-        Storage storage = mock(Storage.class);
-        
-        @SuppressWarnings("unused")
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        
-        verify(storage).registerCategory(ThreadDao.THREAD_HARVESTING_STATUS);
-    }
-
-    @SuppressWarnings("unchecked")
-    private <T extends Pojo> StatementDescriptor<T> anyDescriptor(Class<T> type) {
-        return (StatementDescriptor<T>) any(StatementDescriptor.class);
-    }
-
-    @Test
-    public void testLoadLatestDeadLockStatusWithNoData() throws Exception {
-        Storage storage = mock(Storage.class);
-        @SuppressWarnings("unchecked")
-        PreparedStatement<VmDeadLockData> stmt = (PreparedStatement<VmDeadLockData>) mock(PreparedStatement.class);
-        when(storage.prepareStatement(anyDescriptor(VmDeadLockData.class))).thenReturn(stmt);
-        @SuppressWarnings("unchecked")
-        Cursor<VmDeadLockData> cursor = (Cursor<VmDeadLockData>) mock(Cursor.class);
-
-        when(cursor.hasNext()).thenReturn(false);
-        when(cursor.next()).thenThrow(new IllegalStateException("must not do this"));
-        when(stmt.executeQuery()).thenReturn(cursor);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        VmDeadLockData result = dao.loadLatestDeadLockStatus(vmRef);
-
-        assertNull(result);
-    }
-
-    @Test
-    public void testLoadLatestDeadLockStatus() throws DescriptorParsingException, StatementExecutionException {
-        Storage storage = mock(Storage.class);
-        @SuppressWarnings("unchecked")
-        PreparedStatement<VmDeadLockData> stmt = (PreparedStatement<VmDeadLockData>) mock(PreparedStatement.class);
-        when(storage.prepareStatement(anyDescriptor(VmDeadLockData.class))).thenReturn(stmt);
-        @SuppressWarnings("unchecked")
-        Cursor<VmDeadLockData> cursor = (Cursor<VmDeadLockData>) mock(Cursor.class);
-        VmDeadLockData data = mock(VmDeadLockData.class);
-
-        when(cursor.hasNext()).thenReturn(true);
-        when(cursor.next()).thenReturn(data);
-        when(stmt.executeQuery()).thenReturn(cursor);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        VmDeadLockData result = dao.loadLatestDeadLockStatus(vmRef);
-
-        assertSame(data, result);
-
-        verify(storage).prepareStatement(anyDescriptor(VmDeadLockData.class));
-        verify(stmt).setString(0, AGENT_ID);
-        verify(stmt).setString(1, VM_ID);
-        verify(stmt).executeQuery();
-        verifyNoMoreInteractions(stmt);
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testSaveDeadLockStatus() throws DescriptorParsingException, StatementExecutionException {
-        Storage storage = mock(Storage.class);
-        PreparedStatement<VmDeadLockData> add = mock(PreparedStatement.class);
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(add);
-
-        VmDeadLockData status = mock(VmDeadLockData.class);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        dao.saveDeadLockStatus(status);
-        
-        @SuppressWarnings("rawtypes")
-        ArgumentCaptor<StatementDescriptor> captor = ArgumentCaptor.forClass(StatementDescriptor.class);
-        
-        verify(storage).prepareStatement(captor.capture());
-        StatementDescriptor<VmDeadLockData> desc = captor.getValue();
-        assertEquals(ThreadDaoImpl.DESC_ADD_THREAD_DEADLOCK_DATA, desc.getDescriptor());
-
-        verify(add).setString(0, status.getAgentId());
-        verify(add).setString(1, status.getVmId());
-        verify(add).setLong(2, status.getTimeStamp());
-        verify(add).setString(3, status.getDeadLockDescription());
-        verify(add).execute();
-        Mockito.verifyNoMoreInteractions(add);
-    }
-
-    @Test
-    public void testGetLatestHarvestingStatus()
-            throws DescriptorParsingException, StatementExecutionException {
-        Storage storage = mock(Storage.class);
-        @SuppressWarnings("unchecked")
-        PreparedStatement<ThreadHarvestingStatus> stmt = (PreparedStatement<ThreadHarvestingStatus>) mock(PreparedStatement.class);
-        when(storage.prepareStatement(anyDescriptor(ThreadHarvestingStatus.class))).thenReturn(stmt);
-        @SuppressWarnings("unchecked")
-        Cursor<ThreadHarvestingStatus> cursor = (Cursor<ThreadHarvestingStatus>) mock(Cursor.class);
-        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
-
-        when(cursor.hasNext()).thenReturn(true);
-        when(cursor.next()).thenReturn(status);
-        when(stmt.executeQuery()).thenReturn(cursor);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        ThreadHarvestingStatus result = dao.getLatestHarvestingStatus(vmRef);
-
-        verify(storage).prepareStatement(anyDescriptor(ThreadHarvestingStatus.class));
-        verify(stmt).setString(0, AGENT_ID);
-        verify(stmt).setString(1, VM_ID);
-        verify(stmt).executeQuery();
-        verifyNoMoreInteractions(stmt);
-
-        assertSame(status, result);
-    }
-
-    @SuppressWarnings("unchecked")
-    @Test
-    public void testAddHarvestingStatus() throws DescriptorParsingException, StatementExecutionException {
-        Storage storage = mock(Storage.class);
-        PreparedStatement<ThreadHarvestingStatus> add = mock(PreparedStatement.class);
-        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(add);
-
-        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
-
-        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
-        dao.saveHarvestingStatus(status);
-        
-        @SuppressWarnings("rawtypes")
-        ArgumentCaptor<StatementDescriptor> captor = ArgumentCaptor.forClass(StatementDescriptor.class);
-        
-        verify(storage).prepareStatement(captor.capture());
-        StatementDescriptor<VmDeadLockData> desc = captor.getValue();
-        assertEquals(ThreadDaoImpl.DESC_ADD_THREAD_HARVESTING_STATUS, desc.getDescriptor());
-
-        verify(add).setString(0, status.getAgentId());
-        verify(add).setString(1, status.getVmId());
-        verify(add).setLong(2, status.getTimeStamp());
-        verify(add).setBoolean(3, status.isHarvesting());
-        verify(add).execute();
-        verifyNoMoreInteractions(add);
-    }
-
-}
-
--- a/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/impl/statement/SessionQueriesTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright 2012-2016 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/SummaryQueryTest.java	Wed Apr 06 14:52:26 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * Copyright 2012-2016 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.ThreadSummary;
-import java.util.Set;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class SummaryQueryTest {
-    @Test
-    public void testDescribe() throws Exception {
-        BeanAdapter<ThreadSummary> session =
-                new BeanAdapterBuilder<>(ThreadSummary.class,
-                                         new SummaryQuery()).build();
-        Set<String> statements = session.describeStatements();
-        assertEquals(2, statements.size());
-
-        String expected = "QUERY vm-thread-summary WHERE 'vmId' = ?s AND 'agentId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
-        assertTrue(statements.contains(expected));
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoCategoryRegistrationTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+
+public class LockInfoDaoCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
+
+    public LockInfoDaoCategoryRegistrationTest() {
+        super(CategoryRegistration.class, STORAGE_SERVICES + 1, /* from TheadDao */
+                LockInfoDaoCategoryRegistration.class);
+    }
+
+    @Test
+    public void verifyKnownsCategory() {
+        Set<String> names =
+                new LockInfoDaoCategoryRegistration().getCategoryNames();
+        assertEquals(1, names.size());
+        assertTrue(names.contains("vm-thread-lock"));
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/LockInfoDaoImplStatementDescriptorRegistrationTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+
+public class LockInfoDaoImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
+
+    public LockInfoDaoImplStatementDescriptorRegistrationTest() {
+        super(StatementDescriptorRegistration.class, STORAGE_SERVICES + 1 /* from thread dao */, LockInfoDaoImplStatementDescriptorRegistration.class);
+    }
+
+    @Test
+    public void verifyExportsStatements() {
+        Set<String> statements =
+                new LockInfoDaoImplStatementDescriptorRegistration().getStatementDescriptors();
+
+        assertEquals(3, statements.size());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDAOCategoryRegistrationTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.CategoryRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+import com.redhat.thermostat.thread.dao.ThreadDao;
+
+public class ThreadDAOCategoryRegistrationTest extends ServiceLoaderTest<CategoryRegistration> {
+
+    private static final int EXPECTED_CATEGORIES = 6;
+
+    public ThreadDAOCategoryRegistrationTest() {
+        super(CategoryRegistration.class, STORAGE_SERVICES + 1 /* from lock dao */,
+                ThreadDAOCategoryRegistration.class);
+    }
+
+    @Test
+    public void registersAllCategories() {
+        ThreadDAOCategoryRegistration reg = new ThreadDAOCategoryRegistration();
+        Set<String> categories = reg.getCategoryNames();
+        assertEquals(EXPECTED_CATEGORIES, categories.size());
+
+        assertFalse("null descriptor not allowed", categories.contains(null));
+
+        assertTrue(categories.contains(ThreadDao.DEADLOCK_INFO.getName()));
+        assertTrue(categories.contains(ThreadDao.THREAD_HARVESTING_STATUS.getName()));
+        assertTrue(categories.contains(ThreadDao.THREAD_CONTENTION_SAMPLE.getName()));
+
+        Set<String> sourceCategories = new HashSet<>();
+        ThreadDaoCategories.register(sourceCategories);
+
+        for (String category : sourceCategories) {
+            assertTrue(categories.contains(category));
+        }
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoCategoriesTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.experimental.statement.Category;
+import com.redhat.thermostat.storage.core.experimental.statement.CategoryBuilder;
+import com.redhat.thermostat.storage.model.Pojo;
+
+public class ThreadDaoCategoriesTest {
+
+    @Test
+    public void testRegister() throws Exception {
+        Set<String> set = new HashSet<>();
+        ThreadDaoCategories.register(set);
+
+        assertEquals(ThreadDaoCategories.BEANS.size(), set.size());
+        for (Class<? extends Pojo> categoryClass : ThreadDaoCategories.BEANS) {
+            String name = categoryClass.getAnnotation(Category.class).value();
+            assertTrue(set.contains(name));
+        }
+    }
+
+    @Test
+    public void testRegisterInStorage() throws Exception {
+        Storage storage = mock(Storage.class);
+
+        List<com.redhat.thermostat.storage.core.Category<?>> categories =
+                new ArrayList<>();
+        for (Class<? extends Pojo> categoryClass : ThreadDaoCategories.BEANS) {
+            categories.add(new CategoryBuilder(categoryClass).build());
+        }
+
+        ThreadDaoCategories.register(storage);
+        for (com.redhat.thermostat.storage.core.Category<?> category : categories) {
+            verify(storage).registerCategory(category);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplStatementDescriptorRegistrationTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.auth.StatementDescriptorRegistration;
+import com.redhat.thermostat.testutils.ServiceLoaderTest;
+
+public class ThreadDaoImplStatementDescriptorRegistrationTest extends ServiceLoaderTest<StatementDescriptorRegistration> {
+
+    public ThreadDaoImplStatementDescriptorRegistrationTest() {
+        super(StatementDescriptorRegistration.class, STORAGE_SERVICES + 1 /* from lock dao */, ThreadDaoImplStatementDescriptorRegistration.class);
+    }
+
+    @Test
+    public void registersAllDescriptors() {
+        ThreadDaoImplStatementDescriptorRegistration reg = new ThreadDaoImplStatementDescriptorRegistration();
+        Set<String> descriptors = reg.getStatementDescriptors();
+        assertEquals(14, descriptors.size());
+        assertFalse("null statement not allowed", descriptors.contains(null));
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/ThreadDaoImplTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2012-2016 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.internal;
+
+import static org.junit.Assert.assertEquals;
+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.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import com.redhat.thermostat.storage.core.Cursor;
+import com.redhat.thermostat.storage.core.DescriptorParsingException;
+import com.redhat.thermostat.storage.core.HostRef;
+import com.redhat.thermostat.storage.core.PreparedStatement;
+import com.redhat.thermostat.storage.core.StatementDescriptor;
+import com.redhat.thermostat.storage.core.StatementExecutionException;
+import com.redhat.thermostat.storage.core.Storage;
+import com.redhat.thermostat.storage.core.VmRef;
+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.VmDeadLockData;
+
+public class ThreadDaoImplTest {
+
+    private static final String AGENT_ID = "0xcafe";
+    private static final String VM_ID = "VM42";
+
+    private VmRef vmRef;
+    private HostRef hostRef;
+
+    @Before
+    public void setUp() {
+        hostRef = mock(HostRef.class);
+        when(hostRef.getAgentId()).thenReturn(AGENT_ID);
+
+        vmRef = mock(VmRef.class);
+        when(vmRef.getHostRef()).thenReturn(hostRef);
+        when(vmRef.getVmId()).thenReturn(VM_ID);
+    }
+
+    @Test
+    public void preparedQueryDescriptorsAreSane() {
+
+        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 expectedQueryThreadLatestDeadlockInfo = "QUERY vm-deadlock-data WHERE 'agentId' = ?s AND 'vmId' = ?s SORT 'timeStamp' DSC LIMIT 1";
+        assertEquals(expectedQueryThreadLatestDeadlockInfo, ThreadDaoImpl.QUERY_LATEST_DEADLOCK_INFO);
+
+        String addThreadHarvesting = "ADD vm-thread-harvesting SET 'agentId' = ?s , " +
+                                                    "'vmId' = ?s , " +
+                                                    "'timeStamp' = ?l , " +
+                                                    "'harvesting' = ?b";
+        assertEquals(addThreadHarvesting, ThreadDaoImpl.DESC_ADD_THREAD_HARVESTING_STATUS);
+
+        String addThreadInfo = "ADD vm-thread-header SET 'agentId' = ?s , " +
+                                    "'vmId' = ?s , " +
+                                    "'threadName' = ?s , " +
+                                    "'threadId' = ?l , " +
+                                    "'timeStamp' = ?l , " +
+                                    "'referenceID' = ?s";
+
+        String addContentionSample = "ADD thread-contention-sample SET " +
+                "'agentId' = ?s , 'vmId' = ?s , 'blockedCount' = ?l , " +
+                "'blockedTime' = ?l , 'waitedCount' = ?l , " +
+                "'waitedTime' = ?l , 'referenceID' = ?s , 'timeStamp' = ?l";
+        assertEquals(addContentionSample, ThreadDaoImpl.ADD_CONTENTION_SAMPLE);
+
+        String getLatestContentionSample = "QUERY thread-contention-sample " +
+                "WHERE 'referenceID' = ?s SORT 'timeStamp' DSC LIMIT 1";
+        assertEquals(getLatestContentionSample, ThreadDaoImpl.GET_LATEST_CONTENTION_SAMPLE);
+
+        String getFirstThreadState = "QUERY vm-thread-state WHERE " +
+                "'agentId' = ?s AND 'referenceID' = ?s SORT 'probeStartTime' " +
+                "ASC LIMIT 1";
+    }
+    
+    @Test
+    public void testThreadDaoCategoryRegistration() {
+        Storage storage = mock(Storage.class);
+        
+        @SuppressWarnings("unused")
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        
+        verify(storage).registerCategory(ThreadDao.THREAD_HARVESTING_STATUS);
+    }
+
+    @SuppressWarnings("unchecked")
+    private <T extends Pojo> StatementDescriptor<T> anyDescriptor(Class<T> type) {
+        return (StatementDescriptor<T>) any(StatementDescriptor.class);
+    }
+
+    @Test
+    public void testLoadLatestDeadLockStatusWithNoData() throws Exception {
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<VmDeadLockData> stmt = (PreparedStatement<VmDeadLockData>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor(VmDeadLockData.class))).thenReturn(stmt);
+        @SuppressWarnings("unchecked")
+        Cursor<VmDeadLockData> cursor = (Cursor<VmDeadLockData>) mock(Cursor.class);
+
+        when(cursor.hasNext()).thenReturn(false);
+        when(cursor.next()).thenThrow(new IllegalStateException("must not do this"));
+        when(stmt.executeQuery()).thenReturn(cursor);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        VmDeadLockData result = dao.loadLatestDeadLockStatus(vmRef);
+
+        assertNull(result);
+    }
+
+    @Test
+    public void testLoadLatestDeadLockStatus() throws DescriptorParsingException, StatementExecutionException {
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<VmDeadLockData> stmt = (PreparedStatement<VmDeadLockData>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor(VmDeadLockData.class))).thenReturn(stmt);
+        @SuppressWarnings("unchecked")
+        Cursor<VmDeadLockData> cursor = (Cursor<VmDeadLockData>) mock(Cursor.class);
+        VmDeadLockData data = mock(VmDeadLockData.class);
+
+        when(cursor.hasNext()).thenReturn(true);
+        when(cursor.next()).thenReturn(data);
+        when(stmt.executeQuery()).thenReturn(cursor);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        VmDeadLockData result = dao.loadLatestDeadLockStatus(vmRef);
+
+        assertSame(data, result);
+
+        verify(storage).prepareStatement(anyDescriptor(VmDeadLockData.class));
+        verify(stmt).setString(0, AGENT_ID);
+        verify(stmt).setString(1, VM_ID);
+        verify(stmt).executeQuery();
+        verifyNoMoreInteractions(stmt);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testSaveDeadLockStatus() throws DescriptorParsingException, StatementExecutionException {
+        Storage storage = mock(Storage.class);
+        PreparedStatement<VmDeadLockData> add = mock(PreparedStatement.class);
+        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(add);
+
+        VmDeadLockData status = mock(VmDeadLockData.class);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        dao.saveDeadLockStatus(status);
+        
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<StatementDescriptor> captor = ArgumentCaptor.forClass(StatementDescriptor.class);
+        
+        verify(storage).prepareStatement(captor.capture());
+        StatementDescriptor<VmDeadLockData> desc = captor.getValue();
+        assertEquals(ThreadDaoImpl.DESC_ADD_THREAD_DEADLOCK_DATA, desc.getDescriptor());
+
+        verify(add).setString(0, status.getAgentId());
+        verify(add).setString(1, status.getVmId());
+        verify(add).setLong(2, status.getTimeStamp());
+        verify(add).setString(3, status.getDeadLockDescription());
+        verify(add).execute();
+        Mockito.verifyNoMoreInteractions(add);
+    }
+
+    @Test
+    public void testGetLatestHarvestingStatus()
+            throws DescriptorParsingException, StatementExecutionException {
+        Storage storage = mock(Storage.class);
+        @SuppressWarnings("unchecked")
+        PreparedStatement<ThreadHarvestingStatus> stmt = (PreparedStatement<ThreadHarvestingStatus>) mock(PreparedStatement.class);
+        when(storage.prepareStatement(anyDescriptor(ThreadHarvestingStatus.class))).thenReturn(stmt);
+        @SuppressWarnings("unchecked")
+        Cursor<ThreadHarvestingStatus> cursor = (Cursor<ThreadHarvestingStatus>) mock(Cursor.class);
+        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
+
+        when(cursor.hasNext()).thenReturn(true);
+        when(cursor.next()).thenReturn(status);
+        when(stmt.executeQuery()).thenReturn(cursor);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        ThreadHarvestingStatus result = dao.getLatestHarvestingStatus(vmRef);
+
+        verify(storage).prepareStatement(anyDescriptor(ThreadHarvestingStatus.class));
+        verify(stmt).setString(0, AGENT_ID);
+        verify(stmt).setString(1, VM_ID);
+        verify(stmt).executeQuery();
+        verifyNoMoreInteractions(stmt);
+
+        assertSame(status, result);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testAddHarvestingStatus() throws DescriptorParsingException, StatementExecutionException {
+        Storage storage = mock(Storage.class);
+        PreparedStatement<ThreadHarvestingStatus> add = mock(PreparedStatement.class);
+        when(storage.prepareStatement(any(StatementDescriptor.class))).thenReturn(add);
+
+        ThreadHarvestingStatus status = mock(ThreadHarvestingStatus.class);
+
+        ThreadDaoImpl dao = new ThreadDaoImpl(storage);
+        dao.saveHarvestingStatus(status);
+        
+        @SuppressWarnings("rawtypes")
+        ArgumentCaptor<StatementDescriptor> captor = ArgumentCaptor.forClass(StatementDescriptor.class);
+        
+        verify(storage).prepareStatement(captor.capture());
+        StatementDescriptor<VmDeadLockData> desc = captor.getValue();
+        assertEquals(ThreadDaoImpl.DESC_ADD_THREAD_HARVESTING_STATUS, desc.getDescriptor());
+
+        verify(add).setString(0, status.getAgentId());
+        verify(add).setString(1, status.getVmId());
+        verify(add).setLong(2, status.getTimeStamp());
+        verify(add).setBoolean(3, status.isHarvesting());
+        verify(add).execute();
+        verifyNoMoreInteractions(add);
+    }
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/statement/SessionQueriesTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012-2016 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.internal.statement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+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;
+
+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));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/thread/collector/src/test/java/com/redhat/thermostat/thread/dao/internal/statement/SummaryQueryTest.java	Fri Apr 08 13:18:48 2016 +0200
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2012-2016 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.internal.statement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapter;
+import com.redhat.thermostat.storage.core.experimental.statement.BeanAdapterBuilder;
+import com.redhat.thermostat.thread.model.ThreadSummary;
+
+public class SummaryQueryTest {
+    @Test
+    public void testDescribe() throws Exception {
+        BeanAdapter<ThreadSummary> session =
+                new BeanAdapterBuilder<>(ThreadSummary.class,
+                                         new SummaryQuery()).build();
+        Set<String> statements = session.describeStatements();
+        assertEquals(2, statements.size());
+
+        String expected = "QUERY vm-thread-summary WHERE 'vmId' = ?s AND 'agentId' = ?s AND 'timeStamp' >= ?l AND 'timeStamp' <= ?l SORT 'timeStamp' DSC LIMIT ?i";
+        assertTrue(statements.contains(expected));
+    }
+}