.classpath | 1
etc/dev-utils/TestLdapSettings.java | 738 -----
etc/rhq-ircBot/src/main/resources/org/rhq/etc/ircbot/cacerts.jks |binary
modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java | 5
modules/common/cassandra-ccm/cassandra-ccm-core/src/main/bundle/cassandra/conf/cassandra-jvm.properties | 2
modules/common/cassandra-ccm/cassandra-ccm-core/src/test/resources/expected.cassandra-jvm.properties | 2
modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java | 123
modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditorException.java | 21
modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java | 122
modules/common/cassandra-util/src/test/resources/cassandra.yaml | 690 +++++
modules/core/dbutils/pom.xml | 6
modules/core/dbutils/src/main/scripts/dbsetup/authz-data.xml | 28
modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml | 67
modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java | 3
modules/enterprise/gui/coregui/pom.xml | 13
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/IconEnum.java | 6
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/alert/definitions/GeneralPropertiesAlertDefinitionForm.java | 4
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java | 29
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/table/Table.java | 10
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/AbstractMetricGraph.java | 4
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/ButtonBarDateTimeRangeEditor.java | 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/MetricGraphData.java | 11
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityLineGraphType.java | 311 --
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java | 13
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java | 155 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/LineMetricGraph.java | 158 -
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java | 9
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/D3GroupGraphListView.java | 115
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/table/CompositeGroupMultiLineGraphListView.java | 79
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/DashboardLinkUtility.java | 258 ++
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceDetailView.java | 33
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceTreeView.java | 204 -
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/D3GraphListView.java | 308 +-
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/MetricD3Graph.java | 27
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/avail/ResourceAvailabilityView.java | 399 ---
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java | 186 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java | 237 -
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java | 167 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableDataSource.java | 213 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java | 389 +++
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java | 307 ++
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java | 222 +
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/searchbar/BasicSearchStrategy.java | 15
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/util/enhanced/Enhanced.java | 2
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties | 12
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties | 10
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties | 10
modules/enterprise/gui/coregui/src/main/webapp/js/rhq.js | 4
modules/enterprise/server/appserver/pom.xml | 11
modules/enterprise/server/appserver/src/main/scripts/rhq-container.build.xml | 11
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBeanTest.java | 6
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/test/MeasurementBaselineManagerTest.java | 6
modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml | 28
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java | 362 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java | 12
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java | 1
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java | 7
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java | 185 -
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java | 15
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java | 49
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/helper/LinkDeserializer.java | 28
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java | 18
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java | 83
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandler.java | 17
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandlerBean.java | 90
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java | 153 -
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java | 5
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageResultSetFuture.java | 76
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java | 110
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageStateListener.java | 20
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/ListPagedResult.java | 6
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/SimplePagedResult.java | 11
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java | 35
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ClusterMonitorTest.java | 92
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java | 14
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java | 17
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java | 7
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/TimeoutTest.java | 5
modules/enterprise/server/server-metrics/src/test/resources/log4j.properties | 2
modules/helpers/ldap-tool/pom.xml | 85
modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java | 1289 ++++++++++
modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java | 5
modules/integration-tests/rest-api/pom.xml | 11
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java | 8
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java | 337 +-
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java | 4
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkDeserializer.java | 18
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkSerializer.java | 53
modules/plugins/cassandra/pom.xml | 9
modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java | 151 +
modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml | 3
modules/plugins/rhq-storage/pom.xml | 139 +
modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/InternodeAuthConfUpdateException.java | 22
modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java | 275 +-
modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java | 81
modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml | 31
modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java | 361 ++
modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeConfigDelegateTest.java | 41
modules/plugins/rhq-storage/src/test/resources/cassandra.yaml | 690 +++++
modules/plugins/rhq-storage/src/test/resources/log4j.xml | 40
pom.xml | 4
106 files changed, 7960 insertions(+), 2959 deletions(-)
New commits:
commit d7bf4998586c6daba79a166f2cc2440f27d259be
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Jul 31 17:10:54 2013 -0400
Add new perms to out-of-box roles, start db-upgrade step for fine-grained
bundle perm work
diff --git a/modules/core/dbutils/src/main/scripts/dbsetup/authz-data.xml b/modules/core/dbutils/src/main/scripts/dbsetup/authz-data.xml
index 681bd66..ecc7d4a 100644
--- a/modules/core/dbutils/src/main/scripts/dbsetup/authz-data.xml
+++ b/modules/core/dbutils/src/main/scripts/dbsetup/authz-data.xml
@@ -43,13 +43,19 @@
<table name="RHQ_PERMISSION">
<!-- give super-user-role all permissions
- (explicitly add resource perms, even though they are implied by MANAGE_INVENTORY) -->
+ (explicitly add resource perms, even though they are implied by MANAGE_INVENTORY)
+ (explicitly add bundle perms, even though they are implied by MANAGE_BUNDLE) -->
<data ROLE_ID="1" OPERATION="0"/> <!-- Permission.MANAGE_SECURITY -->
<data ROLE_ID="1" OPERATION="1"/> <!-- Permission.MANAGE_INVENTORY -->
<data ROLE_ID="1" OPERATION="2"/> <!-- Permission.MANAGE_SETTINGS -->
- <data ROLE_ID="1" OPERATION="12"/> <!-- Permission.MANAGE_BUNDLE -->
+ <data ROLE_ID="1" OPERATION="12"/> <!-- Permission.MANAGE_BUNDLE -->
<data ROLE_ID="1" OPERATION="15"/> <!-- Permission.MANAGE_REPOSITORIES -->
<data ROLE_ID="1" OPERATION="17"/> <!-- Permission.VIEW_USERS -->
+ <data ROLE_ID="1" OPERATION="18"/> <!-- Permission.MANAGE_BUNDLE_GROUPS -->
+ <data ROLE_ID="1" OPERATION="19"/> <!-- Permission.CREATE_BUNDLES -->
+ <data ROLE_ID="1" OPERATION="20"/> <!-- Permission.DELETE_BUNDLES -->
+ <data ROLE_ID="1" OPERATION="21"/> <!-- Permission.DEPLOY_BUNDLES -->
+ <data ROLE_ID="1" OPERATION="22"/> <!-- Permission.VIEW_BUNDLES -->
<!-- resource permissions start here-->
<data ROLE_ID="1" OPERATION="3"/> <!-- Permission.VIEW_RESOURCE -->
<data ROLE_ID="1" OPERATION="4"/> <!-- Permission.MODIFY_RESOURCE -->
@@ -63,12 +69,22 @@
<data ROLE_ID="1" OPERATION="13"/> <!-- Permission.CONFIGURE_READ -->
<data ROLE_ID="1" OPERATION="14"/> <!-- Permission.MANAGE_EVENTS -->
<data ROLE_ID="1" OPERATION="16"/> <!-- Permission.MANAGE_DRIFT -->
+ <!-- bundle permissions start here-->
+ <data ROLE_ID="1" OPERATION="23"/> <!-- Permission.ASSIGN_BUNDLES_TO_GROUP -->
+ <data ROLE_ID="1" OPERATION="24"/> <!-- Permission.UNASSIGN_BUNDLES_FROM_GROUP -->
+ <data ROLE_ID="1" OPERATION="25"/> <!-- Permission.CREATE_BUNDLES_IN_GROUP -->
+ <data ROLE_ID="1" OPERATION="26"/> <!-- Permission.DELETE_BUNDLES_FROM_GROUP -->
+ <data ROLE_ID="1" OPERATION="27"/> <!-- Permission.VIEW_BUNDLES_IN_GROUP -->
- <!-- give all-resources-role MANAGE_INVENTORY and MANAGE_BUNDLE permissions
+ <!-- give all-resources-role MANAGE_INVENTORY and all bundle permissions other than MANAGE_BUNDLE_GROUPS
(explicitly add resource perms as well, even though they are implied by MANAGE_INVENTORY) -->
<data ROLE_ID="2" OPERATION="1"/> <!-- Permission.MANAGE_INVENTORY -->
<data ROLE_ID="2" OPERATION="12"/> <!-- Permission.MANAGE_BUNDLE -->
<data ROLE_ID="2" OPERATION="17"/> <!-- Permission.VIEW_USERS -->
+ <data ROLE_ID="2" OPERATION="19"/> <!-- Permission.CREATE_BUNDLES -->
+ <data ROLE_ID="2" OPERATION="20"/> <!-- Permission.DELETE_BUNDLES -->
+ <data ROLE_ID="2" OPERATION="21"/> <!-- Permission.DEPLOY_BUNDLES -->
+ <data ROLE_ID="2" OPERATION="22"/> <!-- Permission.VIEW_BUNDLES -->
<!-- resource permissions start here-->
<data ROLE_ID="2" OPERATION="3"/> <!-- Permission.VIEW_RESOURCE -->
<data ROLE_ID="2" OPERATION="4"/> <!-- Permission.MODIFY_RESOURCE -->
@@ -82,6 +98,12 @@
<data ROLE_ID="2" OPERATION="13"/> <!-- Permission.CONFIGURE_READ -->
<data ROLE_ID="2" OPERATION="14"/> <!-- Permission.MANAGE_EVENTS -->
<data ROLE_ID="2" OPERATION="16"/> <!-- Permission.MANAGE_DRIFT -->
+ <!-- bundle permissions start here-->
+ <data ROLE_ID="2" OPERATION="23"/> <!-- Permission.ASSIGN_BUNDLES_TO_GROUP -->
+ <data ROLE_ID="2" OPERATION="24"/> <!-- Permission.UNASSIGN_BUNDLES_FROM_GROUP -->
+ <data ROLE_ID="2" OPERATION="25"/> <!-- Permission.CREATE_BUNDLES_IN_GROUP -->
+ <data ROLE_ID="2" OPERATION="26"/> <!-- Permission.DELETE_BUNDLES_FROM_GROUP -->
+ <data ROLE_ID="2" OPERATION="27"/> <!-- Permission.VIEW_BUNDLES_IN_GROUP -->
</table>
</dbsetup>
diff --git a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
index bb5fa50..25cdfec 100644
--- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
+++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
@@ -2079,6 +2079,73 @@
<schema-javaTask className="StorageNodeAddressUpgradeTask" />
</schemaSpec>
+ <schemaSpec version="2.135">
+ <!-- Fine Grained Bundle Permissions -->
+
+ <!-- Add new perms to superuser/all-resources roles -->
+ <schema-directSQL>
+ <statement desc="Inserting MANAGE_BUNDLE_GROUPS permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 18)
+ </statement>
+ <statement desc="Inserting CREATE_BUNDLES permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 19)
+ </statement>
+ <statement desc="Inserting DELETE_BUNDLES permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 20)
+ </statement>
+ <statement desc="Inserting DEPLOY_BUNDLES permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 21)
+ </statement>
+ <statement desc="Inserting VIEW_BUNDLES permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 22)
+ </statement>
+ <statement desc="Inserting ASSIGN_BUNDLES_TO_GROUP permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 23)
+ </statement>
+ <statement desc="Inserting UNASSIGN_BUNDLES_FROM_GROUP permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 24)
+ </statement>
+ <statement desc="Inserting CREATE_BUNDLES_IN_GROUP permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 25)
+ </statement>
+ <statement desc="Inserting DELETE_BUNDLES_FROM_GROUP permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 26)
+ </statement>
+ <statement desc="Inserting VIEW_BUNDLES_IN_GROUP permission for 'Super User' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (1, 27)
+ </statement>
+
+ <!-- all-resources-role does not get MANAGE_BUNDLE_GROUPS -->
+ <statement desc="Inserting CREATE_BUNDLES permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 19)
+ </statement>
+ <statement desc="Inserting DELETE_BUNDLES permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 20)
+ </statement>
+ <statement desc="Inserting DEPLOY_BUNDLES permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 21)
+ </statement>
+ <statement desc="Inserting VIEW_BUNDLES permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 22)
+ </statement>
+ <statement desc="Inserting ASSIGN_BUNDLES_TO_GROUP permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 23)
+ </statement>
+ <statement desc="Inserting UNASSIGN_BUNDLES_FROM_GROUP permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 24)
+ </statement>
+ <statement desc="Inserting CREATE_BUNDLES_IN_GROUP permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 25)
+ </statement>
+ <statement desc="Inserting DELETE_BUNDLES_FROM_GROUP permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 26)
+ </statement>
+ <statement desc="Inserting VIEW_BUNDLES_IN_GROUP permission for 'All Resources' role">
+ INSERT INTO RHQ_PERMISSION (ROLE_ID, OPERATION) VALUES (2, 27)
+ </statement>
+ </schema-directSQL>
+ </schemaSpec>
+
</dbupgrade>
</target>
</project>
commit 4b5ce46db93610cc198fd2442027992db72ee36a
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Jul 31 17:09:56 2013 -0400
Fix issues when performing dbsetup-upgrade with defaults
diff --git a/modules/core/dbutils/pom.xml b/modules/core/dbutils/pom.xml
index 483688f..d7e2d65 100644
--- a/modules/core/dbutils/pom.xml
+++ b/modules/core/dbutils/pom.xml
@@ -280,9 +280,9 @@
if (project.getProperty('storage-schema')) {
if (project.getProperty('db') == 'dev') {
self.log('PERFORMING STORAGE NODE SETUP TO LATEST SCHEMA')
- username = project.getProperty('rhq.dev.cassandra.username') ?: "cassandra"
- password = project.getProperty('rhq.dev.cassandra.password') ?: "cassandra"
- seeds = project.getProperty('rhq.dev.cassandra.seeds') ?: "127.0.0.1|7199|9142"
+ username = project.getProperty('rhq.dev.cassandra.username') ?: "rhqadmin"
+ password = project.getProperty('rhq.dev.cassandra.password') ?: "rhqadmin"
+ seeds = project.getProperty('rhq.dev.cassandra.seeds') ?: "127.0.0.1|7299|9142"
schemaManager = new SchemaManager(username, password, seeds)
commit a3f58e21fe5e48819fa89c12e2d6ffb66dab08d4
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Jul 31 17:09:02 2013 -0400
Fix issue when using -Ditest.use-external-storage-node
diff --git a/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java b/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
index a86c49e..6001450 100644
--- a/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
+++ b/modules/common/cassandra-ccm/cassandra-ccm-arquillian/src/main/java/org/rhq/cassandra/ccm/arquillian/CCMSuiteDeploymentExtension.java
@@ -26,7 +26,6 @@
package org.rhq.cassandra.ccm.arquillian;
import java.io.File;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
@@ -115,7 +114,7 @@ public class CCMSuiteDeploymentExtension implements LoadableExtension {
SchemaManager schemaManager;
ClusterInitService clusterInitService = new ClusterInitService();
- List<StorageNode> nodes = Collections.emptyList();
+ List<StorageNode> nodes = null;
if (!Boolean.valueOf(System.getProperty("itest.use-external-storage-node", "false"))) {
@@ -148,7 +147,7 @@ public class CCMSuiteDeploymentExtension implements LoadableExtension {
}
} else {
try {
- String seed = System.getProperty("rhq.cassandra.seeds", "127.0.0.1|7199|9042");
+ String seed = System.getProperty("rhq.cassandra.seeds", "127.0.0.1|7299|9042");
schemaManager = new SchemaManager("rhqadmin", "rhqadmin", seed);
} catch (Exception e) {
commit 2355432987c8498e90cb0002f21079b1bc37300c
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Jul 31 13:36:33 2013 -0400
fix check for determining whether or not read repair is needed when adding nodes
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index ee0f406..1c96e27 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -866,19 +866,21 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
List<StorageNode> storageNodes = entityManager.createNamedQuery(StorageNode.QUERY_FIND_ALL_BY_MODE,
StorageNode.class).setParameter("operationMode", OperationMode.NORMAL).getResultList();
- int clusterSize = storageNodes.size();
+ // The previous cluster size will be the current size - 1 since we currently only
+ // support deploying one node at a time.
+ int previousClusterSize = storageNodes.size() - 1;
boolean isReadRepairNeeded;
- if (clusterSize >= 4) {
+ if (previousClusterSize >= 4) {
// At 4 nodes we increase the RF to 3. We are not increasing the RF beyond
// that for additional nodes; so, there is no need to run repair if we are
// expanding from a 4 node cluster since the RF remains the same.
isReadRepairNeeded = false;
- } else if (clusterSize == 1) {
+ } else if (previousClusterSize == 1) {
// The RF will increase since we are going from a single to a multi-node
// cluster; therefore, we want to run repair.
isReadRepairNeeded = true;
- } else if (clusterSize == 2) {
+ } else if (previousClusterSize == 2) {
if (storageNodes.size() > 3) {
// If we go from 2 to > 3 nodes we will increase the RF to 3; therefore
// we want to run repair.
@@ -888,7 +890,7 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
// to run repair.
isReadRepairNeeded = false;
}
- } else if (clusterSize == 3) {
+ } else if (previousClusterSize == 3) {
// We are increasing the cluster size > 3 which means the RF will be
// updated to 3; therefore, we want to run repair.
isReadRepairNeeded = true;
commit c1963ab6528a823f547814b8d0a574ce4552b15a
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 16:41:18 2013 -0400
update itest
diff --git a/modules/common/cassandra-ccm/cassandra-ccm-core/src/test/resources/expected.cassandra-jvm.properties b/modules/common/cassandra-ccm/cassandra-ccm-core/src/test/resources/expected.cassandra-jvm.properties
index 774a831..588fb3d 100644
--- a/modules/common/cassandra-ccm/cassandra-ccm-core/src/test/resources/expected.cassandra-jvm.properties
+++ b/modules/common/cassandra-ccm/cassandra-ccm-core/src/test/resources/expected.cassandra-jvm.properties
@@ -13,7 +13,7 @@ heap_new=-Xmn${HEAP_NEWSIZE}
heap_dump_on_OOMError="-XX:+HeapDumpOnOutOfMemoryError"
-heap_dump_dir=""
+heap_dump_dir=
thread_stack_size=-Xss180k
commit 6c7eeff519bdecacf17a3ccced43e7815e5d5dbe
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Wed Jul 31 11:05:43 2013 -0400
i)ensure jdk 6+ versions and ii)reuse userDN between the steps.
diff --git a/modules/helpers/ldap-tool/pom.xml b/modules/helpers/ldap-tool/pom.xml
index 770f22c..91416d5 100644
--- a/modules/helpers/ldap-tool/pom.xml
+++ b/modules/helpers/ldap-tool/pom.xml
@@ -60,6 +60,16 @@
</execution>
</executions>
</plugin>
+ <!-- Build for JDK 1.6 and later. -->
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.0.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
diff --git a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
index 89b0698..bc322ab 100644
--- a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
+++ b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
@@ -57,10 +57,10 @@ import javax.swing.border.TitledBorder;
* LDAP calls during auth/authz operations.
*
* The specific LDAP logic below needs to mirror the latest RHQ code and allow the user
- * to test our their configuration without requring a specific RHQ/JON build as a dependency.
+ * to test our their configuration without requiring a specific RHQ/JON build as a dependency.
*
* NOTE: To avoid a runtime dependency on specific versions of RHQ or JON, the small implementation
- * methods were copied into this class with minimatl changes for logging and ui messaging. The
+ * methods were copied into this class with minimal changes for logging and ui messaging. The
* definitive implementation for each 'copied' method can be found in LDAPGroupManagerBean.
*
* @author Simeon Pinder
@@ -85,6 +85,7 @@ public class TestLdapSettings extends JFrame {
private JMenuBar menuBar;
private String advdb = "**Verbose:debug ----";
private static final String BASEDN_DELIMITER = ";";
+ private String userDN;
private static final long serialVersionUID = 1L;
int textBoxWidth = 20;
@@ -419,7 +420,7 @@ public class TestLdapSettings extends JFrame {
}
// testing a valid user involves a filtered ldap search
// using the loginProperty, and optionally searchFilter
- String userDN = "";
+ userDN = "";
if (proceed) {
// default loginProperty to cn if it's not set
if (loginProperty.isEmpty()) {
@@ -895,9 +896,8 @@ public class TestLdapSettings extends JFrame {
// Load any search filter
String groupSearchFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
String groupMemberFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_MEMBER.getInternalName());
- String userDn = (String) systemConfig.get(Context.SECURITY_PRINCIPAL);
- String testUserDN = userDn;
+ String testUserDN = userDN;
String ldapServer = (String) systemConfig.get(Context.PROVIDER_URL);
Properties env = getProperties(ldapServer);
commit a29233274b8c623dc83073e97fb8a7a079ded87d
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Wed Jul 31 07:02:29 2013 -0400
i)Fix issue with authz validation in TestLdapSettings
ii)Remove old TestLdapSettings source.
diff --git a/etc/dev-utils/TestLdapSettings.java b/etc/dev-utils/TestLdapSettings.java
deleted file mode 100644
index 2e29b3d..0000000
--- a/etc/dev-utils/TestLdapSettings.java
+++ /dev/null
@@ -1,738 +0,0 @@
-package com.test;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-import javax.naming.CompositeName;
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.InitialLdapContext;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.border.LineBorder;
-import javax.swing.border.TitledBorder;
-
-//import org.rhq.enterprise.server.util.security.UntrustedSSLSocketFactory;
-
-/* Is a development test tool that allows the user to simulate the RHQ server side
- * LDAP calls during auth/authz operations.
- *
- * @author Simeon Pinder
- */
-public class TestLdapSettings extends JFrame {
- //shared fields
- private JTextArea testResults;
- private JCheckBox ssl;
- private JTextField testUserNameValue;
- private JTextField testUserPasswordValue;
- private HashMap<String, JTextField> fieldMappings;
- private String[] keys;
- private JCheckBox enableLdapReferral;
- private JCheckBox enableVerboseDebugging;
- private JCheckBox enableVerboseGroupParsing;
- private String advdb = "**Verbose:debug ----";
-
- private static final long serialVersionUID = 1L;
- int textBoxWidth = 20;
-
- public static void main(String args[]) {
- new TestLdapSettings();
- }
-
- // Configure window properties
- private TestLdapSettings() {
-
- setTitle("Check LDAP Settings: Simulates LDAP checks/queries of RHQ LDAP integration");
- getContentPane().setLayout(new BorderLayout());
- // top panel definition
- JPanel top = new JPanel();
- top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
- top.setBorder(LineBorder.createGrayLineBorder());
- keys = new String[] { "URL:", "Search Filter:",
- "Search Base:","Login Property",
- "Username:", "Group Search Filter:",
- "Password:", "Group Member Filter:",
- };
- fieldMappings = loadUiFields(top, keys);
-
- //add the two checkboxes for additiona debugging options
- enableLdapReferral= new JCheckBox("[follow] ldap referrals");
- enableLdapReferral.setSelected(false);
- enableVerboseDebugging= new JCheckBox("more verbose logging");
- enableVerboseDebugging.setSelected(false);
- enableVerboseGroupParsing= new JCheckBox("more detailed group parsing");
- enableVerboseGroupParsing.setSelected(false);
- //put into row display
- JPanel advancedDebugRegion = new JPanel();
- advancedDebugRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
- LineBorder advancedBorder = new LineBorder(Color.BLACK, 2);
- TitledBorder debugBorder = new TitledBorder(advancedBorder, "Debug:");
- advancedDebugRegion.setBorder(debugBorder);
- advancedDebugRegion.add(enableLdapReferral);
- advancedDebugRegion.add(enableVerboseDebugging);
- advancedDebugRegion.add(enableVerboseGroupParsing);
- top.add(advancedDebugRegion);
-
- ssl = new JCheckBox("SSL:");
- ssl.setEnabled(false);
- top.add(ssl);
- // test user auth region
- JPanel testUserRegion = new JPanel();
- testUserRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
- LineBorder border = new LineBorder(Color.BLUE, 2);
- TitledBorder tBorder = new TitledBorder(border, "Authentication/Authorization Check Credentials: (insert valid ldap user assigned to group)");
- testUserRegion.setBorder(tBorder);
- JLabel testUserName = new JLabel("Test UserName:");
- testUserNameValue = new JTextField(textBoxWidth);
- JLabel testUserPassword = new JLabel("Test Password:");
- testUserPasswordValue = new JTextField(textBoxWidth);
- testUserRegion.add(testUserName);
- testUserRegion.add(testUserNameValue);
- testUserRegion.add(testUserPassword);
- testUserRegion.add(testUserPasswordValue);
- top.add(testUserRegion);
-
- // center
- JPanel center = new JPanel();
- center.setLayout(new BoxLayout(center, BoxLayout.X_AXIS));
- // build center panel
- buildCenterPanel(center);
-
- // final component layout
- getContentPane().add(top, BorderLayout.NORTH);
- getContentPane().add(center, BorderLayout.CENTER);
- this.setSize(720, 700);
- addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- System.exit(0);
- }
- });
- setVisible(true);
- }
-
- // define the center display panel.
- private void buildCenterPanel(JPanel center) {
- // First element is Test Button
- JButton test = new JButton("Test Settings");
- center.add(test);
- // second is large text box that display ldap queries
- testResults = new JTextArea("(click button to test settings values: simulates 4 separate checks showing ldap filters used)",
- 40, 40);
- JScrollPane jsp = new JScrollPane(testResults);
- center.add(jsp);
- test.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- testResults.setText("");//clear out empty msg
- //trim spaces from all fields
- String ldapServer = fieldMappings.get(keys[0]).getText().trim();
- String searchFilter = fieldMappings.get(keys[1]).getText().trim();
- String searchBase = fieldMappings.get(keys[2]).getText().trim();
- String loginProperty = fieldMappings.get(keys[3]).getText().trim();
- String bindUserName = fieldMappings.get(keys[4]).getText().trim();
- String groupSearchFilter = fieldMappings.get(keys[5]).getText().trim();
- String bindPassword = fieldMappings.get(keys[6]).getText().trim();
- String groupMemberFilter = fieldMappings.get(keys[7]).getText().trim();
- String testUserName = testUserNameValue.getText().trim();
- String testUserPassword = testUserPasswordValue.getText().trim();
- // validate initial required elements
- String msg = null;
- boolean proceed = true;
- //valid required details set.
- if (ldapServer.isEmpty() || bindUserName.isEmpty()
- || bindPassword.isEmpty() || searchBase.isEmpty()) {
- msg ="STEP-1:FAIL: "+ keys[0] + ", " + keys[2] + ", " + keys[4] + ", "
- + keys[6] + " cannot be empty to proceed.";
- log(msg);
- proceed = false;
- }
- Properties env;
- InitialLdapContext ctx = null;
- if (proceed) {// attempt initial ldap bind from RHQ server
- msg = "STEP-1:TESTING: Attempting to bind to server:" + ldapServer
- + "\n with user '" + bindUserName
- + "' and password entered.";
- log(msg);
- env = getProperties(ldapServer);
- env.setProperty(Context.SECURITY_PRINCIPAL, bindUserName);
- env.setProperty(Context.SECURITY_CREDENTIALS, bindPassword);
- env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
- try {
- ctx = new InitialLdapContext(env, null);
- msg = "STEP-1:PASS: LDAP bind credentials are correct. Successfully connected to '"
- + ldapServer
- + "'.\n This means the LDAP Bind credentials for the RHQ Server authentication/authorization requests to ldap server "
- + "are correct.";
- if(enableVerboseDebugging.isSelected()){
- msg+="\n"+advdb+" LDAP simple authentication bind successful.";
- }
- log(msg);
- proceed = true;
- } catch (Exception ex) {
- msg = "STEP-1:FAIL: Unable to connect to the LDAP server with credentials specified.\n";
- msg+="Exception:"+ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed = false;
- }
- }
- if (proceed) {// retrieve test credentials to test run auth
- // load search controls
- SearchControls searchControls = getSearchControls();
- // validating searchFilter and test user/pass creds
- proceed = true;
- if (testUserName.isEmpty() || (testUserPassword.isEmpty())) {
- msg = "STEP-2:FAIL: Test Username/Password fields cannot be empty for this step.";
- log(msg);
- proceed = false;
- }
- // testing a valid user involves a filtered ldap search
- // using the loginProperty, and optionally searchFilter
- String userDN = "";
- if (proceed) {
- // default loginProperty to cn if it's not set
- if (loginProperty.isEmpty()) {
- loginProperty = "cn";
- if(enableVerboseDebugging.isSelected()){
- String mesg = "As you have not specified a login property, defaulting to 'cn'";
- log(advdb+" "+msg);
- }
- }
- String filter;
- if (!searchFilter.isEmpty()) {
- filter = "(&(" + loginProperty + "=" + testUserName
- + ")" + "(" + searchFilter + "))";
- } else {
- filter = "(" + loginProperty + "=" + testUserName
- + ")";
- }
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" The searchfilter is optionally appended to login property for additional shared attribute across users.");
- }
- msg = "STEP-2:TESTING: To validate the test user the following LDAP filtered component will be used to find matching users:\n";
- msg += filter;
- log(msg);
- // test out the search on the target ldap server
- try {
- String[] baseDNs = searchBase.split(";");
- for (int x = 0; x < baseDNs.length; x++) {
- NamingEnumeration answer = ctx.search(
- baseDNs[x], filter, searchControls);
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" this search was excuted against DN component '"+baseDNs[x]+"'.");
- }
- // boolean ldapApiNpeFound = false;
- if (!answer.hasMoreElements()) {
- msg="STEP-2:WARN Unable to locate a matching users for the filter'"+filter+
- "'. Please check your loginProperty. Usually 'cn' or 'uid'";
- log(msg);
- continue;
- }
- // Going with the first match
- SearchResult si = (SearchResult) answer.next();
-
- // Construct the UserDN
- userDN = null;
-
- try {
- userDN = si.getNameInNamespace();
- } catch (UnsupportedOperationException use) {
- userDN = new CompositeName(si.getName()).get(0);
- if (si.isRelative()) {
- userDN += "," + baseDNs[x];
- }
- }
-
- msg = "STEP-2:PASS: The test user '"
- + testUserName
- + "' was succesfully located, and the following userDN will be used in authorization check:\n";
- msg += userDN;
- log(msg);
-
- ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
- ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,testUserPassword);
- ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,"simple");
-
- // if successful then verified that user and pw
- // are valid ldap credentials
- ctx.reconnect(null);
- msg = "STEP-2:PASS: The user '"
- + testUserName
- + "' was succesfully authenticated using userDN '"
- + userDN + "' and password provided.\n"
- +"*Note: the loginProperty must match the loginProperty listed in dn: for the user. It is the DN that RHQ will lookup and use.";
- log(msg);
- }
- } catch (Exception ex) {
- msg = "STEP-2:FAIL: There was an error while searching for or authenticating the user '"
- + testUserName + "'\n";
- msg += ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed=false;
- }
- try {
- ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, bindUserName);
- ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, bindPassword);
- ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
- ctx.reconnect(null);
- } catch (Exception ex) {
- msg = "STEP-2:WARN: There was an error when switching back to the bind user '"
- + bindUserName + "'\n";
- msg += ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- }
-
- }
- // with authentication completed, now check authorization.
- // validate filter components to list all available groups
- proceed = false;
- if (!groupSearchFilter.isEmpty()) {
- Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
- String filter = String
- .format("(%s)", groupSearchFilter);
- msg = "STEP-3:TESTING: This ldap filter "
- + filter
- + " will be used to locate ALL available LDAP groups";
- log(msg);
- try {
- String[] baseDNs = searchBase.split(";");
- for (int x = 0; x < baseDNs.length; x++) {
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" this search was excuted against DN component '"+baseDNs[x]+"'.");
- }
- NamingEnumeration answer = ctx.search(
- baseDNs[x], filter, searchControls);
-
- if(enableVerboseGroupParsing.isSelected()){//in this mode report initial state of Enumeration results
- log(advdb+" LDAP Group Search/Enumeration found "+((answer.hasMore())? " SOME ":" NO ")+" matching group(s).");
- }
-
- boolean ldapApiEnumerationBugEncountered = false;
- while ((!ldapApiEnumerationBugEncountered)
- && answer.hasMoreElements()) {
- // We use the first match
- SearchResult si = null;
- try {
- si = (SearchResult) answer.next();
- } catch (NullPointerException npe) {
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" NullPtr exception detected. If known LDAP api enum npe ignore: "+npe.getMessage()+".");
- }
- ldapApiEnumerationBugEncountered = true;
- break;
- }
- Map<String, String> entry = new HashMap<String, String>();
- if(enableVerboseDebugging.isSelected()||enableVerboseGroupParsing.isSelected()){
- Attributes attributeContainer = si.getAttributes();
- NamingEnumeration<? extends Attribute> attributes = attributeContainer.getAll();
- String attributesReturned = " ";
- while(attributes.hasMore()){
- attributesReturned+=attributes.next().getID()+",";
- }
- String dbugMesg="\n"+advdb+" Group search LDAP ("+attributeContainer.size()+") attributes located for group '"+si.getName()+"' are ["+
- attributesReturned.substring(0, attributesReturned.length()-1)+"].";
- //directly update here to shorten messages for lots of groups
- testResults.setText(testResults.getText() + dbugMesg);
-
- //additionally parse attribute ids and values for illegal ldap characters
- if(enableVerboseGroupParsing.isSelected()){
- attributes = attributeContainer.getAll();
- String currentAttributeId ="";
- String currentValue ="";
- //spinder: 3/17/11: should we bail on first bad data or display them all?
- while(attributes.hasMore()){
- boolean badData = false;
- Attribute att = attributes.next();
- currentAttributeId =att.getID();
- if(containsIllegalLdap(currentAttributeId)){
- log(advdb+" LDAP Group: bad atrribute data detected for group '"+si.getName()+"' for attribute '"+currentAttributeId+"'.");
- badData=true;
- }
- if(att.getAll()!=null){
- NamingEnumeration<?> enumer = att.getAll();
- while(enumer.hasMore()){
- currentValue = enumer.next()+"";
- if(containsIllegalLdap(currentValue)){
- log(advdb+" LDAP Group: bad data detected for group '"+si.getName()+"' with attribute '"+currentAttributeId+"' and value:"+currentValue);
- badData=true;
- }
- }
- }
- if(badData){
- log(advdb+"** LDAP Group: Some bad LDAP data detected for group '"+si.getName()+"'.");
- }
- }
- }
- }
-
- Attribute commonNameAttr = si.getAttributes()
- .get("cn");
- if(commonNameAttr!=null){
- String name = (String) commonNameAttr.get();
- name = name.trim();
- Attribute desc = si.getAttributes().get(
- "description");
- String description = desc != null ? (String) desc
- .get()
- : "";
- description = description.trim();
- entry.put("id", name);
- entry.put("name", name);
- entry.put("description", description);
- ret.add(entry);
- }else{//unable to retrieve details for specific group.
- log(advdb+" There was an error retrieving 'cn' attribute for group '"+si.getName()+"'. Not adding to returned list of groups. ");
- }
- }
- }
- msg = "STEP-3:TESTING: Using Group Search Filter '" + filter
- + "', " + ret.size()
- + " ldap group(s) were located.\n";
- if (ret.size() > 0) {
- HashMap<String, String>[] ldapLists = new HashMap[ret.size()];
- ret.toArray(ldapLists);
- if(enableVerboseGroupParsing.isSelected()){//in this mode go beyond the first ten results.
- msg += "STEP-3:PASS: Listing 'all' of the ldap groups located: \n";
- for (int i = 0; i < ret.size(); i++) {
- msg += ldapLists[i] + "\n";
- }
- }else{//otherwise only show first 10[subset of available groups]
- msg += "STEP-3:PASS: Listing a few(<=10) of the ldap groups located: \n";
- for (int i = 0; (i < ret.size() && i < 10); i++) {
- msg += ldapLists[i] + "\n";
- }
- }
- proceed = true;// then can proceed to next step.
- }
- log(msg);
- } catch (Exception ex) {
- msg = "STEP-3:FAIL: There was an error searching with the groupFilter supplied: "
- + groupSearchFilter + "'\n";
- msg += ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed=false;
- }
- } else {
- msg = "STEP-3:FAIL: Group Search Filter: cannot be empty to proceed.";
- log(msg);
- proceed=false;
- }
- // retrieve lists of authorized groups available for the
- if (proceed) {
- // check groupMember
- if (!groupMemberFilter.isEmpty()) {
- Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
- String filter = String.format("(&(%s)(%s=%s))",
- groupSearchFilter, groupMemberFilter,
- LDAPStringUtil.encodeForFilter(userDN));
- msg = "STEP-4:TESTING: about to do ldap search with filter \n'"
- + filter
- + "'\n to locate groups that test user IS authorized to access.";
- log(msg);
- try {
- String[] baseDNs = searchBase.split(";");
- for (int x = 0; x < baseDNs.length; x++) {
- NamingEnumeration answer = ctx.search(
- baseDNs[x], filter, searchControls);
- boolean ldapApiEnumerationBugEncountered = false;
- //BZ:582471- ldap api bug change
- while ((!ldapApiEnumerationBugEncountered)
- && answer.hasMoreElements()) {
- // We use the first match
- SearchResult si = null;
- try {
- si = (SearchResult) answer.next();
- } catch (NullPointerException npe) {
- ldapApiEnumerationBugEncountered = true;
- break;
- }
- Map<String, String> entry = new HashMap<String, String>();
- String name = (String) si
- .getAttributes().get("cn")
- .get();
- name = name.trim();
- Attribute desc = si.getAttributes()
- .get("description");
- String description = desc != null ? (String) desc
- .get()
- : "";
- description = description.trim();
- entry.put("id", name);
- entry.put("name", name);
- entry.put("description", description);
- ret.add(entry);
- }
- }
- msg = "STEP-4:TESTING: Using Group Search Filter '"
- + filter + "', " + ret.size()
- + " ldap group(s) were located.\n";
- if (ret.size() > 0) {
- HashMap<String, String>[] ldapLists = new HashMap[ret
- .size()];
- ret.toArray(ldapLists);
- msg += "STEP-4:PASS: Listing a few of the ldap groups located: \n";
- // iterate over first ten or less to demonstrate retrieve
- for (int i = 0; (i < ret.size() && i < 10); i++) {
- msg += ldapLists[i] + "\n";
- }
- proceed = true;// then can proceed to next
- // step.
- }else{
- msg+="STEP-4:WARN: With current settings, test user is not authorized for any groups. Is this correct?";
- }
- log(msg);
- } catch (Exception ex) {
- msg = "STEP-4:FAIL: There was an error searching with the groupFilter supplied: "
- + groupSearchFilter + "'\n";
- msg += ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed=false;
- }
-
- } else {
- msg = "STEP-4:FAIL: Group Member Filter must be non-empty to proceed with simulating authorization check for test user.";
- log(msg);
- }
- }
- if(proceed){
- msg="COMPLETED:PASS: The current settings, for successful steps, should be correct to enter into your RHQ server.";
- msg+="\n\n\n\n When you encounter failures, warnings or other unexpected results you should use an external ";
- msg+="LDAP search utility to check that the generated filters return the expected LDAP results.";
- log(msg);
- }
- }
- }
-
- private boolean containsIllegalLdap(String currentValue) {
- boolean invalidData = false;
- if((currentValue!=null)&&(!currentValue.trim().isEmpty())){
- //TODO: spinder 3/17/11: need to figure out regex to filter/detect bad data in returned ldap. Giving up for now.
-// String regex = "(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)[/,+\"><;=#]|(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)\\(?!\\|[/,+\"><;=#]| $|(?<=^\\) )|^";
-// regex = "(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)[/,+\\\"><;=#]|(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)\\\\(?!\\\\|[/,+\\\"><;=#]| $|(?<=^\\\\) )|^";
-// System.out.println("++++++++ CURR VAL:"+currentValue+":INV-CHeck:"+currentValue.matches(",+\"\\<;\n=/")+":NEWCHECK:"+(currentValue.matches(regex)));
-// if(currentValue.matches(",+\"\\<;\n=/")){
-// invalidData=true;
-// }
-// String badList = ",+\"\\<;\n=";
- String badList = "+\"\\<;\n";
- for(char car :currentValue.toCharArray()){
- for(char c :badList.toCharArray()){
- if(car == c){
- invalidData=true;
- }
- }
- }
-
- }
- return invalidData;
- }
-
- private String appendStacktraceToMsg(String msg, Exception ex) {
- String moreVerbose = "";
- moreVerbose+=advdb+" Exception type:"+ex.getClass()+"\n";
- moreVerbose+=advdb+" Exception stack trace reference:"+ex.getStackTrace()+"\n";
- if(ex.getStackTrace()!=null){
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- ex.printStackTrace(pw);
- moreVerbose+=advdb+" stack trace reference:"+sw.toString();
- }
- msg+="\n"+moreVerbose;
- return msg;
- }
- });
- }
-
- // throw the label and fields together, two to a row.
- private HashMap<String, JTextField> loadUiFields(JPanel top,
- String[] componentKeys) {
- HashMap<String, JTextField> mappings = new HashMap<String, JTextField>();
- for (int i = 0; i < componentKeys.length; i++) {
- String firstLabelKey = componentKeys[i];
- String secondLabelKey = componentKeys[++i];
- // locate second key
- JPanel row = new JPanel();
- row.setLayout(new FlowLayout(FlowLayout.LEFT));
- JLabel label1 = new JLabel(firstLabelKey);
- label1.setSize(textBoxWidth, 5);
- JTextField value1 = new JTextField(textBoxWidth);
- JLabel label2 = new JLabel(secondLabelKey);
- JTextField value2 = new JTextField(textBoxWidth);
- row.add(label1);
- row.add(value1);
- row.add(Box.createRigidArea(new Dimension(0, 5)));
- row.add(label2);
- row.add(value2);
- mappings.put(firstLabelKey, value1);
- mappings.put(secondLabelKey, value2);
- top.add(row);
- }
-
- return mappings;
- }
-
- private Properties getProperties(String contentProvider) {
- Properties env = new Properties();
- env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.setProperty(Context.PROVIDER_URL, contentProvider);
- if(!enableLdapReferral.isSelected()){
- env.setProperty(Context.REFERRAL, "ignore");
- }else{
- String msg="**---- You have chosen to tell LDAP servers to [FOLLOW] context referrals. Default is [IGNORE] referrals. --**";
- log(msg);
- env.setProperty(Context.REFERRAL, "follow");
- }
-
-// // Setup SSL if requested
-// String protocol = ssl.isSelected()? "ssl":"";
-// if ((protocol != null) && protocol.equals("ssl")) {
-// String ldapSocketFactory = env
-// .getProperty("java.naming.ldap.factory.socket");
-// if (ldapSocketFactory == null) {
-// env.put("java.naming.ldap.factory.socket",
-// UntrustedSSLSocketFactory.class.getName());
-// }
-// env.put(Context.SECURITY_PROTOCOL, "ssl");
-// }
-
- return env;
- }
-
- private String delineate() {
- String line = "-";
- for (int i = 0; i < 30; i++) {
- line += "-";
- }
- return line;
- }
-
- private void log(String msg) {
- String message = "\n" + delineate() + "\n";
- message += msg;
- message += "\n" + delineate() + "\n\n";
- testResults.setText(testResults.getText() + message);
- }
-
- private SearchControls getSearchControls() {
- int scope = SearchControls.SUBTREE_SCOPE;
- int timeLimit = 0;
- long countLimit = 0;
- String[] returnedAttributes = null;
- boolean returnObject = false;
- boolean deference = false;
- SearchControls constraints = new SearchControls(scope, countLimit,
- timeLimit, returnedAttributes, returnObject, deference);
- return constraints;
- }
-}
-
-class LDAPStringUtil {
-
- /**
- * <p>Encode a string so that it can be used in an LDAP search filter.</p>
- *
- * <p>The following table shows the characters that are encoded and their
- * encoded version.</p>
- *
- * <table>
- * <tr><th align="center">Character</th><th>Encoded As</th></tr>
- * <tr><td align="center">*</td><td>\2a</td></tr>
- * <tr><td align="center">(</td><td>\28</td></tr>
- * <tr><td align="center">)</td><td>\29</td></tr>
- * <tr><td align="center">\</td><td>\5c</td></tr>
- * <tr><td align="center"><code>null</code></td><td>\00</td></tr>
- * </table>
- *
- * <p>In addition to encoding the above characters, any non-ASCII character
- * (any character with a hex value greater then <code>0x7f</code>) is also
- * encoded and rewritten as a UTF-8 character or sequence of characters in
- * hex notation.</p>
- *
- * @param filterString a string that is to be encoded
- * @return the encoded version of <code>filterString</code> suitable for use
- * in a LDAP search filter
- * @see <a href="http://tools.ietf.org/html/rfc4515">RFC 4515</a>
- */
- public static String encodeForFilter(final String filterString) {
- if (filterString != null && filterString.length() > 0) {
- StringBuilder encString = new StringBuilder(filterString.length());
- for (int i = 0; i < filterString.length(); i++) {
- char ch = filterString.charAt(i);
- switch (ch) {
- case '*': // encode a wildcard * character
- encString.append("\\2a");
- break;
- case '(': // encode a open parenthesis ( character
- encString.append("\\28");
- break;
- case ')': // encode a close parenthesis ) character
- encString.append("\\29");
- break;
- case '\\': // encode a backslash \ character
- encString.append("\\5c");
- break;
- case '\u0000': // encode a null character
- encString.append("\\00");
- break;
- default:
- if (ch <= 0x7f) { // an ASCII character
- encString.append(ch);
- } else if (ch >= 0x80) { // encode to UTF-8
- try {
- byte[] utf8bytes = String.valueOf(ch).getBytes("UTF8");
- for (byte b : utf8bytes) {
- encString.append(String.format("\\%02x", b));
- }
- } catch (UnsupportedEncodingException e) {
- // ignore
- }
- }
- }
- }
- return encString.toString();
- }
- return filterString;
- }
-
-}
-
diff --git a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
index f3aee15..89b0698 100644
--- a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
+++ b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
@@ -895,7 +895,9 @@ public class TestLdapSettings extends JFrame {
// Load any search filter
String groupSearchFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
String groupMemberFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_MEMBER.getInternalName());
- String testUserDN=userName;
+ String userDn = (String) systemConfig.get(Context.SECURITY_PRINCIPAL);
+
+ String testUserDN = userDn;
String ldapServer = (String) systemConfig.get(Context.PROVIDER_URL);
Properties env = getProperties(ldapServer);
commit 71cff59023452b91967370821cfa6aeee5a049d6
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Tue Jul 30 19:07:57 2013 -0400
Make warning to users more prominent and accessible.
diff --git a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
index b9667ea..f3aee15 100644
--- a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
+++ b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
@@ -39,6 +39,9 @@ import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
@@ -79,6 +82,7 @@ public class TestLdapSettings extends JFrame {
private JCheckBox iterativeVerboseLogging;
private JCheckBox enablePosixGroups;
private JCheckBox enable32xFeatures;
+ private JMenuBar menuBar;
private String advdb = "**Verbose:debug ----";
private static final String BASEDN_DELIMITER = ";";
@@ -95,10 +99,10 @@ public class TestLdapSettings extends JFrame {
final String warnMessage = "<html>***WARNING: Depending upon<br>" +
"i)how the ldap server is configured <br>" +
"ii)client query paging settings <br>" +
- " enabling <b>'more detailed logging'</b>,<br>" +
- " <b>'more group parsing'</b> AND/OR <b>'also log to console'</b> may cause the console to hang/freeze <br>" +
+ " enabling <b>'more verbose logging'</b>,<br>" +
+ " <b>'more detailed group parsing'</b> AND/OR <b>'also log to console'</b> may cause the console to hang/freeze <br>" +
" as the LDAP tool continues to parse large query results. If that occurs it is <br>" +
- " suggested that you kill tool and re-run with 'also log to console' so that the console logs<br>" +
+ " suggested that you stop this tool and re-run your queries with <b>'also log to console'</b> so that the console logs<br>" +
" will show which dataset is causing the delay and then you should modify your search|group|member<br>" +
" filters accordingly to <b>return smaller results</b> and/or <b>consume larger payloads</b>.<br>" +
"***WARNING</html>";
@@ -108,6 +112,12 @@ public class TestLdapSettings extends JFrame {
setTitle("Check LDAP Settings: Simulates LDAP checks/queries of RHQ LDAP integration");
getContentPane().setLayout(new BorderLayout());
+ menuBar = new JMenuBar();
+ JMenu menu = new JMenu("View ***Warning");
+ JMenuItem menuItem = new JMenuItem(warnMessage);
+ menu.add(menuItem);
+ menuBar.add(menu);
+ setJMenuBar(menuBar);
// top panel definition
top = new JPanel();
top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
commit 6363bfd5135c7444aa0798509bb53e6948b1b573
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Tue Jul 30 17:27:46 2013 -0400
Move under org.rhq like other components.
diff --git a/.classpath b/.classpath
index e5e7a21..386316a 100644
--- a/.classpath
+++ b/.classpath
@@ -215,6 +215,7 @@
<classpathentry kind="src" path="modules/plugins/rhq-storage/src/main/java"/>
<classpathentry kind="src" path="modules/enterprise/server/data-migration/src/main/java"/>
<classpathentry kind="src" path="modules/enterprise/server/data-migration/src/test/java"/>
+ <classpathentry kind="src" path="modules/helpers/ldap-tool/src/main/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/apache/httpcomponents/httpclient/4.2.3/httpclient-4.2.3.jar" sourcepath="M2_REPO/org/apache/httpcomponents/httpclient/4.2.3/httpclient-4.2.3-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-io/commons-io/2.1/commons-io-2.1.jar" sourcepath="M2_REPO/commons-io/commons-io/2.1/commons-io-2.1-sources.jar"/>
diff --git a/modules/helpers/ldap-tool/pom.xml b/modules/helpers/ldap-tool/pom.xml
index 28f8678..770f22c 100644
--- a/modules/helpers/ldap-tool/pom.xml
+++ b/modules/helpers/ldap-tool/pom.xml
@@ -19,6 +19,7 @@
<properties>
<executable.name>TestLdapSettings</executable.name>
<tool.version>1.0.1</tool.version>
+ <test.package>org.rhq</test.package>
</properties>
<name>RHQ ldap test tool</name>
@@ -33,8 +34,8 @@
<configuration>
<archive>
<manifest>
- <packageName>com.test</packageName>
- <mainClass>com.test.TestLdapSettings</mainClass>
+ <packageName>${test.package}</packageName>
+ <mainClass>${test.package}.TestLdapSettings</mainClass>
</manifest>
</archive>
</configuration>
diff --git a/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java
deleted file mode 100644
index 75ff277..0000000
--- a/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java
+++ /dev/null
@@ -1,1285 +0,0 @@
-package com.test;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.FlowLayout;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.ItemEvent;
-import java.awt.event.ItemListener;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-import javax.naming.Context;
-import javax.naming.NamingEnumeration;
-import javax.naming.NamingException;
-import javax.naming.directory.Attribute;
-import javax.naming.directory.Attributes;
-import javax.naming.directory.InvalidSearchFilterException;
-import javax.naming.directory.SearchControls;
-import javax.naming.directory.SearchResult;
-import javax.naming.ldap.Control;
-import javax.naming.ldap.InitialLdapContext;
-import javax.naming.ldap.PagedResultsControl;
-import javax.naming.ldap.PagedResultsResponseControl;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JPasswordField;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-import javax.swing.border.LineBorder;
-import javax.swing.border.TitledBorder;
-import javax.swing.event.ChangeEvent;
-import javax.swing.event.ChangeListener;
-
-//import org.rhq.enterprise.server.util.security.UntrustedSSLSocketFactory;
-
-/* Is a development test tool that allows the user to simulate the RHQ server side
- * LDAP calls during auth/authz operations.
- *
- * The specific LDAP logic below needs to mirror the latest RHQ code and allow the user
- * to test our their configuration without requring a specific RHQ/JON build as a dependency.
- *
- * NOTE: To avoid a runtime dependency on specific versions of RHQ or JON, the small implementation
- * methods were copied into this class with minimatl changes for logging and ui messaging. The
- * definitive implementation for each 'copied' method can be found in LDAPGroupManagerBean.
- *
- * @author Simeon Pinder
- */
-public class TestLdapSettings extends JFrame {
- //shared fields
- private JTextArea testResults;
- private JCheckBox showPasswords;
- private JCheckBox ssl;
- private JLabel groupPageSizeName;
- private JTextField groupMemberQueryValue;
- private JTextField testUserNameValue;
- private JTextField testUserPasswordValue;
- private HashMap<String, JTextField> fieldMappings;
- private String[] keys;
- private JCheckBox enableLdapReferral;
- private JCheckBox enableVerboseDebugging;
- private JCheckBox enableVerboseGroupParsing;
- private JCheckBox iterativeVerboseLogging;
- private JCheckBox enablePosixGroups;
- private JCheckBox enable32xFeatures;
- private String advdb = "**Verbose:debug ----";
- private static final String BASEDN_DELIMITER = ";";
-
- private static final long serialVersionUID = 1L;
- int textBoxWidth = 20;
- private static JPanel top = null;
- private static JPanel testUserRegion = null;
- private static Properties env=null;
-
- public static void main(String args[]) {
- new TestLdapSettings();
- }
- //After enabling support for Query parsing, we need to warn users of the effects.
- final String warnMessage = "<html>***WARNING: Depending upon<br>" +
- "i)how the ldap server is configured <br>" +
- "ii)client query paging settings <br>" +
- " enabling <b>'more detailed logging'</b>,<br>" +
- " <b>'more group parsing'</b> AND/OR <b>'also log to console'</b> may cause the console to hang/freeze <br>" +
- " as the LDAP tool continues to parse large query results. If that occurs it is <br>" +
- " suggested that you kill tool and re-run with 'also log to console' so that the console logs<br>" +
- " will show which dataset is causing the delay and then you should modify your search|group|member<br>" +
- " filters accordingly to <b>return smaller results</b> and/or <b>consume larger payloads</b>.<br>" +
- "***WARNING</html>";
-
- // Configure window properties
- private TestLdapSettings() {
-
- setTitle("Check LDAP Settings: Simulates LDAP checks/queries of RHQ LDAP integration");
- getContentPane().setLayout(new BorderLayout());
- // top panel definition
- top = new JPanel();
- top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
- top.setBorder(LineBorder.createGrayLineBorder());
- //define checkbox here as it's checked when generating UI.
- showPasswords = new JCheckBox("show passwords:");
- showPasswords.setSelected(false);
-
- keys = new String[] { "URL:", "Search Filter:",
- "Search Base:","Login Property",
- "Username:", "Group Search Filter:",
- "Password:", "Group Member Filter:",
- };
- fieldMappings = loadUiFields(top, keys);
-
- //add the two checkboxes for additiona debugging options
- enableLdapReferral= new JCheckBox("[follow] ldap referrals");
- enableLdapReferral.setSelected(false);
- enableVerboseDebugging= new JCheckBox("more verbose logging");
- enableVerboseDebugging.setSelected(false);
- enableVerboseDebugging.setToolTipText(warnMessage);
- enableVerboseGroupParsing= new JCheckBox("more detailed group parsing");
- enableVerboseGroupParsing.setSelected(false);
- enableVerboseGroupParsing.setToolTipText("*Take care when using this mode with a large number of groups* Every group discovered is parsed/listed.");
- iterativeVerboseLogging= new JCheckBox("also log to console");
- iterativeVerboseLogging.setSelected(false);
- iterativeVerboseLogging.setToolTipText("This mode is useful when the test tool is having difficulty returning results from large queries.");
- iterativeVerboseLogging.setToolTipText(warnMessage);
- enablePosixGroups= new JCheckBox("is Posix Group");
- enablePosixGroups.setSelected(false);
- enablePosixGroups.setEnabled(false);
-
- //put into 3.2.x functionality row
- JPanel jon32xRegion = new JPanel();
- jon32xRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
- LineBorder jon32xLineBorder = new LineBorder(Color.BLACK, 2);
- TitledBorder jon32xBorder = new TitledBorder(jon32xLineBorder, "JON 3.2.x/RHQ 4.8.x specific features:");
- jon32xRegion.setBorder(jon32xBorder);
- enable32xFeatures= new JCheckBox("enable JON 3.2.x/RHQ 4.8.x features");
- enable32xFeatures.setToolTipText("This enables features not available before RHQ 4.8.x/JON 3.2.x.");
- enable32xFeatures.setSelected(false);
- enable32xFeatures.addItemListener(new ItemListener() {
- @Override
- public void itemStateChanged(ItemEvent e) {
- if(enable32xFeatures.isSelected()){
- groupPageSizeName.setEnabled(true);
- groupMemberQueryValue.setEnabled(true);
- groupMemberQueryValue.setEditable(true);
- groupMemberQueryValue.setText("1000");
- enablePosixGroups.setEnabled(true);
- }else{
- groupMemberQueryValue.setText("");
- groupPageSizeName.setEnabled(false);
- groupMemberQueryValue.setEnabled(false);
- groupMemberQueryValue.setEditable(false);
- enablePosixGroups.setEnabled(false);
- enablePosixGroups.setSelected(false);
- }
- }
- });
-
- jon32xRegion.add(enable32xFeatures);
- groupPageSizeName = new JLabel("Group Query Page Size:");
- groupPageSizeName.setEnabled(false);
- groupMemberQueryValue = new JTextField(10);
- groupMemberQueryValue.setText("1000");
- groupMemberQueryValue.setEditable(false);
- jon32xRegion.add(groupPageSizeName);
- jon32xRegion.add(groupMemberQueryValue);
- jon32xRegion.add(enablePosixGroups);
- top.add(jon32xRegion);
-
- //put into row display
- JPanel advancedDebugRegion = new JPanel();
- advancedDebugRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
- LineBorder advancedBorder = new LineBorder(Color.BLACK, 2);
- TitledBorder debugBorder = new TitledBorder(advancedBorder, "Debug: **Warning --<hover HERE>**");
- advancedDebugRegion.setBorder(debugBorder);
- advancedDebugRegion.add(enableLdapReferral);
- advancedDebugRegion.add(enableVerboseDebugging);
- advancedDebugRegion.add(enableVerboseGroupParsing);
- advancedDebugRegion.add(iterativeVerboseLogging);
- advancedDebugRegion.setToolTipText(warnMessage);
- top.add(advancedDebugRegion);
-
- JPanel securityPanel = new JPanel();
- securityPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
- showPasswords.addItemListener(new ItemListener() {
- @Override
- public void itemStateChanged(ItemEvent e) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- //store off existing value
- String existingValue = "";
- String existingTestUserPass = "";
- JTextField current = fieldMappings.get("Password:");
- if(current instanceof JPasswordField){
- JPasswordField pass = ((JPasswordField)current);
- if(pass!=null){
- char[] charArray = pass.getPassword();
- if(charArray.length>0){
- existingValue = new String(charArray);
- }
- }
- }else{
- existingValue = current.getText();
- }
- //save off test user password as well
- if(testUserPasswordValue instanceof JPasswordField){
- JPasswordField pass = ((JPasswordField)testUserPasswordValue);
- if(pass!=null){
- char[] charArray = pass.getPassword();
- if(charArray.length>0){
- existingTestUserPass = new String(charArray);
- }
- }
- }else{
- existingTestUserPass=testUserPasswordValue.getText();
- }
-
- JTextField updatedContainer = null;
- if(showPasswords.isSelected()){
- updatedContainer = new JTextField(textBoxWidth);
- updatedContainer.setText(existingValue);
- testUserPasswordValue = new JTextField(textBoxWidth);
- testUserPasswordValue.setText(existingTestUserPass);
- }else{
- updatedContainer = new JPasswordField(textBoxWidth);
- updatedContainer.setText(existingValue);
- testUserPasswordValue = new JPasswordField(textBoxWidth);
- testUserPasswordValue.setText(existingTestUserPass);
- }
- //locate the JPanel and rebuild it Should be at index 3
- JPanel passwordRow = (JPanel) top.getComponent(3);
-// JTextField jf = (JTextField) passwordRow.getComponent(1);
- //store off existing components
- Component[] existing = new Component[passwordRow.getComponentCount()];
- for(int i=0; i<passwordRow.getComponentCount();i++){
- existing[i] = passwordRow.getComponent(i);
- }
- passwordRow.removeAll();
- for(int j=0;j<existing.length;j++){
- if(j==1){//insert new JTextField instead
- passwordRow.add(updatedContainer);
- }else{
- passwordRow.add(existing[j]);
- }
- }
- //reload testUserRegion
- //store off existing components
- Component[] existingTest = new Component[testUserRegion.getComponentCount()];
- for(int i=0; i<testUserRegion.getComponentCount();i++){
- existingTest[i] = testUserRegion.getComponent(i);
- }
- testUserRegion.removeAll();
- for(int j=0;j<existingTest.length;j++){
- if(j==3){//insert new JTextField instead
- testUserRegion.add(testUserPasswordValue);
- }else{
- testUserRegion.add(existingTest[j]);
- }
- }
-
- top.revalidate();
- top.repaint();
- }
- });
- }
- });
- securityPanel.add(showPasswords);
- ssl = new JCheckBox("SSL:");
- ssl.setEnabled(false);
- securityPanel.add(ssl);
- top.add(securityPanel);
-
- // test user auth region
- testUserRegion = new JPanel();
- testUserRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
- LineBorder border = new LineBorder(Color.BLUE, 2);
- TitledBorder tBorder = new TitledBorder(border, "Authentication/Authorization Check Credentials: (insert valid ldap user assigned to group)");
- testUserRegion.setBorder(tBorder);
- JLabel testUserName = new JLabel("Test UserName:");
- testUserNameValue = new JTextField(textBoxWidth);
- JLabel testUserPassword = new JLabel("Test Password:");
-// testUserPasswordValue = new JTextField(textBoxWidth);
- testUserPasswordValue = new JPasswordField(textBoxWidth);
- testUserRegion.add(testUserName);
- testUserRegion.add(testUserNameValue);
- testUserRegion.add(testUserPassword);
- testUserRegion.add(testUserPasswordValue);
- top.add(testUserRegion);
-
- // center
- JPanel center = new JPanel();
- center.setLayout(new BoxLayout(center, BoxLayout.X_AXIS));
- // build center panel
- buildCenterPanel(center);
-
- // final component layout
- getContentPane().add(top, BorderLayout.NORTH);
- getContentPane().add(center, BorderLayout.CENTER);
- this.setSize(720, 700);
- addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- System.exit(0);
- }
- });
- setVisible(true);
- }
-
- // define the center display panel.
- private void buildCenterPanel(JPanel center) {
- // First element is Test Button
- JButton test = new JButton("Test Settings");
- center.add(test);
- // second is large text box that display ldap queries
- testResults = new JTextArea("(click button to test settings values: simulates 4 separate checks showing ldap filters used)",
- 40, 40);
- JScrollPane jsp = new JScrollPane(testResults);
- center.add(jsp);
- test.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- testResults.setText("");//clear out empty msg
- //trim spaces from all fields
- String ldapServer = fieldMappings.get(keys[0]).getText().trim();
- String searchFilter = fieldMappings.get(keys[1]).getText().trim();
- String searchBase = fieldMappings.get(keys[2]).getText().trim();
- String loginProperty = fieldMappings.get(keys[3]).getText().trim();
- String bindUserName = fieldMappings.get(keys[4]).getText().trim();
- String groupSearchFilter = fieldMappings.get(keys[5]).getText().trim();
- String bindPassword = fieldMappings.get(keys[6]).getText().trim();
- String groupMemberFilter = fieldMappings.get(keys[7]).getText().trim();
- String groupMemberQuerySize = groupMemberQueryValue.getText().trim();
- String testUserName = testUserNameValue.getText().trim();
- String testUserPassword = testUserPasswordValue.getText().trim();
- // validate initial required elements
- String msg = null;
- boolean proceed = true;
- //valid required details set.
- if (ldapServer.isEmpty() || bindUserName.isEmpty()
- || bindPassword.isEmpty() || searchBase.isEmpty()) {
- msg ="STEP-1:FAIL: "+ keys[0] + ", " + keys[2] + ", " + keys[4] + ", "
- + keys[6] + " cannot be empty to proceed.";
- log(msg);
- proceed = false;
- }
- env = null;
- InitialLdapContext ctx = null;
- if (proceed) {// attempt initial ldap bind from RHQ server
- msg = "STEP-1:TESTING: Attempting to bind to server:" + ldapServer
- + "\n with user '" + bindUserName
- + "' and password entered.";
- log(msg);
- env = getProperties(ldapServer);
- env.setProperty(Context.SECURITY_PRINCIPAL, bindUserName);
- env.setProperty(Context.SECURITY_CREDENTIALS, bindPassword);
- env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
- //put the rest of the LDAP properties into the Properties instance for use later.
- //there still needs to be separate variables since some are for UI validation.
- env.setProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), groupSearchFilter);
- env.setProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), groupMemberFilter);
- env.setProperty(SystemSetting.LDAP_BASE_DN.getInternalName(), searchBase);
- env.setProperty(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName(), loginProperty);
- env.setProperty(SystemSetting.LDAP_BIND_DN.getInternalName(), bindUserName);
- env.setProperty(SystemSetting.LDAP_BIND_PW.getInternalName(), bindPassword);
- env.setProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.getInternalName(), groupMemberQuerySize);
-
- for(Object key :env.keySet()){
- System.out.println(key+"="+env.getProperty(key+""));
- }
-
- try {
- ctx = new InitialLdapContext(env, null);
- msg = "STEP-1:PASS: LDAP bind credentials are correct. Successfully connected to '"
- + ldapServer
- + "'.\n This means the LDAP Bind credentials for the RHQ Server authentication/authorization requests to ldap server "
- + "are correct.";
- if(enableVerboseDebugging.isSelected()){
- msg+="\n"+advdb+" LDAP simple authentication bind successful.";
- }
- log(msg);
- proceed = true;
- } catch (Exception ex) {
- msg = "STEP-1:FAIL: Unable to connect to the LDAP server with credentials specified.\n";
- msg+="Exception:"+ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed = false;
- }
- }
- if (proceed) {// retrieve test credentials to test run auth
- // load search controls
- SearchControls searchControls = getSearchControls();
- // validating searchFilter and test user/pass creds
- proceed = true;
- if (testUserName.isEmpty() || (testUserPassword.isEmpty())) {
- msg = "STEP-2:FAIL: Test Username/Password fields cannot be empty for this step.";
- log(msg);
- proceed = false;
- }
- // testing a valid user involves a filtered ldap search
- // using the loginProperty, and optionally searchFilter
- String userDN = "";
- if (proceed) {
- // default loginProperty to cn if it's not set
- if (loginProperty.isEmpty()) {
- loginProperty = "cn";
- if(enableVerboseDebugging.isSelected()){
- String mesg = "As you have not specified a login property, defaulting to 'cn'";
- log(advdb+" "+msg);
- }
- }
- String filter;
- if (!searchFilter.isEmpty()) {
- filter = "(&(" + loginProperty + "=" + testUserName
- + ")" + "(" + searchFilter + "))";
- } else {
- filter = "(" + loginProperty + "=" + testUserName
- + ")";
- }
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" The searchfilter is optionally appended to login property for additional shared attribute across users.");
- }
- msg = "STEP-2:TESTING: To validate the test user the following LDAP filtered component will be used to find matching users:\n";
- msg += filter;
- log(msg);
- // test out the search on the target ldap server
- try {
- String[] baseDNs = searchBase.split(";");
- for (int x = 0; x < baseDNs.length; x++) {
- NamingEnumeration answer = ctx.search(
- baseDNs[x], filter, searchControls);
- if(enableVerboseDebugging.isSelected()){
- log(advdb+" this search was excuted against DN component '"+baseDNs[x]+"'.");
- }
- // boolean ldapApiNpeFound = false;
- if (!answer.hasMoreElements()) {
- msg="STEP-2:WARN Unable to locate a matching users for the filter'"+filter+
- "'. Please check your loginProperty. Usually 'cn' or 'uid'";
- log(msg);
- continue;
- }
- // Going with the first match
- SearchResult si = (SearchResult) answer.next();
-
- // Construct the UserDN
- userDN = si.getName() + "," + baseDNs[x];
- msg = "STEP-2:PASS: The test user '"
- + testUserName
- + "' was succesfully located, and the following userDN will be used in authorization check:\n";
- msg += userDN;
- log(msg);
-
- ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
- ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,testUserPassword);
- ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,"simple");
-
- // if successful then verified that user and pw
- // are valid ldap credentials
- ctx.reconnect(null);
- msg = "STEP-2:PASS: The user '"
- + testUserName
- + "' was succesfully authenticated using userDN '"
- + userDN + "' and password provided.\n"
- +"*Note: the loginProperty must match the loginProperty listed in dn: for the user. It is the DN that RHQ will lookup and use.";
- log(msg);
- }
- } catch (Exception ex) {
- msg = "STEP-2:FAIL: There was an error while searching for or authenticating the user '"
- + testUserName + "'\n";
- msg += ex.getMessage();
- if(enableVerboseDebugging.isSelected()){
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- proceed=false;
- }
- }
- // with authentication completed, now check authorization.
- // validate filter components to list all available groups
- proceed = false;
- if (!groupSearchFilter.isEmpty()) {
- Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
- String filter = null;
-
- if (groupSearchFilter.startsWith("(") && groupSearchFilter.endsWith(")")){
- filter = groupSearchFilter; // RFC 2254 does not allow for ((expression))
- }else{
- filter = String
- .format("(%s)", groupSearchFilter);
- }
- msg = "STEP-3:TESTING: This ldap filter "
- + filter
- + " will be used to locate ALL available LDAP groups";
- log(msg);
-
- Properties systemConfig = populateProperties(env);
-
- ret = buildGroup(systemConfig, filter);
- msg = "STEP-3:TESTING: Using Group Search Filter '"
- + filter + "', " + ret.size()
- + " ldap group(s) were located.\n";
- if (ret.size() > 0) {
- HashMap<String, String>[] ldapLists = new HashMap[ret
- .size()];
- ret.toArray(ldapLists);
- // in this mode go beyond the first ten results.
- if (enableVerboseGroupParsing.isSelected()) {
- msg += "STEP-3:PASS: Listing 'all' of the ldap groups located: \n";
- for (int i = 0; i < ret.size(); i++) {
- msg += ldapLists[i] + "\n";
- }
- } else {// otherwise only show first 10[subset of
- // available groups]
- msg += "STEP-3:PASS: Listing a few(<=10) of the ldap groups located: \n";
- for (int i = 0; (i < ret.size() && i < 10); i++) {
- msg += ldapLists[i] + "\n";
- }
- }
- proceed = true;// then can proceed to next step.
- }
- log(msg);
- } else {
- msg = "STEP-3:FAIL: Group Search Filter: cannot be empty to proceed.";
- log(msg);
- proceed=false;
- }
- // retrieve lists of authorized groups available for the
- if (proceed) {
- // check groupMember
- if (!groupMemberFilter.isEmpty()) {
-// Map<String, String> userDetails = new HashMap<String, String>();
-// userDetails = findLdapUserDetails(userDN);
- Set<String> userDetails = findAvailableGroupsFor(testUserName);
-
- if(!userDetails.isEmpty()){
- proceed=true;
- }
- } else {
- msg = "STEP-4:FAIL: Group Member Filter must be non-empty to proceed with simulating authorization check for test user.";
- log(msg);
- }
- }
- if(proceed){
- msg="COMPLETED:PASS: The current settings, for successful steps, should be correct to enter into your RHQ server.";
- msg+="\n\n\n\n When you encounter failures, warnings or other unexpected results you should use an external ";
- msg+="LDAP search utility to check that the generated filters return the expected LDAP results.";
- log(msg);
- }
- }
- }
- });
- }
-
- private String appendStacktraceToMsg(String msg, Exception ex) {
- String moreVerbose = "";
- moreVerbose+=advdb+" Exception type:"+ex.getClass()+"\n";
- moreVerbose+=advdb+" Exception stack trace reference:"+ex.getStackTrace()+"\n";
- if(ex.getStackTrace()!=null){
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw);
- ex.printStackTrace(pw);
- moreVerbose+=advdb+" stack trace reference:"+sw.toString();
- }
- msg+="\n"+moreVerbose;
- return msg;
- }
-
- private boolean containsIllegalLdap(String currentValue) {
- boolean invalidData = false;
- if((currentValue!=null)&&(!currentValue.trim().isEmpty())){
- //TODO: spinder 3/17/11: need to figure out regex to filter/detect bad data in returned ldap. Giving up for now.
-// String regex = "(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)[/,+\"><;=#]|(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)\\(?!\\|[/,+\"><;=#]| $|(?<=^\\) )|^";
-// regex = "(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)[/,+\\\"><;=#]|(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)\\\\(?!\\\\|[/,+\\\"><;=#]| $|(?<=^\\\\) )|^";
-// System.out.println("++++++++ CURR VAL:"+currentValue+":INV-CHeck:"+currentValue.matches(",+\"\\<;\n=/")+":NEWCHECK:"+(currentValue.matches(regex)));
-// if(currentValue.matches(",+\"\\<;\n=/")){
-// invalidData=true;
-// }
-// String badList = ",+\"\\<;\n=";
- String badList = "+\"\\<;\n";
- for(char car :currentValue.toCharArray()){
- for(char c :badList.toCharArray()){
- if(car == c){
- invalidData=true;
- }
- }
- }
-
- }
- return invalidData;
- }
- /**
- * @throws NamingException
- * @see org.jboss.security.auth.spi.UsernamePasswordLoginModule#validatePassword(java.lang.String,java.lang.String)
- */
- protected Set<Map<String, String>> buildGroup(Properties systemConfig, String filter) {
- Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
- // Load our LDAP specific properties
- // Load the BaseDN
- String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
-
- // Load the LoginProperty
- String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
- if (loginProperty == null) {
- // Use the default
- loginProperty = "cn";
- }
- // Load any information we may need to bind
- String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
- String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
- if (bindDN != null) {
- systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
- systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
- systemConfig.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
- }
- try {
- InitialLdapContext ctx = new InitialLdapContext(systemConfig, null);
- SearchControls searchControls = getSearchControls();
- /*String filter = "(&(objectclass=groupOfUniqueNames)(uniqueMember=uid=" + userName
- + ",ou=People, dc=rhndev, dc=redhat, dc=com))";*/
-
- //modify the search control to only include the attributes we will use
- String[] attributes = { "cn", "description" };
- searchControls.setReturningAttributes(attributes);
-
- //BZ:964250: add rfc 2696
- //default to 1000 results. System setting page size from UI should be non-negative integer > 0.
- //additionally as system settings are modifiable via CLI which may not have param checking enabled do some
- //more checking.
- int defaultPageSize = 1000;
- // only if they're enabled in the UI.
- if (enable32xFeatures.isSelected()) {
- String groupPageSize = systemConfig.getProperty(
- SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE
- .getInternalName(), "" + defaultPageSize);
- if ((groupPageSize != null)
- && (!groupPageSize.trim().isEmpty())) {
- int passedInPageSize = -1;
- try {
- passedInPageSize = Integer
- .valueOf(groupPageSize.trim());
- if (passedInPageSize > 0) {
- defaultPageSize = passedInPageSize;
- if(enableVerboseDebugging.isSelected()){
- log(advdb
- + " LDAP Group Query Page Sizing of '"+defaultPageSize+"' is being requested from server.");
- }
- }
- } catch (NumberFormatException nfe) {
- // log issue and do nothing. Go with the default.
- String msg = "LDAP Group Page Size passed in '"
- + groupPageSize
- + "' in is invalid. Defaulting to 1000 results."
- + nfe.getMessage();
- log(msg);
- }
- }
- ctx.setRequestControls(new Control[] { new PagedResultsControl(
- defaultPageSize, Control.CRITICAL) });
- }
- // Loop through each configured base DN. It may be useful
- // in the future to allow for a filter to be configured for
- // each BaseDN, but for now the filter will apply to all.
- String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
-
- for (int x = 0; x < baseDNs.length; x++) {
- if (enableVerboseDebugging.isSelected()) {
- log(advdb
- + " this search was excuted against DN component '"
- + baseDNs[x] + "'.");
- }
- executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
-
- // continually parsing pages of results until we're done.
- // only if they're enabled in the UI.
- if (enable32xFeatures.isSelected()) {
- // handle paged results if they're being used here
- byte[] cookie = null;
- Control[] controls = ctx.getResponseControls();
- if (controls != null) {
- for (Control control : controls) {
- if (control instanceof PagedResultsResponseControl) {
- PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
- cookie = pagedResult.getCookie();
- }
- }
- }
-
- while (cookie != null) {
- String msg = "RFC 2696 is supported by the server and we are paging through the results. "+
- groupDetailsMap.size()+" results returned so far.";
- if(enableVerboseGroupParsing.isSelected()){
- log(advdb
- + msg);
- }
- // ensure the next requests contains the session/cookie
- // details
- ctx.setRequestControls(new Control[] { new PagedResultsControl(
- defaultPageSize, cookie, Control.CRITICAL) });
- executeGroupSearch(filter, groupDetailsMap, ctx,
- searchControls, baseDNs, x);
- // empty out cookie
- cookie = null;
- // test for further iterations
- controls = ctx.getResponseControls();
- if (controls != null) {
- for (Control control : controls) {
- if (control instanceof PagedResultsResponseControl) {
- PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
- cookie = pagedResult.getCookie();
- }
- }
- }
- }
- }
- }//end of for loop
- } catch (NamingException e) {
- if (e instanceof InvalidSearchFilterException) {
- InvalidSearchFilterException fException = (InvalidSearchFilterException) e;
- String message = "The ldap group filter defined is invalid ";
- log(message);
- }
- //TODO: check for ldap connection/unavailable/etc. exceptions.
- else {
- String mesg = "LDAP communication error: " + e.getMessage();
- log(mesg);
- }
- } catch (IOException iex) {
- String msg = "Unexpected LDAP communciation error:" + iex.getMessage();
- log(msg);
- }
-
- return groupDetailsMap;
- }
-
- /** Executes the LDAP group query using the filters, context and search controls, etc. parameters passed in.
- * The matching groups located during processing this pages of results are added as new entries to the
- * groupDetailsMap passed in.
- *
- * @param filter
- * @param groupDetailsMap
- * @param ctx
- * @param searchControls
- * @param baseDNs
- * @param x
- * @throws NamingException
- */
- private void executeGroupSearch(String filter, Set<Map<String, String>> groupDetailsMap, InitialLdapContext ctx,
- SearchControls searchControls, String[] baseDNs, int x) throws NamingException {
- //execute search based on controls and context passed in.
- NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
- boolean ldapApiEnumerationBugEncountered = false;
- while ((!ldapApiEnumerationBugEncountered) && answer.hasMoreElements()) {//BZ:582471- ldap api bug change
- // We use the first match
- SearchResult si = null;
- try {
- si = answer.next();
- } catch (NullPointerException npe) {
- if (enableVerboseDebugging.isSelected()) {
- log(advdb
- + " NullPtr exception detected. If known LDAP api enum npe ignore: "
- + npe.getMessage() + ".");
- }
- ldapApiEnumerationBugEncountered = true;
- break;
- }
-
- if (enableVerboseDebugging.isSelected()
- || enableVerboseGroupParsing.isSelected()) {
- Attributes attributeContainer = si.getAttributes();
- NamingEnumeration<? extends Attribute> attributes = attributeContainer
- .getAll();
- String attributesReturned = " ";
- while (attributes.hasMore()) {
- attributesReturned += attributes.next().getID() + ",";
- }
- String dbugMesg = "\n"
- + advdb
- + " Group search LDAP ("
- + attributeContainer.size()
- + ") attributes located for group '"
- + si.getName()
- + "' are ["
- + attributesReturned.substring(0,
- attributesReturned.length() - 1) + "].";
- // directly update here to shorten messages for lots of groups
- testResults.setText(testResults.getText() + dbugMesg);
- //This flag can be used in the unlikely case that the UI hangs during a test operation.:
- if(iterativeVerboseLogging.isSelected()){
- System.out.println(dbugMesg);
- }
-
- // additionally parse attribute ids and values for illegal ldap
- // characters
- if (enableVerboseGroupParsing.isSelected()) {
- attributes = attributeContainer.getAll();
- String currentAttributeId = "";
- String currentValue = "";
- // spinder: 3/17/11: should we bail on first bad data or
- // display them all?
- while (attributes.hasMore()) {
- boolean badData = false;
- Attribute att = attributes.next();
- currentAttributeId = att.getID();
- if (containsIllegalLdap(currentAttributeId)) {
- log(advdb
- + " LDAP Group: bad atrribute data detected for group '"
- + si.getName() + "' for attribute '"
- + currentAttributeId + "'.");
- badData = true;
- }
- if (att.getAll() != null) {
- NamingEnumeration<?> enumer = att.getAll();
- while (enumer.hasMore()) {
- currentValue = enumer.next() + "";
- if (containsIllegalLdap(currentValue)) {
- log(advdb
- + " LDAP Group: bad data detected for group '"
- + si.getName()
- + "' with attribute '"
- + currentAttributeId
- + "' and value:" + currentValue);
- badData = true;
- }
- }
- }
- if (badData) {
- log(advdb
- + "** LDAP Group: Some bad LDAP data detected for group '"
- + si.getName() + "'.");
- }
- }
- }
- }
-
- Map<String, String> entry = new HashMap<String, String>();
- // String name = (String) si.getAttributes().get("cn").get();
- Attribute commonNameAttr = si.getAttributes().get("cn");
- if (commonNameAttr != null) {
- String name = (String) commonNameAttr.get();
- name = name.trim();
- Attribute desc = si.getAttributes().get("description");
- String description = desc != null ? (String) desc.get() : "";
- description = description.trim();
- entry.put("id", name);
- entry.put("name", name);
- entry.put("description", description);
- groupDetailsMap.add(entry);
- } else {// unable to retrieve details for specific group.
- log(advdb
- + " There was an error retrieving 'cn' attribute for group '"
- + si.getName()
- + "'. Not adding to returned list of groups. ");
- }
- }
- }
-
- public Map<String, String> findLdapUserDetails(String userName) {
- // Load our LDAP specific properties
- Properties systemConfig = env;
- HashMap<String, String> userDetails = new HashMap<String, String>();
-
- // Load the BaseDN
- String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
-
- // Load the LoginProperty
- String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
- if (loginProperty == null) {
- // Use the default
- loginProperty = "cn";
- }
- // Load any information we may need to bind
- String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
- String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
-
- // Load any search filter
- String groupSearchFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
- String groupMemberFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_MEMBER.getInternalName());
- String testUserDN=userName;
- String ldapServer = (String) systemConfig.get(Context.PROVIDER_URL);
-
- Properties env = getProperties(ldapServer);
-
- if (bindDN != null) {
- env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
- env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
- env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
- }
-
- try {
- InitialLdapContext ctx = new InitialLdapContext(env, null);
- SearchControls searchControls = getSearchControls();
-
- String filter = String.format("(&(%s)(%s=%s))",
- groupSearchFilter, groupMemberFilter,
- testUserDN);
-
- generateUiLoggingForStep4LdapFilter(userName, filter);
-
- // Loop through each configured base DN. It may be useful
- // in the future to allow for a filter to be configured for
- // each BaseDN, but for now the filter will apply to all.
- String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
- for (int x = 0; x < baseDNs.length; x++) {
- NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
- if (!answer.hasMoreElements()) { //BZ:582471- ldap api bug change
- // Nothing found for this DN, move to the next one if we have one.
- continue;
- }
-
- // We use the first match
- SearchResult si = answer.next();
- //generate the DN
- String userDN = null;
- try {
- userDN = si.getNameInNamespace();
- } catch (UnsupportedOperationException use) {
- userDN = si.getName();
- if (userDN.startsWith("\"")) {
- userDN = userDN.substring(1, userDN.length());
- }
- if (userDN.endsWith("\"")) {
- userDN = userDN.substring(0, userDN.length() - 1);
- }
- userDN = userDN + "," + baseDNs[x];
- }
- userDetails.put("dn", userDN);
-
- // Construct the UserDN
- NamingEnumeration<String> keys = si.getAttributes().getIDs();
- while (keys.hasMore()) {
- String key = keys.next();
- Attribute value = si.getAttributes().get(key);
- if ((value != null) && (value.get() != null)) {
- userDetails.put(key, value.get().toString());
- }
- }
-// return userDetails;
- }//end of for loop
- generateUiLoggingStep4Authz(filter);
- return userDetails;
- } catch (Exception ex) {
- generateUiLoggingStep4Exception(ex);
- }
- return userDetails;
- }
-
- public Set<String> findAvailableGroupsFor(String userName) {
- // Load our LDAP specific properties
- Properties options = env;
- String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), "");
- String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), "");
- String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.getInternalName(), "false");
- if (groupUsePosix == null) {
- groupUsePosix = Boolean.toString(false);//default to false
- }
- boolean usePosixGroups = Boolean.valueOf(groupUsePosix);
- String userAttribute = getUserAttribute(options, userName, usePosixGroups);
- Set<String> ldapSet = new HashSet<String>();
-
- if (userAttribute != null && userAttribute.trim().length() > 0) {
- //TODO: spinder 4/21/10 put in error/debug logging messages for badly formatted filter combinations
- String filter = "";
- //form assumes examples where groupFilter is like 'objectclass=groupOfNames' and groupMember is 'member'
- // to produce ldap filter like (&(objectclass=groupOfNames)(member=cn=Administrator,ou=People,dc=test,dc=com))
- // or like (&(objectclass=groupOfNames)(memberUid=Administrator)) for posixGroups.
- filter = String.format("(&(%s)(%s=%s))", groupFilter, groupMember, encodeForFilter(userAttribute));
-
- Set<Map<String, String>> matched = buildGroup(options, filter);
-// log.trace("Located '" + matched.size() + "' LDAP groups for user '" + userName
-// + "' using following ldap filter '" + filter + "'.");
-
- //iterate to extract just the group names.
- for (Map<String, String> match : matched) {
- ldapSet.add(match.get("id"));
- }
- } else {
-// log.debug("Group lookup will not be performed due to no UserDN found for user " + userName);
- }
-
- return ldapSet;
- }
-
- private void generateUiLoggingStep4Exception(Exception ex) {
- String groupSearchFilter = env
- .getProperty(SystemSetting.LDAP_GROUP_FILTER
- .getInternalName());
- String msg = "STEP-4:FAIL: There was an error searching with the groupFilter supplied: "
- + groupSearchFilter + "'\n";
- msg += ex.getMessage();
- if (enableVerboseDebugging.isSelected()) {
- msg = appendStacktraceToMsg(msg, ex);
- }
- log(msg);
- }
-
- private void generateUiLoggingStep4Authz(String filter) {
- Set<Map<String, String>> groups = buildGroup(env, filter);
- String msg = "STEP-4:TESTING: Using Group Search Filter '"
- + filter + "', " + groups.size()
- + " ldap group(s) were located.\n";
- if (groups.size() > 0) {
- HashMap<String, String>[] ldapLists = new HashMap[groups
- .size()];
- groups.toArray(ldapLists);
- msg += "STEP-4:PASS: Listing a few of the ldap groups located: \n";
- // iterate over first ten or less to demonstrate retrieve
- for (int i = 0; (i < groups.size() && i < 10); i++) {
- msg += ldapLists[i] + "\n";
- }
- }else{
- msg+="STEP-4:WARN: With current settings, test user is not authorized for any groups. Is this correct?";
- }
- log(msg);
- }
-
- private void generateUiLoggingForStep4LdapFilter(String userName,
- String filter) {
- String msg = "STEP-4:TESTING: about to do ldap search with filter \n'"
- + filter
- + "'\n to locate groups that test user '"+userName+"' IS authorized to access.";
- log(msg);
- }
-
-
- // throw the label and fields together, two to a row.
- private HashMap<String, JTextField> loadUiFields(JPanel top,
- String[] componentKeys) {
- HashMap<String, JTextField> mappings = new HashMap<String, JTextField>();
- for (int i = 0; i < componentKeys.length; i++) {
- String firstLabelKey = componentKeys[i];
- String secondLabelKey = componentKeys[++i];
- // locate second key
- JPanel row = new JPanel();
- row.setLayout(new FlowLayout(FlowLayout.LEFT));
- JLabel label1 = new JLabel(firstLabelKey);
- label1.setSize(textBoxWidth, 5);
-// JTextField value1 = new JTextField(textBoxWidth);
- JTextField value1 = null;
- if (firstLabelKey.equalsIgnoreCase("Password:")&&(!showPasswords.isSelected())) {
- value1 = new JPasswordField(textBoxWidth);
- } else {
- value1 = new JTextField(textBoxWidth);
- }
- JLabel label2 = new JLabel(secondLabelKey);
- JTextField value2 = new JTextField(textBoxWidth);
- row.add(label1);
- row.add(value1);
- row.add(Box.createRigidArea(new Dimension(0, 5)));
- row.add(label2);
- row.add(value2);
- mappings.put(firstLabelKey, value1);
- mappings.put(secondLabelKey, value2);
- top.add(row);
- }
-
- return mappings;
- }
-
- private Properties getProperties(String contentProvider) {
- Properties env = new Properties();
- env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
- "com.sun.jndi.ldap.LdapCtxFactory");
- env.setProperty(Context.PROVIDER_URL, contentProvider);
- if(!enableLdapReferral.isSelected()){
- env.setProperty(Context.REFERRAL, "ignore");
- }else{
- String msg="**---- You have chosen to tell LDAP servers to [FOLLOW] context referrals. Default is [IGNORE] referrals. --**";
- log(msg);
- env.setProperty(Context.REFERRAL, "follow");
- }
-
-// // Setup SSL if requested
-// String protocol = ssl.isSelected()? "ssl":"";
-// if ((protocol != null) && protocol.equals("ssl")) {
-// String ldapSocketFactory = env
-// .getProperty("java.naming.ldap.factory.socket");
-// if (ldapSocketFactory == null) {
-// env.put("java.naming.ldap.factory.socket",
-// UntrustedSSLSocketFactory.class.getName());
-// }
-// env.put(Context.SECURITY_PROTOCOL, "ssl");
-// }
-
- return env;
- }
-
- private String delineate() {
- String line = "-";
- for (int i = 0; i < 30; i++) {
- line += "-";
- }
- return line;
- }
-
- /** Takes care of delineating messages and conditional logging contents passed in.
- * @param msg
- */
- private void log(String msg) {
- String message = "\n" + delineate() + "\n";
- message += msg;
- message += "\n" + delineate() + "\n\n";
- //This flag can be used in the unlikely case that the UI hangs during a test operation.:
- if(iterativeVerboseLogging.isSelected()){
- System.out.println(message);
- }
- testResults.setText(testResults.getText() + message);
- }
-
- private SearchControls getSearchControls() {
- int scope = SearchControls.SUBTREE_SCOPE;
- int timeLimit = 0;
- long countLimit = 0;
- String[] returnedAttributes = null;
- boolean returnObject = false;
- boolean deference = false;
- SearchControls constraints = new SearchControls(scope, countLimit,
- timeLimit, returnedAttributes, returnObject, deference);
- return constraints;
- }
-
- /** Translate SystemSettings to familiar Properties instance since we're
- * passing not one but multiple values.
- *
- * @param systemSettings
- * @return
- */
- private Properties populateProperties(Properties existing) {
- Properties properties = new Properties();
- if(existing!=null){
- properties = existing;
- }
- for (SystemSetting entry : SystemSetting.values()) {
- if(entry!=null){
- switch(entry){
- case LDAP_BASED_JAAS_PROVIDER:
- properties.put(entry.getInternalName(), "");
- break;
- }
- }
- }
- return properties;
- }
-
- /**Build/retrieve the user DN. Not usually a property.
- *
- * @param options
- * @param userName
- * @param usePosixGroups boolean indicating whether we search for groups with posixGroup format
- * @return
- */
- private String getUserAttribute(Properties options, String userName, boolean usePosixGroups) {
- Map<String, String> details = findLdapUserDetails(userName);
- String userAttribute = null;
- if (usePosixGroups) {//return just the username as posixGroup member search uses (&(%s)(memberUid=username))
- userAttribute = userName;
- } else {//this is the default where group search uses (&(%s)(uniqueMember={userDn}))
- userAttribute = details.get("dn");
- }
-
- return userAttribute;
- }
-
- /** See LDAPStringUtil.encodeForFilter() for original code/source/author/etc.
- * <p>Encode a string so that it can be used in an LDAP search filter.</p>
- *
- * <p>The following table shows the characters that are encoded and their
- * encoded version.</p>
- *
- * <table>
- * <tr><th align="center">Character</th><th>Encoded As</th></tr>
- * <tr><td align="center">*</td><td>\2a</td></tr>
- * <tr><td align="center">(</td><td>\28</td></tr>
- * <tr><td align="center">)</td><td>\29</td></tr>
- * <tr><td align="center">\</td><td>\5c</td></tr>
- * <tr><td align="center"><code>null</code></td><td>\00</td></tr>
- * </table>
- *
- * <p>In addition to encoding the above characters, any non-ASCII character
- * (any character with a hex value greater then <code>0x7f</code>) is also
- * encoded and rewritten as a UTF-8 character or sequence of characters in
- * hex notation.</p>
- *
- * @param filterString a string that is to be encoded
- * @return the encoded version of <code>filterString</code> suitable for use
- * in a LDAP search filter
- * @see <a href="http://tools.ietf.org/html/rfc4515">RFC 4515</a>
- */
- public static String encodeForFilter(final String filterString) {
- if (filterString != null && filterString.length() > 0) {
- StringBuilder encString = new StringBuilder(filterString.length());
- for (int i = 0; i < filterString.length(); i++) {
- char ch = filterString.charAt(i);
- switch (ch) {
- case '*': // encode a wildcard * character
- encString.append("\\2a");
- break;
- case '(': // encode a open parenthesis ( character
- encString.append("\\28");
- break;
- case ')': // encode a close parenthesis ) character
- encString.append("\\29");
- break;
- case '\\': // encode a backslash \ character
- encString.append("\\5c");
- break;
- case '\u0000': // encode a null character
- encString.append("\\00");
- break;
- default:
- if (ch <= 0x7f) { // an ASCII character
- encString.append(ch);
- } else if (ch >= 0x80) { // encode to UTF-8
- try {
- byte[] utf8bytes = String.valueOf(ch).getBytes("UTF8");
- for (byte b : utf8bytes) {
- encString.append(String.format("\\%02x", b));
- }
- } catch (UnsupportedEncodingException e) {
- // ignore
- }
- }
- }
- }
- return encString.toString();
- }
- return filterString;
- }
-}
-
-//Mock up the upgraded system properties approach to use SystemSetting
-enum SystemSetting {
- LDAP_BASED_JAAS_PROVIDER("CAM_JAAS_PROVIDER"),
- LDAP_NAMING_PROVIDER_URL("CAM_LDAP_NAMING_PROVIDER_URL"),
- USE_SSL_FOR_LDAP("CAM_LDAP_PROTOCOL"),
- LDAP_LOGIN_PROPERTY("CAM_LDAP_LOGIN_PROPERTY"),
- LDAP_FILTER("CAM_LDAP_FILTER"),
- LDAP_GROUP_FILTER("CAM_LDAP_GROUP_FILTER"),
- LDAP_GROUP_MEMBER("CAM_LDAP_GROUP_MEMBER"),
- LDAP_GROUP_QUERY_PAGE_SIZE("CAM_LDAP_GROUP_QUERY_PAGE_SIZE"),
- LDAP_BASE_DN("CAM_LDAP_BASE_DN"),
- LDAP_BIND_DN("CAM_LDAP_BIND_DN"),
- LDAP_BIND_PW("CAM_LDAP_BIND_PW"),
- LDAP_NAMING_FACTORY("CAM_LDAP_NAMING_FACTORY_INITIAL"),
- LDAP_GROUP_USE_POSIX("CAM_LDAP_GROUP_USE_POSIX"),
- ;
-
- private String internalName;
-
- private SystemSetting(String name) {
- this.internalName = name;
- }
-
- public String getInternalName() {
- return internalName;
- }
-
- public static SystemSetting getByInternalName(String internalName) {
- for (SystemSetting p : SystemSetting.values()) {
- if (p.internalName.equals(internalName)) {
- return p;
- }
- }
- return null;
- }
-}
-
-
diff --git a/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
new file mode 100644
index 0000000..b9667ea
--- /dev/null
+++ b/modules/helpers/ldap-tool/src/main/java/org/rhq/TestLdapSettings.java
@@ -0,0 +1,1277 @@
+package org.rhq;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.PagedResultsControl;
+import javax.naming.ldap.PagedResultsResponseControl;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.border.LineBorder;
+import javax.swing.border.TitledBorder;
+
+//import org.rhq.enterprise.server.util.security.UntrustedSSLSocketFactory;
+
+/* Is a development test tool that allows the user to simulate the RHQ server side
+ * LDAP calls during auth/authz operations.
+ *
+ * The specific LDAP logic below needs to mirror the latest RHQ code and allow the user
+ * to test our their configuration without requring a specific RHQ/JON build as a dependency.
+ *
+ * NOTE: To avoid a runtime dependency on specific versions of RHQ or JON, the small implementation
+ * methods were copied into this class with minimatl changes for logging and ui messaging. The
+ * definitive implementation for each 'copied' method can be found in LDAPGroupManagerBean.
+ *
+ * @author Simeon Pinder
+ */
+public class TestLdapSettings extends JFrame {
+ //shared fields
+ private JTextArea testResults;
+ private JCheckBox showPasswords;
+ private JCheckBox ssl;
+ private JLabel groupPageSizeName;
+ private JTextField groupMemberQueryValue;
+ private JTextField testUserNameValue;
+ private JTextField testUserPasswordValue;
+ private HashMap<String, JTextField> fieldMappings;
+ private String[] keys;
+ private JCheckBox enableLdapReferral;
+ private JCheckBox enableVerboseDebugging;
+ private JCheckBox enableVerboseGroupParsing;
+ private JCheckBox iterativeVerboseLogging;
+ private JCheckBox enablePosixGroups;
+ private JCheckBox enable32xFeatures;
+ private String advdb = "**Verbose:debug ----";
+ private static final String BASEDN_DELIMITER = ";";
+
+ private static final long serialVersionUID = 1L;
+ int textBoxWidth = 20;
+ private static JPanel top = null;
+ private static JPanel testUserRegion = null;
+ private static Properties env=null;
+
+ public static void main(String args[]) {
+ new TestLdapSettings();
+ }
+ //After enabling support for Query parsing, we need to warn users of the effects.
+ final String warnMessage = "<html>***WARNING: Depending upon<br>" +
+ "i)how the ldap server is configured <br>" +
+ "ii)client query paging settings <br>" +
+ " enabling <b>'more detailed logging'</b>,<br>" +
+ " <b>'more group parsing'</b> AND/OR <b>'also log to console'</b> may cause the console to hang/freeze <br>" +
+ " as the LDAP tool continues to parse large query results. If that occurs it is <br>" +
+ " suggested that you kill tool and re-run with 'also log to console' so that the console logs<br>" +
+ " will show which dataset is causing the delay and then you should modify your search|group|member<br>" +
+ " filters accordingly to <b>return smaller results</b> and/or <b>consume larger payloads</b>.<br>" +
+ "***WARNING</html>";
+
+ // Configure window properties
+ private TestLdapSettings() {
+
+ setTitle("Check LDAP Settings: Simulates LDAP checks/queries of RHQ LDAP integration");
+ getContentPane().setLayout(new BorderLayout());
+ // top panel definition
+ top = new JPanel();
+ top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
+ top.setBorder(LineBorder.createGrayLineBorder());
+ //define checkbox here as it's checked when generating UI.
+ showPasswords = new JCheckBox("show passwords:");
+ showPasswords.setSelected(false);
+
+ keys = new String[] { "URL:", "Search Filter:",
+ "Search Base:","Login Property",
+ "Username:", "Group Search Filter:",
+ "Password:", "Group Member Filter:",
+ };
+ fieldMappings = loadUiFields(top, keys);
+
+ //add the two checkboxes for additiona debugging options
+ enableLdapReferral= new JCheckBox("[follow] ldap referrals");
+ enableLdapReferral.setSelected(false);
+ enableVerboseDebugging= new JCheckBox("more verbose logging");
+ enableVerboseDebugging.setSelected(false);
+ enableVerboseDebugging.setToolTipText(warnMessage);
+ enableVerboseGroupParsing= new JCheckBox("more detailed group parsing");
+ enableVerboseGroupParsing.setSelected(false);
+ enableVerboseGroupParsing.setToolTipText("*Take care when using this mode with a large number of groups* Every group discovered is parsed/listed.");
+ iterativeVerboseLogging= new JCheckBox("also log to console");
+ iterativeVerboseLogging.setSelected(false);
+ iterativeVerboseLogging.setToolTipText("This mode is useful when the test tool is having difficulty returning results from large queries.");
+ iterativeVerboseLogging.setToolTipText(warnMessage);
+ enablePosixGroups= new JCheckBox("is Posix Group");
+ enablePosixGroups.setSelected(false);
+ enablePosixGroups.setEnabled(false);
+
+ //put into 3.2.x functionality row
+ JPanel jon32xRegion = new JPanel();
+ jon32xRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder jon32xLineBorder = new LineBorder(Color.BLACK, 2);
+ TitledBorder jon32xBorder = new TitledBorder(jon32xLineBorder, "JON 3.2.x/RHQ 4.8.x specific features:");
+ jon32xRegion.setBorder(jon32xBorder);
+ enable32xFeatures= new JCheckBox("enable JON 3.2.x/RHQ 4.8.x features");
+ enable32xFeatures.setToolTipText("This enables features not available before RHQ 4.8.x/JON 3.2.x.");
+ enable32xFeatures.setSelected(false);
+ enable32xFeatures.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if(enable32xFeatures.isSelected()){
+ groupPageSizeName.setEnabled(true);
+ groupMemberQueryValue.setEnabled(true);
+ groupMemberQueryValue.setEditable(true);
+ groupMemberQueryValue.setText("1000");
+ enablePosixGroups.setEnabled(true);
+ }else{
+ groupMemberQueryValue.setText("");
+ groupPageSizeName.setEnabled(false);
+ groupMemberQueryValue.setEnabled(false);
+ groupMemberQueryValue.setEditable(false);
+ enablePosixGroups.setEnabled(false);
+ enablePosixGroups.setSelected(false);
+ }
+ }
+ });
+
+ jon32xRegion.add(enable32xFeatures);
+ groupPageSizeName = new JLabel("Group Query Page Size:");
+ groupPageSizeName.setEnabled(false);
+ groupMemberQueryValue = new JTextField(10);
+ groupMemberQueryValue.setText("1000");
+ groupMemberQueryValue.setEditable(false);
+ jon32xRegion.add(groupPageSizeName);
+ jon32xRegion.add(groupMemberQueryValue);
+ jon32xRegion.add(enablePosixGroups);
+ top.add(jon32xRegion);
+
+ //put into row display
+ JPanel advancedDebugRegion = new JPanel();
+ advancedDebugRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder advancedBorder = new LineBorder(Color.BLACK, 2);
+ TitledBorder debugBorder = new TitledBorder(advancedBorder, "Debug: **Warning --<hover HERE>**");
+ advancedDebugRegion.setBorder(debugBorder);
+ advancedDebugRegion.add(enableLdapReferral);
+ advancedDebugRegion.add(enableVerboseDebugging);
+ advancedDebugRegion.add(enableVerboseGroupParsing);
+ advancedDebugRegion.add(iterativeVerboseLogging);
+ advancedDebugRegion.setToolTipText(warnMessage);
+ top.add(advancedDebugRegion);
+
+ JPanel securityPanel = new JPanel();
+ securityPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+ showPasswords.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ //store off existing value
+ String existingValue = "";
+ String existingTestUserPass = "";
+ JTextField current = fieldMappings.get("Password:");
+ if(current instanceof JPasswordField){
+ JPasswordField pass = ((JPasswordField)current);
+ if(pass!=null){
+ char[] charArray = pass.getPassword();
+ if(charArray.length>0){
+ existingValue = new String(charArray);
+ }
+ }
+ }else{
+ existingValue = current.getText();
+ }
+ //save off test user password as well
+ if(testUserPasswordValue instanceof JPasswordField){
+ JPasswordField pass = ((JPasswordField)testUserPasswordValue);
+ if(pass!=null){
+ char[] charArray = pass.getPassword();
+ if(charArray.length>0){
+ existingTestUserPass = new String(charArray);
+ }
+ }
+ }else{
+ existingTestUserPass=testUserPasswordValue.getText();
+ }
+
+ JTextField updatedContainer = null;
+ if(showPasswords.isSelected()){
+ updatedContainer = new JTextField(textBoxWidth);
+ updatedContainer.setText(existingValue);
+ testUserPasswordValue = new JTextField(textBoxWidth);
+ testUserPasswordValue.setText(existingTestUserPass);
+ }else{
+ updatedContainer = new JPasswordField(textBoxWidth);
+ updatedContainer.setText(existingValue);
+ testUserPasswordValue = new JPasswordField(textBoxWidth);
+ testUserPasswordValue.setText(existingTestUserPass);
+ }
+ //locate the JPanel and rebuild it Should be at index 3
+ JPanel passwordRow = (JPanel) top.getComponent(3);
+// JTextField jf = (JTextField) passwordRow.getComponent(1);
+ //store off existing components
+ Component[] existing = new Component[passwordRow.getComponentCount()];
+ for(int i=0; i<passwordRow.getComponentCount();i++){
+ existing[i] = passwordRow.getComponent(i);
+ }
+ passwordRow.removeAll();
+ for(int j=0;j<existing.length;j++){
+ if(j==1){//insert new JTextField instead
+ passwordRow.add(updatedContainer);
+ }else{
+ passwordRow.add(existing[j]);
+ }
+ }
+ //reload testUserRegion
+ //store off existing components
+ Component[] existingTest = new Component[testUserRegion.getComponentCount()];
+ for(int i=0; i<testUserRegion.getComponentCount();i++){
+ existingTest[i] = testUserRegion.getComponent(i);
+ }
+ testUserRegion.removeAll();
+ for(int j=0;j<existingTest.length;j++){
+ if(j==3){//insert new JTextField instead
+ testUserRegion.add(testUserPasswordValue);
+ }else{
+ testUserRegion.add(existingTest[j]);
+ }
+ }
+
+ top.revalidate();
+ top.repaint();
+ }
+ });
+ }
+ });
+ securityPanel.add(showPasswords);
+ ssl = new JCheckBox("SSL:");
+ ssl.setEnabled(false);
+ securityPanel.add(ssl);
+ top.add(securityPanel);
+
+ // test user auth region
+ testUserRegion = new JPanel();
+ testUserRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder border = new LineBorder(Color.BLUE, 2);
+ TitledBorder tBorder = new TitledBorder(border, "Authentication/Authorization Check Credentials: (insert valid ldap user assigned to group)");
+ testUserRegion.setBorder(tBorder);
+ JLabel testUserName = new JLabel("Test UserName:");
+ testUserNameValue = new JTextField(textBoxWidth);
+ JLabel testUserPassword = new JLabel("Test Password:");
+// testUserPasswordValue = new JTextField(textBoxWidth);
+ testUserPasswordValue = new JPasswordField(textBoxWidth);
+ testUserRegion.add(testUserName);
+ testUserRegion.add(testUserNameValue);
+ testUserRegion.add(testUserPassword);
+ testUserRegion.add(testUserPasswordValue);
+ top.add(testUserRegion);
+
+ // center
+ JPanel center = new JPanel();
+ center.setLayout(new BoxLayout(center, BoxLayout.X_AXIS));
+ // build center panel
+ buildCenterPanel(center);
+
+ // final component layout
+ getContentPane().add(top, BorderLayout.NORTH);
+ getContentPane().add(center, BorderLayout.CENTER);
+ this.setSize(720, 700);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+ });
+ setVisible(true);
+ }
+
+ // define the center display panel.
+ private void buildCenterPanel(JPanel center) {
+ // First element is Test Button
+ JButton test = new JButton("Test Settings");
+ center.add(test);
+ // second is large text box that display ldap queries
+ testResults = new JTextArea("(click button to test settings values: simulates 4 separate checks showing ldap filters used)",
+ 40, 40);
+ JScrollPane jsp = new JScrollPane(testResults);
+ center.add(jsp);
+ test.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ testResults.setText("");//clear out empty msg
+ //trim spaces from all fields
+ String ldapServer = fieldMappings.get(keys[0]).getText().trim();
+ String searchFilter = fieldMappings.get(keys[1]).getText().trim();
+ String searchBase = fieldMappings.get(keys[2]).getText().trim();
+ String loginProperty = fieldMappings.get(keys[3]).getText().trim();
+ String bindUserName = fieldMappings.get(keys[4]).getText().trim();
+ String groupSearchFilter = fieldMappings.get(keys[5]).getText().trim();
+ String bindPassword = fieldMappings.get(keys[6]).getText().trim();
+ String groupMemberFilter = fieldMappings.get(keys[7]).getText().trim();
+ String groupMemberQuerySize = groupMemberQueryValue.getText().trim();
+ String testUserName = testUserNameValue.getText().trim();
+ String testUserPassword = testUserPasswordValue.getText().trim();
+ // validate initial required elements
+ String msg = null;
+ boolean proceed = true;
+ //valid required details set.
+ if (ldapServer.isEmpty() || bindUserName.isEmpty()
+ || bindPassword.isEmpty() || searchBase.isEmpty()) {
+ msg ="STEP-1:FAIL: "+ keys[0] + ", " + keys[2] + ", " + keys[4] + ", "
+ + keys[6] + " cannot be empty to proceed.";
+ log(msg);
+ proceed = false;
+ }
+ env = null;
+ InitialLdapContext ctx = null;
+ if (proceed) {// attempt initial ldap bind from RHQ server
+ msg = "STEP-1:TESTING: Attempting to bind to server:" + ldapServer
+ + "\n with user '" + bindUserName
+ + "' and password entered.";
+ log(msg);
+ env = getProperties(ldapServer);
+ env.setProperty(Context.SECURITY_PRINCIPAL, bindUserName);
+ env.setProperty(Context.SECURITY_CREDENTIALS, bindPassword);
+ env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ //put the rest of the LDAP properties into the Properties instance for use later.
+ //there still needs to be separate variables since some are for UI validation.
+ env.setProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), groupSearchFilter);
+ env.setProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), groupMemberFilter);
+ env.setProperty(SystemSetting.LDAP_BASE_DN.getInternalName(), searchBase);
+ env.setProperty(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName(), loginProperty);
+ env.setProperty(SystemSetting.LDAP_BIND_DN.getInternalName(), bindUserName);
+ env.setProperty(SystemSetting.LDAP_BIND_PW.getInternalName(), bindPassword);
+ env.setProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.getInternalName(), groupMemberQuerySize);
+
+ try {
+ ctx = new InitialLdapContext(env, null);
+ msg = "STEP-1:PASS: LDAP bind credentials are correct. Successfully connected to '"
+ + ldapServer
+ + "'.\n This means the LDAP Bind credentials for the RHQ Server authentication/authorization requests to ldap server "
+ + "are correct.";
+ if(enableVerboseDebugging.isSelected()){
+ msg+="\n"+advdb+" LDAP simple authentication bind successful.";
+ }
+ log(msg);
+ proceed = true;
+ } catch (Exception ex) {
+ msg = "STEP-1:FAIL: Unable to connect to the LDAP server with credentials specified.\n";
+ msg+="Exception:"+ex.getMessage();
+ if(enableVerboseDebugging.isSelected()){
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ proceed = false;
+ }
+ }
+ if (proceed) {// retrieve test credentials to test run auth
+ // load search controls
+ SearchControls searchControls = getSearchControls();
+ // validating searchFilter and test user/pass creds
+ proceed = true;
+ if (testUserName.isEmpty() || (testUserPassword.isEmpty())) {
+ msg = "STEP-2:FAIL: Test Username/Password fields cannot be empty for this step.";
+ log(msg);
+ proceed = false;
+ }
+ // testing a valid user involves a filtered ldap search
+ // using the loginProperty, and optionally searchFilter
+ String userDN = "";
+ if (proceed) {
+ // default loginProperty to cn if it's not set
+ if (loginProperty.isEmpty()) {
+ loginProperty = "cn";
+ if(enableVerboseDebugging.isSelected()){
+ String mesg = "As you have not specified a login property, defaulting to 'cn'";
+ log(advdb+" "+msg);
+ }
+ }
+ String filter;
+ if (!searchFilter.isEmpty()) {
+ filter = "(&(" + loginProperty + "=" + testUserName
+ + ")" + "(" + searchFilter + "))";
+ } else {
+ filter = "(" + loginProperty + "=" + testUserName
+ + ")";
+ }
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb+" The searchfilter is optionally appended to login property for additional shared attribute across users.");
+ }
+ msg = "STEP-2:TESTING: To validate the test user the following LDAP filtered component will be used to find matching users:\n";
+ msg += filter;
+ log(msg);
+ // test out the search on the target ldap server
+ try {
+ String[] baseDNs = searchBase.split(";");
+ for (int x = 0; x < baseDNs.length; x++) {
+ NamingEnumeration answer = ctx.search(
+ baseDNs[x], filter, searchControls);
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb+" this search was excuted against DN component '"+baseDNs[x]+"'.");
+ }
+ // boolean ldapApiNpeFound = false;
+ if (!answer.hasMoreElements()) {
+ msg="STEP-2:WARN Unable to locate a matching users for the filter'"+filter+
+ "'. Please check your loginProperty. Usually 'cn' or 'uid'";
+ log(msg);
+ continue;
+ }
+ // Going with the first match
+ SearchResult si = (SearchResult) answer.next();
+
+ // Construct the UserDN
+ userDN = si.getName() + "," + baseDNs[x];
+ msg = "STEP-2:PASS: The test user '"
+ + testUserName
+ + "' was succesfully located, and the following userDN will be used in authorization check:\n";
+ msg += userDN;
+ log(msg);
+
+ ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
+ ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,testUserPassword);
+ ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,"simple");
+
+ // if successful then verified that user and pw
+ // are valid ldap credentials
+ ctx.reconnect(null);
+ msg = "STEP-2:PASS: The user '"
+ + testUserName
+ + "' was succesfully authenticated using userDN '"
+ + userDN + "' and password provided.\n"
+ +"*Note: the loginProperty must match the loginProperty listed in dn: for the user. It is the DN that RHQ will lookup and use.";
+ log(msg);
+ }
+ } catch (Exception ex) {
+ msg = "STEP-2:FAIL: There was an error while searching for or authenticating the user '"
+ + testUserName + "'\n";
+ msg += ex.getMessage();
+ if(enableVerboseDebugging.isSelected()){
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ proceed=false;
+ }
+ }
+ // with authentication completed, now check authorization.
+ // validate filter components to list all available groups
+ proceed = false;
+ if (!groupSearchFilter.isEmpty()) {
+ Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
+ String filter = null;
+
+ if (groupSearchFilter.startsWith("(") && groupSearchFilter.endsWith(")")){
+ filter = groupSearchFilter; // RFC 2254 does not allow for ((expression))
+ }else{
+ filter = String
+ .format("(%s)", groupSearchFilter);
+ }
+ msg = "STEP-3:TESTING: This ldap filter "
+ + filter
+ + " will be used to locate ALL available LDAP groups";
+ log(msg);
+
+ Properties systemConfig = populateProperties(env);
+
+ ret = buildGroup(systemConfig, filter);
+ msg = "STEP-3:TESTING: Using Group Search Filter '"
+ + filter + "', " + ret.size()
+ + " ldap group(s) were located.\n";
+ if (ret.size() > 0) {
+ HashMap<String, String>[] ldapLists = new HashMap[ret
+ .size()];
+ ret.toArray(ldapLists);
+ // in this mode go beyond the first ten results.
+ if (enableVerboseGroupParsing.isSelected()) {
+ msg += "STEP-3:PASS: Listing 'all' of the ldap groups located: \n";
+ for (int i = 0; i < ret.size(); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ } else {// otherwise only show first 10[subset of
+ // available groups]
+ msg += "STEP-3:PASS: Listing a few(<=10) of the ldap groups located: \n";
+ for (int i = 0; (i < ret.size() && i < 10); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ }
+ proceed = true;// then can proceed to next step.
+ }
+ log(msg);
+ } else {
+ msg = "STEP-3:FAIL: Group Search Filter: cannot be empty to proceed.";
+ log(msg);
+ proceed=false;
+ }
+ // retrieve lists of authorized groups available for the
+ if (proceed) {
+ // check groupMember
+ if (!groupMemberFilter.isEmpty()) {
+// Map<String, String> userDetails = new HashMap<String, String>();
+// userDetails = findLdapUserDetails(userDN);
+ Set<String> userDetails = findAvailableGroupsFor(testUserName);
+
+ if(!userDetails.isEmpty()){
+ proceed=true;
+ }
+ } else {
+ msg = "STEP-4:FAIL: Group Member Filter must be non-empty to proceed with simulating authorization check for test user.";
+ log(msg);
+ }
+ }
+ if(proceed){
+ msg="COMPLETED:PASS: The current settings, for successful steps, should be correct to enter into your RHQ server.";
+ msg+="\n\n\n\n When you encounter failures, warnings or other unexpected results you should use an external ";
+ msg+="LDAP search utility to check that the generated filters return the expected LDAP results.";
+ log(msg);
+ }
+ }
+ }
+ });
+ }
+
+ private String appendStacktraceToMsg(String msg, Exception ex) {
+ String moreVerbose = "";
+ moreVerbose+=advdb+" Exception type:"+ex.getClass()+"\n";
+ moreVerbose+=advdb+" Exception stack trace reference:"+ex.getStackTrace()+"\n";
+ if(ex.getStackTrace()!=null){
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ ex.printStackTrace(pw);
+ moreVerbose+=advdb+" stack trace reference:"+sw.toString();
+ }
+ msg+="\n"+moreVerbose;
+ return msg;
+ }
+
+ private boolean containsIllegalLdap(String currentValue) {
+ boolean invalidData = false;
+ if((currentValue!=null)&&(!currentValue.trim().isEmpty())){
+ //TODO: spinder 3/17/11: need to figure out regex to filter/detect bad data in returned ldap. Giving up for now.
+// String regex = "(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)[/,+\"><;=#]|(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)\\(?!\\|[/,+\"><;=#]| $|(?<=^\\) )|^";
+// regex = "(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)[/,+\\\"><;=#]|(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)\\\\(?!\\\\|[/,+\\\"><;=#]| $|(?<=^\\\\) )|^";
+// System.out.println("++++++++ CURR VAL:"+currentValue+":INV-CHeck:"+currentValue.matches(",+\"\\<;\n=/")+":NEWCHECK:"+(currentValue.matches(regex)));
+// if(currentValue.matches(",+\"\\<;\n=/")){
+// invalidData=true;
+// }
+// String badList = ",+\"\\<;\n=";
+ String badList = "+\"\\<;\n";
+ for(char car :currentValue.toCharArray()){
+ for(char c :badList.toCharArray()){
+ if(car == c){
+ invalidData=true;
+ }
+ }
+ }
+
+ }
+ return invalidData;
+ }
+ /**
+ * @throws NamingException
+ * @see org.jboss.security.auth.spi.UsernamePasswordLoginModule#validatePassword(java.lang.String,java.lang.String)
+ */
+ protected Set<Map<String, String>> buildGroup(Properties systemConfig, String filter) {
+ Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
+ // Load our LDAP specific properties
+ // Load the BaseDN
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+
+ // Load the LoginProperty
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ if (loginProperty == null) {
+ // Use the default
+ loginProperty = "cn";
+ }
+ // Load any information we may need to bind
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+ if (bindDN != null) {
+ systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ systemConfig.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ }
+ try {
+ InitialLdapContext ctx = new InitialLdapContext(systemConfig, null);
+ SearchControls searchControls = getSearchControls();
+ /*String filter = "(&(objectclass=groupOfUniqueNames)(uniqueMember=uid=" + userName
+ + ",ou=People, dc=rhndev, dc=redhat, dc=com))";*/
+
+ //modify the search control to only include the attributes we will use
+ String[] attributes = { "cn", "description" };
+ searchControls.setReturningAttributes(attributes);
+
+ //BZ:964250: add rfc 2696
+ //default to 1000 results. System setting page size from UI should be non-negative integer > 0.
+ //additionally as system settings are modifiable via CLI which may not have param checking enabled do some
+ //more checking.
+ int defaultPageSize = 1000;
+ // only if they're enabled in the UI.
+ if (enable32xFeatures.isSelected()) {
+ String groupPageSize = systemConfig.getProperty(
+ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE
+ .getInternalName(), "" + defaultPageSize);
+ if ((groupPageSize != null)
+ && (!groupPageSize.trim().isEmpty())) {
+ int passedInPageSize = -1;
+ try {
+ passedInPageSize = Integer
+ .valueOf(groupPageSize.trim());
+ if (passedInPageSize > 0) {
+ defaultPageSize = passedInPageSize;
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb
+ + " LDAP Group Query Page Sizing of '"+defaultPageSize+"' is being requested from server.");
+ }
+ }
+ } catch (NumberFormatException nfe) {
+ // log issue and do nothing. Go with the default.
+ String msg = "LDAP Group Page Size passed in '"
+ + groupPageSize
+ + "' in is invalid. Defaulting to 1000 results."
+ + nfe.getMessage();
+ log(msg);
+ }
+ }
+ ctx.setRequestControls(new Control[] { new PagedResultsControl(
+ defaultPageSize, Control.CRITICAL) });
+ }
+ // Loop through each configured base DN. It may be useful
+ // in the future to allow for a filter to be configured for
+ // each BaseDN, but for now the filter will apply to all.
+ String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
+
+ for (int x = 0; x < baseDNs.length; x++) {
+ if (enableVerboseDebugging.isSelected()) {
+ log(advdb
+ + " this search was excuted against DN component '"
+ + baseDNs[x] + "'.");
+ }
+ executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
+
+ // continually parsing pages of results until we're done.
+ // only if they're enabled in the UI.
+ if (enable32xFeatures.isSelected()) {
+ // handle paged results if they're being used here
+ byte[] cookie = null;
+ Control[] controls = ctx.getResponseControls();
+ if (controls != null) {
+ for (Control control : controls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
+ cookie = pagedResult.getCookie();
+ }
+ }
+ }
+
+ while (cookie != null) {
+ String msg = "RFC 2696 is supported by the server and we are paging through the results. "+
+ groupDetailsMap.size()+" results returned so far.";
+ if(enableVerboseGroupParsing.isSelected()){
+ log(advdb
+ + msg);
+ }
+ // ensure the next requests contains the session/cookie
+ // details
+ ctx.setRequestControls(new Control[] { new PagedResultsControl(
+ defaultPageSize, cookie, Control.CRITICAL) });
+ executeGroupSearch(filter, groupDetailsMap, ctx,
+ searchControls, baseDNs, x);
+ // empty out cookie
+ cookie = null;
+ // test for further iterations
+ controls = ctx.getResponseControls();
+ if (controls != null) {
+ for (Control control : controls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
+ cookie = pagedResult.getCookie();
+ }
+ }
+ }
+ }
+ }
+ }//end of for loop
+ } catch (NamingException e) {
+ if (e instanceof InvalidSearchFilterException) {
+ InvalidSearchFilterException fException = (InvalidSearchFilterException) e;
+ String message = "The ldap group filter defined is invalid ";
+ log(message);
+ }
+ //TODO: check for ldap connection/unavailable/etc. exceptions.
+ else {
+ String mesg = "LDAP communication error: " + e.getMessage();
+ log(mesg);
+ }
+ } catch (IOException iex) {
+ String msg = "Unexpected LDAP communciation error:" + iex.getMessage();
+ log(msg);
+ }
+
+ return groupDetailsMap;
+ }
+
+ /** Executes the LDAP group query using the filters, context and search controls, etc. parameters passed in.
+ * The matching groups located during processing this pages of results are added as new entries to the
+ * groupDetailsMap passed in.
+ *
+ * @param filter
+ * @param groupDetailsMap
+ * @param ctx
+ * @param searchControls
+ * @param baseDNs
+ * @param x
+ * @throws NamingException
+ */
+ private void executeGroupSearch(String filter, Set<Map<String, String>> groupDetailsMap, InitialLdapContext ctx,
+ SearchControls searchControls, String[] baseDNs, int x) throws NamingException {
+ //execute search based on controls and context passed in.
+ NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
+ boolean ldapApiEnumerationBugEncountered = false;
+ while ((!ldapApiEnumerationBugEncountered) && answer.hasMoreElements()) {//BZ:582471- ldap api bug change
+ // We use the first match
+ SearchResult si = null;
+ try {
+ si = answer.next();
+ } catch (NullPointerException npe) {
+ if (enableVerboseDebugging.isSelected()) {
+ log(advdb
+ + " NullPtr exception detected. If known LDAP api enum npe ignore: "
+ + npe.getMessage() + ".");
+ }
+ ldapApiEnumerationBugEncountered = true;
+ break;
+ }
+
+ if (enableVerboseDebugging.isSelected()
+ || enableVerboseGroupParsing.isSelected()) {
+ Attributes attributeContainer = si.getAttributes();
+ NamingEnumeration<? extends Attribute> attributes = attributeContainer
+ .getAll();
+ String attributesReturned = " ";
+ while (attributes.hasMore()) {
+ attributesReturned += attributes.next().getID() + ",";
+ }
+ String dbugMesg = "\n"
+ + advdb
+ + " Group search LDAP ("
+ + attributeContainer.size()
+ + ") attributes located for group '"
+ + si.getName()
+ + "' are ["
+ + attributesReturned.substring(0,
+ attributesReturned.length() - 1) + "].";
+ // directly update here to shorten messages for lots of groups
+ testResults.setText(testResults.getText() + dbugMesg);
+ //This flag can be used in the unlikely case that the UI hangs during a test operation.:
+ if(iterativeVerboseLogging.isSelected()){
+ System.out.println(dbugMesg);
+ }
+
+ // additionally parse attribute ids and values for illegal ldap
+ // characters
+ if (enableVerboseGroupParsing.isSelected()) {
+ attributes = attributeContainer.getAll();
+ String currentAttributeId = "";
+ String currentValue = "";
+ // spinder: 3/17/11: should we bail on first bad data or
+ // display them all?
+ while (attributes.hasMore()) {
+ boolean badData = false;
+ Attribute att = attributes.next();
+ currentAttributeId = att.getID();
+ if (containsIllegalLdap(currentAttributeId)) {
+ log(advdb
+ + " LDAP Group: bad atrribute data detected for group '"
+ + si.getName() + "' for attribute '"
+ + currentAttributeId + "'.");
+ badData = true;
+ }
+ if (att.getAll() != null) {
+ NamingEnumeration<?> enumer = att.getAll();
+ while (enumer.hasMore()) {
+ currentValue = enumer.next() + "";
+ if (containsIllegalLdap(currentValue)) {
+ log(advdb
+ + " LDAP Group: bad data detected for group '"
+ + si.getName()
+ + "' with attribute '"
+ + currentAttributeId
+ + "' and value:" + currentValue);
+ badData = true;
+ }
+ }
+ }
+ if (badData) {
+ log(advdb
+ + "** LDAP Group: Some bad LDAP data detected for group '"
+ + si.getName() + "'.");
+ }
+ }
+ }
+ }
+
+ Map<String, String> entry = new HashMap<String, String>();
+ // String name = (String) si.getAttributes().get("cn").get();
+ Attribute commonNameAttr = si.getAttributes().get("cn");
+ if (commonNameAttr != null) {
+ String name = (String) commonNameAttr.get();
+ name = name.trim();
+ Attribute desc = si.getAttributes().get("description");
+ String description = desc != null ? (String) desc.get() : "";
+ description = description.trim();
+ entry.put("id", name);
+ entry.put("name", name);
+ entry.put("description", description);
+ groupDetailsMap.add(entry);
+ } else {// unable to retrieve details for specific group.
+ log(advdb
+ + " There was an error retrieving 'cn' attribute for group '"
+ + si.getName()
+ + "'. Not adding to returned list of groups. ");
+ }
+ }
+ }
+
+ public Map<String, String> findLdapUserDetails(String userName) {
+ // Load our LDAP specific properties
+ Properties systemConfig = env;
+ HashMap<String, String> userDetails = new HashMap<String, String>();
+
+ // Load the BaseDN
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+
+ // Load the LoginProperty
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ if (loginProperty == null) {
+ // Use the default
+ loginProperty = "cn";
+ }
+ // Load any information we may need to bind
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+
+ // Load any search filter
+ String groupSearchFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
+ String groupMemberFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_MEMBER.getInternalName());
+ String testUserDN=userName;
+ String ldapServer = (String) systemConfig.get(Context.PROVIDER_URL);
+
+ Properties env = getProperties(ldapServer);
+
+ if (bindDN != null) {
+ env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ }
+
+ try {
+ InitialLdapContext ctx = new InitialLdapContext(env, null);
+ SearchControls searchControls = getSearchControls();
+
+ String filter = String.format("(&(%s)(%s=%s))",
+ groupSearchFilter, groupMemberFilter,
+ testUserDN);
+
+ generateUiLoggingForStep4LdapFilter(userName, filter);
+
+ // Loop through each configured base DN. It may be useful
+ // in the future to allow for a filter to be configured for
+ // each BaseDN, but for now the filter will apply to all.
+ String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
+ for (int x = 0; x < baseDNs.length; x++) {
+ NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
+ if (!answer.hasMoreElements()) { //BZ:582471- ldap api bug change
+ // Nothing found for this DN, move to the next one if we have one.
+ continue;
+ }
+
+ // We use the first match
+ SearchResult si = answer.next();
+ //generate the DN
+ String userDN = null;
+ try {
+ userDN = si.getNameInNamespace();
+ } catch (UnsupportedOperationException use) {
+ userDN = si.getName();
+ if (userDN.startsWith("\"")) {
+ userDN = userDN.substring(1, userDN.length());
+ }
+ if (userDN.endsWith("\"")) {
+ userDN = userDN.substring(0, userDN.length() - 1);
+ }
+ userDN = userDN + "," + baseDNs[x];
+ }
+ userDetails.put("dn", userDN);
+
+ // Construct the UserDN
+ NamingEnumeration<String> keys = si.getAttributes().getIDs();
+ while (keys.hasMore()) {
+ String key = keys.next();
+ Attribute value = si.getAttributes().get(key);
+ if ((value != null) && (value.get() != null)) {
+ userDetails.put(key, value.get().toString());
+ }
+ }
+// return userDetails;
+ }//end of for loop
+ generateUiLoggingStep4Authz(filter);
+ return userDetails;
+ } catch (Exception ex) {
+ generateUiLoggingStep4Exception(ex);
+ }
+ return userDetails;
+ }
+
+ public Set<String> findAvailableGroupsFor(String userName) {
+ // Load our LDAP specific properties
+ Properties options = env;
+ String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), "");
+ String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), "");
+ String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.getInternalName(), "false");
+ if (groupUsePosix == null) {
+ groupUsePosix = Boolean.toString(false);//default to false
+ }
+ boolean usePosixGroups = Boolean.valueOf(groupUsePosix);
+ String userAttribute = getUserAttribute(options, userName, usePosixGroups);
+ Set<String> ldapSet = new HashSet<String>();
+
+ if (userAttribute != null && userAttribute.trim().length() > 0) {
+ //TODO: spinder 4/21/10 put in error/debug logging messages for badly formatted filter combinations
+ String filter = "";
+ //form assumes examples where groupFilter is like 'objectclass=groupOfNames' and groupMember is 'member'
+ // to produce ldap filter like (&(objectclass=groupOfNames)(member=cn=Administrator,ou=People,dc=test,dc=com))
+ // or like (&(objectclass=groupOfNames)(memberUid=Administrator)) for posixGroups.
+ filter = String.format("(&(%s)(%s=%s))", groupFilter, groupMember, encodeForFilter(userAttribute));
+
+ Set<Map<String, String>> matched = buildGroup(options, filter);
+// log.trace("Located '" + matched.size() + "' LDAP groups for user '" + userName
+// + "' using following ldap filter '" + filter + "'.");
+
+ //iterate to extract just the group names.
+ for (Map<String, String> match : matched) {
+ ldapSet.add(match.get("id"));
+ }
+ } else {
+// log.debug("Group lookup will not be performed due to no UserDN found for user " + userName);
+ }
+
+ return ldapSet;
+ }
+
+ private void generateUiLoggingStep4Exception(Exception ex) {
+ String groupSearchFilter = env
+ .getProperty(SystemSetting.LDAP_GROUP_FILTER
+ .getInternalName());
+ String msg = "STEP-4:FAIL: There was an error searching with the groupFilter supplied: "
+ + groupSearchFilter + "'\n";
+ msg += ex.getMessage();
+ if (enableVerboseDebugging.isSelected()) {
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ }
+
+ private void generateUiLoggingStep4Authz(String filter) {
+ Set<Map<String, String>> groups = buildGroup(env, filter);
+ String msg = "STEP-4:TESTING: Using Group Search Filter '"
+ + filter + "', " + groups.size()
+ + " ldap group(s) were located.\n";
+ if (groups.size() > 0) {
+ HashMap<String, String>[] ldapLists = new HashMap[groups
+ .size()];
+ groups.toArray(ldapLists);
+ msg += "STEP-4:PASS: Listing a few of the ldap groups located: \n";
+ // iterate over first ten or less to demonstrate retrieve
+ for (int i = 0; (i < groups.size() && i < 10); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ }else{
+ msg+="STEP-4:WARN: With current settings, test user is not authorized for any groups. Is this correct?";
+ }
+ log(msg);
+ }
+
+ private void generateUiLoggingForStep4LdapFilter(String userName,
+ String filter) {
+ String msg = "STEP-4:TESTING: about to do ldap search with filter \n'"
+ + filter
+ + "'\n to locate groups that test user '"+userName+"' IS authorized to access.";
+ log(msg);
+ }
+
+
+ // throw the label and fields together, two to a row.
+ private HashMap<String, JTextField> loadUiFields(JPanel top,
+ String[] componentKeys) {
+ HashMap<String, JTextField> mappings = new HashMap<String, JTextField>();
+ for (int i = 0; i < componentKeys.length; i++) {
+ String firstLabelKey = componentKeys[i];
+ String secondLabelKey = componentKeys[++i];
+ // locate second key
+ JPanel row = new JPanel();
+ row.setLayout(new FlowLayout(FlowLayout.LEFT));
+ JLabel label1 = new JLabel(firstLabelKey);
+ label1.setSize(textBoxWidth, 5);
+// JTextField value1 = new JTextField(textBoxWidth);
+ JTextField value1 = null;
+ if (firstLabelKey.equalsIgnoreCase("Password:")&&(!showPasswords.isSelected())) {
+ value1 = new JPasswordField(textBoxWidth);
+ } else {
+ value1 = new JTextField(textBoxWidth);
+ }
+ JLabel label2 = new JLabel(secondLabelKey);
+ JTextField value2 = new JTextField(textBoxWidth);
+ row.add(label1);
+ row.add(value1);
+ row.add(Box.createRigidArea(new Dimension(0, 5)));
+ row.add(label2);
+ row.add(value2);
+ mappings.put(firstLabelKey, value1);
+ mappings.put(secondLabelKey, value2);
+ top.add(row);
+ }
+
+ return mappings;
+ }
+
+ private Properties getProperties(String contentProvider) {
+ Properties env = new Properties();
+ env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
+ "com.sun.jndi.ldap.LdapCtxFactory");
+ env.setProperty(Context.PROVIDER_URL, contentProvider);
+ if(!enableLdapReferral.isSelected()){
+ env.setProperty(Context.REFERRAL, "ignore");
+ }else{
+ String msg="**---- You have chosen to tell LDAP servers to [FOLLOW] context referrals. Default is [IGNORE] referrals. --**";
+ log(msg);
+ env.setProperty(Context.REFERRAL, "follow");
+ }
+
+// // Setup SSL if requested
+// String protocol = ssl.isSelected()? "ssl":"";
+// if ((protocol != null) && protocol.equals("ssl")) {
+// String ldapSocketFactory = env
+// .getProperty("java.naming.ldap.factory.socket");
+// if (ldapSocketFactory == null) {
+// env.put("java.naming.ldap.factory.socket",
+// UntrustedSSLSocketFactory.class.getName());
+// }
+// env.put(Context.SECURITY_PROTOCOL, "ssl");
+// }
+
+ return env;
+ }
+
+ private String delineate() {
+ String line = "-";
+ for (int i = 0; i < 30; i++) {
+ line += "-";
+ }
+ return line;
+ }
+
+ /** Takes care of delineating messages and conditional logging contents passed in.
+ * @param msg
+ */
+ private void log(String msg) {
+ String message = "\n" + delineate() + "\n";
+ message += msg;
+ message += "\n" + delineate() + "\n\n";
+ //This flag can be used in the unlikely case that the UI hangs during a test operation.:
+ if(iterativeVerboseLogging.isSelected()){
+ System.out.println(message);
+ }
+ testResults.setText(testResults.getText() + message);
+ }
+
+ private SearchControls getSearchControls() {
+ int scope = SearchControls.SUBTREE_SCOPE;
+ int timeLimit = 0;
+ long countLimit = 0;
+ String[] returnedAttributes = null;
+ boolean returnObject = false;
+ boolean deference = false;
+ SearchControls constraints = new SearchControls(scope, countLimit,
+ timeLimit, returnedAttributes, returnObject, deference);
+ return constraints;
+ }
+
+ /** Translate SystemSettings to familiar Properties instance since we're
+ * passing not one but multiple values.
+ *
+ * @param systemSettings
+ * @return
+ */
+ private Properties populateProperties(Properties existing) {
+ Properties properties = new Properties();
+ if(existing!=null){
+ properties = existing;
+ }
+ for (SystemSetting entry : SystemSetting.values()) {
+ if(entry!=null){
+ switch(entry){
+ case LDAP_BASED_JAAS_PROVIDER:
+ properties.put(entry.getInternalName(), "");
+ break;
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**Build/retrieve the user DN. Not usually a property.
+ *
+ * @param options
+ * @param userName
+ * @param usePosixGroups boolean indicating whether we search for groups with posixGroup format
+ * @return
+ */
+ private String getUserAttribute(Properties options, String userName, boolean usePosixGroups) {
+ Map<String, String> details = findLdapUserDetails(userName);
+ String userAttribute = null;
+ if (usePosixGroups) {//return just the username as posixGroup member search uses (&(%s)(memberUid=username))
+ userAttribute = userName;
+ } else {//this is the default where group search uses (&(%s)(uniqueMember={userDn}))
+ userAttribute = details.get("dn");
+ }
+
+ return userAttribute;
+ }
+
+ /** See LDAPStringUtil.encodeForFilter() for original code/source/author/etc.
+ * <p>Encode a string so that it can be used in an LDAP search filter.</p>
+ *
+ * <p>The following table shows the characters that are encoded and their
+ * encoded version.</p>
+ *
+ * <table>
+ * <tr><th align="center">Character</th><th>Encoded As</th></tr>
+ * <tr><td align="center">*</td><td>\2a</td></tr>
+ * <tr><td align="center">(</td><td>\28</td></tr>
+ * <tr><td align="center">)</td><td>\29</td></tr>
+ * <tr><td align="center">\</td><td>\5c</td></tr>
+ * <tr><td align="center"><code>null</code></td><td>\00</td></tr>
+ * </table>
+ *
+ * <p>In addition to encoding the above characters, any non-ASCII character
+ * (any character with a hex value greater then <code>0x7f</code>) is also
+ * encoded and rewritten as a UTF-8 character or sequence of characters in
+ * hex notation.</p>
+ *
+ * @param filterString a string that is to be encoded
+ * @return the encoded version of <code>filterString</code> suitable for use
+ * in a LDAP search filter
+ * @see <a href="http://tools.ietf.org/html/rfc4515">RFC 4515</a>
+ */
+ public static String encodeForFilter(final String filterString) {
+ if (filterString != null && filterString.length() > 0) {
+ StringBuilder encString = new StringBuilder(filterString.length());
+ for (int i = 0; i < filterString.length(); i++) {
+ char ch = filterString.charAt(i);
+ switch (ch) {
+ case '*': // encode a wildcard * character
+ encString.append("\\2a");
+ break;
+ case '(': // encode a open parenthesis ( character
+ encString.append("\\28");
+ break;
+ case ')': // encode a close parenthesis ) character
+ encString.append("\\29");
+ break;
+ case '\\': // encode a backslash \ character
+ encString.append("\\5c");
+ break;
+ case '\u0000': // encode a null character
+ encString.append("\\00");
+ break;
+ default:
+ if (ch <= 0x7f) { // an ASCII character
+ encString.append(ch);
+ } else if (ch >= 0x80) { // encode to UTF-8
+ try {
+ byte[] utf8bytes = String.valueOf(ch).getBytes("UTF8");
+ for (byte b : utf8bytes) {
+ encString.append(String.format("\\%02x", b));
+ }
+ } catch (UnsupportedEncodingException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ return encString.toString();
+ }
+ return filterString;
+ }
+}
+
+//Mock up the upgraded system properties approach to use SystemSetting
+enum SystemSetting {
+ LDAP_BASED_JAAS_PROVIDER("CAM_JAAS_PROVIDER"),
+ LDAP_NAMING_PROVIDER_URL("CAM_LDAP_NAMING_PROVIDER_URL"),
+ USE_SSL_FOR_LDAP("CAM_LDAP_PROTOCOL"),
+ LDAP_LOGIN_PROPERTY("CAM_LDAP_LOGIN_PROPERTY"),
+ LDAP_FILTER("CAM_LDAP_FILTER"),
+ LDAP_GROUP_FILTER("CAM_LDAP_GROUP_FILTER"),
+ LDAP_GROUP_MEMBER("CAM_LDAP_GROUP_MEMBER"),
+ LDAP_GROUP_QUERY_PAGE_SIZE("CAM_LDAP_GROUP_QUERY_PAGE_SIZE"),
+ LDAP_BASE_DN("CAM_LDAP_BASE_DN"),
+ LDAP_BIND_DN("CAM_LDAP_BIND_DN"),
+ LDAP_BIND_PW("CAM_LDAP_BIND_PW"),
+ LDAP_NAMING_FACTORY("CAM_LDAP_NAMING_FACTORY_INITIAL"),
+ LDAP_GROUP_USE_POSIX("CAM_LDAP_GROUP_USE_POSIX"),
+ ;
+
+ private String internalName;
+
+ private SystemSetting(String name) {
+ this.internalName = name;
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ public static SystemSetting getByInternalName(String internalName) {
+ for (SystemSetting p : SystemSetting.values()) {
+ if (p.internalName.equals(internalName)) {
+ return p;
+ }
+ }
+ return null;
+ }
+}
+
+
commit ec33bbd668d34ab132f4fc46a545e2c9e4cb0951
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Tue Jul 30 17:11:49 2013 -0400
Move TestLdapSettings to it's own module.
diff --git a/modules/helpers/ldap-tool/pom.xml b/modules/helpers/ldap-tool/pom.xml
new file mode 100644
index 0000000..28f8678
--- /dev/null
+++ b/modules/helpers/ldap-tool/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-parent</artifactId>
+ <version>4.9.0-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.rhq.helpers</groupId>
+ <artifactId>ldap-tool</artifactId>
+ <packaging>jar</packaging>
+ <version>4.9.0-SNAPSHOT</version>
+
+ <properties>
+ <executable.name>TestLdapSettings</executable.name>
+ <tool.version>1.0.1</tool.version>
+ </properties>
+
+ <name>RHQ ldap test tool</name>
+ <description>Executable jar to exercise LDAP settings used by RHQ with external LDAP server.</description>
+
+ <build>
+
+ <plugins>
+
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <configuration>
+ <archive>
+ <manifest>
+ <packageName>com.test</packageName>
+ <mainClass>com.test.TestLdapSettings</mainClass>
+ </manifest>
+ </archive>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.7</version>
+ <executions>
+ <execution>
+ <id>tool-finalize</id>
+ <phase>verify</phase>
+ <configuration>
+ <target>
+ <copy file="${project.build.directory}/${project.build.finalName}.jar"
+ tofile="${project.build.directory}/${executable.name}.jar"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.1</version>
+ </plugin>
+
+ </plugins>
+
+ </build>
+
+</project>
+
+
diff --git a/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java b/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java
new file mode 100644
index 0000000..75ff277
--- /dev/null
+++ b/modules/helpers/ldap-tool/src/main/java/com/test/TestLdapSettings.java
@@ -0,0 +1,1285 @@
+package com.test;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+import javax.naming.ldap.Control;
+import javax.naming.ldap.InitialLdapContext;
+import javax.naming.ldap.PagedResultsControl;
+import javax.naming.ldap.PagedResultsResponseControl;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.border.LineBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+//import org.rhq.enterprise.server.util.security.UntrustedSSLSocketFactory;
+
+/* Is a development test tool that allows the user to simulate the RHQ server side
+ * LDAP calls during auth/authz operations.
+ *
+ * The specific LDAP logic below needs to mirror the latest RHQ code and allow the user
+ * to test our their configuration without requring a specific RHQ/JON build as a dependency.
+ *
+ * NOTE: To avoid a runtime dependency on specific versions of RHQ or JON, the small implementation
+ * methods were copied into this class with minimatl changes for logging and ui messaging. The
+ * definitive implementation for each 'copied' method can be found in LDAPGroupManagerBean.
+ *
+ * @author Simeon Pinder
+ */
+public class TestLdapSettings extends JFrame {
+ //shared fields
+ private JTextArea testResults;
+ private JCheckBox showPasswords;
+ private JCheckBox ssl;
+ private JLabel groupPageSizeName;
+ private JTextField groupMemberQueryValue;
+ private JTextField testUserNameValue;
+ private JTextField testUserPasswordValue;
+ private HashMap<String, JTextField> fieldMappings;
+ private String[] keys;
+ private JCheckBox enableLdapReferral;
+ private JCheckBox enableVerboseDebugging;
+ private JCheckBox enableVerboseGroupParsing;
+ private JCheckBox iterativeVerboseLogging;
+ private JCheckBox enablePosixGroups;
+ private JCheckBox enable32xFeatures;
+ private String advdb = "**Verbose:debug ----";
+ private static final String BASEDN_DELIMITER = ";";
+
+ private static final long serialVersionUID = 1L;
+ int textBoxWidth = 20;
+ private static JPanel top = null;
+ private static JPanel testUserRegion = null;
+ private static Properties env=null;
+
+ public static void main(String args[]) {
+ new TestLdapSettings();
+ }
+ //After enabling support for Query parsing, we need to warn users of the effects.
+ final String warnMessage = "<html>***WARNING: Depending upon<br>" +
+ "i)how the ldap server is configured <br>" +
+ "ii)client query paging settings <br>" +
+ " enabling <b>'more detailed logging'</b>,<br>" +
+ " <b>'more group parsing'</b> AND/OR <b>'also log to console'</b> may cause the console to hang/freeze <br>" +
+ " as the LDAP tool continues to parse large query results. If that occurs it is <br>" +
+ " suggested that you kill tool and re-run with 'also log to console' so that the console logs<br>" +
+ " will show which dataset is causing the delay and then you should modify your search|group|member<br>" +
+ " filters accordingly to <b>return smaller results</b> and/or <b>consume larger payloads</b>.<br>" +
+ "***WARNING</html>";
+
+ // Configure window properties
+ private TestLdapSettings() {
+
+ setTitle("Check LDAP Settings: Simulates LDAP checks/queries of RHQ LDAP integration");
+ getContentPane().setLayout(new BorderLayout());
+ // top panel definition
+ top = new JPanel();
+ top.setLayout(new BoxLayout(top, BoxLayout.Y_AXIS));
+ top.setBorder(LineBorder.createGrayLineBorder());
+ //define checkbox here as it's checked when generating UI.
+ showPasswords = new JCheckBox("show passwords:");
+ showPasswords.setSelected(false);
+
+ keys = new String[] { "URL:", "Search Filter:",
+ "Search Base:","Login Property",
+ "Username:", "Group Search Filter:",
+ "Password:", "Group Member Filter:",
+ };
+ fieldMappings = loadUiFields(top, keys);
+
+ //add the two checkboxes for additiona debugging options
+ enableLdapReferral= new JCheckBox("[follow] ldap referrals");
+ enableLdapReferral.setSelected(false);
+ enableVerboseDebugging= new JCheckBox("more verbose logging");
+ enableVerboseDebugging.setSelected(false);
+ enableVerboseDebugging.setToolTipText(warnMessage);
+ enableVerboseGroupParsing= new JCheckBox("more detailed group parsing");
+ enableVerboseGroupParsing.setSelected(false);
+ enableVerboseGroupParsing.setToolTipText("*Take care when using this mode with a large number of groups* Every group discovered is parsed/listed.");
+ iterativeVerboseLogging= new JCheckBox("also log to console");
+ iterativeVerboseLogging.setSelected(false);
+ iterativeVerboseLogging.setToolTipText("This mode is useful when the test tool is having difficulty returning results from large queries.");
+ iterativeVerboseLogging.setToolTipText(warnMessage);
+ enablePosixGroups= new JCheckBox("is Posix Group");
+ enablePosixGroups.setSelected(false);
+ enablePosixGroups.setEnabled(false);
+
+ //put into 3.2.x functionality row
+ JPanel jon32xRegion = new JPanel();
+ jon32xRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder jon32xLineBorder = new LineBorder(Color.BLACK, 2);
+ TitledBorder jon32xBorder = new TitledBorder(jon32xLineBorder, "JON 3.2.x/RHQ 4.8.x specific features:");
+ jon32xRegion.setBorder(jon32xBorder);
+ enable32xFeatures= new JCheckBox("enable JON 3.2.x/RHQ 4.8.x features");
+ enable32xFeatures.setToolTipText("This enables features not available before RHQ 4.8.x/JON 3.2.x.");
+ enable32xFeatures.setSelected(false);
+ enable32xFeatures.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if(enable32xFeatures.isSelected()){
+ groupPageSizeName.setEnabled(true);
+ groupMemberQueryValue.setEnabled(true);
+ groupMemberQueryValue.setEditable(true);
+ groupMemberQueryValue.setText("1000");
+ enablePosixGroups.setEnabled(true);
+ }else{
+ groupMemberQueryValue.setText("");
+ groupPageSizeName.setEnabled(false);
+ groupMemberQueryValue.setEnabled(false);
+ groupMemberQueryValue.setEditable(false);
+ enablePosixGroups.setEnabled(false);
+ enablePosixGroups.setSelected(false);
+ }
+ }
+ });
+
+ jon32xRegion.add(enable32xFeatures);
+ groupPageSizeName = new JLabel("Group Query Page Size:");
+ groupPageSizeName.setEnabled(false);
+ groupMemberQueryValue = new JTextField(10);
+ groupMemberQueryValue.setText("1000");
+ groupMemberQueryValue.setEditable(false);
+ jon32xRegion.add(groupPageSizeName);
+ jon32xRegion.add(groupMemberQueryValue);
+ jon32xRegion.add(enablePosixGroups);
+ top.add(jon32xRegion);
+
+ //put into row display
+ JPanel advancedDebugRegion = new JPanel();
+ advancedDebugRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder advancedBorder = new LineBorder(Color.BLACK, 2);
+ TitledBorder debugBorder = new TitledBorder(advancedBorder, "Debug: **Warning --<hover HERE>**");
+ advancedDebugRegion.setBorder(debugBorder);
+ advancedDebugRegion.add(enableLdapReferral);
+ advancedDebugRegion.add(enableVerboseDebugging);
+ advancedDebugRegion.add(enableVerboseGroupParsing);
+ advancedDebugRegion.add(iterativeVerboseLogging);
+ advancedDebugRegion.setToolTipText(warnMessage);
+ top.add(advancedDebugRegion);
+
+ JPanel securityPanel = new JPanel();
+ securityPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
+ showPasswords.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ SwingUtilities.invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ //store off existing value
+ String existingValue = "";
+ String existingTestUserPass = "";
+ JTextField current = fieldMappings.get("Password:");
+ if(current instanceof JPasswordField){
+ JPasswordField pass = ((JPasswordField)current);
+ if(pass!=null){
+ char[] charArray = pass.getPassword();
+ if(charArray.length>0){
+ existingValue = new String(charArray);
+ }
+ }
+ }else{
+ existingValue = current.getText();
+ }
+ //save off test user password as well
+ if(testUserPasswordValue instanceof JPasswordField){
+ JPasswordField pass = ((JPasswordField)testUserPasswordValue);
+ if(pass!=null){
+ char[] charArray = pass.getPassword();
+ if(charArray.length>0){
+ existingTestUserPass = new String(charArray);
+ }
+ }
+ }else{
+ existingTestUserPass=testUserPasswordValue.getText();
+ }
+
+ JTextField updatedContainer = null;
+ if(showPasswords.isSelected()){
+ updatedContainer = new JTextField(textBoxWidth);
+ updatedContainer.setText(existingValue);
+ testUserPasswordValue = new JTextField(textBoxWidth);
+ testUserPasswordValue.setText(existingTestUserPass);
+ }else{
+ updatedContainer = new JPasswordField(textBoxWidth);
+ updatedContainer.setText(existingValue);
+ testUserPasswordValue = new JPasswordField(textBoxWidth);
+ testUserPasswordValue.setText(existingTestUserPass);
+ }
+ //locate the JPanel and rebuild it Should be at index 3
+ JPanel passwordRow = (JPanel) top.getComponent(3);
+// JTextField jf = (JTextField) passwordRow.getComponent(1);
+ //store off existing components
+ Component[] existing = new Component[passwordRow.getComponentCount()];
+ for(int i=0; i<passwordRow.getComponentCount();i++){
+ existing[i] = passwordRow.getComponent(i);
+ }
+ passwordRow.removeAll();
+ for(int j=0;j<existing.length;j++){
+ if(j==1){//insert new JTextField instead
+ passwordRow.add(updatedContainer);
+ }else{
+ passwordRow.add(existing[j]);
+ }
+ }
+ //reload testUserRegion
+ //store off existing components
+ Component[] existingTest = new Component[testUserRegion.getComponentCount()];
+ for(int i=0; i<testUserRegion.getComponentCount();i++){
+ existingTest[i] = testUserRegion.getComponent(i);
+ }
+ testUserRegion.removeAll();
+ for(int j=0;j<existingTest.length;j++){
+ if(j==3){//insert new JTextField instead
+ testUserRegion.add(testUserPasswordValue);
+ }else{
+ testUserRegion.add(existingTest[j]);
+ }
+ }
+
+ top.revalidate();
+ top.repaint();
+ }
+ });
+ }
+ });
+ securityPanel.add(showPasswords);
+ ssl = new JCheckBox("SSL:");
+ ssl.setEnabled(false);
+ securityPanel.add(ssl);
+ top.add(securityPanel);
+
+ // test user auth region
+ testUserRegion = new JPanel();
+ testUserRegion.setLayout(new FlowLayout(FlowLayout.LEFT));
+ LineBorder border = new LineBorder(Color.BLUE, 2);
+ TitledBorder tBorder = new TitledBorder(border, "Authentication/Authorization Check Credentials: (insert valid ldap user assigned to group)");
+ testUserRegion.setBorder(tBorder);
+ JLabel testUserName = new JLabel("Test UserName:");
+ testUserNameValue = new JTextField(textBoxWidth);
+ JLabel testUserPassword = new JLabel("Test Password:");
+// testUserPasswordValue = new JTextField(textBoxWidth);
+ testUserPasswordValue = new JPasswordField(textBoxWidth);
+ testUserRegion.add(testUserName);
+ testUserRegion.add(testUserNameValue);
+ testUserRegion.add(testUserPassword);
+ testUserRegion.add(testUserPasswordValue);
+ top.add(testUserRegion);
+
+ // center
+ JPanel center = new JPanel();
+ center.setLayout(new BoxLayout(center, BoxLayout.X_AXIS));
+ // build center panel
+ buildCenterPanel(center);
+
+ // final component layout
+ getContentPane().add(top, BorderLayout.NORTH);
+ getContentPane().add(center, BorderLayout.CENTER);
+ this.setSize(720, 700);
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ System.exit(0);
+ }
+ });
+ setVisible(true);
+ }
+
+ // define the center display panel.
+ private void buildCenterPanel(JPanel center) {
+ // First element is Test Button
+ JButton test = new JButton("Test Settings");
+ center.add(test);
+ // second is large text box that display ldap queries
+ testResults = new JTextArea("(click button to test settings values: simulates 4 separate checks showing ldap filters used)",
+ 40, 40);
+ JScrollPane jsp = new JScrollPane(testResults);
+ center.add(jsp);
+ test.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ testResults.setText("");//clear out empty msg
+ //trim spaces from all fields
+ String ldapServer = fieldMappings.get(keys[0]).getText().trim();
+ String searchFilter = fieldMappings.get(keys[1]).getText().trim();
+ String searchBase = fieldMappings.get(keys[2]).getText().trim();
+ String loginProperty = fieldMappings.get(keys[3]).getText().trim();
+ String bindUserName = fieldMappings.get(keys[4]).getText().trim();
+ String groupSearchFilter = fieldMappings.get(keys[5]).getText().trim();
+ String bindPassword = fieldMappings.get(keys[6]).getText().trim();
+ String groupMemberFilter = fieldMappings.get(keys[7]).getText().trim();
+ String groupMemberQuerySize = groupMemberQueryValue.getText().trim();
+ String testUserName = testUserNameValue.getText().trim();
+ String testUserPassword = testUserPasswordValue.getText().trim();
+ // validate initial required elements
+ String msg = null;
+ boolean proceed = true;
+ //valid required details set.
+ if (ldapServer.isEmpty() || bindUserName.isEmpty()
+ || bindPassword.isEmpty() || searchBase.isEmpty()) {
+ msg ="STEP-1:FAIL: "+ keys[0] + ", " + keys[2] + ", " + keys[4] + ", "
+ + keys[6] + " cannot be empty to proceed.";
+ log(msg);
+ proceed = false;
+ }
+ env = null;
+ InitialLdapContext ctx = null;
+ if (proceed) {// attempt initial ldap bind from RHQ server
+ msg = "STEP-1:TESTING: Attempting to bind to server:" + ldapServer
+ + "\n with user '" + bindUserName
+ + "' and password entered.";
+ log(msg);
+ env = getProperties(ldapServer);
+ env.setProperty(Context.SECURITY_PRINCIPAL, bindUserName);
+ env.setProperty(Context.SECURITY_CREDENTIALS, bindPassword);
+ env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ //put the rest of the LDAP properties into the Properties instance for use later.
+ //there still needs to be separate variables since some are for UI validation.
+ env.setProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), groupSearchFilter);
+ env.setProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), groupMemberFilter);
+ env.setProperty(SystemSetting.LDAP_BASE_DN.getInternalName(), searchBase);
+ env.setProperty(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName(), loginProperty);
+ env.setProperty(SystemSetting.LDAP_BIND_DN.getInternalName(), bindUserName);
+ env.setProperty(SystemSetting.LDAP_BIND_PW.getInternalName(), bindPassword);
+ env.setProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.getInternalName(), groupMemberQuerySize);
+
+ for(Object key :env.keySet()){
+ System.out.println(key+"="+env.getProperty(key+""));
+ }
+
+ try {
+ ctx = new InitialLdapContext(env, null);
+ msg = "STEP-1:PASS: LDAP bind credentials are correct. Successfully connected to '"
+ + ldapServer
+ + "'.\n This means the LDAP Bind credentials for the RHQ Server authentication/authorization requests to ldap server "
+ + "are correct.";
+ if(enableVerboseDebugging.isSelected()){
+ msg+="\n"+advdb+" LDAP simple authentication bind successful.";
+ }
+ log(msg);
+ proceed = true;
+ } catch (Exception ex) {
+ msg = "STEP-1:FAIL: Unable to connect to the LDAP server with credentials specified.\n";
+ msg+="Exception:"+ex.getMessage();
+ if(enableVerboseDebugging.isSelected()){
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ proceed = false;
+ }
+ }
+ if (proceed) {// retrieve test credentials to test run auth
+ // load search controls
+ SearchControls searchControls = getSearchControls();
+ // validating searchFilter and test user/pass creds
+ proceed = true;
+ if (testUserName.isEmpty() || (testUserPassword.isEmpty())) {
+ msg = "STEP-2:FAIL: Test Username/Password fields cannot be empty for this step.";
+ log(msg);
+ proceed = false;
+ }
+ // testing a valid user involves a filtered ldap search
+ // using the loginProperty, and optionally searchFilter
+ String userDN = "";
+ if (proceed) {
+ // default loginProperty to cn if it's not set
+ if (loginProperty.isEmpty()) {
+ loginProperty = "cn";
+ if(enableVerboseDebugging.isSelected()){
+ String mesg = "As you have not specified a login property, defaulting to 'cn'";
+ log(advdb+" "+msg);
+ }
+ }
+ String filter;
+ if (!searchFilter.isEmpty()) {
+ filter = "(&(" + loginProperty + "=" + testUserName
+ + ")" + "(" + searchFilter + "))";
+ } else {
+ filter = "(" + loginProperty + "=" + testUserName
+ + ")";
+ }
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb+" The searchfilter is optionally appended to login property for additional shared attribute across users.");
+ }
+ msg = "STEP-2:TESTING: To validate the test user the following LDAP filtered component will be used to find matching users:\n";
+ msg += filter;
+ log(msg);
+ // test out the search on the target ldap server
+ try {
+ String[] baseDNs = searchBase.split(";");
+ for (int x = 0; x < baseDNs.length; x++) {
+ NamingEnumeration answer = ctx.search(
+ baseDNs[x], filter, searchControls);
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb+" this search was excuted against DN component '"+baseDNs[x]+"'.");
+ }
+ // boolean ldapApiNpeFound = false;
+ if (!answer.hasMoreElements()) {
+ msg="STEP-2:WARN Unable to locate a matching users for the filter'"+filter+
+ "'. Please check your loginProperty. Usually 'cn' or 'uid'";
+ log(msg);
+ continue;
+ }
+ // Going with the first match
+ SearchResult si = (SearchResult) answer.next();
+
+ // Construct the UserDN
+ userDN = si.getName() + "," + baseDNs[x];
+ msg = "STEP-2:PASS: The test user '"
+ + testUserName
+ + "' was succesfully located, and the following userDN will be used in authorization check:\n";
+ msg += userDN;
+ log(msg);
+
+ ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN);
+ ctx.addToEnvironment(Context.SECURITY_CREDENTIALS,testUserPassword);
+ ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,"simple");
+
+ // if successful then verified that user and pw
+ // are valid ldap credentials
+ ctx.reconnect(null);
+ msg = "STEP-2:PASS: The user '"
+ + testUserName
+ + "' was succesfully authenticated using userDN '"
+ + userDN + "' and password provided.\n"
+ +"*Note: the loginProperty must match the loginProperty listed in dn: for the user. It is the DN that RHQ will lookup and use.";
+ log(msg);
+ }
+ } catch (Exception ex) {
+ msg = "STEP-2:FAIL: There was an error while searching for or authenticating the user '"
+ + testUserName + "'\n";
+ msg += ex.getMessage();
+ if(enableVerboseDebugging.isSelected()){
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ proceed=false;
+ }
+ }
+ // with authentication completed, now check authorization.
+ // validate filter components to list all available groups
+ proceed = false;
+ if (!groupSearchFilter.isEmpty()) {
+ Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
+ String filter = null;
+
+ if (groupSearchFilter.startsWith("(") && groupSearchFilter.endsWith(")")){
+ filter = groupSearchFilter; // RFC 2254 does not allow for ((expression))
+ }else{
+ filter = String
+ .format("(%s)", groupSearchFilter);
+ }
+ msg = "STEP-3:TESTING: This ldap filter "
+ + filter
+ + " will be used to locate ALL available LDAP groups";
+ log(msg);
+
+ Properties systemConfig = populateProperties(env);
+
+ ret = buildGroup(systemConfig, filter);
+ msg = "STEP-3:TESTING: Using Group Search Filter '"
+ + filter + "', " + ret.size()
+ + " ldap group(s) were located.\n";
+ if (ret.size() > 0) {
+ HashMap<String, String>[] ldapLists = new HashMap[ret
+ .size()];
+ ret.toArray(ldapLists);
+ // in this mode go beyond the first ten results.
+ if (enableVerboseGroupParsing.isSelected()) {
+ msg += "STEP-3:PASS: Listing 'all' of the ldap groups located: \n";
+ for (int i = 0; i < ret.size(); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ } else {// otherwise only show first 10[subset of
+ // available groups]
+ msg += "STEP-3:PASS: Listing a few(<=10) of the ldap groups located: \n";
+ for (int i = 0; (i < ret.size() && i < 10); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ }
+ proceed = true;// then can proceed to next step.
+ }
+ log(msg);
+ } else {
+ msg = "STEP-3:FAIL: Group Search Filter: cannot be empty to proceed.";
+ log(msg);
+ proceed=false;
+ }
+ // retrieve lists of authorized groups available for the
+ if (proceed) {
+ // check groupMember
+ if (!groupMemberFilter.isEmpty()) {
+// Map<String, String> userDetails = new HashMap<String, String>();
+// userDetails = findLdapUserDetails(userDN);
+ Set<String> userDetails = findAvailableGroupsFor(testUserName);
+
+ if(!userDetails.isEmpty()){
+ proceed=true;
+ }
+ } else {
+ msg = "STEP-4:FAIL: Group Member Filter must be non-empty to proceed with simulating authorization check for test user.";
+ log(msg);
+ }
+ }
+ if(proceed){
+ msg="COMPLETED:PASS: The current settings, for successful steps, should be correct to enter into your RHQ server.";
+ msg+="\n\n\n\n When you encounter failures, warnings or other unexpected results you should use an external ";
+ msg+="LDAP search utility to check that the generated filters return the expected LDAP results.";
+ log(msg);
+ }
+ }
+ }
+ });
+ }
+
+ private String appendStacktraceToMsg(String msg, Exception ex) {
+ String moreVerbose = "";
+ moreVerbose+=advdb+" Exception type:"+ex.getClass()+"\n";
+ moreVerbose+=advdb+" Exception stack trace reference:"+ex.getStackTrace()+"\n";
+ if(ex.getStackTrace()!=null){
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ ex.printStackTrace(pw);
+ moreVerbose+=advdb+" stack trace reference:"+sw.toString();
+ }
+ msg+="\n"+moreVerbose;
+ return msg;
+ }
+
+ private boolean containsIllegalLdap(String currentValue) {
+ boolean invalidData = false;
+ if((currentValue!=null)&&(!currentValue.trim().isEmpty())){
+ //TODO: spinder 3/17/11: need to figure out regex to filter/detect bad data in returned ldap. Giving up for now.
+// String regex = "(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)[/,+\"><;=#]|(?<=(?:[^\\]|^)(\\\\)+|[^\\]|^)\\(?!\\|[/,+\"><;=#]| $|(?<=^\\) )|^";
+// regex = "(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)[/,+\\\"><;=#]|(?<=(?:[^\\\\]|^)(\\\\\\\\)+|[^\\\\]|^)\\\\(?!\\\\|[/,+\\\"><;=#]| $|(?<=^\\\\) )|^";
+// System.out.println("++++++++ CURR VAL:"+currentValue+":INV-CHeck:"+currentValue.matches(",+\"\\<;\n=/")+":NEWCHECK:"+(currentValue.matches(regex)));
+// if(currentValue.matches(",+\"\\<;\n=/")){
+// invalidData=true;
+// }
+// String badList = ",+\"\\<;\n=";
+ String badList = "+\"\\<;\n";
+ for(char car :currentValue.toCharArray()){
+ for(char c :badList.toCharArray()){
+ if(car == c){
+ invalidData=true;
+ }
+ }
+ }
+
+ }
+ return invalidData;
+ }
+ /**
+ * @throws NamingException
+ * @see org.jboss.security.auth.spi.UsernamePasswordLoginModule#validatePassword(java.lang.String,java.lang.String)
+ */
+ protected Set<Map<String, String>> buildGroup(Properties systemConfig, String filter) {
+ Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
+ // Load our LDAP specific properties
+ // Load the BaseDN
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+
+ // Load the LoginProperty
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ if (loginProperty == null) {
+ // Use the default
+ loginProperty = "cn";
+ }
+ // Load any information we may need to bind
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+ if (bindDN != null) {
+ systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ systemConfig.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ }
+ try {
+ InitialLdapContext ctx = new InitialLdapContext(systemConfig, null);
+ SearchControls searchControls = getSearchControls();
+ /*String filter = "(&(objectclass=groupOfUniqueNames)(uniqueMember=uid=" + userName
+ + ",ou=People, dc=rhndev, dc=redhat, dc=com))";*/
+
+ //modify the search control to only include the attributes we will use
+ String[] attributes = { "cn", "description" };
+ searchControls.setReturningAttributes(attributes);
+
+ //BZ:964250: add rfc 2696
+ //default to 1000 results. System setting page size from UI should be non-negative integer > 0.
+ //additionally as system settings are modifiable via CLI which may not have param checking enabled do some
+ //more checking.
+ int defaultPageSize = 1000;
+ // only if they're enabled in the UI.
+ if (enable32xFeatures.isSelected()) {
+ String groupPageSize = systemConfig.getProperty(
+ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE
+ .getInternalName(), "" + defaultPageSize);
+ if ((groupPageSize != null)
+ && (!groupPageSize.trim().isEmpty())) {
+ int passedInPageSize = -1;
+ try {
+ passedInPageSize = Integer
+ .valueOf(groupPageSize.trim());
+ if (passedInPageSize > 0) {
+ defaultPageSize = passedInPageSize;
+ if(enableVerboseDebugging.isSelected()){
+ log(advdb
+ + " LDAP Group Query Page Sizing of '"+defaultPageSize+"' is being requested from server.");
+ }
+ }
+ } catch (NumberFormatException nfe) {
+ // log issue and do nothing. Go with the default.
+ String msg = "LDAP Group Page Size passed in '"
+ + groupPageSize
+ + "' in is invalid. Defaulting to 1000 results."
+ + nfe.getMessage();
+ log(msg);
+ }
+ }
+ ctx.setRequestControls(new Control[] { new PagedResultsControl(
+ defaultPageSize, Control.CRITICAL) });
+ }
+ // Loop through each configured base DN. It may be useful
+ // in the future to allow for a filter to be configured for
+ // each BaseDN, but for now the filter will apply to all.
+ String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
+
+ for (int x = 0; x < baseDNs.length; x++) {
+ if (enableVerboseDebugging.isSelected()) {
+ log(advdb
+ + " this search was excuted against DN component '"
+ + baseDNs[x] + "'.");
+ }
+ executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
+
+ // continually parsing pages of results until we're done.
+ // only if they're enabled in the UI.
+ if (enable32xFeatures.isSelected()) {
+ // handle paged results if they're being used here
+ byte[] cookie = null;
+ Control[] controls = ctx.getResponseControls();
+ if (controls != null) {
+ for (Control control : controls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
+ cookie = pagedResult.getCookie();
+ }
+ }
+ }
+
+ while (cookie != null) {
+ String msg = "RFC 2696 is supported by the server and we are paging through the results. "+
+ groupDetailsMap.size()+" results returned so far.";
+ if(enableVerboseGroupParsing.isSelected()){
+ log(advdb
+ + msg);
+ }
+ // ensure the next requests contains the session/cookie
+ // details
+ ctx.setRequestControls(new Control[] { new PagedResultsControl(
+ defaultPageSize, cookie, Control.CRITICAL) });
+ executeGroupSearch(filter, groupDetailsMap, ctx,
+ searchControls, baseDNs, x);
+ // empty out cookie
+ cookie = null;
+ // test for further iterations
+ controls = ctx.getResponseControls();
+ if (controls != null) {
+ for (Control control : controls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
+ cookie = pagedResult.getCookie();
+ }
+ }
+ }
+ }
+ }
+ }//end of for loop
+ } catch (NamingException e) {
+ if (e instanceof InvalidSearchFilterException) {
+ InvalidSearchFilterException fException = (InvalidSearchFilterException) e;
+ String message = "The ldap group filter defined is invalid ";
+ log(message);
+ }
+ //TODO: check for ldap connection/unavailable/etc. exceptions.
+ else {
+ String mesg = "LDAP communication error: " + e.getMessage();
+ log(mesg);
+ }
+ } catch (IOException iex) {
+ String msg = "Unexpected LDAP communciation error:" + iex.getMessage();
+ log(msg);
+ }
+
+ return groupDetailsMap;
+ }
+
+ /** Executes the LDAP group query using the filters, context and search controls, etc. parameters passed in.
+ * The matching groups located during processing this pages of results are added as new entries to the
+ * groupDetailsMap passed in.
+ *
+ * @param filter
+ * @param groupDetailsMap
+ * @param ctx
+ * @param searchControls
+ * @param baseDNs
+ * @param x
+ * @throws NamingException
+ */
+ private void executeGroupSearch(String filter, Set<Map<String, String>> groupDetailsMap, InitialLdapContext ctx,
+ SearchControls searchControls, String[] baseDNs, int x) throws NamingException {
+ //execute search based on controls and context passed in.
+ NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
+ boolean ldapApiEnumerationBugEncountered = false;
+ while ((!ldapApiEnumerationBugEncountered) && answer.hasMoreElements()) {//BZ:582471- ldap api bug change
+ // We use the first match
+ SearchResult si = null;
+ try {
+ si = answer.next();
+ } catch (NullPointerException npe) {
+ if (enableVerboseDebugging.isSelected()) {
+ log(advdb
+ + " NullPtr exception detected. If known LDAP api enum npe ignore: "
+ + npe.getMessage() + ".");
+ }
+ ldapApiEnumerationBugEncountered = true;
+ break;
+ }
+
+ if (enableVerboseDebugging.isSelected()
+ || enableVerboseGroupParsing.isSelected()) {
+ Attributes attributeContainer = si.getAttributes();
+ NamingEnumeration<? extends Attribute> attributes = attributeContainer
+ .getAll();
+ String attributesReturned = " ";
+ while (attributes.hasMore()) {
+ attributesReturned += attributes.next().getID() + ",";
+ }
+ String dbugMesg = "\n"
+ + advdb
+ + " Group search LDAP ("
+ + attributeContainer.size()
+ + ") attributes located for group '"
+ + si.getName()
+ + "' are ["
+ + attributesReturned.substring(0,
+ attributesReturned.length() - 1) + "].";
+ // directly update here to shorten messages for lots of groups
+ testResults.setText(testResults.getText() + dbugMesg);
+ //This flag can be used in the unlikely case that the UI hangs during a test operation.:
+ if(iterativeVerboseLogging.isSelected()){
+ System.out.println(dbugMesg);
+ }
+
+ // additionally parse attribute ids and values for illegal ldap
+ // characters
+ if (enableVerboseGroupParsing.isSelected()) {
+ attributes = attributeContainer.getAll();
+ String currentAttributeId = "";
+ String currentValue = "";
+ // spinder: 3/17/11: should we bail on first bad data or
+ // display them all?
+ while (attributes.hasMore()) {
+ boolean badData = false;
+ Attribute att = attributes.next();
+ currentAttributeId = att.getID();
+ if (containsIllegalLdap(currentAttributeId)) {
+ log(advdb
+ + " LDAP Group: bad atrribute data detected for group '"
+ + si.getName() + "' for attribute '"
+ + currentAttributeId + "'.");
+ badData = true;
+ }
+ if (att.getAll() != null) {
+ NamingEnumeration<?> enumer = att.getAll();
+ while (enumer.hasMore()) {
+ currentValue = enumer.next() + "";
+ if (containsIllegalLdap(currentValue)) {
+ log(advdb
+ + " LDAP Group: bad data detected for group '"
+ + si.getName()
+ + "' with attribute '"
+ + currentAttributeId
+ + "' and value:" + currentValue);
+ badData = true;
+ }
+ }
+ }
+ if (badData) {
+ log(advdb
+ + "** LDAP Group: Some bad LDAP data detected for group '"
+ + si.getName() + "'.");
+ }
+ }
+ }
+ }
+
+ Map<String, String> entry = new HashMap<String, String>();
+ // String name = (String) si.getAttributes().get("cn").get();
+ Attribute commonNameAttr = si.getAttributes().get("cn");
+ if (commonNameAttr != null) {
+ String name = (String) commonNameAttr.get();
+ name = name.trim();
+ Attribute desc = si.getAttributes().get("description");
+ String description = desc != null ? (String) desc.get() : "";
+ description = description.trim();
+ entry.put("id", name);
+ entry.put("name", name);
+ entry.put("description", description);
+ groupDetailsMap.add(entry);
+ } else {// unable to retrieve details for specific group.
+ log(advdb
+ + " There was an error retrieving 'cn' attribute for group '"
+ + si.getName()
+ + "'. Not adding to returned list of groups. ");
+ }
+ }
+ }
+
+ public Map<String, String> findLdapUserDetails(String userName) {
+ // Load our LDAP specific properties
+ Properties systemConfig = env;
+ HashMap<String, String> userDetails = new HashMap<String, String>();
+
+ // Load the BaseDN
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+
+ // Load the LoginProperty
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ if (loginProperty == null) {
+ // Use the default
+ loginProperty = "cn";
+ }
+ // Load any information we may need to bind
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+
+ // Load any search filter
+ String groupSearchFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
+ String groupMemberFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_MEMBER.getInternalName());
+ String testUserDN=userName;
+ String ldapServer = (String) systemConfig.get(Context.PROVIDER_URL);
+
+ Properties env = getProperties(ldapServer);
+
+ if (bindDN != null) {
+ env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ }
+
+ try {
+ InitialLdapContext ctx = new InitialLdapContext(env, null);
+ SearchControls searchControls = getSearchControls();
+
+ String filter = String.format("(&(%s)(%s=%s))",
+ groupSearchFilter, groupMemberFilter,
+ testUserDN);
+
+ generateUiLoggingForStep4LdapFilter(userName, filter);
+
+ // Loop through each configured base DN. It may be useful
+ // in the future to allow for a filter to be configured for
+ // each BaseDN, but for now the filter will apply to all.
+ String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
+ for (int x = 0; x < baseDNs.length; x++) {
+ NamingEnumeration<SearchResult> answer = ctx.search(baseDNs[x], filter, searchControls);
+ if (!answer.hasMoreElements()) { //BZ:582471- ldap api bug change
+ // Nothing found for this DN, move to the next one if we have one.
+ continue;
+ }
+
+ // We use the first match
+ SearchResult si = answer.next();
+ //generate the DN
+ String userDN = null;
+ try {
+ userDN = si.getNameInNamespace();
+ } catch (UnsupportedOperationException use) {
+ userDN = si.getName();
+ if (userDN.startsWith("\"")) {
+ userDN = userDN.substring(1, userDN.length());
+ }
+ if (userDN.endsWith("\"")) {
+ userDN = userDN.substring(0, userDN.length() - 1);
+ }
+ userDN = userDN + "," + baseDNs[x];
+ }
+ userDetails.put("dn", userDN);
+
+ // Construct the UserDN
+ NamingEnumeration<String> keys = si.getAttributes().getIDs();
+ while (keys.hasMore()) {
+ String key = keys.next();
+ Attribute value = si.getAttributes().get(key);
+ if ((value != null) && (value.get() != null)) {
+ userDetails.put(key, value.get().toString());
+ }
+ }
+// return userDetails;
+ }//end of for loop
+ generateUiLoggingStep4Authz(filter);
+ return userDetails;
+ } catch (Exception ex) {
+ generateUiLoggingStep4Exception(ex);
+ }
+ return userDetails;
+ }
+
+ public Set<String> findAvailableGroupsFor(String userName) {
+ // Load our LDAP specific properties
+ Properties options = env;
+ String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), "");
+ String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), "");
+ String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.getInternalName(), "false");
+ if (groupUsePosix == null) {
+ groupUsePosix = Boolean.toString(false);//default to false
+ }
+ boolean usePosixGroups = Boolean.valueOf(groupUsePosix);
+ String userAttribute = getUserAttribute(options, userName, usePosixGroups);
+ Set<String> ldapSet = new HashSet<String>();
+
+ if (userAttribute != null && userAttribute.trim().length() > 0) {
+ //TODO: spinder 4/21/10 put in error/debug logging messages for badly formatted filter combinations
+ String filter = "";
+ //form assumes examples where groupFilter is like 'objectclass=groupOfNames' and groupMember is 'member'
+ // to produce ldap filter like (&(objectclass=groupOfNames)(member=cn=Administrator,ou=People,dc=test,dc=com))
+ // or like (&(objectclass=groupOfNames)(memberUid=Administrator)) for posixGroups.
+ filter = String.format("(&(%s)(%s=%s))", groupFilter, groupMember, encodeForFilter(userAttribute));
+
+ Set<Map<String, String>> matched = buildGroup(options, filter);
+// log.trace("Located '" + matched.size() + "' LDAP groups for user '" + userName
+// + "' using following ldap filter '" + filter + "'.");
+
+ //iterate to extract just the group names.
+ for (Map<String, String> match : matched) {
+ ldapSet.add(match.get("id"));
+ }
+ } else {
+// log.debug("Group lookup will not be performed due to no UserDN found for user " + userName);
+ }
+
+ return ldapSet;
+ }
+
+ private void generateUiLoggingStep4Exception(Exception ex) {
+ String groupSearchFilter = env
+ .getProperty(SystemSetting.LDAP_GROUP_FILTER
+ .getInternalName());
+ String msg = "STEP-4:FAIL: There was an error searching with the groupFilter supplied: "
+ + groupSearchFilter + "'\n";
+ msg += ex.getMessage();
+ if (enableVerboseDebugging.isSelected()) {
+ msg = appendStacktraceToMsg(msg, ex);
+ }
+ log(msg);
+ }
+
+ private void generateUiLoggingStep4Authz(String filter) {
+ Set<Map<String, String>> groups = buildGroup(env, filter);
+ String msg = "STEP-4:TESTING: Using Group Search Filter '"
+ + filter + "', " + groups.size()
+ + " ldap group(s) were located.\n";
+ if (groups.size() > 0) {
+ HashMap<String, String>[] ldapLists = new HashMap[groups
+ .size()];
+ groups.toArray(ldapLists);
+ msg += "STEP-4:PASS: Listing a few of the ldap groups located: \n";
+ // iterate over first ten or less to demonstrate retrieve
+ for (int i = 0; (i < groups.size() && i < 10); i++) {
+ msg += ldapLists[i] + "\n";
+ }
+ }else{
+ msg+="STEP-4:WARN: With current settings, test user is not authorized for any groups. Is this correct?";
+ }
+ log(msg);
+ }
+
+ private void generateUiLoggingForStep4LdapFilter(String userName,
+ String filter) {
+ String msg = "STEP-4:TESTING: about to do ldap search with filter \n'"
+ + filter
+ + "'\n to locate groups that test user '"+userName+"' IS authorized to access.";
+ log(msg);
+ }
+
+
+ // throw the label and fields together, two to a row.
+ private HashMap<String, JTextField> loadUiFields(JPanel top,
+ String[] componentKeys) {
+ HashMap<String, JTextField> mappings = new HashMap<String, JTextField>();
+ for (int i = 0; i < componentKeys.length; i++) {
+ String firstLabelKey = componentKeys[i];
+ String secondLabelKey = componentKeys[++i];
+ // locate second key
+ JPanel row = new JPanel();
+ row.setLayout(new FlowLayout(FlowLayout.LEFT));
+ JLabel label1 = new JLabel(firstLabelKey);
+ label1.setSize(textBoxWidth, 5);
+// JTextField value1 = new JTextField(textBoxWidth);
+ JTextField value1 = null;
+ if (firstLabelKey.equalsIgnoreCase("Password:")&&(!showPasswords.isSelected())) {
+ value1 = new JPasswordField(textBoxWidth);
+ } else {
+ value1 = new JTextField(textBoxWidth);
+ }
+ JLabel label2 = new JLabel(secondLabelKey);
+ JTextField value2 = new JTextField(textBoxWidth);
+ row.add(label1);
+ row.add(value1);
+ row.add(Box.createRigidArea(new Dimension(0, 5)));
+ row.add(label2);
+ row.add(value2);
+ mappings.put(firstLabelKey, value1);
+ mappings.put(secondLabelKey, value2);
+ top.add(row);
+ }
+
+ return mappings;
+ }
+
+ private Properties getProperties(String contentProvider) {
+ Properties env = new Properties();
+ env.setProperty(Context.INITIAL_CONTEXT_FACTORY,
+ "com.sun.jndi.ldap.LdapCtxFactory");
+ env.setProperty(Context.PROVIDER_URL, contentProvider);
+ if(!enableLdapReferral.isSelected()){
+ env.setProperty(Context.REFERRAL, "ignore");
+ }else{
+ String msg="**---- You have chosen to tell LDAP servers to [FOLLOW] context referrals. Default is [IGNORE] referrals. --**";
+ log(msg);
+ env.setProperty(Context.REFERRAL, "follow");
+ }
+
+// // Setup SSL if requested
+// String protocol = ssl.isSelected()? "ssl":"";
+// if ((protocol != null) && protocol.equals("ssl")) {
+// String ldapSocketFactory = env
+// .getProperty("java.naming.ldap.factory.socket");
+// if (ldapSocketFactory == null) {
+// env.put("java.naming.ldap.factory.socket",
+// UntrustedSSLSocketFactory.class.getName());
+// }
+// env.put(Context.SECURITY_PROTOCOL, "ssl");
+// }
+
+ return env;
+ }
+
+ private String delineate() {
+ String line = "-";
+ for (int i = 0; i < 30; i++) {
+ line += "-";
+ }
+ return line;
+ }
+
+ /** Takes care of delineating messages and conditional logging contents passed in.
+ * @param msg
+ */
+ private void log(String msg) {
+ String message = "\n" + delineate() + "\n";
+ message += msg;
+ message += "\n" + delineate() + "\n\n";
+ //This flag can be used in the unlikely case that the UI hangs during a test operation.:
+ if(iterativeVerboseLogging.isSelected()){
+ System.out.println(message);
+ }
+ testResults.setText(testResults.getText() + message);
+ }
+
+ private SearchControls getSearchControls() {
+ int scope = SearchControls.SUBTREE_SCOPE;
+ int timeLimit = 0;
+ long countLimit = 0;
+ String[] returnedAttributes = null;
+ boolean returnObject = false;
+ boolean deference = false;
+ SearchControls constraints = new SearchControls(scope, countLimit,
+ timeLimit, returnedAttributes, returnObject, deference);
+ return constraints;
+ }
+
+ /** Translate SystemSettings to familiar Properties instance since we're
+ * passing not one but multiple values.
+ *
+ * @param systemSettings
+ * @return
+ */
+ private Properties populateProperties(Properties existing) {
+ Properties properties = new Properties();
+ if(existing!=null){
+ properties = existing;
+ }
+ for (SystemSetting entry : SystemSetting.values()) {
+ if(entry!=null){
+ switch(entry){
+ case LDAP_BASED_JAAS_PROVIDER:
+ properties.put(entry.getInternalName(), "");
+ break;
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**Build/retrieve the user DN. Not usually a property.
+ *
+ * @param options
+ * @param userName
+ * @param usePosixGroups boolean indicating whether we search for groups with posixGroup format
+ * @return
+ */
+ private String getUserAttribute(Properties options, String userName, boolean usePosixGroups) {
+ Map<String, String> details = findLdapUserDetails(userName);
+ String userAttribute = null;
+ if (usePosixGroups) {//return just the username as posixGroup member search uses (&(%s)(memberUid=username))
+ userAttribute = userName;
+ } else {//this is the default where group search uses (&(%s)(uniqueMember={userDn}))
+ userAttribute = details.get("dn");
+ }
+
+ return userAttribute;
+ }
+
+ /** See LDAPStringUtil.encodeForFilter() for original code/source/author/etc.
+ * <p>Encode a string so that it can be used in an LDAP search filter.</p>
+ *
+ * <p>The following table shows the characters that are encoded and their
+ * encoded version.</p>
+ *
+ * <table>
+ * <tr><th align="center">Character</th><th>Encoded As</th></tr>
+ * <tr><td align="center">*</td><td>\2a</td></tr>
+ * <tr><td align="center">(</td><td>\28</td></tr>
+ * <tr><td align="center">)</td><td>\29</td></tr>
+ * <tr><td align="center">\</td><td>\5c</td></tr>
+ * <tr><td align="center"><code>null</code></td><td>\00</td></tr>
+ * </table>
+ *
+ * <p>In addition to encoding the above characters, any non-ASCII character
+ * (any character with a hex value greater then <code>0x7f</code>) is also
+ * encoded and rewritten as a UTF-8 character or sequence of characters in
+ * hex notation.</p>
+ *
+ * @param filterString a string that is to be encoded
+ * @return the encoded version of <code>filterString</code> suitable for use
+ * in a LDAP search filter
+ * @see <a href="http://tools.ietf.org/html/rfc4515">RFC 4515</a>
+ */
+ public static String encodeForFilter(final String filterString) {
+ if (filterString != null && filterString.length() > 0) {
+ StringBuilder encString = new StringBuilder(filterString.length());
+ for (int i = 0; i < filterString.length(); i++) {
+ char ch = filterString.charAt(i);
+ switch (ch) {
+ case '*': // encode a wildcard * character
+ encString.append("\\2a");
+ break;
+ case '(': // encode a open parenthesis ( character
+ encString.append("\\28");
+ break;
+ case ')': // encode a close parenthesis ) character
+ encString.append("\\29");
+ break;
+ case '\\': // encode a backslash \ character
+ encString.append("\\5c");
+ break;
+ case '\u0000': // encode a null character
+ encString.append("\\00");
+ break;
+ default:
+ if (ch <= 0x7f) { // an ASCII character
+ encString.append(ch);
+ } else if (ch >= 0x80) { // encode to UTF-8
+ try {
+ byte[] utf8bytes = String.valueOf(ch).getBytes("UTF8");
+ for (byte b : utf8bytes) {
+ encString.append(String.format("\\%02x", b));
+ }
+ } catch (UnsupportedEncodingException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ return encString.toString();
+ }
+ return filterString;
+ }
+}
+
+//Mock up the upgraded system properties approach to use SystemSetting
+enum SystemSetting {
+ LDAP_BASED_JAAS_PROVIDER("CAM_JAAS_PROVIDER"),
+ LDAP_NAMING_PROVIDER_URL("CAM_LDAP_NAMING_PROVIDER_URL"),
+ USE_SSL_FOR_LDAP("CAM_LDAP_PROTOCOL"),
+ LDAP_LOGIN_PROPERTY("CAM_LDAP_LOGIN_PROPERTY"),
+ LDAP_FILTER("CAM_LDAP_FILTER"),
+ LDAP_GROUP_FILTER("CAM_LDAP_GROUP_FILTER"),
+ LDAP_GROUP_MEMBER("CAM_LDAP_GROUP_MEMBER"),
+ LDAP_GROUP_QUERY_PAGE_SIZE("CAM_LDAP_GROUP_QUERY_PAGE_SIZE"),
+ LDAP_BASE_DN("CAM_LDAP_BASE_DN"),
+ LDAP_BIND_DN("CAM_LDAP_BIND_DN"),
+ LDAP_BIND_PW("CAM_LDAP_BIND_PW"),
+ LDAP_NAMING_FACTORY("CAM_LDAP_NAMING_FACTORY_INITIAL"),
+ LDAP_GROUP_USE_POSIX("CAM_LDAP_GROUP_USE_POSIX"),
+ ;
+
+ private String internalName;
+
+ private SystemSetting(String name) {
+ this.internalName = name;
+ }
+
+ public String getInternalName() {
+ return internalName;
+ }
+
+ public static SystemSetting getByInternalName(String internalName) {
+ for (SystemSetting p : SystemSetting.values()) {
+ if (p.internalName.equals(internalName)) {
+ return p;
+ }
+ }
+ return null;
+ }
+}
+
+
commit 8bac47d3d8425fc0aa0b0781bacfa4efe10cf651
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Tue Jul 30 17:07:58 2013 -0400
Load ldap properties in same way as before refactor and deprecate old property load mechanism as does not work well with reusing existing properties.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index 097b993..8a0e321 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -586,7 +586,34 @@ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
}
}
//now load default/shared LDAP properties as we always have
- properties = getProperties(properties);
+ // Set our default factory name if one is not given
+ String factoryName = properties.getProperty(SystemSetting.LDAP_NAMING_FACTORY.name());
+ properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
+
+ // Setup SSL if requested
+ String value = properties.getProperty(SystemSetting.USE_SSL_FOR_LDAP.name());
+ boolean ldapSsl = "ssl".equalsIgnoreCase(value);
+ if (ldapSsl) {
+ String ldapSocketFactory = properties.getProperty("java.naming.ldap.factory.socket");
+ if (ldapSocketFactory == null) {
+ properties.put("java.naming.ldap.factory.socket", UntrustedSSLSocketFactory.class.getName());
+ }
+ properties.put(Context.SECURITY_PROTOCOL, "ssl");
+ }
+
+ // Set the LDAP url
+ String providerUrl = properties.getProperty(SystemSetting.LDAP_NAMING_PROVIDER_URL.name());
+ if (providerUrl == null) {
+ int port = (ldapSsl) ? 636 : 389;
+ providerUrl = "ldap://localhost:" + port;
+ }
+
+ properties.setProperty(Context.PROVIDER_URL, providerUrl);
+
+ // Follow referrals automatically
+ properties.setProperty(Context.REFERRAL, "ignore"); //BZ:582471- active directory query change
+
+ // properties = getProperties(properties);
}
return properties;
}
@@ -598,6 +625,7 @@ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
*
* @return properties that are to be used when connecting to LDAP server
*/
+ @Deprecated
private Properties getProperties(Properties systemConfig) {
Properties env = new Properties(systemConfig);
// Set our default factory name if one is not given
commit f977354b21882b73b2da5ea6dd02784e83d9957a
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Mon Jul 29 12:02:56 2013 -0400
Include default ldap property settings in initialization as well.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index cd27c32..097b993 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -585,6 +585,8 @@ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
}
}
}
+ //now load default/shared LDAP properties as we always have
+ properties = getProperties(properties);
}
return properties;
}
commit 0b2ca875a21f1e7cce3508f46a2e482611846c7f
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Mon Jul 29 01:32:10 2013 -0400
Fix some deprecated property references.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index 02de558..cd27c32 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -98,7 +98,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
Properties systemConfig = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
//retrieve the filters.
- String groupFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
+ String groupFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.name());
if ((groupFilter != null) && (!groupFilter.trim().isEmpty())) {
String filter;
if (groupFilter.startsWith("(") && groupFilter.endsWith(")")) {
@@ -116,9 +116,9 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
public Set<String> findAvailableGroupsFor(String userName) {
Properties options = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
- String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), "");
- String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), "");
- String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.getInternalName(), "false");
+ String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.name(), "");
+ String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.name(), "");
+ String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.name(), "false");
if (groupUsePosix == null) {
groupUsePosix = Boolean.toString(false);//default to false
}
@@ -299,20 +299,20 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
HashMap<String, String> userDetails = new HashMap<String, String>();
// Load the BaseDN
- String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.name());
// Load the LoginProperty
- String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.name());
if (loginProperty == null) {
// Use the default
loginProperty = "cn";
}
// Load any information we may need to bind
- String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
- String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.name());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.name());
// Load any search filter
- String searchFilter = (String) systemConfig.get(SystemSetting.LDAP_FILTER.getInternalName());
+ String searchFilter = (String) systemConfig.get(SystemSetting.LDAP_FILTER.name());
if (bindDN != null) {
systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
@@ -384,17 +384,17 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
//Load our LDAP specific properties
// Load the BaseDN
- String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.name());
// Load the LoginProperty
- String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.name());
if (loginProperty == null) {
// Use the default
loginProperty = "cn";
}
// Load any information we may need to bind
- String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
- String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.name());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.name());
if (bindDN != null) {
systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
@@ -411,7 +411,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
searchControls.setReturningAttributes(attributes);
//detect whether to use Query Page Control
- String groupUseQueryPaging = systemConfig.getProperty(SystemSetting.LDAP_GROUP_PAGING.getInternalName(),
+ String groupUseQueryPaging = systemConfig.getProperty(SystemSetting.LDAP_GROUP_PAGING.name(),
"false");
if (groupUseQueryPaging == null) {
groupUseQueryPaging = Boolean.toString(false);//default to false
@@ -426,7 +426,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
// only if they're enabled in the UI.
if (useQueryPaging) {
String groupPageSize = systemConfig.getProperty(
- SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.getInternalName(), ""
+SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
+ defaultPageSize);
if ((groupPageSize != null) && (!groupPageSize.trim().isEmpty())) {
int passedInPageSize = -1;
@@ -599,7 +599,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
private Properties getProperties(Properties systemConfig) {
Properties env = new Properties(systemConfig);
// Set our default factory name if one is not given
- String factoryName = env.getProperty(SystemSetting.LDAP_NAMING_FACTORY.getInternalName());
+ String factoryName = env.getProperty(SystemSetting.LDAP_NAMING_FACTORY.name());
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
// Setup SSL if requested
@@ -614,7 +614,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
}
// Set the LDAP url
- String providerUrl = env.getProperty(RHQConstants.LDAPUrl);
+ String providerUrl = env.getProperty(SystemSetting.LDAP_NAMING_PROVIDER_URL.name());
if (providerUrl == null) {
int port = (ldapSsl) ? 636 : 389;
providerUrl = "ldap://localhost:" + port;
commit 34ad4a2a2894435c84663c52b06bed9ee231bded
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Wed Jun 19 13:51:17 2013 -0400
Some more doc and cleanup/refactor.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index 4c0c2fc..02de558 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -156,6 +156,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
throw new IllegalArgumentException("Role with id [" + roleId + "] does not exist.");
}
+ //add some code to synch up the current list of ldap groups.
Set<LdapGroup> currentGroups = role.getLdapGroups();
List<String> currentGroupNames = new ArrayList<String>(currentGroups.size());
for (LdapGroup group : currentGroups) {
@@ -167,10 +168,12 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
newGroupNames.add(group.getName());
}
+ //figure out which ones are new then add them.
List<String> namesOfGroupsToBeAdded = new ArrayList<String>(newGroupNames);
namesOfGroupsToBeAdded.removeAll(currentGroupNames);
addLdapGroupsToRole(subject, roleId, namesOfGroupsToBeAdded);
+ //figure out which ones need to be removed. then remove them.
List<String> namesOfGroupsToBeRemoved = new ArrayList<String>(currentGroupNames);
namesOfGroupsToBeRemoved.removeAll(newGroupNames);
int[] idsOfGroupsToBeRemoved = new int[namesOfGroupsToBeRemoved.size()];
@@ -290,11 +293,10 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
}
public Map<String, String> findLdapUserDetails(String userName) {
+ // Load our LDAP specific properties
Properties systemConfig = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
HashMap<String, String> userDetails = new HashMap<String, String>();
- // Load our LDAP specific properties
- Properties env = getProperties(systemConfig);
// Load the BaseDN
String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
@@ -312,13 +314,13 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
// Load any search filter
String searchFilter = (String) systemConfig.get(SystemSetting.LDAP_FILTER.getInternalName());
if (bindDN != null) {
- env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
- env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
- env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ systemConfig.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
}
try {
- InitialLdapContext ctx = new InitialLdapContext(env, null);
+ InitialLdapContext ctx = new InitialLdapContext(systemConfig, null);
SearchControls searchControls = getSearchControls();
// Add the search filter if specified. This only allows for a single search filter.. i.e. foo=bar.
commit 34a7e70c60a4032b70ef299dcf1584a084557c9e
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Wed Jun 19 13:36:52 2013 -0400
Use property internal name.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index d80732e..4c0c2fc 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -380,7 +380,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
*/
protected Set<Map<String, String>> buildGroup(Properties systemConfig, String filter) {
Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
-
+ //Load our LDAP specific properties
// Load the BaseDN
String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
@@ -423,7 +423,8 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
int defaultPageSize = 1000;
// only if they're enabled in the UI.
if (useQueryPaging) {
- String groupPageSize = systemConfig.getProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
+ String groupPageSize = systemConfig.getProperty(
+ SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.getInternalName(), ""
+ defaultPageSize);
if ((groupPageSize != null) && (!groupPageSize.trim().isEmpty())) {
int passedInPageSize = -1;
commit dcd7ed118b33c3de223f4e78b8eec62234dd0a48
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Mon Jun 17 12:26:59 2013 -0400
Clean up some deprecated usage of Properties by LDAP, and small refactoring.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
index 7473321..d80732e 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/LdapGroupManagerBean.java
@@ -94,27 +94,30 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
private SystemManagerLocal systemManager;
public Set<Map<String, String>> findAvailableGroups() {
- Properties systemConfig = systemManager.getSystemConfiguration(subjectManager.getOverlord());
- Set<Map<String, String>> emptyAvailableGroups = new HashSet<Map<String, String>>();
+ //load current system properties
+ Properties systemConfig = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
//retrieve the filters.
- String groupFilter = (String) systemConfig.get(RHQConstants.LDAPGroupFilter);
+ String groupFilter = (String) systemConfig.get(SystemSetting.LDAP_GROUP_FILTER.getInternalName());
if ((groupFilter != null) && (!groupFilter.trim().isEmpty())) {
String filter;
- if (groupFilter.startsWith("(") && groupFilter.endsWith(")"))
+ if (groupFilter.startsWith("(") && groupFilter.endsWith(")")) {
filter = groupFilter; // RFC 2254 does not allow for ((expression))
- else
+ } else {
filter = String.format("(%s)", groupFilter); // not wrapped in (), wrap it
+ }
return buildGroup(systemConfig, filter);
}
+
+ Set<Map<String, String>> emptyAvailableGroups = new HashSet<Map<String, String>>();
return emptyAvailableGroups;
}
public Set<String> findAvailableGroupsFor(String userName) {
- Properties options = systemManager.getSystemConfiguration(subjectManager.getOverlord());
- String groupFilter = options.getProperty(RHQConstants.LDAPGroupFilter, "");
- String groupMember = options.getProperty(RHQConstants.LDAPGroupMember, "");
+ Properties options = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
+ String groupFilter = options.getProperty(SystemSetting.LDAP_GROUP_FILTER.getInternalName(), "");
+ String groupMember = options.getProperty(SystemSetting.LDAP_GROUP_MEMBER.getInternalName(), "");
String groupUsePosix = options.getProperty(SystemSetting.LDAP_GROUP_USE_POSIX.getInternalName(), "false");
if (groupUsePosix == null) {
groupUsePosix = Boolean.toString(false);//default to false
@@ -287,26 +290,27 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
}
public Map<String, String> findLdapUserDetails(String userName) {
- Properties systemConfig = systemManager.getSystemConfiguration(subjectManager.getOverlord());
+ Properties systemConfig = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
+
HashMap<String, String> userDetails = new HashMap<String, String>();
// Load our LDAP specific properties
Properties env = getProperties(systemConfig);
// Load the BaseDN
- String baseDN = (String) systemConfig.get(RHQConstants.LDAPBaseDN);
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
// Load the LoginProperty
- String loginProperty = (String) systemConfig.get(RHQConstants.LDAPLoginProperty);
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
if (loginProperty == null) {
// Use the default
loginProperty = "cn";
}
// Load any information we may need to bind
- String bindDN = (String) systemConfig.get(RHQConstants.LDAPBindDN);
- String bindPW = (String) systemConfig.get(RHQConstants.LDAPBindPW);
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
// Load any search filter
- String searchFilter = (String) systemConfig.get(RHQConstants.LDAPFilter);
+ String searchFilter = (String) systemConfig.get(SystemSetting.LDAP_FILTER.getInternalName());
if (bindDN != null) {
env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
@@ -375,29 +379,27 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
* @see org.jboss.security.auth.spi.UsernamePasswordLoginModule#validatePassword(java.lang.String,java.lang.String)
*/
protected Set<Map<String, String>> buildGroup(Properties systemConfig, String filter) {
- Set<Map<String, String>> ret = new HashSet<Map<String, String>>();
- // Load our LDAP specific properties
- Properties env = getProperties(systemConfig);
+ Set<Map<String, String>> groupDetailsMap = new HashSet<Map<String, String>>();
// Load the BaseDN
- String baseDN = (String) systemConfig.get(RHQConstants.LDAPBaseDN);
+ String baseDN = (String) systemConfig.get(SystemSetting.LDAP_BASE_DN.getInternalName());
// Load the LoginProperty
- String loginProperty = (String) systemConfig.get(RHQConstants.LDAPLoginProperty);
+ String loginProperty = (String) systemConfig.get(SystemSetting.LDAP_LOGIN_PROPERTY.getInternalName());
if (loginProperty == null) {
// Use the default
loginProperty = "cn";
}
// Load any information we may need to bind
- String bindDN = (String) systemConfig.get(RHQConstants.LDAPBindDN);
- String bindPW = (String) systemConfig.get(RHQConstants.LDAPBindPW);
+ String bindDN = (String) systemConfig.get(SystemSetting.LDAP_BIND_DN.getInternalName());
+ String bindPW = (String) systemConfig.get(SystemSetting.LDAP_BIND_PW.getInternalName());
if (bindDN != null) {
- env.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
- env.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
- env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
+ systemConfig.setProperty(Context.SECURITY_PRINCIPAL, bindDN);
+ systemConfig.setProperty(Context.SECURITY_CREDENTIALS, bindPW);
+ systemConfig.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
}
try {
- InitialLdapContext ctx = new InitialLdapContext(env, null);
+ InitialLdapContext ctx = new InitialLdapContext(systemConfig, null);
SearchControls searchControls = getSearchControls();
/*String filter = "(&(objectclass=groupOfUniqueNames)(uniqueMember=uid=" + userName
+ ",ou=People, dc=rhndev, dc=redhat, dc=com))";*/
@@ -421,8 +423,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
int defaultPageSize = 1000;
// only if they're enabled in the UI.
if (useQueryPaging) {
- Properties options = populateProperties(systemManager.getSystemSettings(subjectManager.getOverlord()));
- String groupPageSize = options.getProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
+ String groupPageSize = systemConfig.getProperty(SystemSetting.LDAP_GROUP_QUERY_PAGE_SIZE.name(), ""
+ defaultPageSize);
if ((groupPageSize != null) && (!groupPageSize.trim().isEmpty())) {
int passedInPageSize = -1;
@@ -445,11 +446,12 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
String[] baseDNs = baseDN.split(BASEDN_DELIMITER);
for (int x = 0; x < baseDNs.length; x++) {
- executeGroupSearch(filter, ret, ctx, searchControls, baseDNs, x);
+ executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
// continually parsing pages of results until we're done.
// only if they're enabled in the UI.
if (useQueryPaging) {
+
//handle paged results if they're being used here
byte[] cookie = null;
Control[] controls = ctx.getResponseControls();
@@ -466,7 +468,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
//ensure the next requests contains the session/cookie details
ctx.setRequestControls(new Control[] { new PagedResultsControl(defaultPageSize, cookie,
Control.CRITICAL) });
- executeGroupSearch(filter, ret, ctx, searchControls, baseDNs, x);
+ executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
//empty out cookie
cookie = null;
//test for further iterations
@@ -479,6 +481,25 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
}
}
}
+ //continually parsing pages of results until we're done.
+ while (cookie != null) {
+ //ensure the next requests contains the session/cookie details
+ ctx.setRequestControls(new Control[] { new PagedResultsControl(defaultPageSize, cookie,
+ Control.CRITICAL) });
+ executeGroupSearch(filter, groupDetailsMap, ctx, searchControls, baseDNs, x);
+ //empty out cookie
+ cookie = null;
+ //test for further iterations
+ controls = ctx.getResponseControls();
+ if (controls != null) {
+ for (Control control : controls) {
+ if (control instanceof PagedResultsResponseControl) {
+ PagedResultsResponseControl pagedResult = (PagedResultsResponseControl) control;
+ cookie = pagedResult.getCookie();
+ }
+ }
+ }
+ }
}
}
}
@@ -498,32 +519,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
log.error("Unexpected LDAP communciation error:" + iex.getMessage(), iex);
throw new LdapCommunicationException(iex);
}
-
- return ret;
- }
-
- /** Translate SystemSettings to familiar Properties instance since we're
- * passing not one but multiple values.
- *
- * @param systemSettings
- * @return
- */
- private Properties populateProperties(SystemSettings systemSettings) {
- Properties properties = null;
- if (systemSettings != null) {
- properties = new Properties();
- Set<Entry<SystemSetting, String>> entries = systemSettings.entrySet();
- for (Entry<SystemSetting, String> entry : entries) {
- SystemSetting key = entry.getKey();
- if (key != null) {
- String value = entry.getValue();
- if (value != null) {
- properties.put(key.name(), value);
- }
- }
- }
- }
- return properties;
+ return groupDetailsMap;
}
/** Executes the LDAP group query using the filters, context and search controls, etc. parameters passed in.
@@ -566,6 +562,30 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
}
}
+ /** Translate SystemSettings to familiar Properties instance since we're
+ * passing not one but multiple values.
+ *
+ * @param systemSettings
+ * @return
+ */
+ private Properties populateProperties(SystemSettings systemSettings) {
+ Properties properties = null;
+ if (systemSettings != null) {
+ properties = new Properties();
+ Set<Entry<SystemSetting, String>> entries = systemSettings.entrySet();
+ for (Entry<SystemSetting, String> entry : entries) {
+ SystemSetting key = entry.getKey();
+ if (key != null) {
+ String value = entry.getValue();
+ if (value != null) {
+ properties.put(key.name(), value);
+ }
+ }
+ }
+ }
+ return properties;
+ }
+
/**
* Load a default set of properties to use when connecting to the LDAP server. If basic authentication is needed,
* the caller must set Context.SECURITY_PRINCIPAL, Context.SECURITY_CREDENTIALS and Context.SECURITY_AUTHENTICATION
@@ -576,7 +596,7 @@ public class LdapGroupManagerBean implements LdapGroupManagerLocal {
private Properties getProperties(Properties systemConfig) {
Properties env = new Properties(systemConfig);
// Set our default factory name if one is not given
- String factoryName = env.getProperty(RHQConstants.LDAPFactory);
+ String factoryName = env.getProperty(SystemSetting.LDAP_NAMING_FACTORY.getInternalName());
env.setProperty(Context.INITIAL_CONTEXT_FACTORY, factoryName);
// Setup SSL if requested
commit 32327e648f5b7a8f05da633f3aed3abc27adf875
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Wed Jul 31 15:09:45 2013 +0200
[BZ 976827] - Description text area in General properties tab under Alerts -> Definition is not getting set to blank one it has been edited. Instead it displays the previous value that was stored - setting an empty string if descriptionField.getValue() == null
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/alert/definitions/GeneralPropertiesAlertDefinitionForm.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/alert/definitions/GeneralPropertiesAlertDefinitionForm.java
index 1f47a81..d501787 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/alert/definitions/GeneralPropertiesAlertDefinitionForm.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/alert/definitions/GeneralPropertiesAlertDefinitionForm.java
@@ -175,9 +175,7 @@ public class GeneralPropertiesAlertDefinitionForm extends DynamicForm implements
public void saveAlertDefinition() {
alertDefinition.setName(nameField.getValue().toString());
Object description = descriptionField.getValue();
- if (null != description) {
- alertDefinition.setDescription(description.toString());
- }
+ alertDefinition.setDescription(null == description ? "" : description.toString());
String prioritySelected = prioritySelection.getValue().toString();
alertDefinition.setPriority(AlertPriority.valueOf(prioritySelected));
commit ac9f0b85670fa41d9d08f3de7455ce8436ec5e73
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Tue Jul 30 17:43:16 2013 -0700
i18n availability summary pie graph.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
index 057a77f..fb8e096 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
@@ -18,14 +18,15 @@
*/
package org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype;
+import java.util.ArrayList;
+import java.util.List;
+
import com.smartgwt.client.widgets.HTMLFlow;
+
import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Contains the javascript chart definition for an implementation of the d3 availability chart. This implementation is
* just a line that changes color based on availability type: up=green, down=red, orange=disabled, unknown=grey,
@@ -48,7 +49,8 @@ public class AvailabilitySummaryPieGraphType {
StringBuilder divAndSvgDefs = new StringBuilder();
divAndSvgDefs.append("<div id=\"availSummaryChart\" >");
- divAndSvgDefs.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"height:"+HEIGHT+"px;\" ></svg>");
+ divAndSvgDefs.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"height:" + HEIGHT
+ + "px;\" ></svg>");
divAndSvgDefs.append("</div>");
HTMLFlow graphFlow = new HTMLFlow(divAndSvgDefs.toString());
graphFlow.setWidth(WIDTH);
@@ -56,7 +58,8 @@ public class AvailabilitySummaryPieGraphType {
return graphFlow;
}
- public void setAvailabilityData(String upLabel, double upPercent, String downLabel, double downPercent, String disabledLabel, double disabledPercent ){
+ public void setAvailabilityData(String upLabel, double upPercent, String downLabel, double downPercent,
+ String disabledLabel, double disabledPercent) {
availabilitySummaries = new ArrayList<AvailabilitySummary>();
availabilitySummaries.add(new AvailabilitySummary(upLabel, upPercent));
availabilitySummaries.add(new AvailabilitySummary(downLabel, downPercent));
@@ -70,8 +73,9 @@ public class AvailabilitySummaryPieGraphType {
// loop through the avail intervals
for (AvailabilitySummary availabilitySummary : availabilitySummaries) {
sb.append("{ \"label\":\"" + availabilitySummary.getLabel() + "\", ");
- sb.append(" \"value\": \"" + MeasurementConverterClient.format(availabilitySummary.getValue(),
- MeasurementUnits.PERCENTAGE, true) + "\" },");
+ sb.append(" \"value\": \""
+ + MeasurementConverterClient.format(availabilitySummary.getValue(), MeasurementUnits.PERCENTAGE,
+ true) + "\" },");
}
sb.setLength(sb.length() - 1);
}
@@ -87,47 +91,50 @@ public class AvailabilitySummaryPieGraphType {
public native void drawJsniChart() /*-{
console.log("Draw Availability Summary Pie Chart");
- var w = 100,
- h = 100,
- r = h/2,
- color = $wnd.d3.scale.category10(),
- data = this.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType::getAvailabilitySummaryJson()(),
- vis = $wnd.d3.select("#availSummaryChart svg")
- .append("g")
- .data(data)
- .attr("width", w)
- .attr("height", h)
- .attr("transform", "translate(" + r + "," + r + ")"),
- arc = $wnd.d3.svg.arc()
- .outerRadius(r),
- pie = $wnd.d3.layout.pie(),
- arcs = vis.selectAll("g.slice")
- .data(pie)
- .enter()
- .append("g")
- .attr("class", "slice");
+ var global = this,
+ w = 100,
+ h = 100,
+ r = h / 2,
+ color = $wnd.d3.scale.category10(),
+ data = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType::getAvailabilitySummaryJson()(),
+ vis = $wnd.d3.select("#availSummaryChart svg")
+ .append("g")
+ .data(data)
+ .attr("width", w)
+ .attr("height", h)
+ .attr("transform", "translate(" + r + "," + r + ")"),
+ arc = $wnd.d3.svg.arc()
+ .outerRadius(r),
+ pie = $wnd.d3.layout.pie(),
+ arcs = vis.selectAll("g.slice")
+ .data(pie)
+ .enter()
+ .append("g")
+ .attr("class", "slice");
arcs.append("path")
- .attr("fill", function (d, i) {
- return color(i);
- })
- .attr("d", arc);
+ .attr("fill", function (d, i) {
+ return color(i);
+ })
+ .attr("d", arc);
arcs.append("text")
- .attr("transform", function (d) {
- d.innerRadius = 0;
- d.outerRadius = r;
- return "translate(" + arc.centroid(d) + ")";
- })
- .attr("text-anchor", "middle")
- .text(function (d, i) {
- return data[i].value;
- });
+ .attr("transform", function (d) {
+ d.innerRadius = 0;
+ d.outerRadius = r;
+ return "translate(" + arc.centroid(d) + ")";
+ })
+ .attr("text-anchor", "middle")
+ .style("font-size", "9px")
+ .style("font-family", "Arial, Verdana, sans-serif;")
+ .attr("fill", "#000")
+ .text(function (d, i) {
+ return data[i].value;
+ });
console.log("done with avail summary pie graph");
}-*/;
-
private static class AvailabilitySummary {
final private String label;
final private double value;
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java
index ce4f8ef..52b0d86 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java
@@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
@@ -79,7 +80,7 @@ public class AddToDashboardComponent extends ToolStrip implements Enhanced {
addToDashboardButton.disable();
dashboardSelectItem = new SelectItem();
- dashboardSelectItem.setTitle("Dashboards");
+ dashboardSelectItem.setTitle(MSG.chart_metrics_add_to_dashboard_label());
dashboardSelectItem.setWidth(300);
dashboardSelectItem.setPickListWidth(210);
populateDashboardMenu();
@@ -102,7 +103,7 @@ public class AddToDashboardComponent extends ToolStrip implements Enhanced {
.getMetricDefinitions()) {
if (measurementDefinition.getId() == selectedRecord
.getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID)) {
- Log.debug("Add to Dashboard -- Storing: " + measurementDefinition.getDisplayName()
+ Log.info("Add to Dashboard -- Storing: " + measurementDefinition.getDisplayName()
+ " in " + selectedDashboard.getName());
storeDashboardMetric(selectedDashboard, resource, measurementDefinition);
break;
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
index cffcfd7..5835f61 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
@@ -40,7 +40,7 @@ import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
/**
- * This shows the availability history for a resource.
+ * This view shows the detail availability data in tabular form and a pie chart for available %.
*
* @author Jay Shaughnessy
* @author John Mazzitelli
@@ -49,6 +49,7 @@ import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
public class ResourceMetricAvailabilityView extends EnhancedVLayout {
private Resource resource;
+ private DynamicForm form;
private StaticTextItem currentField;
private StaticTextItem availTimeField;
private StaticTextItem downTimeField;
@@ -81,7 +82,7 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
private DynamicForm createSummaryForm() {
- DynamicForm form = new DynamicForm();
+ form = new DynamicForm();
form.setWidth100();
form.setAutoHeight();
form.setMargin(10);
@@ -138,14 +139,13 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
currentTimeField.setShowTitle(false);
CanvasItem availPieChartItem = new CanvasItem();
- //@todo: i18n
- availPieChartItem.setTitle("Availability");
+ availPieChartItem.setTitle(MSG.pie_chart_availability_summary_label());
availPieChartItem.setCanvas(availabilitySummaryPieGraph.createGraphMarker());
availPieChartItem.setRowSpan(3);
availPieChartItem.setVAlign(VerticalAlignment.TOP);
availPieChartItem.setTitleVAlign(VerticalAlignment.TOP);
- availPieChartItem.setHeight(60);
- availPieChartItem.setWidth(60);
+ availPieChartItem.setHeight(AvailabilitySummaryPieGraphType.HEIGHT);
+ availPieChartItem.setWidth(AvailabilitySummaryPieGraphType.WIDTH);
form.setItems(currentField, availPieChartItem, availTimeField, downTimeField,
disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
@@ -164,17 +164,15 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
public void onSuccess(ResourceAvailabilitySummary result) {
Log.debug("reloadSummaryData");
- //@todo: i18n
availabilitySummaryPieGraph.setAvailabilityData(
- "Up", result.getUpPercentage(),
- "Down", result.getDownPercentage(),
- "Disabled" ,result.getDisabledPercentage()
+ MSG.pie_chart_availability_summary_up(), result.getUpPercentage(),
+ MSG.pie_chart_availability_summary_down(), result.getDownPercentage(),
+ MSG.pie_chart_availability_summary_disabled() ,result.getDisabledPercentage()
);
new Timer(){
@Override
public void run() {
- Log.debug("Run Avail Graph");
availabilitySummaryPieGraph.drawJsniChart();
}
}.schedule(150);
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
index 040537d..b800267 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
@@ -21,6 +21,7 @@ chart_hover_start_label = Start
chart_hover_time_format = %I:%M:%S %p
chart_ie_not_supported = Charting is not available for this browser.
chart_metrics= Metrics
+chart_metrics_add_to_dashboard_label= Dashboards
chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
chart_metrics_sparkline_header= Chart
@@ -460,6 +461,10 @@ filter_from_date = From
filter_to_date = To
group_tree_groupOfResourceType = Group of [{0}]
group_tree_partialClusterTooltip = {0} out of {1} group members have a ''{2}'' resource
+pie_chart_availability_summary_disabled = Disabled
+pie_chart_availability_summary_down = Down
+pie_chart_availability_summary_label = Availability
+pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = Parent Ancestry for:
util_errorHandler_nullException = exception was null
util_monitoringRequestCallback_error_checkServerStatusFailure = Unable to determine login status - check Server status.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
index 2e23eff..d57e8e9 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
@@ -49,6 +49,7 @@ chart_hover_date_format = %d.%m.%y
chart_hover_time_format = %H:%M:%S
##chart_ie_not_supported = Charting is not available for this browser.
##chart_metrics= Metrics
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
@@ -479,6 +480,10 @@ filter_from_date = Od
filter_to_date = Do
group_tree_groupOfResourceType = Skupina: [{0}]
group_tree_partialClusterTooltip = {0} z {1} členů skupiny má "{2}" zdroj
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = Původ rodiče pro:
util_errorHandler_nullException = výjimka byla null
util_monitoringRequestCallback_error_checkServerStatusFailure = Nepodařilo se určit stav přihlašování - zkontrolujte stav serveru.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
index b4fe981..93f8426 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
@@ -27,6 +27,7 @@ chart_ie_not_supported = Charting ist bei diesem Browser nicht unterstützt
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
chart_no_data_label = Keine Daten vorhanden
##chart_single_value_label = Value
chart_slider_button_bar_day = Tag
@@ -458,6 +459,10 @@ filter_from_date = Von
filter_to_date = Bis
group_tree_groupOfResourceType = Gruppe von [{0}]
group_tree_partialClusterTooltip = {0} der {1} Gruppenmitglieder haben eine ''{2}'' Ressource
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_disambiguationReportDecorator_pluginSuffix = ({0} Plugin)
util_errorHandler_nullException = Exception war null
##util_monitoringRequestCallback_error_checkServerStatusFailure = Unable to determine login status - check Server status.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
index c1a8521..ae2e805 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
@@ -28,6 +28,7 @@
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -456,6 +457,10 @@ filter_from_date = 開始
filter_to_date = 終了
##group_tree_groupOfResourceType = Group of [{0}]
group_tree_partialClusterTooltip = {1} の中の {0} グループメンバーが ''{2}'' リソースを持っています
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = 親の先祖 :
util_disambiguationReportDecorator_pluginSuffix = ({0} プラグイン)
util_errorHandler_nullException = 例外は null でした
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
index 2958fbe..c61d0d6 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
@@ -26,6 +26,7 @@
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -413,6 +414,10 @@ favorites_resources = 즐겨찾기 리소스
filter_from_date = 시작
group_tree_groupOfResourceType = Group of [{0}]
group_tree_partialClusterTooltip = {1} 중 {0} 그룹 멤버가 ''{2}'' 자원을 가지고 있습니다.
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = 부모의 조상:
util_errorHandler_nullException = 예외는 null이었습니다.
util_monitoringRequestCallback_error_checkServerStatusFailure = 로그인 상태를 확인할 수 없습니다 - 서버의 상태를 확인하십시요
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
index 62f31f5..0d505ec 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
@@ -25,6 +25,7 @@
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -461,6 +462,10 @@ favorites_resources = Recursos Favoritos
##filter_to_date = To
##group_tree_groupOfResourceType = Group of [{0}]
group_tree_partialClusterTooltip = {0} out of {1} group members have a ''{2}'' resource
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = Ancestral para:
util_errorHandler_nullException = Exce\u00E7\u00E3o nula
util_monitoringRequestCallback_error_checkServerStatusFailure = Imposs\u00EDvel verificar o status do login - verifique o status do Servidor.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
index eca8c24..ace5cf2 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
@@ -25,6 +25,7 @@
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -2550,6 +2551,10 @@ favorites_resources = Избранные ресурсы
filter_from_date = От
filter_to_date = До
group_tree_partialClusterTooltip = {0} из {1} членов группы имеют ресурс ''{2}''
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_errorHandler_nullException = Возникло null исключение
util_monitoringRequestCallback_error_checkServerStatusFailure = Не удается определить статус авторизации - проверьте состояние сервера.
util_userPerm_loadFailGlobal = Не удается загрузить ваши глобальные полномочия - не предоставлены.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
index e71f1fb..287a7f9 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
@@ -25,6 +25,7 @@
##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
##chart_metrics_sparkline_header= Chart
+##chart_metrics_add_to_dashboard_label= Dashboards
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -451,6 +452,10 @@ favorites_resources = \u8d44\u6e90\u6536\u85cf\u5939
##filter_to_date = To
##group_tree_groupOfResourceType = Group of [{0}]
group_tree_partialClusterTooltip = \u9664\u53bb {1},{0}\u7684\u7ec4\u6210\u5458\u62e5\u6709 \u4e00\u4e2a''{2}''\u8d44\u6e90
+##pie_chart_availability_summary_disabled = Disabled
+##pie_chart_availability_summary_down = Down
+##pie_chart_availability_summary_label = Availability
+##pie_chart_availability_summary_up = Up
util_ancestry_parentAncestry = Parent Ancestry for:
util_errorHandler_nullException = \u5f02\u5e38\u662f null
util_monitoringRequestCallback_error_checkServerStatusFailure = \u65e0\u6cd5\u786e\u5b9a\u767b\u5f55\u72b6\u6001- \u68c0\u67e5\u670d\u52a1\u5668\u72b6\u6001.
commit b6290571f9b6c3f72886ce4ebf85dad38e11698c
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Tue Jul 30 11:42:37 2013 -0700
Fix bug with new metrics database and not much history results in NPE on metrics page.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
index 82a9d98..bb67587 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
@@ -121,9 +121,9 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
metricsTableListGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
@Override
public void onSelectionChanged(SelectionEvent selectionEvent) {
- if(metricsTableListGrid.getSelectedRecords().length > 0){
+ if (metricsTableListGrid.getSelectedRecords().length > 0) {
addToDashboardComponent.enableAddToDashboardButton();
- }else {
+ } else {
addToDashboardComponent.disableAddToDashboardButton();
}
}
@@ -304,11 +304,15 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
public void onDataArrived(DataArrivedEvent dataArrivedEvent) {
int startRow = dataArrivedEvent.getStartRow();
int endRow = dataArrivedEvent.getEndRow();
+
for (int i = startRow; i < endRow; i++) {
- if (null != metricsTableView && null != metricsTableView.expandedRows
- && metricsTableView.expandedRows.contains(getRecord(i).getAttributeAsInt(
- MetricsViewDataSource.FIELD_METRIC_DEF_ID))) {
- expandRecord(getRecord(i));
+ ListGridRecord listGridRecord = getRecord(i);
+ if (null != listGridRecord) {
+ int metricDefinitionId = listGridRecord
+ .getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
+ if (null != metricsTableView && metricsTableView.expandedRows.contains(metricDefinitionId)) {
+ expandRecord(listGridRecord);
+ }
}
}
}
commit 865522aac13f00413c818ad6714118ffcf913f6f
Author: mtho11 <mikecthompson(a)gmail.com>
Date: Tue Jul 30 10:59:02 2013 -0700
Change layout of fields in availability detail form of metric page.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
index ee06724..3e573dd 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
@@ -230,8 +230,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
- console.error("avail.chartSelection: "+ availChartContext.chartSelection);
-
svg.selectAll("rect.availBars")
.data(availChartContext.data)
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
index 23effb0..057a77f 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
@@ -18,7 +18,10 @@
*/
package org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype;
+import com.smartgwt.client.widgets.HTMLFlow;
+import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
import java.util.ArrayList;
import java.util.List;
@@ -32,11 +35,27 @@ import java.util.List;
*/
public class AvailabilitySummaryPieGraphType {
+ public static final int HEIGHT = 100;
+ public static final int WIDTH = 100;
+
private List<AvailabilitySummary> availabilitySummaries;
public AvailabilitySummaryPieGraphType() {
}
+ public HTMLFlow createGraphMarker() {
+ Log.debug("drawGraph marker in AvailabilitySummaryPieGraph");
+
+ StringBuilder divAndSvgDefs = new StringBuilder();
+ divAndSvgDefs.append("<div id=\"availSummaryChart\" >");
+ divAndSvgDefs.append("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"height:"+HEIGHT+"px;\" ></svg>");
+ divAndSvgDefs.append("</div>");
+ HTMLFlow graphFlow = new HTMLFlow(divAndSvgDefs.toString());
+ graphFlow.setWidth(WIDTH);
+ graphFlow.setHeight(HEIGHT);
+ return graphFlow;
+ }
+
public void setAvailabilityData(String upLabel, double upPercent, String downLabel, double downPercent, String disabledLabel, double disabledPercent ){
availabilitySummaries = new ArrayList<AvailabilitySummary>();
availabilitySummaries.add(new AvailabilitySummary(upLabel, upPercent));
@@ -51,7 +70,8 @@ public class AvailabilitySummaryPieGraphType {
// loop through the avail intervals
for (AvailabilitySummary availabilitySummary : availabilitySummaries) {
sb.append("{ \"label\":\"" + availabilitySummary.getLabel() + "\", ");
- sb.append(" \"value\": \"" + availabilitySummary.getValue() * 100 + "\" },");
+ sb.append(" \"value\": \"" + MeasurementConverterClient.format(availabilitySummary.getValue(),
+ MeasurementUnits.PERCENTAGE, true) + "\" },");
}
sb.setLength(sb.length() - 1);
}
@@ -67,26 +87,21 @@ public class AvailabilitySummaryPieGraphType {
public native void drawJsniChart() /*-{
console.log("Draw Availability Summary Pie Chart");
- var data = this.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType::getAvailabilitySummaryJson()();
-
var w = 100,
h = 100,
- r = 30,
- color = $wnd.d3.scale.category10();
-
- var vis = $wnd.d3.select("#availSummaryChart svg")
+ r = h/2,
+ color = $wnd.d3.scale.category10(),
+ data = this.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType::getAvailabilitySummaryJson()(),
+ vis = $wnd.d3.select("#availSummaryChart svg")
.append("g")
.data(data)
.attr("width", w)
.attr("height", h)
- .attr("transform", "translate(" + r + "," + r + ")");
-
- var arc = $wnd.d3.svg.arc()
- .outerRadius(r);
-
- var pie = $wnd.d3.layout.pie();
-
- var arcs = vis.selectAll("g.slice")
+ .attr("transform", "translate(" + r + "," + r + ")"),
+ arc = $wnd.d3.svg.arc()
+ .outerRadius(r),
+ pie = $wnd.d3.layout.pie(),
+ arcs = vis.selectAll("g.slice")
.data(pie)
.enter()
.append("g")
@@ -108,7 +123,7 @@ public class AvailabilitySummaryPieGraphType {
.text(function (d, i) {
return data[i].value;
});
- console.warn("done with avail summary pie graph");
+ console.log("done with avail summary pie graph");
}-*/;
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
index 961ea5a..cffcfd7 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
@@ -22,8 +22,9 @@ package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitori
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.smartgwt.client.widgets.HTMLFlow;
+import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.CanvasItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
@@ -49,11 +50,8 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
private Resource resource;
private StaticTextItem currentField;
- private StaticTextItem availField;
private StaticTextItem availTimeField;
- private StaticTextItem downField;
private StaticTextItem downTimeField;
- private StaticTextItem disabledField;
private StaticTextItem disabledTimeField;
private StaticTextItem failureCountField;
private StaticTextItem disabledCountField;
@@ -71,29 +69,16 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
availabilitySummaryPieGraph = new AvailabilitySummaryPieGraphType();
setWidth100();
- setHeight(265);
+ setHeight(165);
}
@Override
protected void onInit() {
super.onInit();
- addMember(createGraphMarker());
addMember(createSummaryForm());
}
- public HTMLFlow createGraphMarker() {
- Log.debug("drawGraph marker in AvailabilitySummaryPieGraph");
-
- StringBuilder divAndSvgDefs = new StringBuilder();
- divAndSvgDefs.append("<div id=\"availSummaryChart"
- + "\" ><svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"height:100px;\">");
- divAndSvgDefs.append("</svg></div>");
- HTMLFlow graph = new HTMLFlow(divAndSvgDefs.toString());
- graph.setWidth100();
- graph.setHeight(100);
- //addMember(graph);
- return graph;
- }
+
private DynamicForm createSummaryForm() {
DynamicForm form = new DynamicForm();
@@ -108,28 +93,16 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
currentField.setColSpan(4);
// row 2
- availField = new StaticTextItem("avail", MSG.view_resource_monitor_availability_availability());
- availField.setWrapTitle(false);
- prepareTooltip(availField, MSG.view_resource_monitor_availability_availability_tooltip());
-
availTimeField = new StaticTextItem("availTime", MSG.view_resource_monitor_availability_uptime());
availTimeField.setWrapTitle(false);
prepareTooltip(availTimeField, MSG.view_resource_monitor_availability_uptime_tooltip());
// row 3
- downField = new StaticTextItem("down", MSG.view_resource_monitor_availability_down());
- downField.setWrapTitle(false);
- prepareTooltip(downField, MSG.view_resource_monitor_availability_down_tooltip());
-
downTimeField = new StaticTextItem("downTime", MSG.view_resource_monitor_availability_downtime());
downTimeField.setWrapTitle(false);
prepareTooltip(downTimeField, MSG.view_resource_monitor_availability_downtime_tooltip());
// row 4
- disabledField = new StaticTextItem("disabled", MSG.view_resource_monitor_availability_disabled());
- disabledField.setWrapTitle(false);
- prepareTooltip(disabledField, MSG.view_resource_monitor_availability_disabled_tooltip());
-
disabledTimeField = new StaticTextItem("disabledTime", MSG.view_resource_monitor_availability_disabledTime());
disabledTimeField.setWrapTitle(false);
prepareTooltip(disabledTimeField, MSG.view_resource_monitor_availability_disabledTime_tooltip());
@@ -164,7 +137,17 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
currentTimeField.setColSpan(4);
currentTimeField.setShowTitle(false);
- form.setItems(currentField, availField, availTimeField, downField, downTimeField, disabledField,
+ CanvasItem availPieChartItem = new CanvasItem();
+ //@todo: i18n
+ availPieChartItem.setTitle("Availability");
+ availPieChartItem.setCanvas(availabilitySummaryPieGraph.createGraphMarker());
+ availPieChartItem.setRowSpan(3);
+ availPieChartItem.setVAlign(VerticalAlignment.TOP);
+ availPieChartItem.setTitleVAlign(VerticalAlignment.TOP);
+ availPieChartItem.setHeight(60);
+ availPieChartItem.setWidth(60);
+
+ form.setItems(currentField, availPieChartItem, availTimeField, downTimeField,
disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
currentTimeField);
@@ -181,6 +164,7 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
public void onSuccess(ResourceAvailabilitySummary result) {
Log.debug("reloadSummaryData");
+ //@todo: i18n
availabilitySummaryPieGraph.setAvailabilityData(
"Up", result.getUpPercentage(),
"Down", result.getDownPercentage(),
@@ -197,16 +181,10 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(result
.getCurrent().getName(), TimestampCellFormatter.format(result.getLastChange().getTime())));
- availField.setValue(MeasurementConverterClient.format(result.getUpPercentage(),
- MeasurementUnits.PERCENTAGE, true));
availTimeField.setValue(MeasurementConverterClient.format((double) result.getUpTime(),
MeasurementUnits.MILLISECONDS, true));
- downField.setValue(MeasurementConverterClient.format(result.getDownPercentage(),
- MeasurementUnits.PERCENTAGE, true));
downTimeField.setValue(MeasurementConverterClient.format((double) result.getDownTime(),
MeasurementUnits.MILLISECONDS, true));
- disabledField.setValue(MeasurementConverterClient.format(result.getDisabledPercentage(),
- MeasurementUnits.PERCENTAGE, true));
disabledTimeField.setValue(MeasurementConverterClient.format((double) result.getDisabledTime(),
MeasurementUnits.MILLISECONDS, true));
failureCountField.setValue(result.getFailures());
commit 3c77e8807e7f07b3f309622156637a519ce0ef1b
Author: mtho11 <mikecthompson(a)gmail.com>
Date: Mon Jul 29 20:07:11 2013 -0700
Availability pie chart for metrics page.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
index 811a579..ee06724 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
@@ -230,6 +230,8 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
.attr("height", height + margin.top + margin.bottom)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+ console.error("avail.chartSelection: "+ availChartContext.chartSelection);
+
svg.selectAll("rect.availBars")
.data(availChartContext.data)
@@ -329,7 +331,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
// Public API
draw: function (availChartContext) {
"use strict";
- console.log("AvailabilityChart");
drawBars(availChartContext);
createHovers();
}
@@ -338,7 +339,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
}();
- console.log("Avail Data records: "+availChartContext.data.length);
if (typeof availChartContext.data !== 'undefined' && availChartContext.data.length > 0) {
availabilityGraph.draw(availChartContext);
console.log("Availability Chart Drawn");
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
new file mode 100644
index 0000000..23effb0
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilitySummaryPieGraphType.java
@@ -0,0 +1,133 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2012 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype;
+
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains the javascript chart definition for an implementation of the d3 availability chart. This implementation is
+ * just a line that changes color based on availability type: up=green, down=red, orange=disabled, unknown=grey,
+ * empty=grey, warn=yellow. This version of the availability graph shows continuous intervals.
+ *
+ * @author Mike Thompson
+ */
+public class AvailabilitySummaryPieGraphType {
+
+ private List<AvailabilitySummary> availabilitySummaries;
+
+ public AvailabilitySummaryPieGraphType() {
+ }
+
+ public void setAvailabilityData(String upLabel, double upPercent, String downLabel, double downPercent, String disabledLabel, double disabledPercent ){
+ availabilitySummaries = new ArrayList<AvailabilitySummary>();
+ availabilitySummaries.add(new AvailabilitySummary(upLabel, upPercent));
+ availabilitySummaries.add(new AvailabilitySummary(downLabel, downPercent));
+ availabilitySummaries.add(new AvailabilitySummary(disabledLabel, disabledPercent));
+
+ }
+
+ public String getAvailabilitySummaryJson() {
+ StringBuilder sb = new StringBuilder("[");
+ if (null != availabilitySummaries) {
+ // loop through the avail intervals
+ for (AvailabilitySummary availabilitySummary : availabilitySummaries) {
+ sb.append("{ \"label\":\"" + availabilitySummary.getLabel() + "\", ");
+ sb.append(" \"value\": \"" + availabilitySummary.getValue() * 100 + "\" },");
+ }
+ sb.setLength(sb.length() - 1);
+ }
+
+ sb.append("]");
+ Log.debug(sb.toString());
+ return sb.toString();
+ }
+
+ /**
+ * The magic JSNI to draw the charts with d3.
+ */
+ public native void drawJsniChart() /*-{
+ console.log("Draw Availability Summary Pie Chart");
+
+ var data = this.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType::getAvailabilitySummaryJson()();
+
+ var w = 100,
+ h = 100,
+ r = 30,
+ color = $wnd.d3.scale.category10();
+
+ var vis = $wnd.d3.select("#availSummaryChart svg")
+ .append("g")
+ .data(data)
+ .attr("width", w)
+ .attr("height", h)
+ .attr("transform", "translate(" + r + "," + r + ")");
+
+ var arc = $wnd.d3.svg.arc()
+ .outerRadius(r);
+
+ var pie = $wnd.d3.layout.pie();
+
+ var arcs = vis.selectAll("g.slice")
+ .data(pie)
+ .enter()
+ .append("g")
+ .attr("class", "slice");
+
+ arcs.append("path")
+ .attr("fill", function (d, i) {
+ return color(i);
+ })
+ .attr("d", arc);
+
+ arcs.append("text")
+ .attr("transform", function (d) {
+ d.innerRadius = 0;
+ d.outerRadius = r;
+ return "translate(" + arc.centroid(d) + ")";
+ })
+ .attr("text-anchor", "middle")
+ .text(function (d, i) {
+ return data[i].value;
+ });
+ console.warn("done with avail summary pie graph");
+
+ }-*/;
+
+
+ private static class AvailabilitySummary {
+ final private String label;
+ final private double value;
+
+ private AvailabilitySummary(String label, double value) {
+ this.label = label;
+ this.value = value;
+ }
+
+ private String getLabel() {
+ return label;
+ }
+
+ private double getValue() {
+ return value;
+ }
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
index 09a7bc3..82a9d98 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
@@ -305,7 +305,7 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
int startRow = dataArrivedEvent.getStartRow();
int endRow = dataArrivedEvent.getEndRow();
for (int i = startRow; i < endRow; i++) {
- if (null != metricsTableView.expandedRows
+ if (null != metricsTableView && null != metricsTableView.expandedRows
&& metricsTableView.expandedRows.contains(getRecord(i).getAttributeAsInt(
MetricsViewDataSource.FIELD_METRIC_DEF_ID))) {
expandRecord(getRecord(i));
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
index 9d6b892..961ea5a 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
@@ -20,7 +20,9 @@
package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
@@ -31,6 +33,8 @@ import org.rhq.core.domain.resource.composite.ResourceAvailabilitySummary;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.components.table.TimestampCellFormatter;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilitySummaryPieGraphType;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
@@ -58,22 +62,39 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
private StaticTextItem unknownField;
private StaticTextItem currentTimeField;
+ private AvailabilitySummaryPieGraphType availabilitySummaryPieGraph;
+
public ResourceMetricAvailabilityView(Resource resource) {
super();
this.resource = resource;
+ availabilitySummaryPieGraph = new AvailabilitySummaryPieGraphType();
setWidth100();
- setHeight(165);
+ setHeight(265);
}
@Override
protected void onInit() {
super.onInit();
-
+ addMember(createGraphMarker());
addMember(createSummaryForm());
}
+ public HTMLFlow createGraphMarker() {
+ Log.debug("drawGraph marker in AvailabilitySummaryPieGraph");
+
+ StringBuilder divAndSvgDefs = new StringBuilder();
+ divAndSvgDefs.append("<div id=\"availSummaryChart"
+ + "\" ><svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" style=\"height:100px;\">");
+ divAndSvgDefs.append("</svg></div>");
+ HTMLFlow graph = new HTMLFlow(divAndSvgDefs.toString());
+ graph.setWidth100();
+ graph.setHeight(100);
+ //addMember(graph);
+ return graph;
+ }
+
private DynamicForm createSummaryForm() {
DynamicForm form = new DynamicForm();
form.setWidth100();
@@ -158,6 +179,21 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
@Override
public void onSuccess(ResourceAvailabilitySummary result) {
+ Log.debug("reloadSummaryData");
+
+ availabilitySummaryPieGraph.setAvailabilityData(
+ "Up", result.getUpPercentage(),
+ "Down", result.getDownPercentage(),
+ "Disabled" ,result.getDisabledPercentage()
+ );
+ new Timer(){
+
+ @Override
+ public void run() {
+ Log.debug("Run Avail Graph");
+ availabilitySummaryPieGraph.drawJsniChart();
+ }
+ }.schedule(150);
currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(result
.getCurrent().getName(), TimestampCellFormatter.format(result.getLastChange().getTime())));
@@ -189,6 +225,8 @@ public class ResourceMetricAvailabilityView extends EnhancedVLayout {
currentTimeField.setValue(MSG.view_resource_monitor_availability_currentAsOf(TimestampCellFormatter
.format(result.getCurrentTime())));
+
+
}
@Override
commit 476581aacd624552d8757c4b82015c3afc6920cb
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 16:13:09 2013 -0400
forgot to add test resource file in previous commit
diff --git a/modules/plugins/rhq-storage/src/test/resources/cassandra.yaml b/modules/plugins/rhq-storage/src/test/resources/cassandra.yaml
new file mode 100644
index 0000000..fd7973b
--- /dev/null
+++ b/modules/plugins/rhq-storage/src/test/resources/cassandra.yaml
@@ -0,0 +1,690 @@
+# Cassandra storage config YAML
+
+# NOTE:
+# See http://wiki.apache.org/cassandra/StorageConfiguration for
+# full explanations of configuration directives
+# /NOTE
+
+# The name of the cluster. This is mainly used to prevent machines in
+# one logical cluster from joining another.
+cluster_name: rhq
+
+# This defines the number of tokens randomly assigned to this node on the ring
+# The more tokens, relative to other nodes, the larger the proportion of data
+# that this node will store. You probably want all nodes to have the same number
+# of tokens assuming they have equal hardware capability.
+#
+# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
+# and will use the initial_token as described below.
+#
+# Specifying initial_token will override this setting.
+#
+# If you already have a cluster with 1 token per node, and wish to migrate to
+# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
+num_tokens: 256
+
+# If you haven't specified num_tokens, or have set it to the default of 1 then
+# you should always specify InitialToken when setting up a production
+# cluster for the first time, and often when adding capacity later.
+# The principle is that each node should be given an equal slice of
+# the token ring; see http://wiki.apache.org/cassandra/Operations
+# for more details.
+#
+# If blank, Cassandra will request a token bisecting the range of
+# the heaviest-loaded existing node. If there is no load information
+# available, such as is the case with a new cluster, it will pick
+# a random token, which will lead to hot spots.
+#initial_token:
+
+# See http://wiki.apache.org/cassandra/HintedHandoff
+hinted_handoff_enabled: true
+# this defines the maximum amount of time a dead host will have hints
+# generated. After it has been dead this long, new hints for it will not be
+# created until it has been seen alive and gone down again.
+max_hint_window_in_ms: 10800000 # 3 hours
+# throttle in KB's per second, per delivery thread
+hinted_handoff_throttle_in_kb: 1024
+# Number of threads with which to deliver hints;
+# Consider increasing this number when you have multi-dc deployments, since
+# cross-dc handoff tends to be slower
+max_hints_delivery_threads: 2
+
+# The following setting populates the page cache on memtable flush and compaction
+# WARNING: Enable this setting only when the whole node's data fits in memory.
+# Defaults to: false
+# populate_io_cache_on_flush: false
+
+# Authentication backend, implementing IAuthenticator; used to identify users
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
+# PasswordAuthenticator}.
+#
+# - AllowAllAuthenticator performs no checks - set it to disable authentication.
+# - PasswordAuthenticator relies on username/password pairs to authenticate
+# users. It keeps usernames and hashed passwords in system_auth.credentials table.
+# Please increase system_auth keyspace replication factor if you use this authenticator.
+authenticator: org.apache.cassandra.auth.PasswordAuthenticator
+
+# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
+# CassandraAuthorizer}.
+#
+# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
+# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
+# increase system_auth keyspace replication factor if you use this authorizer.
+authorizer: org.apache.cassandra.auth.CassandraAuthorizer
+
+# Validity period for permissions cache (fetching permissions can be an
+# expensive operation depending on the authorizer, CassandraAuthorizer is
+# one example). Defaults to 2000, set to 0 to disable.
+# Will be disabled automatically for AllowAllAuthorizer.
+permissions_validity_in_ms: 2000
+
+# The partitioner is responsible for distributing rows (by key) across
+# nodes in the cluster. Any IPartitioner may be used, including your
+# own as long as it is on the classpath. Out of the box, Cassandra
+# provides org.apache.cassandra.dht.{Murmur3Partitioner, RandomPartitioner
+# ByteOrderedPartitioner, OrderPreservingPartitioner (deprecated)}.
+#
+# - RandomPartitioner distributes rows across the cluster evenly by md5.
+# This is the default prior to 1.2 and is retained for compatibility.
+# - Murmur3Partitioner is similar to RandomPartioner but uses Murmur3_128
+# Hash Function instead of md5. When in doubt, this is the best option.
+# - ByteOrderedPartitioner orders rows lexically by key bytes. BOP allows
+# scanning rows in key order, but the ordering can generate hot spots
+# for sequential insertion workloads.
+# - OrderPreservingPartitioner is an obsolete form of BOP, that stores
+# - keys in a less-efficient format and only works with keys that are
+# UTF8-encoded Strings.
+# - CollatingOPP colates according to EN,US rules rather than lexical byte
+# ordering. Use this as an example if you need custom collation.
+#
+# See http://wiki.apache.org/cassandra/Operations for more on
+# partitioners and token selection.
+partitioner: org.apache.cassandra.dht.Murmur3Partitioner
+
+# directories where Cassandra should store data on disk.
+data_file_directories:
+ - /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/data
+
+# commit log
+commitlog_directory: /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/commit_log
+
+# policy for data disk failures:
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# still inspectable via JMX.
+# best_effort: stop using the failed disk and respond to requests based on
+# remaining available sstables. This means you WILL see obsolete
+# data at CL.ONE!
+# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
+disk_failure_policy: stop
+
+# Maximum size of the key cache in memory.
+#
+# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
+# minimum, sometimes more. The key cache is fairly tiny for the amount of
+# time it saves, so it's worthwhile to use it at large numbers.
+# The row cache saves even more time, but must store the whole values of
+# its rows, so it is extremely space-intensive. It's best to only use the
+# row cache if you have hot rows or static rows.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
+key_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# safe the keys cache. Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 14400 or 4 hours.
+key_cache_save_period: 14400
+
+# Number of keys from the key cache to save
+# Disabled by default, meaning all keys are going to be saved
+# key_cache_keys_to_save: 100
+
+# Maximum size of the row cache in memory.
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is 0, to disable row caching.
+row_cache_size_in_mb: 0
+
+# Duration in seconds after which Cassandra should
+# safe the row cache. Caches are saved to saved_caches_directory as specified
+# in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 0 to disable saving the row cache.
+row_cache_save_period: 0
+
+# Number of keys from the row cache to save
+# Disabled by default, meaning all keys are going to be saved
+# row_cache_keys_to_save: 100
+
+# The provider for the row cache to use.
+#
+# Supported values are: ConcurrentLinkedHashCacheProvider, SerializingCacheProvider
+#
+# SerializingCacheProvider serialises the contents of the row and stores
+# it in native memory, i.e., off the JVM Heap. Serialized rows take
+# significantly less memory than "live" rows in the JVM, so you can cache
+# more rows in a given memory footprint. And storing the cache off-heap
+# means you can use smaller heap sizes, reducing the impact of GC pauses.
+#
+# It is also valid to specify the fully-qualified class name to a class
+# that implements org.apache.cassandra.cache.IRowCacheProvider.
+#
+# Defaults to SerializingCacheProvider
+row_cache_provider: SerializingCacheProvider
+
+# saved caches
+saved_caches_directory: /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/saved_caches
+
+# commitlog_sync may be either "periodic" or "batch."
+# When in batch mode, Cassandra won't ack writes until the commit log
+# has been fsynced to disk. It will wait up to
+# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
+# performing the sync.
+#
+# commitlog_sync: batch
+# commitlog_sync_batch_window_in_ms: 50
+#
+# the other option is "periodic" where writes may be acked immediately
+# and the CommitLog is simply synced every commitlog_sync_period_in_ms
+# milliseconds.
+commitlog_sync: periodic
+commitlog_sync_period_in_ms: 10000
+
+# The size of the individual commitlog file segments. A commitlog
+# segment may be archived, deleted, or recycled once all the data
+# in it (potentally from each columnfamily in the system) has been
+# flushed to sstables.
+#
+# The default size is 32, which is almost always fine, but if you are
+# archiving commitlog segments (see commitlog_archiving.properties),
+# then you probably want a finer granularity of archiving; 8 or 16 MB
+# is reasonable.
+commitlog_segment_size_in_mb: 32
+
+# any class that implements the SeedProvider interface and has a
+# constructor that takes a Map<String, String> of parameters will do.
+seed_provider:
+ # Addresses of hosts that are deemed contact points.
+ # Cassandra nodes use this list of hosts to find each other and learn
+ # the topology of the ring. You must change this if you are running
+ # multiple nodes!
+ - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+ parameters:
+ # seeds is actually a comma-delimited list of addresses.
+ # Ex: "<ip1>,<ip2>,<ip3>"
+ - seeds: "127.0.0.1"
+
+# emergency pressure valve: each time heap usage after a full (CMS)
+# garbage collection is above this fraction of the max, Cassandra will
+# flush the largest memtables.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+#
+# RELYING ON THIS AS YOUR PRIMARY TUNING MECHANISM WILL WORK POORLY:
+# it is most effective under light to moderate load, or read-heavy
+# workloads; under truly massive write load, it will often be too
+# little, too late.
+flush_largest_memtables_at: 0.75
+
+# emergency pressure valve #2: the first time heap usage after a full
+# (CMS) garbage collection is above this fraction of the max,
+# Cassandra will reduce cache maximum _capacity_ to the given fraction
+# of the current _size_. Should usually be set substantially above
+# flush_largest_memtables_at, since that will have less long-term
+# impact on the system.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+reduce_cache_sizes_at: 0.85
+reduce_cache_capacity_to: 0.6
+
+# For workloads with more data than can fit in memory, Cassandra's
+# bottleneck will be reads that need to fetch data from
+# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
+# order to allow the operations to enqueue low enough in the stack
+# that the OS and drives can reorder them.
+#
+# On the other hand, since writes are almost never IO bound, the ideal
+# number of "concurrent_writes" is dependent on the number of cores in
+# your system; (8 * number_of_cores) is a good rule of thumb.
+concurrent_reads: 32
+concurrent_writes: 32
+
+# Total memory to use for memtables. Cassandra will flush the largest
+# memtable when this much memory is used.
+# If omitted, Cassandra will set it to 1/3 of the heap.
+# memtable_total_space_in_mb: 2048
+
+# Total space to use for commitlogs. Since commitlog segments are
+# mmapped, and hence use up address space, the default size is 32
+# on 32-bit JVMs, and 1024 on 64-bit JVMs.
+#
+# If space gets above this value (it will round up to the next nearest
+# segment multiple), Cassandra will flush every dirty CF in the oldest
+# segment and remove it. So a small total commitlog space will tend
+# to cause more flush activity on less-active columnfamilies.
+# commitlog_total_space_in_mb: 4096
+
+# This sets the amount of memtable flush writer threads. These will
+# be blocked by disk io, and each one will hold a memtable in memory
+# while blocked. If you have a large heap and many data directories,
+# you can increase this value for better flush performance.
+# By default this will be set to the amount of data directories defined.
+#memtable_flush_writers: 1
+
+# the number of full memtables to allow pending flush, that is,
+# waiting for a writer thread. At a minimum, this should be set to
+# the maximum number of secondary indexes created on a single CF.
+memtable_flush_queue_size: 4
+
+# Whether to, when doing sequential writing, fsync() at intervals in
+# order to force the operating system to flush the dirty
+# buffers. Enable this to avoid sudden dirty buffer flushing from
+# impacting read latencies. Almost always a good idea on SSD:s; not
+# necessarily on platters.
+trickle_fsync: false
+trickle_fsync_interval_in_kb: 10240
+
+# TCP port, for commands and data
+storage_port: 7100
+
+# SSL port, for encrypted communication. Unused unless enabled in
+# encryption_options
+ssl_storage_port: 7101
+
+# Address to bind to and tell other Cassandra nodes to connect to. You
+# _must_ change this if you want multiple nodes to be able to
+# communicate!
+#
+# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
+# will always do the Right Thing *if* the node is properly configured
+# (hostname, name resolution, etc), and the Right Thing is to use the
+# address associated with the hostname (it might not be).
+#
+# Setting this to 0.0.0.0 is always wrong.
+listen_address: 127.0.0.1
+
+# Address to broadcast to other Cassandra nodes
+# Leaving this blank will set it to the same value as listen_address
+# broadcast_address: 1.2.3.4
+
+# Internode authentication backend, implementing IInternodeAuthenticator;
+# used to allow/disallow connections from peer nodes.
+internode_authenticator: org.rhq.cassandra.auth.RhqInternodeAuthenticator
+
+# Whether to start the native transport server.
+# Currently, only the thrift server is started by default because the native
+# transport is considered beta.
+# Please note that the address on which the native transport is bound is the
+# same as the rpc_address. The port however is different and specified below.
+start_native_transport: true
+# port for the CQL native transport to listen for clients on
+native_transport_port: 9142
+# The minimum and maximum threads for handling requests when the native
+# transport is used. The meaning is those is similar to the one of
+# rpc_min_threads and rpc_max_threads, though the default differ slightly and
+# are the ones below:
+# native_transport_min_threads: 16
+native_transport_max_threads: 128
+
+
+# Whether to start the thrift rpc server.
+start_rpc: false
+# The address to bind the Thrift RPC service to -- clients connect
+# here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if
+# you want Thrift to listen on all interfaces.
+#
+# Leaving this blank has the same effect it does for ListenAddress,
+# (i.e. it will be based on the configured hostname of the node).
+rpc_address: 127.0.0.1
+# port for Thrift to listen for clients on
+rpc_port: 9160
+
+# enable or disable keepalive on rpc connections
+rpc_keepalive: true
+
+# Cassandra provides three out-of-the-box options for the RPC Server:
+#
+# sync -> One thread per thrift connection. For a very large number of clients, memory
+# will be your limiting factor. On a 64 bit JVM, 128KB is the minimum stack size
+# per thread, and that will correspond to your use of virtual memory (but physical memory
+# may be limited depending on use of stack space).
+#
+# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
+# asynchronously using a small number of threads that does not vary with the amount
+# of thrift clients (and thus scales well to many clients). The rpc requests are still
+# synchronous (one thread per active request).
+#
+# The default is sync because on Windows hsha is about 30% slower. On Linux,
+# sync/hsha performance is about the same, with hsha of course using less memory.
+#
+# Alternatively, can provide your own RPC server by providing the fully-qualified class name
+# of an o.a.c.t.TServerFactory that can create an instance of it.
+rpc_server_type: sync
+
+# Uncomment rpc_min|max_thread to set request pool size limits.
+#
+# Regardless of your choice of RPC server (see above), the number of maximum requests in the
+# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
+# RPC server, it also dictates the number of clients that can be connected at all).
+#
+# The default is unlimited and thus provide no protection against clients overwhelming the server. You are
+# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
+# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
+#
+# rpc_min_threads: 16
+# rpc_max_threads: 2048
+
+# uncomment to set socket buffer sizes on rpc connections
+# rpc_send_buff_size_in_bytes:
+# rpc_recv_buff_size_in_bytes:
+
+# uncomment to set socket buffer size for internode communication
+# Note that when setting this, the buffer size is limited by net.core.wmem_max
+# and when not setting it it is defined by net.ipv4.tcp_wmem
+# See:
+# /proc/sys/net/core/wmem_max
+# /proc/sys/net/core/rmem_max
+# /proc/sys/net/ipv4/tcp_wmem
+# /proc/sys/net/ipv4/tcp_wmem
+# and: man tcp
+# internode_send_buff_size_in_bytes:
+# internode_recv_buff_size_in_bytes:
+
+# Frame size for thrift (maximum field length).
+thrift_framed_transport_size_in_mb: 15
+
+# The max length of a thrift message, including all fields and
+# internal thrift overhead.
+thrift_max_message_length_in_mb: 16
+
+# Set to true to have Cassandra create a hard link to each sstable
+# flushed or streamed locally in a backups/ subdirectory of the
+# Keyspace data. Removing these links is the operator's
+# responsibility.
+incremental_backups: false
+
+# Whether or not to take a snapshot before each compaction. Be
+# careful using this option, since Cassandra won't clean up the
+# snapshots for you. Mostly useful if you're paranoid when there
+# is a data format change.
+snapshot_before_compaction: false
+
+# Whether or not a snapshot is taken of the data before keyspace truncation
+# or dropping of column families. The STRONGLY advised default of true
+# should be used to provide data safety. If you set this flag to false, you will
+# lose data on truncation or drop.
+auto_snapshot: true
+
+# Add column indexes to a row after its contents reach this size.
+# Increase if your column values are large, or if you have a very large
+# number of columns. The competing causes are, Cassandra has to
+# deserialize this much of the row to read a single column, so you want
+# it to be small - at least if you do many partial-row reads - but all
+# the index data is read for each access, so you don't want to generate
+# that wastefully either.
+column_index_size_in_kb: 64
+
+# Size limit for rows being compacted in memory. Larger rows will spill
+# over to disk and use a slower two-pass compaction process. A message
+# will be logged specifying the row key.
+in_memory_compaction_limit_in_mb: 64
+
+# Number of simultaneous compactions to allow, NOT including
+# validation "compactions" for anti-entropy repair. Simultaneous
+# compactions can help preserve read performance in a mixed read/write
+# workload, by mitigating the tendency of small sstables to accumulate
+# during a single long running compactions. The default is usually
+# fine and if you experience problems with compaction running too
+# slowly or too fast, you should look at
+# compaction_throughput_mb_per_sec first.
+#
+# concurrent_compactors defaults to the number of cores.
+# Uncomment to make compaction mono-threaded, the pre-0.8 default.
+#concurrent_compactors: 1
+
+# Multi-threaded compaction. When enabled, each compaction will use
+# up to one thread per core, plus one thread per sstable being merged.
+# This is usually only useful for SSD-based hardware: otherwise,
+# your concern is usually to get compaction to do LESS i/o (see:
+# compaction_throughput_mb_per_sec), not more.
+multithreaded_compaction: false
+
+# Throttles compaction to the given total throughput across the entire
+# system. The faster you insert data, the faster you need to compact in
+# order to keep the sstable count down, but in general, setting this to
+# 16 to 32 times the rate you are inserting data is more than sufficient.
+# Setting this to 0 disables throttling. Note that this account for all types
+# of compaction, including validation compaction.
+compaction_throughput_mb_per_sec: 16
+
+# Track cached row keys during compaction, and re-cache their new
+# positions in the compacted sstable. Disable if you use really large
+# key caches.
+compaction_preheat_key_cache: true
+
+# Throttles all outbound streaming file transfers on this node to the
+# given total throughput in Mbps. This is necessary because Cassandra does
+# mostly sequential IO when streaming data during bootstrap or repair, which
+# can lead to saturating the network connection and degrading rpc performance.
+# When unset, the default is 200 Mbps or 25 MB/s.
+# stream_throughput_outbound_megabits_per_sec: 200
+
+# How long the coordinator should wait for read operations to complete
+read_request_timeout_in_ms: 20000
+# How long the coordinator should wait for seq or index scans to complete
+range_request_timeout_in_ms: 20000
+# How long the coordinator should wait for writes to complete
+write_request_timeout_in_ms: 20000
+# How long the coordinator should wait for truncates to complete
+# (This can be much longer, because we need to flush all CFs
+# to make sure we can clear out anythink in the commitlog that could
+# cause truncated data to reappear.)
+truncate_request_timeout_in_ms: 60000
+# The default timeout for other, miscellaneous operations
+request_timeout_in_ms: 20000
+
+# Enable operation timeout information exchange between nodes to accurately
+# measure request timeouts, If disabled cassandra will assuming the request
+# was forwarded to the replica instantly by the coordinator
+#
+# Warning: before enabling this property make sure to ntp is installed
+# and the times are synchronized between the nodes.
+cross_node_timeout: false
+
+# Enable socket timeout for streaming operation.
+# When a timeout occurs during streaming, streaming is retried from the start
+# of the current file. This *can* involve re-streaming an important amount of
+# data, so you should avoid setting the value too low.
+# Default value is 0, which never timeout streams.
+# streaming_socket_timeout_in_ms: 0
+
+# phi value that must be reached for a host to be marked down.
+# most users should never need to adjust this.
+# phi_convict_threshold: 8
+
+# endpoint_snitch -- Set this to a class that implements
+# IEndpointSnitch. The snitch has two functions:
+# - it teaches Cassandra enough about your network topology to route
+# requests efficiently
+# - it allows Cassandra to spread replicas around your cluster to avoid
+# correlated failures. It does this by grouping machines into
+# "datacenters" and "racks." Cassandra will do its best not to have
+# more than one replica on the same "rack" (which may not actually
+# be a physical location)
+#
+# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
+# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
+# ARE PLACED.
+#
+# Out of the box, Cassandra provides
+# - SimpleSnitch:
+# Treats Strategy order as proximity. This improves cache locality
+# when disabling read repair, which can further improve throughput.
+# Only appropriate for single-datacenter deployments.
+# - PropertyFileSnitch:
+# Proximity is determined by rack and data center, which are
+# explicitly configured in cassandra-topology.properties.
+# - GossipingPropertyFileSnitch
+# The rack and datacenter for the local node are defined in
+# cassandra-rackdc.properties and propagated to other nodes via gossip. If
+# cassandra-topology.properties exists, it is used as a fallback, allowing
+# migration from the PropertyFileSnitch.
+# - RackInferringSnitch:
+# Proximity is determined by rack and data center, which are
+# assumed to correspond to the 3rd and 2nd octet of each node's
+# IP address, respectively. Unless this happens to match your
+# deployment conventions (as it did Facebook's), this is best used
+# as an example of writing a custom Snitch class.
+# - Ec2Snitch:
+# Appropriate for EC2 deployments in a single Region. Loads Region
+# and Availability Zone information from the EC2 API. The Region is
+# treated as the Datacenter, and the Availability Zone as the rack.
+# Only private IPs are used, so this will not work across multiple
+# Regions.
+# - Ec2MultiRegionSnitch:
+# Uses public IPs as broadcast_address to allow cross-region
+# connectivity. (Thus, you should set seed addresses to the public
+# IP as well.) You will need to open the storage_port or
+# ssl_storage_port on the public IP firewall. (For intra-Region
+# traffic, Cassandra will switch to the private IP after
+# establishing a connection.)
+#
+# You can use a custom Snitch by setting this to the full class name
+# of the snitch, which will be assumed to be on your classpath.
+endpoint_snitch: SimpleSnitch
+
+# controls how often to perform the more expensive part of host score
+# calculation
+dynamic_snitch_update_interval_in_ms: 100
+# controls how often to reset all host scores, allowing a bad host to
+# possibly recover
+dynamic_snitch_reset_interval_in_ms: 600000
+# if set greater than zero and read_repair_chance is < 1.0, this will allow
+# 'pinning' of replicas to hosts in order to increase cache capacity.
+# The badness threshold will control how much worse the pinned host has to be
+# before the dynamic snitch will prefer other replicas over it. This is
+# expressed as a double which represents a percentage. Thus, a value of
+# 0.2 means Cassandra would continue to prefer the static snitch values
+# until the pinned host was 20% worse than the fastest.
+dynamic_snitch_badness_threshold: 0.1
+
+# request_scheduler -- Set this to a class that implements
+# RequestScheduler, which will schedule incoming client requests
+# according to the specific policy. This is useful for multi-tenancy
+# with a single Cassandra cluster.
+# NOTE: This is specifically for requests from the client and does
+# not affect inter node communication.
+# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
+# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
+# client requests to a node with a separate queue for each
+# request_scheduler_id. The scheduler is further customized by
+# request_scheduler_options as described below.
+request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+
+# Scheduler Options vary based on the type of scheduler
+# NoScheduler - Has no options
+# RoundRobin
+# - throttle_limit -- The throttle_limit is the number of in-flight
+# requests per client. Requests beyond
+# that limit are queued up until
+# running requests can complete.
+# The value of 80 here is twice the number of
+# concurrent_reads + concurrent_writes.
+# - default_weight -- default_weight is optional and allows for
+# overriding the default which is 1.
+# - weights -- Weights are optional and will default to 1 or the
+# overridden default_weight. The weight translates into how
+# many requests are handled during each turn of the
+# RoundRobin, based on the scheduler id.
+#
+# request_scheduler_options:
+# throttle_limit: 80
+# default_weight: 5
+# weights:
+# Keyspace1: 1
+# Keyspace2: 5
+
+# request_scheduler_id -- An identifer based on which to perform
+# the request scheduling. Currently the only valid option is keyspace.
+# request_scheduler_id: keyspace
+
+# index_interval controls the sampling of entries from the primrary
+# row index in terms of space versus time. The larger the interval,
+# the smaller and less effective the sampling will be. In technicial
+# terms, the interval coresponds to the number of index entries that
+# are skipped between taking each sample. All the sampled entries
+# must fit in memory. Generally, a value between 128 and 512 here
+# coupled with a large key cache size on CFs results in the best trade
+# offs. This value is not often changed, however if you have many
+# very small rows (many to an OS page), then increasing this will
+# often lower memory usage without a impact on performance.
+index_interval: 128
+
+# Enable or disable inter-node encryption
+# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
+# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
+# suite for authentication, key exchange and encryption of the actual data transfers.
+# NOTE: No custom encryption options are enabled at the moment
+# The available internode options are : all, none, dc, rack
+#
+# If set to dc cassandra will encrypt the traffic between the DCs
+# If set to rack cassandra will encrypt the traffic between the racks
+#
+# The passwords used in these options must match the passwords used when generating
+# the keystore and truststore. For instructions on generating these files, see:
+# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSS…
+#
+server_encryption_options:
+ internode_encryption: none
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ truststore: conf/.truststore
+ truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
+ # require_client_auth: false
+
+# enable or disable client/server encryption.
+client_encryption_options:
+ enabled: false
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ # require_client_auth: false
+ # Set trustore and truststore_password if require_client_auth is true
+ # truststore: conf/.truststore
+ # truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
+
+# internode_compression controls whether traffic between nodes is
+# compressed.
+# can be: all - all traffic is compressed
+# dc - traffic between different datacenters is compressed
+# none - nothing is compressed.
+internode_compression: none
+
+# Enable or disable tcp_nodelay for inter-dc communication.
+# Disabling it will result in larger (but fewer) network packets being sent,
+# reducing overhead from the TCP protocol itself, at the cost of increasing
+# latency if you block for cross-datacenter responses.
+inter_dc_tcp_nodelay: true
commit af4705c8854779ba9354641589cdf2e3f478a712
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 14:28:38 2013 -0400
add resource config support for cql and gossip ports in rhq-storage plugin
diff --git a/modules/common/cassandra-ccm/cassandra-ccm-core/src/main/bundle/cassandra/conf/cassandra-jvm.properties b/modules/common/cassandra-ccm/cassandra-ccm-core/src/main/bundle/cassandra/conf/cassandra-jvm.properties
index 1faee9d..2eb5ab5 100644
--- a/modules/common/cassandra-ccm/cassandra-ccm-core/src/main/bundle/cassandra/conf/cassandra-jvm.properties
+++ b/modules/common/cassandra-ccm/cassandra-ccm-core/src/main/bundle/cassandra/conf/cassandra-jvm.properties
@@ -13,7 +13,7 @@ heap_new="-Xmn64M"
heap_dump_on_OOMError="-XX:+HeapDumpOnOutOfMemoryError"
-heap_dump_dir=""
+heap_dump_dir=
thread_stack_size="-Xss180k"
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
index 5276c84..c53c19b 100644
--- a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
@@ -108,7 +108,7 @@ public class ConfigEditor {
return (Integer) config.get("native_transport_port");
}
- public void setNativeTransportPort(int port) {
+ public void setNativeTransportPort(Integer port) {
config.put("native_transport_port", port);
}
@@ -116,7 +116,7 @@ public class ConfigEditor {
return (Integer) config.get("storage_port");
}
- public void setStoragePort(int port) {
+ public void setStoragePort(Integer port) {
config.put("storage_port", port);
}
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
index dd5b8a2..edaf9f9 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
@@ -5,6 +5,12 @@ import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.yaml.snakeyaml.error.YAMLException;
+
+import org.rhq.cassandra.util.ConfigEditor;
+import org.rhq.cassandra.util.ConfigEditorException;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.PropertySimple;
@@ -18,12 +24,16 @@ import org.rhq.core.util.StringUtil;
*/
public class StorageNodeConfigDelegate implements ConfigurationFacet {
+ private Log log = LogFactory.getLog(StorageNodeConfigDelegate.class);
+
private File jvmOptsFile;
private File wrapperEnvFile;
+ private File cassandraYamlFile;
public StorageNodeConfigDelegate(File basedir) {
File confDir = new File(basedir, "conf");
jvmOptsFile = new File(confDir, "cassandra-jvm.properties");
+ cassandraYamlFile = new File(confDir, "cassandra.yaml");
// for windows, config props also get propagated to the wrapper env
if (isWindows()) {
@@ -60,6 +70,11 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
config.put(new PropertySimple("heapDumpDir", new File(basedir, "bin").getAbsolutePath()));
}
+ ConfigEditor yamlEditor = new ConfigEditor(cassandraYamlFile);
+ yamlEditor.load();
+ config.put(new PropertySimple("cqlPort", yamlEditor.getNativeTransportPort()));
+ config.put(new PropertySimple("gossipPort", yamlEditor.getStoragePort()));
+
return config;
}
@@ -135,6 +150,7 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
Configuration config = configurationUpdateReport.getConfiguration();
updateCassandraJvmProps(config);
+ updateCassandraYaml(config);
if (isWindows()) {
updateWrapperEnv(config);
@@ -145,6 +161,8 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
configurationUpdateReport.setErrorMessage("No configuration update was applied: " + e.getMessage());
} catch (IOException e) {
configurationUpdateReport.setErrorMessageFromThrowable(e);
+ } catch (ConfigEditorException e) {
+ configurationUpdateReport.setErrorMessageFromThrowable(e);
}
}
@@ -195,6 +213,43 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
propertiesUpdater.update(properties);
}
+ private void updateCassandraYaml(Configuration newConfig) {
+ ConfigEditor editor = new ConfigEditor(cassandraYamlFile);
+ try {
+ editor.load();
+
+ PropertySimple cqlPortProperty = newConfig.getSimple("cqlPort");
+ if (cqlPortProperty != null) {
+ editor.setNativeTransportPort(cqlPortProperty.getIntegerValue());
+ }
+
+ PropertySimple gossipPortProperty = newConfig.getSimple("gossipPort");
+ if (gossipPortProperty != null) {
+ editor.setStoragePort(gossipPortProperty.getIntegerValue());
+ }
+
+ editor.save();
+ } catch (ConfigEditorException e) {
+ if (e.getCause() instanceof YAMLException) {
+ log.error("Failed to update " + cassandraYamlFile);
+ log.info("Attempting to restore " + cassandraYamlFile);
+ try {
+ editor.restore();
+ throw e;
+ } catch (ConfigEditorException e1) {
+ log.error("Failed to restore " + cassandraYamlFile + ". A copy of the file prior to any " +
+ "modifications can be found at " + editor.getBackupFile());
+ throw new ConfigEditorException("There was an error updating " + cassandraYamlFile + " and " +
+ "undoing the changes failed. A copy of the file can be found at " + editor.getBackupFile() +
+ ". See the agent logs for more details.", e);
+ }
+ } else {
+ log.error("No updates were made to " + cassandraYamlFile + " due to an unexpected error", e);
+ throw e;
+ }
+ }
+ }
+
private void updateWrapperEnv(Configuration config) throws IOException {
PropertiesFileUpdate propertiesUpdater = new PropertiesFileUpdate(wrapperEnvFile.getAbsolutePath());
Properties properties = propertiesUpdater.loadExistingProperties();
diff --git a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
index f5a4f6d..84bb832 100644
--- a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
@@ -175,6 +175,17 @@
<c:simple-property name="heapDumpDir" displayName="Heap Dump Directory" required="false"
description="The directory in which heap dumps will be written."/>
</c:group>
+ <c:group name="Ports">
+ <c:simple-property name="cqlPort"
+ displayName="CQL Port"
+ type="integer"
+ description="The port on which to listen for client requests over the native, CQL protocol.
+ This is a shared, cluster-wide setting."/>
+ <c:simple-property name="gossipPort"
+ type="integer"
+ description="The port used for internode communication. This is a shared, cluster-wide setting."/>
+
+ </c:group>
</resource-configuration>
<server name="Cassandra Server JVM"
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeConfigDelegateTest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeConfigDelegateTest.java
index 64d14b2..15861c1 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeConfigDelegateTest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeConfigDelegateTest.java
@@ -6,17 +6,20 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import org.rhq.cassandra.util.ConfigEditor;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.util.file.FileUtil;
+import org.rhq.core.util.stream.StreamUtil;
/**
* @author John Sanda
@@ -25,6 +28,8 @@ public class StorageNodeConfigDelegateTest {
private File basedir;
+ private File cassandraYamlFile;
+
private StorageNodeConfigDelegate configDelegate;
@BeforeMethod
@@ -33,6 +38,11 @@ public class StorageNodeConfigDelegateTest {
basedir = new File(dir, getClass().getSimpleName() + "/" + test.getName());
FileUtil.purge(basedir, true);
configDelegate = new StorageNodeConfigDelegate(basedir);
+
+ cassandraYamlFile = new File(confDir(), "cassandra.yaml");
+ InputStream inputStream = getClass().getResourceAsStream("/cassandra.yaml");
+ FileOutputStream outputStream = new FileOutputStream(cassandraYamlFile);
+ StreamUtil.copy(inputStream, outputStream);
}
@@ -55,13 +65,24 @@ public class StorageNodeConfigDelegateTest {
public void updateValidConfig() throws Exception {
createDefaultConfig();
- Configuration config = new Configuration();
- config.put(new PropertySimple("minHeapSize", "1024M"));
- config.put(new PropertySimple("maxHeapSize", "1024M"));
- config.put(new PropertySimple("heapNewSize", "256M"));
- config.put(new PropertySimple("threadStackSize", "240"));
- config.put(new PropertySimple("heapDumpOnOOMError", true));
- config.put(new PropertySimple("heapDumpDir", confDir()));
+ Configuration config = Configuration.builder()
+ .addSimple("minHeapSize", "1024M")
+ .addSimple("maxHeapSize", "1024M")
+ .addSimple("heapNewSize", "256M")
+ .addSimple("threadStackSize", "240")
+ .addSimple("heapDumpOnOOMError", true)
+ .addSimple("heapDumpDir", confDir())
+ .addSimple("cqlPort", 9595)
+ .addSimple("gossipPort", 9696)
+ .build();
+// config.put(new PropertySimple("minHeapSize", "1024M"));
+// config.put(new PropertySimple("maxHeapSize", "1024M"));
+// config.put(new PropertySimple("heapNewSize", "256M"));
+// config.put(new PropertySimple("threadStackSize", "240"));
+// config.put(new PropertySimple("heapDumpOnOOMError", true));
+// config.put(new PropertySimple("heapDumpDir", confDir()));
+// config.put(new PropertySimple("cqlPort", 9595));
+// config.put(new PropertySimple("gossipPort", 9696));
ConfigurationUpdateReport report = new ConfigurationUpdateReport(config);
@@ -78,6 +99,12 @@ public class StorageNodeConfigDelegateTest {
"Failed to update property [heap_dump_on_OOMError]");
assertEquals(properties.getProperty("heap_dump_dir"), confDir().getAbsolutePath(),
"Failed to update property [heap_dump_dir]");
+
+ ConfigEditor yamlEditor = new ConfigEditor(cassandraYamlFile);
+ yamlEditor.load();
+
+ assertEquals(yamlEditor.getNativeTransportPort(), (Integer) 9595, "Failed to update native_transport_port");
+ assertEquals(yamlEditor.getStoragePort(), (Integer) 9696, "Failed to update storage_port");
}
@Test
commit f19e7d84f953356db55deabebc8fe2ec43d276af
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 13:17:15 2013 -0400
make dataFileDirectories property read/write
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
index 9e1f86b..5276c84 100644
--- a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
@@ -88,6 +88,10 @@ public class ConfigEditor {
return (List<String>) config.get("data_file_directories");
}
+ public void setDataFileDirectories(List<String> dirs) {
+ config.put("data_file_directories", dirs);
+ }
+
public String getSavedCachesDirectory() {
return (String) config.get("saved_caches_directory");
}
diff --git a/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
index f339b48..9c3cc16 100644
--- a/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
+++ b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
@@ -1,5 +1,6 @@
package org.rhq.cassandra.util;
+import static java.util.Arrays.asList;
import static org.testng.Assert.assertEquals;
import java.io.File;
@@ -88,6 +89,24 @@ public class ConfigEditorTest {
assertEquals(editor.getStoragePort(), config.storage_port, "Failed to fetch storage_port");
}
+ @Test
+ public void updateDataFilesDirectories() throws Exception {
+ ConfigEditor editor = new ConfigEditor(configFile);
+ editor.load();
+ editor.setDataFileDirectories(asList("/data/dir1", "/data/dir2", "data/dir3"));
+ editor.save();
+
+ Config config = loadConfig();
+
+ assertEquals(config.data_file_directories, new String[] {"/data/dir1", "/data/dir2", "data/dir3"},
+ "Failed to update data_file_directories");
+
+ editor.load();
+
+ assertEquals(editor.getDataFileDirectories().toArray(new String[3]), config.data_file_directories,
+ "Failed to fetch data_file_directories");
+ }
+
private Config loadConfig() throws Exception {
FileInputStream inputStream = new FileInputStream(configFile);
org.yaml.snakeyaml.constructor.Constructor constructor =
commit 9fe2884cd3c6602d045dc4fc1a7c6c8820f9ec83
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 12:57:25 2013 -0400
make storagePort and nativeTransportPort read/write properties
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
index 0b4a127..9e1f86b 100644
--- a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
@@ -100,10 +100,18 @@ public class ConfigEditor {
params.put("seeds", StringUtil.arrayToString(seeds));
}
+ public Integer getNativeTransportPort() {
+ return (Integer) config.get("native_transport_port");
+ }
+
public void setNativeTransportPort(int port) {
config.put("native_transport_port", port);
}
+ public Integer getStoragePort() {
+ return (Integer) config.get("storage_port");
+ }
+
public void setStoragePort(int port) {
config.put("storage_port", port);
}
diff --git a/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
index cf344e2..f339b48 100644
--- a/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
+++ b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
@@ -65,6 +65,11 @@ public class ConfigEditorTest {
Config config = loadConfig();
assertEquals(config.native_transport_port, (Integer) 9393, "Failed to update native_transport_port");
+
+ editor.load();
+
+ assertEquals(editor.getNativeTransportPort(), config.native_transport_port,
+ "Failed to fetch native_transport_port");
}
@Test
@@ -77,6 +82,10 @@ public class ConfigEditorTest {
Config config = loadConfig();
assertEquals(config.storage_port, (Integer) 6767, "Failed to update storage_port");
+
+ editor.load();
+
+ assertEquals(editor.getStoragePort(), config.storage_port, "Failed to fetch storage_port");
}
private Config loadConfig() throws Exception {
commit 76badf25b9a3932837fb00af3d2bc484b6045a2b
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 30 10:53:24 2013 -0400
adding back support for deploying additional nodes with internode authentication
When the user installs a new storage node the following steps occur,
* The node is committed into inventory (but not part of storage cluster)
* StorageNode entity is created with mode set to INSTALLED
* New node is announced to the cluster
* Server schedules operation for cluster nodes to update internode auth conf
file to include the new node
* Server prepares new node for bootstrap
* Schedules operation on new node to
* Apply cluster settings
* Update internode auth conf settings
* Restart the node so it bootstraps into cluster
* Token ranges will be assigned to node
* New node will stream data from other nodes
* Server is notified that new node is up and part of the cluster
* The operation mode is set to NORMAL
* addNodeMaintenance operation is run on each cluster node
* Updates replication_factor if necessary
* Runs repair if necessary
* Deletes old keys
* Updates seeds property
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java
index 575edc74..f4d3934 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/cloud/StorageNode.java
@@ -56,6 +56,8 @@ import org.rhq.core.domain.resource.Resource;
+ " FROM StorageNode s " //
+ "LEFT JOIN FETCH s.resource r " //
+ " WHERE s.address = :address"),
+ @NamedQuery(name = StorageNode.QUERY_FIND_ALL_BY_MODE, query =
+ "SELECT s FROM StorageNode s WHERE s.operationMode = :operationMode"),
@NamedQuery(name = StorageNode.QUERY_FIND_ALL_NOT_INSTALLED, query = "SELECT s FROM StorageNode s WHERE NOT s.operationMode = 'INSTALLED'"),
@NamedQuery(name = StorageNode.QUERY_FIND_ALL_NORMAL, query = "SELECT s FROM StorageNode s WHERE s.operationMode = 'NORMAL'"),
@NamedQuery(name = StorageNode.QUERY_DELETE_BY_ID, query = "" //
@@ -91,6 +93,7 @@ public class StorageNode implements Serializable {
public static final String QUERY_FIND_ALL = "StorageNode.findAll";
public static final String QUERY_FIND_BY_ADDRESS = "StorageNode.findByAddress";
+ public static final String QUERY_FIND_ALL_BY_MODE = "StorageNode.findAllByMode";
public static final String QUERY_FIND_ALL_NOT_INSTALLED = "StorageNode.findAllCloudMembers";
public static final String QUERY_DELETE_BY_ID = "StorageNode.deleteById";
public static final String QUERY_FIND_ALL_NORMAL = "StorageNode.findAllNormalCloudMembers";
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index 049cf42..ee0f406 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -24,6 +24,8 @@
*/
package org.rhq.enterprise.server.cloud;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@@ -47,6 +49,7 @@ import org.quartz.JobDataMap;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
+import org.rhq.cassandra.schema.SchemaManager;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.authz.Permission;
@@ -57,6 +60,8 @@ import org.rhq.core.domain.cloud.StorageNodeConfigurationComposite;
import org.rhq.core.domain.cloud.StorageNodeLoadComposite;
import org.rhq.core.domain.common.JobTrigger;
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.PropertyList;
+import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.criteria.ResourceOperationHistoryCriteria;
@@ -73,6 +78,7 @@ import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
+import org.rhq.core.util.StringUtil;
import org.rhq.enterprise.server.RHQConstants;
import org.rhq.enterprise.server.alert.AlertManagerLocal;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
@@ -82,6 +88,7 @@ import org.rhq.enterprise.server.cloud.instance.ServerManagerLocal;
import org.rhq.enterprise.server.configuration.ConfigurationManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementDataManagerLocal;
import org.rhq.enterprise.server.operation.OperationManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.rest.reporting.MeasurementConverter;
@@ -100,6 +107,14 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
private final Log log = LogFactory.getLog(StorageNodeManagerBean.class);
+ private static final String USERNAME_PROPERTY = "rhq.cassandra.username";
+ private static final String PASSWORD_PROPERTY = "rhq.cassandra.password";
+ private final static String MAINTENANCE_OPERATION = "addNodeMaintenance";
+ private final static String MAINTENANCE_OPERATION_NOTE = "Topology change maintenance.";
+ private final static String RUN_REPAIR_PROPERTY = "runRepair";
+ private final static String UPDATE_SEEDS_LIST = "updateSeedsList";
+ private final static String SEEDS_LIST = "seedsList";
+
private static final String RHQ_STORAGE_CQL_PORT_PROPERTY = "nativeTransportPort";
private static final String RHQ_STORAGE_JMX_PORT_PROPERTY = "jmxPort";
private static final String RHQ_STORAGE_ADDRESS_PROPERTY = "host";
@@ -139,46 +154,86 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
@EJB
private StorageNodeManagerLocal storageNodeManger;
+ @EJB
+ private ResourceManagerLocal resourceManager;
+
@Override
public void linkResource(Resource resource) {
- List<StorageNode> storageNodes = this.getStorageNodes();
-
Configuration resourceConfig = resource.getPluginConfiguration();
- String configAddress = resourceConfig.getSimpleValue(RHQ_STORAGE_ADDRESS_PROPERTY);
-
- if (configAddress != null) {
- // TODO Do not add the node to the group until we have verified it has joined the cluster
- // StorageNodeMaintenanceJob currently determines if a new node has successfully joined the cluster.
- addStorageNodeToGroup(resource);
-
- boolean storageNodeFound = false;
- if (storageNodes != null) {
- for (StorageNode storageNode : storageNodes) {
- if (configAddress.equals(storageNode.getAddress())) {
- storageNode.setResource(resource);
- storageNode.setOperationMode(OperationMode.NORMAL);
- storageNodeFound = true;
- break;
- }
- }
- }
+ String address = resourceConfig.getSimpleValue(RHQ_STORAGE_ADDRESS_PROPERTY);
- if (!storageNodeFound) {
- int cqlPort = Integer.parseInt(resourceConfig.getSimpleValue(RHQ_STORAGE_CQL_PORT_PROPERTY));
- int jmxPort = Integer.parseInt(resourceConfig.getSimpleValue(RHQ_STORAGE_JMX_PORT_PROPERTY));
+ if (log.isInfoEnabled()) {
+ log.info("Linking " + resource + " to storage node at " + address);
+ }
+ try {
+ StorageNode storageNode = findStorageNodeByAddress(InetAddress.getByName(address));
- StorageNode storageNode = new StorageNode();
- storageNode.setAddress(configAddress);
- storageNode.setCqlPort(cqlPort);
- storageNode.setJmxPort(jmxPort);
+ if (storageNode != null) {
+ if (log.isInfoEnabled()) {
+ log.info(storageNode + " is an existing storage node. No cluster maintenance is necessary.");
+ }
storageNode.setResource(resource);
storageNode.setOperationMode(OperationMode.NORMAL);
+ addStorageNodeToGroup(resource);
+ } else {
+ storageNode = new StorageNode();
+ storageNode.setAddress(address);
+ storageNode.setCqlPort(Integer.parseInt(resourceConfig.getSimpleValue(RHQ_STORAGE_CQL_PORT_PROPERTY)));
+ storageNode.setJmxPort(Integer.parseInt(resourceConfig.getSimpleValue(RHQ_STORAGE_JMX_PORT_PROPERTY)));
+ storageNode.setResource(resource);
+ storageNode.setOperationMode(OperationMode.INSTALLED);
entityManager.persist(storageNode);
-// scheduleQuartzJob(storageNodes.size());
+ if (log.isInfoEnabled()) {
+ log.info(storageNode + " is a new storage node and not part of the storage node cluster.");
+ log.info("Scheduling maintenance operations to bring " + storageNode + " into the cluster...");
+ }
+
+ announceNewNode(storageNode);
}
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Could not resolve address [" + address + "]. The resource " + resource +
+ " cannot be linked to a storage node", e);
+ }
+ }
+
+ private void announceNewNode(StorageNode newStorageNode) {
+ if (log.isInfoEnabled()) {
+ log.info("Announcing " + newStorageNode + " to storage node cluster.");
+ }
+
+ ResourceGroup storageNodeGroup = getStorageNodeGroup();
+
+ GroupOperationSchedule schedule = new GroupOperationSchedule();
+ schedule.setGroup(storageNodeGroup);
+ schedule.setHaltOnFailure(false);
+ schedule.setExecutionOrder(new ArrayList<Resource>(storageNodeGroup.getExplicitResources()));
+ schedule.setJobTrigger(JobTrigger.createNowTrigger());
+ schedule.setSubject(subjectManager.getOverlord());
+ schedule.setOperationName("updateKnownNodes");
+
+ Configuration parameters = new Configuration();
+ parameters.put(createPropertyListOfAddresses("ipAddresses", combine(getStorageNodes(), newStorageNode)));
+ schedule.setParameters(parameters);
+
+ operationManager.scheduleGroupOperation(subjectManager.getOverlord(), schedule);
+ }
+
+ private List<StorageNode> combine(List<StorageNode> storageNodes, StorageNode storageNode) {
+ List<StorageNode> newList = new ArrayList<StorageNode>(storageNodes.size() + 1);
+ newList.addAll(storageNodes);
+ newList.add(storageNode);
+
+ return newList;
+ }
+
+ private PropertyList createPropertyListOfAddresses(String propertyName, List<StorageNode> nodes) {
+ PropertyList list = new PropertyList(propertyName);
+ for (StorageNode storageNode : nodes) {
+ list.add(new PropertySimple("address", storageNode.getAddress()));
}
+ return list;
}
@Override
@@ -214,7 +269,7 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
ResourceGroup group = getStorageNodeGroup();
resourceGroupManager.addResourcesToGroup(subjectManager.getOverlord(), group.getId(),
- new int[] {resource.getId()});
+ new int[]{resource.getId()});
}
@Override
@@ -232,6 +287,13 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
}
@Override
+ public void addToStorageNodeGroup(StorageNode storageNode) {
+ storageNode.setOperationMode(OperationMode.NORMAL);
+ entityManager.merge(storageNode);
+ addStorageNodeToGroup(storageNode.getResource());
+ }
+
+ @Override
public ResourceGroup getStorageNodeGroup() {
Subject overlord = subjectManager.getOverlord();
@@ -398,6 +460,19 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
return runner.execute();
}
+ public StorageNode findStorageNodeByAddress(InetAddress address) {
+ TypedQuery<StorageNode> query = entityManager.<StorageNode> createNamedQuery(StorageNode.QUERY_FIND_BY_ADDRESS,
+ StorageNode.class);
+ query.setParameter("address", address.getHostAddress());
+ List<StorageNode> result = query.getResultList();
+
+ if (result != null && result.size() > 0) {
+ return result.get(0);
+ }
+
+ return null;
+ }
+
@Override
@RequiredPermissions({ @RequiredPermission(Permission.MANAGE_SETTINGS),
@RequiredPermission(Permission.MANAGE_INVENTORY) })
@@ -472,19 +547,6 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
entityManager.flush();
}
- private StorageNode findStorageNodeByAddress(String address) {
- TypedQuery<StorageNode> query = entityManager.<StorageNode> createNamedQuery(StorageNode.QUERY_FIND_BY_ADDRESS,
- StorageNode.class);
- query.setParameter("address", address);
- List<StorageNode> result = query.getResultList();
-
- if (result != null && result.size() > 0) {
- return result.get(0);
- }
-
- return null;
- }
-
private StorageNodeLoadComposite.MeasurementAggregateWithUnits getMeasurementAggregateWithUnits(Subject subject,
int schedId, MeasurementUnits units, long beginTime, long endTime) {
MeasurementAggregate measurementAggregate = measurementManager.getAggregate(subject, schedId, beginTime,
@@ -641,63 +703,68 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
@Override
public boolean updateConfiguration(Subject subject, StorageNodeConfigurationComposite storageNodeConfiguration) {
- StorageNode storageNode = findStorageNodeByAddress(storageNodeConfiguration.getStorageNode().getAddress());
+ try {
+ StorageNode storageNode = findStorageNodeByAddress(InetAddress.getByName(
+ storageNodeConfiguration.getStorageNode().getAddress()));
+
+ if (storageNode != null && storageNode.getResource() != null) {
+ Configuration parameters = new Configuration();
+ parameters.setSimpleValue("jmxPort", storageNodeConfiguration.getJmxPort() + "");
+ if (storageNodeConfiguration.getHeapSize() != null) {
+ parameters.setSimpleValue("heapSize", storageNodeConfiguration.getHeapSize() + "");
+ }
+ if (storageNodeConfiguration.getHeapNewSize() != null) {
+ parameters.setSimpleValue("heapNewSize", storageNodeConfiguration.getHeapNewSize() + "");
+ }
+ if (storageNodeConfiguration.getThreadStackSize() != null) {
+ parameters.setSimpleValue("threadStackSize", storageNodeConfiguration.getThreadStackSize() + "");
+ }
+ parameters.setSimpleValue("restartIfRequired", "false");
- if (storageNode != null && storageNode.getResource() != null) {
- Configuration parameters = new Configuration();
- parameters.setSimpleValue("jmxPort", storageNodeConfiguration.getJmxPort() + "");
- if (storageNodeConfiguration.getHeapSize() != null) {
- parameters.setSimpleValue("heapSize", storageNodeConfiguration.getHeapSize() + "");
- }
- if (storageNodeConfiguration.getHeapNewSize() != null) {
- parameters.setSimpleValue("heapNewSize", storageNodeConfiguration.getHeapNewSize() + "");
- }
- if (storageNodeConfiguration.getThreadStackSize() != null) {
- parameters.setSimpleValue("threadStackSize", storageNodeConfiguration.getThreadStackSize() + "");
- }
- parameters.setSimpleValue("restartIfRequired", "false");
+ Resource storageNodeResource = storageNode.getResource();
- Resource storageNodeResource = storageNode.getResource();
+ boolean result = runOperationAndWaitForResult(subject, storageNodeResource, UPDATE_CONFIGURATION_OPERATION,
+ parameters);
- boolean result = runOperationAndWaitForResult(subject, storageNodeResource, UPDATE_CONFIGURATION_OPERATION,
- parameters);
+ if (result) {
+ //2. Update the JMX port
+ //this is a fast operation compared to the restart
+ storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
+ entityManager.merge(storageNode);
- if (result) {
- //2. Update the JMX port
- //this is a fast operation compared to the restart
- storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
- entityManager.merge(storageNode);
+ //3. Restart the storage node
+ result = runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION,
+ new Configuration());
- //3. Restart the storage node
- result = runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION,
- new Configuration());
+ //4. Update the plugin configuration to talk with the new server
+ //Up to this point communication with the storage node should not have been affected by the intermediate
+ //changes
+ Configuration storageNodePluginConfig = configurationManager.getPluginConfiguration(subject,
+ storageNodeResource.getId());
- //4. Update the plugin configuration to talk with the new server
- //Up to this point communication with the storage node should not have been affected by the intermediate
- //changes
- Configuration storageNodePluginConfig = configurationManager.getPluginConfiguration(subject,
- storageNodeResource.getId());
+ String existingJMXPort = storageNodePluginConfig.getSimpleValue("jmxPort");
+ String newJMXPort = storageNodeConfiguration.getJmxPort() + "";
- String existingJMXPort = storageNodePluginConfig.getSimpleValue("jmxPort");
- String newJMXPort = storageNodeConfiguration.getJmxPort() + "";
+ if (!existingJMXPort.equals(newJMXPort)) {
+ storageNodePluginConfig.setSimpleValue("jmxPort", newJMXPort);
- if (!existingJMXPort.equals(newJMXPort)) {
- storageNodePluginConfig.setSimpleValue("jmxPort", newJMXPort);
+ String existingConnectionURL = storageNodePluginConfig.getSimpleValue("connectorAddress");
+ String newConnectionURL = existingConnectionURL.replace(":" + existingJMXPort + "/", ":"
+ + storageNodeConfiguration.getJmxPort() + "/");
+ storageNodePluginConfig.setSimpleValue("connectorAddress", newConnectionURL);
- String existingConnectionURL = storageNodePluginConfig.getSimpleValue("connectorAddress");
- String newConnectionURL = existingConnectionURL.replace(":" + existingJMXPort + "/", ":"
- + storageNodeConfiguration.getJmxPort() + "/");
- storageNodePluginConfig.setSimpleValue("connectorAddress", newConnectionURL);
+ configurationManager.updatePluginConfiguration(subject, storageNodeResource.getId(),
+ storageNodePluginConfig);
+ }
- configurationManager.updatePluginConfiguration(subject, storageNodeResource.getId(),
- storageNodePluginConfig);
+ return result;
}
-
- return result;
}
- }
- return false;
+ return false;
+ } catch (UnknownHostException e) {
+ throw new RuntimeException("Failed to resolve address for " + storageNodeConfiguration, e);
+ }
}
@Override
@@ -754,4 +821,129 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
return successResultFound;
}
-}
+
+ @Override
+ public void prepareNewNodesForBootstrap() {
+ List<StorageNode> newStorageNodes = entityManager.createNamedQuery(StorageNode.QUERY_FIND_ALL_BY_MODE)
+ .setParameter("operationMode", OperationMode.INSTALLED).getResultList();
+ if (newStorageNodes.isEmpty()) {
+ throw new RuntimeException("Failed to find storage node to bootstrap into cluster.");
+ }
+ // Right now, without some user input, we can only reliably bootstrap one node at a
+ // time. To support bootstrapping multiple nodes concurrently, a mechanism will have
+ // to be put in place for the user to declare in advance the nodes that are coming
+ // online. Then we can wait until all declared nodes have been committed into
+ // inventory and announced to the cluster
+ StorageNode storageNode = newStorageNodes.get(0);
+
+ if (log.isInfoEnabled()) {
+ log.info("Preparing to bootstrap " + storageNode + " into cluster...");
+ }
+
+ List<StorageNode> existingStorageNodes = getStorageNodes();
+
+ ResourceOperationSchedule schedule = new ResourceOperationSchedule();
+ schedule.setResource(storageNode.getResource());
+ schedule.setJobTrigger(JobTrigger.createNowTrigger());
+ schedule.setSubject(subjectManager.getOverlord());
+ schedule.setOperationName("prepareForBootstrap");
+
+ Configuration parameters = new Configuration();
+ parameters.put(new PropertySimple("cqlPort", existingStorageNodes.get(0).getCqlPort()));
+ // TODO need to add support for storage_port in cassandra/storage plugins
+ parameters.put(new PropertySimple("gossipPort", 7100));
+ parameters.put(createPropertyListOfAddresses("storageNodeIPAddresses", getStorageNodes()));
+
+ schedule.setParameters(parameters);
+
+ operationManager.scheduleResourceOperation(subjectManager.getOverlord(), schedule);
+ }
+
+ @Override
+ public void runAddNodeMaintenance() {
+ log.info("Preparing to schedule addNodeMaintenance on the storage cluster...");
+
+ List<StorageNode> storageNodes = entityManager.createNamedQuery(StorageNode.QUERY_FIND_ALL_BY_MODE,
+ StorageNode.class).setParameter("operationMode", OperationMode.NORMAL).getResultList();
+
+ int clusterSize = storageNodes.size();
+ boolean isReadRepairNeeded;
+
+ if (clusterSize >= 4) {
+ // At 4 nodes we increase the RF to 3. We are not increasing the RF beyond
+ // that for additional nodes; so, there is no need to run repair if we are
+ // expanding from a 4 node cluster since the RF remains the same.
+ isReadRepairNeeded = false;
+ } else if (clusterSize == 1) {
+ // The RF will increase since we are going from a single to a multi-node
+ // cluster; therefore, we want to run repair.
+ isReadRepairNeeded = true;
+ } else if (clusterSize == 2) {
+ if (storageNodes.size() > 3) {
+ // If we go from 2 to > 3 nodes we will increase the RF to 3; therefore
+ // we want to run repair.
+ isReadRepairNeeded = true;
+ } else {
+ // If we go from 2 to 3 nodes, we keep the RF at 2 so there is no need
+ // to run repair.
+ isReadRepairNeeded = false;
+ }
+ } else if (clusterSize == 3) {
+ // We are increasing the cluster size > 3 which means the RF will be
+ // updated to 3; therefore, we want to run repair.
+ isReadRepairNeeded = true;
+ } else {
+ // If we cluster size of zero, then something is really screwed up. It
+ // should always be > 0.
+ isReadRepairNeeded = storageNodes.size() > 1;
+ }
+
+ if (isReadRepairNeeded) {
+ updateTopology(storageNodes);
+ }
+
+ ResourceGroup storageNodeGroup = getStorageNodeGroup();
+
+ GroupOperationSchedule schedule = new GroupOperationSchedule();
+ schedule.setGroup(storageNodeGroup);
+ schedule.setHaltOnFailure(false);
+ schedule.setExecutionOrder(new ArrayList<Resource>(storageNodeGroup.getExplicitResources()));
+ schedule.setJobTrigger(JobTrigger.createNowTrigger());
+ schedule.setSubject(subjectManager.getOverlord());
+ schedule.setOperationName(MAINTENANCE_OPERATION);
+ schedule.setDescription(MAINTENANCE_OPERATION_NOTE);
+
+ Configuration config = new Configuration();
+ config.put(createPropertyListOfAddresses(SEEDS_LIST, storageNodes));
+ config.put(new PropertySimple(RUN_REPAIR_PROPERTY, isReadRepairNeeded));
+ config.put(new PropertySimple(UPDATE_SEEDS_LIST, Boolean.TRUE));
+
+ schedule.setParameters(config);
+
+ operationManager.scheduleGroupOperation(subjectManager.getOverlord(), schedule);
+ }
+
+ private boolean updateTopology(List<StorageNode> storageNodes) {
+ String username = getRequiredStorageProperty(USERNAME_PROPERTY);
+ String password = getRequiredStorageProperty(PASSWORD_PROPERTY);
+ SchemaManager schemaManager = new SchemaManager(username, password, storageNodes);
+ try{
+ return schemaManager.updateTopology(false);
+ } catch (Exception e) {
+ log.error("An error occurred while applying schema topology changes", e);
+ }
+
+ return false;
+ }
+
+ private String getRequiredStorageProperty(String property) {
+ String value = System.getProperty(property);
+ if (StringUtil.isEmpty(property)) {
+ throw new IllegalStateException("The system property [" + property + "] is not set. The RHQ "
+ + "server will not be able connect to the RHQ storage node(s). This property should be defined "
+ + "in rhq-server.properties.");
+ }
+ return value;
+ }
+
+}
\ No newline at end of file
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
index 15fa85c..00ba9e7 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
@@ -18,6 +18,7 @@
*/
package org.rhq.enterprise.server.cloud;
+import java.net.InetAddress;
import java.util.List;
import javax.ejb.Local;
@@ -106,6 +107,8 @@ public interface StorageNodeManagerLocal {
*/
PageList<Alert> findAllStorageNodeAlerts(Subject subject, StorageNode storageNode);
+ StorageNode findStorageNodeByAddress(InetAddress address);
+
/**
* Find ids for all resources and sub-resources of Storage Nodes that
@@ -170,6 +173,8 @@ public interface StorageNodeManagerLocal {
*/
boolean storageNodeGroupExists();
+ void addToStorageNodeGroup(StorageNode storageNode);
+
/**
* This method assumes the storage node resource group already exists; as such, it should only be called from places
* in the code that are after the point(s) where the group has been created.
@@ -181,4 +186,8 @@ public interface StorageNodeManagerLocal {
void scheduleOperationInNewTransaction(Subject subject, ResourceOperationSchedule schedule);
+ void prepareNewNodesForBootstrap();
+
+ void runAddNodeMaintenance();
+
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java
index 088f13e..5d3b1ae 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/operation/OperationManagerBean.java
@@ -96,6 +96,7 @@ import org.rhq.enterprise.server.resource.ResourceNotFoundException;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.resource.group.ResourceGroupNotFoundException;
import org.rhq.enterprise.server.scheduler.SchedulerLocal;
+import org.rhq.enterprise.server.storage.StorageNodeOperationsHandler;
import org.rhq.enterprise.server.util.CriteriaQueryGenerator;
import org.rhq.enterprise.server.util.CriteriaQueryRunner;
@@ -123,6 +124,9 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
@EJB
private SubjectManagerLocal subjectManager;
+ @EJB
+ private StorageNodeOperationsHandler storageNodeOperationsHandler;
+
@SuppressWarnings("unchecked")
public List<IntegerOptionItem> getResourceNameOptionItems(int groupId) {
String queryName = ResourceGroup.QUERY_FIND_RESOURCE_NAMES_BY_GROUP_ID;
@@ -917,7 +921,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
if (history.getParameters() != null) {
history.getParameters().getId(); // eagerly reload the parameters
}
-
+ storageNodeOperationsHandler.handleOperationUpdateIfNecessary(history);
notifyAlertConditionCacheManager("updateOperationHistory", history);
return history;
}
@@ -1723,6 +1727,7 @@ public class OperationManagerBean implements OperationManagerLocal, OperationMan
if (!stillInProgress) {
groupHistory.setErrorMessage((groupErrorMessage == null) ? null : groupErrorMessage.toString());
groupHistory.setStatus(groupStatus);
+ storageNodeOperationsHandler.handleGroupOperationUpdateIfNecessary(groupHistory);
notifyAlertConditionCacheManager("checkForCompletedGroupOperation", groupHistory);
}
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java
index ec28888..0b404bb 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java
@@ -9,7 +9,9 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.cloud.Server;
+import org.rhq.core.domain.cloud.StorageNode;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
+import org.rhq.enterprise.server.cloud.StorageNodeManagerLocal;
import org.rhq.enterprise.server.cloud.TopologyManagerLocal;
import org.rhq.enterprise.server.cloud.instance.ServerManagerLocal;
import org.rhq.enterprise.server.util.LookupUtil;
@@ -36,6 +38,18 @@ public class StorageClusterMonitor implements StorageStateListener {
log.info("Taking server out of maintenance mode");
updateServerMode(Server.OperationMode.NORMAL);
}
+
+ StorageNodeManagerLocal storageNodeManager = LookupUtil.getStorageNodeManager();
+ StorageNode newClusterNode = storageNodeManager.findStorageNodeByAddress(address);
+
+ if (newClusterNode == null) {
+ log.error("Did not find storage node with address [" + address.getHostAddress() + "]. This should not " +
+ "happen.");
+ } else {
+ log.info("Adding " + newClusterNode + " to storage cluster and scheduling cluster maintenance...");
+ storageNodeManager.addToStorageNodeGroup(newClusterNode);
+ storageNodeManager.runAddNodeMaintenance();
+ }
}
@Override
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandler.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandler.java
new file mode 100644
index 0000000..96e8de8
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandler.java
@@ -0,0 +1,17 @@
+package org.rhq.enterprise.server.storage;
+
+import javax.ejb.Asynchronous;
+
+import org.rhq.core.domain.operation.GroupOperationHistory;
+import org.rhq.core.domain.operation.OperationHistory;
+
+/**
+ * @author John Sanda
+ */
+public interface StorageNodeOperationsHandler {
+ @Asynchronous
+ void handleOperationUpdateIfNecessary(OperationHistory operationHistory);
+
+ @Asynchronous
+ void handleGroupOperationUpdateIfNecessary(GroupOperationHistory operationHistory);
+}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandlerBean.java
new file mode 100644
index 0000000..6da5cca
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageNodeOperationsHandlerBean.java
@@ -0,0 +1,90 @@
+package org.rhq.enterprise.server.storage;
+
+import javax.ejb.Asynchronous;
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.cloud.StorageNode;
+import org.rhq.core.domain.operation.GroupOperationHistory;
+import org.rhq.core.domain.operation.OperationDefinition;
+import org.rhq.core.domain.operation.OperationHistory;
+import org.rhq.core.domain.operation.OperationRequestStatus;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.enterprise.server.RHQConstants;
+import org.rhq.enterprise.server.cloud.StorageNodeManagerLocal;
+
+/**
+ * @author John Sanda
+ */
+@Stateless
+public class StorageNodeOperationsHandlerBean implements StorageNodeOperationsHandler {
+
+ private final Log log = LogFactory.getLog(StorageNodeOperationsHandlerBean.class);
+
+ private static final String STORAGE_NODE_TYPE_NAME = "RHQ Storage Node";
+ private static final String STORAGE_NODE_PLUGIN_NAME = "RHQStorage";
+
+ @PersistenceContext(unitName = RHQConstants.PERSISTENCE_UNIT_NAME)
+ private EntityManager entityManager;
+
+ @EJB
+ private StorageNodeManagerLocal storageNodeManager;
+
+ @Override
+ @Asynchronous
+ public void handleOperationUpdateIfNecessary(OperationHistory operationHistory) {
+// if (isStorageNodeOperation(operationHistory.getOperationDefinition())) {
+// if (operationHistory.getOperationDefinition().getName().equals("prepareForBootstrap")) {
+// ResourceOperationHistory resourceOperationHistory = entityManager.find(ResourceOperationHistory.class,
+// operationHistory.getId());
+// if (resourceOperationHistory.getStatus() == OperationRequestStatus.SUCCESS) {
+//
+// }
+// StorageNode storageNode = findStorageNode(resourceOperationHistory.getResource());
+// storageNode.setOperationMode(StorageNode.OperationMode.NORMAL);
+// }
+// }
+ }
+
+ private StorageNode findStorageNode(Resource resource) {
+ for (StorageNode storageNode : storageNodeManager.getStorageNodes()) {
+ if (storageNode.getResource().getId() == resource.getId()) {
+ return storageNode;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ @Asynchronous
+ public void handleGroupOperationUpdateIfNecessary(GroupOperationHistory groupOperationHistory) {
+ if (isStorageNodeOperation(groupOperationHistory.getOperationDefinition())) {
+ if (groupOperationHistory.getOperationDefinition().getName().equals("updateKnownNodes")) {
+ if (groupOperationHistory.getStatus() == OperationRequestStatus.SUCCESS) {
+ log.info("New storage has been successfully announced to the storage node cluster.");
+ storageNodeManager.prepareNewNodesForBootstrap();
+ } else if (groupOperationHistory.getStatus() == OperationRequestStatus.FAILURE) {
+ log.warn("Failed to announce new storage node to the cluster. It cannot join the cluster until " +
+ "it has been announced to existing cluster nodes.");
+ } else if (groupOperationHistory.getStatus() == OperationRequestStatus.CANCELED) {
+ log.warn("New storage node has not been announced to the cluster. The group operation " +
+ groupOperationHistory.getOperationDefinition().getName() + " has been canceled. The new node " +
+ "cannot join the cluster until it has been announced to existing cluster nodes.");
+ }
+ }
+ }
+ }
+
+ private boolean isStorageNodeOperation(OperationDefinition operationDefinition) {
+ ResourceType resourceType = operationDefinition.getResourceType();
+ return resourceType.getName().equals(STORAGE_NODE_TYPE_NAME) &&
+ resourceType.getPlugin().equals(STORAGE_NODE_PLUGIN_NAME);
+ }
+
+}
commit 313023782c4bdddf63d1e3773d499717ddaa2687
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Jul 29 14:45:30 2013 +0200
If there is just one group definition in ConfigurationEditor, normal form is used instead SectionStack component. Also the "Jump to Section" navigation is hidden for just one group to save some space on the screen.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
index 779772f..418300f 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/configuration/ConfigurationEditor.java
@@ -415,17 +415,29 @@ public class ConfigurationEditor extends EnhancedVLayout {
EnhancedVLayout layout = new EnhancedVLayout();
List<PropertyGroupDefinition> groupDefinitions = configurationDefinition.getGroupDefinitions();
- if (groupDefinitions.isEmpty()) {
- // No prop groups, so we just need a single form for the non-grouped props.
+ if (groupDefinitions.isEmpty() || groupDefinitions.size() == 1) {
+ // No or one prop groups, so we just need a single form for the non-grouped props
+ // and another one if there is just one group
List<PropertyDefinition> propertyDefinitions = new ArrayList<PropertyDefinition>(
configurationDefinition.getNonGroupedProperties());
-
- DynamicForm form = buildPropertiesForm(propertyDefinitions, configuration);
- form.setBorder("1px solid #AAA");
- form.validate();
- layout.addMember(form);
+ if (!propertyDefinitions.isEmpty()) {
+ DynamicForm form = buildPropertiesForm(propertyDefinitions, configuration);
+ form.setBorder("1px solid #AAA");
+ form.validate();
+ layout.addMember(form);
+ }
+ if (groupDefinitions.size() == 1) {
+ propertyDefinitions.addAll(configurationDefinition.getPropertiesInGroup(groupDefinitions.get(0)
+ .getName()));
+ DynamicForm groupForm = buildPropertiesForm(propertyDefinitions, configuration);
+ groupForm.setIsGroup(true);
+ groupForm.setGroupTitle(groupDefinitions.get(0).getDisplayName());
+ groupForm.setBorder("1px solid #AAA");
+ groupForm.validate();
+ layout.addMember(groupForm);
+ }
} else {
- // One or more prop groups, so create a section stack with one section per group.
+ // Two or more prop groups, so create a section stack with one section per group.
final SectionStack sectionStack = new SectionStack();
sectionStack.setVisibilityMode(VisibilityMode.MULTIPLE);
sectionStack.setWidth100();
@@ -441,7 +453,6 @@ public class ConfigurationEditor extends EnhancedVLayout {
// com.allen_sauer.gwt.log.client.Log.info("building: " + definition.getDisplayName());
sectionStack.addSection(buildGroupSection(definition));
}
-
this.toolStrip = buildToolStrip(layout, sectionStack);
layout.addMember(toolStrip);
layout.addMember(sectionStack);
commit 33821fe1c902246701d7dd423de3abcff948cdaa
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Jul 29 13:19:16 2013 +0200
Adding the newly (6/28/13) issued certificate for accessing the pto calendar (mail.corp.redhat.com domain) to the rhq-ircbot keystore. This one should be valid until 6/28/15.
diff --git a/etc/rhq-ircBot/src/main/resources/org/rhq/etc/ircbot/cacerts.jks b/etc/rhq-ircBot/src/main/resources/org/rhq/etc/ircbot/cacerts.jks
index 3d73cbc..431de7c 100644
Binary files a/etc/rhq-ircBot/src/main/resources/org/rhq/etc/ircbot/cacerts.jks and b/etc/rhq-ircBot/src/main/resources/org/rhq/etc/ircbot/cacerts.jks differ
commit f8b594d1eb6032d5e8f097c924bb96316b62b483
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Jul 29 12:54:06 2013 +0200
Calling the right setter.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index 7d861f1..049cf42 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -310,7 +310,7 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
if ((scheduleId = scheduleIdsMap.get(totalDiskUsedPercentageMetric)) != null) {
StorageNodeLoadComposite.MeasurementAggregateWithUnits totalDiskUsedPercentageAggregateWithUnits = getMeasurementAggregateWithUnits(
subject, scheduleId, MeasurementUnits.PERCENTAGE, beginTime, endTime);
- result.setDataDiskUsedPercentage(totalDiskUsedPercentageAggregateWithUnits);
+ result.setTotalDiskUsedPercentage(totalDiskUsedPercentageAggregateWithUnits);
}
if ((scheduleId = scheduleIdsMap.get(freeDiskToDataRatioMetric)) != null) {
MeasurementAggregate freeDiskToDataRatioAggregate = measurementManager.getAggregate(subject,
@@ -754,4 +754,4 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
return successResultFound;
}
-}
\ No newline at end of file
+}
commit 817d5628d4afbe41c38c9e2d82cdd1a7ef079916
Author: John Sanda <jsanda(a)redhat.com>
Date: Sun Jul 28 07:36:07 2013 -0400
override default ring delay to speed up test and hopefully fix jenkins failure
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index ef92510..bd171a4 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -29,6 +29,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.net.InetAddress;
+import java.util.Properties;
import java.util.Set;
import com.google.common.collect.ImmutableSet;
@@ -73,6 +74,7 @@ import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.system.SystemInfoFactory;
+import org.rhq.core.util.PropertiesFileUpdate;
import org.rhq.core.util.stream.StreamUtil;
/**
@@ -124,6 +126,18 @@ public class StorageNodeComponentITest {
deployer.updateFilePerms();
deployer.updateStorageAuthConf(Sets.newHashSet(InetAddress.getByName(address)));
+ File confDir = new File(basedir, "conf");
+ File cassandraJvmPropsFile = new File(confDir, "cassandra-jvm.properties");
+ PropertiesFileUpdate propertiesUpdater = new PropertiesFileUpdate(cassandraJvmPropsFile.getAbsolutePath());
+ Properties properties = propertiesUpdater.loadExistingProperties();
+
+ String jvmOpts = properties.getProperty("JVM_OPTS");
+ jvmOpts = jvmOpts.substring(0, jvmOpts.lastIndexOf("\""));
+ jvmOpts = jvmOpts + " -Dcassandra.ring_delay_ms=100\"";
+ properties.setProperty("JVM_OPTS", jvmOpts);
+
+ propertiesUpdater.update(properties);
+
File binDir = new File(basedir, "bin");
SystemInfo systemInfo = SystemInfoFactory.createSystemInfo();
@@ -261,9 +275,8 @@ public class StorageNodeComponentITest {
log.info("Waiting for node to boostrap...");
// When a node goes through bootstrap, StorageService sleeps for RING_DELAY ms
// while it determines the ranges of the token ring it will own. RING_DELAY defaults
- // to 30 seconds by default.
- // TODO Override the default RING_DELAY to speed up tests
- Thread.sleep(33000);
+ // to 30 seconds by default but we are overriding it to be 100 ms.
+ Thread.sleep(3000);
assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The operation failed: " +
result.getErrorStackTrace());
commit 9c9ec83b26445713b67526f43c489493613a5abd
Author: John Sanda <jsanda(a)redhat.com>
Date: Sat Jul 27 14:08:18 2013 -0400
updating exception handling and logging in prepareForBootstrap
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/InternodeAuthConfUpdateException.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/InternodeAuthConfUpdateException.java
new file mode 100644
index 0000000..d1e6e56
--- /dev/null
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/InternodeAuthConfUpdateException.java
@@ -0,0 +1,22 @@
+package org.rhq.plugins.storage;
+
+/**
+ * @author John Sanda
+ */
+public class InternodeAuthConfUpdateException extends Exception {
+
+ public InternodeAuthConfUpdateException() {
+ }
+
+ public InternodeAuthConfUpdateException(String message) {
+ super(message);
+ }
+
+ public InternodeAuthConfUpdateException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public InternodeAuthConfUpdateException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
index eb0b9fd..2cb9501 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
@@ -29,12 +29,10 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
-import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
@@ -95,6 +93,14 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
return new File(pluginConfig.getSimpleValue("baseDir"));
}
+ private File getConfDir() {
+ return new File(getBasedir(), "conf");
+ }
+
+ private File getInternodeAuthConfFile() {
+ return new File(getConfDir(), "rhq-storage-auth.conf");
+ }
+
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
if (name.equals("addNodeMaintenance")) {
@@ -242,56 +248,24 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
ipAddresses.add(propertySimple.getStringValue());
}
- if (updateAuthFile(result, ipAddresses)) return result;
-
- EmsBean authBean = getEmsConnection().getBean("org.rhq.cassandra.auth:type=RhqInternodeAuthenticator");
- EmsOperation emsOperation = authBean.getOperation("reloadConfiguration");
- emsOperation.invoke();
-
- result.setSimpleResult("Successfully updated the set of known nodes.");
-
- return result;
- }
-
- private boolean updateAuthFile(OperationResult result, Set<String> ipAddresses) {
- log.info("Updating known nodes to " + ipAddresses);
+ try {
+ updateInternodeAuthConfFile(ipAddresses);
- File confDir = new File(getBasedir(), "conf");
- File authFile = new File(confDir, "rhq-storage-auth.conf");
- File authBackupFile = new File(confDir, "." + authFile.getName() + ".bak");
+ EmsBean authBean = getEmsConnection().getBean("org.rhq.cassandra.auth:type=RhqInternodeAuthenticator");
+ EmsOperation emsOperation = authBean.getOperation("reloadConfiguration");
+ emsOperation.invoke();
- if (authBackupFile.exists()) {
- if (log.isDebugEnabled()) {
- log.debug(authBackupFile + " already exists. Deleting it now in preparation of creating new backup " +
- "for " + authFile.getName());
- }
- if (!authBackupFile.delete()) {
- String msg = "Failed to delete backup file " + authBackupFile + ". The operation will abort " +
- "since " + authFile + " cannot reliably be backed up before making changes. Please delete " +
- authBackupFile + " manually and reschedule the operation once the file has been removed.";
- log.error(msg);
- result.setErrorMessage(msg);
-
- return true;
- }
- }
+ result.setSimpleResult("Successfully updated the set of known nodes.");
- try {
- StreamUtil.copy(new StringReader(StringUtil.collectionToString(ipAddresses, "\n")),
- new FileWriter(authFile), true);
- } catch (IOException e) {
- log.error("An error occurred while updating " + authFile, e);
- try {
- log.info("Restoring back up file " + authBackupFile);
- FileUtil.copyFile(authBackupFile, authFile);
- authBackupFile.delete();
- } catch (IOException e1) {
- log.error("Failed to revert backup of " + authFile, e1);
- }
- result.setErrorMessage("There was an unexpected error while updating " + authFile);
- return true;
+ return result;
+ } catch (InternodeAuthConfUpdateException e) {
+ File authFile = getInternodeAuthConfFile();
+ log.error("Failed to update set of trusted nodes in " + authFile + " due to the following error(s): " +
+ ThrowableUtil.getAllMessages(e)) ;
+ result.setErrorMessage("Failed to update set of trusted nodes in " + authFile + " due to the following " +
+ "error(s): " + ThrowableUtil.getAllMessages(e));
+ return result;
}
- return false;
}
private OperationResult prepareForBootstrap(Configuration params) {
@@ -326,7 +300,6 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
}
purgeDir(new File(configEditor.getSavedCachesDirectory()));
-
log.info("Updating cluster settings");
String address = pluginConfig.getSimpleValue("host");
@@ -344,10 +317,9 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
configEditor.setStoragePort(gossipPort);
configEditor.save();
+ log.info("Cluster configuration settings have been applied to " + yamlFile);
- if (updateAuthFile(result, new HashSet<String>(addresses))) {
- return result;
- }
+ updateInternodeAuthConfFile(new HashSet<String>(addresses));
log.info(this + " is ready to be bootstrap. Restarting storage node...");
OperationResult startResult = startNode();
@@ -376,6 +348,11 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
}
}
return result;
+ } catch (InternodeAuthConfUpdateException e) {
+ File authFile = getInternodeAuthConfFile();
+ result.setErrorMessage("Failed to update " + authFile + " due to the following error(s): " +
+ ThrowableUtil.getAllMessages(e));
+ return result;
}
}
@@ -384,23 +361,18 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
FileUtil.purge(dir, true);
}
- private File getCommitLogDir(Map yamlConfig) {
- return new File((String) yamlConfig.get("commitlog_directory"));
- }
+ private void updateInternodeAuthConfFile(Set<String> ipAddresses) throws InternodeAuthConfUpdateException {
+ File authFile = getInternodeAuthConfFile();
- private List<File> getDataDirs(Map yamlConfig) {
- List<File> dirs = new ArrayList<File>();
- List<String> dirNames = (List<String>) yamlConfig.get("data_file_directories");
+ log.info("Updating " + authFile);
- for (String dirName : dirNames) {
- dirs.add(new File(dirName));
+ try {
+ StreamUtil.copy(new StringReader(StringUtil.collectionToString(ipAddresses, "\n")),
+ new FileWriter(authFile), true);
+ } catch (Exception e) {
+ log.error("An error occurred while trying to update " + authFile, e);
+ throw new InternodeAuthConfUpdateException("An error occurred while trying to update " + authFile, e);
}
-
- return dirs;
- }
-
- private File getSavedCachesDir(Map yamlConfig) {
- return new File((String) yamlConfig.get("saved_caches_directory"));
}
private OperationResult nodeAdded(Configuration params) {
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index d10e428..ef92510 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -259,6 +259,10 @@ public class StorageNodeComponentITest {
params, timeout);
log.info("Waiting for node to boostrap...");
+ // When a node goes through bootstrap, StorageService sleeps for RING_DELAY ms
+ // while it determines the ranges of the token ring it will own. RING_DELAY defaults
+ // to 30 seconds by default.
+ // TODO Override the default RING_DELAY to speed up tests
Thread.sleep(33000);
assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The operation failed: " +
commit b38c4b5ea27f83caaa15f1cd2d29daa8ec4ac969
Author: John Sanda <jsanda(a)redhat.com>
Date: Sat Jul 27 12:49:30 2013 -0400
adding some initial test coverage for prepareForBootstrap operation
The prepareForBootstrap method has been refactored to use ConfigEditor but
there is still a good bit of clean up to do.
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
index 679a84c..0b4a127 100644
--- a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
@@ -76,6 +76,22 @@ public class ConfigEditor {
}
}
+ public File getBackupFile() {
+ return backupFile;
+ }
+
+ public String getCommitLogDirectory() {
+ return (String) config.get("commitlog_directory");
+ }
+
+ public List<String> getDataFileDirectories() {
+ return (List<String>) config.get("data_file_directories");
+ }
+
+ public String getSavedCachesDirectory() {
+ return (String) config.get("saved_caches_directory");
+ }
+
public void setSeeds(String... seeds) {
List seedProviderList = (List) config.get("seed_provider");
Map seedProvider = (Map) seedProviderList.get(0);
diff --git a/modules/plugins/cassandra/pom.xml b/modules/plugins/cassandra/pom.xml
index da90f09..bafc8d7 100644
--- a/modules/plugins/cassandra/pom.xml
+++ b/modules/plugins/cassandra/pom.xml
@@ -36,6 +36,11 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>rhq-cassandra-util</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>rhq-core-domain</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
@@ -89,6 +94,10 @@
<outputDirectory>${project.build.outputDirectory}/lib</outputDirectory>
<artifactItems>
<artifactItem>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-cassandra-util</artifactId>
+ </artifactItem>
+ <artifactItem>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>${cassandra.driver.version}</version>
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
index 5933093..eb0b9fd 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
@@ -26,7 +26,6 @@
package org.rhq.plugins.storage;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
@@ -45,9 +44,10 @@ import org.mc4j.ems.connection.EmsConnection;
import org.mc4j.ems.connection.bean.EmsBean;
import org.mc4j.ems.connection.bean.attribute.EmsAttribute;
import org.mc4j.ems.connection.bean.operation.EmsOperation;
-import org.yaml.snakeyaml.DumperOptions;
-import org.yaml.snakeyaml.Yaml;
+import org.yaml.snakeyaml.error.YAMLException;
+import org.rhq.cassandra.util.ConfigEditor;
+import org.rhq.cassandra.util.ConfigEditorException;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
@@ -277,27 +277,18 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
}
try {
- FileUtil.copyFile(authFile, authBackupFile);
- } catch (IOException e) {
- String msg = "Failed to backup " + authFile + " prior to making updates. The operation will abort due " +
- "to unexpected error";
- log.error(msg, e);
- result.setErrorMessage(msg + ": " + ThrowableUtil.getRootMessage(e));
- return true;
- }
-
- try {
StreamUtil.copy(new StringReader(StringUtil.collectionToString(ipAddresses, "\n")),
new FileWriter(authFile), true);
} catch (IOException e) {
log.error("An error occurred while updating " + authFile, e);
try {
+ log.info("Restoring back up file " + authBackupFile);
FileUtil.copyFile(authBackupFile, authFile);
+ authBackupFile.delete();
} catch (IOException e1) {
log.error("Failed to revert backup of " + authFile, e1);
}
- result.setErrorMessage("There was an unexpected error while updating " + authFile + ". Make sure that " +
- "it matches " + authBackupFile + " and then reschedule the operation.");
+ result.setErrorMessage("There was an unexpected error while updating " + authFile);
return true;
}
return false;
@@ -325,74 +316,67 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
String yamlProp = pluginConfig.getSimpleValue("yamlConfiguration");
File yamlFile = new File(yamlProp);
- DumperOptions options = new DumperOptions();
- options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
- Yaml yaml = new Yaml(options);
-
- Map yamlConfig = null;
+ ConfigEditor configEditor = new ConfigEditor(yamlFile);
try {
- yamlConfig = (Map) yaml.load(new FileInputStream(yamlFile));
- } catch (FileNotFoundException e) {
- log.error("Failed to load " + yamlFile, e);
- log.error("Cannot prepare " + this + " for bootstrap. " + yamlFile + " must exist in order to make the " +
- "necessary configuration changes.");
- result.setErrorMessage("Cannot prepare storage node for bootstrap. It appears that " + yamlFile +
- " does not exist. Make sure that it exists so that the necessary configuration changes can be made.");
+ configEditor.load();
- return result;
- }
+ purgeDir(new File(configEditor.getCommitLogDirectory()));
+ for (String dir : configEditor.getDataFileDirectories()) {
+ purgeDir(new File(dir));
+ }
+ purgeDir(new File(configEditor.getSavedCachesDirectory()));
- purgeDir(getCommitLogDir(yamlConfig));
- for (File dataDir : getDataDirs(yamlConfig)) {
- purgeDir(dataDir);
- }
- purgeDir(getSavedCachesDir(yamlConfig));
- log.info("Updating cluster settings");
+ log.info("Updating cluster settings");
- String address = pluginConfig.getSimpleValue("host");
- List<String> seeds = getAddresses(params.getList("storageNodeIPAddresses"));
- // Make sure this node's address is not in the list; otherwise, it
- // won't bootstrap properly.
- seeds.remove(address);
- try {
- updateSeedsList(seeds);
- } catch (IOException e) {
- log.error("Failed to update seeds property in " + yamlFile, e);
- result.setErrorMessage("Failed to prepared node for bootstrap due to unexpected error that occurred " +
- "while updating seeds property in " + yamlFile + ":\n" + ThrowableUtil.getAllMessages(e));
- return result;
- }
+ String address = pluginConfig.getSimpleValue("host");
+ int cqlPort = Integer.parseInt(params.getSimpleValue("cqlPort"));
+ int gossipPort = Integer.parseInt(params.getSimpleValue("gossipPort"));
+ List<String> addresses = getAddresses(params.getList("storageNodeIPAddresses"));
- if (updateAuthFile(result, new HashSet<String>(seeds))) {
- return result;
- }
+ // Make sure this node's address is not in the list; otherwise, it
+ // won't bootstrap properly.
+ List<String> seeds = new ArrayList<String>(addresses);
+ seeds.remove(address);
- int cqlPort = Integer.parseInt(params.getSimpleValue("cqlPort"));
- int gossipPort = Integer.parseInt(params.getSimpleValue("gossipPort"));
+ configEditor.setSeeds(seeds.toArray(new String[seeds.size()]));
+ configEditor.setNativeTransportPort(cqlPort);
+ configEditor.setStoragePort(gossipPort);
- yamlConfig.put("native_transport_port", cqlPort);
- yamlConfig.put("storage_port", gossipPort);
+ configEditor.save();
- try {
- yaml.dump(yamlConfig, new FileWriter(yamlFile));
- } catch (IOException e) {
- log.error("Could not update cluster settings in " + yamlFile, e);
- result.setErrorMessage("Could not update cluster settings in " + yamlFile + ":\n" +
- ThrowableUtil.getAllMessages(e));
- return result;
- }
+ if (updateAuthFile(result, new HashSet<String>(addresses))) {
+ return result;
+ }
- log.info(this + " is ready to be bootstrap. Restarting storage node...");
- OperationResult startResult = startNode();
- if (startResult.getErrorMessage() != null) {
- log.error("Failed to restart storage node:\n" + startResult.getErrorMessage());
- result.setErrorMessage("Failed to restart storage node:\n" + startResult.getErrorMessage());
- } else {
- result.setSimpleResult("The storage node was succesfully updated is now bootstrapping into the cluster.");
- }
+ log.info(this + " is ready to be bootstrap. Restarting storage node...");
+ OperationResult startResult = startNode();
+ if (startResult.getErrorMessage() != null) {
+ log.error("Failed to restart storage node:\n" + startResult.getErrorMessage());
+ result.setErrorMessage("Failed to restart storage node:\n" + startResult.getErrorMessage());
+ } else {
+ result.setSimpleResult("The storage node was succesfully updated is now bootstrapping into the cluster.");
+ }
- return result;
+ return result;
+ } catch (ConfigEditorException e) {
+ log.error("There was an error while trying to update " + yamlFile, e);
+ if (e.getCause() instanceof YAMLException) {
+ log.info("Attempting to restore " + yamlFile);
+ try {
+ configEditor.restore();
+ result.setErrorMessage("Failed to update configuration file [" + yamlFile + "]: " +
+ ThrowableUtil.getAllMessages(e.getCause()));
+ } catch (ConfigEditorException e1) {
+ log.error("Failed to restore " + yamlFile + ". A copy of the file prior to any modifications " +
+ "can be found at " + configEditor.getBackupFile());
+ result.setErrorMessage("There was an error updating [" + yamlFile + "] and undoing the changes " +
+ "Failed. A copy of the file can be found at " + configEditor.getBackupFile() + ". See the " +
+ "agent logs for more details");
+ }
+ }
+ return result;
+ }
}
private void purgeDir(File dir) {
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index eb4d545..d10e428 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -26,15 +26,25 @@ import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
import java.net.InetAddress;
import java.util.Set;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.SeedProviderDef;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.OperatingSystem;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
+import org.yaml.snakeyaml.Loader;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
import org.rhq.cassandra.CassandraClusterManager;
import org.rhq.cassandra.ClusterInitService;
@@ -63,12 +73,15 @@ import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.system.SystemInfoFactory;
+import org.rhq.core.util.stream.StreamUtil;
/**
* @author John Sanda
*/
public class StorageNodeComponentITest {
+ private final Log log = LogFactory.getLog(StorageNodeComponentITest.class);
+
private File basedir;
private Resource storageNode;
@@ -232,7 +245,7 @@ public class StorageNodeComponentITest {
}
@Test(dependsOnMethods = "restartStorageNode")
- public void prepareForBootstrap() {
+ public void prepareForBootstrap() throws Exception {
Configuration params = Configuration.builder().addSimple("cqlPort", 9242).addSimple("gossipPort", 7200)
.openList("storageNodeIPAddresses", "storageNodeIPAddresse").addSimples("127.0.0.1", "127.0.0.2")
.closeList().build();
@@ -245,10 +258,22 @@ public class StorageNodeComponentITest {
OperationServicesResult result = operationsService.invokeOperation(operationContext, "prepareForBootstrap",
params, timeout);
+ log.info("Waiting for node to boostrap...");
+ Thread.sleep(33000);
+
assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The operation failed: " +
result.getErrorStackTrace());
assertNodeIsUp("Expected " + storageNode + " to be up after the prepareForBootstrap operation completes.");
+
+ assertThatInternodeAuthConfFileMatches("127.0.0.1", "127.0.0.2");
+
+ File confDir = new File(basedir, "conf");
+ File cassandraYamlFile = new File(confDir, "cassandra.yaml");
+ Config config = loadConfig(cassandraYamlFile);
+
+ assertEquals(config.seed_provider.parameters.get("seeds"), "127.0.0.2", "Failed to update seeds " +
+ "property in " + cassandraYamlFile);
}
private void assertNodeIsUp(String msg) {
@@ -292,4 +317,28 @@ public class StorageNodeComponentITest {
return type.getPlugin().equals("RHQStorage") && type.getName().equals("RHQ Storage Node");
}
+ private void assertThatInternodeAuthConfFileMatches(String... addresses) throws Exception {
+ File confDir = new File(basedir, "conf");
+ File internodeAuthConfFile = new File(confDir, "rhq-storage-auth.conf");
+ String contents = StreamUtil.slurp(new FileReader(internodeAuthConfFile));
+
+ Set<String> expected = ImmutableSet.copyOf(addresses);
+ Set<String> actual = ImmutableSet.copyOf(contents.split("\n"));
+
+ assertEquals(actual, expected, "Failed to update internode authentication conf file " +
+ internodeAuthConfFile + ".");
+ }
+
+ private Config loadConfig(File configFile) throws Exception {
+ FileInputStream inputStream = new FileInputStream(configFile);
+ org.yaml.snakeyaml.constructor.Constructor constructor =
+ new org.yaml.snakeyaml.constructor.Constructor(Config.class);
+ TypeDescription seedDesc = new TypeDescription(SeedProviderDef.class);
+ seedDesc.putMapPropertyType("parameters", String.class, String.class);
+ constructor.addTypeDescription(seedDesc);
+ Yaml yaml = new Yaml(new Loader(constructor));
+
+ return (Config) yaml.load(inputStream);
+ }
+
}
commit ba2427bc9dfc4e4c3ac9d778aa15d6c521535efe
Author: John Sanda <jsanda(a)redhat.com>
Date: Sat Jul 27 10:14:18 2013 -0400
initial commit for ConfigEditor which handles updating cassandra.yaml
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
new file mode 100644
index 0000000..679a84c
--- /dev/null
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditor.java
@@ -0,0 +1,95 @@
+package org.rhq.cassandra.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.yaml.snakeyaml.DumperOptions;
+import org.yaml.snakeyaml.Yaml;
+
+import org.rhq.core.util.StringUtil;
+import org.rhq.core.util.file.FileUtil;
+
+/**
+ * @author John Sanda
+ */
+public class ConfigEditor {
+
+ private File configFile;
+
+ private File backupFile;
+
+ private Yaml yaml;
+
+ private Map config;
+
+ public ConfigEditor(File cassandraYamlFile) {
+ configFile = cassandraYamlFile;
+ }
+
+ public void load() {
+ try {
+ DumperOptions options = new DumperOptions();
+ options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
+ yaml = new Yaml(options);
+ config = (Map) yaml.load(new FileInputStream(configFile));
+ createBackup();
+ } catch (FileNotFoundException e) {
+ throw new ConfigEditorException("Failed to load " + configFile, e);
+ }
+ }
+
+ public void save() {
+ try {
+ yaml.dump(config, new FileWriter(configFile));
+ backupFile.delete();
+ yaml = null;
+ config = null;
+ backupFile = null;
+ } catch (Exception e) {
+ throw new ConfigEditorException("Failed to save changes to " + configFile, e);
+ }
+ }
+
+ public void restore() {
+ try {
+ FileUtil.copyFile(backupFile, configFile);
+ backupFile.delete();
+ yaml = null;
+ config = null;
+ backupFile = null;
+ } catch (IOException e) {
+ throw new ConfigEditorException("Failed to restore " + configFile + " from " + backupFile, e);
+ }
+ }
+
+ private void createBackup() {
+ backupFile = new File(configFile.getParent(), "." + configFile.getName() + ".bak");
+ try {
+ FileUtil.copyFile(configFile, backupFile);
+ } catch (IOException e) {
+ throw new ConfigEditorException("Failed to create " + backupFile, e);
+ }
+ }
+
+ public void setSeeds(String... seeds) {
+ List seedProviderList = (List) config.get("seed_provider");
+ Map seedProvider = (Map) seedProviderList.get(0);
+ List paramsList = (List) seedProvider.get("parameters");
+ Map params = (Map) paramsList.get(0);
+ params.put("seeds", StringUtil.arrayToString(seeds));
+ }
+
+ public void setNativeTransportPort(int port) {
+ config.put("native_transport_port", port);
+ }
+
+ public void setStoragePort(int port) {
+ config.put("storage_port", port);
+ }
+
+}
diff --git a/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditorException.java b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditorException.java
new file mode 100644
index 0000000..db9e7ea
--- /dev/null
+++ b/modules/common/cassandra-util/src/main/java/org/rhq/cassandra/util/ConfigEditorException.java
@@ -0,0 +1,21 @@
+package org.rhq.cassandra.util;
+
+/**
+ * @author John Sanda
+ */
+public class ConfigEditorException extends RuntimeException {
+
+ public ConfigEditorException() {
+ }
+
+ public ConfigEditorException(String message) {
+ }
+
+ public ConfigEditorException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ConfigEditorException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
new file mode 100644
index 0000000..cf344e2
--- /dev/null
+++ b/modules/common/cassandra-util/src/test/java/org/rhq/cassandra/util/ConfigEditorTest.java
@@ -0,0 +1,94 @@
+package org.rhq.cassandra.util;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+
+import org.apache.cassandra.config.Config;
+import org.apache.cassandra.config.SeedProviderDef;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+import org.yaml.snakeyaml.Loader;
+import org.yaml.snakeyaml.TypeDescription;
+import org.yaml.snakeyaml.Yaml;
+
+import org.rhq.core.util.file.FileUtil;
+import org.rhq.core.util.stream.StreamUtil;
+
+/**
+ * @author John Sanda
+ */
+public class ConfigEditorTest {
+
+ private File basedir;
+
+ private File configFile;
+
+ @BeforeMethod
+ public void initTestDir(Method test) throws Exception {
+ File dir = new File(getClass().getResource(".").toURI());
+ basedir = new File(dir, getClass().getSimpleName() + "/" + test.getName());
+ FileUtil.purge(basedir, true);
+ basedir.mkdirs();
+
+ configFile = new File(basedir, "cassandra.yaml");
+
+ InputStream inputStream = getClass().getResourceAsStream("/cassandra.yaml");
+ FileOutputStream outputStream = new FileOutputStream(configFile);
+ StreamUtil.copy(inputStream, outputStream);
+ }
+
+ @Test
+ public void updateSeeds() throws Exception {
+ ConfigEditor editor = new ConfigEditor(configFile);
+ editor.load();
+ editor.setSeeds("127.0.0.1", "127.0.0.2", "127.0.0.3");
+ editor.save();
+
+ Config config = loadConfig();
+
+ assertEquals(config.seed_provider.parameters.get("seeds"), "127.0.0.1,127.0.0.2,127.0.0.3",
+ "Failed to update seeds property.");
+ }
+
+ @Test
+ public void updateNativeTransportPort() throws Exception {
+ ConfigEditor editor = new ConfigEditor(configFile);
+ editor.load();
+ editor.setNativeTransportPort(9393);
+ editor.save();
+
+ Config config = loadConfig();
+
+ assertEquals(config.native_transport_port, (Integer) 9393, "Failed to update native_transport_port");
+ }
+
+ @Test
+ public void updateStoragePort() throws Exception {
+ ConfigEditor editor = new ConfigEditor(configFile);
+ editor.load();
+ editor.setStoragePort(6767);
+ editor.save();
+
+ Config config = loadConfig();
+
+ assertEquals(config.storage_port, (Integer) 6767, "Failed to update storage_port");
+ }
+
+ private Config loadConfig() throws Exception {
+ FileInputStream inputStream = new FileInputStream(configFile);
+ org.yaml.snakeyaml.constructor.Constructor constructor =
+ new org.yaml.snakeyaml.constructor.Constructor(Config.class);
+ TypeDescription seedDesc = new TypeDescription(SeedProviderDef.class);
+ seedDesc.putMapPropertyType("parameters", String.class, String.class);
+ constructor.addTypeDescription(seedDesc);
+ Yaml yaml = new Yaml(new Loader(constructor));
+
+ return (Config) yaml.load(inputStream);
+ }
+
+}
diff --git a/modules/common/cassandra-util/src/test/resources/cassandra.yaml b/modules/common/cassandra-util/src/test/resources/cassandra.yaml
new file mode 100644
index 0000000..fd7973b
--- /dev/null
+++ b/modules/common/cassandra-util/src/test/resources/cassandra.yaml
@@ -0,0 +1,690 @@
+# Cassandra storage config YAML
+
+# NOTE:
+# See http://wiki.apache.org/cassandra/StorageConfiguration for
+# full explanations of configuration directives
+# /NOTE
+
+# The name of the cluster. This is mainly used to prevent machines in
+# one logical cluster from joining another.
+cluster_name: rhq
+
+# This defines the number of tokens randomly assigned to this node on the ring
+# The more tokens, relative to other nodes, the larger the proportion of data
+# that this node will store. You probably want all nodes to have the same number
+# of tokens assuming they have equal hardware capability.
+#
+# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
+# and will use the initial_token as described below.
+#
+# Specifying initial_token will override this setting.
+#
+# If you already have a cluster with 1 token per node, and wish to migrate to
+# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
+num_tokens: 256
+
+# If you haven't specified num_tokens, or have set it to the default of 1 then
+# you should always specify InitialToken when setting up a production
+# cluster for the first time, and often when adding capacity later.
+# The principle is that each node should be given an equal slice of
+# the token ring; see http://wiki.apache.org/cassandra/Operations
+# for more details.
+#
+# If blank, Cassandra will request a token bisecting the range of
+# the heaviest-loaded existing node. If there is no load information
+# available, such as is the case with a new cluster, it will pick
+# a random token, which will lead to hot spots.
+#initial_token:
+
+# See http://wiki.apache.org/cassandra/HintedHandoff
+hinted_handoff_enabled: true
+# this defines the maximum amount of time a dead host will have hints
+# generated. After it has been dead this long, new hints for it will not be
+# created until it has been seen alive and gone down again.
+max_hint_window_in_ms: 10800000 # 3 hours
+# throttle in KB's per second, per delivery thread
+hinted_handoff_throttle_in_kb: 1024
+# Number of threads with which to deliver hints;
+# Consider increasing this number when you have multi-dc deployments, since
+# cross-dc handoff tends to be slower
+max_hints_delivery_threads: 2
+
+# The following setting populates the page cache on memtable flush and compaction
+# WARNING: Enable this setting only when the whole node's data fits in memory.
+# Defaults to: false
+# populate_io_cache_on_flush: false
+
+# Authentication backend, implementing IAuthenticator; used to identify users
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
+# PasswordAuthenticator}.
+#
+# - AllowAllAuthenticator performs no checks - set it to disable authentication.
+# - PasswordAuthenticator relies on username/password pairs to authenticate
+# users. It keeps usernames and hashed passwords in system_auth.credentials table.
+# Please increase system_auth keyspace replication factor if you use this authenticator.
+authenticator: org.apache.cassandra.auth.PasswordAuthenticator
+
+# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
+# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
+# CassandraAuthorizer}.
+#
+# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
+# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
+# increase system_auth keyspace replication factor if you use this authorizer.
+authorizer: org.apache.cassandra.auth.CassandraAuthorizer
+
+# Validity period for permissions cache (fetching permissions can be an
+# expensive operation depending on the authorizer, CassandraAuthorizer is
+# one example). Defaults to 2000, set to 0 to disable.
+# Will be disabled automatically for AllowAllAuthorizer.
+permissions_validity_in_ms: 2000
+
+# The partitioner is responsible for distributing rows (by key) across
+# nodes in the cluster. Any IPartitioner may be used, including your
+# own as long as it is on the classpath. Out of the box, Cassandra
+# provides org.apache.cassandra.dht.{Murmur3Partitioner, RandomPartitioner
+# ByteOrderedPartitioner, OrderPreservingPartitioner (deprecated)}.
+#
+# - RandomPartitioner distributes rows across the cluster evenly by md5.
+# This is the default prior to 1.2 and is retained for compatibility.
+# - Murmur3Partitioner is similar to RandomPartioner but uses Murmur3_128
+# Hash Function instead of md5. When in doubt, this is the best option.
+# - ByteOrderedPartitioner orders rows lexically by key bytes. BOP allows
+# scanning rows in key order, but the ordering can generate hot spots
+# for sequential insertion workloads.
+# - OrderPreservingPartitioner is an obsolete form of BOP, that stores
+# - keys in a less-efficient format and only works with keys that are
+# UTF8-encoded Strings.
+# - CollatingOPP colates according to EN,US rules rather than lexical byte
+# ordering. Use this as an example if you need custom collation.
+#
+# See http://wiki.apache.org/cassandra/Operations for more on
+# partitioners and token selection.
+partitioner: org.apache.cassandra.dht.Murmur3Partitioner
+
+# directories where Cassandra should store data on disk.
+data_file_directories:
+ - /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/data
+
+# commit log
+commitlog_directory: /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/commit_log
+
+# policy for data disk failures:
+# stop: shut down gossip and Thrift, leaving the node effectively dead, but
+# still inspectable via JMX.
+# best_effort: stop using the failed disk and respond to requests based on
+# remaining available sstables. This means you WILL see obsolete
+# data at CL.ONE!
+# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
+disk_failure_policy: stop
+
+# Maximum size of the key cache in memory.
+#
+# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
+# minimum, sometimes more. The key cache is fairly tiny for the amount of
+# time it saves, so it's worthwhile to use it at large numbers.
+# The row cache saves even more time, but must store the whole values of
+# its rows, so it is extremely space-intensive. It's best to only use the
+# row cache if you have hot rows or static rows.
+#
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
+key_cache_size_in_mb:
+
+# Duration in seconds after which Cassandra should
+# safe the keys cache. Caches are saved to saved_caches_directory as
+# specified in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 14400 or 4 hours.
+key_cache_save_period: 14400
+
+# Number of keys from the key cache to save
+# Disabled by default, meaning all keys are going to be saved
+# key_cache_keys_to_save: 100
+
+# Maximum size of the row cache in memory.
+# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
+#
+# Default value is 0, to disable row caching.
+row_cache_size_in_mb: 0
+
+# Duration in seconds after which Cassandra should
+# safe the row cache. Caches are saved to saved_caches_directory as specified
+# in this configuration file.
+#
+# Saved caches greatly improve cold-start speeds, and is relatively cheap in
+# terms of I/O for the key cache. Row cache saving is much more expensive and
+# has limited use.
+#
+# Default is 0 to disable saving the row cache.
+row_cache_save_period: 0
+
+# Number of keys from the row cache to save
+# Disabled by default, meaning all keys are going to be saved
+# row_cache_keys_to_save: 100
+
+# The provider for the row cache to use.
+#
+# Supported values are: ConcurrentLinkedHashCacheProvider, SerializingCacheProvider
+#
+# SerializingCacheProvider serialises the contents of the row and stores
+# it in native memory, i.e., off the JVM Heap. Serialized rows take
+# significantly less memory than "live" rows in the JVM, so you can cache
+# more rows in a given memory footprint. And storing the cache off-heap
+# means you can use smaller heap sizes, reducing the impact of GC pauses.
+#
+# It is also valid to specify the fully-qualified class name to a class
+# that implements org.apache.cassandra.cache.IRowCacheProvider.
+#
+# Defaults to SerializingCacheProvider
+row_cache_provider: SerializingCacheProvider
+
+# saved caches
+saved_caches_directory: /Users/jsanda/Development/redhat/rhq/rhq-data/storage-1/saved_caches
+
+# commitlog_sync may be either "periodic" or "batch."
+# When in batch mode, Cassandra won't ack writes until the commit log
+# has been fsynced to disk. It will wait up to
+# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
+# performing the sync.
+#
+# commitlog_sync: batch
+# commitlog_sync_batch_window_in_ms: 50
+#
+# the other option is "periodic" where writes may be acked immediately
+# and the CommitLog is simply synced every commitlog_sync_period_in_ms
+# milliseconds.
+commitlog_sync: periodic
+commitlog_sync_period_in_ms: 10000
+
+# The size of the individual commitlog file segments. A commitlog
+# segment may be archived, deleted, or recycled once all the data
+# in it (potentally from each columnfamily in the system) has been
+# flushed to sstables.
+#
+# The default size is 32, which is almost always fine, but if you are
+# archiving commitlog segments (see commitlog_archiving.properties),
+# then you probably want a finer granularity of archiving; 8 or 16 MB
+# is reasonable.
+commitlog_segment_size_in_mb: 32
+
+# any class that implements the SeedProvider interface and has a
+# constructor that takes a Map<String, String> of parameters will do.
+seed_provider:
+ # Addresses of hosts that are deemed contact points.
+ # Cassandra nodes use this list of hosts to find each other and learn
+ # the topology of the ring. You must change this if you are running
+ # multiple nodes!
+ - class_name: org.apache.cassandra.locator.SimpleSeedProvider
+ parameters:
+ # seeds is actually a comma-delimited list of addresses.
+ # Ex: "<ip1>,<ip2>,<ip3>"
+ - seeds: "127.0.0.1"
+
+# emergency pressure valve: each time heap usage after a full (CMS)
+# garbage collection is above this fraction of the max, Cassandra will
+# flush the largest memtables.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+#
+# RELYING ON THIS AS YOUR PRIMARY TUNING MECHANISM WILL WORK POORLY:
+# it is most effective under light to moderate load, or read-heavy
+# workloads; under truly massive write load, it will often be too
+# little, too late.
+flush_largest_memtables_at: 0.75
+
+# emergency pressure valve #2: the first time heap usage after a full
+# (CMS) garbage collection is above this fraction of the max,
+# Cassandra will reduce cache maximum _capacity_ to the given fraction
+# of the current _size_. Should usually be set substantially above
+# flush_largest_memtables_at, since that will have less long-term
+# impact on the system.
+#
+# Set to 1.0 to disable. Setting this lower than
+# CMSInitiatingOccupancyFraction is not likely to be useful.
+reduce_cache_sizes_at: 0.85
+reduce_cache_capacity_to: 0.6
+
+# For workloads with more data than can fit in memory, Cassandra's
+# bottleneck will be reads that need to fetch data from
+# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
+# order to allow the operations to enqueue low enough in the stack
+# that the OS and drives can reorder them.
+#
+# On the other hand, since writes are almost never IO bound, the ideal
+# number of "concurrent_writes" is dependent on the number of cores in
+# your system; (8 * number_of_cores) is a good rule of thumb.
+concurrent_reads: 32
+concurrent_writes: 32
+
+# Total memory to use for memtables. Cassandra will flush the largest
+# memtable when this much memory is used.
+# If omitted, Cassandra will set it to 1/3 of the heap.
+# memtable_total_space_in_mb: 2048
+
+# Total space to use for commitlogs. Since commitlog segments are
+# mmapped, and hence use up address space, the default size is 32
+# on 32-bit JVMs, and 1024 on 64-bit JVMs.
+#
+# If space gets above this value (it will round up to the next nearest
+# segment multiple), Cassandra will flush every dirty CF in the oldest
+# segment and remove it. So a small total commitlog space will tend
+# to cause more flush activity on less-active columnfamilies.
+# commitlog_total_space_in_mb: 4096
+
+# This sets the amount of memtable flush writer threads. These will
+# be blocked by disk io, and each one will hold a memtable in memory
+# while blocked. If you have a large heap and many data directories,
+# you can increase this value for better flush performance.
+# By default this will be set to the amount of data directories defined.
+#memtable_flush_writers: 1
+
+# the number of full memtables to allow pending flush, that is,
+# waiting for a writer thread. At a minimum, this should be set to
+# the maximum number of secondary indexes created on a single CF.
+memtable_flush_queue_size: 4
+
+# Whether to, when doing sequential writing, fsync() at intervals in
+# order to force the operating system to flush the dirty
+# buffers. Enable this to avoid sudden dirty buffer flushing from
+# impacting read latencies. Almost always a good idea on SSD:s; not
+# necessarily on platters.
+trickle_fsync: false
+trickle_fsync_interval_in_kb: 10240
+
+# TCP port, for commands and data
+storage_port: 7100
+
+# SSL port, for encrypted communication. Unused unless enabled in
+# encryption_options
+ssl_storage_port: 7101
+
+# Address to bind to and tell other Cassandra nodes to connect to. You
+# _must_ change this if you want multiple nodes to be able to
+# communicate!
+#
+# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
+# will always do the Right Thing *if* the node is properly configured
+# (hostname, name resolution, etc), and the Right Thing is to use the
+# address associated with the hostname (it might not be).
+#
+# Setting this to 0.0.0.0 is always wrong.
+listen_address: 127.0.0.1
+
+# Address to broadcast to other Cassandra nodes
+# Leaving this blank will set it to the same value as listen_address
+# broadcast_address: 1.2.3.4
+
+# Internode authentication backend, implementing IInternodeAuthenticator;
+# used to allow/disallow connections from peer nodes.
+internode_authenticator: org.rhq.cassandra.auth.RhqInternodeAuthenticator
+
+# Whether to start the native transport server.
+# Currently, only the thrift server is started by default because the native
+# transport is considered beta.
+# Please note that the address on which the native transport is bound is the
+# same as the rpc_address. The port however is different and specified below.
+start_native_transport: true
+# port for the CQL native transport to listen for clients on
+native_transport_port: 9142
+# The minimum and maximum threads for handling requests when the native
+# transport is used. The meaning is those is similar to the one of
+# rpc_min_threads and rpc_max_threads, though the default differ slightly and
+# are the ones below:
+# native_transport_min_threads: 16
+native_transport_max_threads: 128
+
+
+# Whether to start the thrift rpc server.
+start_rpc: false
+# The address to bind the Thrift RPC service to -- clients connect
+# here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if
+# you want Thrift to listen on all interfaces.
+#
+# Leaving this blank has the same effect it does for ListenAddress,
+# (i.e. it will be based on the configured hostname of the node).
+rpc_address: 127.0.0.1
+# port for Thrift to listen for clients on
+rpc_port: 9160
+
+# enable or disable keepalive on rpc connections
+rpc_keepalive: true
+
+# Cassandra provides three out-of-the-box options for the RPC Server:
+#
+# sync -> One thread per thrift connection. For a very large number of clients, memory
+# will be your limiting factor. On a 64 bit JVM, 128KB is the minimum stack size
+# per thread, and that will correspond to your use of virtual memory (but physical memory
+# may be limited depending on use of stack space).
+#
+# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
+# asynchronously using a small number of threads that does not vary with the amount
+# of thrift clients (and thus scales well to many clients). The rpc requests are still
+# synchronous (one thread per active request).
+#
+# The default is sync because on Windows hsha is about 30% slower. On Linux,
+# sync/hsha performance is about the same, with hsha of course using less memory.
+#
+# Alternatively, can provide your own RPC server by providing the fully-qualified class name
+# of an o.a.c.t.TServerFactory that can create an instance of it.
+rpc_server_type: sync
+
+# Uncomment rpc_min|max_thread to set request pool size limits.
+#
+# Regardless of your choice of RPC server (see above), the number of maximum requests in the
+# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
+# RPC server, it also dictates the number of clients that can be connected at all).
+#
+# The default is unlimited and thus provide no protection against clients overwhelming the server. You are
+# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
+# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
+#
+# rpc_min_threads: 16
+# rpc_max_threads: 2048
+
+# uncomment to set socket buffer sizes on rpc connections
+# rpc_send_buff_size_in_bytes:
+# rpc_recv_buff_size_in_bytes:
+
+# uncomment to set socket buffer size for internode communication
+# Note that when setting this, the buffer size is limited by net.core.wmem_max
+# and when not setting it it is defined by net.ipv4.tcp_wmem
+# See:
+# /proc/sys/net/core/wmem_max
+# /proc/sys/net/core/rmem_max
+# /proc/sys/net/ipv4/tcp_wmem
+# /proc/sys/net/ipv4/tcp_wmem
+# and: man tcp
+# internode_send_buff_size_in_bytes:
+# internode_recv_buff_size_in_bytes:
+
+# Frame size for thrift (maximum field length).
+thrift_framed_transport_size_in_mb: 15
+
+# The max length of a thrift message, including all fields and
+# internal thrift overhead.
+thrift_max_message_length_in_mb: 16
+
+# Set to true to have Cassandra create a hard link to each sstable
+# flushed or streamed locally in a backups/ subdirectory of the
+# Keyspace data. Removing these links is the operator's
+# responsibility.
+incremental_backups: false
+
+# Whether or not to take a snapshot before each compaction. Be
+# careful using this option, since Cassandra won't clean up the
+# snapshots for you. Mostly useful if you're paranoid when there
+# is a data format change.
+snapshot_before_compaction: false
+
+# Whether or not a snapshot is taken of the data before keyspace truncation
+# or dropping of column families. The STRONGLY advised default of true
+# should be used to provide data safety. If you set this flag to false, you will
+# lose data on truncation or drop.
+auto_snapshot: true
+
+# Add column indexes to a row after its contents reach this size.
+# Increase if your column values are large, or if you have a very large
+# number of columns. The competing causes are, Cassandra has to
+# deserialize this much of the row to read a single column, so you want
+# it to be small - at least if you do many partial-row reads - but all
+# the index data is read for each access, so you don't want to generate
+# that wastefully either.
+column_index_size_in_kb: 64
+
+# Size limit for rows being compacted in memory. Larger rows will spill
+# over to disk and use a slower two-pass compaction process. A message
+# will be logged specifying the row key.
+in_memory_compaction_limit_in_mb: 64
+
+# Number of simultaneous compactions to allow, NOT including
+# validation "compactions" for anti-entropy repair. Simultaneous
+# compactions can help preserve read performance in a mixed read/write
+# workload, by mitigating the tendency of small sstables to accumulate
+# during a single long running compactions. The default is usually
+# fine and if you experience problems with compaction running too
+# slowly or too fast, you should look at
+# compaction_throughput_mb_per_sec first.
+#
+# concurrent_compactors defaults to the number of cores.
+# Uncomment to make compaction mono-threaded, the pre-0.8 default.
+#concurrent_compactors: 1
+
+# Multi-threaded compaction. When enabled, each compaction will use
+# up to one thread per core, plus one thread per sstable being merged.
+# This is usually only useful for SSD-based hardware: otherwise,
+# your concern is usually to get compaction to do LESS i/o (see:
+# compaction_throughput_mb_per_sec), not more.
+multithreaded_compaction: false
+
+# Throttles compaction to the given total throughput across the entire
+# system. The faster you insert data, the faster you need to compact in
+# order to keep the sstable count down, but in general, setting this to
+# 16 to 32 times the rate you are inserting data is more than sufficient.
+# Setting this to 0 disables throttling. Note that this account for all types
+# of compaction, including validation compaction.
+compaction_throughput_mb_per_sec: 16
+
+# Track cached row keys during compaction, and re-cache their new
+# positions in the compacted sstable. Disable if you use really large
+# key caches.
+compaction_preheat_key_cache: true
+
+# Throttles all outbound streaming file transfers on this node to the
+# given total throughput in Mbps. This is necessary because Cassandra does
+# mostly sequential IO when streaming data during bootstrap or repair, which
+# can lead to saturating the network connection and degrading rpc performance.
+# When unset, the default is 200 Mbps or 25 MB/s.
+# stream_throughput_outbound_megabits_per_sec: 200
+
+# How long the coordinator should wait for read operations to complete
+read_request_timeout_in_ms: 20000
+# How long the coordinator should wait for seq or index scans to complete
+range_request_timeout_in_ms: 20000
+# How long the coordinator should wait for writes to complete
+write_request_timeout_in_ms: 20000
+# How long the coordinator should wait for truncates to complete
+# (This can be much longer, because we need to flush all CFs
+# to make sure we can clear out anythink in the commitlog that could
+# cause truncated data to reappear.)
+truncate_request_timeout_in_ms: 60000
+# The default timeout for other, miscellaneous operations
+request_timeout_in_ms: 20000
+
+# Enable operation timeout information exchange between nodes to accurately
+# measure request timeouts, If disabled cassandra will assuming the request
+# was forwarded to the replica instantly by the coordinator
+#
+# Warning: before enabling this property make sure to ntp is installed
+# and the times are synchronized between the nodes.
+cross_node_timeout: false
+
+# Enable socket timeout for streaming operation.
+# When a timeout occurs during streaming, streaming is retried from the start
+# of the current file. This *can* involve re-streaming an important amount of
+# data, so you should avoid setting the value too low.
+# Default value is 0, which never timeout streams.
+# streaming_socket_timeout_in_ms: 0
+
+# phi value that must be reached for a host to be marked down.
+# most users should never need to adjust this.
+# phi_convict_threshold: 8
+
+# endpoint_snitch -- Set this to a class that implements
+# IEndpointSnitch. The snitch has two functions:
+# - it teaches Cassandra enough about your network topology to route
+# requests efficiently
+# - it allows Cassandra to spread replicas around your cluster to avoid
+# correlated failures. It does this by grouping machines into
+# "datacenters" and "racks." Cassandra will do its best not to have
+# more than one replica on the same "rack" (which may not actually
+# be a physical location)
+#
+# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
+# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
+# ARE PLACED.
+#
+# Out of the box, Cassandra provides
+# - SimpleSnitch:
+# Treats Strategy order as proximity. This improves cache locality
+# when disabling read repair, which can further improve throughput.
+# Only appropriate for single-datacenter deployments.
+# - PropertyFileSnitch:
+# Proximity is determined by rack and data center, which are
+# explicitly configured in cassandra-topology.properties.
+# - GossipingPropertyFileSnitch
+# The rack and datacenter for the local node are defined in
+# cassandra-rackdc.properties and propagated to other nodes via gossip. If
+# cassandra-topology.properties exists, it is used as a fallback, allowing
+# migration from the PropertyFileSnitch.
+# - RackInferringSnitch:
+# Proximity is determined by rack and data center, which are
+# assumed to correspond to the 3rd and 2nd octet of each node's
+# IP address, respectively. Unless this happens to match your
+# deployment conventions (as it did Facebook's), this is best used
+# as an example of writing a custom Snitch class.
+# - Ec2Snitch:
+# Appropriate for EC2 deployments in a single Region. Loads Region
+# and Availability Zone information from the EC2 API. The Region is
+# treated as the Datacenter, and the Availability Zone as the rack.
+# Only private IPs are used, so this will not work across multiple
+# Regions.
+# - Ec2MultiRegionSnitch:
+# Uses public IPs as broadcast_address to allow cross-region
+# connectivity. (Thus, you should set seed addresses to the public
+# IP as well.) You will need to open the storage_port or
+# ssl_storage_port on the public IP firewall. (For intra-Region
+# traffic, Cassandra will switch to the private IP after
+# establishing a connection.)
+#
+# You can use a custom Snitch by setting this to the full class name
+# of the snitch, which will be assumed to be on your classpath.
+endpoint_snitch: SimpleSnitch
+
+# controls how often to perform the more expensive part of host score
+# calculation
+dynamic_snitch_update_interval_in_ms: 100
+# controls how often to reset all host scores, allowing a bad host to
+# possibly recover
+dynamic_snitch_reset_interval_in_ms: 600000
+# if set greater than zero and read_repair_chance is < 1.0, this will allow
+# 'pinning' of replicas to hosts in order to increase cache capacity.
+# The badness threshold will control how much worse the pinned host has to be
+# before the dynamic snitch will prefer other replicas over it. This is
+# expressed as a double which represents a percentage. Thus, a value of
+# 0.2 means Cassandra would continue to prefer the static snitch values
+# until the pinned host was 20% worse than the fastest.
+dynamic_snitch_badness_threshold: 0.1
+
+# request_scheduler -- Set this to a class that implements
+# RequestScheduler, which will schedule incoming client requests
+# according to the specific policy. This is useful for multi-tenancy
+# with a single Cassandra cluster.
+# NOTE: This is specifically for requests from the client and does
+# not affect inter node communication.
+# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
+# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
+# client requests to a node with a separate queue for each
+# request_scheduler_id. The scheduler is further customized by
+# request_scheduler_options as described below.
+request_scheduler: org.apache.cassandra.scheduler.NoScheduler
+
+# Scheduler Options vary based on the type of scheduler
+# NoScheduler - Has no options
+# RoundRobin
+# - throttle_limit -- The throttle_limit is the number of in-flight
+# requests per client. Requests beyond
+# that limit are queued up until
+# running requests can complete.
+# The value of 80 here is twice the number of
+# concurrent_reads + concurrent_writes.
+# - default_weight -- default_weight is optional and allows for
+# overriding the default which is 1.
+# - weights -- Weights are optional and will default to 1 or the
+# overridden default_weight. The weight translates into how
+# many requests are handled during each turn of the
+# RoundRobin, based on the scheduler id.
+#
+# request_scheduler_options:
+# throttle_limit: 80
+# default_weight: 5
+# weights:
+# Keyspace1: 1
+# Keyspace2: 5
+
+# request_scheduler_id -- An identifer based on which to perform
+# the request scheduling. Currently the only valid option is keyspace.
+# request_scheduler_id: keyspace
+
+# index_interval controls the sampling of entries from the primrary
+# row index in terms of space versus time. The larger the interval,
+# the smaller and less effective the sampling will be. In technicial
+# terms, the interval coresponds to the number of index entries that
+# are skipped between taking each sample. All the sampled entries
+# must fit in memory. Generally, a value between 128 and 512 here
+# coupled with a large key cache size on CFs results in the best trade
+# offs. This value is not often changed, however if you have many
+# very small rows (many to an OS page), then increasing this will
+# often lower memory usage without a impact on performance.
+index_interval: 128
+
+# Enable or disable inter-node encryption
+# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
+# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
+# suite for authentication, key exchange and encryption of the actual data transfers.
+# NOTE: No custom encryption options are enabled at the moment
+# The available internode options are : all, none, dc, rack
+#
+# If set to dc cassandra will encrypt the traffic between the DCs
+# If set to rack cassandra will encrypt the traffic between the racks
+#
+# The passwords used in these options must match the passwords used when generating
+# the keystore and truststore. For instructions on generating these files, see:
+# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSS…
+#
+server_encryption_options:
+ internode_encryption: none
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ truststore: conf/.truststore
+ truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
+ # require_client_auth: false
+
+# enable or disable client/server encryption.
+client_encryption_options:
+ enabled: false
+ keystore: conf/.keystore
+ keystore_password: cassandra
+ # require_client_auth: false
+ # Set trustore and truststore_password if require_client_auth is true
+ # truststore: conf/.truststore
+ # truststore_password: cassandra
+ # More advanced defaults below:
+ # protocol: TLS
+ # algorithm: SunX509
+ # store_type: JKS
+ # cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
+
+# internode_compression controls whether traffic between nodes is
+# compressed.
+# can be: all - all traffic is compressed
+# dc - traffic between different datacenters is compressed
+# none - nothing is compressed.
+internode_compression: none
+
+# Enable or disable tcp_nodelay for inter-dc communication.
+# Disabling it will result in larger (but fewer) network packets being sent,
+# reducing overhead from the TCP protocol itself, at the cost of increasing
+# latency if you block for cross-datacenter responses.
+inter_dc_tcp_nodelay: true
commit 9981901120d09f9eaef388ef596f11f297ea55c7
Author: John Sanda <jsanda(a)redhat.com>
Date: Fri Jul 26 15:42:46 2013 -0400
add storage node shutdown operation that uses pid file
There can be problems with getting the actual, current pid from Sigar, so the
storage node plugin will first attempt to get the pid from the pid file which
should exist on disk. If that fails, then we will attempt to get the pid from
Sigar.
This implementation for shutdown does not work for Windows so need to revist it
to add in Windows support. We may even consider delegating to rhqtcl.
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index 3e55a93..d648ad8 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -246,8 +246,6 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
long pid = process.getPid();
try {
- getEmsConnection().close();
-
process.kill("KILL");
Configuration pluginConfig = getResourceContext().getPluginConfiguration();
@@ -266,7 +264,7 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
- private void waitForNodeToGoDown() throws InterruptedException {
+ protected void waitForNodeToGoDown() throws InterruptedException {
if (OperatingSystem.getInstance().getName().equals(OperatingSystem.NAME_MACOSX)) {
// See this thread on VMWare forum: http://communities.vmware.com/message/2187972#2187972
// Unfortunately there is no work around for this failure on Mac OSX so the method will silently return on
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
index 125f4d2..5933093 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
@@ -28,6 +28,7 @@ package org.rhq.plugins.storage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
+import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
@@ -39,6 +40,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.hyperic.sigar.SigarException;
import org.mc4j.ems.connection.EmsConnection;
import org.mc4j.ems.connection.bean.EmsBean;
import org.mc4j.ems.connection.bean.attribute.EmsAttribute;
@@ -54,9 +56,11 @@ import org.rhq.core.domain.configuration.PropertyMap;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.configuration.ConfigurationFacet;
import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
+import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
+import org.rhq.core.system.ProcessInfo;
import org.rhq.core.util.StringUtil;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.file.FileUtil;
@@ -105,11 +109,80 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
return updateKnownNodes(parameters);
} else if (name.equals("prepareForBootstrap")) {
return prepareForBootstrap(parameters);
+ } else if (name.equals("shutdown")) {
+ return shutdownStorageNode();
} else {
return super.invokeOperation(name, parameters);
}
}
+ private OperationResult shutdownStorageNode() {
+ OperationResult result = new OperationResult();
+ File binDir = new File(getBasedir(), "bin");
+ File pidFile = new File(binDir, "cassandra.pid");
+
+ try {
+ if (pidFile.exists()) {
+ long pid = readPidFile(pidFile);
+ log.info("Shutting down storage node with pid " + pid);
+ ProcessInfo process = findProcessInfo(pid);
+ if (process != null) {
+ try {
+ process.kill("KILL");
+ waitForNodeToGoDown();
+ pidFile.delete();
+ result.setSimpleResult("Successfully storage node with pid " + pid);
+ } catch (SigarException e) {
+ log.error("Failed to delete storage node with pid " + process.getPid(), e);
+ result.setErrorMessage("Failed to delete storage node with pid " + pid + ": " +
+ ThrowableUtil.getAllMessages(e));
+ }
+ } else {
+ log.warn("Could not find process info for pid " + pid);
+ result = shutdownUsingNativeProcessInfo();
+ }
+
+ } else {
+ log.warn("Did not find pid file " + pidFile + ". It should not be modified, deleted, or moved.");
+ result = shutdownUsingNativeProcessInfo();
+ }
+ } catch (FileNotFoundException e) {
+ log.error("Could not read pid file " + pidFile, e);
+ result.setErrorMessage("Could not read pid file " + pidFile + ": " + ThrowableUtil.getAllMessages(e));
+ } catch (InterruptedException e) {
+ log.warn("The shutdown operation was cancelled or interrupted. This interruption occurred while trying " +
+ "to verify that the storage node process has exited.");
+ result.setErrorMessage("The operation was cancelled or interrupted while trying to verify that the " +
+ "storage node process has exited.");
+ }
+ return result;
+ }
+
+ private long readPidFile(File pidFile) throws FileNotFoundException {
+ return Long.parseLong(StreamUtil.slurp(new FileReader(pidFile)));
+ }
+
+ private ProcessInfo findProcessInfo(long pid) {
+ List<ProcessScanResult> scanResults = getResourceContext().getNativeProcessesForType();
+
+ for (ProcessScanResult scanResult : scanResults) {
+ if (scanResult.getProcessInfo().getPid() == pid) {
+ return scanResult.getProcessInfo();
+ }
+ }
+ return null;
+ }
+
+ private OperationResult shutdownUsingNativeProcessInfo() throws InterruptedException {
+ log.warn("Could not obtain process info from pid file");
+ log.info("Obtaining process info from the system to perform the shutdown");
+
+ OperationResult result = shutdownNode();
+ waitForNodeToGoDown();
+
+ return result;
+ }
+
private OperationResult updateConfiguration(Configuration params) {
boolean restartIsRequired = false;
@@ -237,7 +310,7 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
OperationResult result = new OperationResult();
log.info("Stopping storage node");
- OperationResult stopNodeResult = stopNode();
+ OperationResult stopNodeResult = shutdownStorageNode();
if (stopNodeResult.getErrorMessage() != null) {
log.error("Failed to stop storage node " + this + " Cannot prepare the node for bootstrap which means " +
"that the storage node cannot join the cluster. Make sure the storage node is not running and retry " +
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index f0744a4..eb4d545 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -231,6 +231,26 @@ public class StorageNodeComponentITest {
assertNodeIsUp("Expected " + storageNode + " to be up after restarting it.");
}
+ @Test(dependsOnMethods = "restartStorageNode")
+ public void prepareForBootstrap() {
+ Configuration params = Configuration.builder().addSimple("cqlPort", 9242).addSimple("gossipPort", 7200)
+ .openList("storageNodeIPAddresses", "storageNodeIPAddresse").addSimples("127.0.0.1", "127.0.0.2")
+ .closeList().build();
+
+ OperationManager operationManager = PluginContainer.getInstance().getOperationManager();
+ OperationServicesAdapter operationsService = new OperationServicesAdapter(operationManager);
+
+ long timeout = 1000 * 60;
+ OperationContextImpl operationContext = new OperationContextImpl(storageNode.getId());
+ OperationServicesResult result = operationsService.invokeOperation(operationContext, "prepareForBootstrap",
+ params, timeout);
+
+ assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The operation failed: " +
+ result.getErrorStackTrace());
+
+ assertNodeIsUp("Expected " + storageNode + " to be up after the prepareForBootstrap operation completes.");
+ }
+
private void assertNodeIsUp(String msg) {
executeAvailabilityScan();
@@ -251,7 +271,6 @@ public class StorageNodeComponentITest {
private Availability getAvailability() {
InventoryManager inventoryManager = PluginContainer.getInstance().getInventoryManager();
-// return inventoryManager.getAvailabilityIfKnown(storageNode);
return inventoryManager.getCurrentAvailability(storageNode);
}
commit 9b9c2ccb1e5336cf8630accb13e4118d36698a3a
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Jul 26 14:14:15 2013 +0200
Put the real version in here, as otherwise we have a circular dependency.
diff --git a/modules/enterprise/gui/coregui/pom.xml b/modules/enterprise/gui/coregui/pom.xml
index 353f4d9..a227ed4 100644
--- a/modules/enterprise/gui/coregui/pom.xml
+++ b/modules/enterprise/gui/coregui/pom.xml
@@ -19,7 +19,7 @@
<properties>
<!-- dependency versions -->
- <gwt.version>${gwt.version}</gwt.version>
+ <gwt.version>2.5.0</gwt.version>
<smartgwt.version>3.0</smartgwt.version>
<!-- If this is too much memory to allocate to your gwt:debug process then override this property in
commit db58d3fc79994b022810e7c2b38e067161cb66e0
Author: John Sanda <jsanda(a)redhat.com>
Date: Thu Jul 25 22:08:20 2013 -0400
check cluster status using cql driver instead of using jmx
The server had been polling storage cluster nodes with a jmx call to make sure
that at least one node is up for client requests. If no nodes are up, then the
server goes into maintenance mode.
That check is no longer done with a quartz job using jmx. It is now performed
with event notifications we receive from the cql driver. This means that the
server no longer has to care about storage node jmx ports. This has a couple
benefits. First, it reduces the complexity involved with changing the jmx port.
Secondly, since the server no longer is making jmx calls to storage nodes, the
port can be locked down to localhost access.
Any server side code that needs access to the driver's Session should instead
use the new StorageSession class. StorageSession provides the same API, and it
also encapsulates the event handling/propagation logic that is necesary for
monitoring the cluster availability.
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBeanTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBeanTest.java
index 4cc67c0..9f49143 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBeanTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBeanTest.java
@@ -39,7 +39,6 @@ import java.util.List;
import javax.ejb.EJB;
-import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.joda.time.DateTime;
@@ -60,13 +59,14 @@ import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
-import org.rhq.enterprise.server.storage.StorageClientManagerBean;
import org.rhq.enterprise.server.drift.DriftServerPluginService;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.storage.StorageClientManagerBean;
import org.rhq.enterprise.server.test.AbstractEJB3Test;
import org.rhq.enterprise.server.test.TransactionCallback;
import org.rhq.enterprise.server.util.ResourceTreeHelper;
import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageSession;
import org.rhq.server.metrics.domain.AggregateNumericMetric;
import org.rhq.server.metrics.domain.AggregateType;
import org.rhq.server.metrics.domain.MetricsTable;
@@ -360,7 +360,7 @@ public class MeasurementDataManagerBeanTest extends AbstractEJB3Test {
private void purgeMetricsTables() {
try {
- Session session = storageClientManager.getSession();
+ StorageSession session = storageClientManager.getSession();
session.execute("TRUNCATE " + MetricsTable.RAW);
session.execute("TRUNCATE " + MetricsTable.ONE_HOUR);
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/test/MeasurementBaselineManagerTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/test/MeasurementBaselineManagerTest.java
index 49048ee..fdf2b1a 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/test/MeasurementBaselineManagerTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/measurement/test/MeasurementBaselineManagerTest.java
@@ -28,7 +28,6 @@ import java.util.Random;
import javax.inject.Inject;
import javax.persistence.Query;
-import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.testng.annotations.Test;
@@ -45,13 +44,14 @@ import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.util.PageControl;
-import org.rhq.enterprise.server.storage.StorageClientManagerBean;
import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementOOBManagerLocal;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.storage.StorageClientManagerBean;
import org.rhq.enterprise.server.test.AbstractEJB3Test;
import org.rhq.enterprise.server.util.LookupUtil;
import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageSession;
import org.rhq.server.metrics.domain.AggregateNumericMetric;
import org.rhq.server.metrics.domain.AggregateType;
import org.rhq.server.metrics.domain.MetricsTable;
@@ -645,7 +645,7 @@ public class MeasurementBaselineManagerTest extends AbstractEJB3Test {
// Query q = em.createNativeQuery(sql);
// q.executeUpdate();
try {
- Session session = storageClientManager.getSession();
+ StorageSession session = storageClientManager.getSession();
session.execute("DELETE FROM " + MetricsTable.ONE_HOUR.getTableName() + " WHERE schedule_id = " +
schedule.getId());
} catch (NoHostAvailableException e) {
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
index 7ce77d8..fa88263 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/core/StartupBean.java
@@ -450,7 +450,6 @@ public class StartupBean implements StartupLocal {
}
storageClientManager.init();
- storageClusterHeartBeatJob.scheduleJob();
}
/**
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
index 2d48092..ae80e50 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
@@ -50,6 +50,7 @@ import org.rhq.server.metrics.DateTimeService;
import org.rhq.server.metrics.MetricsConfiguration;
import org.rhq.server.metrics.MetricsDAO;
import org.rhq.server.metrics.MetricsServer;
+import org.rhq.server.metrics.StorageSession;
/**
* @author John Sanda
@@ -70,7 +71,7 @@ public class StorageClientManagerBean {
@EJB
private StorageNodeManagerLocal storageNodeManager;
- private Session session;
+ private StorageSession session;
private MetricsConfiguration metricsConfiguration;
private MetricsDAO metricsDAO;
private MetricsServer metricsServer;
@@ -95,7 +96,12 @@ public class StorageClientManagerBean {
String password = getRequiredStorageProperty(PASSWORD_PROP);
metricsConfiguration = new MetricsConfiguration();
- session = createSession(username, password, storageNodeManager.getStorageNodes());
+
+ Session wrappedSession = createSession(username, password, storageNodeManager.getStorageNodes());
+ session = new StorageSession(wrappedSession);
+
+ session.addStorageStateListener(new StorageClusterMonitor());
+
metricsDAO = new MetricsDAO(session, metricsConfiguration);
Server server = serverManager.getServer();
@@ -121,15 +127,15 @@ public class StorageClientManagerBean {
}
public MetricsDAO getMetricsDAO() {
- return this.metricsDAO;
+ return metricsDAO;
}
public MetricsServer getMetricsServer() {
- return this.metricsServer;
+ return metricsServer;
}
- public Session getSession() {
- return this.session;
+ public StorageSession getSession() {
+ return session;
}
public MetricsConfiguration getMetricsConfiguration() {
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java
new file mode 100644
index 0000000..ec28888
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClusterMonitor.java
@@ -0,0 +1,69 @@
+package org.rhq.enterprise.server.storage;
+
+import java.net.InetAddress;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.datastax.driver.core.exceptions.NoHostAvailableException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.cloud.Server;
+import org.rhq.enterprise.server.auth.SubjectManagerLocal;
+import org.rhq.enterprise.server.cloud.TopologyManagerLocal;
+import org.rhq.enterprise.server.cloud.instance.ServerManagerLocal;
+import org.rhq.enterprise.server.util.LookupUtil;
+import org.rhq.server.metrics.StorageStateListener;
+
+/**
+ * @author John Sanda
+ */
+public class StorageClusterMonitor implements StorageStateListener {
+
+ private Log log = LogFactory.getLog(StorageClusterMonitor.class);
+
+ private AtomicBoolean isClusterDown = new AtomicBoolean(false);
+
+ public boolean isClusterDown() {
+ return isClusterDown.get();
+ }
+
+ @Override
+ public void onStorageNodeUp(InetAddress address) {
+ log.info("Storage node at " + address.getHostAddress() + " is up");
+
+ if (isClusterDown.compareAndSet(true, false)) {
+ log.info("Taking server out of maintenance mode");
+ updateServerMode(Server.OperationMode.NORMAL);
+ }
+ }
+
+ @Override
+ public void onStorageNodeDown(InetAddress address) {
+ log.info("Storage node at " + address.getHostAddress() + " is down");
+ }
+
+ @Override
+ public void onStorageNodeRemoved(InetAddress address) {
+ log.info("Storage node at " + address.getHostAddress() + " has been removed from the cluster");
+ }
+
+ @Override
+ public void onStorageClusterDown(NoHostAvailableException e) {
+ if (isClusterDown.compareAndSet(false, true)) {
+ log.error("The server cannot connect to any storage nodes. The server will now go into maintenance mode.");
+ updateServerMode(Server.OperationMode.MAINTENANCE);
+ }
+ }
+
+ private void updateServerMode(Server.OperationMode mode) {
+ ServerManagerLocal serverManager = LookupUtil.getServerManager();
+ TopologyManagerLocal topologyManager = LookupUtil.getTopologyManager();
+ SubjectManagerLocal subjectManager = LookupUtil.getSubjectManager();
+
+ Server server = serverManager.getServer();
+
+ topologyManager.updateServerMode(subjectManager.getOverlord(), new Integer[] {server.getId()},
+ Server.OperationMode.MAINTENANCE);
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
index 3ea47e8..63c11f1 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
@@ -26,20 +26,13 @@
package org.rhq.server.metrics;
-import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
-
-import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.ResultSetFuture;
-import com.datastax.driver.core.Session;
-import com.datastax.driver.core.Statement;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.apache.commons.logging.Log;
@@ -67,7 +60,7 @@ public class MetricsDAO {
private final Log log = LogFactory.getLog(MetricsDAO.class);
- private Session session;
+ private StorageSession storageSession;
private MetricsConfiguration configuration;
@@ -86,8 +79,8 @@ public class MetricsDAO {
private PreparedStatement findTimeSliceForIndex;
private PreparedStatement deleteIndexEntries;
- public MetricsDAO(Session session, MetricsConfiguration configuration) {
- this.session = session;
+ public MetricsDAO(StorageSession session, MetricsConfiguration configuration) {
+ this.storageSession = session;
this.configuration = configuration;
initPreparedStatements();
}
@@ -104,176 +97,118 @@ public class MetricsDAO {
// re-initialized and re-prepared with the new TTLs. None of this would be necessary
// if the TTL value could be a bound value.
- insertRawData = session.prepare(
+ insertRawData = storageSession.prepare(
"INSERT INTO " + MetricsTable.RAW + " (schedule_id, time, value) VALUES (?, ?, ?) USING TTL " +
configuration.getRawTTL());
- rawMetricsQuery = session.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
+ rawMetricsQuery = storageSession.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
" WHERE schedule_id = ? AND time >= ? AND time < ? ORDER BY time");
- insertOneHourData = session.prepare("INSERT INTO " + MetricsTable.ONE_HOUR + "(schedule_id, time, " +
+ insertOneHourData = storageSession.prepare("INSERT INTO " + MetricsTable.ONE_HOUR + "(schedule_id, time, " +
"type, value) VALUES (?, ?, ?, ?) USING TTL " + configuration.getOneHourTTL());
- insertSixHourData = session.prepare("INSERT INTO " + MetricsTable.SIX_HOUR + "(schedule_id, time, " +
+ insertSixHourData = storageSession.prepare("INSERT INTO " + MetricsTable.SIX_HOUR + "(schedule_id, time, " +
"type, value) VALUES (?, ?, ?, ?) USING TTL " + configuration.getOneHourTTL());
- insertTwentyFourHourData = session.prepare("INSERT INTO " + MetricsTable.TWENTY_FOUR_HOUR + "(schedule_id, " +
+ insertTwentyFourHourData = storageSession.prepare("INSERT INTO " + MetricsTable.TWENTY_FOUR_HOUR + "(schedule_id, " +
"time, type, value) VALUES (?, ?, ?, ?) USING TTL " + configuration.getOneHourTTL());
- updateMetricsIndex = session.prepare("INSERT INTO " + MetricsTable.INDEX + " (bucket, time, schedule_id) " +
+ updateMetricsIndex = storageSession.prepare("INSERT INTO " + MetricsTable.INDEX + " (bucket, time, schedule_id) " +
"VALUES (?, ?, ?)");
- findLatestRawMetric = session.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
+ findLatestRawMetric = storageSession.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
" WHERE schedule_id = ? ORDER BY time DESC LIMIT 1");
- findRawMetrics = session.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
+ findRawMetrics = storageSession.prepare("SELECT schedule_id, time, value FROM " + MetricsTable.RAW +
" WHERE schedule_id = ? AND time >= ? AND time <= ?");
- findOneHourMetricsByDateRange = session.prepare("SELECT schedule_id, time, type, value FROM " +
+ findOneHourMetricsByDateRange = storageSession.prepare("SELECT schedule_id, time, type, value FROM " +
MetricsTable.ONE_HOUR + " WHERE schedule_id = ? AND time >= ? AND time < ?");
- findSixHourMetricsByDateRange = session.prepare("SELECT schedule_id, time, type, value FROM "
+ findSixHourMetricsByDateRange = storageSession.prepare("SELECT schedule_id, time, type, value FROM "
+ MetricsTable.SIX_HOUR + " WHERE schedule_id = ? AND time >= ? AND time < ?");
- findTwentyFourHourMetricsByDateRange = session.prepare("SELECT schedule_id, time, type, value FROM " +
+ findTwentyFourHourMetricsByDateRange = storageSession.prepare("SELECT schedule_id, time, type, value FROM " +
MetricsTable.TWENTY_FOUR_HOUR + " WHERE schedule_id = ? AND time >= ? AND time < ?");
- findIndexEntries = session.prepare("SELECT time, schedule_id FROM " + MetricsTable.INDEX +
+ findIndexEntries = storageSession.prepare("SELECT time, schedule_id FROM " + MetricsTable.INDEX +
" WHERE bucket = ? AND time = ?");
- findTimeSliceForIndex = session.prepare("SELECT time FROM " + MetricsTable.INDEX +
+ findTimeSliceForIndex = storageSession.prepare("SELECT time FROM " + MetricsTable.INDEX +
" WHERE bucket = ? AND time = ?");
- deleteIndexEntries = session.prepare("DELETE FROM " + MetricsTable.INDEX + " WHERE bucket = ? AND time = ?");
+ deleteIndexEntries = storageSession.prepare("DELETE FROM " + MetricsTable.INDEX + " WHERE bucket = ? AND time = ?");
long endTime = System.currentTimeMillis();
log.info("Finished initializing prepared statements in " + (endTime - startTime) + " ms");
}
- public ResultSetFuture insertRawData(MeasurementDataNumeric data) {
+ public StorageResultSetFuture insertRawData(MeasurementDataNumeric data) {
BoundStatement statement = insertRawData.bind(data.getScheduleId(), new Date(data.getTimestamp()),
data.getValue());
- return session.executeAsync(statement);
- }
-
- public List<MetricResultFuture<MeasurementDataNumeric>> insertRawMetricsAsync(Set<MeasurementDataNumeric> dataSet,
- int ttl) {
- try {
- List<MetricResultFuture<MeasurementDataNumeric>> resultFutures = new ArrayList<MetricResultFuture<MeasurementDataNumeric>>();
-
- String cql = "INSERT INTO raw_metrics (schedule_id, time, value) VALUES (?, ?, ?) " + "USING TTL " + ttl;
- PreparedStatement statement = session.prepare(cql);
-
- for (MeasurementDataNumeric data : dataSet) {
- BoundStatement boundStatement = statement.bind(data.getScheduleId(), new Date(data.getTimestamp()),
- data.getValue());
-
- resultFutures.add(new MetricResultFuture<MeasurementDataNumeric>(session.executeAsync(boundStatement),
- data));
- }
-
- return resultFutures;
- } catch (NoHostAvailableException e) {
- throw new CQLException(e);
- }
+ return storageSession.executeAsync(statement);
}
public ResultSet insertOneHourData(int scheduleId, long timestamp, AggregateType type, double value) {
BoundStatement statement = insertOneHourData.bind(scheduleId, new Date(timestamp), type.ordinal(), value);
- return session.execute(statement);
+ return storageSession.execute(statement);
}
public ResultSet insertSixHourData(int scheduleId, long timestamp, AggregateType type, double value) {
BoundStatement statement = insertSixHourData.bind(scheduleId, new Date(timestamp), type.ordinal(), value);
- return session.execute(statement);
+ return storageSession.execute(statement);
}
public ResultSet insertTwentyFourHourData(int scheduleId, long timestamp, AggregateType type, double value) {
BoundStatement statement = insertTwentyFourHourData.bind(scheduleId, new Date(timestamp), type.ordinal(),
value);
- return session.execute(statement);
- }
-
- public List<MetricResultFuture<AggregateNumericMetric>> insertAggregatesAsync(MetricsTable table,
- List<AggregateNumericMetric> metrics, int ttl) {
- List<MetricResultFuture<AggregateNumericMetric>> updates = new ArrayList<MetricResultFuture<AggregateNumericMetric>>();
-
- if (metrics.isEmpty()) {
- return updates;
- }
-
- try {
- Statement statement = null;
-
- for (AggregateNumericMetric metric : metrics) {
- statement = insertInto(table.getTableName())
- .value("schedule_id", metric.getScheduleId())
- .value("time", new Date(metric.getTimestamp()))
- .value("type", AggregateType.MIN.ordinal())
- .value("value", metric.getMin());
- updates.add(new MetricResultFuture<AggregateNumericMetric>(session.executeAsync(statement), metric));
-
- statement = insertInto(table.getTableName())
- .value("schedule_id", metric.getScheduleId())
- .value("time", new Date(metric.getTimestamp()))
- .value("type", AggregateType.MAX.ordinal())
- .value("value", metric.getMax());
- updates.add(new MetricResultFuture<AggregateNumericMetric>(session.executeAsync(statement), metric));
-
- statement = insertInto(table.getTableName())
- .value("schedule_id", metric.getScheduleId())
- .value("time", new Date(metric.getTimestamp()))
- .value("type", AggregateType.AVG.ordinal())
- .value("value", metric.getAvg());
- updates.add(new MetricResultFuture<AggregateNumericMetric>(session.executeAsync(statement), metric));
- }
-
- return updates;
- } catch (Exception e) {
- throw new CQLException(e);
- }
+ return storageSession.execute(statement);
}
public Iterable<RawNumericMetric> findRawMetrics(int scheduleId, long startTime, long endTime) {
try {
BoundStatement boundStatement = rawMetricsQuery.bind(scheduleId, new Date(startTime), new Date(endTime));
- return new SimplePagedResult<RawNumericMetric>(boundStatement, new RawNumericMetricMapper(false), session);
+ return new SimplePagedResult<RawNumericMetric>(boundStatement, new RawNumericMetricMapper(false),
+ storageSession);
} catch (NoHostAvailableException e) {
throw new CQLException(e);
}
}
- public ResultSetFuture findRawMetricsAsync(int scheduleId, long startTime, long endTime) {
+ public StorageResultSetFuture findRawMetricsAsync(int scheduleId, long startTime, long endTime) {
BoundStatement boundStatement = rawMetricsQuery.bind(scheduleId, new Date(startTime), new Date(endTime));
- return session.executeAsync(boundStatement);
+ return storageSession.executeAsync(boundStatement);
}
public RawNumericMetric findLatestRawMetric(int scheduleId) {
RawNumericMetricMapper mapper = new RawNumericMetricMapper(false);
BoundStatement boundStatement = findLatestRawMetric.bind(scheduleId);
- ResultSet resultSet = session.execute(boundStatement);
+ ResultSet resultSet = storageSession.execute(boundStatement);
return mapper.mapOne(resultSet);
}
public Iterable<RawNumericMetric> findRawMetrics(List<Integer> scheduleIds, long startTime, long endTime) {
return new ListPagedResult<RawNumericMetric>(findRawMetrics, scheduleIds, startTime, endTime,
- new RawNumericMetricMapper(), session);
+ new RawNumericMetricMapper(), storageSession);
}
public Iterable<AggregateNumericMetric> findOneHourMetrics(int scheduleId, long startTime, long endTime) {
BoundStatement statement = findOneHourMetricsByDateRange.bind(scheduleId, new Date(startTime), new Date(endTime));
- return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(), session);
+ return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(),
+ storageSession);
}
public Iterable<AggregateNumericMetric> findSixHourMetrics(int scheduleId, long startTime, long endTime) {
BoundStatement statement = findSixHourMetricsByDateRange.bind(scheduleId, new Date(startTime), new Date(endTime));
- return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(), session);
+ return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(),
+ storageSession);
}
public Iterable<AggregateNumericMetric> findTwentyFourHourMetrics(int scheduleId, long startTime, long endTime) {
BoundStatement statement = findTwentyFourHourMetricsByDateRange.bind(scheduleId, new Date(startTime), new Date(endTime));
- return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(), session);
+ return new SimplePagedResult<AggregateNumericMetric>(statement, new AggregateNumericMetricMapper(),
+ storageSession);
}
public Iterable<AggregateSimpleNumericMetric> findAggregatedSimpleOneHourMetric(int scheduleId, long startTime,
@@ -281,52 +216,52 @@ public class MetricsDAO {
BoundStatement statement = findOneHourMetricsByDateRange.bind(scheduleId, new Date(startTime),
new Date(endTime));
return new SimplePagedResult<AggregateSimpleNumericMetric>(statement, new AggregateSimpleNumericMetricMapper(),
- session);
+ storageSession);
}
public Iterable<AggregateNumericMetric> findOneHourMetrics(List<Integer> scheduleIds, long startTime,
long endTime) {
return new ListPagedResult<AggregateNumericMetric>(findOneHourMetricsByDateRange, scheduleIds, startTime, endTime,
- new AggregateNumericMetricMapper(), session);
+ new AggregateNumericMetricMapper(), storageSession);
}
public Iterable<AggregateNumericMetric> findSixHourMetrics(List<Integer> scheduleIds, long startTime,
long endTime) {
return new ListPagedResult<AggregateNumericMetric>(findSixHourMetricsByDateRange, scheduleIds, startTime, endTime,
- new AggregateNumericMetricMapper(), session);
+ new AggregateNumericMetricMapper(), storageSession);
}
public Iterable<AggregateNumericMetric> findTwentyFourHourMetrics(List<Integer> scheduleIds, long startTime,
long endTime) {
return new ListPagedResult<AggregateNumericMetric>(findTwentyFourHourMetricsByDateRange, scheduleIds, startTime, endTime,
- new AggregateNumericMetricMapper(), session);
+ new AggregateNumericMetricMapper(), storageSession);
}
public Iterable<MetricsIndexEntry> findMetricsIndexEntries(final MetricsTable table, long timestamp) {
BoundStatement statement = findIndexEntries.bind(table.toString(), new Date(timestamp));
- return new SimplePagedResult<MetricsIndexEntry>(statement, new MetricsIndexEntryMapper(table), session);
+ return new SimplePagedResult<MetricsIndexEntry>(statement, new MetricsIndexEntryMapper(table), storageSession);
}
public ResultSet setFindTimeSliceForIndex(MetricsTable table, long timestamp) {
BoundStatement statement = findTimeSliceForIndex.bind(table.toString(), new Date(timestamp));
- return session.execute(statement);
+ return storageSession.execute(statement);
}
public void updateMetricsIndex(MetricsTable table, Map<Integer, Long> updates) {
for (Integer scheduleId : updates.keySet()) {
BoundStatement statement = updateMetricsIndex.bind(table.getTableName(),
new Date(updates.get(scheduleId)), scheduleId);
- session.execute(statement);
+ storageSession.execute(statement);
}
}
- public ResultSetFuture updateMetricsIndex(MetricsTable table, int scheduleId, long timestamp) {
+ public StorageResultSetFuture updateMetricsIndex(MetricsTable table, int scheduleId, long timestamp) {
BoundStatement statement = updateMetricsIndex.bind(table.getTableName(), new Date(timestamp), scheduleId);
- return session.executeAsync(statement);
+ return storageSession.executeAsync(statement);
}
public void deleteMetricsIndexEntries(MetricsTable table, long timestamp) {
BoundStatement statement = deleteIndexEntries.bind(table.getTableName(), new Date(timestamp));
- session.execute(statement);
+ storageSession.execute(statement);
}
}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
index 7395953..9756006 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
@@ -35,7 +35,6 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Row;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
@@ -299,7 +298,7 @@ public class MetricsServer {
for (final MeasurementDataNumeric data : dataSet) {
semaphore.acquire();
- ResultSetFuture resultSetFuture = dao.insertRawData(data);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(data);
Futures.addCallback(resultSetFuture, new FutureCallback<ResultSet>() {
@Override
public void onSuccess(ResultSet rows) {
@@ -325,7 +324,7 @@ public class MetricsServer {
long timeSlice = dateTimeService.getTimeSlice(new DateTime(rawData.getTimestamp()),
configuration.getRawTimeSliceDuration()).getMillis();
- ResultSetFuture resultSetFuture = dao.updateMetricsIndex(MetricsTable.ONE_HOUR, rawData.getScheduleId(),
+ StorageResultSetFuture resultSetFuture = dao.updateMetricsIndex(MetricsTable.ONE_HOUR, rawData.getScheduleId(),
timeSlice);
Futures.addCallback(resultSetFuture, new FutureCallback<ResultSet>() {
@Override
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageResultSetFuture.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageResultSetFuture.java
new file mode 100644
index 0000000..902bf4a
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageResultSetFuture.java
@@ -0,0 +1,76 @@
+package org.rhq.server.metrics;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.ResultSetFuture;
+import com.datastax.driver.core.exceptions.NoHostAvailableException;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * @author John Sanda
+ */
+public class StorageResultSetFuture implements ListenableFuture<ResultSet> {
+
+ private ResultSetFuture wrapperFuture;
+
+ private List<StorageStateListener> listeners;
+
+ public StorageResultSetFuture(ResultSetFuture resultSetFuture, List<StorageStateListener> listeners) {
+ wrapperFuture = resultSetFuture;
+ this.listeners = listeners;
+ }
+
+ @Override
+ public void addListener(Runnable listener, Executor executor) {
+ wrapperFuture.addListener(listener, executor);
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ return wrapperFuture.cancel(mayInterruptIfRunning);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return wrapperFuture.isCancelled();
+ }
+
+ @Override
+ public boolean isDone() {
+ return wrapperFuture.isDone();
+ }
+
+ @Override
+ public ResultSet get() throws InterruptedException, ExecutionException {
+ try {
+ return wrapperFuture.get();
+ } catch (ExecutionException e) {
+ return handleException(e);
+ }
+ }
+
+ @Override
+ public ResultSet get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
+ TimeoutException {
+ try {
+ return wrapperFuture.get(timeout, unit);
+ } catch (ExecutionException e) {
+ return handleException(e);
+ }
+ }
+
+ private ResultSet handleException(ExecutionException e) throws ExecutionException {
+ if (e.getCause() instanceof NoHostAvailableException) {
+ NoHostAvailableException cause = (NoHostAvailableException) e.getCause();
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageClusterDown(cause);
+ }
+ }
+ throw e;
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
new file mode 100644
index 0000000..3f7af3f
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageSession.java
@@ -0,0 +1,110 @@
+package org.rhq.server.metrics;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Host;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Query;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.ResultSetFuture;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.exceptions.NoHostAvailableException;
+
+/**
+ * @author John Sanda
+ */
+public class StorageSession implements Host.StateListener {
+
+ private Session wrappedSession;
+
+ private List<StorageStateListener> listeners = new ArrayList<StorageStateListener>();
+
+ public StorageSession(Session wrappedSession) {
+ this.wrappedSession = wrappedSession;
+ this.wrappedSession.getCluster().register(this);
+ }
+
+ public void addStorageStateListener(StorageStateListener listener) {
+ listeners.add(listener);
+ }
+
+ public ResultSet execute(String query) {
+ try {
+ return wrappedSession.execute(query);
+ } catch (NoHostAvailableException e) {
+ return handleException(e);
+ }
+ }
+
+ public ResultSet execute(Query query) {
+ try {
+ return wrappedSession.execute(query);
+ } catch (NoHostAvailableException e) {
+ return handleException(e);
+ }
+ }
+
+ public StorageResultSetFuture executeAsync(String query) {
+ ResultSetFuture future = wrappedSession.executeAsync(query);
+ return new StorageResultSetFuture(future, listeners);
+ }
+
+ public StorageResultSetFuture executeAsync(Query query) {
+ ResultSetFuture future = wrappedSession.executeAsync(query);
+ return new StorageResultSetFuture(future, listeners);
+ }
+
+ public PreparedStatement prepare(String query) {
+ return wrappedSession.prepare(query);
+ }
+
+ public void shutdown() {
+ wrappedSession.shutdown();
+ }
+
+ public boolean shutdown(long timeout, TimeUnit unit) {
+ return wrappedSession.shutdown(timeout, unit);
+ }
+
+ public Cluster getCluster() {
+ return wrappedSession.getCluster();
+ }
+
+ @Override
+ public void onAdd(Host host) {
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageNodeUp(host.getAddress());
+ }
+ }
+
+ @Override
+ public void onUp(Host host) {
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageNodeUp(host.getAddress());
+ }
+ }
+
+ @Override
+ public void onDown(Host host) {
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageNodeDown(host.getAddress());
+ }
+ }
+
+ @Override
+ public void onRemove(Host host) {
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageNodeRemoved(host.getAddress());
+ }
+ }
+
+ private ResultSet handleException(NoHostAvailableException e) {
+ for (StorageStateListener listener : listeners) {
+ listener.onStorageClusterDown(e);
+ }
+ throw e;
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageStateListener.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageStateListener.java
new file mode 100644
index 0000000..6a3a216
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/StorageStateListener.java
@@ -0,0 +1,20 @@
+package org.rhq.server.metrics;
+
+import java.net.InetAddress;
+
+import com.datastax.driver.core.exceptions.NoHostAvailableException;
+
+/**
+ * @author John Sanda
+ */
+public interface StorageStateListener {
+
+ void onStorageNodeUp(InetAddress address);
+
+ void onStorageNodeDown(InetAddress address);
+
+ void onStorageNodeRemoved(InetAddress address);
+
+ void onStorageClusterDown(NoHostAvailableException e);
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/ListPagedResult.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/ListPagedResult.java
index 1c1fdd2..f3abd20 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/ListPagedResult.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/ListPagedResult.java
@@ -33,10 +33,10 @@ import java.util.List;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.rhq.server.metrics.CQLException;
+import org.rhq.server.metrics.StorageSession;
/**
* This class helps paginate Cassandra results that have a list in the matching clause. Instead of running
@@ -52,12 +52,12 @@ public class ListPagedResult<T> implements Iterable<T> {
private final long startTime;
private final long endTime;
private final ResultSetMapper<T> mapper;
- private final Session session;
+ private final StorageSession session;
private final PreparedStatement preparedStatement;
public ListPagedResult(PreparedStatement preparedStatement, List<Integer> scheduleIds, long startTime, long endTime,
- ResultSetMapper<T> mapper, Session session) {
+ ResultSetMapper<T> mapper, StorageSession session) {
this.preparedStatement = preparedStatement;
this.scheduleIds = new LinkedList<Integer>(scheduleIds);
this.startTime = startTime;
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/SimplePagedResult.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/SimplePagedResult.java
index d14195c..e31db95 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/SimplePagedResult.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/domain/SimplePagedResult.java
@@ -34,6 +34,7 @@ import com.datastax.driver.core.SimpleStatement;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import org.rhq.server.metrics.CQLException;
+import org.rhq.server.metrics.StorageSession;
/**
* This class is just a placeholder for future pagination implementations once Cassandra gets native support for paging results.
@@ -51,7 +52,7 @@ public class SimplePagedResult<T> implements Iterable<T> {
private final ResultSetMapper<T> mapper;
private final Query query;
- private final Session session;
+ private final StorageSession session;
private final int pageSize;
/**
@@ -60,7 +61,7 @@ public class SimplePagedResult<T> implements Iterable<T> {
* @param session Cassandra session
* @param pageSize page size
*/
- public SimplePagedResult(Query query, ResultSetMapper<T> mapper, Session session, int pageSize) {
+ public SimplePagedResult(Query query, ResultSetMapper<T> mapper, StorageSession session, int pageSize) {
this.query = query;
this.mapper = mapper;
this.session = session;
@@ -73,7 +74,7 @@ public class SimplePagedResult<T> implements Iterable<T> {
* @param session Cassandra session
* @param pageSize page size
*/
- public SimplePagedResult(String query, ResultSetMapper<T> mapper, Session session, int pageSize) {
+ public SimplePagedResult(String query, ResultSetMapper<T> mapper, StorageSession session, int pageSize) {
this(new SimpleStatement(query), mapper, session, pageSize);
}
@@ -82,7 +83,7 @@ public class SimplePagedResult<T> implements Iterable<T> {
* @param mapper result set mapper
* @param session Cassandra session
*/
- public SimplePagedResult(Query query, ResultSetMapper<T> mapper, Session session) {
+ public SimplePagedResult(Query query, ResultSetMapper<T> mapper, StorageSession session) {
this(query, mapper, session, DEFAULT_PAGE_SIZE);
}
@@ -92,7 +93,7 @@ public class SimplePagedResult<T> implements Iterable<T> {
* @param session Cassandra session
* @param pageSize page size
*/
- public SimplePagedResult(String query, ResultSetMapper<T> mapper, Session session) {
+ public SimplePagedResult(String query, ResultSetMapper<T> mapper, StorageSession session) {
this(new SimpleStatement(query), mapper, session);
}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
index c4c48c8..b227b2d 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
@@ -33,6 +33,7 @@ import java.util.concurrent.CountDownLatch;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.Host;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
@@ -65,8 +66,12 @@ public class CassandraIntegrationTest {
protected static Session session;
+ protected static StorageSession storageSession;
+
private static DateTimeService dateTimeService;
+ private final Log log = LogFactory.getLog(CassandraIntegrationTest.class);
+
@BeforeSuite
@DeployCluster(numNodes = 2, username = "rhqadmin", password = "rhqadmin", waitForSchemaAgreement = true)
public void deployCluster() throws Exception {
@@ -76,7 +81,31 @@ public class CassandraIntegrationTest {
.addContactPoints("127.0.0.1", "127.0.02")
.withCredentials("rhqadmin", "rhqadmin")
.build();
+
+ cluster.register(new Host.StateListener() {
+ @Override
+ public void onAdd(Host host) {
+ log.info("host " + host + " added");
+ }
+
+ @Override
+ public void onUp(Host host) {
+ log.info("host " + host + " up");
+ }
+
+ @Override
+ public void onDown(Host host) {
+ log.info("host " + host + " down");
+ }
+
+ @Override
+ public void onRemove(Host host) {
+ log.info("host " + host + " removed");
+ }
+ });
+
session = cluster.connect("rhq");
+ storageSession = new StorageSession(session);
}
@AfterSuite(alwaysRun = true)
@@ -98,7 +127,7 @@ public class CassandraIntegrationTest {
BoundStatement boundStatement = statement.bind(scheduleId);
return new SimplePagedResult<AggregateNumericMetric>(boundStatement, new AggregateNumericMetricMapper(),
- session);
+ storageSession);
}
protected Iterable<AggregateNumericMetric> findAggregateMetricsWithMetadata(MetricsTable table, int scheduleId,
@@ -109,11 +138,11 @@ public class CassandraIntegrationTest {
"SELECT schedule_id, time, type, value, ttl(value), writetime(value) " +
"FROM " + table + " " +
"WHERE schedule_id = ? AND time >= ? AND time < ?";
- PreparedStatement statement = session.prepare(cql);
+ PreparedStatement statement = storageSession.prepare(cql);
BoundStatement boundStatement = statement.bind(scheduleId, new Date(startTime), new Date(endTime));
return new SimplePagedResult<AggregateNumericMetric>(boundStatement, new AggregateNumericMetricMapper(true),
- session);
+ storageSession);
} catch (NoHostAvailableException e) {
throw new CQLException(e);
}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ClusterMonitorTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ClusterMonitorTest.java
new file mode 100644
index 0000000..871b7c5
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/ClusterMonitorTest.java
@@ -0,0 +1,92 @@
+package org.rhq.server.metrics;
+
+import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.ConsistencyLevel;
+import com.datastax.driver.core.Host;
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Session;
+import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.cassandra.util.ClusterBuilder;
+import org.rhq.core.domain.measurement.MeasurementDataNumeric;
+
+/**
+ * @author John Sanda
+ */
+public class ClusterMonitorTest {
+
+ private final Log log = LogFactory.getLog(ClusterMonitorTest.class);
+
+// @Test
+ public void monitorCluster() {
+ Cluster cluster = new ClusterBuilder()
+ .addContactPoints("127.0.0.1")
+// .withCredentials("cassandra", "cassandra")
+ .withCredentials("rhqadmin", "rhqadmin")
+ .build();
+
+ cluster.register(new Host.StateListener() {
+ @Override
+ public void onAdd(Host host) {
+ log.info("host " + host + " ADDED");
+ }
+
+ @Override
+ public void onUp(Host host) {
+ log.info("host " + host + " UP");
+ }
+
+ @Override
+ public void onDown(Host host) {
+ log.info("host " + host + " DOWN");
+ }
+
+ @Override
+ public void onRemove(Host host) {
+ log.info("host " + host + " REMOVED");
+ }
+ });
+
+ Session session = cluster.connect("rhq");
+ StorageSession storageSession = new StorageSession(session);
+
+ MetricsDAO dao = new MetricsDAO(storageSession, new MetricsConfiguration());
+
+ while (true) {
+ try {
+ Thread.sleep(10000);
+ try {
+// session.execute("select * from system.schema_keyspaces");
+ com.datastax.driver.core.Query query = QueryBuilder.select().from("rhq", "raw_metrics").setConsistencyLevel(
+ ConsistencyLevel.ALL);
+// session.execute(query);
+// session.execute("select * from rhq.raw_metrics");
+// log.info("query succeeded");
+ StorageResultSetFuture future = dao.insertRawData
+ (new MeasurementDataNumeric(System.currentTimeMillis(), 123, 1.1));
+
+ Futures.addCallback(future, new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(ResultSet rows) {
+ log.info("insert succeeded");
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ log.error("insert failed", throwable);
+ }
+ });
+ } catch (Exception e) {
+ log.error("query failed", e);
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java
index ab4bd03..2b58222 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsBaselineCalculatorTest.java
@@ -35,8 +35,6 @@ import java.util.Arrays;
import java.util.List;
import java.util.Random;
-import com.datastax.driver.core.Session;
-
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockObjectFactory;
@@ -71,9 +69,9 @@ public class MetricsBaselineCalculatorTest {
//tell the method story as it happens: mock dependencies and configure
//those dependencies to get the method under test to completion.
- Session mockSession = mock(Session.class);
+ StorageSession mockSession = mock(StorageSession.class);
MetricsDAO mockMetricsDAO = mock(MetricsDAO.class);
- PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class, MetricsConfiguration.class)
+ PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(StorageSession.class, MetricsConfiguration.class)
.withArguments(eq(mockSession), eq(metricsConfiguration)).thenReturn(mockMetricsDAO);
when(mockMetricsDAO.findAggregatedSimpleOneHourMetric(eq(1), eq(0),
@@ -147,9 +145,9 @@ public class MetricsBaselineCalculatorTest {
//tell the method story as it happens: mock dependencies and configure
//those dependencies to get the method under test to completion.
- Session mockSession = mock(Session.class);
+ StorageSession mockSession = mock(StorageSession.class);
MetricsDAO mockMetricsDAO = mock(MetricsDAO.class);
- PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class, MetricsConfiguration.class)
+ PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(StorageSession.class, MetricsConfiguration.class)
.withArguments(eq(mockSession), eq(metricsConfiguration)).thenReturn(mockMetricsDAO);
when(
@@ -214,9 +212,9 @@ public class MetricsBaselineCalculatorTest {
//tell the method story as it happens: mock dependencies and configure
//those dependencies to get the method under test to completion.
- Session mockSession = mock(Session.class);
+ StorageSession mockSession = mock(StorageSession.class);
MetricsDAO mockMetricsDAO = mock(MetricsDAO.class);
- PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(Session.class, MetricsConfiguration.class)
+ PowerMockito.whenNew(MetricsDAO.class).withParameterTypes(StorageSession.class, MetricsConfiguration.class)
.withArguments(eq(mockSession), eq(metricsConfiguration)).thenReturn(mockMetricsDAO);
when(
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
index ee65292..b06dfda 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
@@ -38,7 +38,6 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
-import com.datastax.driver.core.ResultSetFuture;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
@@ -78,7 +77,7 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
@BeforeClass
public void initDAO() throws Exception {
- dao = new MetricsDAO(session, new MetricsConfiguration());
+ dao = new MetricsDAO(storageSession, new MetricsConfiguration());
}
@BeforeMethod
@@ -101,7 +100,7 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
WaitForWrite waitForResults = new WaitForWrite(1);
- ResultSetFuture resultSetFuture = dao.insertRawData(expected);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(expected);
Futures.addCallback(resultSetFuture, waitForResults);
waitForResults.await("Failed to insert raw data");
@@ -131,7 +130,7 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
WaitForWrite waitForWrite = new WaitForWrite(data.size());
for (MeasurementDataNumeric raw : data) {
- ResultSetFuture resultSetFuture = dao.insertRawData(raw);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(raw);
Futures.addCallback(resultSetFuture, waitForWrite);
}
waitForWrite.await("Failed to insert raw data");
@@ -160,14 +159,14 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
WaitForWrite waitForWrite = new WaitForWrite(data.size());
for (MeasurementDataNumeric raw : data) {
- ResultSetFuture resultSetFuture = dao.insertRawData(raw);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(raw);
Futures.addCallback(resultSetFuture, waitForWrite);
}
waitForWrite.await("Failed to insert raw data");
RawNumericMetricMapper mapper = new RawNumericMetricMapper();
WaitForRead<RawNumericMetric> waitForRead = new WaitForRead<RawNumericMetric>(mapper);
- ResultSetFuture resultSetFuture = dao.findRawMetricsAsync(scheduleId,
+ StorageResultSetFuture resultSetFuture = dao.findRawMetricsAsync(scheduleId,
threeMinutesAgo.minusSeconds(5).getMillis(), oneMinuteAgo.plusSeconds(5).getMillis());
Futures.addCallback(resultSetFuture, waitForRead);
@@ -200,7 +199,7 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
WaitForWrite waitForWrite = new WaitForWrite(data.size());
for (MeasurementDataNumeric raw : data) {
- ResultSetFuture resultSetFuture = dao.insertRawData(raw);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(raw);
Futures.addCallback(resultSetFuture, waitForWrite);
}
waitForWrite.await("Failed to insert raw data");
@@ -321,9 +320,9 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
WaitForWrite waitForWrite = new WaitForWrite(2);
- ResultSetFuture resultSetFuture1 = dao.updateMetricsIndex(MetricsTable.TWENTY_FOUR_HOUR, scheduleId1,
+ StorageResultSetFuture resultSetFuture1 = dao.updateMetricsIndex(MetricsTable.TWENTY_FOUR_HOUR, scheduleId1,
hour0().getMillis());
- ResultSetFuture resultSetFuture2 = dao.updateMetricsIndex(MetricsTable.TWENTY_FOUR_HOUR, scheduleId2,
+ StorageResultSetFuture resultSetFuture2 = dao.updateMetricsIndex(MetricsTable.TWENTY_FOUR_HOUR, scheduleId2,
hour0().getMillis());
Futures.addCallback(resultSetFuture1, waitForWrite);
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
index 1ec4771..b4de4e0 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
@@ -43,7 +43,6 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
-import com.datastax.driver.core.ResultSetFuture;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
@@ -113,7 +112,7 @@ public class MetricsServerTest extends CassandraIntegrationTest {
dateTimeService.setConfiguration(configuration);
metricsServer.setDateTimeService(dateTimeService);
- dao = new MetricsDAO(session, configuration);
+ dao = new MetricsDAO(storageSession, configuration);
metricsServer.setDAO(dao);
purgeDB();
@@ -230,7 +229,7 @@ public class MetricsServerTest extends CassandraIntegrationTest {
WaitForWrite waitForRawInserts = new WaitForWrite(rawMetrics.size());
for (MeasurementDataNumeric raw : rawMetrics) {
- ResultSetFuture resultSetFuture = dao.insertRawData(raw);
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(raw);
Futures.addCallback(resultSetFuture, waitForRawInserts);
}
waitForRawInserts.await("Failed to insert raw data");
@@ -1008,7 +1007,7 @@ public class MetricsServerTest extends CassandraIntegrationTest {
"SELECT schedule_id, time, value, ttl(value), writetime(value) " +
"FROM " + MetricsTable.RAW + " " +
"WHERE schedule_id = " + scheduleId + " AND time >= " + startTime + " AND time < " + endTime;
- return new SimplePagedResult<RawNumericMetric>(cql, new RawNumericMetricMapper(true), session);
+ return new SimplePagedResult<RawNumericMetric>(cql, new RawNumericMetricMapper(true), storageSession);
}
private static class WaitForRawInserts implements RawDataInsertedCallback {
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/TimeoutTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/TimeoutTest.java
index e2b23ec..dabd110 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/TimeoutTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/TimeoutTest.java
@@ -35,7 +35,6 @@ import com.google.common.base.Throwables;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.testng.annotations.Test;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
@@ -43,7 +42,7 @@ public class TimeoutTest extends CassandraIntegrationTest {
private final Log log = LogFactory.getLog(TimeoutTest.class);
- @Test
+// @Test
public void generateTimeout() throws Exception {
MetricsConfiguration configuration = new MetricsConfiguration();
@@ -54,7 +53,7 @@ public class TimeoutTest extends CassandraIntegrationTest {
dateTimeService.setConfiguration(configuration);
metricsServer.setDateTimeService(dateTimeService);
- MetricsDAO dao = new MetricsDAO(session, configuration);
+ MetricsDAO dao = new MetricsDAO(new StorageSession(session), configuration);
metricsServer.setDAO(dao);
long time = hour0().getMillis();
diff --git a/modules/enterprise/server/server-metrics/src/test/resources/log4j.properties b/modules/enterprise/server/server-metrics/src/test/resources/log4j.properties
index 67db049..59bd5db 100644
--- a/modules/enterprise/server/server-metrics/src/test/resources/log4j.properties
+++ b/modules/enterprise/server/server-metrics/src/test/resources/log4j.properties
@@ -39,4 +39,4 @@ log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %-5p %c %m%n
log4j.logger.org.rhq=DEBUG
-log4j.logger.com.datastax=DEBUG
+log4j.logger.com.datastax=WARN
diff --git a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
index 7ff8f6c..40e00bf 100644
--- a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
+++ b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
@@ -60,6 +60,7 @@ import org.rhq.metrics.simulator.stats.Stats;
import org.rhq.server.metrics.DateTimeService;
import org.rhq.server.metrics.MetricsDAO;
import org.rhq.server.metrics.MetricsServer;
+import org.rhq.server.metrics.StorageSession;
/**
* @author John Sanda
@@ -96,7 +97,9 @@ public class Simulator implements ShutdownManager {
session = createSession(nodes, compression);
}
- MetricsDAO metricsDAO = new MetricsDAO(session, plan.getMetricsServerConfiguration());
+ StorageSession storageSession = new StorageSession(session);
+
+ MetricsDAO metricsDAO = new MetricsDAO(storageSession, plan.getMetricsServerConfiguration());
MetricsServer metricsServer = new MetricsServer();
metricsServer.setDAO(metricsDAO);
metricsServer.setConfiguration(plan.getMetricsServerConfiguration());
commit 638017e6ee0a4f752bafb6352f6d63cc17d5f5ad
Author: John Sanda <jsanda(a)redhat.com>
Date: Thu Jul 25 22:06:31 2013 -0400
upgrade to version 1.0.2 of datastax driver
diff --git a/pom.xml b/pom.xml
index f909033..3f2adbd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -178,7 +178,7 @@
<!-- cassandra dependency versions -->
<cassandra.version>1.2.4</cassandra.version>
<cassandra.thrift.version>0.7.0</cassandra.thrift.version>
- <cassandra.driver.version>1.0.0-rhq-1.2.4</cassandra.driver.version>
+ <cassandra.driver.version>1.0.2-rhq-1.2.4</cassandra.driver.version>
<cassandra.driver.netty.version>3.6.3.Final</cassandra.driver.netty.version>
<cassandra.snappy.version>1.0.4.1-rhq-p1</cassandra.snappy.version>
<cassandra.snakeyaml.version>1.6</cassandra.snakeyaml.version>
commit 5112e60ff848296ecce96d5ae7e363dbea6c0e12
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Thu Jul 25 17:52:00 2013 -0500
Operations are now created in a new transaction. Also, change the way the configuration is updated.
The steps for a full configuration update:
1) Invoke config update operation on the plugin
2) Update the JMX port on the storage node entity
3) Restart the storage node server
4) Update the connection settings for the storage node if JMX port changed
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index 7a7eda4..7d861f1 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -35,6 +35,8 @@ import java.util.Queue;
import javax.ejb.EJB;
import javax.ejb.Stateless;
+import javax.ejb.TransactionAttribute;
+import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
@@ -134,6 +136,9 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
@EJB
private ConfigurationManagerLocal configurationManager;
+ @EJB
+ private StorageNodeManagerLocal storageNodeManger;
+
@Override
public void linkResource(Resource resource) {
List<StorageNode> storageNodes = this.getStorageNodes();
@@ -658,31 +663,49 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
parameters);
if (result) {
+ //2. Update the JMX port
+ //this is a fast operation compared to the restart
+ storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
+ entityManager.merge(storageNode);
+
+ //3. Restart the storage node
+ result = runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION,
+ new Configuration());
+
+ //4. Update the plugin configuration to talk with the new server
+ //Up to this point communication with the storage node should not have been affected by the intermediate
+ //changes
Configuration storageNodePluginConfig = configurationManager.getPluginConfiguration(subject,
storageNodeResource.getId());
String existingJMXPort = storageNodePluginConfig.getSimpleValue("jmxPort");
- storageNodePluginConfig.setSimpleValue("jmxPort", storageNodeConfiguration.getJmxPort() + "");
+ String newJMXPort = storageNodeConfiguration.getJmxPort() + "";
- String existingConnectionURL = storageNodePluginConfig.getSimpleValue("connectorAddress");
- String newConnectionURL = existingConnectionURL.replace(":" + existingJMXPort + "/", ":"
- + storageNodeConfiguration.getJmxPort() + "/");
- storageNodePluginConfig.setSimpleValue("connectorAddress", newConnectionURL);
+ if (!existingJMXPort.equals(newJMXPort)) {
+ storageNodePluginConfig.setSimpleValue("jmxPort", newJMXPort);
- configurationManager.updatePluginConfiguration(subject, storageNodeResource.getId(),
- storageNodePluginConfig);
+ String existingConnectionURL = storageNodePluginConfig.getSimpleValue("connectorAddress");
+ String newConnectionURL = existingConnectionURL.replace(":" + existingJMXPort + "/", ":"
+ + storageNodeConfiguration.getJmxPort() + "/");
+ storageNodePluginConfig.setSimpleValue("connectorAddress", newConnectionURL);
- storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
- entityManager.merge(storageNode);
- entityManager.flush();
+ configurationManager.updatePluginConfiguration(subject, storageNodeResource.getId(),
+ storageNodePluginConfig);
+ }
- return runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION, null);
+ return result;
}
}
return false;
}
+ @Override
+ @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
+ public void scheduleOperationInNewTransaction(Subject subject, ResourceOperationSchedule schedule) {
+ operationManager.scheduleResourceOperation(subject, schedule);
+ }
+
private boolean runOperationAndWaitForResult(Subject subject, Resource storageNodeResource, String operationToRun,
Configuration parameters) {
@@ -696,8 +719,7 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
newSchedule.setDescription("Run by StorageNodeManagerBean");
newSchedule.setParameters(parameters);
- operationManager.scheduleResourceOperation(subject, newSchedule);
- entityManager.flush();
+ storageNodeManger.scheduleOperationInNewTransaction(subject, newSchedule);
//waiting for the operation result then return it
int iteration = 0;
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
index 69b16c4..15fa85c 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerLocal.java
@@ -28,6 +28,7 @@ import org.rhq.core.domain.cloud.StorageNode;
import org.rhq.core.domain.cloud.StorageNodeConfigurationComposite;
import org.rhq.core.domain.cloud.StorageNodeLoadComposite;
import org.rhq.core.domain.criteria.StorageNodeCriteria;
+import org.rhq.core.domain.operation.bean.ResourceOperationSchedule;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.util.PageList;
@@ -178,4 +179,6 @@ public interface StorageNodeManagerLocal {
*/
ResourceGroup getStorageNodeGroup();
+ void scheduleOperationInNewTransaction(Subject subject, ResourceOperationSchedule schedule);
+
}
diff --git a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
index a42040d..f5a4f6d 100644
--- a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
@@ -132,10 +132,10 @@
<operation name="updateConfiguration" description="Updates the node configuration. Will require a separate server restart for the settings to take effect.">
<parameters>
- <c:simple-property name="jmxPort" type="integer" description="JMX port JVM option."/>
- <c:simple-property name="heapSize" type="string" description="The heap size to be used for both -Xms and -Xmx JVM options."/>
- <c:simple-property name="heapNewSize" type="string" description="The heap new size to be used be used with -Xmn JVM option."/>
- <c:simple-property name="threadStackSize" type="integer" description="The thread stack size. This memory is allocated to each thread off heap. The
+ <c:simple-property name="jmxPort" type="integer" required="false" description="JMX port JVM option."/>
+ <c:simple-property name="heapSize" type="string" required="false" description="The heap size to be used for both -Xms and -Xmx JVM options."/>
+ <c:simple-property name="heapNewSize" type="string" required="false" description="The heap new size to be used be used with -Xmn JVM option."/>
+ <c:simple-property name="threadStackSize" type="integer" required="false" description="The thread stack size. This memory is allocated to each thread off heap. The
value should be an integer that will be interpreted in kilobytes."/>
<c:simple-property name="restartIfRequired" type="boolean" required="true" default="false" description="If [true] then restart the server at the end of the update if and if only the updates made require a restart. If [false] the server will not be restarted regardless of the updates made."/>
</parameters>
commit af85b8b1c687e025ea95898dda6a5c46f127d151
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Thu Jul 25 14:16:38 2013 -0700
[BZ 988574] - Consolidated Metrics Screen - UXD Redesign
diff --git a/modules/enterprise/gui/coregui/pom.xml b/modules/enterprise/gui/coregui/pom.xml
index 969ab8e..353f4d9 100644
--- a/modules/enterprise/gui/coregui/pom.xml
+++ b/modules/enterprise/gui/coregui/pom.xml
@@ -19,7 +19,7 @@
<properties>
<!-- dependency versions -->
- <gwt.version>2.5.0</gwt.version>
+ <gwt.version>${gwt.version}</gwt.version>
<smartgwt.version>3.0</smartgwt.version>
<!-- If this is too much memory to allocate to your gwt:debug process then override this property in
@@ -136,16 +136,7 @@
</dependency>
- <!-- the GWT graphing library (note, this provides jquery 1.3.2. If we get rid of GFlot we will need
- to provide jquery explcitly for jquery.sparkline support. See CoreGUI.gwt.xml for the jquery.sparkline
- declaration and coregui/webapp/js for the lib inclusion.) -->
- <!-- NOTE: soon to be deprecated by d3.js -->
- <dependency>
- <groupId>com.googlecode.gflot</groupId>
- <artifactId>gflot</artifactId>
- <version>2.4.3</version>
- <scope>provided</scope>
- </dependency>
+
<!-- for file uploads -->
<dependency>
@@ -245,7 +236,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>gwt-maven-plugin</artifactId>
- <version>2.5.0</version>
+ <version>${gwt.version}</version>
<configuration>
<noServer>true</noServer>
<inplace>false</inplace>
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/IconEnum.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/IconEnum.java
index d11467a..b48a712 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/IconEnum.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/IconEnum.java
@@ -114,6 +114,12 @@ public enum IconEnum {
STORAGE_NODE("global/StorageNode_16.png", "global/StorageNode_24.png"),
/////////////////////////////
+ // General
+ /////////////////////////////
+ EXPANDED_ICON("[SKIN]/ListGrid/row_expanded.png"),
+ COLLAPSED_ICON("[SKIN]/ListGrid/row_collapsed.png"),
+
+ /////////////////////////////
// Resource Specific Tabs
/////////////////////////////
CALLTIME("global/Recent_16.png", "global/Recent_24.png");
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/table/Table.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/table/Table.java
index 4cfe199..4ce05d2 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/table/Table.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/table/Table.java
@@ -1202,16 +1202,15 @@ public class Table<DS extends RPCDataSource> extends EnhancedHLayout implements
}
if (hiddenItem != null) {
- Log.debug("Found hidden items");
// Add the hidden item if it exists
FormItem[] tmpItems = new FormItem[items.length + 1];
System.arraycopy(items, 0, tmpItems, 0, items.length);
tmpItems[items.length] = hiddenItem;
items = tmpItems;
}
- for (FormItem item : items) {
- Log.debug(" ******** Form Items sent: " + item.getName() + ": " + item.getValue());
- }
+// for (FormItem item : items) {
+// Log.debug(" ******** Form Items sent: " + item.getName() + ": " + item.getValue());
+// }
super.setItems(items);
}
@@ -1223,19 +1222,16 @@ public class Table<DS extends RPCDataSource> extends EnhancedHLayout implements
@Override
public void onKeyPress(KeyPressEvent event) {
if (event.getKeyName().equals("Enter")) {
- Log.debug("Table.TableFilter Pressed Enter key");
if (null != searchBarItem) {
if (searchBarItem.getSearchBar().isFilterEnabled()) {
TextItem searchTextItem = searchBarItem.getSearchBar().getSearchTextItem();
String searchBarValue = searchTextItem.getValueAsString();
String hiddenValue = (String) hiddenItem.getValue();
- Log.debug("Table.TableFilter searchBarValue :" + searchBarValue + ", hiddenValue" + hiddenValue);
// Only send a fetch request if the user actually changed the search expression.
if (!equals(searchBarValue, hiddenValue)) {
hiddenItem.setValue(searchBarValue);
- Log.debug("Table.TableFilter fetchFilteredTableData");
fetchFilteredTableData();
}
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/AbstractMetricGraph.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/AbstractMetricGraph.java
index e75b65a..ace6154 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/AbstractMetricGraph.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/AbstractMetricGraph.java
@@ -202,6 +202,10 @@ public abstract class AbstractMetricGraph extends VLayout implements HasD3Metric
return metricGraphData.getPortalId();
}
+ public boolean isHideLegend(){
+ return metricGraphData.isHideLegend();
+ }
+
public void setGraphListView(AbstractD3GraphListView graphListView) {
this.graphListView = graphListView;
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/ButtonBarDateTimeRangeEditor.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/ButtonBarDateTimeRangeEditor.java
index 2075783..b4f1354 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/ButtonBarDateTimeRangeEditor.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/ButtonBarDateTimeRangeEditor.java
@@ -77,7 +77,7 @@ public class ButtonBarDateTimeRangeEditor extends EnhancedVLayout {
prefs = measurementUserPreferences.getMetricRangePreferences();
Log.debug("ButtonBarDateTimeRangeEditor initialized with start Date: " + new Date(prefs.begin) + " end Date: "
+ new Date(prefs.end));
- createButtons();
+ //createButtons();
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/MetricGraphData.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/MetricGraphData.java
index ecd3b46..5ae392f 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/MetricGraphData.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/MetricGraphData.java
@@ -73,6 +73,7 @@ public class MetricGraphData implements JsonMetricProducer {
private MeasurementOOBComposite lastOOB;
private Integer chartHeight;
private boolean isPortalGraph;
+ private boolean hideLegend;
private MetricGraphData(int portalId) {
@@ -249,6 +250,14 @@ public class MetricGraphData implements JsonMetricProducer {
return isPortalGraph;
}
+ public boolean isHideLegend() {
+ return hideLegend;
+ }
+
+ public void setHideLegend(boolean hideLegend) {
+ this.hideLegend = hideLegend;
+ }
+
public String getChartTitle() {
if(definition != null){
@@ -380,8 +389,8 @@ public class MetricGraphData implements JsonMetricProducer {
* @see StackedBarMetricGraphImpl
*/
public boolean showBarAvgTrendLine() {
+ int numberOfAggBars = 0;
for (MeasurementDataNumericHighLowComposite measurement : metricData) {
- int numberOfAggBars = 0;
boolean noValuesInCurrentBarUndefined = (!Double.isNaN(measurement.getValue()) && !Double.isNaN(measurement.getHighValue()) && !Double.isNaN(measurement.getLowValue()));
boolean foundAggregateBar = (measurement.getValue() != measurement.getHighValue() || measurement.getHighValue() != measurement.getLowValue());
// if there exists a even one aggregate bar then I can short circuit this and exit
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityLineGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityLineGraphType.java
deleted file mode 100644
index 34ca60b..0000000
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityLineGraphType.java
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype;
-
-import java.util.Date;
-import java.util.List;
-
-import org.rhq.core.domain.measurement.Availability;
-import org.rhq.core.domain.measurement.MeasurementUnits;
-import org.rhq.core.domain.resource.group.composite.ResourceGroupAvailability;
-import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
-import org.rhq.enterprise.gui.coregui.client.CoreGUI;
-import org.rhq.enterprise.gui.coregui.client.Messages;
-import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AvailabilityGraphType;
-import org.rhq.enterprise.gui.coregui.client.util.Log;
-import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
-
-/**
- * This is now old and for demonstration purposes only.
- * Contains the javascript chart definition for an implementation of the d3 availability chart. This implementation is
- * just a line that changes color based on availability type: up=green, down=red, orange=disabled, unknown=grey,
- * empty=grey, warn=yellow. This version of the availability graph shows continuous intervals.
- * @deprecated
- * @see AvailabilityOverUnderGraphType
- *
- * @author Mike Thompson
- */
-public class AvailabilityLineGraphType implements AvailabilityGraphType {
-
- private static Messages MSG = CoreGUI.getMessages();
- private List<Availability> availabilityList;
- private List<ResourceGroupAvailability> groupAvailabilityList;
- private Integer entityId;
-
- /**
- * General constructor for stacked bar graph when you have all the data needed to produce the graph. (This is true
- * for all cases but the dashboard portlet).
- */
- public AvailabilityLineGraphType(Integer entityId) {
- this.entityId = entityId;
- }
-
- public void setAvailabilityList(List<Availability> availabilityList) {
- this.availabilityList = availabilityList;
- }
-
- public void setGroupAvailabilityList(List<ResourceGroupAvailability> groupAvailabilityList) {
- this.groupAvailabilityList = groupAvailabilityList;
- }
-
- public String getAvailabilityJson() {
- StringBuilder sb = new StringBuilder("[");
- if (null != availabilityList) {
- // loop through the avail intervals
- for (Availability availability : availabilityList) {
- sb.append("{ \"availType\":\"" + availability.getAvailabilityType() + "\", ");
- sb.append(" \"availTypeMessage\":\"" + availability.getAvailabilityType()+ "\", ");
- sb.append(" \"availStart\":" + availability.getStartTime() + ", ");
- // last record will be null
- long endTime = availability.getEndTime() != null ? availability.getEndTime() : (new Date()).getTime();
- sb.append(" \"availEnd\":" + endTime + ", ");
-
- long availDuration = endTime - availability.getStartTime();
- String availDurationString = MeasurementConverterClient.format((double) availDuration,
- MeasurementUnits.MILLISECONDS, true);
- sb.append(" \"availDuration\": \"" + availDurationString + "\" },");
-
- }
- sb.setLength(sb.length() - 1);
-
- } else if (null != groupAvailabilityList) {
- // loop through the group avail down intervals
- for (ResourceGroupAvailability groupAvailability : groupAvailabilityList) {
- // allows substitution for situations like WARN=MIXED for easier terminology
- String availabilityTypeMessage = (groupAvailability.getGroupAvailabilityType().equals(ResourceGroupComposite.GroupAvailabilityType.WARN))
- ? MSG.chart_hover_availability_type_warn() : groupAvailability.getGroupAvailabilityType().name();
-
- sb.append("{ \"availType\":\"" + groupAvailability.getGroupAvailabilityType() + "\", ");
- sb.append(" \"availTypeMessage\":\"" + availabilityTypeMessage + "\", ");
- sb.append(" \"availStart\":" + groupAvailability.getStartTime() + ", ");
- // last record will be null
- long endTime = groupAvailability.getEndTime() != null ? groupAvailability.getEndTime() : (new Date())
- .getTime();
- sb.append(" \"availEnd\":" + endTime + ", ");
-
- long availDuration = endTime - groupAvailability.getStartTime();
- String availDurationString = MeasurementConverterClient.format((double) availDuration,
- MeasurementUnits.MILLISECONDS, true);
- sb.append(" \"availDuration\": \"" + availDurationString + "\" },");
-
- }
- sb.setLength(sb.length() - 1);
- }
-
- sb.append("]");
- Log.debug(sb.toString());
- return sb.toString();
- }
-
- /**
- * The magic JSNI to draw the charts with d3.
- */
- public native void drawJsniChart() /*-{
- console.log("Draw Availability chart");
-
- var global = this,
- // tidy up all of our interactions with java (via JSNI) thru AvailChartContext class
- // NOTE: rhq.js has the javascript object constructors in it.
- availChartContext = new $wnd.AvailChartContext(global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartId()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getAvailabilityJson()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartDateLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartTimeLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverStartLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverEndLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverBarLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverAvailabilityLabel()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverTimeFormat()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityLineGraphType::getChartHoverDateFormat()()
- );
-
-
- var availabilityGraph = function () {
- "use strict";
- // privates
-
- var margin = {top: 5, right: 5, bottom: 5, left: 40},
- barOffset = 10,
- width = 750 - margin.left - margin.right + barOffset,
- height = 20 - margin.top - margin.bottom,
- pixelsOffHeight = 0,
- svg;
-
-
- function drawBars(availChartContext) {
- var xAxisMin = $wnd.d3.min(availChartContext.data, function (d) {
- return +d.availStart;
- }),
- xAxisMax = $wnd.d3.max(availChartContext.data, function (d) {
- return +d.availEnd;
- }),
-
- timeScale = $wnd.d3.time.scale()
- .range([0, width])
- .domain([xAxisMin, xAxisMax]),
-
- yScale = $wnd.d3.scale.linear()
- .clamp(true)
- .rangeRound([height, 0])
- .domain([0, 4]),
-
- svg = $wnd.d3.select(availChartContext.chartSelection).append("g")
- .attr("width", width + margin.left + margin.right)
- .attr("height", height + margin.top + margin.bottom)
- .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
-
-
- // The gray bars at the bottom leading up
- svg.selectAll("rect.availBars")
- .data(availChartContext.data)
- .enter().append("rect")
- .attr("class", "availBars")
- .attr("x", function (d) {
- return timeScale(+d.availStart);
- })
- .attr("y", function (d) {
- return yScale(0);
- })
- .attr("height", function (d) {
- return height - yScale(4) - pixelsOffHeight;
- })
- .attr("width", function (d) {
- return timeScale(+d.availEnd) - timeScale(+d.availStart);
- })
-
- .attr("opacity", ".9")
- .attr("fill", function (d) {
- if (d.availType === 'DOWN') {
- return "#FF1919"; // red
- }
- else if (d.availType === 'DISABLED') {
- return "#FF9933"; // orange
- }
- else if (d.availType === 'UNKNOWN') {
- return "#CCC"; // gray
- }
- else if (d.availType === 'UP') {
- return "#198C19"; // green
- }
- else if (d.availType === 'WARN') {
- return "#FFFF00"; // yellow
- }
- else if (d.availType === 'EMPTY') {
- return "#CCC"; // gray
- }
- else {
- // should not ever happen, but...
- console.warn("AvailabilityType not valid.");
- return "#000"; //black
- }
- });
- }
-
- function createHovers() {
- $wnd.jQuery('svg rect.availBars').tipsy({
- gravity: 'n',
- html: true,
- trigger: 'hover',
- title: function () {
- var d = this.__data__;
- return formatHovers(d);
- },
- show: function (e, el) {
- el.css({ 'z-index': '990000'})
- }
- });
- }
-
- function formatHovers(d) {
- var hoverString,
- timeFormatter = $wnd.d3.time.format(availChartContext.chartHoverTimeFormat),
- dateFormatter = $wnd.d3.time.format(availChartContext.chartHoverDateFormat),
- availStart = new Date(+d.availStart),
- availEnd = new Date(+d.availEnd);
-
- hoverString =
- '<div class="chartHoverEnclosingDiv">' +
- '<div class="chartHoverAlignRight"><span >' + availChartContext.hoverBarAvailabilityLabel + ': </span><span style="width:50px;">' + d.availTypeMessage + '</span></div>' +
- '<div class="chartHoverAlignRight"><span >' + availChartContext.hoverStartLabel + ': </span><span style="width:50px;">' + timeFormatter(availStart) + '</span></div>' +
- '<div class="chartHoverAlignRight"><span >' + ' </span><span style="width:50px;">' + dateFormatter(availStart) + '</span></div>' +
- '<div class="chartHoverAlignRight"><span >' + availChartContext.hoverEndLabel + ': </span><span style="width:50px;">' + timeFormatter(availEnd) + '</span></div>' +
- '<div class="chartHoverAlignRight"><span >' + ' </span><span style="width:50px;">' + dateFormatter(availEnd) + '</span></div>' +
- '<div class="chartHoverAlignRight"><span >' + availChartContext.hoverBarLabel + ': </span><span style="width:50px;">' + d.availDuration + '</span></div>' +
- '</div>';
- return hoverString;
-
- }
-
- return {
- // Public API
- draw: function (chartContext) {
- "use strict";
- console.info("AvailabilityChart");
- //console.time("availabilityChart");
-
- drawBars(availChartContext);
- createHovers();
- //console.timeEnd("availabilityChart");
- }
- }; // end public closure
-
-
- }();
-
- if (availChartContext.data !== undefined && availChartContext.data.length > 0) {
- availabilityGraph.draw(availChartContext);
- }
-
- }-*/;
-
- public String getChartId() {
- return String.valueOf(entityId);
- }
-
- public String getChartTimeLabel() {
- return MSG.chart_time_label();
- }
-
- public String getChartDateLabel() {
- return MSG.chart_date_label();
- }
-
- public String getChartHoverAvailabilityLabel() {
- return MSG.chart_hover_availability_label();
- }
-
- public String getChartHoverStartLabel() {
- return MSG.chart_hover_start_label();
- }
-
- public String getChartHoverEndLabel() {
- return MSG.chart_hover_end_label();
- }
-
- public String getChartHoverBarLabel() {
- return MSG.chart_hover_bar_label();
- }
-
- public String getChartHoverTimeFormat() {
- return MSG.chart_hover_time_format();
- }
-
- public String getChartHoverDateFormat() {
- return MSG.chart_hover_date_format();
- }
-}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
index 7c5f80a..811a579 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
@@ -311,13 +311,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
});
}
- function timeFormat(formats) {
- return function(date) {
- var i = formats.length - 1, f = formats[i];
- while (!f[1](date)) f = formats[--i];
- return f[0](date);
- }
- }
function formatHovers(d) {
var timeFormatter = $wnd.d3.time.format(availChartContext.chartHoverTimeFormat),
@@ -345,11 +338,13 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
}();
- if (availChartContext.data !== undefined && availChartContext.data.length > 0) {
+ console.log("Avail Data records: "+availChartContext.data.length);
+ if (typeof availChartContext.data !== 'undefined' && availChartContext.data.length > 0) {
availabilityGraph.draw(availChartContext);
+ console.log("Availability Chart Drawn");
}
- }-*/;
+ }-*/;
public String getChartId() {
return String.valueOf(entityId);
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/LineMetricGraph.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/LineMetricGraph.java
deleted file mode 100644
index 079b2ab..0000000
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/LineMetricGraph.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-package org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype;
-
-import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph;
-import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData;
-
-/**
- * Contains the javascript chart definition for a d3 Line graph chart.
- * NOTE: this class isn't used just provided as an example as how to create
- * other graph types.
- *
- * @author Mike Thompson
- */
-public final class LineMetricGraph extends AbstractMetricGraph {
-
- /**
- * General constructor for stacked bar graph when you have all the data needed to
- * produce the graph. (This is true for all cases but the dashboard portlet).
- */
- public LineMetricGraph(MetricGraphData metricGraphData) {
- setMetricGraphData(metricGraphData);
- }
-
- /**
- * The magic JSNI to draw the charts with d3.
- */
- public native void drawJsniChart() /*-{
- console.log("Draw Metric Line jsni chart");
- var global = this,
- chartId = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getChartId()(),
- chartHandle = "#rChart-"+chartId,
- chartSelection = chartHandle + " svg",
- json = $wnd.jQuery.parseJSON(global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getJsonMetrics()()),
- yAxisLabel = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getChartTitle()(),
- yAxisUnits = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getYAxisUnits()(),
- xAxisLabel = global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getXAxisTitle()();
-
- console.log("chart id: "+chartSelection );
- console.log(global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData::getJsonMetrics()());
-
-
- function draw(data){
- "use strict";
-
- var margin = {top: 10, right: 5, bottom: 30, left: 70},
- width = 400 - margin.left - margin.right,
- height = 150 - margin.top - margin.bottom;
-
- var timeScale = $wnd.d3.time.scale()
- .range([0, width])
- .domain($wnd.d3.extent(data, function(d) { return d.x; }));
-
- var yScale = $wnd.d3.scale.linear()
- .rangeRound([height, 0])
- .domain([$wnd.d3.min(data.map(function(x) {return x.low;})), $wnd.d3.max(data.map(function(x){return x.high;}))]);
-
- var xAxis = $wnd.d3.svg.axis()
- .scale(timeScale)
- .ticks(5)
- .orient("bottom");
-
- var yAxis = $wnd.d3.svg.axis()
- .scale(yScale)
- .ticks(5)
- .orient("left");
-
- var interpolation = "basis";
-
- var line = $wnd.d3.svg.line()
- .interpolate(interpolation)
- .x(function(d) { return timeScale(d.x); })
- .y(function(d) { return yScale(+d.y); });
-
- var highLine = $wnd.d3.svg.line()
- .interpolate(interpolation)
- .x(function(d) { return timeScale(d.x); })
- .y(function(d) { return yScale(+d.high); });
-
- var lowLine = $wnd.d3.svg.line()
- .interpolate(interpolation)
- .x(function(d) { return timeScale(d.x); })
- .y(function(d) { return yScale(+d.low); });
-
- var svg = $wnd.d3.select(chartSelection).append("g")
- .attr("width", width + margin.left + margin.right)
- .attr("height", height + margin.top + margin.bottom)
- .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
-
- svg.append("g")
- .attr("class", "x axis")
- .attr("transform", "translate(0," + height + ")")
- .call(xAxis);
-
-
- svg.append("g")
- .attr("class", "y axis")
- .call(yAxis)
- .append("text")
- .attr("transform", "rotate(-90)")
- .attr("y", -60)
- .attr("dy", ".71em")
- .style("text-anchor", "end")
- .text(yAxisUnits === "NONE" ? "" : yAxisUnits);
-
- console.log("finished axes");
-
- svg.append("path")
- .datum(data)
- .attr("class", "line")
- .attr("fill", "none")
- .attr("stroke", "steelblue")
- .attr("stroke-width", "2")
- .attr("d", line);
-
- svg.append("path")
- .datum(data)
- .attr("class", "highLine")
- .attr("fill", "none")
- .attr("stroke", "red")
- .attr("stroke-width", "1.5")
- //.attr("stroke-dasharray", "20,10,5,5,5,10")
- .attr("stroke-dasharray", "5,5")
- .attr("stroke-opacity", ".3")
- .attr("d", highLine);
-
- svg.append("path")
- .datum(data)
- .attr("class", "lowLine")
- .attr("fill", "none")
- .attr("stroke", "blue")
- .attr("stroke-width", "1.5")
- .attr("stroke-dasharray", "5,5")
- .attr("stroke-opacity", ".3")
- .attr("d", lowLine);
-
- console.log("finished paths");
- }(data);
-
- }-*/;
-
-}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
index 9058ea5..67552d3 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
@@ -73,7 +73,8 @@ public class StackedBarMetricGraphImpl extends AbstractMetricGraph {
global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::getButtonBarDateTimeFormat()(),
global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::getChartSingleValueLabel()(),
global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::getXAxisTimeFormatHours()(),
- global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::getXAxisTimeFormatHoursMinutes()()
+ global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::getXAxisTimeFormatHoursMinutes()(),
+ global.@org.rhq.enterprise.gui.coregui.client.inventory.common.graph.AbstractMetricGraph::isHideLegend()()
);
@@ -217,8 +218,8 @@ public class StackedBarMetricGraphImpl extends AbstractMetricGraph {
.attr("transform", "translate(" + margin.left + "," + (+titleHeight + titleSpace + margin.top) + ")");
legendUnDefined = (typeof min === 'undefined') || (typeof avg === 'undefined') || (typeof peak === 'undefined');
- if (!useSmallCharts() && !legendUnDefined) {
- createMinAvgPeakSidePanel(chartContext.minChartTitle, min, chartContext.avgChartTitle, avg, chartContext.peakChartTitle, peak, chartContext.yAxisUnits);
+ if (!(chartContext.hideLegend && !useSmallCharts())) {
+ createMinAvgPeakSidePanel(chartContext.minChartTitle, min, chartContext.avgChartTitle, avg, chartContext.peakChartTitle, peak, chartContext.yAxisUnits);
}
}
@@ -693,7 +694,7 @@ public class StackedBarMetricGraphImpl extends AbstractMetricGraph {
}; // end public closure
}();
- if(typeof chartContext.data !== 'undefined' && chartContext.data.length > 0){
+ if(typeof chartContext.data !== 'undefined' && chartContext.data !== null && chartContext.data.length > 0){
metricStackedBarGraph.draw(chartContext);
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/D3GroupGraphListView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
index 59737ac..a9789c6 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
@@ -21,7 +21,6 @@ package org.rhq.enterprise.gui.coregui.client.inventory.groups.detail;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.EnumSet;
import java.util.List;
import com.google.gwt.core.client.GWT;
@@ -35,19 +34,17 @@ import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.DisplayType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
-import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupAvailability;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
import org.rhq.enterprise.gui.coregui.client.inventory.AutoRefresh;
import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractD3GraphListView;
-import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.MetricD3Graph;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
-import org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
@@ -55,7 +52,7 @@ import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
* Build the Group version of the View that shows the individual graph views.
* @author Mike Thompson
*/
-public final class D3GroupGraphListView extends AbstractD3GraphListView implements AutoRefresh{
+public final class D3GroupGraphListView extends AbstractD3GraphListView implements AutoRefresh {
private ResourceGroup resourceGroup;
private VLayout graphsVLayout;
@@ -75,7 +72,8 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
addMember(buttonBarDateTimeRangeEditor);
if (showAvailabilityGraph) {
- availabilityGraph = new AvailabilityD3GraphView<AvailabilityOverUnderGraphType>(new AvailabilityOverUnderGraphType(resourceGroup.getId()));
+ availabilityGraph = new AvailabilityD3GraphView<AvailabilityOverUnderGraphType>(
+ new AvailabilityOverUnderGraphType(resourceGroup.getId()));
addMember(availabilityGraph);
}
graphsVLayout = new VLayout();
@@ -89,7 +87,6 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
addMember(graphsVLayout);
}
-
public void redrawGraphs() {
this.onDraw();
}
@@ -100,68 +97,60 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
private void buildGraphs() {
queryAvailability(EntityContext.forGroup(resourceGroup), buttonBarDateTimeRangeEditor.getStartTime(),
- buttonBarDateTimeRangeEditor.getEndTime(), null);
+ buttonBarDateTimeRangeEditor.getEndTime(), null);
- ResourceTypeRepository.Cache.getInstance().getResourceTypes(resourceGroup.getResourceType().getId(),
- EnumSet.of(ResourceTypeRepository.MetadataType.measurements),
- new ResourceTypeRepository.TypeLoadedCallback() {
- public void onTypesLoaded(final ResourceType type) {
+ final ArrayList<MeasurementDefinition> measurementDefinitions = new ArrayList<MeasurementDefinition>();
- final ArrayList<MeasurementDefinition> measurementDefinitions = new ArrayList<MeasurementDefinition>();
+ for (MeasurementDefinition def : resourceGroup.getResourceType().getMetricDefinitions()) {
+ if (def.getDataType() == DataType.MEASUREMENT && def.getDisplayType() == DisplayType.SUMMARY) {
+ measurementDefinitions.add(def);
+ }
+ }
- for (MeasurementDefinition def : type.getMetricDefinitions()) {
- if (def.getDataType() == DataType.MEASUREMENT && def.getDisplayType() == DisplayType.SUMMARY) {
- measurementDefinitions.add(def);
- }
- }
+ Collections.sort(measurementDefinitions, new Comparator<MeasurementDefinition>() {
+ public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
+ return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
+ }
+ });
- Collections.sort(measurementDefinitions, new Comparator<MeasurementDefinition>() {
- public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
- return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
- }
- });
+ int[] measDefIdArray = new int[measurementDefinitions.size()];
+ for (int i = 0; i < measDefIdArray.length; i++) {
+ measDefIdArray[i] = measurementDefinitions.get(i).getId();
+ }
- int[] measDefIdArray = new int[measurementDefinitions.size()];
- for (int i = 0; i < measDefIdArray.length; i++) {
- measDefIdArray[i] = measurementDefinitions.get(i).getId();
- }
+ GWTServiceLookup.getMeasurementDataService().findDataForCompatibleGroup(resourceGroup.getId(), measDefIdArray,
+ buttonBarDateTimeRangeEditor.getStartTime(), buttonBarDateTimeRangeEditor.getEndTime(), 60,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_loadFailed(), caught);
+ loadingLabel.setContents(MSG.view_resource_monitor_graphs_loadFailed());
+ }
- GWTServiceLookup.getMeasurementDataService().findDataForCompatibleGroup(resourceGroup.getId(),
- measDefIdArray, buttonBarDateTimeRangeEditor.getStartTime(), buttonBarDateTimeRangeEditor.getEndTime(), 60,
- new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
- @Override
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_loadFailed(),
- caught);
- loadingLabel.setContents(MSG.view_resource_monitor_graphs_loadFailed());
- }
-
- @Override
- public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> result) {
- if (result.isEmpty()) {
- loadingLabel.setContents(MSG.view_resource_monitor_graphs_noneAvailable());
- } else {
- loadingLabel.hide();
- int i = 0;
- for (List<MeasurementDataNumericHighLowComposite> data : result) {
- buildIndividualGraph(measurementDefinitions.get(i++), data);
- }
- // There is a weird timing case when availabilityGraph can be null
- if (availabilityGraph != null) {
- availabilityGraph.setGroupAvailabilityList(groupAvailabilityList);
- new Timer(){
- @Override
- public void run() {
- availabilityGraph.drawJsniChart();
- }
- }.schedule(150);
- }
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> result) {
+ if (result.isEmpty()) {
+ loadingLabel.setContents(MSG.view_resource_monitor_graphs_noneAvailable());
+ } else {
+ loadingLabel.hide();
+ int i = 0;
+ for (List<MeasurementDataNumericHighLowComposite> data : result) {
+ buildIndividualGraph(measurementDefinitions.get(i++), data);
+ }
+ // There is a weird timing case when availabilityGraph can be null
+ if (availabilityGraph != null) {
+ availabilityGraph.setGroupAvailabilityList(groupAvailabilityList);
+ new Timer() {
+ @Override
+ public void run() {
+ availabilityGraph.drawJsniChart();
}
- }
- });
-
+ }.schedule(150);
+ }
+ }
}
});
+
}
protected void queryAvailability(final EntityContext groupContext, Long startTime, Long endTime,
@@ -197,7 +186,7 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
List<MeasurementDataNumericHighLowComposite> data) {
MetricGraphData metricGraphData = MetricGraphData.createForResourceGroup(resourceGroup.getId(),
- resourceGroup.getName(), measurementDefinition, data );
+ resourceGroup.getName(), measurementDefinition, data);
StackedBarMetricGraphImpl graph = GWT.create(StackedBarMetricGraphImpl.class);
graph.setMetricGraphData(metricGraphData);
@@ -207,11 +196,9 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
graphView.setWidth("95%");
graphView.setHeight(MULTI_CHART_HEIGHT);
- if(graphsVLayout != null){
+ if (graphsVLayout != null) {
graphsVLayout.addMember(graphView);
}
}
-
-
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/table/CompositeGroupMultiLineGraphListView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/table/CompositeGroupMultiLineGraphListView.java
new file mode 100644
index 0000000..101df60
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/monitoring/table/CompositeGroupMultiLineGraphListView.java
@@ -0,0 +1,79 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2012 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table;
+
+
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.Messages;
+
+/**
+ * A MultiLine version of the Composite group single metric multiple resource charts.
+ *
+ * @author Mike Thompson
+ */
+public final class CompositeGroupMultiLineGraphListView extends CompositeGroupD3GraphListView
+{
+ private static final Messages MSG = CoreGUI.getMessages();
+
+ public CompositeGroupMultiLineGraphListView(int groupId, int defId, boolean isAutogroup)
+ {
+ super(groupId, defId, isAutogroup);
+ }
+
+
+
+ @Override
+ public native void drawJsniChart() /*-{
+ console.log("Draw nvd3 charts for composite multiline graph");
+ var chartId = global.@org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getChartId()(),
+ chartHandle = "#mChart-"+chartId,
+ chartSelection = chartHandle + " svg",
+ yAxisUnits = this.@org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getYAxisUnits()(),
+ xAxisLabel = this.@org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getXAxisTitle()(),
+ xAxisTimeFormat = this.@org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupMultiLineGraphListView::getXAxisTimeFormatHoursMinutes()();
+ json = eval(this.@org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.monitoring.table.CompositeGroupD3GraphListView::getJsonMetrics()());
+
+ $wnd.nv.addGraph(function() {
+ var chart = $wnd.nv.models.lineChart();
+
+ chart.xAxis.axisLabel(xAxisLabel)
+ .tickFormat(function(d) { return $wnd.d3.time.format(xAxisTimeFormat)(new Date(d)) });
+
+ chart.yAxis
+ .axisLabel(yAxisUnits)
+ .tickFormat($wnd.d3.format('.02f'));
+
+ $wnd.d3.select(chartSelection)
+ .datum(json)
+ .transition().duration(300)
+ .call(chart);
+
+ $wnd.nv.utils.windowResize(chart.update);
+
+ return chart;
+ });
+
+ }-*/;
+
+
+
+ public String getXAxisTimeFormatHoursMinutes() {
+ return MSG.chart_xaxis_time_format_hours_minutes();
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/DashboardLinkUtility.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/DashboardLinkUtility.java
new file mode 100644
index 0000000..8ca1ac5
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/DashboardLinkUtility.java
@@ -0,0 +1,258 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.History;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.widgets.menu.Menu;
+import com.smartgwt.client.widgets.menu.MenuItem;
+import com.smartgwt.client.widgets.menu.events.ClickHandler;
+import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;
+
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.criteria.DashboardCriteria;
+import org.rhq.core.domain.criteria.SubjectCriteria;
+import org.rhq.core.domain.dashboard.Dashboard;
+import org.rhq.core.domain.dashboard.DashboardPortlet;
+import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementSchedule;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.domain.util.PageList;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.Messages;
+import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
+import org.rhq.enterprise.gui.coregui.client.dashboard.portlets.inventory.resource.graph.ResourceD3GraphPortlet;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.message.Message;
+import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
+import org.rhq.enterprise.gui.coregui.client.util.preferences.UserPreferences;
+
+/**
+ * Utility Class to build menus for linking to the Dashboard.
+ * @author Jay Shaughnessy
+ * @author Greg Hinkle
+ * @author Mike Thompson
+ */
+public class DashboardLinkUtility {
+ final static Messages MSG = CoreGUI.getMessages();
+
+ private DashboardLinkUtility() {
+ }
+
+ public static MenuItem buildMetricsMenu(final ResourceType resourceType, final Resource resource, String label) {
+
+ MenuItem measurements = new MenuItem(label);
+ final Menu measurementsSubMenu = new Menu();
+
+ DashboardCriteria criteria = new DashboardCriteria();
+ GWTServiceLookup.getDashboardService().findDashboardsByCriteria(criteria,
+ new AsyncCallback<PageList<Dashboard>>() {
+
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_loadFailed_dashboard(),
+ caught);
+ }
+
+ public void onSuccess(PageList<Dashboard> result) {
+ //sort the display items alphabetically
+ TreeSet<String> ordered = new TreeSet<String>();
+ Map<String, MeasurementDefinition> definitionMap = new HashMap<String, MeasurementDefinition>();
+ for (MeasurementDefinition m : resourceType.getMetricDefinitions()) {
+ ordered.add(m.getDisplayName());
+ definitionMap.put(m.getDisplayName(), m);
+ }
+
+ for (String displayName : ordered) {
+ final MeasurementDefinition def = definitionMap.get(displayName);
+ //only add menu items for Measurement
+ if (def.getDataType().equals(DataType.MEASUREMENT)) {
+ MenuItem defItem = new MenuItem(def.getDisplayName());
+ measurementsSubMenu.addItem(defItem);
+ Menu defSubItem = new Menu();
+ defItem.setSubmenu(defSubItem);
+
+ for (final Dashboard d : result) {
+ MenuItem addToDBItem = new MenuItem(MSG
+ .view_tree_common_contextMenu_addChartToDashboard(d.getName()));
+ defSubItem.addItem(addToDBItem);
+
+ addToDBItem.addClickHandler(new ClickHandler() {
+
+ public void onClick(MenuItemClickEvent menuItemClickEvent) {
+ DashboardPortlet p = new DashboardPortlet(MSG
+ .view_tree_common_contextMenu_resourceGraph(), ResourceD3GraphPortlet.KEY,
+ 250);
+ p.getConfiguration()
+ .put(
+ new PropertySimple(ResourceD3GraphPortlet.CFG_RESOURCE_ID, resource
+ .getId()));
+ p.getConfiguration().put(
+ new PropertySimple(ResourceD3GraphPortlet.CFG_DEFINITION_ID, def.getId()));
+
+ d.addPortlet(p);
+
+ GWTServiceLookup.getDashboardService().storeDashboard(d,
+ new AsyncCallback<Dashboard>() {
+
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(
+ MSG.view_tree_common_contextMenu_saveChartToDashboardFailure(),
+ caught);
+ }
+
+ public void onSuccess(Dashboard result) {
+ CoreGUI
+ .getMessageCenter()
+ .notify(
+ new Message(
+ MSG.view_tree_common_contextMenu_saveChartToDashboardSuccessful(result
+ .getName()), Message.Severity.Info));
+ }
+ });
+
+ }
+
+ });
+
+ }
+
+ //add new menu item for adding current graphable element to view if on Monitor/Graphs tab
+ String currentViewPath = History.getToken();
+ if (currentViewPath.contains("Monitoring/Metrics")) {
+ MenuItem addGraphItem = new MenuItem(MSG.common_title_add_graph_to_view());
+ defSubItem.addItem(addGraphItem);
+
+ addGraphItem.addClickHandler(new ClickHandler() {
+ public void onClick(MenuItemClickEvent menuItemClickEvent) {
+ //generate javascript to call out to.
+ //Ex. menuLayers.hide();addMetric('${metric.resourceId},${metric.scheduleId}')
+ if (getScheduleDefinitionId(resource, def.getName()) > -1) {
+ final String resourceGraphElements = resource.getId() + ","
+ + getScheduleDefinitionId(resource, def.getName());
+
+ //Once, the portal-war will be rewritten to GWT and operations performed
+ //within the iframe + JSF will update the user preferences, the following
+ //2 lines could be uncommented and the lines below them refactorized
+ //MeasurementUserPreferences measurementPreferences = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
+ //String selectedView = measurementPreferences.getSelectedView(String.valueOf(resource.getId()));
+
+ final int sid = UserSessionManager.getSessionSubject().getId();
+ SubjectCriteria c = new SubjectCriteria();
+ c.addFilterId(sid);
+
+ GWTServiceLookup.getSubjectService().findSubjectsByCriteria(c,
+ new AsyncCallback<PageList<Subject>>() {
+ public void onSuccess(PageList<Subject> result) {
+ if (result.size() > 0) {
+ UserPreferences uPreferences = new UserPreferences(result
+ .get(0));
+ MeasurementUserPreferences mPreferences = new MeasurementUserPreferences(
+ uPreferences);
+ String selectedView = mPreferences.getSelectedView(String
+ .valueOf(resource.getId()));
+
+ addNewMetric(String.valueOf(resource.getId()),
+ selectedView, resourceGraphElements);
+ } else {
+ Log.warn("DashboardLinkUtility: Error obtaining subject with id:" + sid);
+ }
+ }
+
+ public void onFailure(Throwable caught) {
+ Log.warn("DashboardLinkUtility: Error obtaining subject with id:" + sid, caught);
+ }
+ });
+ }
+ }
+ });
+ }
+ }
+ }
+
+ }
+ });
+ measurements.setSubmenu(measurementsSubMenu);
+ return measurements;
+ }
+
+ /** Locate the specific schedule definition using the definition identifier.
+ */
+ private static int getScheduleDefinitionId(Resource resource, String definitionName) {
+ int id = -1;
+ if (resource.getSchedules() != null) {
+ boolean located = false;
+ MeasurementSchedule[] schedules = new MeasurementSchedule[resource.getSchedules().size()];
+ resource.getSchedules().toArray(schedules);
+ for (int i = 0; (!located && i < resource.getSchedules().size()); i++) {
+ MeasurementSchedule schedule = schedules[i];
+ MeasurementDefinition definition = schedule.getDefinition();
+ if ((definition != null) && definition.getName().equals(definitionName)) {
+ located = true;
+ id = schedule.getId();
+ }
+ }
+ }
+ return id;
+ }
+
+ private static void addNewMetric(String id, String selectedView, String resourceGraphElements) {
+ //construct portal.war url to access
+ String baseUrl = "/resource/common/monitor/visibility/IndicatorCharts.do";
+ baseUrl += "?id=" + id;
+ baseUrl += "&view=" + selectedView;
+ baseUrl += "&action=addChart&metric=" + resourceGraphElements;
+ final String url = baseUrl;
+ //initiate HTTP request
+ final RequestBuilder b = new RequestBuilder(RequestBuilder.GET, baseUrl);
+
+ try {
+ b.setCallback(new RequestCallback() {
+ public void onResponseReceived(final Request request, final Response response) {
+ Log.trace("Successfully submitted request to add graph to view:" + url);
+
+ //kick off a page reload.
+ String currentViewPath = History.getToken();
+ CoreGUI.goToView(currentViewPath, true);
+ }
+
+ @Override
+ public void onError(Request request, Throwable t) {
+ Log.trace("Error adding Metric:" + url, t);
+ }
+ });
+ b.send();
+ } catch (RequestException e) {
+ Log.warn("Error adding Metric:" + url, e);
+ }
+
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceDetailView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceDetailView.java
index 2d33d66..ae51195 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceDetailView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceDetailView.java
@@ -66,9 +66,8 @@ import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.inventory
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.inventory.ResourceAgentView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.CalltimeView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.D3GraphListView;
-import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.avail.ResourceAvailabilityView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.schedules.ResourceSchedulesView;
-import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table.MeasurementTableView;
+import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table.MetricsResourceView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.traits.TraitsView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.operation.history.ResourceOperationHistoryListView;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.operation.schedule.ResourceOperationScheduleListView;
@@ -129,10 +128,8 @@ public class ResourceDetailView extends
private SubTab summaryActivity;
private SubTab summaryTimeline;
- private SubTab monitorGraphs;
private SubTab monitorMetrics;
private SubTab monitorTraits;
- private SubTab monitorAvail;
private SubTab monitorSched;
private SubTab monitorCallTime;
private SubTab inventoryChildren;
@@ -196,16 +193,11 @@ public class ResourceDetailView extends
monitoringTab = new TwoLevelTab(new ViewName("Monitoring", MSG.view_tabs_common_monitoring()),
IconEnum.SUSPECT_METRICS);
- monitorGraphs = new SubTab(monitoringTab, new ViewName("Graphs", MSG.view_tabs_common_graphs()), null);
-
- monitorMetrics = new SubTab(monitoringTab, new ViewName("Metrics", "Metrics"), null);
+ monitorMetrics = new SubTab(monitoringTab, new ViewName("Metrics", MSG.view_tabs_common_metrics()), null);
monitorTraits = new SubTab(monitoringTab, new ViewName("Traits", MSG.view_tabs_common_traits()), null);
- monitorAvail = new SubTab(monitoringTab, new ViewName("Availability", MSG.view_tabs_common_availability()),
- null);
monitorSched = new SubTab(monitoringTab, new ViewName("Schedules", MSG.view_tabs_common_schedules()), null);
monitorCallTime = new SubTab(monitoringTab, new ViewName("CallTime", MSG.view_tabs_common_calltime()), null);
- monitoringTab.registerSubTabs(monitorGraphs, monitorMetrics, monitorTraits, monitorAvail,
- monitorSched, monitorCallTime);
+ monitoringTab.registerSubTabs( monitorMetrics, monitorTraits, monitorSched, monitorCallTime);
tabs.add(monitoringTab);
eventsTab = new TwoLevelTab(new ViewName("Events", MSG.view_tabs_common_events()), IconEnum.EVENTS);
@@ -395,22 +387,14 @@ public class ResourceDetailView extends
boolean visibleToIE8 = !BrowserUtility.isBrowserPreIE9();
- viewFactory = (!visibleToIE8) ? null : new ViewFactory() {
- @Override
- public Canvas createView() {
- return createD3GraphListView();
- }
- };
- updateSubTab(this.monitoringTab, this.monitorGraphs, visible, visibleToIE8, viewFactory);
-
// visible = same test as above
viewFactory = (!visible) ? null : new ViewFactory() {
@Override
public Canvas createView() {
- return new MeasurementTableView(resource.getId());
+ return new MetricsResourceView(resource);
}
};
- updateSubTab(this.monitoringTab, this.monitorMetrics, visible, true, viewFactory);
+ updateSubTab(this.monitoringTab, this.monitorMetrics, visible, visibleToIE8, viewFactory);
visible = hasMetricsOfType(this.resourceComposite, DataType.TRAIT);
viewFactory = (!visible) ? null : new ViewFactory() {
@@ -421,13 +405,6 @@ public class ResourceDetailView extends
};
updateSubTab(this.monitoringTab, this.monitorTraits, visible, true, viewFactory);
- updateSubTab(this.monitoringTab, this.monitorAvail, true, true, new ViewFactory() {
- @Override
- public Canvas createView() {
- return new ResourceAvailabilityView(resourceComposite);
- }
- });
-
updateSubTab(this.monitoringTab, this.monitorSched, hasMetricsOfType(this.resourceComposite, null), true,
new ViewFactory() {
@Override
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceTreeView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceTreeView.java
index 071f831..a3990bb 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceTreeView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/ResourceTreeView.java
@@ -32,11 +32,6 @@ import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
-import com.google.gwt.http.client.Request;
-import com.google.gwt.http.client.RequestBuilder;
-import com.google.gwt.http.client.RequestCallback;
-import com.google.gwt.http.client.RequestException;
-import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSCallback;
@@ -60,17 +55,8 @@ import com.smartgwt.client.widgets.tree.events.DataArrivedHandler;
import com.smartgwt.client.widgets.tree.events.NodeContextClickEvent;
import com.smartgwt.client.widgets.tree.events.NodeContextClickHandler;
-import org.rhq.core.domain.auth.Subject;
-import org.rhq.core.domain.configuration.PropertySimple;
-import org.rhq.core.domain.criteria.DashboardCriteria;
import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
-import org.rhq.core.domain.criteria.SubjectCriteria;
-import org.rhq.core.domain.dashboard.Dashboard;
-import org.rhq.core.domain.dashboard.DashboardPortlet;
-import org.rhq.core.domain.measurement.DataType;
-import org.rhq.core.domain.measurement.MeasurementDefinition;
-import org.rhq.core.domain.measurement.MeasurementSchedule;
import org.rhq.core.domain.operation.OperationDefinition;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
@@ -82,11 +68,9 @@ import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.ImageManager;
import org.rhq.enterprise.gui.coregui.client.LinkManager;
-import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
import org.rhq.enterprise.gui.coregui.client.ViewId;
import org.rhq.enterprise.gui.coregui.client.ViewPath;
import org.rhq.enterprise.gui.coregui.client.components.tree.EnhancedTreeNode;
-import org.rhq.enterprise.gui.coregui.client.dashboard.portlets.inventory.resource.graph.ResourceD3GraphPortlet;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
import org.rhq.enterprise.gui.coregui.client.gwt.ResourceGWTServiceAsync;
import org.rhq.enterprise.gui.coregui.client.gwt.ResourceGroupGWTServiceAsync;
@@ -101,8 +85,6 @@ import org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTyp
import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
import org.rhq.enterprise.gui.coregui.client.util.message.Message;
-import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
-import org.rhq.enterprise.gui.coregui.client.util.preferences.UserPreferences;
/**
* @author Jay Shaughnessy
@@ -562,7 +544,7 @@ public class ResourceTreeView extends EnhancedVLayout {
resourceContextMenu.addItem(operations);
// Metric graph addition menu
- resourceContextMenu.addItem(buildMetricsMenu(resourceType, resource));
+ resourceContextMenu.addItem(DashboardLinkUtility.buildMetricsMenu(resourceType, resource, MSG.view_tree_common_contextMenu_measurements()));
// Create Child Menu and Manual Import Menu
final Set<ResourceType> creatableChildTypes = getCreatableChildTypes(resourceType);
@@ -725,190 +707,6 @@ public class ResourceTreeView extends EnhancedVLayout {
tree.reloadChildren(refreshNode);
}
- private MenuItem buildMetricsMenu(final ResourceType type, final Resource resource) {
- MenuItem measurements = new MenuItem(MSG.view_tree_common_contextMenu_measurements());
- final Menu measurementsSubMenu = new Menu();
-
- DashboardCriteria criteria = new DashboardCriteria();
- GWTServiceLookup.getDashboardService().findDashboardsByCriteria(criteria,
- new AsyncCallback<PageList<Dashboard>>() {
-
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_loadFailed_dashboard(),
- caught);
- }
-
- public void onSuccess(PageList<Dashboard> result) {
- //sort the display items alphabetically
- TreeSet<String> ordered = new TreeSet<String>();
- Map<String, MeasurementDefinition> definitionMap = new HashMap<String, MeasurementDefinition>();
- for (MeasurementDefinition m : type.getMetricDefinitions()) {
- ordered.add(m.getDisplayName());
- definitionMap.put(m.getDisplayName(), m);
- }
-
- for (String displayName : ordered) {
- final MeasurementDefinition def = definitionMap.get(displayName);
- //only add menu items for Measurement
- if (def.getDataType().equals(DataType.MEASUREMENT)) {
- MenuItem defItem = new MenuItem(def.getDisplayName());
- measurementsSubMenu.addItem(defItem);
- Menu defSubItem = new Menu();
- defItem.setSubmenu(defSubItem);
-
- for (final Dashboard d : result) {
- MenuItem addToDBItem = new MenuItem(MSG
- .view_tree_common_contextMenu_addChartToDashboard(d.getName()));
- defSubItem.addItem(addToDBItem);
-
- addToDBItem.addClickHandler(new ClickHandler() {
-
- public void onClick(MenuItemClickEvent menuItemClickEvent) {
- DashboardPortlet p = new DashboardPortlet(MSG
- .view_tree_common_contextMenu_resourceGraph(), ResourceD3GraphPortlet.KEY,
- 250);
- p.getConfiguration().put(
- new PropertySimple(ResourceD3GraphPortlet.CFG_RESOURCE_ID, resource.getId()));
- p.getConfiguration().put(
- new PropertySimple(ResourceD3GraphPortlet.CFG_DEFINITION_ID, def.getId()));
-
- d.addPortlet(p);
-
- GWTServiceLookup.getDashboardService().storeDashboard(d,
- new AsyncCallback<Dashboard>() {
-
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError(
- MSG.view_tree_common_contextMenu_saveChartToDashboardFailure(),
- caught);
- }
-
- public void onSuccess(Dashboard result) {
- CoreGUI
- .getMessageCenter()
- .notify(
- new Message(
- MSG.view_tree_common_contextMenu_saveChartToDashboardSuccessful(result
- .getName()), Message.Severity.Info));
- }
- });
-
- }
-
- });
-
-
- }//end dashboard iteration
-
- //add new menu item for adding current graphable element to view if on Monitor/Graphs tab
- String currentViewPath = History.getToken();
- if (currentViewPath.contains("Monitoring/NewGraphs")) {
- MenuItem addGraphItem = new MenuItem(MSG.common_title_add_graph_to_view());
- defSubItem.addItem(addGraphItem);
-
- addGraphItem.addClickHandler(new ClickHandler() {
- public void onClick(MenuItemClickEvent menuItemClickEvent) {
- //generate javascript to call out to.
- //Ex. menuLayers.hide();addMetric('${metric.resourceId},${metric.scheduleId}')
- if (getScheduleDefinitionId(resource, def.getName()) > -1) {
- final String resourceGraphElements = resource.getId() + ","
- + getScheduleDefinitionId(resource, def.getName());
-
- //Once, the portal-war will be rewritten to GWT and operations performed
- //within the iframe + JSF will update the user preferences, the following
- //2 lines could be uncommented and the lines below them refactorized
- //MeasurementUserPreferences measurementPreferences = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
- //String selectedView = measurementPreferences.getSelectedView(String.valueOf(resource.getId()));
-
- final int sid = UserSessionManager.getSessionSubject().getId();
- SubjectCriteria c = new SubjectCriteria();
- c.addFilterId(sid);
-
- GWTServiceLookup.getSubjectService().findSubjectsByCriteria(c,
- new AsyncCallback<PageList<Subject>>() {
- public void onSuccess(PageList<Subject> result) {
- if (result.size() > 0) {
- UserPreferences uPreferences = new UserPreferences(result
- .get(0));
- MeasurementUserPreferences mPreferences = new MeasurementUserPreferences(
- uPreferences);
- String selectedView = mPreferences.getSelectedView(String
- .valueOf(resource.getId()));
-
- addNewMetric(String.valueOf(resource.getId()),
- selectedView, resourceGraphElements);
- } else {
- Log.trace("Error obtaining subject with id:" + sid);
- }
- }
-
- public void onFailure(Throwable caught) {
- Log.trace("Error obtaining subject with id:" + sid, caught);
- }
- });
- }
- }
- });
- } // end add the "add to view" menu item
- }//end trait exclusion
- }//end measurement def iteration
-
- }
- });
- measurements.setSubmenu(measurementsSubMenu);
- return measurements;
- }
-
- private void addNewMetric(String id, String selectedView, String resourceGraphElements) {
- //construct portal.war url to access
- String baseUrl = "/resource/common/monitor/visibility/IndicatorCharts.do";
- baseUrl += "?id=" + id;
- baseUrl += "&view=" + selectedView;
- baseUrl += "&action=addChart&metric=" + resourceGraphElements;
- final String url = baseUrl;
- //initiate HTTP request
- final RequestBuilder b = new RequestBuilder(RequestBuilder.GET, baseUrl);
-
- try {
- b.setCallback(new RequestCallback() {
- public void onResponseReceived(final Request request, final Response response) {
- Log.trace("Successfully submitted request to add graph to view:" + url);
-
- //kick off a page reload.
- String currentViewPath = History.getToken();
- CoreGUI.goToView(currentViewPath, true);
- }
-
- @Override
- public void onError(Request request, Throwable t) {
- Log.trace("Error adding Metric:" + url, t);
- }
- });
- b.send();
- } catch (RequestException e) {
- Log.trace("Error adding Metric:" + url, e);
- }
- }
-
- /** Locate the specific schedule definition using the definition identifier.
- */
- private int getScheduleDefinitionId(Resource resource, String definitionName) {
- int id = -1;
- if (resource.getSchedules() != null) {
- boolean located = false;
- MeasurementSchedule[] schedules = new MeasurementSchedule[resource.getSchedules().size()];
- resource.getSchedules().toArray(schedules);
- for (int i = 0; (!located && i < resource.getSchedules().size()); i++) {
- MeasurementSchedule schedule = schedules[i];
- MeasurementDefinition definition = schedule.getDefinition();
- if ((definition != null) && definition.getName().equals(definitionName)) {
- located = true;
- id = schedule.getId();
- }
- }
- }
- return id;
- }
private void setRootResource(Resource rootResource) {
this.rootResource = rootResource;
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/D3GraphListView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/D3GraphListView.java
index 65893ce..8b9f327 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/D3GraphListView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/D3GraphListView.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,6 @@ package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitori
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
-import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
@@ -30,7 +29,6 @@ import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.Overflow;
-import com.smartgwt.client.widgets.form.fields.events.ClickHandler;
import com.smartgwt.client.widgets.layout.VLayout;
import org.rhq.core.domain.common.EntityContext;
@@ -41,16 +39,14 @@ import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
import org.rhq.core.domain.measurement.composite.MeasurementOOBComposite;
import org.rhq.core.domain.resource.Resource;
-import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractD3GraphListView;
-import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
-import org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.async.Command;
import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
@@ -115,17 +111,17 @@ public class D3GraphListView extends AbstractD3GraphListView {
setOverflow(Overflow.HIDDEN);
}
-
@Override
protected void onDraw() {
super.onDraw();
- Log.debug("D3GraphListView.onDraw() for: " + resource.getName()+ " id: "+ resource.getId());
+ Log.debug("D3GraphListView.onDraw() for: " + resource.getName() + " id: " + resource.getId());
destroyMembers();
addMember(buttonBarDateTimeRangeEditor);
if (showAvailabilityGraph) {
- availabilityGraph = new AvailabilityD3GraphView<AvailabilityOverUnderGraphType>(new AvailabilityOverUnderGraphType(resource.getId()));
+ availabilityGraph = new AvailabilityD3GraphView<AvailabilityOverUnderGraphType>(
+ new AvailabilityOverUnderGraphType(resource.getId()));
addMember(availabilityGraph);
}
@@ -163,8 +159,8 @@ public class D3GraphListView extends AbstractD3GraphListView {
@Override
public void onSuccess(List<Availability> availList) {
- Log.debug("\nSuccessfully queried availability in: "
- + (System.currentTimeMillis() - timerStart) + " ms.");
+ Log.debug("\nSuccessfully queried availability in: " + (System.currentTimeMillis() - timerStart)
+ + " ms.");
availabilityList = availList;
if (countDownLatch != null) {
countDownLatch.countDown();
@@ -180,193 +176,179 @@ public class D3GraphListView extends AbstractD3GraphListView {
private void queryAndBuildGraphs() {
final long startTimer = System.currentTimeMillis();
- if(null != availabilityGraph){
+ if (null != availabilityGraph) {
queryAvailability(EntityContext.forResource(resource.getId()), buttonBarDateTimeRangeEditor.getStartTime(),
buttonBarDateTimeRangeEditor.getEndTime(), null);
}
- ResourceTypeRepository.Cache.getInstance().getResourceTypes(resource.getResourceType().getId(),
- EnumSet.of(ResourceTypeRepository.MetadataType.measurements),
- new ResourceTypeRepository.TypeLoadedCallback() {
- public void onTypesLoaded(final ResourceType type) {
+ final ArrayList<MeasurementDefinition> measurementDefinitions = new ArrayList<MeasurementDefinition>();
+ final ArrayList<MeasurementDefinition> summaryMeasurementDefinitions = new ArrayList<MeasurementDefinition>();
- final ArrayList<MeasurementDefinition> measurementDefinitions = new ArrayList<MeasurementDefinition>();
- final ArrayList<MeasurementDefinition> summaryMeasurementDefinitions = new ArrayList<MeasurementDefinition>();
+ for (MeasurementDefinition def : resource.getResourceType().getMetricDefinitions()) {
+ if (def.getDataType() == DataType.MEASUREMENT && def.getDisplayType() == DisplayType.SUMMARY) {
+ summaryMeasurementDefinitions.add(def);
+ }
+ measurementDefinitions.add(def);
+ }
- for (MeasurementDefinition def : type.getMetricDefinitions()) {
- if (def.getDataType() == DataType.MEASUREMENT && def.getDisplayType() == DisplayType.SUMMARY) {
- summaryMeasurementDefinitions.add(def);
- }
- measurementDefinitions.add(def);
- }
+ Collections.sort(measurementDefinitions, new Comparator<MeasurementDefinition>() {
+ @Override
+ public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
+ return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
+ }
+ });
+ Collections.sort(summaryMeasurementDefinitions, new Comparator<MeasurementDefinition>() {
+ @Override
+ public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
+ return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
+ }
+ });
+
+ int[] measDefIdArray = new int[measurementDefinitions.size()];
+ for (int i = 0; i < measDefIdArray.length; i++) {
+ measDefIdArray[i] = measurementDefinitions.get(i).getId();
+ }
- Collections.sort(measurementDefinitions, new Comparator<MeasurementDefinition>() {
- @Override
- public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
- return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
- }
- });
- Collections.sort(summaryMeasurementDefinitions, new Comparator<MeasurementDefinition>() {
- @Override
- public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
- return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
- }
- });
-
- int[] measDefIdArray = new int[measurementDefinitions.size()];
- for (int i = 0; i < measDefIdArray.length; i++) {
- measDefIdArray[i] = measurementDefinitions.get(i).getId();
+ // setting up a deferred Command to execute after all resource queries have completed (successfully or unsuccessfully)
+ // we know there are exactly 2 resources
+ final CountDownLatch countDownLatch = CountDownLatch.create(NUM_ASYNC_CALLS, new Command() {
+ @Override
+ /**
+ * Satisfied only after ALL of the metric queries AND availability have completed
+ */
+ public void execute() {
+ Log.debug("Total Time for async metrics/avail query: " + (System.currentTimeMillis() - startTimer));
+ if (null == metricsDataList || metricsDataList.isEmpty()) {
+ loadingLabel.setContents(MSG.view_resource_monitor_graphs_noneAvailable());
+ } else {
+ loadingLabel.hide();
+ if (useSummaryData) {
+ buildSummaryGraphs(metricsDataList, summaryMeasurementDefinitions, measurementDefinitions);
+ } else {
+ determineGraphsToBuild(metricsDataList, measurementDefinitions, definitionIds);
}
-
- // setting up a deferred Command to execute after all resource queries have completed (successfully or unsuccessfully)
- // we know there are exactly 2 resources
- final CountDownLatch countDownLatch = CountDownLatch.create(NUM_ASYNC_CALLS, new Command() {
- @Override
- /**
- * Satisfied only after ALL of the metric queries AND availability have completed
- */
- public void execute() {
- Log.debug("Total Time for async metrics/avail query: "
- + (System.currentTimeMillis() - startTimer));
- if (null == metricsDataList || metricsDataList.isEmpty()) {
- loadingLabel.setContents(MSG.view_resource_monitor_graphs_noneAvailable());
- } else {
- loadingLabel.hide();
- if (useSummaryData) {
- buildSummaryGraphs(metricsDataList, summaryMeasurementDefinitions,
- measurementDefinitions);
- } else {
- determineGraphsToBuild(metricsDataList, measurementDefinitions, definitionIds);
- }
- // There is a weird timing case when availabilityGraph can be null
- if (null != availabilityGraph) {
- // we only need the first metricData since we are only taking the
- // availability data set in there for the dropdowns already
- availabilityGraph.setAvailabilityList(availabilityList);
- new Timer(){
- @Override
- public void run() {
- availabilityGraph.drawJsniChart();
- }
- }.schedule(150);
- }
+ // There is a weird timing case when availabilityGraph can be null
+ if (null != availabilityGraph) {
+ // we only need the first metricData since we are only taking the
+ // availability data set in there for the dropdowns already
+ availabilityGraph.setAvailabilityList(availabilityList);
+ new Timer() {
+ @Override
+ public void run() {
+ availabilityGraph.drawJsniChart();
}
+ }.schedule(150);
+ }
+ }
- }
- });
+ }
+ });
- queryMetricData(measDefIdArray, countDownLatch);
- queryOOBMetrics(resource, countDownLatch);
- // now the countDown latch will run sometime asynchronously
+ queryMetricData(measDefIdArray, countDownLatch);
+ queryOOBMetrics(resource, countDownLatch);
+ // now the countDown latch will run sometime asynchronously
+ }
+ private void queryMetricData(final int[] measDefIdArray, final CountDownLatch countDownLatch) {
+ GWTServiceLookup.getMeasurementDataService().findDataForResource(resource.getId(), measDefIdArray,
+ buttonBarDateTimeRangeEditor.getStartTime(), buttonBarDateTimeRangeEditor.getEndTime(), 60,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_loadFailed(), caught);
+ loadingLabel.setContents(MSG.view_resource_monitor_graphs_loadFailed());
+ countDownLatch.countDown();
}
- private void queryMetricData(final int[] measDefIdArray, final CountDownLatch countDownLatch) {
- GWTServiceLookup.getMeasurementDataService().findDataForResource(resource.getId(), measDefIdArray,
- buttonBarDateTimeRangeEditor.getStartTime(), buttonBarDateTimeRangeEditor.getEndTime(), 60,
- new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
- @Override
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_loadFailed(),
- caught);
- loadingLabel.setContents(MSG.view_resource_monitor_graphs_loadFailed());
- countDownLatch.countDown();
- }
-
- @Override
- public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> metrics) {
- metricsDataList = metrics;
- Log.debug("Regular Metric graph data queried in: "
- + (System.currentTimeMillis() - startTimer + " ms."));
- countDownLatch.countDown();
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> metrics) {
+ metricsDataList = metrics;
+ countDownLatch.countDown();
- }
- });
}
+ });
+ }
- private void queryOOBMetrics(final Resource resource, final CountDownLatch countDownLatch) {
+ private void queryOOBMetrics(final Resource resource, final CountDownLatch countDownLatch) {
- final long startTime = System.currentTimeMillis();
+ final long startTime = System.currentTimeMillis();
- GWTServiceLookup.getMeasurementDataService().getHighestNOOBsForResource(resource.getId(), 60,
+ GWTServiceLookup.getMeasurementDataService().getHighestNOOBsForResource(resource.getId(), 60,
- new AsyncCallback<PageList<MeasurementOOBComposite>>() {
- @Override
- public void onSuccess(PageList<MeasurementOOBComposite> measurementOOBComposites) {
+ new AsyncCallback<PageList<MeasurementOOBComposite>>() {
+ @Override
+ public void onSuccess(PageList<MeasurementOOBComposite> measurementOOBComposites) {
- measurementOOBCompositeList = measurementOOBComposites;
- Log.debug("\nSuccessfully queried "+measurementOOBCompositeList.size() +" OOB records in: " + (System.currentTimeMillis() - startTime)
- + " ms.");
- countDownLatch.countDown();
- }
+ measurementOOBCompositeList = measurementOOBComposites;
+ Log.debug("\nSuccessfully queried " + measurementOOBCompositeList.size() + " OOB records in: "
+ + (System.currentTimeMillis() - startTime) + " ms.");
+ countDownLatch.countDown();
+ }
- @Override
- public void onFailure(Throwable caught) {
- Log.debug("Error retrieving out of bound metrics for resource [" + resource.getId() + "]:"
- + caught.getMessage());
- countDownLatch.countDown();
- }
- });
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.debug("Error retrieving out of bound metrics for resource [" + resource.getId() + "]:"
+ + caught.getMessage());
+ countDownLatch.countDown();
+ }
+ });
- }
+ }
- /**
- * Spin through the measurement definitions (in order) checking to see if they are in the
- * summary measurement definition set and if so build a graph.
- * @param measurementData
- * @param summaryMeasurementDefinitions
- * @param measurementDefinitions
- */
- private void buildSummaryGraphs(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
- List<MeasurementDefinition> summaryMeasurementDefinitions,
- List<MeasurementDefinition> measurementDefinitions) {
- Set<Integer> summaryIds = new TreeSet<Integer>();
- for (MeasurementDefinition summaryMeasurementDefinition : summaryMeasurementDefinitions) {
- summaryIds.add(summaryMeasurementDefinition.getId());
- }
+ /**
+ * Spin through the measurement definitions (in order) checking to see if they are in the
+ * summary measurement definition set and if so build a graph.
+ * @param measurementData
+ * @param summaryMeasurementDefinitions
+ * @param measurementDefinitions
+ */
+ private void buildSummaryGraphs(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
+ List<MeasurementDefinition> summaryMeasurementDefinitions, List<MeasurementDefinition> measurementDefinitions) {
+ Set<Integer> summaryIds = new TreeSet<Integer>();
+ for (MeasurementDefinition summaryMeasurementDefinition : summaryMeasurementDefinitions) {
+ summaryIds.add(summaryMeasurementDefinition.getId());
+ }
- int i = 0;
- for (MeasurementDefinition measurementDefinition : measurementDefinitions) {
- if (summaryIds.contains(measurementDefinition.getId())) {
- buildSingleGraph(measurementOOBCompositeList, measurementDefinition,
- measurementData.get(i), MULTI_CHART_HEIGHT);
- }
- i++;
- }
+ int i = 0;
+ for (MeasurementDefinition measurementDefinition : measurementDefinitions) {
+ if (summaryIds.contains(measurementDefinition.getId())) {
+ buildSingleGraph(measurementOOBCompositeList, measurementDefinition, measurementData.get(i),
+ MULTI_CHART_HEIGHT);
+ }
+ i++;
+ }
- }
+ }
- private void determineGraphsToBuild(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
- List<MeasurementDefinition> measurementDefinitions, Set<Integer> definitionIds) {
- int i = 0;
- for (List<MeasurementDataNumericHighLowComposite> metric : measurementData) {
-
- for (Integer selectedDefinitionId : definitionIds) {
- final MeasurementDefinition measurementDefinition = measurementDefinitions.get(i);
- final int measurementId = measurementDefinition.getId();
-
- if (null != selectedDefinitionId) {
- // single graph case
- if (measurementId == selectedDefinitionId) {
- buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric, SINGLE_CHART_HEIGHT);
- }
- } else {
- // multiple graph case
- buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric, MULTI_CHART_HEIGHT);
- }
- }
- i++;
+ private void determineGraphsToBuild(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
+ List<MeasurementDefinition> measurementDefinitions, Set<Integer> definitionIds) {
+ int i = 0;
+ for (List<MeasurementDataNumericHighLowComposite> metric : measurementData) {
+
+ for (Integer selectedDefinitionId : definitionIds) {
+ final MeasurementDefinition measurementDefinition = measurementDefinitions.get(i);
+ final int measurementId = measurementDefinition.getId();
+
+ if (null != selectedDefinitionId) {
+ // single graph case
+ if (measurementId == selectedDefinitionId) {
+ buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric,
+ SINGLE_CHART_HEIGHT);
}
+ } else {
+ // multiple graph case
+ buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric, MULTI_CHART_HEIGHT);
}
- });
-
+ }
+ i++;
+ }
}
private void buildSingleGraph(PageList<MeasurementOOBComposite> measurementOOBCompositeList,
MeasurementDefinition measurementDefinition, List<MeasurementDataNumericHighLowComposite> data, int height) {
MetricGraphData metricGraphData = MetricGraphData.createForResource(resource.getId(), resource.getName(),
- measurementDefinition, data, measurementOOBCompositeList );
+ measurementDefinition, data, measurementOOBCompositeList);
StackedBarMetricGraphImpl graph = GWT.create(StackedBarMetricGraphImpl.class);
graph.setMetricGraphData(metricGraphData);
graph.setGraphListView(this);
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/MetricD3Graph.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/MetricD3Graph.java
index 3b4ec1f..65807f9 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/MetricD3Graph.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/MetricD3Graph.java
@@ -22,6 +22,7 @@ import com.google.gwt.user.client.Timer;
import com.smartgwt.client.widgets.HTMLFlow;
import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractD3GraphListView;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.RedrawGraphs;
import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
@@ -30,7 +31,7 @@ import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
* A D3 graph implementation for graphing Resource metrics.
* Just the graph only. No avail graph no buttons just he graph.
*/
-public class MetricD3Graph<T extends AbstractD3GraphListView> extends EnhancedVLayout {
+public class MetricD3Graph<T extends AbstractD3GraphListView> extends EnhancedVLayout implements RedrawGraphs{
protected StackedBarMetricGraphImpl graph;
private HTMLFlow graphDiv = null;
@@ -59,18 +60,18 @@ public class MetricD3Graph<T extends AbstractD3GraphListView> extends EnhancedVL
*/
private static String getSvgDefs() {
return " <defs>"
- + " <linearGradient id=\"headerGrad\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">"
- + " <stop offset=\"0%\" style=\"stop-color:#E6E6E6;stop-opacity:1\"/>"
- + " <stop offset=\"100%\" style=\"stop-color:#F0F0F0;stop-opacity:1\"/>"
- + " </linearGradient>"
- + " <pattern id=\"noDataStripes\" patternUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\""
- + " width=\"6\" height=\"3\">"
- + " <path d=\"M 0 0 6 0\" style=\"stroke:#CCCCCC; fill:none;\"/>"
- + " </pattern>"
- + " <pattern id=\"unknownStripes\" patternUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\""
- + " width=\"6\" height=\"3\">"
- + " <path d=\"M 0 0 6 0\" style=\"stroke:#2E9EC2; fill:none;\"/>"
- + " </pattern>"
+ + " <linearGradient id=\"headerGrad\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">"
+ + " <stop offset=\"0%\" style=\"stop-color:#E6E6E6;stop-opacity:1\"/>"
+ + " <stop offset=\"100%\" style=\"stop-color:#F0F0F0;stop-opacity:1\"/>"
+ + " </linearGradient>"
+ + " <pattern id=\"noDataStripes\" patternUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\""
+ + " width=\"6\" height=\"3\">"
+ + " <path d=\"M 0 0 6 0\" style=\"stroke:#CCCCCC; fill:none;\"/>"
+ + " </pattern>"
+ + " <pattern id=\"unknownStripes\" patternUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\""
+ + " width=\"6\" height=\"3\">"
+ + " <path d=\"M 0 0 6 0\" style=\"stroke:#2E9EC2; fill:none;\"/>"
+ + " </pattern>"
+ "<pattern id=\"diagonalHatchFill\" patternUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"105\" height=\"105\">"
+ "<g style=\"fill:none; stroke:black; stroke-width:1\">"
+ "<path d=\"M0 90 l15,15\"/>"
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/avail/ResourceAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/avail/ResourceAvailabilityView.java
deleted file mode 100644
index 6719070..0000000
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/avail/ResourceAvailabilityView.java
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright 2012, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.avail;
-
-import java.util.ArrayList;
-import java.util.Date;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.smartgwt.client.data.DSRequest;
-import com.smartgwt.client.data.DSResponse;
-import com.smartgwt.client.data.Record;
-import com.smartgwt.client.data.SortSpecifier;
-import com.smartgwt.client.rpc.RPCResponse;
-import com.smartgwt.client.types.Alignment;
-import com.smartgwt.client.types.ListGridFieldType;
-import com.smartgwt.client.types.SortDirection;
-import com.smartgwt.client.widgets.form.DynamicForm;
-import com.smartgwt.client.widgets.form.fields.FormItem;
-import com.smartgwt.client.widgets.form.fields.StaticTextItem;
-import com.smartgwt.client.widgets.grid.ListGridField;
-import com.smartgwt.client.widgets.grid.ListGridRecord;
-import com.smartgwt.client.widgets.layout.Layout;
-
-import org.rhq.core.domain.criteria.AvailabilityCriteria;
-import org.rhq.core.domain.measurement.Availability;
-import org.rhq.core.domain.measurement.MeasurementUnits;
-import org.rhq.core.domain.resource.composite.ResourceAvailabilitySummary;
-import org.rhq.core.domain.resource.composite.ResourceComposite;
-import org.rhq.core.domain.util.PageControl;
-import org.rhq.core.domain.util.PageList;
-import org.rhq.core.domain.util.PageOrdering;
-import org.rhq.enterprise.gui.coregui.client.CoreGUI;
-import org.rhq.enterprise.gui.coregui.client.ImageManager;
-import org.rhq.enterprise.gui.coregui.client.components.table.Table;
-import org.rhq.enterprise.gui.coregui.client.components.table.TimestampCellFormatter;
-import org.rhq.enterprise.gui.coregui.client.gwt.AvailabilityGWTServiceAsync;
-import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
-import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
-import org.rhq.enterprise.gui.coregui.client.util.RPCDataSource;
-import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
-
-/**
- * This shows the availability history for a resource.
- *
- * @author Jay Shaughnessy
- * @author John Mazzitelli
- */
-public class ResourceAvailabilityView extends EnhancedVLayout {
-
- private ResourceComposite resourceComposite;
- private StaticTextItem currentField;
- private StaticTextItem availField;
- private StaticTextItem availTimeField;
- private StaticTextItem downField;
- private StaticTextItem downTimeField;
- private StaticTextItem disabledField;
- private StaticTextItem disabledTimeField;
- private StaticTextItem failureCountField;
- private StaticTextItem disabledCountField;
- private StaticTextItem mtbfField;
- private StaticTextItem mttrField;
- private StaticTextItem unknownField;
- private StaticTextItem currentTimeField;
-
- public ResourceAvailabilityView(ResourceComposite resourceComposite) {
- super();
-
- this.resourceComposite = resourceComposite;
-
- setWidth100();
- setHeight100();
- }
-
- @Override
- protected void onInit() {
- super.onInit();
-
- addMember(createSummaryForm());
- addMember(createListView());
- }
-
- private DynamicForm createSummaryForm() {
- DynamicForm form = new DynamicForm();
- form.setWidth100();
- form.setAutoHeight();
- form.setMargin(10);
- form.setNumCols(4);
-
- // row 1
- currentField = new StaticTextItem("current", MSG.view_resource_monitor_availability_currentStatus());
- currentField.setWrapTitle(false);
- currentField.setColSpan(4);
-
- // row 2
- availField = new StaticTextItem("avail", MSG.view_resource_monitor_availability_availability());
- availField.setWrapTitle(false);
- prepareTooltip(availField, MSG.view_resource_monitor_availability_availability_tooltip());
-
- availTimeField = new StaticTextItem("availTime", MSG.view_resource_monitor_availability_uptime());
- availTimeField.setWrapTitle(false);
- prepareTooltip(availTimeField, MSG.view_resource_monitor_availability_uptime_tooltip());
-
- // row 3
- downField = new StaticTextItem("down", MSG.view_resource_monitor_availability_down());
- downField.setWrapTitle(false);
- prepareTooltip(downField, MSG.view_resource_monitor_availability_down_tooltip());
-
- downTimeField = new StaticTextItem("downTime", MSG.view_resource_monitor_availability_downtime());
- downTimeField.setWrapTitle(false);
- prepareTooltip(downTimeField, MSG.view_resource_monitor_availability_downtime_tooltip());
-
- // row 4
- disabledField = new StaticTextItem("disabled", MSG.view_resource_monitor_availability_disabled());
- disabledField.setWrapTitle(false);
- prepareTooltip(disabledField, MSG.view_resource_monitor_availability_disabled_tooltip());
-
- disabledTimeField = new StaticTextItem("disabledTime", MSG.view_resource_monitor_availability_disabledTime());
- disabledTimeField.setWrapTitle(false);
- prepareTooltip(disabledTimeField, MSG.view_resource_monitor_availability_disabledTime_tooltip());
-
- // row 5
- failureCountField = new StaticTextItem("failureCount", MSG.view_resource_monitor_availability_numFailures());
- failureCountField.setWrapTitle(false);
- prepareTooltip(failureCountField, MSG.view_resource_monitor_availability_numFailures_tooltip());
-
- disabledCountField = new StaticTextItem("disabledCount", MSG.view_resource_monitor_availability_numDisabled());
- disabledCountField.setWrapTitle(false);
- prepareTooltip(disabledCountField, MSG.view_resource_monitor_availability_numDisabled_tooltip());
-
- // row 6
- mtbfField = new StaticTextItem("mtbf", MSG.view_resource_monitor_availability_mtbf());
- mtbfField.setWrapTitle(false);
- prepareTooltip(mtbfField, MSG.view_resource_monitor_availability_mtbf_tooltip());
-
- mttrField = new StaticTextItem("mttr", MSG.view_resource_monitor_availability_mttr());
- mttrField.setWrapTitle(false);
- prepareTooltip(mttrField, MSG.view_resource_monitor_availability_mttr_tooltip());
-
- // row 7
- unknownField = new StaticTextItem("unknown");
- unknownField.setWrapTitle(false);
- unknownField.setColSpan(4);
- unknownField.setShowTitle(false);
-
- // row 8
- currentTimeField = new StaticTextItem("currentTime");
- currentTimeField.setWrapTitle(false);
- currentTimeField.setColSpan(4);
- currentTimeField.setShowTitle(false);
-
- form.setItems(currentField, availField, availTimeField, downField, downTimeField, disabledField,
- disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
- currentTimeField);
-
- reloadSummaryData();
-
- return form;
- }
-
- private void reloadSummaryData() {
- GWTServiceLookup.getResourceService().getResourceAvailabilitySummary(resourceComposite.getResource().getId(),
- new AsyncCallback<ResourceAvailabilitySummary>() {
-
- @Override
- public void onSuccess(ResourceAvailabilitySummary result) {
-
- currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(result
- .getCurrent().getName(), TimestampCellFormatter.format(result.getLastChange().getTime())));
- availField.setValue(MeasurementConverterClient.format(result.getUpPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- availTimeField.setValue(MeasurementConverterClient.format((double) result.getUpTime(),
- MeasurementUnits.MILLISECONDS, true));
- downField.setValue(MeasurementConverterClient.format(result.getDownPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- downTimeField.setValue(MeasurementConverterClient.format((double) result.getDownTime(),
- MeasurementUnits.MILLISECONDS, true));
- disabledField.setValue(MeasurementConverterClient.format(result.getDisabledPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- disabledTimeField.setValue(MeasurementConverterClient.format((double) result.getDisabledTime(),
- MeasurementUnits.MILLISECONDS, true));
- failureCountField.setValue(result.getFailures());
- disabledCountField.setValue(result.getDisabled());
- mtbfField.setValue(MeasurementConverterClient.format((double) result.getMTBF(),
- MeasurementUnits.MILLISECONDS, true));
- mttrField.setValue(MeasurementConverterClient.format((double) result.getMTTR(),
- MeasurementUnits.MILLISECONDS, true));
-
- if (result.getUnknownTime() > 0L) {
- unknownField.setValue(MSG.view_resource_monitor_availability_unknown(MeasurementConverterClient
- .format((double) result.getUnknownTime(), MeasurementUnits.MILLISECONDS, true)));
- } else {
- unknownField.setValue("");
- }
-
- currentTimeField.setValue(MSG.view_resource_monitor_availability_currentAsOf(TimestampCellFormatter
- .format(result.getCurrentTime())));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- currentField.setValue(MSG.common_label_error());
- CoreGUI.getErrorHandler()
- .handleError(MSG.view_resource_monitor_availability_summaryError(), caught);
- }
- });
- }
-
- private void prepareTooltip(FormItem item, String tooltip) {
- item.setHoverWidth(400);
- item.setPrompt(tooltip);
- }
-
- private Table<ListView.DS> createListView() {
- ListView listView = new ListView(resourceComposite.getResource().getId());
- return listView;
- }
-
- private class ListView extends Table<ListView.DS> {
-
- private DS dataSource;
- private int resourceId;
-
- public ListView(int resourceId) {
- super(null, new SortSpecifier[] { new SortSpecifier("startTime", SortDirection.DESCENDING) });
-
- this.resourceId = resourceId;
-
- setDataSource(getDataSource());
- }
-
- @Override
- public DS getDataSource() {
- if (null == this.dataSource) {
- this.dataSource = new DS(resourceId);
- }
- return this.dataSource;
- }
-
- @Override
- public void refresh() {
- super.refresh();
- reloadSummaryData();
- }
-
- @Override
- protected void configureTableContents(Layout contents) {
- super.configureTableContents(contents);
- setAutoHeight();
- }
-
- @Override
- protected void configureTable() {
- ArrayList<ListGridField> dataSourceFields = getDataSource().getListGridFields();
- getListGrid().setFields(dataSourceFields.toArray(new ListGridField[dataSourceFields.size()]));
-
- super.configureTable();
- }
-
- private class DS extends RPCDataSource<Availability, AvailabilityCriteria> {
-
- public static final String ATTR_ID = "id";
- public static final String ATTR_AVAILABILITY = "availabilityType";
- public static final String ATTR_START_TIME = "startTime";
- public static final String ATTR_END_TIME = "endTime";
-
- public static final String ATTR_DURATION = "duration";
-
- private AvailabilityGWTServiceAsync availService = GWTServiceLookup.getAvailabilityService();
- private int resourceId;
-
- public DS(int resourceId) {
- super();
- this.resourceId = resourceId;
- addDataSourceFields();
- }
-
- /**
- * The view that contains the list grid which will display this datasource's data will call this
- * method to get the field information which is used to control the display of the data.
- *
- * @return list grid fields used to display the datasource data
- */
- public ArrayList<ListGridField> getListGridFields() {
- ArrayList<ListGridField> fields = new ArrayList<ListGridField>(6);
-
- ListGridField startTimeField = new ListGridField(ATTR_START_TIME, MSG.common_title_start());
- startTimeField.setCellFormatter(new TimestampCellFormatter());
- startTimeField.setShowHover(true);
- startTimeField.setHoverCustomizer(TimestampCellFormatter.getHoverCustomizer(ATTR_START_TIME));
- startTimeField.setCanSortClientOnly(true);
- fields.add(startTimeField);
-
- ListGridField endTimeField = new ListGridField(ATTR_END_TIME, MSG.common_title_end());
- endTimeField.setCellFormatter(new TimestampCellFormatter());
- endTimeField.setShowHover(true);
- endTimeField.setHoverCustomizer(TimestampCellFormatter.getHoverCustomizer(ATTR_END_TIME));
- endTimeField.setCanSortClientOnly(true);
- fields.add(endTimeField);
-
- ListGridField durationField = new ListGridField(ATTR_DURATION, MSG.common_title_duration());
- durationField.setAlign(Alignment.RIGHT);
- fields.add(durationField);
-
- ListGridField availabilityField = new ListGridField(ATTR_AVAILABILITY, MSG.common_title_availability());
- availabilityField.setType(ListGridFieldType.IMAGE);
- availabilityField.setAlign(Alignment.CENTER);
- fields.add(availabilityField);
-
- return fields;
- }
-
- @Override
- protected AvailabilityCriteria getFetchCriteria(DSRequest request) {
- AvailabilityCriteria c = new AvailabilityCriteria();
- c.addFilterResourceId(resourceId);
- c.addFilterInitialAvailability(false);
-
- // This code is unlikely to be necessary as the encompassing view should be using an initial
- // sort specifier. But just in case, make sure we set the initial sort. Note that we have to
- // manipulate the PageControl directly as per the restrictions on getFetchCriteria() (see jdoc).
- PageControl pageControl = getPageControl(request);
- if (pageControl.getOrderingFields().isEmpty()) {
- pageControl.initDefaultOrderingField("startTime", PageOrdering.DESC);
- }
-
- return c;
- }
-
- @Override
- protected void executeFetch(final DSRequest request, final DSResponse response,
- AvailabilityCriteria criteria) {
-
- this.availService.findAvailabilityByCriteria(criteria, new AsyncCallback<PageList<Availability>>() {
- public void onFailure(Throwable caught) {
- // TODO fix message
- CoreGUI.getErrorHandler().handleError(MSG.common_label_error(), caught);
- response.setStatus(RPCResponse.STATUS_FAILURE);
- processResponse(request.getRequestId(), response);
- }
-
- public void onSuccess(final PageList<Availability> result) {
- response.setData(buildRecords(result));
- response.setTotalRows(result.size());
- processResponse(request.getRequestId(), response);
- }
- });
- }
-
- @Override
- public Availability copyValues(Record from) {
- return null;
- }
-
- @Override
- public ListGridRecord copyValues(Availability from) {
- ListGridRecord record = new ListGridRecord();
-
- record.setAttribute(ATTR_ID, from.getId());
- record.setAttribute(ATTR_AVAILABILITY,
- ImageManager.getAvailabilityIconFromAvailType(from.getAvailabilityType()));
- record.setAttribute(ATTR_START_TIME, new Date(from.getStartTime()));
- if (null != from.getEndTime()) {
- record.setAttribute(ATTR_END_TIME, new Date(from.getEndTime()));
- long duration = from.getEndTime() - from.getStartTime();
- record.setAttribute(ATTR_DURATION,
- MeasurementConverterClient.format((double) duration, MeasurementUnits.MILLISECONDS, true));
-
- } else {
- record.setAttribute(ATTR_END_TIME, MSG.common_label_none2());
- long duration = System.currentTimeMillis() - from.getStartTime();
- record.setAttribute(ATTR_DURATION,
- MeasurementConverterClient.format((double) duration, MeasurementUnits.MILLISECONDS, true));
-
- }
-
- return record;
- }
- }
- }
-
-}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java
new file mode 100644
index 0000000..ce4f8ef
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/AddToDashboardComponent.java
@@ -0,0 +1,185 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+
+import java.util.LinkedHashMap;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.widgets.IButton;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.toolbar.ToolStrip;
+
+import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.criteria.DashboardCriteria;
+import org.rhq.core.domain.dashboard.Dashboard;
+import org.rhq.core.domain.dashboard.DashboardPortlet;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.util.PageList;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.dashboard.portlets.inventory.resource.graph.ResourceD3GraphPortlet;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.enhanced.Enhanced;
+import org.rhq.enterprise.gui.coregui.client.util.message.Message;
+
+/**
+ * @author Mike Thompson
+ */
+public class AddToDashboardComponent extends ToolStrip implements Enhanced {
+ final private Resource resource;
+ private SelectItem dashboardSelectItem;
+ private Dashboard selectedDashboard;
+ private IButton addToDashboardButton;
+ private LinkedHashMap<String, String> dashboardMenuMap;
+ private LinkedHashMap<Integer, Dashboard> dashboardMap;
+ private MetricsTableView.MetricsTableListGrid metricsListGrid;
+
+ public AddToDashboardComponent(Resource resource) {
+ this.resource = resource;
+ setPadding(5);
+ setMembersMargin(15);
+ setWidth(300);
+ dashboardMenuMap = new LinkedHashMap<String, String>();
+ dashboardMap = new LinkedHashMap<Integer, Dashboard>();
+ createToolstrip();
+ }
+
+ @Override
+ protected void onDraw() {
+ super.onDraw();
+ removeMembers(getMembers());
+ createToolstrip();
+ }
+
+ private void createToolstrip() {
+ addSpacer(15);
+ dashboardSelectItem = new SelectItem();
+ addToDashboardButton = new IButton(MSG.view_metric_addToDashboard());
+ addToDashboardButton.disable();
+
+ dashboardSelectItem = new SelectItem();
+ dashboardSelectItem.setTitle("Dashboards");
+ dashboardSelectItem.setWidth(300);
+ dashboardSelectItem.setPickListWidth(210);
+ populateDashboardMenu();
+ addFormItem(dashboardSelectItem);
+ addMember(addToDashboardButton);
+
+ dashboardSelectItem.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(ChangeEvent changeEvent) {
+ Integer selectedDashboardId = Integer.valueOf((String) changeEvent.getValue());
+ selectedDashboard = dashboardMap.get(selectedDashboardId);
+ }
+ });
+ addToDashboardButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
+ @Override
+ public void onClick(ClickEvent clickEvent) {
+ ListGridRecord[] selectedRecords = metricsListGrid.getSelectedRecords();
+ for (ListGridRecord selectedRecord : selectedRecords) {
+ for (MeasurementDefinition measurementDefinition : resource.getResourceType()
+ .getMetricDefinitions()) {
+ if (measurementDefinition.getId() == selectedRecord
+ .getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID)) {
+ Log.debug("Add to Dashboard -- Storing: " + measurementDefinition.getDisplayName()
+ + " in " + selectedDashboard.getName());
+ storeDashboardMetric(selectedDashboard, resource, measurementDefinition);
+ break;
+ }
+ }
+ }
+ }
+ });
+ }
+
+ public void disableAddToDashboardButton(){
+ addToDashboardButton.disable();
+ }
+
+ public void enableAddToDashboardButton(){
+ addToDashboardButton.enable();
+ }
+
+
+ public void populateDashboardMenu() {
+ dashboardMenuMap.clear();
+ dashboardMap.clear();
+
+ DashboardCriteria criteria = new DashboardCriteria();
+ GWTServiceLookup.getDashboardService().findDashboardsByCriteria(criteria,
+ new AsyncCallback<PageList<Dashboard>>() {
+
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_loadFailed_dashboard(),
+ caught);
+ }
+
+ public void onSuccess(PageList<Dashboard> dashboards) {
+ for (final Dashboard dashboard : dashboards) {
+ dashboardMenuMap.put(String.valueOf(dashboard.getId()),
+ MSG.view_tree_common_contextMenu_addChartToDashboard(dashboard.getName()));
+ dashboardMap.put(dashboard.getId(), dashboard);
+ }
+ selectedDashboard = dashboards.get(0);
+ dashboardSelectItem.setValueMap(dashboardMenuMap);
+ dashboardSelectItem.setValue(selectedDashboard.getId());
+ }
+ });
+ }
+
+ /**
+ * The metrics list grid is not available on object creation so we must attach later after it has been initialized.
+ * @param metricsListGrid
+ */
+ public void setMetricsListGrid(MetricsTableView.MetricsTableListGrid metricsListGrid) {
+ this.metricsListGrid = metricsListGrid;
+ }
+
+
+ private void storeDashboardMetric(Dashboard dashboard, Resource resource, MeasurementDefinition definition) {
+ DashboardPortlet dashboardPortlet = new DashboardPortlet(MSG.view_tree_common_contextMenu_resourceGraph(),
+ ResourceD3GraphPortlet.KEY, 250);
+ dashboardPortlet.getConfiguration().put(
+ new PropertySimple(ResourceD3GraphPortlet.CFG_RESOURCE_ID, resource.getId()));
+ dashboardPortlet.getConfiguration().put(
+ new PropertySimple(ResourceD3GraphPortlet.CFG_DEFINITION_ID, definition.getId()));
+
+ dashboard.addPortlet(dashboardPortlet);
+
+ GWTServiceLookup.getDashboardService().storeDashboard(dashboard, new AsyncCallback<Dashboard>() {
+
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_saveChartToDashboardFailure(),
+ caught);
+ }
+
+ public void onSuccess(Dashboard result) {
+ CoreGUI.getMessageCenter().notify(
+ new Message(MSG.view_tree_common_contextMenu_saveChartToDashboardSuccessful(result.getName()),
+ Message.Severity.Info));
+ }
+ });
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java
deleted file mode 100644
index 7022648..0000000
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MeasurementTableView.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.TreeSet;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.smartgwt.client.types.SelectionStyle;
-import com.smartgwt.client.widgets.Window;
-import com.smartgwt.client.widgets.events.CloseClickEvent;
-import com.smartgwt.client.widgets.events.CloseClickHandler;
-import com.smartgwt.client.widgets.grid.ListGrid;
-import com.smartgwt.client.widgets.grid.ListGridField;
-import com.smartgwt.client.widgets.grid.ListGridRecord;
-
-import org.rhq.core.domain.criteria.ResourceCriteria;
-import org.rhq.core.domain.measurement.MeasurementData;
-import org.rhq.core.domain.measurement.MeasurementUnits;
-import org.rhq.core.domain.resource.composite.ResourceComposite;
-import org.rhq.core.domain.util.PageList;
-import org.rhq.enterprise.gui.coregui.client.CoreGUI;
-import org.rhq.enterprise.gui.coregui.client.components.measurement.UserPreferencesMeasurementRangeEditor;
-import org.rhq.enterprise.gui.coregui.client.components.table.Table;
-import org.rhq.enterprise.gui.coregui.client.components.table.TableAction;
-import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
-import org.rhq.enterprise.gui.coregui.client.inventory.InventoryView;
-import org.rhq.enterprise.gui.coregui.client.inventory.common.detail.summary.AbstractActivityView.ChartViewWindow;
-import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.D3GraphListView;
-import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
-import org.rhq.enterprise.gui.coregui.client.util.message.Message;
-
-/**
- * Views a resource's measurements in a tabular view.
- *
- * @author John Mazzitelli
- */
-public class MeasurementTableView extends Table<MetricsTableDataSource> {
-
- private final int resourceId;
-
- public MeasurementTableView(int resourceId) {
- super();
- this.resourceId = resourceId;
- setDataSource(new MetricsTableDataSource(resourceId));
- }
-
- protected void configureTable() {
- ArrayList<ListGridField> fields = getDataSource().getListGridFields();
- setListGridFields(fields.toArray(new ListGridField[0]));
- addExtraWidget(new UserPreferencesMeasurementRangeEditor(), true);
- addTableAction(MSG.view_measureTable_getLive(), new TableAction() {
- @Override
- public boolean isEnabled(ListGridRecord[] selection) {
- return selection != null && selection.length > 0;
- }
-
- @Override
- public void executeAction(ListGridRecord[] selection, Object actionValue) {
- if (selection == null || selection.length == 0) {
- return;
- }
- // keyed on metric name - string[0] is the metric label, [1] is the units
- final HashMap<String, String[]> scheduleNamesAndUnits = new HashMap<String, String[]>();
- int[] definitionIds = new int[selection.length];
- int i = 0;
- for (ListGridRecord record : selection) {
- Integer defId = record.getAttributeAsInt(MetricsTableDataSource.FIELD_METRIC_DEF_ID);
- definitionIds[i++] = defId.intValue();
-
- String name = record.getAttribute(MetricsTableDataSource.FIELD_METRIC_NAME);
- String label = record.getAttribute(MetricsTableDataSource.FIELD_METRIC_LABEL);
- String units = record.getAttribute(MetricsTableDataSource.FIELD_METRIC_UNITS);
- if (units == null || units.length() < 1) {
- units = MeasurementUnits.NONE.name();
- }
-
- scheduleNamesAndUnits.put(name, new String[] { label, units });
- }
-
- // actually go out and ask the agents for the data
- GWTServiceLookup.getMeasurementDataService(60000).findLiveData(resourceId, definitionIds,
- new AsyncCallback<Set<MeasurementData>>() {
- @Override
- public void onSuccess(Set<MeasurementData> result) {
- if (result == null) {
- result = new HashSet<MeasurementData>(0);
- }
- ArrayList<ListGridRecord> records = new ArrayList<ListGridRecord>(result.size());
- for (MeasurementData data : result) {
- String[] nameAndUnits = scheduleNamesAndUnits.get(data.getName());
- if (nameAndUnits != null) {
- double doubleValue;
- if (data.getValue() instanceof Number) {
- doubleValue = ((Number) data.getValue()).doubleValue();
- } else {
- doubleValue = Double.parseDouble(data.getValue().toString());
- }
- String value = MeasurementConverterClient.formatToSignificantPrecision(
- new double[] { doubleValue }, MeasurementUnits.valueOf(nameAndUnits[1]), true)[0];
-
- ListGridRecord record = new ListGridRecord();
- record.setAttribute("name", nameAndUnits[0]);
- record.setAttribute("value", value);
- records.add(record);
- }
- }
- Collections.sort(records, new Comparator<ListGridRecord>() {
- public int compare(ListGridRecord o1, ListGridRecord o2) {
- return o1.getAttribute("name").compareTo(o2.getAttribute("name"));
- }
- });
- showLiveData(records);
- }
-
- @Override
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError(MSG.view_measureTable_getLive_failure(), caught);
- }
- });
- }
- });
-
-
- addTableAction(MSG.view_measureTable_chartMetricValues(), new TableAction() {
- @Override
- public boolean isEnabled(ListGridRecord[] selection) {
- return selection != null && selection.length > 0;
- }
-
- @Override
- public void executeAction(ListGridRecord[] selection, Object actionValue) {
- if (selection == null || selection.length == 0) {
- return;
- }
- final TreeSet<Integer> definitionIds = new TreeSet<Integer>();
- for (ListGridRecord record : selection) {
- Integer defId = record.getAttributeAsInt(MetricsTableDataSource.FIELD_METRIC_DEF_ID);
- definitionIds.add(defId);
- }
-
- ResourceCriteria criteria = new ResourceCriteria();
- criteria.addFilterId(resourceId);
- criteria.fetchSchedules(true);
- GWTServiceLookup.getResourceService().findResourceCompositesByCriteria(criteria,
- new AsyncCallback<PageList<ResourceComposite>>() {
-
- @Override
- public void onFailure(Throwable caught) {
- CoreGUI.getMessageCenter().notify(
- new Message(MSG.view_inventory_resource_loadFailed(String.valueOf(resourceId)),
- Message.Severity.Warning));
-
- CoreGUI.goToView(InventoryView.VIEW_ID.getName());
- }
-
- @Override
- public void onSuccess(PageList<ResourceComposite> result) {
- if (result.isEmpty()) {
- onFailure(new Exception(MSG.view_inventory_resource_loadFailed(String
- .valueOf(resourceId))));
- } else {
- final ResourceComposite resourceComposite = result.get(0);
-
- ChartViewWindow window = new ChartViewWindow("");
- final D3GraphListView graphListView = D3GraphListView.createMultipleGraphs(
- resourceComposite.getResource(), definitionIds, true);
-
- window.addItem(graphListView);
- window.show();
- refreshTableInfo();
-
- }
- }
- });
-
- }
- });
- }
-
- private void showLiveData(ArrayList<ListGridRecord> records) {
- final Window liveDataWindow = new Window();
- liveDataWindow.setTitle(MSG.view_measureTable_live_title());
- liveDataWindow.setShowModalMask(true);
- liveDataWindow.setShowMinimizeButton(false);
- liveDataWindow.setShowMaximizeButton(true);
- liveDataWindow.setShowCloseButton(true);
- liveDataWindow.setShowResizer(true);
- liveDataWindow.setCanDragResize(true);
- liveDataWindow.setDismissOnEscape(true);
- liveDataWindow.setIsModal(true);
- liveDataWindow.setWidth(700);
- liveDataWindow.setHeight(425);
- liveDataWindow.setAutoCenter(true);
- liveDataWindow.centerInPage();
- liveDataWindow.addCloseClickHandler(new CloseClickHandler() {
- @Override
- public void onCloseClick(CloseClickEvent event) {
- liveDataWindow.destroy();
- refreshTableInfo();
- }
- });
-
- ListGrid liveDataGrid = new ListGrid();
- liveDataGrid.setShowAllRecords(true);
- liveDataGrid.setData(records.toArray(new ListGridRecord[records.size()]));
- liveDataGrid.setSelectionType(SelectionStyle.NONE);
- ListGridField name = new ListGridField("name", MSG.common_title_metric());
- ListGridField value = new ListGridField("value", MSG.common_title_value());
- liveDataGrid.setFields(name, value);
-
- liveDataWindow.addItem(liveDataGrid);
- liveDataWindow.show();
- }
-}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java
new file mode 100644
index 0000000..bcea825
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java
@@ -0,0 +1,167 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+
+import java.util.List;
+
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.Overflow;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+
+import org.rhq.core.domain.common.EntityContext;
+import org.rhq.core.domain.measurement.Availability;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.IconEnum;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractD3GraphListView;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
+import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
+import org.rhq.enterprise.gui.coregui.client.util.BrowserUtility;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
+import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedHLayout;
+
+/**
+ * The consolidated metrics view showing metric graphs and availability data both in graphical and tabular form.
+ *
+ * @author Mike Thompson
+ */
+public class MetricsResourceView extends AbstractD3GraphListView {
+
+ private static final String COLLAPSED_TOOLTIP = MSG.chart_metrics_collapse_tooltip();
+ private static final String EXPANDED_TOOLTIP = MSG.chart_metrics_expand_tooltip();
+
+ private final Resource resource;
+ private Img expandCollapseArrow;
+ private final MetricsTableView metricsTableView;
+ private final ResourceMetricAvailabilityView availabilityDetails;
+
+ public MetricsResourceView(Resource resource) {
+ super();
+ setOverflow(Overflow.AUTO);
+ setWidth100();
+ setHeight100();
+ this.resource = resource;
+ metricsTableView = new MetricsTableView(resource, this);
+ availabilityDetails = new ResourceMetricAvailabilityView(resource);
+ }
+
+
+ public void redrawGraphs() {
+ this.onDraw();
+ }
+
+ public void refreshGraphs(){
+ new Timer() {
+ @Override
+ public void run() {
+ availabilityGraph.drawJsniChart();
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+
+ @Override
+ protected void onDraw() {
+ super.onDraw();
+ Log.debug("MetricResourceView.onDraw() for: " + resource.getName() + " id: " + resource.getId());
+ destroyMembers();
+
+
+ addMember(buttonBarDateTimeRangeEditor);
+
+ availabilityGraph = new AvailabilityD3GraphView<AvailabilityOverUnderGraphType>(
+ new AvailabilityOverUnderGraphType(resource.getId()));
+
+ EnhancedHLayout expandCollapseHLayout = new EnhancedHLayout();
+
+ //add expand/collapse icon
+ expandCollapseArrow = new Img(IconEnum.COLLAPSED_ICON.getIcon16x16Path(), 16, 16);
+ expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP);
+ expandCollapseArrow.setLayoutAlign(VerticalAlignment.BOTTOM);
+ expandCollapseArrow.addClickHandler(new ClickHandler() {
+ private boolean collapsed = true;
+
+ @Override
+ public void onClick(ClickEvent event) {
+ collapsed = !collapsed;
+ if (collapsed) {
+ expandCollapseArrow.setSrc(IconEnum.COLLAPSED_ICON.getIcon16x16Path());
+ expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP);
+ availabilityDetails.hide();
+ } else {
+ expandCollapseArrow.setSrc(IconEnum.EXPANDED_ICON.getIcon16x16Path());
+ expandCollapseArrow.setTooltip(EXPANDED_TOOLTIP);
+ availabilityDetails.show();
+
+ }
+ refreshGraphs();
+ }
+ });
+
+
+ expandCollapseHLayout.addMember(expandCollapseArrow);
+ expandCollapseHLayout.addMember(availabilityGraph);
+ addMember(expandCollapseHLayout);
+
+ availabilityDetails.hide();
+ addMember(availabilityDetails);
+
+ metricsTableView.setHeight100();
+ addMember(metricsTableView);
+
+
+ queryAvailability(EntityContext.forResource(resource.getId()), buttonBarDateTimeRangeEditor.getStartTime(),
+ buttonBarDateTimeRangeEditor.getEndTime(), null);
+ }
+
+ @Override
+ protected void queryAvailability(final EntityContext context, Long startTime, Long endTime, CountDownLatch notUsed ) {
+
+ final long timerStart = System.currentTimeMillis();
+
+ // now return the availability
+ GWTServiceLookup.getAvailabilityService().getAvailabilitiesForResource(context.getResourceId(), startTime,
+ endTime, new AsyncCallback<List<Availability>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_availability_loadFailed(), caught);
+ }
+
+ @Override
+ public void onSuccess(List<Availability> availList) {
+ Log.debug("\nSuccessfully queried availability in: " + (System.currentTimeMillis() - timerStart)
+ + " ms.");
+ availabilityGraph.setAvailabilityList(availList);
+ new Timer() {
+ @Override
+ public void run() {
+ availabilityGraph.drawJsniChart();
+ }
+ }.schedule(150);
+ }
+ });
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableDataSource.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableDataSource.java
index 318166d..57e62ab 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableDataSource.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableDataSource.java
@@ -1,7 +1,13 @@
package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
@@ -11,18 +17,28 @@ import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.criteria.Criteria;
+import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementSchedule;
+import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
import org.rhq.core.domain.measurement.ui.MetricDisplayValue;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.domain.resource.composite.ResourceComposite;
+import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.enterprise.gui.coregui.client.util.BrowserUtility;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
import org.rhq.enterprise.gui.coregui.client.util.MeasurementUtility;
import org.rhq.enterprise.gui.coregui.client.util.RPCDataSource;
+import org.rhq.enterprise.gui.coregui.client.util.async.Command;
+import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
-import org.rhq.enterprise.gui.coregui.client.util.preferences.UserPreferences;
/**
* A simple data source to read in metric data summaries for a resource.
@@ -31,9 +47,13 @@ import org.rhq.enterprise.gui.coregui.client.util.preferences.UserPreferences;
* we just load them all in at once.
*
* @author John Mazzitelli
+ * @author Mike Thompson
+ * @todo: get rid of this once we have tested the new screen out
*/
+@Deprecated
public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary, Criteria> {
+ public static final String FIELD_SPARKLINE = "sparkline";
public static final String FIELD_METRIC_LABEL = "label";
public static final String FIELD_ALERT_COUNT = "alertCount";
public static final String FIELD_MIN_VALUE = "min";
@@ -44,11 +64,15 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
public static final String FIELD_METRIC_SCHED_ID = "schedId";
public static final String FIELD_METRIC_UNITS = "units";
public static final String FIELD_METRIC_NAME = "name";
-
+ public static final String FIELD_RESOURCE_ID = "resourceId";
private int resourceId;
+ private List<MetricDisplaySummary> metricDisplaySummaries;
+ private List<List<MeasurementDataNumericHighLowComposite>> metricsDataList;
+ private MeasurementUserPreferences measurementUserPrefs;
public MetricsTableDataSource(int resourceId) {
this.resourceId = resourceId;
+ measurementUserPrefs = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
}
/**
@@ -58,7 +82,25 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
* @return list grid fields used to display the datasource data
*/
public ArrayList<ListGridField> getListGridFields() {
- ArrayList<ListGridField> fields = new ArrayList<ListGridField>(6);
+ ArrayList<ListGridField> fields = new ArrayList<ListGridField>(7);
+
+ ListGridField sparklineField = new ListGridField(FIELD_SPARKLINE, "chart");
+ sparklineField.setCellFormatter(new CellFormatter() {
+ @Override
+ public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
+ if (value == null) {
+ return "";
+ }
+ String contents = "<span id='sparkline_" + resourceId + "-"
+ + record.getAttributeAsInt(FIELD_METRIC_DEF_ID) + "' class='dynamicsparkline' width='70' "
+ + "values='" + record.getAttribute(FIELD_SPARKLINE) + "'></span>";
+ return contents;
+
+ }
+ });
+
+ sparklineField.setWidth(80);
+ fields.add(sparklineField);
ListGridField nameField = new ListGridField(FIELD_METRIC_LABEL, MSG.common_title_name());
nameField.setWidth("30%");
@@ -101,6 +143,7 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
MeasurementUtility.formatSimpleMetrics(from);
ListGridRecord record = new ListGridRecord();
+ record.setAttribute(FIELD_SPARKLINE, getCsvMetricsForSparkline());
record.setAttribute(FIELD_METRIC_LABEL, from.getLabel());
record.setAttribute(FIELD_ALERT_COUNT, String.valueOf(from.getAlertCount()));
record.setAttribute(FIELD_MIN_VALUE, getMetricStringValue(from.getMinMetric()));
@@ -111,9 +154,32 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
record.setAttribute(FIELD_METRIC_SCHED_ID, from.getScheduleId());
record.setAttribute(FIELD_METRIC_UNITS, from.getUnits());
record.setAttribute(FIELD_METRIC_NAME, from.getMetricName());
+ record.setAttribute(FIELD_RESOURCE_ID, resourceId);
return record;
}
+ private String getCsvMetricsForSparkline() {
+ StringBuilder sb = new StringBuilder();
+ Log.debug("getCsvMetricsForSparkline.metricsDataList: " + metricsDataList.size());
+ for (List<MeasurementDataNumericHighLowComposite> measurementData : metricsDataList) {
+ for (int i = 0; i < measurementData.size(); i++) {
+ // take the last 20 values
+ if (i >= measurementData.size() - 20) {
+ if (!Double.isNaN(measurementData.get(i).getValue())) {
+ sb.append((int) measurementData.get(i).getValue());
+ sb.append(",");
+ }
+ }
+ }
+ if (sb.toString().endsWith(",")) {
+ sb.setLength(sb.length() - 1);
+ }
+ }
+ Log.debug("getCsvMetricsForSparkline: " + sb.toString());
+
+ return sb.toString();
+ }
+
protected String getMetricStringValue(MetricDisplayValue value) {
return (value != null) ? value.toString() : "";
}
@@ -127,34 +193,54 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
@Override
protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
- // see MetricsTableUIBean for the old JSF class to see where this came from
-
GWTServiceLookup.getMeasurementScheduleService().findSchedulesForResourceAndType(resourceId,
DataType.MEASUREMENT, null, true, new AsyncCallback<ArrayList<MeasurementSchedule>>() {
@Override
- public void onSuccess(ArrayList<MeasurementSchedule> result) {
- int[] schedIds = new int[result.size()];
+ public void onSuccess(ArrayList<MeasurementSchedule> measurementSchedules) {
+ int[] scheduleIds = new int[measurementSchedules.size()];
int i = 0;
- for (MeasurementSchedule measurementSchedule : result) {
- schedIds[i++] = measurementSchedule.getId();
+ for (MeasurementSchedule measurementSchedule : measurementSchedules) {
+ scheduleIds[i++] = measurementSchedule.getId();
}
- UserPreferences prefs = UserSessionManager.getUserPreferences();
- MeasurementUserPreferences mprefs = new MeasurementUserPreferences(prefs);
- ArrayList<Long> range = mprefs.getMetricRangePreferences().getBeginEndTimes();
+ final CountDownLatch countDownLatch = CountDownLatch.create(2, new Command() {
+
+ @Override
+ public void execute() {
+ response.setData(buildRecords(metricDisplaySummaries));
+ processResponse(request.getRequestId(), response);
+
+ new Timer() {
+
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ Log.debug("*** Finished CountdownLatch for metrics loaded: " + metricsDataList.size());
+ }
+ });
+
+ retrieveResourceMetrics(resourceId, countDownLatch);
+
GWTServiceLookup.getMeasurementChartsService().getMetricDisplaySummariesForResource(resourceId,
- schedIds, range.get(0), range.get(1), new AsyncCallback<ArrayList<MetricDisplaySummary>>() {
- @Override
- public void onSuccess(ArrayList<MetricDisplaySummary> result) {
- response.setData(buildRecords(result));
- processResponse(request.getRequestId(), response);
- }
+ scheduleIds, measurementUserPrefs.getMetricRangePreferences().begin,
+ measurementUserPrefs.getMetricRangePreferences().end,
+ new AsyncCallback<ArrayList<MetricDisplaySummary>>() {
+ @Override
+ public void onSuccess(ArrayList<MetricDisplaySummary> metricDisplaySummaries) {
+ setMetricDisplaySummaries(metricDisplaySummaries);
+ countDownLatch.countDown();
+ }
- @Override
- public void onFailure(Throwable caught) {
- CoreGUI.getErrorHandler().handleError("Cannot load metrics", caught);
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError("Cannot load metrics", caught);
+ countDownLatch.countDown();
+ }
}
- });
+
+ );
}
@Override
@@ -163,4 +249,87 @@ public class MetricsTableDataSource extends RPCDataSource<MetricDisplaySummary,
}
});
}
+
+ void setMetricDisplaySummaries(List<MetricDisplaySummary> metricDisplaySummaries) {
+ this.metricDisplaySummaries = metricDisplaySummaries;
+ }
+
+ public void retrieveResourceMetrics(final Integer resourceId, final CountDownLatch countDownLatch) {
+
+ ResourceCriteria criteria = new ResourceCriteria();
+ criteria.addFilterId(resourceId);
+
+ //locate the resource
+ GWTServiceLookup.getResourceService().findResourceCompositesByCriteria(criteria,
+ new AsyncCallback<PageList<ResourceComposite>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.debug("Error retrieving resource resource composite for resource [" + resourceId + "]:"
+ + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(PageList<ResourceComposite> resourceCompositePageList) {
+ if (!resourceCompositePageList.isEmpty()) {
+ final ResourceComposite resourceComposite = resourceCompositePageList.get(0);
+ final Resource resource = resourceComposite.getResource();
+ // Load the fully fetched ResourceType.
+ ResourceType resourceType = resource.getResourceType();
+ ResourceTypeRepository.Cache.getInstance().getResourceTypes(resourceType.getId(),
+ EnumSet.of(ResourceTypeRepository.MetadataType.measurements),
+ new ResourceTypeRepository.TypeLoadedCallback() {
+ public void onTypesLoaded(ResourceType type) {
+ resource.setResourceType(type);
+ //metric definitions
+ Set<MeasurementDefinition> definitions = type.getMetricDefinitions();
+
+ //build id mapping for measurementDefinition instances Ex. Free Memory -> MeasurementDefinition[100071]
+ final HashMap<String, MeasurementDefinition> measurementDefMap = new HashMap<String, MeasurementDefinition>();
+ for (MeasurementDefinition definition : definitions) {
+ measurementDefMap.put(definition.getDisplayName(), definition);
+ }
+ //bundle definition ids for asynch call.
+ int[] definitionArrayIds = new int[definitions.size()];
+ final String[] displayOrder = new String[definitions.size()];
+ measurementDefMap.keySet().toArray(displayOrder);
+ //sort the charting data ex. Free Memory, Free Swap Space,..System Load
+ Arrays.sort(displayOrder);
+
+ //organize definitionArrayIds for ordered request on server.
+ int index = 0;
+ for (String definitionToDisplay : displayOrder) {
+ definitionArrayIds[index++] = measurementDefMap.get(definitionToDisplay)
+ .getId();
+ }
+
+ GWTServiceLookup.getMeasurementDataService().findDataForResource(resourceId,
+ definitionArrayIds, measurementUserPrefs.getMetricRangePreferences().begin,
+ measurementUserPrefs.getMetricRangePreferences().end, 60,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.warn("Error retrieving recent metrics charting data for resource ["
+ + resourceId + "]:" + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(
+ List<List<MeasurementDataNumericHighLowComposite>> measurementDataList) {
+
+ if (!measurementDataList.isEmpty()) {
+ metricsDataList = measurementDataList;
+ Log.debug("*** Setting metricsDataList.size: "
+ + metricsDataList.size());
+ countDownLatch.countDown();
+ }
+ }
+ });
+
+ }
+ });
+ }
+ }
+ });
+
+ }
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
new file mode 100644
index 0000000..09a7bc3
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
@@ -0,0 +1,385 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.ExpansionMode;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.HTMLFlow;
+import com.smartgwt.client.widgets.Window;
+import com.smartgwt.client.widgets.events.CloseClickEvent;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.DataArrivedEvent;
+import com.smartgwt.client.widgets.grid.events.DataArrivedHandler;
+import com.smartgwt.client.widgets.grid.events.RecordCollapseEvent;
+import com.smartgwt.client.widgets.grid.events.RecordCollapseHandler;
+import com.smartgwt.client.widgets.grid.events.RecordExpandEvent;
+import com.smartgwt.client.widgets.grid.events.RecordExpandHandler;
+import com.smartgwt.client.widgets.grid.events.SelectionChangedHandler;
+import com.smartgwt.client.widgets.grid.events.SelectionEvent;
+import com.smartgwt.client.widgets.grid.events.SortChangedHandler;
+import com.smartgwt.client.widgets.grid.events.SortEvent;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementUnits;
+import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
+import org.rhq.enterprise.gui.coregui.client.components.table.Table;
+import org.rhq.enterprise.gui.coregui.client.components.table.TableAction;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.AbstractD3GraphListView;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.MetricGraphData;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.RedrawGraphs;
+import org.rhq.enterprise.gui.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
+import org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.MetricD3Graph;
+import org.rhq.enterprise.gui.coregui.client.util.BrowserUtility;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
+import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
+
+/**
+ * Views a resource's metrics in a tabular view with sparkline graph and optional detailed d3 graph.
+ *
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class MetricsTableView extends Table<MetricsViewDataSource> implements RedrawGraphs {
+
+ private final Resource resource;
+ private final AbstractD3GraphListView abstractD3GraphListView;
+
+ private final MeasurementUserPreferences measurementUserPrefs;
+ private final AddToDashboardComponent addToDashboardComponent;
+ private MetricsTableListGrid metricsTableListGrid;
+
+ Set<Integer> expandedRows = new HashSet<Integer>();
+
+ public MetricsTableView(Resource resource, AbstractD3GraphListView abstractD3GraphListView) {
+ super();
+ this.resource = resource;
+ this.abstractD3GraphListView = abstractD3GraphListView;
+ measurementUserPrefs = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
+ setDataSource(new MetricsViewDataSource(resource));
+ addToDashboardComponent = new AddToDashboardComponent(resource);
+ }
+
+ /**
+ * Creates this Table's list grid (called by onInit()). Subclasses can override this if they require a custom
+ * subclass of ListGrid.
+ *
+ * @return this Table's list grid (must be an instance of ListGrid)
+ */
+ @Override
+ protected ListGrid createListGrid() {
+ metricsTableListGrid = new MetricsTableListGrid(this, resource);
+ addToDashboardComponent.setMetricsListGrid(metricsTableListGrid);
+ return metricsTableListGrid;
+ }
+
+ protected void configureTable() {
+ ArrayList<ListGridField> fields = getDataSource().getListGridFields();
+ setListGridFields(fields.toArray(new ListGridField[0]));
+
+ addTableAction(MSG.view_measureTable_getLive(), new ShowLiveDataTableAction(this));
+ addExtraWidget(addToDashboardComponent, false);
+ addToDashboardComponent.disableAddToDashboardButton();
+ metricsTableListGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
+ @Override
+ public void onSelectionChanged(SelectionEvent selectionEvent) {
+ if(metricsTableListGrid.getSelectedRecords().length > 0){
+ addToDashboardComponent.enableAddToDashboardButton();
+ }else {
+ addToDashboardComponent.disableAddToDashboardButton();
+ }
+ }
+ });
+ }
+
+ private static class ShowLiveDataTableAction implements TableAction {
+ private MetricsTableView metricsTableView;
+
+ public ShowLiveDataTableAction(MetricsTableView metricsTableView) {
+ this.metricsTableView = metricsTableView;
+ }
+
+ @Override
+ public boolean isEnabled(ListGridRecord[] selection) {
+ return selection != null && selection.length > 0;
+ }
+
+ @Override
+ public void executeAction(ListGridRecord[] selection, Object actionValue) {
+ if (selection == null || selection.length == 0) {
+ return;
+ }
+ // keyed on metric name - string[0] is the metric label, [1] is the units
+ final HashMap<String, String[]> scheduleNamesAndUnits = new HashMap<String, String[]>();
+ int[] definitionIds = new int[selection.length];
+ int i = 0;
+ for (ListGridRecord record : selection) {
+ Integer defId = record.getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
+ definitionIds[i++] = defId;
+
+ String name = record.getAttribute(MetricsViewDataSource.FIELD_METRIC_NAME);
+ String label = record.getAttribute(MetricsViewDataSource.FIELD_METRIC_LABEL);
+ String units = record.getAttribute(MetricsViewDataSource.FIELD_METRIC_UNITS);
+ if (units == null || units.length() < 1) {
+ units = MeasurementUnits.NONE.name();
+ }
+
+ scheduleNamesAndUnits.put(name, new String[] { label, units });
+ }
+
+ // actually go out and ask the agents for the data
+ GWTServiceLookup.getMeasurementDataService(60000).findLiveData(metricsTableView.resource.getId(),
+ definitionIds, new AsyncCallback<Set<MeasurementData>>() {
+ @Override
+ public void onSuccess(Set<MeasurementData> result) {
+ if (result == null) {
+ result = new HashSet<MeasurementData>(0);
+ }
+ ArrayList<ListGridRecord> records = new ArrayList<ListGridRecord>(result.size());
+ for (MeasurementData data : result) {
+ String[] nameAndUnits = scheduleNamesAndUnits.get(data.getName());
+ if (nameAndUnits != null) {
+ double doubleValue;
+ if (data.getValue() instanceof Number) {
+ doubleValue = ((Number) data.getValue()).doubleValue();
+ } else {
+ doubleValue = Double.parseDouble(data.getValue().toString());
+ }
+ String value = MeasurementConverterClient.formatToSignificantPrecision(
+ new double[] { doubleValue }, MeasurementUnits.valueOf(nameAndUnits[1]), true)[0];
+
+ ListGridRecord record = new ListGridRecord();
+ record.setAttribute("name", nameAndUnits[0]);
+ record.setAttribute("value", value);
+ records.add(record);
+ }
+ }
+ Collections.sort(records, new Comparator<ListGridRecord>() {
+ public int compare(ListGridRecord o1, ListGridRecord o2) {
+ return o1.getAttribute("name").compareTo(o2.getAttribute("name"));
+ }
+ });
+ showLiveData(records);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_measureTable_getLive_failure(), caught);
+ }
+ });
+ }
+
+ private void showLiveData(ArrayList<ListGridRecord> records) {
+ final Window liveDataWindow = new Window();
+ liveDataWindow.setTitle(MSG.view_measureTable_live_title());
+ liveDataWindow.setShowModalMask(true);
+ liveDataWindow.setShowMinimizeButton(false);
+ liveDataWindow.setShowMaximizeButton(true);
+ liveDataWindow.setShowCloseButton(true);
+ liveDataWindow.setShowResizer(true);
+ liveDataWindow.setCanDragResize(true);
+ liveDataWindow.setDismissOnEscape(true);
+ liveDataWindow.setIsModal(true);
+ liveDataWindow.setWidth(700);
+ liveDataWindow.setHeight(425);
+ liveDataWindow.setAutoCenter(true);
+ liveDataWindow.centerInPage();
+ liveDataWindow.addCloseClickHandler(new CloseClickHandler() {
+ @Override
+ public void onCloseClick(CloseClickEvent event) {
+ liveDataWindow.destroy();
+ metricsTableView.refreshTableInfo();
+ }
+ });
+
+ ListGrid liveDataGrid = new ListGrid();
+ liveDataGrid.setShowAllRecords(true);
+ liveDataGrid.setData(records.toArray(new ListGridRecord[records.size()]));
+ liveDataGrid.setSelectionType(SelectionStyle.NONE);
+ ListGridField name = new ListGridField("name", MSG.common_title_metric());
+ ListGridField value = new ListGridField("value", MSG.common_title_value());
+ liveDataGrid.setFields(name, value);
+
+ liveDataWindow.addItem(liveDataGrid);
+ liveDataWindow.show();
+ }
+
+ }
+
+ @Override
+ /**
+ * Redraw Graphs in this context means to refresh the table and redraw open graphs.
+ */
+ public void redrawGraphs() {
+ Log.debug("MetricsView.redrawGraphs.");
+
+ new Timer() {
+
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+
+ }
+
+ public class MetricsTableListGrid extends ListGrid {
+
+ private static final int TREEVIEW_DETAIL_CHART_HEIGHT = 205;
+ private static final int NUM_METRIC_POINTS = 60;
+ private Resource resource;
+ final MetricsTableView metricsTableView;
+
+ public MetricsTableListGrid(final MetricsTableView metricsTableView, final Resource resource) {
+ super();
+ this.resource = resource;
+ this.metricsTableView = metricsTableView;
+ setCanExpandRecords(true);
+ setCanExpandMultipleRecords(true);
+ setExpansionMode(ExpansionMode.DETAIL_FIELD);
+
+ addRecordExpandHandler(new RecordExpandHandler() {
+ @Override
+ public void onRecordExpand(RecordExpandEvent recordExpandEvent) {
+ metricsTableView.expandedRows.add(recordExpandEvent.getRecord().getAttributeAsInt(
+ MetricsViewDataSource.FIELD_METRIC_DEF_ID));
+ redrawGraphs();
+ }
+
+ });
+ addRecordCollapseHandler(new RecordCollapseHandler() {
+ @Override
+ public void onRecordCollapse(RecordCollapseEvent recordCollapseEvent) {
+ metricsTableView.expandedRows.remove(recordCollapseEvent.getRecord().getAttributeAsInt(
+ MetricsViewDataSource.FIELD_METRIC_DEF_ID));
+ redrawGraphs();
+ }
+ });
+ addSortChangedHandler(new SortChangedHandler() {
+ @Override
+ public void onSortChanged(SortEvent sortEvent) {
+ redrawGraphs();
+ }
+ });
+ addDataArrivedHandler(new DataArrivedHandler() {
+ @Override
+ public void onDataArrived(DataArrivedEvent dataArrivedEvent) {
+ int startRow = dataArrivedEvent.getStartRow();
+ int endRow = dataArrivedEvent.getEndRow();
+ for (int i = startRow; i < endRow; i++) {
+ if (null != metricsTableView.expandedRows
+ && metricsTableView.expandedRows.contains(getRecord(i).getAttributeAsInt(
+ MetricsViewDataSource.FIELD_METRIC_DEF_ID))) {
+ expandRecord(getRecord(i));
+ }
+ }
+ }
+ });
+
+ }
+
+ @Override
+ protected Canvas getExpansionComponent(final ListGridRecord record) {
+ final Integer definitionId = record.getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
+ final Integer resourceId = record.getAttributeAsInt(MetricsViewDataSource.FIELD_RESOURCE_ID);
+ VLayout vLayout = new VLayout();
+ vLayout.setPadding(5);
+
+ final String chartId = "rChart-" + resourceId + "-" + definitionId;
+ HTMLFlow htmlFlow = new HTMLFlow(MetricD3Graph.createGraphMarkerTemplate(chartId,
+ TREEVIEW_DETAIL_CHART_HEIGHT));
+ vLayout.addMember(htmlFlow);
+
+ int[] definitionArrayIds = new int[1];
+ definitionArrayIds[0] = definitionId;
+ GWTServiceLookup.getMeasurementDataService().findDataForResource(resourceId, definitionArrayIds,
+ measurementUserPrefs.getMetricRangePreferences().begin,
+ measurementUserPrefs.getMetricRangePreferences().end, NUM_METRIC_POINTS,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.warn("Error retrieving recent metrics charting data for resource [" + resourceId + "]:"
+ + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> results) {
+ if (!results.isEmpty()) {
+
+ //load the data results for the given metric definition
+ List<MeasurementDataNumericHighLowComposite> measurementList = results.get(0);
+
+ MeasurementDefinition measurementDefinition = null;
+ for (MeasurementDefinition definition : resource.getResourceType().getMetricDefinitions()) {
+ if (definition.getId() == definitionId) {
+ measurementDefinition = definition;
+ break;
+ }
+ }
+
+ MetricGraphData metricGraphData = MetricGraphData.createForResource(resourceId,
+ resource.getName(), measurementDefinition, measurementList, null);
+ metricGraphData.setHideLegend(true);
+
+ StackedBarMetricGraphImpl graph = GWT.create(StackedBarMetricGraphImpl.class);
+ graph.setMetricGraphData(metricGraphData);
+ final MetricD3Graph graphView = new MetricD3Graph(graph, abstractD3GraphListView);
+ new Timer() {
+ @Override
+ public void run() {
+ graphView.drawJsniChart();
+ BrowserUtility.graphSparkLines();
+
+ }
+ }.schedule(150);
+
+ } else {
+ Log.warn("No chart data retrieving for resource [" + resourceId + "-" + definitionId + "]");
+
+ }
+ }
+ });
+
+ return vLayout;
+ }
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java
new file mode 100644
index 0000000..5c2fe25
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java
@@ -0,0 +1,307 @@
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.data.DSRequest;
+import com.smartgwt.client.data.DSResponse;
+import com.smartgwt.client.data.Record;
+import com.smartgwt.client.widgets.grid.CellFormatter;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import org.rhq.core.domain.criteria.Criteria;
+import org.rhq.core.domain.measurement.DataType;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementSchedule;
+import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
+import org.rhq.core.domain.measurement.ui.MetricDisplayValue;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.util.BrowserUtility;
+import org.rhq.enterprise.gui.coregui.client.util.Log;
+import org.rhq.enterprise.gui.coregui.client.util.MeasurementUtility;
+import org.rhq.enterprise.gui.coregui.client.util.RPCDataSource;
+import org.rhq.enterprise.gui.coregui.client.util.async.Command;
+import org.rhq.enterprise.gui.coregui.client.util.async.CountDownLatch;
+import org.rhq.enterprise.gui.coregui.client.util.preferences.MeasurementUserPreferences;
+
+/**
+ * A simple data source to read in metric data summaries for a resource.
+ * This doesn't support paging - everything is returned in one query. Since
+ * the number of metrics per resource is relatively small (never more than tens of them),
+ * we just load them all in at once.
+ *
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, Criteria> {
+
+ private static final int NUMBER_OF_METRIC_POINTS = 60;
+ public static final String FIELD_SPARKLINE = "sparkline";
+ public static final String FIELD_METRIC_LABEL = "label";
+ public static final String FIELD_ALERT_COUNT = "alertCount";
+ public static final String FIELD_MIN_VALUE = "min";
+ public static final String FIELD_MAX_VALUE = "max";
+ public static final String FIELD_AVG_VALUE = "avg";
+ public static final String FIELD_LAST_VALUE = "last";
+ public static final String FIELD_METRIC_DEF_ID = "defId";
+ public static final String FIELD_METRIC_SCHED_ID = "schedId";
+ public static final String FIELD_METRIC_UNITS = "units";
+ public static final String FIELD_METRIC_NAME = "name";
+ public static final String FIELD_RESOURCE_ID = "resourceId";
+ private final Resource resource;
+ private List<MetricDisplaySummary> metricDisplaySummaries;
+ private List<List<MeasurementDataNumericHighLowComposite>> metricsDataList;
+ private int[] definitionArrayIds;
+ private final MeasurementUserPreferences measurementUserPrefs;
+
+ public MetricsViewDataSource(Resource resource) {
+ this.resource = resource;
+ measurementUserPrefs = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
+ }
+
+ /**
+ * The view that contains the list grid which will display this datasource's data will call this
+ * method to get the field information which is used to control the display of the data.
+ *
+ * @return list grid fields used to display the datasource data
+ */
+ public ArrayList<ListGridField> getListGridFields() {
+ ArrayList<ListGridField> fields = new ArrayList<ListGridField>(7);
+
+ ListGridField sparklineField = new ListGridField(FIELD_SPARKLINE, MSG.chart_metrics_sparkline_header());
+ sparklineField.setCellFormatter(new CellFormatter() {
+ @Override
+ public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
+ if (value == null) {
+ return "";
+ }
+ String contents = "<span id='sparkline_" + resource.getId() + "-"
+ + record.getAttributeAsInt(FIELD_METRIC_DEF_ID) + "' class='dynamicsparkline' width='70' "
+ + "values='" + record.getAttribute(FIELD_SPARKLINE) + "'></span>";
+ return contents;
+
+ }
+ });
+
+ sparklineField.setWidth(80);
+ fields.add(sparklineField);
+
+ ListGridField nameField = new ListGridField(FIELD_METRIC_LABEL, MSG.common_title_name());
+ nameField.setWidth("30%");
+ fields.add(nameField);
+
+ ListGridField minField = new ListGridField(FIELD_MIN_VALUE, MSG.view_resource_monitor_table_min());
+ minField.setWidth("15%");
+ fields.add(minField);
+
+ ListGridField maxField = new ListGridField(FIELD_MAX_VALUE, MSG.view_resource_monitor_table_max());
+ maxField.setWidth("15%");
+ fields.add(maxField);
+
+ ListGridField avgField = new ListGridField(FIELD_AVG_VALUE, MSG.view_resource_monitor_table_avg());
+ avgField.setWidth("15%");
+ fields.add(avgField);
+
+ ListGridField lastField = new ListGridField(FIELD_LAST_VALUE, MSG.view_resource_monitor_table_last());
+ lastField.setWidth("15%");
+ fields.add(lastField);
+
+ ListGridField alertsField = new ListGridField(FIELD_ALERT_COUNT, MSG.common_title_alerts());
+ alertsField.setWidth("10%");
+ fields.add(alertsField);
+
+ return fields;
+ }
+
+ @Override
+ public MetricDisplaySummary copyValues(Record from) {
+ // we should never need this method - we only go in one direction
+ // if we ever need this, just have copyValues store an "object" attribute whose value is "from"
+ // which this method then just reads out. Since we don't need this now, save memory by not
+ // keeping the MetricDisplayValue around
+ return null;
+ }
+
+ @Override
+ public ListGridRecord copyValues(MetricDisplaySummary from) {
+ MeasurementUtility.formatSimpleMetrics(from);
+
+ ListGridRecord record = new ListGridRecord();
+ record.setAttribute(FIELD_SPARKLINE, getCsvMetricsForSparkline(from.getDefinitionId()));
+ record.setAttribute(FIELD_METRIC_LABEL, from.getLabel());
+ record.setAttribute(FIELD_ALERT_COUNT, String.valueOf(from.getAlertCount()));
+ record.setAttribute(FIELD_MIN_VALUE, getMetricStringValue(from.getMinMetric()));
+ record.setAttribute(FIELD_MAX_VALUE, getMetricStringValue(from.getMaxMetric()));
+ record.setAttribute(FIELD_AVG_VALUE, getMetricStringValue(from.getAvgMetric()));
+ record.setAttribute(FIELD_LAST_VALUE, getMetricStringValue(from.getLastMetric()));
+ record.setAttribute(FIELD_METRIC_DEF_ID, from.getDefinitionId());
+ record.setAttribute(FIELD_METRIC_SCHED_ID, from.getScheduleId());
+ record.setAttribute(FIELD_METRIC_UNITS, from.getUnits());
+ record.setAttribute(FIELD_METRIC_NAME, from.getMetricName());
+ record.setAttribute(FIELD_RESOURCE_ID, resource.getId());
+ return record;
+ }
+
+ private String getCsvMetricsForSparkline(int definitionId) {
+ StringBuilder sb = new StringBuilder();
+ List<MeasurementDataNumericHighLowComposite> selectedMetricsList = getMeasurementsForMeasurementDefId(definitionId);
+
+ for (int i = 0; i < selectedMetricsList.size(); i++) {
+ MeasurementDataNumericHighLowComposite measurementData = selectedMetricsList.get(i);
+ if (!Double.isNaN(measurementData.getValue())) {
+ sb.append((int) measurementData.getValue());
+ sb.append(",");
+ }
+ }
+
+ if (sb.toString().endsWith(",")) {
+ sb.setLength(sb.length() - 1);
+ }
+
+ return sb.toString();
+ }
+
+ List<MeasurementDataNumericHighLowComposite> getMeasurementsForMeasurementDefId(int definitionId) {
+ int selectedIndex = 0;
+
+ // find the ordinal position as specified when querying the metrics
+ for (int i = 0; i < definitionArrayIds.length; i++) {
+ if (definitionArrayIds[i] == definitionId) {
+ selectedIndex = i;
+ break;
+ }
+ }
+
+ return metricsDataList.get(selectedIndex);
+ }
+
+ protected String getMetricStringValue(MetricDisplayValue value) {
+ return (value != null) ? value.toString() : "";
+ }
+
+ @Override
+ protected Criteria getFetchCriteria(DSRequest request) {
+ // NOTE: we don't use criterias for this datasource, just return null
+ return null;
+ }
+
+ @Override
+ protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
+
+ GWTServiceLookup.getMeasurementScheduleService().findSchedulesForResourceAndType(resource.getId(),
+ DataType.MEASUREMENT, null, true, new AsyncCallback<ArrayList<MeasurementSchedule>>() {
+ @Override
+ public void onSuccess(ArrayList<MeasurementSchedule> measurementSchedules) {
+ int[] scheduleIds = new int[measurementSchedules.size()];
+ int i = 0;
+ for (MeasurementSchedule measurementSchedule : measurementSchedules) {
+ scheduleIds[i++] = measurementSchedule.getId();
+ }
+
+ final CountDownLatch countDownLatch = CountDownLatch.create(2, new Command() {
+
+ @Override
+ public void execute() {
+ response.setData(buildRecords(metricDisplaySummaries));
+ processResponse(request.getRequestId(), response);
+
+ new Timer() {
+
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+ });
+
+ queryResourceMetrics(resource, measurementUserPrefs.getMetricRangePreferences().begin,
+ measurementUserPrefs.getMetricRangePreferences().end, countDownLatch);
+ queryMetricDisplaySummaries(scheduleIds, measurementUserPrefs.getMetricRangePreferences().begin,
+ measurementUserPrefs.getMetricRangePreferences().end, countDownLatch);
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError("Cannot load schedules", caught);
+ }
+ });
+ }
+
+ private void queryMetricDisplaySummaries(int[] scheduleIds, Long startTime, Long endTime,
+ final CountDownLatch countDownLatch) {
+ GWTServiceLookup.getMeasurementChartsService().getMetricDisplaySummariesForResource(resource.getId(),
+ scheduleIds, startTime, endTime, new AsyncCallback<ArrayList<MetricDisplaySummary>>() {
+ @Override
+ public void onSuccess(ArrayList<MetricDisplaySummary> metricDisplaySummaries) {
+ setMetricDisplaySummaries(metricDisplaySummaries);
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError("Cannot load metrics", caught);
+ countDownLatch.countDown();
+ }
+ }
+
+ );
+ }
+
+ void setMetricDisplaySummaries(List<MetricDisplaySummary> metricDisplaySummaries) {
+ this.metricDisplaySummaries = metricDisplaySummaries;
+ }
+
+ public void queryResourceMetrics(final Resource resource, Long startTime, Long endTime,
+ final CountDownLatch countDownLatch) {
+ Set<MeasurementDefinition> definitions = resource.getResourceType().getMetricDefinitions();
+
+ //build id mapping for measurementDefinition instances Ex. Free Memory -> MeasurementDefinition[100071]
+ final HashMap<String, MeasurementDefinition> measurementDefMap = new HashMap<String, MeasurementDefinition>();
+ for (MeasurementDefinition definition : definitions) {
+ measurementDefMap.put(definition.getDisplayName(), definition);
+ }
+ //bundle definition ids for asynch call.
+ definitionArrayIds = new int[definitions.size()];
+ final String[] displayOrder = new String[definitions.size()];
+ measurementDefMap.keySet().toArray(displayOrder);
+ //sort the charting data ex. Free Memory, Free Swap Space,..System Load
+ Arrays.sort(displayOrder);
+
+ //organize definitionArrayIds for ordered request on server.
+ int index = 0;
+ for (String definitionToDisplay : displayOrder) {
+ definitionArrayIds[index++] = measurementDefMap.get(definitionToDisplay).getId();
+ }
+
+ GWTServiceLookup.getMeasurementDataService().findDataForResource(resource.getId(), definitionArrayIds,
+ startTime, endTime, NUMBER_OF_METRIC_POINTS,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.warn("Error retrieving recent metrics charting data for resource [" + resource.getId() + "]:"
+ + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> measurementDataList) {
+
+ if (null != measurementDataList && !measurementDataList.isEmpty()) {
+ metricsDataList = measurementDataList;
+ countDownLatch.countDown();
+ }
+ }
+ });
+
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
new file mode 100644
index 0000000..9d6b892
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
@@ -0,0 +1,208 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring.table;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+
+import org.rhq.core.domain.measurement.MeasurementUnits;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.composite.ResourceAvailabilitySummary;
+import org.rhq.enterprise.gui.coregui.client.CoreGUI;
+import org.rhq.enterprise.gui.coregui.client.components.table.TimestampCellFormatter;
+import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.enterprise.gui.coregui.client.util.MeasurementConverterClient;
+import org.rhq.enterprise.gui.coregui.client.util.enhanced.EnhancedVLayout;
+
+/**
+ * This shows the availability history for a resource.
+ *
+ * @author Jay Shaughnessy
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class ResourceMetricAvailabilityView extends EnhancedVLayout {
+
+ private Resource resource;
+ private StaticTextItem currentField;
+ private StaticTextItem availField;
+ private StaticTextItem availTimeField;
+ private StaticTextItem downField;
+ private StaticTextItem downTimeField;
+ private StaticTextItem disabledField;
+ private StaticTextItem disabledTimeField;
+ private StaticTextItem failureCountField;
+ private StaticTextItem disabledCountField;
+ private StaticTextItem mtbfField;
+ private StaticTextItem mttrField;
+ private StaticTextItem unknownField;
+ private StaticTextItem currentTimeField;
+
+ public ResourceMetricAvailabilityView(Resource resource) {
+ super();
+
+ this.resource = resource;
+
+ setWidth100();
+ setHeight(165);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+
+ addMember(createSummaryForm());
+ }
+
+ private DynamicForm createSummaryForm() {
+ DynamicForm form = new DynamicForm();
+ form.setWidth100();
+ form.setAutoHeight();
+ form.setMargin(10);
+ form.setNumCols(4);
+
+ // row 1
+ currentField = new StaticTextItem("current", MSG.view_resource_monitor_availability_currentStatus());
+ currentField.setWrapTitle(false);
+ currentField.setColSpan(4);
+
+ // row 2
+ availField = new StaticTextItem("avail", MSG.view_resource_monitor_availability_availability());
+ availField.setWrapTitle(false);
+ prepareTooltip(availField, MSG.view_resource_monitor_availability_availability_tooltip());
+
+ availTimeField = new StaticTextItem("availTime", MSG.view_resource_monitor_availability_uptime());
+ availTimeField.setWrapTitle(false);
+ prepareTooltip(availTimeField, MSG.view_resource_monitor_availability_uptime_tooltip());
+
+ // row 3
+ downField = new StaticTextItem("down", MSG.view_resource_monitor_availability_down());
+ downField.setWrapTitle(false);
+ prepareTooltip(downField, MSG.view_resource_monitor_availability_down_tooltip());
+
+ downTimeField = new StaticTextItem("downTime", MSG.view_resource_monitor_availability_downtime());
+ downTimeField.setWrapTitle(false);
+ prepareTooltip(downTimeField, MSG.view_resource_monitor_availability_downtime_tooltip());
+
+ // row 4
+ disabledField = new StaticTextItem("disabled", MSG.view_resource_monitor_availability_disabled());
+ disabledField.setWrapTitle(false);
+ prepareTooltip(disabledField, MSG.view_resource_monitor_availability_disabled_tooltip());
+
+ disabledTimeField = new StaticTextItem("disabledTime", MSG.view_resource_monitor_availability_disabledTime());
+ disabledTimeField.setWrapTitle(false);
+ prepareTooltip(disabledTimeField, MSG.view_resource_monitor_availability_disabledTime_tooltip());
+
+ // row 5
+ failureCountField = new StaticTextItem("failureCount", MSG.view_resource_monitor_availability_numFailures());
+ failureCountField.setWrapTitle(false);
+ prepareTooltip(failureCountField, MSG.view_resource_monitor_availability_numFailures_tooltip());
+
+ disabledCountField = new StaticTextItem("disabledCount", MSG.view_resource_monitor_availability_numDisabled());
+ disabledCountField.setWrapTitle(false);
+ prepareTooltip(disabledCountField, MSG.view_resource_monitor_availability_numDisabled_tooltip());
+
+ // row 6
+ mtbfField = new StaticTextItem("mtbf", MSG.view_resource_monitor_availability_mtbf());
+ mtbfField.setWrapTitle(false);
+ prepareTooltip(mtbfField, MSG.view_resource_monitor_availability_mtbf_tooltip());
+
+ mttrField = new StaticTextItem("mttr", MSG.view_resource_monitor_availability_mttr());
+ mttrField.setWrapTitle(false);
+ prepareTooltip(mttrField, MSG.view_resource_monitor_availability_mttr_tooltip());
+
+ // row 7
+ unknownField = new StaticTextItem("unknown");
+ unknownField.setWrapTitle(false);
+ unknownField.setColSpan(4);
+ unknownField.setShowTitle(false);
+
+ // row 8
+ currentTimeField = new StaticTextItem("currentTime");
+ currentTimeField.setWrapTitle(false);
+ currentTimeField.setColSpan(4);
+ currentTimeField.setShowTitle(false);
+
+ form.setItems(currentField, availField, availTimeField, downField, downTimeField, disabledField,
+ disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
+ currentTimeField);
+
+ reloadSummaryData();
+
+ return form;
+ }
+
+ private void reloadSummaryData() {
+ GWTServiceLookup.getResourceService().getResourceAvailabilitySummary(resource.getId(),
+ new AsyncCallback<ResourceAvailabilitySummary>() {
+
+ @Override
+ public void onSuccess(ResourceAvailabilitySummary result) {
+
+ currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(result
+ .getCurrent().getName(), TimestampCellFormatter.format(result.getLastChange().getTime())));
+ availField.setValue(MeasurementConverterClient.format(result.getUpPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ availTimeField.setValue(MeasurementConverterClient.format((double) result.getUpTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ downField.setValue(MeasurementConverterClient.format(result.getDownPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ downTimeField.setValue(MeasurementConverterClient.format((double) result.getDownTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ disabledField.setValue(MeasurementConverterClient.format(result.getDisabledPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ disabledTimeField.setValue(MeasurementConverterClient.format((double) result.getDisabledTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ failureCountField.setValue(result.getFailures());
+ disabledCountField.setValue(result.getDisabled());
+ mtbfField.setValue(MeasurementConverterClient.format((double) result.getMTBF(),
+ MeasurementUnits.MILLISECONDS, true));
+ mttrField.setValue(MeasurementConverterClient.format((double) result.getMTTR(),
+ MeasurementUnits.MILLISECONDS, true));
+
+ if (result.getUnknownTime() > 0L) {
+ unknownField.setValue(MSG.view_resource_monitor_availability_unknown(MeasurementConverterClient
+ .format((double) result.getUnknownTime(), MeasurementUnits.MILLISECONDS, true)));
+ } else {
+ unknownField.setValue("");
+ }
+
+ currentTimeField.setValue(MSG.view_resource_monitor_availability_currentAsOf(TimestampCellFormatter
+ .format(result.getCurrentTime())));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ currentField.setValue(MSG.common_label_error());
+ CoreGUI.getErrorHandler()
+ .handleError(MSG.view_resource_monitor_availability_summaryError(), caught);
+ }
+ });
+ }
+
+ private void prepareTooltip(FormItem item, String tooltip) {
+ item.setHoverWidth(400);
+ item.setPrompt(tooltip);
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/searchbar/BasicSearchStrategy.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/searchbar/BasicSearchStrategy.java
index 1edd076..94fd1e8 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/searchbar/BasicSearchStrategy.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/searchbar/BasicSearchStrategy.java
@@ -26,7 +26,6 @@ import com.smartgwt.client.data.fields.DataSourceTextField;
import com.smartgwt.client.widgets.form.fields.events.KeyUpEvent;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
-import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
@@ -89,17 +88,14 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
*/
@Override
public void onRecordClick(RecordClickEvent event) {
- Log.debug("BasicSearchStrategy click");
String kind = event.getRecord().getAttribute(ATTR_KIND);
String searchExpression;
if (kind.equals("SAVED") || kind.equals("GLOBAL")) {
- Log.debug("Saved or Global Search Click");
searchExpression = event.getRecord().getAttribute(ATTR_PATTERN);
} else {
- Log.debug("Regular Search Click");
searchExpression = event.getRecord().getAttribute(ATTR_NAME);
}
@@ -112,14 +108,12 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
@Override
public void searchFocusHandler() {
- Log.debug("focus in BasicSearchStrategy");
String searchExpression = searchBar.getSearchTextItem().getValueAsString();
doSearch(searchExpression);
}
@Override
public void searchKeyUpHandler(KeyUpEvent keyUpEvent) {
- Log.debug("Keyup in BasicSearchStrategy: " + keyUpEvent.getKeyName());
String searchExpression = searchBar.getSearchTextItem().getValueAsString();
doSearch(searchExpression);
}
@@ -131,20 +125,16 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
private void doSearch(String searchExpression) {
if (isSearchInProgress) {
- Log.debug("Adding pending search [" + searchExpression + "]");
pendingSearchExpression = (null == searchExpression) ? "" : searchExpression;
return;
}
- Log.debug("Search Start");
isSearchInProgress = true;
if (null == searchExpression || searchExpression.isEmpty()) {
- Log.debug("Empty Search expression");
getSearchSuggestions(SearchSubsystem.RESOURCE, null, 0);
} else {
- Log.debug("doSearch: " + searchExpression);
getSearchSuggestions(SearchSubsystem.RESOURCE, searchBar.getSearchTextItem().getValueAsString(), searchBar
.getSearchTextItem().getValueAsString().length());
}
@@ -154,7 +144,6 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
final long suggestStart = System.currentTimeMillis();
- Log.debug("Searching for: " + expression);
searchService.getTabAwareSuggestions(searchSubsystem, expression, caretPosition, null,
new AsyncCallback<List<SearchSuggestion>>() {
@@ -179,7 +168,6 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
ds.setFields(idField, valueField);
searchBarPickListGrid.setDataSource(ds);
- ListGridField[] fields = searchBarPickListGrid.getAllFields();
searchBarPickListGrid.getField(ATTR_VALUE).setShowHover(true);
searchBarPickListGrid.getField(ATTR_VALUE).setHoverCustomizer(new HoverCustomizer() {
@@ -223,7 +211,7 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
searchBarPickListGrid.setData(new ListGridRecord[] {});
searchBarPickListGrid.fetchData();
} catch (Exception e) {
- Log.debug("Caught exception on fetchData: " + e);
+ Log.info("Caught exception on fetchData: " + e);
}
long suggestFetchTime = System.currentTimeMillis() - suggestStart;
@@ -250,7 +238,6 @@ public class BasicSearchStrategy extends AbstractSearchStrategy {
@Override
public void onFailure(Throwable caught) {
- Log.debug("Search End");
isSearchInProgress = false;
pendingSearchExpression = null;
CoreGUI.getErrorHandler().handleError(MSG.view_searchBar_suggest_failSuggest(), caught);
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/util/enhanced/Enhanced.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/util/enhanced/Enhanced.java
index 10505e9..a069a5d 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/util/enhanced/Enhanced.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/util/enhanced/Enhanced.java
@@ -31,5 +31,5 @@ import org.rhq.enterprise.gui.coregui.client.Messages;
*/
public interface Enhanced {
- Messages MSG = CoreGUI.getMessages();
+ final static Messages MSG = CoreGUI.getMessages();
}
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
index 5a42502..040537d 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
@@ -20,6 +20,10 @@ chart_hover_period_label = Period
chart_hover_start_label = Start
chart_hover_time_format = %I:%M:%S %p
chart_ie_not_supported = Charting is not available for this browser.
+chart_metrics= Metrics
+chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+chart_metrics_sparkline_header= Chart
chart_no_data_label = No Data
chart_single_value_label = Value
chart_slider_button_bar_day = Day
@@ -1816,6 +1820,7 @@ view_messageCenter_messageTime = Time
view_messageCenter_messageTitle = Message Center
view_messageCenter_noRecentMessages = No Recent Messages
view_messageCenter_stackTraceFollows = --- STACK TRACE FOLLOWS ---
+view_metric_addToDashboard = Add to Dashboard
view_metric_traits = Traits
view_metric_viewTraitHistory = Value History for Trait [{0}]
view_operationHistoryDetails_dateCompleted = Date Completed
@@ -2135,7 +2140,7 @@ view_titleBar_common_updateTagsSuccessful = The tags for [{0}] have been updated
view_titleBar_group_failInfo = Failed to get general info on group [{0}] with ID [{1}]
view_titleBar_group_summary_collapsedTooltip = Click to show more details for this group
view_titleBar_group_summary_expandedTooltip = Click to hide details for this group
-view_tree_common_contextMenu_addChartToDashboard = Add chart to dashboard [{0}]
+view_tree_common_contextMenu_addChartToDashboard = Add Graph to Dashboard [{0}]
view_tree_common_contextMenu_editPluginConfiguration = Edit [{0}] Plugin Configuration
view_tree_common_contextMenu_editResourceConfiguration = Edit [{0}] Resource Configuration
view_tree_common_contextMenu_groupGraph = Group Metric Graph
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
index 69c8452..2e23eff 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_cs.properties
@@ -48,6 +48,10 @@ chart_hover_date_format = %d.%m.%y
##chart_hover_start_label = Start
chart_hover_time_format = %H:%M:%S
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_day = Day
@@ -1828,6 +1832,7 @@ view_messageCenter_messageTime = Čas
view_messageCenter_messageTitle = Centrum zpráv
view_messageCenter_noRecentMessages = Žádné nové zprávy
view_messageCenter_stackTraceFollows = --- VÝPIS ZÁSOBNÍKU NÍŽE ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = Vlastnosti
view_metric_viewTraitHistory = Historie hodnot pro vlastnost [{0}]
view_operationHistoryDetails_dateCompleted = Datum ukončení
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
index ddd4686..b4fe981 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
@@ -23,6 +23,10 @@ chart_hover_period_label = Zeitraum
chart_hover_start_label = Start
chart_hover_time_format = %H:%M:%S
chart_ie_not_supported = Charting ist bei diesem Browser nicht unterstützt
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
chart_no_data_label = Keine Daten vorhanden
##chart_single_value_label = Value
chart_slider_button_bar_day = Tag
@@ -1625,6 +1629,7 @@ view_messageCenter_messageTime = Zeitpukt
view_messageCenter_messageTitle = Nachrichtencenter
view_messageCenter_noRecentMessages = Keine aktuellen Nachrichten
view_messageCenter_stackTraceFollows = --- STACK TRACE FOLGT ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = Traits
view_metric_viewTraitHistory = Werteverlauf für Trait [{0}]
##view_operationCreateWizard_error_scheduleOperationFailure = Failed to schedule operation execution.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
index 49cc7e6..c1a8521 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
@@ -24,6 +24,10 @@
##chart_hover_date_format = %m/%d/%y
##chart_hover_time_format = %I:%M:%S %p
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -1799,6 +1803,7 @@ view_messageCenter_messageTime = 時間
view_messageCenter_messageTitle = メッセージセンター
view_messageCenter_noRecentMessages = 最近のメッセージはありません
view_messageCenter_stackTraceFollows = --- スタックトレース ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = トレイト
view_metric_viewTraitHistory = トレイト [{0}] のための値の履歴
view_operationHistoryDetails_dateCompleted = 完了日
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
index 89c10c0..2958fbe 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ko.properties
@@ -22,6 +22,10 @@
##chart_hover_date_format = %m/%d/%y
##chart_hover_time_format = %I:%M:%S %p
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -1539,6 +1543,7 @@ view_messageCenter_messageTime = 시간
view_messageCenter_messageTitle = 메시지 센터
view_messageCenter_noRecentMessages = 최근 메시지는 없습니다
view_messageCenter_stackTraceFollows = --- 스택 추적 ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = 특성
view_metric_viewTraitHistory = 특성 [{0}]에 대한 값 기록
view_operationHistoryDetails_dateCompleted = 완료일
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
index 26546fb..62f31f5 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
@@ -21,6 +21,10 @@
##chart_hover_date_format = %m/%d/%y
##chart_hover_time_format = %I:%M:%S %p
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -1833,6 +1837,7 @@ view_messageCenter_messageTime = Tempo
view_messageCenter_messageTitle = Centro de Mensagens
view_messageCenter_noRecentMessages = N\u00E3o existem Mensagens Recentes
view_messageCenter_stackTraceFollows = --- STACK TRACE FOLLOWS ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = Traits
view_metric_viewTraitHistory = Value History for Trait [{0}]
view_operationCreateWizard_button_execute = Execute
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
index 5692d33..eca8c24 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ru.properties
@@ -21,6 +21,10 @@
##chart_hover_date_format = %m/%d/%y
##chart_hover_time_format = %I:%M:%S %p
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -1728,6 +1732,7 @@
#view_messageCenter_messageTitle = Message Center
#view_messageCenter_noRecentMessages = No Recent Messages
#view_messageCenter_stackTraceFollows = --- STACK TRACE FOLLOWS ---
+##view_metric_addToDashboard = Add to Dashboard
#view_metric_traits = Traits
#view_metric_viewTraitHistory = Value History for Trait [{0}]
#view_operationHistoryDetails_dateCompleted = Date Completed
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
index f126e51..e71f1fb 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
@@ -21,6 +21,10 @@
##chart_hover_date_format = %m/%d/%y
##chart_hover_time_format = %I:%M:%S %p
##chart_ie_not_supported = Charting is not available for this browser.
+##chart_metrics= Metrics
+##chart_metrics_collapse_tooltip= Click here to see additional tabular availability data.
+##chart_metrics_expand_tooltip= Click here to collapse additional availability detail.
+##chart_metrics_sparkline_header= Chart
##chart_no_data_label = No Data
##chart_single_value_label = Value
##chart_slider_button_bar_minute = Min
@@ -1802,6 +1806,7 @@ view_messageCenter_messageTime = \u65f6\u95f4
view_messageCenter_messageTitle = \u6d88\u606f\u4e2d\u5fc3
view_messageCenter_noRecentMessages = \u65e0\u8fd1\u671f\u6d88\u606f
view_messageCenter_stackTraceFollows = --- \u540e\u9762\u662f\u5f02\u5e38\u4fe1\u606f ---
+##view_metric_addToDashboard = Add to Dashboard
view_metric_traits = \u7279\u5f81
view_metric_viewTraitHistory = Value History for Trait [{0}]
view_operationHistoryDetails_dateCompleted = \u5b8c\u6210\u65e5\u671f
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/rhq.js b/modules/enterprise/gui/coregui/src/main/webapp/js/rhq.js
index 40c7ca8..ef65e69 100644
--- a/modules/enterprise/gui/coregui/src/main/webapp/js/rhq.js
+++ b/modules/enterprise/gui/coregui/src/main/webapp/js/rhq.js
@@ -38,9 +38,10 @@ if (!window.console.log) window.console.log = function () {
* @param singleValueLabel
* @param chartXaxisTimeFormatHours
* @param chartXaxisTimeFormatHoursMinutes
+ * @param showLegend
* @constructor
*/
-var ChartContext = function (chartId, chartHeight, metricsData, xAxisLabel, chartTitle, yAxisUnits, minChartTitle, avgChartTitle, peakChartTitle, dateLabel, timeLabel, downLabel, unknownLabel, noDataLabel, hoverStartLabel, hoverEndLabel, hoverPeriodLabel, hoverBarLabel, chartHoverTimeFormat, chartHoverDateFormat, isPortalGraph, portalId, buttonBarDateTimeFormat, singleValueLabel, chartXaxisTimeFormatHours, chartXaxisTimeFormatHoursMinutes) {
+var ChartContext = function (chartId, chartHeight, metricsData, xAxisLabel, chartTitle, yAxisUnits, minChartTitle, avgChartTitle, peakChartTitle, dateLabel, timeLabel, downLabel, unknownLabel, noDataLabel, hoverStartLabel, hoverEndLabel, hoverPeriodLabel, hoverBarLabel, chartHoverTimeFormat, chartHoverDateFormat, isPortalGraph, portalId, buttonBarDateTimeFormat, singleValueLabel, chartXaxisTimeFormatHours, chartXaxisTimeFormatHoursMinutes, hideLegend) {
"use strict";
if (!(this instanceof ChartContext)) {
throw new Error("ChartContext function cannot be called as a function.")
@@ -78,6 +79,7 @@ var ChartContext = function (chartId, chartHeight, metricsData, xAxisLabel, char
this.buttonBarDateTimeFormat = buttonBarDateTimeFormat;
this.chartXaxisTimeFormatHours = chartXaxisTimeFormatHours;
this.chartXaxisTimeFormatHoursMinutes = chartXaxisTimeFormatHoursMinutes;
+ this.hideLegend = hideLegend;
},
/**
commit 1d740172121ae51d6a6c183a894710cc58b61619
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jul 25 18:06:07 2013 +0200
Bug 886126 - Datasource JNDI change takes too long to be propagated to JON server
In EAP5, JNDI name is the key of Datasource and Connection Factory managed components. Consequently, there is no way to change the JNDI name of such components without creating a new managed component, which will in the end be discovered by RHQ.
So in RHQ, jndi-name config property is now marked as read-only (it will still be required when creating a Datasource or a Connection Factory).
diff --git a/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
index 2b14f5b..9a53a80 100644
--- a/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
@@ -133,6 +133,7 @@
<!ENTITY datasourceConnectionResourceConfigProps '
<c:simple-property name="jndi-name"
+ readOnly="true"
displayName="JNDI Name"
description="The global JNDI Name under which to bind the datasource"
type="string"/>
@@ -502,7 +503,7 @@
-->
<!ENTITY connectionFactoryConnectionResourceConfigProps '
- <c:simple-property required="true" name="jndi-name" displayName="JNDI Name"
+ <c:simple-property required="true" name="jndi-name" readOnly="true" displayName="JNDI Name"
description="The global JNDI name to bind the connection factory under."/>
<c:simple-property required="true" name="rar-name" displayName="RAR Name">
commit 8fa51829ab0467d8518b095d303528664c7f58a3
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jul 25 17:17:27 2013 +0200
Make the parser check its input and bail out if it is not valid.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/helper/LinkDeserializer.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/helper/LinkDeserializer.java
index e9582d4..f320d09 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/helper/LinkDeserializer.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/helper/LinkDeserializer.java
@@ -20,7 +20,11 @@
package org.rhq.enterprise.server.rest.helper;
import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.codehaus.jackson.JsonLocation;
+import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
@@ -44,25 +48,47 @@ import org.rhq.enterprise.server.rest.domain.Link;
*/
public class LinkDeserializer extends JsonDeserializer<Link>{
+ Pattern textPattern = Pattern.compile("\\S+"); // Non whitespace; could possibly be narrowed
+
@Override
public Link deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
String tmp = jp.getText(); // {
+ validate(jp, tmp,"{");
jp.nextToken(); // skip over { to the rel
String rel = jp.getText();
+ validateText(jp, rel);
jp.nextToken(); // skip over {
tmp = jp.getText();
+ validate(jp, tmp,"{");
jp.nextToken(); // skip over "href"
tmp = jp.getText();
-// jp.nextToken(); // skip over :
+ validate(jp, tmp,"href");
jp.nextToken(); // skip to "http:// ... "
String href = jp.getText();
+ validateText(jp, href);
jp.nextToken(); // skip }
tmp = jp.getText();
+ validate(jp, tmp, "}");
jp.nextToken(); // skip }
+ tmp = jp.getText();
+ validate(jp, tmp, "}");
Link link = new Link(rel,href);
return link;
}
+
+ private void validateText(JsonParser jsonParser, String input) throws JsonProcessingException {
+ Matcher m = textPattern.matcher(input);
+ if (!m.matches()) {
+ throw new JsonParseException("Unexpected token: " + input, jsonParser.getTokenLocation());
+ }
+ }
+
+ private void validate(JsonParser jsonParser, String input, String expected) throws JsonProcessingException {
+ if (!input.equals(expected)) {
+ throw new JsonParseException("Unexpected token: " + input, jsonParser.getTokenLocation());
+ }
+ }
}
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
index 3d39181..7789f9e 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
@@ -18,7 +18,6 @@
*/
package org.rhq.modules.integrationTests.restApi;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -219,10 +218,61 @@ public class OperationsTest extends AbstractBase {
.when()
.delete("/operation/" + draftId);
}
+ }
+
+ @Test
+ public void testCatchBadLinkSerialization() throws Exception {
+
+ // Test that when we get Links back in bad format, we
+ // correctly bail out.
+
+ Operation draft = getADraftOperation(_platformId, discoveryDefinitionId);
+
+ int draftId = draft.getId();
+ draft.getParams().put("detailedDiscovery",true);
+
+ String jsonWithBadLinkSer = //
+ "{\n" +
+ " \"id\": " + draftId + ",\n" +
+ " \"name\": \"discovery\",\n" +
+ " \"readyToSubmit\": false,\n" +
+ " \"resourceId\": " + _platformId + ",\n" +
+ " \"definitionId\": " + discoveryDefinitionId + ",\n" +
+ " \"params\": {\n" +
+ " \"detailedDiscovery\": true\n" +
+ " },\n" +
+ " \"links\": [\n" +
+ " {\n" +
+ " \"rel\": \"edit\",\n" +
+ " \"href\": \"http://localhost:7080/rest/operation/" + draftId + "\"\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+
+ try {
+ given()
+ .contentType(ContentType.JSON)
+ .pathParam("id", draftId)
+ .body(jsonWithBadLinkSer)
+ .log().everything()
+ .expect()
+ .statusCode(503)
+ .log().ifError()
+ .when()
+ .put("/operation/{id}");
+ } finally {
+ // delete the draft again
+ expect()
+ .statusCode(204)
+ .when()
+ .delete("/operation/" + draftId);
+ }
}
+
+
@Test
public void testCreateDraftOperationAndScheduleExecution() throws Exception {
commit e2e18c6332505611c2b87e81b3369967a0c2a956
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jul 25 14:57:07 2013 +0200
Add a LinkSerializer to send links in correct format (one could argue not to send links at all).
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
index 57c1a49..3d39181 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
@@ -318,8 +318,6 @@ public class OperationsTest extends AbstractBase {
assert draft != null;
assert draft.getDefinitionId() == definitionId;
- draft.setLinks(new ArrayList<Link>()); // Clean out links TODO
-
System.out.println("--- Draft created --");
System.out.flush();
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
index c3f7b2b..c199729 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
@@ -20,11 +20,13 @@
package org.rhq.modules.integrationTests.restApi.d;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
/**
* A Link inside objects
* @author Heiko W. Rupp
*/
+@JsonSerialize(using = LinkSerializer.class)
@JsonDeserialize(using = LinkDeserializer.class)
public class Link {
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkSerializer.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkSerializer.java
new file mode 100644
index 0000000..759c8dc
--- /dev/null
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkSerializer.java
@@ -0,0 +1,53 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.modules.integrationTests.restApi.d;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+/**
+ * Special serializer for Link objects that does not map the classical {rel:abc, href:xyz} scheme,
+ * but which puts the rel name "at the outside" like { abc : { href : xyz }} to make it easier for
+ * clients to access the link.
+ * See also https://bugzilla.redhat.com/show_bug.cgi?id=845244
+ * @author Heiko W. Rupp
+ * @see LinkDeserializer
+ */
+public class LinkSerializer extends JsonSerializer<Link> {
+
+ @Override
+ public void serialize(Link link, JsonGenerator jsonGenerator,
+ SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
+
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeFieldName(link.getRel());
+
+ jsonGenerator.writeStartObject();
+ jsonGenerator.writeFieldName("href");
+ jsonGenerator.writeString(link.getHref());
+ jsonGenerator.writeEndObject();
+
+ jsonGenerator.writeEndObject();
+ }
+}
commit 80144152f0d7b919df9cb09de7170250edb26b03
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jul 25 12:55:54 2013 +0200
BZ 974963 Allow to schedule operations that have no parameters.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java
index 5e8e68e..070675e 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java
@@ -303,12 +303,15 @@ public class OperationsHandlerBean extends AbstractRestBean {
// Validate parameters
ConfigurationDefinition parameterDefinition = opDef.getParametersConfigurationDefinition();
- List<String> errorMessages = ConfigurationHelper.checkConfigurationWrtDefinition(parameters, parameterDefinition);
-
- if (errorMessages.size()>0) {
- // Configuration is not ok
- operation.setReadyToSubmit(false);
- throw new BadArgumentException("Validation of parameters failed", StringUtils.getListAsString(errorMessages,", "));
+ if (parameterDefinition!=null) {
+ // There are parameters defined, so lets validate them.
+ List<String> errorMessages = ConfigurationHelper.checkConfigurationWrtDefinition(parameters, parameterDefinition);
+
+ if (errorMessages.size()>0) {
+ // Configuration is not ok
+ operation.setReadyToSubmit(false);
+ throw new BadArgumentException("Validation of parameters failed", StringUtils.getListAsString(errorMessages,", "));
+ }
}
}
diff --git a/modules/integration-tests/rest-api/pom.xml b/modules/integration-tests/rest-api/pom.xml
index 47bfe18..5d7bde5 100644
--- a/modules/integration-tests/rest-api/pom.xml
+++ b/modules/integration-tests/rest-api/pom.xml
@@ -18,7 +18,7 @@
<properties>
<surefire-plugin.version>2.10</surefire-plugin.version>
- <jackson.version>1.9.5</jackson.version>
+ <jackson.version>2.0.5</jackson.version>
<rest-assured.version>1.8.1</rest-assured.version>
</properties>
@@ -53,15 +53,14 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-core-asl</artifactId>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
-
<dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-mapper-asl</artifactId>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
<scope>test</scope>
</dependency>
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
index 7800e5c..57c1a49 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/OperationsTest.java
@@ -18,6 +18,7 @@
*/
package org.rhq.modules.integrationTests.restApi;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -42,7 +43,8 @@ import static org.hamcrest.core.Is.is;
*/
public class OperationsTest extends AbstractBase {
- private int definitionId;
+ private int discoveryDefinitionId;
+ private int viewPLDefinitionId;
@Before
public void setUp() throws Exception {
@@ -58,15 +60,20 @@ public class OperationsTest extends AbstractBase {
.when()
.get("/operation/definitions");
- definitionId = -1;
+ discoveryDefinitionId = -1;
List<Map<String,Object>> list = r.as(List.class);
for (Map<String,Object> map : list) {
- if (map.get("name").equals("discovery")) {
- definitionId = (Integer) map.get("id");
+ String name = (String) map.get("name");
+ Integer id = (Integer) map.get("id");
+ if (name.equals("discovery")) {
+ discoveryDefinitionId = id;
+ }
+ if (name.equals("viewProcessList")) {
+ viewPLDefinitionId = id;
}
}
- assert definitionId !=-1 : "No discovery operation found";
+ assert discoveryDefinitionId !=-1 : "No discovery operation found";
}
@Test
@@ -76,7 +83,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
- .pathParam("did",definitionId)
+ .pathParam("did", discoveryDefinitionId)
.expect()
.statusCode(200)
.body("name",is("discovery"))
@@ -92,7 +99,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
- .pathParam("did",-42)
+ .pathParam("did", -42)
.expect()
.statusCode(404)
.when()
@@ -107,7 +114,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
- .queryParam("resourceId",42)
+ .queryParam("resourceId", 42)
.expect()
.statusCode(404)
.when()
@@ -134,7 +141,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
- .pathParam("did",-42)
+ .pathParam("did", -42)
.expect()
.statusCode(406)
.when()
@@ -150,7 +157,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
.queryParam("resourceId", 42)
- .pathParam("definitionId", definitionId)
+ .pathParam("definitionId", discoveryDefinitionId)
.expect()
.statusCode(404)
.when()
@@ -164,7 +171,7 @@ public class OperationsTest extends AbstractBase {
given()
.header(acceptJson)
- .pathParam("definitionId", definitionId)
+ .pathParam("definitionId", discoveryDefinitionId)
.expect()
.statusCode(406)
.when()
@@ -174,20 +181,7 @@ public class OperationsTest extends AbstractBase {
@Test
public void testCreateDraftOperation() throws Exception {
- Operation draft =
- given()
- .header(acceptJson)
- .pathParam("definitionId",definitionId)
- .queryParam("resourceId",_platformId)
- .expect()
- .statusCode(200)
- .log().ifError()
- .when()
- .post("/operation/definition/{definitionId}")
- .as(Operation.class);
-
- assert draft != null;
- assert draft.getDefinitionId() == definitionId;
+ Operation draft = getADraftOperation(_platformId, discoveryDefinitionId);
int draftId = draft.getId();
@@ -202,29 +196,17 @@ public class OperationsTest extends AbstractBase {
@Test
public void testCreateAndUpdateDraftOperation() throws Exception {
- Operation draft =
- given()
- .header(acceptJson)
- .pathParam("definitionId",definitionId)
- .queryParam("resourceId",_platformId)
- .expect()
- .statusCode(200)
- .log().ifError()
- .when()
- .post("/operation/definition/{definitionId}")
- .as(Operation.class);
-
- assert draft != null;
- assert draft.getDefinitionId() == definitionId;
+ Operation draft = getADraftOperation(_platformId, discoveryDefinitionId);
int draftId = draft.getId();
- draft.getParams().put("detailed",true);
+ draft.getParams().put("detailedDiscovery",true);
try {
given()
.contentType(ContentType.JSON)
.pathParam("id", draftId)
.body(draft)
+ .log().everything()
.expect()
.statusCode(200)
.log().ifError()
@@ -246,25 +228,51 @@ public class OperationsTest extends AbstractBase {
int platformId = findIdOfARealPlatform();
- Operation draft =
+ Operation draft = getADraftOperation(platformId, discoveryDefinitionId);
+
+ int draftId = draft.getId();
+
+ draft.setReadyToSubmit(true);
+ draft.getParams().put("detailedDiscovery", false);
+
+ // update to schedule
+ Operation scheduled =
given()
- .header(acceptJson)
- .pathParam("definitionId",definitionId)
- .queryParam("resourceId",platformId)
+ .contentType(ContentType.JSON)
+ .pathParam("id",draftId)
+ .body(draft)
.expect()
.statusCode(200)
.log().ifError()
.when()
- .post("/operation/definition/{definitionId}")
+ .put("/operation/{id}")
.as(Operation.class);
- assert draft != null;
- assert draft.getDefinitionId() == definitionId;
+ System.out.println(scheduled.getId());
+ String history = findHistoryItem(scheduled);
+
+ String historyId = history.substring(history.lastIndexOf("/")+1);
+ try {
+ waitAndCheckStatus(platformId, historyId);
+
+ } finally {
+
+ // Wait until the operation has finished and then delete
+ waitForTerminationAndDelete(historyId);
+
+ }
+ }
+
+ @Test
+ public void testCreateDraftOperationNoParamsAndScheduleExecution() throws Exception {
+
+ int platformId = findIdOfARealPlatform();
+
+ Operation draft = getADraftOperation(platformId, viewPLDefinitionId);
int draftId = draft.getId();
draft.setReadyToSubmit(true);
- draft.getParams().put("detailedDiscovery", false);
// update to schedule
Operation scheduled =
@@ -280,6 +288,45 @@ public class OperationsTest extends AbstractBase {
.as(Operation.class);
System.out.println(scheduled.getId());
+ String history = findHistoryItem(scheduled);
+
+ String historyId = history.substring(history.lastIndexOf("/")+1);
+ try {
+ waitAndCheckStatus(platformId, historyId);
+
+ } finally {
+
+ // Wait until the operation has finished and then delete
+ waitForTerminationAndDelete(historyId);
+
+ }
+ }
+
+ private Operation getADraftOperation(int platformId, int definitionId) {
+ Operation draft =
+ given()
+ .header(acceptJson)
+ .pathParam("definitionId", definitionId)
+ .queryParam("resourceId",platformId)
+ .expect()
+ .statusCode(200)
+ .log().ifError()
+ .when()
+ .post("/operation/definition/{definitionId}")
+ .as(Operation.class);
+
+ assert draft != null;
+ assert draft.getDefinitionId() == definitionId;
+
+ draft.setLinks(new ArrayList<Link>()); // Clean out links TODO
+
+ System.out.println("--- Draft created --");
+ System.out.flush();
+
+ return draft;
+ }
+
+ private String findHistoryItem(Operation scheduled) {
String history = null;
List<Link> links = scheduled.getLinks();
for (Link link : links) {
@@ -288,76 +335,74 @@ public class OperationsTest extends AbstractBase {
}
}
assert history != null;
+ return history;
+ }
- String historyId = history.substring(history.lastIndexOf("/")+1);
- try {
- Thread.sleep(15000); // we need to wait a little as the execution may take time
+ private void waitAndCheckStatus(int platformId, String historyId) throws InterruptedException {
+ Thread.sleep(15000); // we need to wait a little as the execution may take time
- given()
- .pathParam("hid",historyId)
- .expect()
- .statusCode(200)
- .log().ifError()
- .when()
- .get("/operation/history/{hid}");
+ given()
+ .pathParam("hid", historyId)
+ .expect()
+ .statusCode(200)
+ .log().ifError()
+ .when()
+ .get("/operation/history/{hid}");
+
+ // See if we also find it when we are looking for histories on the resource
+ Response response =
+ given()
+ .queryParam("resourceId", platformId)
+ .header(acceptJson)
+ .expect()
+ .statusCode(200)
+ .log().ifError()
+ .when()
+ .get("/operation/history");
- // See if we also find it when we are looking for histories on the resource
+ // compare
+ List<Map<String,Object>> list = response.as(List.class);
+ boolean found = false;
+ for (Map<String,Object> map : list) {
+ if (map.get("jobId").equals(historyId)) {
+ found = true;
+ }
+ }
+ assert found;
+ }
+
+ private void waitForTerminationAndDelete(String historyId) throws InterruptedException {
+ boolean done = false;
+ int count = 0;
+ while (!done) {
Response response =
given()
- .queryParam("resourceId",platformId)
.header(acceptJson)
- .expect()
- .statusCode(200)
- .log().ifError()
+ .pathParam("hid", historyId)
.when()
- .get("/operation/history");
-
- // compare
- List<Map<String,Object>> list = response.as(List.class);
- boolean found = false;
- for (Map<String,Object> map : list) {
- if (map.get("jobId").equals(historyId)) {
- found = true;
- }
- }
- assert found;
+ .get("/operation/history/{hid}");
- } finally {
+ JsonPath jsonPath = response.jsonPath();
+ String status= jsonPath.getString("status");
+ int code = response.statusCode();
- // Wait until the operation has finished and then delete
- boolean done = false;
- int count = 0;
- while (!done) {
- Response response =
- given()
- .header(acceptJson)
- .pathParam("hid", historyId)
- .when()
- .get("/operation/history/{hid}");
-
- JsonPath jsonPath = response.jsonPath();
- String status= jsonPath.getString("status");
- int code = response.statusCode();
-
- if (code==200 && (status.equals("Success") || status.equals("Failed"))) {
- done = true;
- } else {
- Thread.sleep(2000);
- }
- count ++;
- assert count < 10 :"Waited for 20sec -- something is wrong";
+ if (code==200 && (status.equals("Success") || status.equals("Failed"))) {
+ done = true;
+ } else {
+ Thread.sleep(2000);
}
-
- // Delete the history item
- given()
- .pathParam("hid",historyId)
- .expect()
- .statusCode(204)
- .log().ifError()
- .when()
- .delete("/operation/history/{hid}");
-
+ count ++;
+ assert count < 10 :"Waited for 20sec -- something is wrong";
}
+
+ // Delete the history item
+ given()
+ .pathParam("hid", historyId)
+ .expect()
+ .statusCode(204)
+ .log().ifError()
+ .when()
+ .delete("/operation/history/{hid}");
}
@Test
@@ -365,20 +410,7 @@ public class OperationsTest extends AbstractBase {
int platformId = findIdOfARealPlatform();
- Operation draft =
- given()
- .header(acceptJson)
- .pathParam("definitionId",definitionId)
- .queryParam("resourceId",platformId)
- .expect()
- .statusCode(200)
- .log().ifError()
- .when()
- .post("/operation/definition/{definitionId}")
- .as(Operation.class);
-
- assert draft != null;
- assert draft.getDefinitionId() == definitionId;
+ Operation draft = getADraftOperation(platformId, discoveryDefinitionId);
int draftId = draft.getId();
@@ -423,20 +455,7 @@ public class OperationsTest extends AbstractBase {
int platformId = findIdOfARealPlatform();
- Operation draft =
- given()
- .header(acceptJson)
- .pathParam("definitionId",definitionId)
- .queryParam("resourceId",platformId)
- .expect()
- .statusCode(200)
- .log().ifError()
- .when()
- .post("/operation/definition/{definitionId}")
- .as(Operation.class);
-
- assert draft != null;
- assert draft.getDefinitionId() == definitionId;
+ Operation draft = getADraftOperation(platformId, discoveryDefinitionId);
int draftId = draft.getId();
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
index e16213c..c3f7b2b 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/Link.java
@@ -19,7 +19,7 @@
package org.rhq.modules.integrationTests.restApi.d;
-import org.codehaus.jackson.map.annotate.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
/**
* A Link inside objects
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkDeserializer.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkDeserializer.java
index 5d45724..c138761 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkDeserializer.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/d/LinkDeserializer.java
@@ -21,19 +21,19 @@ package org.rhq.modules.integrationTests.restApi.d;
import java.io.IOException;
-import org.codehaus.jackson.JsonNode;
-import org.codehaus.jackson.JsonParser;
-import org.codehaus.jackson.JsonProcessingException;
-import org.codehaus.jackson.ObjectCodec;
-import org.codehaus.jackson.map.DeserializationContext;
-import org.codehaus.jackson.map.JsonDeserializer;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonNode;
/**
* Custom deserializer for link object that are now in the form { relname : { "href": link}}
*
* @author Heiko W. Rupp
*/
-public class LinkDeserializer extends JsonDeserializer<Link>{
+public class LinkDeserializer extends JsonDeserializer<Link> {
@Override
public Link deserialize(JsonParser jsonParser,
@@ -41,8 +41,8 @@ public class LinkDeserializer extends JsonDeserializer<Link>{
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
- String rel = node.getFieldNames().next();
- String href = node.getElements().next().get("href").getTextValue();
+ String rel = node.fieldNames().next();
+ String href = node.elements().next().get("href").textValue();
return new Link(rel,href);
commit 9e805285971e53b4483db2a6f15e5ae9c81d884f
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jul 25 16:50:06 2013 +0200
Make CassandraNodeComponent shutdown operation wait for server to go down (unless running on OSX)
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index 37aa425..3e55a93 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -68,6 +68,7 @@ import org.rhq.core.pluginapi.util.ProcessExecutionUtility;
import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.ProcessInfo;
+import org.rhq.core.system.ProcessInfo.ProcessInfoSnapshot;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.util.StringUtil;
import org.rhq.core.util.exception.ThrowableUtil;
@@ -140,7 +141,6 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
if (isStorageServiceReachable()) {
return AvailabilityType.UP;
}
-
return AvailabilityType.DOWN;
} finally {
long totalTimeMillis = NANOSECONDS.toMillis(System.nanoTime() - start);
@@ -190,6 +190,7 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
if (name.equals("shutdown")) {
OperationResult operationResult = shutdownNode();
+ waitForNodeToGoDown();
return operationResult;
} else if (name.equals("start")) {
return startNode();
@@ -238,7 +239,7 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
protected OperationResult stopNode() {
ProcessInfo process = getResourceContext().getNativeProcess();
- if (processInfo == null) {
+ if (process == null) {
LOG.warn("Failed to obtain process info. It appears Cassandra is already shutdown.");
return new OperationResult("Failed to obtain process info. It appears Cassandra is already shutdown.");
}
@@ -265,6 +266,39 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
+ private void waitForNodeToGoDown() throws InterruptedException {
+ if (OperatingSystem.getInstance().getName().equals(OperatingSystem.NAME_MACOSX)) {
+ // See this thread on VMWare forum: http://communities.vmware.com/message/2187972#2187972
+ // Unfortunately there is no work around for this failure on Mac OSX so the method will silently return on
+ // this platform.
+ return;
+ }
+ for (ProcessInfoSnapshot processInfoSnapshot = getProcessInfoSnapshot();; processInfoSnapshot = getProcessInfoSnapshot()) {
+ if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
+ // Process not found, so it died, that's fine
+ // OR
+ // Process info says process is no longer running, that's fine as well
+ break;
+ }
+ if (getResourceContext().getComponentInvocationContext().isInterrupted()) {
+ // Operation canceled or timed out
+ throw new InterruptedException();
+ }
+ // Process is still running, wait a second and check again
+ Thread.sleep(SECONDS.toMillis(2));
+ }
+ }
+
+ private ProcessInfoSnapshot getProcessInfoSnapshot() {
+ ProcessInfoSnapshot processInfoSnapshot = (processInfo == null) ? null : processInfo.freshSnapshot();
+ if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
+ processInfo = getResourceContext().getNativeProcess();
+ // Safe to get prior snapshot here, we've just recreated the process info instance
+ processInfoSnapshot = (processInfo == null) ? null : processInfo.priorSnaphot();
+ }
+ return processInfoSnapshot;
+ }
+
protected OperationResult startNode() {
Configuration pluginConfig = getResourceContext().getPluginConfiguration();
String baseDir = pluginConfig.getSimpleValue("baseDir");
commit 127c679b6d152398d4645f3e6dee488f3d043841
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Thu Jul 25 08:51:47 2013 -0500
Add support for updating the JMX Port. The process sends a restart to the resource last to avoid putting the Server in maintenance mode.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index 1441785..7a7eda4 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -103,7 +103,7 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
private static final String RHQ_STORAGE_ADDRESS_PROPERTY = "host";
private static final int OPERATION_QUERY_TIMEOUT = 20000;
- private static final int MAX_ITERATIONS = 6;
+ private static final int MAX_ITERATIONS = 10;
private static final String UPDATE_CONFIGURATION_OPERATION = "updateConfiguration";
private static final String RESTART_OPERATION = "restart";
@@ -639,16 +639,45 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
StorageNode storageNode = findStorageNodeByAddress(storageNodeConfiguration.getStorageNode().getAddress());
if (storageNode != null && storageNode.getResource() != null) {
- Resource storageNodeResource = storageNode.getResource();
Configuration parameters = new Configuration();
parameters.setSimpleValue("jmxPort", storageNodeConfiguration.getJmxPort() + "");
- parameters.setSimpleValue("heapSize", storageNodeConfiguration.getHeapSize() + "");
- parameters.setSimpleValue("heapNewSize", storageNodeConfiguration.getHeapNewSize() + "");
- parameters.setSimpleValue("threadStackSize", storageNodeConfiguration.getThreadStackSize() + "");
- parameters.setSimpleValue("restartIfRequired", "true");
+ if (storageNodeConfiguration.getHeapSize() != null) {
+ parameters.setSimpleValue("heapSize", storageNodeConfiguration.getHeapSize() + "");
+ }
+ if (storageNodeConfiguration.getHeapNewSize() != null) {
+ parameters.setSimpleValue("heapNewSize", storageNodeConfiguration.getHeapNewSize() + "");
+ }
+ if (storageNodeConfiguration.getThreadStackSize() != null) {
+ parameters.setSimpleValue("threadStackSize", storageNodeConfiguration.getThreadStackSize() + "");
+ }
+ parameters.setSimpleValue("restartIfRequired", "false");
- return runOperationAndWaitForResult(subject, storageNodeResource, UPDATE_CONFIGURATION_OPERATION,
+ Resource storageNodeResource = storageNode.getResource();
+
+ boolean result = runOperationAndWaitForResult(subject, storageNodeResource, UPDATE_CONFIGURATION_OPERATION,
parameters);
+
+ if (result) {
+ Configuration storageNodePluginConfig = configurationManager.getPluginConfiguration(subject,
+ storageNodeResource.getId());
+
+ String existingJMXPort = storageNodePluginConfig.getSimpleValue("jmxPort");
+ storageNodePluginConfig.setSimpleValue("jmxPort", storageNodeConfiguration.getJmxPort() + "");
+
+ String existingConnectionURL = storageNodePluginConfig.getSimpleValue("connectorAddress");
+ String newConnectionURL = existingConnectionURL.replace(":" + existingJMXPort + "/", ":"
+ + storageNodeConfiguration.getJmxPort() + "/");
+ storageNodePluginConfig.setSimpleValue("connectorAddress", newConnectionURL);
+
+ configurationManager.updatePluginConfiguration(subject, storageNodeResource.getId(),
+ storageNodePluginConfig);
+
+ storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
+ entityManager.merge(storageNode);
+ entityManager.flush();
+
+ return runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION, null);
+ }
}
return false;
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
index 41a0c42..125f4d2 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
@@ -117,6 +117,7 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
//update storage node jvm settings
Configuration config = new Configuration();
+ config.put(new PropertySimple("jmxPort", params.getSimpleValue("jmxPort")));
config.put(new PropertySimple("minHeapSize", params.getSimpleValue("heapSize")));
config.put(new PropertySimple("maxHeapSize", params.getSimpleValue("heapSize")));
config.put(new PropertySimple("heapNewSize", params.getSimpleValue("heapNewSize")));
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
index 1667877..dd5b8a2 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeConfigDelegate.java
@@ -148,11 +148,17 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
}
}
- private void updateCassandraJvmProps(Configuration config) throws IOException {
+ private void updateCassandraJvmProps(Configuration newConfig) throws IOException {
PropertiesFileUpdate propertiesUpdater = new PropertiesFileUpdate(jvmOptsFile.getAbsolutePath());
Properties properties = propertiesUpdater.loadExistingProperties();
- String maxHeapSize = config.getSimpleValue("maxHeapSize");
+ String jmxPort = newConfig.getSimpleValue("jmxPort");
+ if (!StringUtil.isEmpty(jmxPort)) {
+ validateIntegerArg("jmx_port", jmxPort);
+ properties.setProperty("jmx_port", jmxPort);
+ }
+
+ String maxHeapSize = newConfig.getSimpleValue("maxHeapSize");
if (!StringUtil.isEmpty(maxHeapSize)) {
validateHeapArg("maxHeapSize", maxHeapSize);
// We want min and max heap to be the same
@@ -160,19 +166,19 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
properties.setProperty("heap_max", "-Xmx" + maxHeapSize);
}
- String heapNewSize = config.getSimpleValue("heapNewSize");
+ String heapNewSize = newConfig.getSimpleValue("heapNewSize");
if (!StringUtil.isEmpty(heapNewSize)) {
validateHeapArg("heapNewSize", heapNewSize);
properties.setProperty("heap_new", "-Xmn" + heapNewSize);
}
- String threadStackSize = config.getSimpleValue("threadStackSize");
+ String threadStackSize = newConfig.getSimpleValue("threadStackSize");
if (!StringUtil.isEmpty(threadStackSize)) {
- validateStackArg(threadStackSize);
+ validateIntegerArg("threadStackSize", threadStackSize);
properties.setProperty("thread_stack_size", "-Xss" + threadStackSize + "k");
}
- PropertySimple heapDumpOnOMMError = config.getSimple("heapDumpOnOOMError");
+ PropertySimple heapDumpOnOMMError = newConfig.getSimple("heapDumpOnOOMError");
if (heapDumpOnOMMError != null) {
if (heapDumpOnOMMError.getBooleanValue()) {
properties.setProperty("heap_dump_on_OOMError", "-XX:+HeapDumpOnOutOfMemoryError");
@@ -181,7 +187,7 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
}
}
- String heapDumpDir = useForwardSlash(config.getSimpleValue("heapDumpDir"));
+ String heapDumpDir = useForwardSlash(newConfig.getSimpleValue("heapDumpDir"));
if (!StringUtil.isEmpty(heapDumpDir)) {
properties.setProperty("heap_dump_dir", heapDumpDir);
}
@@ -209,7 +215,7 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
String threadStackSize = config.getSimpleValue("threadStackSize");
if (!StringUtil.isEmpty(threadStackSize)) {
- validateStackArg(threadStackSize);
+ validateIntegerArg("threadStackSize", threadStackSize);
properties.setProperty("set.thread_stack_size", "-Xss" + threadStackSize + "k");
}
@@ -248,11 +254,11 @@ public class StorageNodeConfigDelegate implements ConfigurationFacet {
}
}
- private void validateStackArg(String value) {
+ private void validateIntegerArg(String name, String value) {
try {
Integer.parseInt(value);
} catch (NumberFormatException e) {
- throw new IllegalArgumentException(value + " is not a legal value for the property [threadStackSize]");
+ throw new IllegalArgumentException(value + " is not a legal value for the property [" + name + "]");
}
}
diff --git a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
index aca3ba2..a42040d 100644
--- a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
@@ -32,6 +32,7 @@
<c:simple-property name="baseDir" displayName="Base Directory" description="The base directory from which the Cassandra Daemon was launched." required="false"/>
<c:simple-property name="yamlConfiguration" displayName="YAML Configuration File" description="YAML Configuration File"/>
<c:simple-property name="nativeTransportPort" description="The port on which Cassandra listens for CQL client connections." default="9042" type="integer"/>
+ <c:simple-property name="jmxPort" description="The JMX port for Cassandra" default="7299" type="integer" readOnly="true"/>
<c:simple-property name="host" description="The host on which cassandra listens to CQL client connections" default="localhost"/>
<c:simple-property name="clusterName" description="Cluster name" default="localhost"/>
<c:simple-property name="authenticator" required="true" default="org.apache.cassandra.auth.AllowAllAuthenticator" description="Cassandra client authenticator">
@@ -131,11 +132,11 @@
<operation name="updateConfiguration" description="Updates the node configuration. Will require a separate server restart for the settings to take effect.">
<parameters>
+ <c:simple-property name="jmxPort" type="integer" description="JMX port JVM option."/>
<c:simple-property name="heapSize" type="string" description="The heap size to be used for both -Xms and -Xmx JVM options."/>
<c:simple-property name="heapNewSize" type="string" description="The heap new size to be used be used with -Xmn JVM option."/>
<c:simple-property name="threadStackSize" type="integer" description="The thread stack size. This memory is allocated to each thread off heap. The
value should be an integer that will be interpreted in kilobytes."/>
- <c:simple-property name="jmxPort" type="integer" description="JMX port JVM option."/>
<c:simple-property name="restartIfRequired" type="boolean" required="true" default="false" description="If [true] then restart the server at the end of the update if and if only the updates made require a restart. If [false] the server will not be restarted regardless of the updates made."/>
</parameters>
<results>
commit 925e8c881487e9e27c7b0801890ef6f95f48a352
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Tue Jul 23 16:43:29 2013 -0500
Move the restart operation to the plugin rather than server bean. This simplifies the interface between the server bean and storage node plugin.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
index 9416c67..1441785 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/cloud/StorageNodeManagerBean.java
@@ -645,21 +645,10 @@ public class StorageNodeManagerBean implements StorageNodeManagerLocal, StorageN
parameters.setSimpleValue("heapSize", storageNodeConfiguration.getHeapSize() + "");
parameters.setSimpleValue("heapNewSize", storageNodeConfiguration.getHeapNewSize() + "");
parameters.setSimpleValue("threadStackSize", storageNodeConfiguration.getThreadStackSize() + "");
+ parameters.setSimpleValue("restartIfRequired", "true");
- boolean updateConfigurationResult = runOperationAndWaitForResult(subject, storageNodeResource,
- UPDATE_CONFIGURATION_OPERATION, parameters);
-
- if (updateConfigurationResult) {
- boolean restartResult = runOperationAndWaitForResult(subject, storageNodeResource, RESTART_OPERATION,
- null);
-
- if (restartResult) {
- storageNode.setJmxPort(storageNodeConfiguration.getJmxPort());
- entityManager.persist(storageNode);
-
- return true;
- }
- }
+ return runOperationAndWaitForResult(subject, storageNodeResource, UPDATE_CONFIGURATION_OPERATION,
+ parameters);
}
return false;
diff --git a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
index d9b35b9..41a0c42 100644
--- a/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
+++ b/modules/plugins/rhq-storage/src/main/java/org/rhq/plugins/storage/StorageNodeComponent.java
@@ -111,6 +111,8 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
}
private OperationResult updateConfiguration(Configuration params) {
+ boolean restartIsRequired = false;
+
OperationResult result = new OperationResult("Configuration updated.");
//update storage node jvm settings
@@ -125,6 +127,31 @@ public class StorageNodeComponent extends CassandraNodeComponent implements Oper
if (!configurationUpdate.getStatus().equals(ConfigurationUpdateStatus.SUCCESS)) {
result.setErrorMessage(configurationUpdate.getErrorMessage());
+ } else {
+ if (params.getSimpleValue("heapSize") != null
+ || params.getSimpleValue("heapNewSize") != null
+ || params.getSimpleValue("threadStackSize") != null) {
+ restartIsRequired = true;
+ }
+ }
+
+ //restart the server if:
+ //- requested by the user
+ //- the updates done require restart
+ boolean restartIfRequiredConfig = false;
+ if (params.getSimpleValue("restartIfRequired") != null) {
+ restartIfRequiredConfig = Boolean.parseBoolean(params.getSimpleValue("restartIfRequired"));
+ }
+
+ if (restartIfRequiredConfig && restartIsRequired) {
+ try {
+ OperationResult restartResult = this.invokeOperation("restart", null);
+ if (restartResult.getErrorMessage() != null) {
+ result.setErrorMessage(restartResult.getErrorMessage());
+ }
+ } catch (Exception e) {
+ result.setErrorMessage(e.getMessage());
+ }
}
return result;
diff --git a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
index cd84de6..aca3ba2 100644
--- a/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/rhq-storage/src/main/resources/META-INF/rhq-plugin.xml
@@ -136,6 +136,7 @@
<c:simple-property name="threadStackSize" type="integer" description="The thread stack size. This memory is allocated to each thread off heap. The
value should be an integer that will be interpreted in kilobytes."/>
<c:simple-property name="jmxPort" type="integer" description="JMX port JVM option."/>
+ <c:simple-property name="restartIfRequired" type="boolean" required="true" default="false" description="If [true] then restart the server at the end of the update if and if only the updates made require a restart. If [false] the server will not be restarted regardless of the updates made."/>
</parameters>
<results>
<c:simple-property name="operationResult" description="Outcome of updating the configuration."/>
commit bddfb48623718d3eb99fad6cd1a67bae56a82572
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jul 25 12:16:39 2013 +0200
CassandraNodeComponent#startNode now starts C* with a short CLASSPATH to allow later discovery on Linux
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index 70ba4b2..37aa425 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -47,6 +47,7 @@ import com.datastax.driver.core.Session;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.hyperic.sigar.OperatingSystem;
import org.hyperic.sigar.SigarException;
import org.mc4j.ems.connection.EmsConnection;
import org.mc4j.ems.connection.bean.EmsBean;
@@ -67,7 +68,6 @@ import org.rhq.core.pluginapi.util.ProcessExecutionUtility;
import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.system.ProcessInfo.ProcessInfoSnapshot;
import org.rhq.core.system.SystemInfo;
import org.rhq.core.util.StringUtil;
import org.rhq.core.util.exception.ThrowableUtil;
@@ -186,16 +186,6 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
- private ProcessInfoSnapshot getProcessInfoSnapshot() {
- ProcessInfoSnapshot processInfoSnapshot = (processInfo == null) ? null : processInfo.freshSnapshot();
- if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
- processInfo = getResourceContext().getNativeProcess();
- // Safe to get prior snapshot here, we've just recreated the process info instance
- processInfoSnapshot = (processInfo == null) ? null : processInfo.priorSnaphot();
- }
- return processInfoSnapshot;
- }
-
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
if (name.equals("shutdown")) {
@@ -212,23 +202,6 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
return null;
}
- private void waitForNodeToGoDown() throws InterruptedException {
- for (ProcessInfoSnapshot processInfoSnapshot = getProcessInfoSnapshot();; processInfoSnapshot = getProcessInfoSnapshot()) {
- if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
- // Process not found, so it died, that's fine
- // OR
- // Process info says process is no longer running, that's fine as well
- break;
- }
- if (getResourceContext().getComponentInvocationContext().isInterrupted()) {
- // Operation canceled or timed out
- throw new InterruptedException();
- }
- // Process is still running, wait a second and check again
- Thread.sleep(SECONDS.toMillis(1));
- }
- }
-
@SuppressWarnings("rawtypes")
protected OperationResult shutdownNode() {
ResourceContext<?> context = getResourceContext();
@@ -292,21 +265,19 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
-
protected OperationResult startNode() {
- ResourceContext<?> context = getResourceContext();
- Configuration pluginConfig = context.getPluginConfiguration();
+ Configuration pluginConfig = getResourceContext().getPluginConfiguration();
String baseDir = pluginConfig.getSimpleValue("baseDir");
File binDir = new File(baseDir, "bin");
- File startScript = new File(binDir, getStartScript());
- File pidFile = new File(binDir, "cassandra.pid");
-
- ProcessExecution scriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
- scriptExe.addArguments(asList("-p", pidFile.getAbsolutePath()));
- SystemInfo systemInfo = context.getSystemInformation();
+ if (!startScriptExists(binDir)) {
+ OperationResult failure = new OperationResult("Failed to start Cassandra daemon");
+ failure.setErrorMessage("Start script does not exists");
+ return failure;
+ }
+ ProcessExecution scriptExe = getProcessExecution(binDir);
+ SystemInfo systemInfo = getResourceContext().getSystemInformation();
ProcessExecutionResults results = systemInfo.executeProcess(scriptExe);
-
- if (results.getError() == null) {
+ if (results.getError() == null) {
return new OperationResult("Successfully started Cassandra daemon");
} else {
OperationResult failure = new OperationResult("Failed to start Cassandra daemon");
@@ -315,6 +286,30 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
+ private boolean startScriptExists(File binDir) {
+ File file = new File(binDir, getStartScript());
+ return file.exists() && !file.isDirectory();
+ }
+
+ private ProcessExecution getProcessExecution(File binDir) {
+ ProcessExecution scriptExe;
+ if (OperatingSystem.getInstance().getName().equals(OperatingSystem.NAME_WIN32)) {
+ File startScript = new File(binDir, getStartScript());
+ scriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ } else {
+ // On Linux, when Cassandra is started with an absolute path, the command line is too long and is truncated
+ // in /proc/pid/cmdline (beacuse of a long CLASSPATH made of absolute paths)
+ // This prevents the process from being later discovered because the process query argument criteria
+ // expects org.apache.cassandra.service.CassandraDaemon to be found
+ File startScript = new File("./" + getStartScript());
+ scriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ scriptExe.setCheckExecutableExists(false);
+ }
+ scriptExe.setWorkingDirectory(binDir.getAbsolutePath());
+ scriptExe.addArguments(asList("-p", "cassandra.pid"));
+ return scriptExe;
+ }
+
protected OperationResult restartNode() {
OperationResult result = shutdownNode();
diff --git a/modules/plugins/rhq-storage/pom.xml b/modules/plugins/rhq-storage/pom.xml
index b1d50b8..ab97902 100644
--- a/modules/plugins/rhq-storage/pom.xml
+++ b/modules/plugins/rhq-storage/pom.xml
@@ -121,21 +121,27 @@
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.13</version>
+ <configuration>
+ <includes>
+ <include>**/*ITest.java</include>
+ </includes>
+ <argLine>-Djava.library.path=${pc.lib.dir}</argLine>
+ <systemPropertyVariables>
+ <pc.plugins.dir>${pc.plugins.dir}</pc.plugins.dir>
+ </systemPropertyVariables>
+ <properties>
+ <property>
+ <name>listener</name>
+ <value>org.rhq.test.testng.StdoutReporter</value>
+ </property>
+ </properties>
+ </configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
- <configuration>
- <includes>
- <include>**/*ITest.java</include>
- </includes>
- <argLine>-Djava.library.path=${pc.lib.dir}</argLine>
- <systemPropertyVariables>
- <pc.plugins.dir>${pc.plugins.dir}</pc.plugins.dir>
- </systemPropertyVariables>
- </configuration>
</execution>
<execution>
<id>verify</id>
@@ -155,6 +161,12 @@
<excludes>
<exclude>**/*ITest.java</exclude>
</excludes>
+ <properties>
+ <property>
+ <name>listener</name>
+ <value>org.rhq.test.testng.StdoutReporter</value>
+ </property>
+ </properties>
</configuration>
</plugin>
</plugins>
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index 5bc8b31..f0744a4 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -31,6 +31,7 @@ import java.util.Set;
import com.google.common.collect.Sets;
+import org.hyperic.sigar.OperatingSystem;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
@@ -113,14 +114,8 @@ public class StorageNodeComponentITest {
File binDir = new File(basedir, "bin");
SystemInfo systemInfo = SystemInfoFactory.createSystemInfo();
- File startScript = new File("./cassandra");
- ProcessExecution startScriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
- startScriptExe.setWorkingDirectory(binDir.getAbsolutePath());
- startScriptExe.setCheckExecutableExists(false);
-
- startScriptExe.addArguments(asList("-p", "cassandra.pid"));
- startScriptExe.setCaptureOutput(true);
- ProcessExecutionResults results = systemInfo.executeProcess(startScriptExe);
+ ProcessExecution processExecution = getProcessExecution(binDir);
+ ProcessExecutionResults results = systemInfo.executeProcess(processExecution);
assertEquals(results.getExitCode(), (Integer) 0, "Cassandra failed to start: " + results.getCapturedOutput());
@@ -135,6 +130,22 @@ public class StorageNodeComponentITest {
schemaManager.updateTopology(true);
}
+ private ProcessExecution getProcessExecution(File binDir) {
+ ProcessExecution startScriptExe;
+ if (OperatingSystem.getInstance().getName().equals(OperatingSystem.NAME_WIN32)) {
+ File startScript = new File(binDir, "cassandra.bat");
+ startScriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ } else {
+ File startScript = new File("./cassandra");
+ startScriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ startScriptExe.setCheckExecutableExists(false);
+ }
+ startScriptExe.setWorkingDirectory(binDir.getAbsolutePath());
+ startScriptExe.addArguments(asList("-p", "cassandra.pid"));
+ startScriptExe.setCaptureOutput(true);
+ return startScriptExe;
+ }
+
private void initPluginContainer() {
PluginContainerConfiguration pcConfig = new PluginContainerConfiguration();
File pluginsDir = new File(System.getProperty("pc.plugins.dir"));
commit c1bfeb61b38892c39ac89d813e4a50687a1f56f1
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jul 25 11:04:44 2013 +0200
Update intentional changes file (methods added in StorageNodeManagerRemote)
diff --git a/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml b/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
index 26e9e95..44886b4 100644
--- a/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
+++ b/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
@@ -32,6 +32,20 @@
<difference>
<className>org/rhq/enterprise/server/cloud/StorageNodeManagerRemote</className>
<differenceType>7012</differenceType> <!-- method added to an interface -->
+ <method>org.rhq.core.domain.cloud.StorageNodeConfigurationComposite retrieveConfiguration(org.rhq.core.domain.auth.Subject, org.rhq.core.domain.cloud.StorageNode)</method>
+ <justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
+ </difference>
+
+ <difference>
+ <className>org/rhq/enterprise/server/cloud/StorageNodeManagerRemote</className>
+ <differenceType>7012</differenceType> <!-- method added to an interface -->
+ <method>boolean updateConfiguration(org.rhq.core.domain.auth.Subject, org.rhq.core.domain.cloud.StorageNodeConfigurationComposite)</method>
+ <justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
+ </difference>
+
+ <difference>
+ <className>org/rhq/enterprise/server/cloud/StorageNodeManagerRemote</className>
+ <differenceType>7012</differenceType> <!-- method added to an interface -->
<method>org.rhq.core.domain.util.PageList findNotAcknowledgedStorageNodeAlerts(org.rhq.core.domain.auth.Subject, org.rhq.core.domain.cloud.StorageNode)</method>
<justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
</difference>
commit 79b44e371d1c1c9464ec0660ac0ad26a33a82c4b
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jul 25 09:44:45 2013 +0200
Update intentional changes file (methods added in StorageNodeManagerRemote)
diff --git a/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml b/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
index 58c4eda..26e9e95 100644
--- a/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
+++ b/modules/enterprise/server/jar/intentional-api-changes-since-4.8.0.xml
@@ -29,4 +29,18 @@
<justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
</difference>
+ <difference>
+ <className>org/rhq/enterprise/server/cloud/StorageNodeManagerRemote</className>
+ <differenceType>7012</differenceType> <!-- method added to an interface -->
+ <method>org.rhq.core.domain.util.PageList findNotAcknowledgedStorageNodeAlerts(org.rhq.core.domain.auth.Subject, org.rhq.core.domain.cloud.StorageNode)</method>
+ <justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
+ </difference>
+
+ <difference>
+ <className>org/rhq/enterprise/server/cloud/StorageNodeManagerRemote</className>
+ <differenceType>7012</differenceType> <!-- method added to an interface -->
+ <method>org.rhq.core.domain.util.PageList findAllStorageNodeAlerts(org.rhq.core.domain.auth.Subject, org.rhq.core.domain.cloud.StorageNode)</method>
+ <justification>Adding a method to a remote API interface is safe. This is newly implemented functionality.</justification>
+ </difference>
+
</differences>
commit 00326c0b70787a56e3cbc30e8a8951a22e99c681
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Jul 24 23:07:53 2013 -0400
do availability check via jmx
CassandraNodeComponent previously was doing availability checks at the process
level. Results were some times inconsistent in large part due to different
behaviors on different platforms. Doing a JMX check is a more representative
check since we perform most of the managemeent via JMX. It also eliminates any
platform-specific issues.
The check is done via a direct JMX connection instead of EMS to avoid the
caching issues with EMS that could yield inaccurate results.
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index c41e8e7..70ba4b2 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -35,6 +35,12 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Cluster.Builder;
import com.datastax.driver.core.Session;
@@ -131,10 +137,11 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
public AvailabilityType getAvailability() {
long start = System.nanoTime();
try {
- // Get a fresh snapshot of the process
- ProcessInfoSnapshot processInfoSnapshot = getProcessInfoSnapshot();
- return (processInfoSnapshot != null && processInfoSnapshot.isRunning()) ? AvailabilityType.UP
- : AvailabilityType.DOWN;
+ if (isStorageServiceReachable()) {
+ return AvailabilityType.UP;
+ }
+
+ return AvailabilityType.DOWN;
} finally {
long totalTimeMillis = NANOSECONDS.toMillis(System.nanoTime() - start);
if (LOG.isDebugEnabled()) {
@@ -146,6 +153,39 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
+ private boolean isStorageServiceReachable() {
+ JMXConnector connector = null;
+ try {
+ Configuration pluginConfig = getResourceContext().getPluginConfiguration();
+ String url = pluginConfig.getSimpleValue("connectorAddress");
+ JMXServiceURL serviceURL = new JMXServiceURL(url);
+ connector = JMXConnectorFactory.connect(serviceURL, null);
+
+ MBeanServerConnection serverConnection = connector.getMBeanServerConnection();
+ ObjectName storageService = new ObjectName("org.apache.cassandra.db:type=StorageService");
+
+ // query an attribute to make sure it is in fact available
+ serverConnection.getAttribute(storageService, "NativeTransportRunning");
+
+ return true;
+ } catch (Exception e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Failed to make JMX connection to StorageService", e);
+ }
+ return false;
+ } finally {
+ if (connector != null) {
+ try {
+ connector.close();
+ } catch (IOException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("An error occurred closing the JMX connector", e);
+ }
+ }
+ }
+ }
+ }
+
private ProcessInfoSnapshot getProcessInfoSnapshot() {
ProcessInfoSnapshot processInfoSnapshot = (processInfo == null) ? null : processInfo.freshSnapshot();
if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
@@ -158,10 +198,8 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
-
if (name.equals("shutdown")) {
OperationResult operationResult = shutdownNode();
- waitForNodeToGoDown();
return operationResult;
} else if (name.equals("start")) {
return startNode();
@@ -234,6 +272,8 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
long pid = process.getPid();
try {
+ getEmsConnection().close();
+
process.kill("KILL");
Configuration pluginConfig = getResourceContext().getPluginConfiguration();
commit c2bb725d469642b7985eba579f6b50c30a1a776f
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Wed Jul 24 18:55:25 2013 +0200
Fix StorageNodeComponentITest.shutdownStorageNode
Fixed storage node module pom typo
Made test Cassandra server start with relative paths in classpath (otherwise the command line is too long and gets truncated in /proc/pid/cmdline this preventing the process query to find the server)
Made CassandraNodeComponent shutdown operation to wait for server to go down
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index 8d74ccc..c41e8e7 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -132,12 +132,7 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
long start = System.nanoTime();
try {
// Get a fresh snapshot of the process
- ProcessInfoSnapshot processInfoSnapshot = (processInfo == null) ? null : processInfo.freshSnapshot();
- if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
- processInfo = getResourceContext().getNativeProcess();
- // Safe to get prior snapshot here, we've just recreated the process info instance
- processInfoSnapshot = (processInfo == null) ? null : processInfo.priorSnaphot();
- }
+ ProcessInfoSnapshot processInfoSnapshot = getProcessInfoSnapshot();
return (processInfoSnapshot != null && processInfoSnapshot.isRunning()) ? AvailabilityType.UP
: AvailabilityType.DOWN;
} finally {
@@ -151,11 +146,23 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
}
}
+ private ProcessInfoSnapshot getProcessInfoSnapshot() {
+ ProcessInfoSnapshot processInfoSnapshot = (processInfo == null) ? null : processInfo.freshSnapshot();
+ if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
+ processInfo = getResourceContext().getNativeProcess();
+ // Safe to get prior snapshot here, we've just recreated the process info instance
+ processInfoSnapshot = (processInfo == null) ? null : processInfo.priorSnaphot();
+ }
+ return processInfoSnapshot;
+ }
+
@Override
public OperationResult invokeOperation(String name, Configuration parameters) throws Exception {
if (name.equals("shutdown")) {
- return shutdownNode();
+ OperationResult operationResult = shutdownNode();
+ waitForNodeToGoDown();
+ return operationResult;
} else if (name.equals("start")) {
return startNode();
} else if (name.equals("restart")) {
@@ -167,6 +174,23 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
return null;
}
+ private void waitForNodeToGoDown() throws InterruptedException {
+ for (ProcessInfoSnapshot processInfoSnapshot = getProcessInfoSnapshot();; processInfoSnapshot = getProcessInfoSnapshot()) {
+ if (processInfoSnapshot == null || !processInfoSnapshot.isRunning()) {
+ // Process not found, so it died, that's fine
+ // OR
+ // Process info says process is no longer running, that's fine as well
+ break;
+ }
+ if (getResourceContext().getComponentInvocationContext().isInterrupted()) {
+ // Operation canceled or timed out
+ throw new InterruptedException();
+ }
+ // Process is still running, wait a second and check again
+ Thread.sleep(SECONDS.toMillis(1));
+ }
+ }
+
@SuppressWarnings("rawtypes")
protected OperationResult shutdownNode() {
ResourceContext<?> context = getResourceContext();
diff --git a/modules/plugins/rhq-storage/pom.xml b/modules/plugins/rhq-storage/pom.xml
index df79e40..b1d50b8 100644
--- a/modules/plugins/rhq-storage/pom.xml
+++ b/modules/plugins/rhq-storage/pom.xml
@@ -55,6 +55,20 @@
</dependency>
<dependency>
+ <groupId>${rhq.groupId}</groupId>
+ <artifactId>test-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-nop</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+
+ <dependency>
<groupId>${project.groupId}</groupId>
<artifactId>rhq-cassandra-schema</artifactId>
<version>${project.version}</version>
@@ -70,7 +84,7 @@
<phase>pre-integration-test</phase>
<configuration>
<target>
- <property name="sigar.dir" value="${project.build.directory/sigar}"/>
+ <property name="sigar.dir" value="${project.build.directory}/sigar"/>
<mkdir dir="${pc.basedir}"/>
<mkdir dir="${pc.lib.dir}"/>
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index b668073..5bc8b31 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.storage;
import static java.util.Arrays.asList;
@@ -94,8 +113,10 @@ public class StorageNodeComponentITest {
File binDir = new File(basedir, "bin");
SystemInfo systemInfo = SystemInfoFactory.createSystemInfo();
- File startScript = new File(binDir, "cassandra");
+ File startScript = new File("./cassandra");
ProcessExecution startScriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ startScriptExe.setWorkingDirectory(binDir.getAbsolutePath());
+ startScriptExe.setCheckExecutableExists(false);
startScriptExe.addArguments(asList("-p", "cassandra.pid"));
startScriptExe.setCaptureOutput(true);
@@ -176,8 +197,7 @@ public class StorageNodeComponentITest {
assertFalse(pidFile.exists(), pidFile + " should be deleted when the storage node is shutdown.");
- // TODO why is this failing?
- //assertNodeIsDown("Expected " + storageNode + " to be DOWN after shutting it down");
+ assertNodeIsDown("Expected " + storageNode + " to be DOWN after shutting it down");
}
@Test(dependsOnMethods = "shutdownStorageNode")
diff --git a/modules/plugins/rhq-storage/src/test/resources/log4j.properties b/modules/plugins/rhq-storage/src/test/resources/log4j.properties
deleted file mode 100644
index 67db049..0000000
--- a/modules/plugins/rhq-storage/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# /*
-# * RHQ Management Platform
-# * Copyright (C) 2005-2012 Red Hat, Inc.
-# * All rights reserved.
-# *
-# * This program is free software; you can redistribute it and/or modify
-# * it under the terms of the GNU General Public License, version 2, as
-# * published by the Free Software Foundation, and/or the GNU Lesser
-# * General Public License, version 2.1, also as published by the Free
-# * Software Foundation.
-# *
-# * This program is distributed in the hope that it will be useful,
-# * but WITHOUT ANY WARRANTY; without even the implied warranty of
-# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# * GNU General Public License and the GNU Lesser General Public License
-# * for more details.
-# *
-# * You should have received a copy of the GNU General Public License
-# * and the GNU Lesser General Public License along with this program;
-# * if not, write to the Free Software Foundation, Inc.,
-# * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-# */
-#
-
-log4j.rootCategory=WARN, FILE, CONSOLE
-
-log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
-log4j.appender.FILE.DatePattern='.'yyyy-MM-dd
-log4j.appender.FILE.File=./target/test.log
-log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
-log4j.appender.FILE.layout.ConversionPattern=%d{ABSOLUTE} %-5p %c %m%n
-#log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
-log4j.appender.FILE.Append=false
-
-log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
-log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
-#log4j.appender.CONSOLE.layout.ConversionPattern=%5p %d{HH:mm:ss,SSS} %m%n
-log4j.appender.CONSOLE.layout.ConversionPattern=%d{ABSOLUTE} %-5p %c %m%n
-
-log4j.logger.org.rhq=DEBUG
-log4j.logger.com.datastax=DEBUG
diff --git a/modules/plugins/rhq-storage/src/test/resources/log4j.xml b/modules/plugins/rhq-storage/src/test/resources/log4j.xml
new file mode 100644
index 0000000..ec3cd98
--- /dev/null
+++ b/modules/plugins/rhq-storage/src/test/resources/log4j.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+
+<!-- | For more configuration information and examples, see the Jakarta Log4j | website: http://jakarta.apache.org/log4j -->
+
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <param name="Target" value="System.out" />
+ <param name="Threshold" value="WARN" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%-5p %d{dd-MM HH:mm:ss,SSS} (%F:%M:%L) - %m%n" />
+ </layout>
+ </appender>
+
+ <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
+ <param name="File" value="target/test.log" />
+ <param name="Append" value="false" />
+ <param name="Threshold" value="DEBUG" />
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%-5p %d{dd-MM HH:mm:ss,SSS} (%F:%M:%L) - %m%n" />
+ </layout>
+ </appender>
+
+ <logger name="org.rhq">
+ <level value="DEBUG" />
+ </logger>
+
+ <logger name="com.datastax">
+ <level value="DEBUG" />
+ </logger>
+
+ <root>
+ <level value="WARN" />
+ <appender-ref ref="CONSOLE" />
+ <appender-ref ref="FILE" />
+ </root>
+
+</log4j:configuration>
commit 280733b7eb74489f2f31695b3536e804447eb349
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Jul 24 07:47:24 2013 -0400
[BZ 987899] remove and create pid file during shutdown and start operations
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index cf24fcd..8d74ccc 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -18,6 +18,7 @@
*/
package org.rhq.plugins.cassandra;
+import static java.util.Arrays.asList;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.rhq.core.system.OperatingSystemType.WINDOWS;
@@ -210,6 +211,14 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
long pid = process.getPid();
try {
process.kill("KILL");
+
+ Configuration pluginConfig = getResourceContext().getPluginConfiguration();
+ File basedir = new File(pluginConfig.getSimpleValue("baseDir"));
+ File binDir = new File(basedir, "bin");
+ File pidFile = new File(binDir, "cassandra.pid");
+
+ pidFile.delete();
+
return new OperationResult("Successfully shut down Cassandra daemon with pid " + pid);
} catch (SigarException e) {
LOG.warn("Failed to shut down Cassandra node with pid " + pid, e);
@@ -226,8 +235,10 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
String baseDir = pluginConfig.getSimpleValue("baseDir");
File binDir = new File(baseDir, "bin");
File startScript = new File(binDir, getStartScript());
+ File pidFile = new File(binDir, "cassandra.pid");
ProcessExecution scriptExe = ProcessExecutionUtility.createProcessExecution(startScript);
+ scriptExe.addArguments(asList("-p", pidFile.getAbsolutePath()));
SystemInfo systemInfo = context.getSystemInformation();
ProcessExecutionResults results = systemInfo.executeProcess(scriptExe);
diff --git a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
index cd9f148..b668073 100644
--- a/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
+++ b/modules/plugins/rhq-storage/src/test/java/org/rhq/plugins/storage/StorageNodeComponentITest.java
@@ -2,7 +2,9 @@ package org.rhq.plugins.storage;
import static java.util.Arrays.asList;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
import java.io.File;
import java.net.InetAddress;
@@ -168,8 +170,34 @@ public class StorageNodeComponentITest {
new Configuration(), timeout);
assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The shutdown operation failed");
+
+ File binDir = new File(basedir, "bin");
+ File pidFile = new File(binDir, "cassandra.pid");
+
+ assertFalse(pidFile.exists(), pidFile + " should be deleted when the storage node is shutdown.");
+
// TODO why is this failing?
- assertNodeIsDown("Expected " + storageNode + " to be DOWN after shutting it down");
+ //assertNodeIsDown("Expected " + storageNode + " to be DOWN after shutting it down");
+ }
+
+ @Test(dependsOnMethods = "shutdownStorageNode")
+ public void restartStorageNode() {
+ OperationManager operationManager = PluginContainer.getInstance().getOperationManager();
+ OperationServicesAdapter operationsService = new OperationServicesAdapter(operationManager);
+
+ long timeout = 1000 * 60;
+ OperationContextImpl operationContext = new OperationContextImpl(storageNode.getId());
+ OperationServicesResult result = operationsService.invokeOperation(operationContext, "start",
+ new Configuration(), timeout);
+
+ assertEquals(result.getResultCode(), OperationServicesResultCode.SUCCESS, "The start operation failed.");
+
+ File binDir = new File(basedir, "bin");
+ File pidFile = new File(binDir, "cassandra.pid");
+
+ assertTrue(pidFile.exists(), pidFile + " should be created when starting the storage node.");
+
+ assertNodeIsUp("Expected " + storageNode + " to be up after restarting it.");
}
private void assertNodeIsUp(String msg) {
@@ -192,7 +220,8 @@ public class StorageNodeComponentITest {
private Availability getAvailability() {
InventoryManager inventoryManager = PluginContainer.getInstance().getInventoryManager();
- return inventoryManager.getAvailabilityIfKnown(storageNode);
+// return inventoryManager.getAvailabilityIfKnown(storageNode);
+ return inventoryManager.getCurrentAvailability(storageNode);
}
private void executeAvailabilityScan() {
commit 00162805c6037034f445c90390d46e36fd004a3a
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Jul 24 07:08:09 2013 -0400
uncommented code that was done while debugging tests
diff --git a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
index f76da22..cf24fcd 100644
--- a/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
+++ b/modules/plugins/cassandra/src/main/java/org/rhq/plugins/cassandra/CassandraNodeComponent.java
@@ -111,7 +111,7 @@ public class CassandraNodeComponent extends JMXServerComponent<ResourceComponent
clusterBuilder = clusterBuilder.withCredentials(username, password);
}
-// this.cassandraSession = clusterBuilder.build().connect(clusterName);
+ this.cassandraSession = clusterBuilder.build().connect(clusterName);
} catch (Exception e) {
LOG.error("Connect to Cassandra " + host + ":" + nativePort, e);
throw e;
commit eda94d462cb324316e19486c13308b2bd75dad2c
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Jul 24 11:54:53 2013 +0200
BZ 976786 Add a bit more wait time and an additional check if SUCCESS really means it. Return IN_PROGRESS otherwise.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
index 4bfbb7c..7a6fb33 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
@@ -721,6 +721,9 @@ public class ResourceHandlerBean extends AbstractRestBean {
@POST
@Path("/")
+ @ApiErrors({
+ @ApiError(code = 302, reason = "Creation is still happening. Check back with a GET on the Location.")
+ })
@ApiOperation(value = "Create a new resource as a child of an existing resource. ",
notes= "If a handle is given, a content based resource is created; the content identified by the handle is not removed from the content store." +
"If no handle is given, a resource is created from the data of the passed 'resource' object.")
@@ -824,6 +827,11 @@ public class ResourceHandlerBean extends AbstractRestBean {
CreateResourceStatus status = history.getStatus();
+ try {
+ Thread.sleep(2000L); // give the agent time to do the work
+ } catch (InterruptedException e) {
+ ; // nothing
+ }
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
@@ -832,11 +840,16 @@ public class ResourceHandlerBean extends AbstractRestBean {
if ( status == CreateResourceStatus.SUCCESS) {
ResourceWithType rwt = findCreatedResource(history.getParentResource().getId(),history.getCreatedResourceName(),uriInfo);
-
- builder = Response.ok();
- builder.entity(rwt);
+ if (rwt!=null) {
+ builder = Response.ok();
+ builder.entity(rwt);
+ } else {
+ // History says we had success but due to internal timing
+ // the resource is not yet visible, so switch to in_progress
+ status = CreateResourceStatus.IN_PROGRESS;
+ }
}
- else if (status==CreateResourceStatus.IN_PROGRESS) {
+ if (status==CreateResourceStatus.IN_PROGRESS) {
try {
Thread.sleep(2000L); // give the agent time to do the work
@@ -865,6 +878,7 @@ public class ResourceHandlerBean extends AbstractRestBean {
@GET
@Path("/creationStatus/{id}")
@ApiOperation("Get the status of a resource creation for content based resources.")
+ @ApiError(code = 302, reason = "Creation is still going on. Check back later with the same URL.")
public Response getHistoryItem(@PathParam("id") int historyId, @Context HttpHeaders headers, @Context UriInfo uriInfo) {
CreateResourceHistory history;
@@ -888,13 +902,17 @@ public class ResourceHandlerBean extends AbstractRestBean {
if (status== CreateResourceStatus.SUCCESS) {
ResourceWithType rwt = findCreatedResource(history.getParentResource().getId(),history.getCreatedResourceName(),uriInfo);
-
- builder = Response.ok();
- setCachingHeader(builder, 600);
- builder.entity(rwt);
-
+ if (rwt!=null) {
+ builder = Response.ok();
+ setCachingHeader(builder, 600);
+ builder.entity(rwt);
+ } else {
+ // History says we had success but due to internal timing
+ // the resource is not yet visible, so switch to in_progress
+ status = CreateResourceStatus.IN_PROGRESS;
+ }
}
- else if (status==CreateResourceStatus.IN_PROGRESS) {
+ if (status==CreateResourceStatus.IN_PROGRESS) {
UriBuilder uriBuilder = uriInfo.getRequestUriBuilder();
@@ -913,6 +931,14 @@ public class ResourceHandlerBean extends AbstractRestBean {
}
+ /**
+ * Find the created resource by its name and parent. Will only return it
+ * if the resource is already committed.
+ * @param parentId Id of the parent
+ * @param name Name of the resource to find
+ * @param uriInfo UriInfo object to fill links in the returned resource
+ * @return A ResourceWithType if found, null otherwise.
+ */
private ResourceWithType findCreatedResource(int parentId, String name, UriInfo uriInfo) {
ResourceCriteria criteria = new ResourceCriteria();
criteria.setStrict(true);
@@ -920,6 +946,9 @@ public class ResourceHandlerBean extends AbstractRestBean {
criteria.addFilterName(name);
criteria.addFilterInventoryStatus(InventoryStatus.COMMITTED);
List<Resource> resources = resMgr.findResourcesByCriteria(caller,criteria);
+ if (resources.size()==0) {
+ return null;
+ }
Resource res = resources.get(0);
return fillRWT(res,uriInfo);
}
diff --git a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java
index 8303513..b12eea3 100644
--- a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java
+++ b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ContentTest.java
@@ -222,8 +222,10 @@ public class ContentTest extends AbstractBase {
int status = response.getStatusCode();
String location = response.getHeader("Location");
- System.out.println("\nLocation " + location + "\n\n");
- assert location!=null;
+ if (status!=200) {
+ System.out.println("\nLocation " + location + "\n\n");
+ assert location!=null;
+ }
// We need to check what we got. A 302 means the deploy is still
// in progress, so we need to wait a little longer
@@ -244,6 +246,7 @@ public class ContentTest extends AbstractBase {
createdResourceId = response.jsonPath().getInt("resourceId");
+ System.out.flush();
System.out.println("\n Deploy is done, resource Id = " + createdResourceId + " \n");
System.out.flush();
@@ -254,6 +257,7 @@ public class ContentTest extends AbstractBase {
// Remove the uploaded content
removeContent(handle, false);
+ System.out.flush();
System.out.println("\n Content removed \n");
System.out.flush();
commit 6c63ae75f9d688d9cbe3c62406c58242a574b789
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Jul 23 22:23:06 2013 -0400
initial commit for StorageNodeComponentITest
This is a first stab at some integration tests for the storage plugin.
diff --git a/modules/plugins/rhq-storage/pom.xml b/modules/plugins/rhq-storage/pom.xml
index a88f56e..df79e40 100644
--- a/modules/plugins/rhq-storage/pom.xml
+++ b/modules/plugins/rhq-storage/pom.xml
@@ -10,11 +10,16 @@
<groupId>org.rhq</groupId>
<artifactId>rhq-rhqstorage-plugin</artifactId>
- <packaging>jar</packaging>
<name>RHQ Storage Plugin</name>
<description>A plugin for managing RHQ Storage Nodes</description>
+ <properties>
+ <pc.basedir>${project.build.directory}/plugin-container</pc.basedir>
+ <pc.plugins.dir>${pc.basedir}/plugins</pc.plugins.dir>
+ <pc.lib.dir>${pc.basedir}/lib</pc.lib.dir>
+ </properties>
+
<dependencies>
<dependency>
<groupId>${rhq.groupId}</groupId>
@@ -27,7 +32,6 @@
<groupId>${rhq.groupId}</groupId>
<artifactId>rhq-cassandra-plugin</artifactId>
<version>${project.version}</version>
- <!--<scope>provided</scope>-->
</dependency>
<dependency>
@@ -35,8 +39,113 @@
<artifactId>org-mc4j-ems</artifactId>
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-cassandra-ccm-core</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-platform-plugin</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-cassandra-schema</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>pre-integration-test</phase>
+ <configuration>
+ <target>
+ <property name="sigar.dir" value="${project.build.directory/sigar}"/>
+
+ <mkdir dir="${pc.basedir}"/>
+ <mkdir dir="${pc.lib.dir}"/>
+ <mkdir dir="${pc.plugins.dir}"/>
+
+ <copy file="${org.rhq:rhq-platform-plugin:jar}" todir="${pc.plugins.dir}"/>
+ <copy file="${org.rhq:rhq-jmx-plugin:jar}" todir="${pc.plugins.dir}"/>
+ <copy file="${org.rhq:rhq-cassandra-plugin:jar}" todir="${pc.plugins.dir}"/>
+ <copy file="${project.build.directory}/${project.build.finalName}.jar" todir="${pc.plugins.dir}"/>
+
+ <unzip src="${org.hyperic:sigar-dist:zip}" dest="${sigar.dir}">
+ <patternset>
+ <include name="**/lib/sigar.jar" />
+ <include name="**/lib/bcel*.jar" />
+ <include name="**/lib/*.so" />
+ <include name="**/lib/*.sl" />
+ <include name="**/lib/*.dll" />
+ <include name="**/lib/*.dylib" />
+ </patternset>
+ </unzip>
+ <move todir="${pc.lib.dir}" flatten="true">
+ <fileset dir="${sigar.dir}" includes="**/lib/*"/>
+ </move>
+ <delete dir="${sigar.dir}"/>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-failsafe-plugin</artifactId>
+ <version>2.13</version>
+ <executions>
+ <execution>
+ <id>integration-test</id>
+ <goals>
+ <goal>integration-test</goal>
+ </goals>
+ <configuration>
+ <includes>
+ <include>**/*ITest.java</include>
+ </includes>
+ <argLine>-Djava.library.path=${pc.lib.dir}</argLine>
+ <systemPropertyVariables>
+ <pc.plugins.dir>${pc.plugins.dir}</pc.plugins.dir>
+ </systemPropertyVariables>
+ </configuration>
+ </execution>
+ <execution>
+ <id>verify</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <testFailureIgnore>false</testFailureIgnore>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/*ITest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ &