.classpath
| 1
modules/common/drift/src/main/java/org/rhq/common/drift/Headers.java
| 6
modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
| 2
modules/core/dbutils/pom.xml
| 2
modules/core/dbutils/src/main/java/org/rhq/core/db/DatabaseType.java
| 20
modules/core/dbutils/src/main/java/org/rhq/core/db/OracleDatabaseType.java
| 4
modules/core/dbutils/src/main/java/org/rhq/core/db/PostgresqlDatabaseType.java
| 2
modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftPathDirectoryDatabaseUpgradeTask.java
| 71
modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
| 116
modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
| 60
modules/core/dbutils/src/test/java/org/rhq/core/db/ant/dbupdate/SchemaVersionTest.java
| 2
modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceError.java
| 4
modules/core/domain/src/main/java/org/rhq/core/domain/resource/group/ResourceGroup.java
| 15
modules/core/plugin-container/src/main/java/org/rhq/core/pc/bundle/BundleManager.java
| 39
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
| 192
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
| 85
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueue.java
| 2
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueueImpl.java
| 31
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
| 5
modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftDetectorTest.java
| 94
modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
| 2
modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
| 23
modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
| 4
modules/enterprise/binding/src/main/java/org/rhq/bindings/output/TabularWriter.java
| 94
modules/enterprise/binding/src/test/java/org/rhq/bindings/TabularWriterTest.java
| 58
modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
| 2
modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
| 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/LinkManager.java
| 9
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
| 61
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/create/BundleUploadDataStep.java
| 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleDeploymentListView.java
| 10
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleResourceDeploymentHistoryListView.java
| 3
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/carousel/Carousel.java
| 14
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/form/IsLongValidator.java
| 13
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizard.java
| 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizardStep.java
| 5
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/Wizard.java
| 1
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardStep.java
| 38
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardView.java
| 13
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
| 7
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/groups/graph/ResourceGroupGraphPortlet.java
| 41
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/resource/graph/ResourceGraphPortlet.java
| 46
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselMemberView.java
| 1
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
| 63
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
| 72
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionsView.java
| 35
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDetailsView.java
| 3
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftSnapshotView.java
| 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftPinTemplateWizard.java
| 19
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/SystemGWTService.java
| 11
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/summary/ActivityView.java
| 46
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/LiveGraphView.java
| 9
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/factory/ResourceFactoryConfigurationStep.java
| 37
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
| 29
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages.properties
| 6
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_de.properties
| 6
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
| 6
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_pt.properties
| 6
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_zh.properties
| 6
modules/enterprise/gui/installer-war/src/main/webapp/header.jsp
| 2
modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
| 6
modules/enterprise/gui/rest-war/src/main/webapp/bars_simple.html
| 206
modules/enterprise/gui/rest-war/src/main/webapp/index.html
| 1
modules/enterprise/gui/rest-war/src/main/webapp/js/jquery.js
| 9266 ++++++++++
modules/enterprise/gui/rest-war/src/main/webapp/js/resource_tree.js
| 3
modules/enterprise/gui/rest-war/src/main/webapp/js/rhq.js
| 120
modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
| 92
modules/enterprise/gui/rest-war/src/main/webapp/stacked1.html
| 74
modules/enterprise/gui/rest-war/src/main/webapp/stacked2.html
| 182
modules/enterprise/gui/rest-war/src/main/webapp/whisker.html
| 92
modules/enterprise/gui/rest-war/src/main/webapp/whisker2.html
| 61
modules/enterprise/pom.xml
| 1
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
| 1
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
| 1
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
| 4
modules/enterprise/remoting/cli/src/main/samples/drift.js
| 60
modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
| 2
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java
| 506
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClientProxy.java
| 101
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
| 506
modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
| 101
modules/enterprise/server/container/pom.xml
| 19
modules/enterprise/server/container/src/main/downloadable-resources/cli-alert-scripts/fix-bundle-deployment.js
| 467
modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-log4j.xml
| 6
modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
| 170
modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/index.html
| 6
modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/start.jsf
| 2
modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/welcome.jsf
| 2
modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
| 46
modules/enterprise/server/ear/pom.xml
| 69
modules/enterprise/server/itests/pom.xml
| 2
modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/server/drift/DriftTemplateManagerBeanTest.java
| 112
modules/enterprise/server/jar/pom.xml
| 32
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossRemote.java
| 15
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
| 51
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftUtil.java
| 59
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
| 239
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerLocal.java
| 14
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/ResourceGroupManagerBean.java
| 5
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
| 283
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
| 38
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
| 286
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
| 78
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
| 266
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
| 49
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java
| 315
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
| 94
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ParameterMissingException.java
| 12
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
| 207
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
| 60
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
| 68
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
| 13
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerBean.java
| 12
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerLocal.java
| 4
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertDefinitionRest.java
| 3
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertRest.java
| 4
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AvailabilityRest.java
| 3
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
| 82
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Link.java
| 50
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
| 4
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
| 58
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationDefinitionRest.java
| 52
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationHistoryRest.java
| 72
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationRest.java
| 117
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
| 28
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Status.java
| 68
modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
| 49
modules/enterprise/server/jar/src/main/resources/rest_templates/listGroup.ftl
| 32
modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
| 4
modules/enterprise/server/jar/src/main/resources/rest_templates/metricSchedule.ftl
| 9
modules/enterprise/server/jar/src/main/resources/rest_templates/resourceWithType.ftl
| 2
modules/enterprise/server/jar/src/main/resources/rest_templates/status.ftl
| 21
modules/enterprise/server/plugins/drift-mongodb/src/main/java/org/rhq/enterprise/server/plugins/drift/mongodb/MongoDBDriftServer.java
| 6
modules/integration-tests/pom.xml
| 42
modules/integration-tests/rest-api/README.md
| 13
modules/integration-tests/rest-api/pom.xml
| 56
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/GetStatusTest.java
| 78
modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ResourcesTest.java
| 104
modules/jopr/modules-plugins-pom.xml
| 148
modules/jopr/modules-pom.xml
| 248
modules/plugins/jboss-as-5/src/main/resources/META-INF/rhq-plugin.xml
| 18
modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
| 96
modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
| 51
modules/plugins/platform/src/main/java/org/rhq/plugins/platform/CpuComponent.java
| 11
modules/plugins/rhq-agent/src/main/java/org/rhq/plugins/agent/AgentDiscoveryComponent.java
| 2
modules/plugins/script/src/main/java/org/rhq/plugins/script/ScriptDiscoveryComponent.java
| 2
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatDiscoveryComponent.java
| 8
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatServerOperationsDelegate.java
| 4
modules/test-utils/src/main/java/org/rhq/test/DuplicatePackagesDetector.java
| 194
pom.xml
| 3
150 files changed, 15452 insertions(+), 2339 deletions(-)
New commits:
commit 1ca18175b45456a6963f706cf1c4c7427384edcb
Merge: 579510c e8db9a1
Author: John Sanda <jsanda(a)redhat.com>
Date: Sat Dec 10 12:46:22 2011 -0500
Merge branch 'master' into jsanda/drift
Conflicts:
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
diff --cc
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
index c8c3bb0,21341c4..848ae93
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
@@@ -210,37 -216,42 +216,41 @@@ public class DriftDetector implements R
// If the basedir is still valid we need to do a directory tree scan to look for
newly added files
if (basedir.isDirectory()) {
- forEachFile(basedir, new FilterFileVisitor(basedir,
schedule.getDriftDefinition().getIncludes(), schedule
- .getDriftDefinition().getExcludes(), new FileVisitor() {
- @Override
- public void visit(File file) {
- try {
- if (processedFiles.contains(file)) {
- return;
- }
+ DriftDefinition driftDef = schedule.getDriftDefinition();
+ List<Filter> includes = driftDef.getIncludes();
+ List<Filter> excludes = driftDef.getExcludes();
+
+ for (File dir : getScanDirectories(basedir, includes)) {
+ forEachFile(dir, new FilterFileVisitor(basedir, includes, excludes, new
FileVisitor() {
+ @Override
+ public void visit(File file) {
+ try {
+ if (processedFiles.contains(file)) {
+ return;
+ }
- if (!file.canRead()) {
- if (log.isDebugEnabled()) {
- log.debug("Skipping " + file.getPath() +
" since it is not readable.");
+ if (!file.canRead()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + file.getPath() +
" since it is not readable.");
+ }
return;
}
- }
- if (log.isInfoEnabled()) {
- log.info("Detected added file for " + schedule +
" --> " + file.getAbsolutePath());
- }
+ if (log.isInfoEnabled()) {
+ log.info("Detected added file for " + schedule
+ " --> " + file.getAbsolutePath());
+ }
- FileEntry newEntry = addedFileEntry(relativePath(basedir,
file), sha256(file));
- deltaEntries.add(newEntry);
- snapshotEntries.add(newEntry);
- } catch (IOException e) {
- log.error("An error occurred while generating a drift
change set for " + schedule + ": "
- + e.getMessage());
- throw new DriftDetectionException("An error occurred
while generating a drift change set",
- e);
- }
+ FileEntry newEntry = addedFileEntry(relativePath(basedir, file),
sha256(file),
+ file.lastModified(), file.length());
+ deltaEntries.add(newEntry);
+ snapshotEntries.add(newEntry);
+ } catch (IOException e) {
+ log.error("An error occurred while generating a drift
change set for " + schedule + ": "
+ + e.getMessage());
+ throw new DriftDetectionException("An error occurred while
generating a drift change set", e);
}
- }
- }));
+ }));
+ }
}
if (deltaEntries.isEmpty()) {
diff --cc
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
index 4241b31,2790f13..1182407
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
@@@ -758,16 -771,16 +771,16 @@@ public class DriftManager extends Agent
private void writeSnapshotToFile(DriftSnapshot snapshot, File file, Headers headers)
throws IOException {
ChangeSetWriter writer = changeSetMgr.getChangeSetWriter(file, headers);
try {
- for (Drift drift : snapshot.getDriftInstances()) {
+ for (Drift<?, ?> drift : snapshot.getDriftInstances()) {
switch (drift.getCategory()) {
case FILE_ADDED:
- writer.write(addedFileEntry(drift.getPath(),
drift.getNewDriftFile().getHashId()));
+ writer.write(addedFileEntry(drift.getPath(),
drift.getNewDriftFile().getHashId(), -1L, -1L));
break;
case FILE_CHANGED:
- writer.write(changedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId(), drift
- .getNewDriftFile().getHashId()));
+ writer.write(changedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId(),
+ drift.getNewDriftFile().getHashId(), -1L, -1L));
break;
- default: // FILE_REMOVED
+ default: // FILE_REMOVED
writer.write(removedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId()));
}
}
commit e8db9a190cdf7a1e070bbc077a4d025e30835a24
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 8 16:02:39 2011 -0500
Add 2.117 to remove an obsolete drift table that may exist after upgrades.
diff --git a/modules/core/dbutils/pom.xml b/modules/core/dbutils/pom.xml
index 85a5526..3350cdd 100644
--- a/modules/core/dbutils/pom.xml
+++ b/modules/core/dbutils/pom.xml
@@ -15,7 +15,7 @@
<description>Database schema setup, upgrade and other
utilities</description>
<properties>
- <db.schema.version>2.116</db.schema.version>
+ <db.schema.version>2.117</db.schema.version>
<rhq.ds.type-mapping>${rhq.test.ds.type-mapping}</rhq.ds.type-mapping>
<rhq.ds.db-name>${rhq.test.ds.db-name}</rhq.ds.db-name>
<rhq.ds.connection-url>${rhq.test.ds.connection-url}</rhq.ds.connection-url>
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 92ebef7..9e3bbd6 100644
--- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
+++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
@@ -3823,7 +3823,12 @@
</schemaSpec>
<!-- JON 3.0 RELEASE uses DB Schema 2.116 -->
-
+
+ <schemaSpec version="2.117">
+ <!-- Remove an obsolete drift table that may be hanging around -->
+ <schema-dropTable table="RHQ_DRIFT_TEMPLATE_MAP" />
+ </schemaSpec>
+
</dbupgrade>
</target>
</project>
commit dedfe6ad8738d1be1c5a411943e4e48d4410ffd2
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 8 15:28:14 2011 -0500
[Bug 753866 - DB Upgrade needs more work for existing drift records]
More work to support 4.1 and 4.2 upgrades for drift.
- In the java template map upgrade task
- add logic to generate the rhq_drift_def_template records
- add logic to generate rhq_drift_set records for each initial changeset
- Move some logic from 2.115 to 2.116 to make 2.115 match the
schema produced by a 4.2 dbsetup (fresh db). Note that prior to
an upgrade 4.2 databases must (for now at least) be manually set
to schema version 2.115. They are errantly set to 2.114 in the 4.2
release.
diff --git
a/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
index d060a2a..95d9ef7 100644
---
a/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
+++
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
@@ -20,6 +20,7 @@ package org.rhq.core.db.upgrade;
import java.sql.Connection;
import java.sql.SQLException;
+import java.util.List;
import mazz.i18n.Logger;
@@ -39,6 +40,8 @@ public class DriftTemplateMapDatabaseUpgradeTask implements
DatabaseUpgradeTask
public void execute(DatabaseType databaseType, Connection connection) throws
SQLException {
+ // First generate a drift def template for each of the existing mappings
+
String sql = "" //
+ " insert into RHQ_DRIFT_DEF_TEMPLATE (" //
+ " ID," //
@@ -61,5 +64,53 @@ public class DriftTemplateMapDatabaseUpgradeTask implements
DatabaseUpgradeTask
LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
databaseType.executeSql(connection, sql);
+
+ // Second, assign one of the new templates to each of the existing definitions.
Note that in the
+ // upgrade scenario we are guaranteed to only have 1 template for the given
resource type so the
+ // select below is safe to return a single value.
+ sql = "" //
+ + " update RHQ_DRIFT_DEFINITION dd set DRIFT_DEF_TEMPLATE_ID =" //
+ + " ( select dt.ID" //
+ + " from RHQ_DRIFT_DEF_TEMPLATE dt" //
+ + " join RHQ_RESOURCE_TYPE rt on dt.RESOURCE_TYPE_ID = rt.ID"
//
+ + " join RHQ_RESOURCE res on rt.ID =
res.RESOURCE_TYPE_ID" //
+ + " and dd.RESOURCE_ID = res.ID" //
+ + " )" //
+ + " where dd.DRIFT_DEF_TEMPLATE_ID is null";
+
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
+
+ databaseType.executeSql(connection, sql);
+
+ // third, for each initial snapshot, create the newly required rhq_drift_set and
map the drift to it
+ sql = "select cs.ID from RHQ_DRIFT_CHANGE_SET cs where cs.VERSION =
0";
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
+
+ List<Object[]> rows = databaseType.executeSelectSql(connection, sql);
+
+ sql = "insert into RHQ_DRIFT_SET ( ID ) values ( "
+ + databaseType.getSequenceInsertValue(connection,
"RHQ_DRIFT_SET_ID_SEQ") + " )";
+
+ for (Object[] row : rows) {
+ int csId = ((Number) row[0]).intValue();
+
+ // insert the new drift set row
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
+
+ databaseType.executeSql(connection, sql);
+ int setId = databaseType.getSequenceValue(connection,
"RHQ_DRIFT_SET", "ID");
+
+ // update the drift records for the initial change set to refer to the drift
set
+ String updateSql = "update RHQ_DRIFT set DRIFT_SET_ID = " + setId +
" where DRIFT_CHANGE_SET_ID = " + csId;
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, updateSql);
+
+ databaseType.executeSql(connection, updateSql);
+
+ // update the change set to refer to the drift set
+ updateSql = "update RHQ_DRIFT_CHANGE_SET set DRIFT_SET_ID = " +
setId + " where ID = " + csId;
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, updateSql);
+
+ databaseType.executeSql(connection, updateSql);
+ }
}
}
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 8f10a3b..92ebef7 100644
--- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
+++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
@@ -3629,7 +3629,9 @@
<!-- RHQ 4.1 RELEASE uses DB Schema 2.114 -->
<!-- RHQ 4.2 RELEASE uses DB Schema 2.114
- Note, the 4.1 to 4.2 upgrade is broken because most ofthe changes in 2.115 should
have gone into 4.2.
+ Note, the 4.1 to 4.2 upgrade is broken because a 4.2 dbsetup actually creates a
2.115 schema. So,
+ for 4.2 dbs to upgrade to 4.3 the schema version must first be corrected and set the
schema
+ version to 2.115.
-->
<schemaSpec version="2.115">
@@ -3686,22 +3688,15 @@
</statement>
</schema-directSQL>
- <!-- increase size from 128 to 512 -->
- <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="NAME" columnType="VARCHAR2" precision="512" />
-
<!-- add new columns -->
<schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="DESCRIPTION" columnType="VARCHAR2" precision="512"
/>
<schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="DRIFT_HANDLING_MODE" columnType="VARCHAR2"
precision="20" />
<schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="IS_PINNED" columnType="BOOLEAN"/>
<schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="IS_ATTACHED" columnType="BOOLEAN"/>
- <schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="COMPLIANCE_STATUS" columnType="INTEGER"/>
<schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="DRIFT_DEF_TEMPLATE_ID" columnType="INTEGER"/>
<!-- initialize new required columns -->
<schema-directSQL>
- <statement desc="RHQ_DRIFT_DEFINITION: set compliance_status
column (in compliance)">
- UPDATE RHQ_DRIFT_DEFINITION SET COMPLIANCE_STATUS = 0
- </statement>
<statement targetDBVendor="postgresql"
desc="RHQ_DRIFT_DEFINITION: set is_pinned column (false)">
UPDATE RHQ_DRIFT_DEFINITION SET IS_PINNED = FALSE
</statement>
@@ -3719,8 +3714,7 @@
</statement>
</schema-directSQL>
<schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="IS_PINNED" nullable="FALSE"/>
- <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="IS_ATTACHED" nullable="FALSE"/>
- <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="COMPLIANCE_STATUS" nullable="FALSE"/>
+ <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="IS_ATTACHED" nullable="FALSE"/>
<schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="DRIFT_HANDLING_MODE" nullable="FALSE" />
<!-- add new FK -->
@@ -3771,8 +3765,8 @@
<!-- Add new drift_handling_mode column -->
<schema-addColumn table="RHQ_DRIFT_CHANGE_SET"
column="DRIFT_HANDLING_MODE" columnType="VARCHAR2"
precision="20" />
<schema-directSQL>
- <statement desc="RHQ_DRIFT_DEFINITION: set
drift_handling_mode column (normal)">
- UPDATE RHQ_DRIFT_DEFINITION SET DRIFT_HANDLING_MODE =
'normal'
+ <statement desc="RHQ_DRIFT_CHANGE_SET: set
drift_handling_mode column (normal)">
+ UPDATE RHQ_DRIFT_CHANGE_SET SET DRIFT_HANDLING_MODE =
'normal'
</statement>
</schema-directSQL>
<schema-alterColumn table="RHQ_DRIFT_CHANGE_SET"
column="DRIFT_HANDLING_MODE" nullable="FALSE" />
@@ -3805,11 +3799,31 @@
</schemaSpec>
+<!-- RHQ 4.2 RELEASE uses DB Schema 2.114, which is incorrect, it should have been
2.115. So, for the
+ upgrade to work for an RHQ 4.2 install the schema version must be set to 2.115 prior
to the upgrade.
+
+ The 4.2 upgrade should start with 2.116.
+-->
<schemaSpec version="2.116">
+ <!-- RHQ_DRIFT_DEFINITION changes -->
+ <schema-addColumn table="RHQ_DRIFT_DEFINITION"
column="COMPLIANCE_STATUS" columnType="INTEGER"/>
+ <schema-directSQL>
+ <statement desc="RHQ_DRIFT_DEFINITION: set compliance_status
column (in compliance)">
+ UPDATE RHQ_DRIFT_DEFINITION SET COMPLIANCE_STATUS = 0
+ </statement>
+ </schema-directSQL>
+ <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="COMPLIANCE_STATUS" nullable="FALSE"/>
+
+ <schema-alterColumn table="RHQ_DRIFT_DEFINITION"
column="NAME" columnType="VARCHAR2" precision="512" />
+
+
+ <!-- RHQ_PRD_VER changes -->
<!-- Increase the max length of a product version from 50 to 100.
-->
<schema-alterColumn table="RHQ_PRD_VER"
column="VERSION" columnType="VARCHAR2" precision="100"
/>
</schemaSpec>
+<!-- JON 3.0 RELEASE uses DB Schema 2.116 -->
+
</dbupgrade>
</target>
</project>
commit 37acc1c7cc76cc8a069936d66f0182640ffb569d
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 8 15:22:04 2011 -0500
Change the getSequenceValue() method for postgres db type so that it
actually works.
diff --git
a/modules/core/dbutils/src/main/java/org/rhq/core/db/PostgresqlDatabaseType.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/PostgresqlDatabaseType.java
index 53555ed..4d376f3 100644
--- a/modules/core/dbutils/src/main/java/org/rhq/core/db/PostgresqlDatabaseType.java
+++ b/modules/core/dbutils/src/main/java/org/rhq/core/db/PostgresqlDatabaseType.java
@@ -64,7 +64,7 @@ public abstract class PostgresqlDatabaseType extends DatabaseType {
* @see DatabaseType#getSequenceValue(Connection, String, String)
*/
public int getSequenceValue(Connection conn, String table, String key) throws
SQLException {
- String query = "SELECT last_value FROM " + table + "_" + key
+ "_seq";
+ String query = "SELECT currval('" + table + "_" + key +
"_seq')";
PreparedStatement selectPS = null;
ResultSet rs = null;
commit 518ccccdc4ad05353de809068c289c1fc64a5384
Author: John Sanda <jsanda(a)redhat.com>
Date: Thu Dec 8 13:39:47 2011 -0500
[BZ 761320] Only update the detection schedule if drift detection runs
With this commit we no longer update the detection schedule's next scan
time if detection does not run. Drift detection might not run for a few
different reasons - the definition could be disabled, it it earlier than
the next scheduled run, or the server has not acknowledged the previous
change set sent by the agent.
Some additional debug logging has also been added to output when the
next scheduled run is supposed to happen.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
index 71ddf44..21341c4 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
@@ -19,26 +19,8 @@
package org.rhq.core.pc.drift;
-import static org.rhq.common.drift.FileEntry.addedFileEntry;
-import static org.rhq.common.drift.FileEntry.changedFileEntry;
-import static org.rhq.common.drift.FileEntry.removedFileEntry;
-import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
-import static org.rhq.core.domain.drift.DriftChangeSetCategory.DRIFT;
-import static org.rhq.core.util.file.FileUtil.copyFile;
-import static org.rhq.core.util.file.FileUtil.forEachFile;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.rhq.common.drift.ChangeSetReader;
import org.rhq.common.drift.ChangeSetWriter;
import org.rhq.common.drift.FileEntry;
@@ -49,6 +31,17 @@ import org.rhq.core.domain.drift.Filter;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.core.util.file.FileVisitor;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.util.*;
+
+import static org.rhq.common.drift.FileEntry.*;
+import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
+import static org.rhq.core.domain.drift.DriftChangeSetCategory.DRIFT;
+import static org.rhq.core.util.file.FileUtil.copyFile;
+import static org.rhq.core.util.file.FileUtil.forEachFile;
+
public class DriftDetector implements Runnable {
private Log log = LogFactory.getLog(DriftDetector.class);
@@ -76,14 +69,17 @@ public class DriftDetector implements Runnable {
public void run() {
log.debug("Starting drift detection...");
long startTime = System.currentTimeMillis();
+ boolean updateSchedule = true;
+ DriftDetectionSchedule schedule = null;
try {
if (log.isDebugEnabled()) {
log.debug("Fetching next schedule from " + scheduleQueue);
}
- DriftDetectionSchedule schedule = scheduleQueue.getNextSchedule();
+ schedule = scheduleQueue.getNextSchedule();
if (schedule == null) {
log.debug("No schedules are in the queue.");
+ updateSchedule = false;
return;
}
@@ -93,17 +89,26 @@ public class DriftDetector implements Runnable {
}
if (schedule.getNextScan() > (System.currentTimeMillis() + 100L)) {
- log.debug("Skipping " + schedule + " because it is too
early to do the next detection.");
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + schedule + " because it is too
early to do the next detection.");
+ }
+ updateSchedule = false;
return;
}
if (!schedule.getDriftDefinition().isEnabled()) {
- log.debug("Skipping " + schedule + " because the drift
definition is disabled.");
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + schedule + " because the drift
definition is disabled.");
+ }
+ updateSchedule = false;
return;
}
if (previousSnapshotExists(schedule)) {
- log.debug("Skipping " + schedule + " because server has
not yet acked previous change set");
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + schedule + " because server
has not yet acked previous change set");
+ }
+ updateSchedule = false;
return;
}
@@ -140,7 +145,7 @@ public class DriftDetector implements Runnable {
} finally {
try {
- scheduleQueue.deactivateSchedule();
+ scheduleQueue.deactivateSchedule(updateSchedule);
long endTime = System.currentTimeMillis();
log.debug("Finished drift detection in " + (endTime -
startTime) + " ms");
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueue.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueue.java
index 35960ad..13f078d 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueue.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueue.java
@@ -35,7 +35,7 @@ public interface ScheduleQueue {
* at the head of the queue to become active. If there is no active schedule this
* method simply does nothing and returns.
*/
- void deactivateSchedule();
+ void deactivateSchedule(boolean updateSchedule);
/**
* Adds a schedule to the queue for processing by the drift detector
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueueImpl.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueueImpl.java
index cbc50b8..d008f36 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueueImpl.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/ScheduleQueueImpl.java
@@ -1,15 +1,14 @@
package org.rhq.core.pc.drift;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.PriorityQueue;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.drift.DriftDefinition;
import org.rhq.core.domain.drift.DriftDefinitionComparator;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
public class ScheduleQueueImpl implements ScheduleQueue {
private static final Runnable NO_OP = new Runnable() {
@@ -26,6 +25,8 @@ public class ScheduleQueueImpl implements ScheduleQueue {
private Runnable deactivationTask;
+ private Log log = LogFactory.getLog(ScheduleQueueImpl.class);
+
@Override
public DriftDetectionSchedule getNextSchedule() {
try {
@@ -66,16 +67,28 @@ public class ScheduleQueueImpl implements ScheduleQueue {
}
@Override
- public void deactivateSchedule() {
+ public void deactivateSchedule(boolean updateSchedule) {
try {
lock.writeLock().lock();
if (deactivationTask != null) {
deactivationTask.run();
}
+
if (activeSchedule == null) {
return;
}
- activeSchedule.updateShedule();
+
+ if (updateSchedule) {
+ activeSchedule.updateShedule();
+ }
+
+ if (log.isDebugEnabled()) {
+ SimpleDateFormat formatter = new SimpleDateFormat();
+ formatter.applyPattern("hh:mm:ss:SSS a");
+ log.debug("The next drift detection run for " + activeSchedule
+ " is set for " +
+ formatter.format(new Date(activeSchedule.getNextScan())));
+ }
+
queue.offer(activeSchedule);
activeSchedule = null;
} finally {
diff --git
a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftDetectorTest.java
b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftDetectorTest.java
index 4a2b333..08373ec 100644
---
a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftDetectorTest.java
+++
b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftDetectorTest.java
@@ -19,22 +19,12 @@
package org.rhq.core.pc.drift;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptyList;
-import static org.apache.commons.io.FileUtils.copyFile;
-import static org.apache.commons.io.FileUtils.deleteDirectory;
-import static org.apache.commons.io.FileUtils.touch;
-import static org.rhq.common.drift.FileEntry.addedFileEntry;
-import static org.rhq.common.drift.FileEntry.changedFileEntry;
-import static org.rhq.common.drift.FileEntry.removedFileEntry;
-import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
-import static org.rhq.core.domain.drift.DriftChangeSetCategory.DRIFT;
-import static org.rhq.test.AssertUtils.assertCollectionMatchesNoOrder;
-import static org.rhq.test.AssertUtils.assertPropertiesMatch;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import static org.testng.Assert.fail;
+import org.rhq.common.drift.*;
+import org.rhq.core.domain.drift.DriftDefinition;
+import org.rhq.core.system.OperatingSystemType;
+import org.rhq.core.system.SystemInfoFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
import java.io.BufferedReader;
import java.io.File;
@@ -43,18 +33,15 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-import org.rhq.common.drift.ChangeSetReader;
-import org.rhq.common.drift.ChangeSetReaderImpl;
-import org.rhq.common.drift.ChangeSetWriter;
-import org.rhq.common.drift.ChangeSetWriterImpl;
-import org.rhq.common.drift.FileEntry;
-import org.rhq.common.drift.Headers;
-import org.rhq.core.domain.drift.DriftDefinition;
-import org.rhq.core.system.OperatingSystemType;
-import org.rhq.core.system.SystemInfoFactory;
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
+import static org.apache.commons.io.FileUtils.*;
+import static org.rhq.common.drift.FileEntry.*;
+import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
+import static org.rhq.core.domain.drift.DriftChangeSetCategory.DRIFT;
+import static org.rhq.test.AssertUtils.assertCollectionMatchesNoOrder;
+import static org.rhq.test.AssertUtils.assertPropertiesMatch;
+import static org.testng.Assert.*;
public class DriftDetectorTest extends DriftTest {
@@ -168,6 +155,30 @@ public class DriftDetectorTest extends DriftTest {
changeSet);
}
+ @Test
+ public void updateScheduleAfterGeneratingCoverageChangeSet() throws Exception {
+ DriftDefinition driftDef =
driftDefinition("update-schedule-after-coverage-changeset",
+ resourceDir.getAbsolutePath());
+ DriftDetectionSchedule schedule = new DriftDetectionSchedule(resourceId(),
driftDef);
+
+ File confDir = mkdir(resourceDir, "conf");
+ File serverConf = createRandomFile(confDir, "server.conf");
+
+ long currentTime = System.currentTimeMillis();
+
+ scheduleQueue.addSchedule(schedule);
+ detector.run();
+
+ assertTrue(schedule.getNextScan() >= (currentTime + driftDef.getInterval()),
+ "Failed to update schedule. next scan is " +
schedule.getNextScan() + " and should be greater than " +
+ (currentTime + driftDef.getInterval()));
+ }
+
+ @Test
+ public void updateScheduleAfterGeneratingDriftChangeSet() throws Exception {
+
+ }
+
@SuppressWarnings("unchecked")
@Test
public void doNotUpdateSnapshotOrGenerateDriftChangeSetIfNothingChanges() throws
Exception {
@@ -219,12 +230,37 @@ public class DriftDetectorTest extends DriftTest {
DriftDefinition def = driftDefinition("disabled-config-test",
resourceDir.getAbsolutePath());
def.setEnabled(false);
+ DriftDetectionSchedule schedule = new DriftDetectionSchedule(resourceId(), def);
+ long nextScan = schedule.getNextScan();
+
File confDir = mkdir(resourceDir, "conf");
File server1Conf = new File(confDir, "server-1.conf");
touch(server1Conf);
- scheduleQueue.addSchedule(new DriftDetectionSchedule(resourceId(), def));
+ scheduleQueue.addSchedule(schedule);
+ detector.run();
+
+ // make sure that the next scan time is not updated
+ assertEquals(schedule.getNextScan(), nextScan, "The next scan time for the
drift detection schedule should " +
+ " not get updated if drift detection does not actually run for the
definition.");
+ }
+
+ @Test
+ public void doNotUpdateScheduleIfItIsTooEarlyToRunDetection() throws Exception {
+ DriftDefinition driftDef = driftDefinition("schedule-not-ready-test",
resourceDir.getAbsolutePath());
+ DriftDetectionSchedule schedule = new DriftDetectionSchedule(resourceId(),
driftDef);
+
+ File confDir = mkdir(resourceDir, "conf");
+ File server1Conf = new File(confDir, "server.conf");
+
+ scheduleQueue.addSchedule(schedule);
detector.run();
+
+ long nextScan = schedule.getNextScan();
+ detector.run();
+
+ assertEquals(schedule.getNextScan(), nextScan, "The next scan time for the
drift detection schedule should " +
+ " not get updated if drift detection does not actually run for the
definition.");
}
@Test
diff --git
a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
index 518e8e4..c3e28f5 100644
---
a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
+++
b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
@@ -260,7 +260,7 @@ public class DriftManagerTest extends DriftTest {
assertTrue(changeSetDir.exists(), "The change set directory should not be
deleted while the schedule is "
+ "still active.");
- driftMgr.getSchedulesQueue().deactivateSchedule();
+ driftMgr.getSchedulesQueue().deactivateSchedule(false);
assertFalse(changeSetDir.exists(), "The change set directory should have
been deleted after the schedule is "
+ "deactivated.");
}
commit d7ef96e848f139f231a8c875f8bce655fc463812
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Wed Dec 7 16:22:57 2011 -0500
make sure we can support schema versions that we put inbetween existing versions.
for example, if we have db schema 2.115 and 2.116 and we want something in between,
make sure
2.115.1 does what we think.
diff --git
a/modules/core/dbutils/src/test/java/org/rhq/core/db/ant/dbupdate/SchemaVersionTest.java
b/modules/core/dbutils/src/test/java/org/rhq/core/db/ant/dbupdate/SchemaVersionTest.java
index 393a293..4fdf435 100644
---
a/modules/core/dbutils/src/test/java/org/rhq/core/db/ant/dbupdate/SchemaVersionTest.java
+++
b/modules/core/dbutils/src/test/java/org/rhq/core/db/ant/dbupdate/SchemaVersionTest.java
@@ -19,6 +19,7 @@
package org.rhq.core.db.ant.dbupdate;
import org.testng.annotations.Test;
+
import org.rhq.core.db.ant.dbupgrade.SchemaVersion;
/**
@@ -50,6 +51,7 @@ public class SchemaVersionTest {
* Tests checking between versions.
*/
public void testBetween() {
+ assert new SchemaVersion("2.115.1").between(new
SchemaVersion("2.115"), new SchemaVersion("2.116"));
assert new SchemaVersion("2").between(new SchemaVersion("1"),
new SchemaVersion("3"));
assert new SchemaVersion("222").between(new
SchemaVersion("111"), new SchemaVersion("333"));
assert new SchemaVersion("1.0.2").between(new
SchemaVersion("1.0.0"), new SchemaVersion("1.0.3"));
commit 74d3e194811007373811bc1cdc8766b68f23de54
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Wed Dec 7 14:27:05 2011 -0500
[BZ 760721] fix bug where negative values would always get incorrectly normalized to
"-" (
https://bugzilla.redhat.com/show_bug.cgi?id=760721)
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/form/IsLongValidator.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/form/IsLongValidator.java
index cb61cab..2b25e73 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/form/IsLongValidator.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/form/IsLongValidator.java
@@ -62,11 +62,16 @@ public class IsLongValidator extends CustomValidator {
}
String baseStringValue = stripOffNegativeSignAndLeadingZeroes(stringValue,
isNegative);
- updateInputValue((isNegative) ? "-" : "" + baseStringValue);
+ String normalizedValue;
+ if (isNegative) {
+ normalizedValue = "-" + baseStringValue;
+ } else {
+ normalizedValue = baseStringValue;
+ }
+ updateInputValue(normalizedValue);
// all zeroes, optionally prefixed with negative sign, is valid
- if (baseStringValue.length() == 0) {
- updateInputValue("0");
+ if (baseStringValue.equals("0")) {
return true;
}
@@ -107,7 +112,7 @@ public class IsLongValidator extends CustomValidator {
charsToSkip++;
}
- for (int i = charsToSkip; i < stringValue.length(); i++) {
+ for (int i = charsToSkip; i < stringValue.length() - 1; i++) {
if (stringValue.charAt(i) == '0') {
charsToSkip++;
} else {
commit 6245b0d8756929ada716e3c3c70d2089fc1d5a48
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Wed Dec 7 13:58:12 2011 -0500
[BZ 751418] Finish button is now disabled / grayed out on final step of resource
import/create wizards until the config editor reports the config as being valid; note, to
achieve this a new method isNextButtonEnabled() was added to the WizardStep interface and
a new method updateButtonEnablement() was added to the WizardView class
(
https://bugzilla.redhat.com/show_bug.cgi?id=751418); also added javadoc to the WizardStep
interface
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizard.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizard.java
index 1b9fa0c..2e75a05 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizard.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizard.java
@@ -77,4 +77,6 @@ public abstract class AbstractWizard implements Wizard {
public WizardView getView() {
return view;
}
+
+
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizardStep.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizardStep.java
index 299d0f2..d0aabaf 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizardStep.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/AbstractWizardStep.java
@@ -28,6 +28,10 @@ public abstract class AbstractWizardStep implements WizardStep {
abstract public String getName();
+ public boolean isNextButtonEnabled() {
+ return true;
+ }
+
public boolean nextPage() {
return true;
}
@@ -35,4 +39,5 @@ public abstract class AbstractWizardStep implements WizardStep {
public boolean previousPage() {
return true;
}
+
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/Wizard.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/Wizard.java
index 8b38b39..ff4dd08 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/Wizard.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/Wizard.java
@@ -46,4 +46,5 @@ public interface Wizard {
* This is called when the cancel button or the wizard window's "X"
close button is clicked.
*/
void cancel();
+
}
\ No newline at end of file
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardStep.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardStep.java
index f5005c0..16c7ba0 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardStep.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardStep.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2010 Red Hat, Inc.
+ * Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -25,17 +25,53 @@ import org.rhq.enterprise.gui.coregui.client.Messages;
import org.rhq.enterprise.gui.coregui.client.util.selenium.Locatable;
/**
+ * A step in a {@link Wizard wizard}.
+ *
* @author Greg Hinkle
*/
public interface WizardStep {
Messages MSG = CoreGUI.getMessages();
+ /**
+ * Returns the canvas containing this step's widget content. This method should
<b>not</b> add the canvas to the
+ * parent canvas - the wizard framework will take care of that.
+ *
+ * @param parent the parent canvas to which the returned canvas will be added -
+ *
+ * @return the canvas containing this step's widget content
+ */
Canvas getCanvas(Locatable parent);
+ /**
+ * Returns true if this step's Next or Finish button should be enabled, or false
if it should be disabled.
+ *
+ * @return true if this step's Next or Finish button should be enabled, or false
if it should be disabled
+ */
+ boolean isNextButtonEnabled();
+
+ /**
+ * Called when the user clicks this step's Next or Finish button. If all required
data has been entered for the
+ * step and is valid, true should be returned, and the wizard will advance to the
next step, otherwise false
+ * should be returned, and the wizard will not advance.
+ *
+ * @return true if all required data has been entered for the step and is valid, or
false otherwise
+ */
boolean nextPage();
+ /**
+ * Called when the user clicks this step's Previous button. Should return true if
the wizard should go to the
+ * previous step, or false if it should not.
+ *
+ * @return true if the wizard should go to the previous step, or false if it should
not.
+ */
boolean previousPage();
+ /**
+ * Returns the title of this step, to be displayed in the wizard modal's title
bar.
+ *
+ * @return the title of this step, to be displayed in the wizard modal's title
bar
+ */
String getName();
+
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardView.java
index d5fe441..34291f0 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/wizard/WizardView.java
@@ -48,6 +48,7 @@ import
org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableWindow;
* @author Greg Hinkle
*/
public class WizardView extends LocatableVLayout {
+
static private final Messages MSG = CoreGUI.getMessages();
static private final String CANCEL = MSG.common_button_cancel();
@@ -229,7 +230,7 @@ public class WizardView extends LocatableVLayout {
.size())));
stepLabel.setWrap(false);
- WizardStep step = wizardSteps.get(currentStep);
+ final WizardStep step = wizardSteps.get(currentStep);
stepTitleLabel.setContents(step.getName());
@@ -255,7 +256,7 @@ public class WizardView extends LocatableVLayout {
}
currentCanvas = wizardSteps.get(currentStep).getCanvas(this);
- // if null advance to the next step by automatically "clicking" the
Next button
+ // if null, advance to the next step by automatically "clicking" the
Next button
if (null == currentCanvas) {
if (step.nextPage()) {
wizardSteps.get(currentStep).nextPage();
@@ -282,7 +283,7 @@ public class WizardView extends LocatableVLayout {
cancelButton.setDisabled(false);
previousButton.setDisabled(stepIndex == 0);
- nextButton.setDisabled(false);
+ nextButton.setDisabled(!step.isNextButtonEnabled());
for (IButton button : customButtons) {
button.setDisabled(false);
@@ -356,4 +357,10 @@ public class WizardView extends LocatableVLayout {
public void decrementStep() {
setStep(currentStep - 1);
}
+
+ public void updateButtonEnablement() {
+ WizardStep step = wizard.getSteps().get(currentStep);
+ getNextButton().setDisabled(!step.isNextButtonEnabled());
+ }
+
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/factory/ResourceFactoryConfigurationStep.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/factory/ResourceFactoryConfigurationStep.java
index 932f197..538aa7f 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/factory/ResourceFactoryConfigurationStep.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/factory/ResourceFactoryConfigurationStep.java
@@ -29,6 +29,8 @@ import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import
org.rhq.enterprise.gui.coregui.client.components.configuration.ConfigurationEditor;
+import
org.rhq.enterprise.gui.coregui.client.components.configuration.PropertyValueChangeEvent;
+import
org.rhq.enterprise.gui.coregui.client.components.configuration.PropertyValueChangeListener;
import org.rhq.enterprise.gui.coregui.client.components.form.DurationItem;
import org.rhq.enterprise.gui.coregui.client.components.form.TimeUnit;
import org.rhq.enterprise.gui.coregui.client.components.wizard.AbstractWizardStep;
@@ -44,14 +46,13 @@ import
org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableVLayout;
* @author Jay Shaughnessy
* @author Greg Hinkle
*/
-public class ResourceFactoryConfigurationStep extends AbstractWizardStep {
+public class ResourceFactoryConfigurationStep extends AbstractWizardStep implements
PropertyValueChangeListener {
- private boolean noConfigurationNeeded = false; // if true, it has been determined the
user doesn't have to set any config
private LocatableVLayout vLayout;
private ConfigurationEditor editor;
private Configuration startingConfig;
private DurationItem timeoutItem;
- AbstractResourceFactoryWizard wizard;
+ private AbstractResourceFactoryWizard wizard;
public ResourceFactoryConfigurationStep(AbstractResourceFactoryWizard wizard) {
this.wizard = wizard;
@@ -102,17 +103,19 @@ public class ResourceFactoryConfigurationStep extends
AbstractWizardStep {
ConfigurationGWTServiceAsync configurationService =
GWTServiceLookup.getConfigurationService();
configurationService.getOptionValuesForConfigDefinition(def,
new AsyncCallback<ConfigurationDefinition>() {
+ public void onSuccess(ConfigurationDefinition result) {
+ createAndAddConfigurationEditor(result);
+ }
public void onFailure(Throwable throwable) {
- editor = new
ConfigurationEditor(vLayout.extendLocatorId("Editor"), def, startingConfig);
- editor.setAllPropertiesWritable(true);
- vLayout.addMember(editor, 0);
-
+ createAndAddConfigurationEditor(def);
}
- public void onSuccess(ConfigurationDefinition result) {
- editor = new
ConfigurationEditor(vLayout.extendLocatorId("Editor"), result, startingConfig);
+ private void
createAndAddConfigurationEditor(ConfigurationDefinition def) {
+ editor = new
ConfigurationEditor(vLayout.extendLocatorId("Editor"), def, startingConfig);
editor.setAllPropertiesWritable(true);
+
editor.addPropertyValueChangeListener(ResourceFactoryConfigurationStep.this);
+ wizard.getView().updateButtonEnablement();
vLayout.addMember(editor, 0);
}
});
@@ -122,9 +125,15 @@ public class ResourceFactoryConfigurationStep extends
AbstractWizardStep {
return vLayout;
}
+ @Override
+ public boolean isNextButtonEnabled() {
+ return (editor != null) && editor.isValid();
+ }
+
public boolean nextPage() {
- if (noConfigurationNeeded == true || (editor != null &&
editor.validate())) {
- wizard.setNewResourceConfiguration((noConfigurationNeeded) ? null :
editor.getConfiguration());
+ // Finish.
+ if ((editor != null) && editor.isValid()) {
+ wizard.setNewResourceConfiguration(editor.getConfiguration());
wizard.setNewResourceCreateTimeout(timeoutItem.getValueAsInteger());
wizard.execute();
return true;
@@ -136,4 +145,10 @@ public class ResourceFactoryConfigurationStep extends
AbstractWizardStep {
public String getName() {
return MSG.widget_resourceFactoryWizard_editConfigStepName();
}
+
+ @Override
+ public void propertyValueChanged(PropertyValueChangeEvent event) {
+ wizard.getView().updateButtonEnablement();
+ }
+
}
\ No newline at end of file
commit b967a205193b3dbb70f507f7a3df74f0474cb06b
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Wed Dec 7 13:50:58 2011 -0500
minor - remove manual boxing
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/create/BundleUploadDataStep.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/create/BundleUploadDataStep.java
index 9fa7dd2..b1364b0 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/create/BundleUploadDataStep.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/create/BundleUploadDataStep.java
@@ -64,7 +64,7 @@ public class BundleUploadDataStep extends AbstractWizardStep {
mainLayout = new LocatableVLayout("BundleCreateUploadData");
}
- mainLayout.setMargin(Integer.valueOf(20));
+ mainLayout.setMargin(20);
mainLayout.setWidth100();
mainLayout.setHeight(10);
commit f736fd47d4d07204a9f9b8b0f98f8f74bc0fe9f2
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Wed Dec 7 13:49:40 2011 -0500
don't prepend RHQ Server resource name with "AS "; include JNP port in
RHQ Server resource name; refactor formatServerName() to use a StringBuilder and be more
readable
diff --git
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
index 185ecff..62b39b6 100644
---
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
+++
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
@@ -66,7 +66,7 @@ import org.rhq.plugins.jbossas.util.JnpConfig;
import org.rhq.plugins.jmx.JMXDiscoveryComponent;
/**
- * Discovers JBossAS 3.2.x and 4.x server instances.
+ * Discovers instances of JBoss AS 3.2.3 through 4.2.x, and JBoss EAP and SOA-P 4.x.
*
* @author John Mazzitelli
* @author Ian Springer
@@ -296,8 +296,8 @@ public class JBossASDiscoveryComponent implements
ResourceDiscoveryComponent, Ma
final JBossProductType productType = installInfo.getProductType();
String description = productType.DESCRIPTION + " " +
installInfo.getMajorVersion();
- boolean isInServer = isRhqServer(absoluteConfigPath);
- if (isInServer) {
+ boolean isRhqServer = isRhqServer(absoluteConfigPath);
+ if (isRhqServer) {
description += " hosting the RHQ Server";
// RHQ-633 : We know this is an RHQ Server. Let's auto-configure for
tracking its log file, which is not in
@@ -324,13 +324,13 @@ public class JBossASDiscoveryComponent implements
ResourceDiscoveryComponent, Ma
}
}
}
- final String PRODUCT_PREFIX = productType.name() + " ";
- String name = PRODUCT_PREFIX
- + formatServerName(bindingAddress, namingPort,
discoveryContext.getSystemInformation().getHostname(),
- absoluteConfigPath.getName(), isInServer);
-
- return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key,
name, PRODUCT_PREFIX
- + installInfo.getVersion(), description, pluginConfiguration, processInfo);
+
+ String name = formatServerName(bindingAddress, namingPort,
discoveryContext.getSystemInformation().getHostname(),
+ absoluteConfigPath.getName(), productType, isRhqServer);
+ String version = productType.name() + " " + installInfo.getVersion();
+
+ return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key,
name, version, description,
+ pluginConfiguration, processInfo);
}
/**
@@ -379,7 +379,8 @@ public class JBossASDiscoveryComponent implements
ResourceDiscoveryComponent, Ma
try {
installInfo = new JBossInstallationInfo(homeDir);
} catch (IOException e) {
- throw new IllegalStateException(e);
+ throw new IllegalStateException("Failed to determine
installation info for JBoss home dir ["
+ + homeDir + "].", e);
}
File configDir = (File) server.getAttribute(configObjectName,
"ServerHomeDir");
@@ -405,51 +406,70 @@ public class JBossASDiscoveryComponent implements
ResourceDiscoveryComponent, Ma
// Now set default values on any props that are still not set.
setPluginConfigurationDefaults(pluginConfiguration);
- String resourceName = formatServerName(bindAddress, jnpPort,
context.getSystemInformation()
- .getHostname(), configName, isRhqServer(configDir));
+ JBossProductType productType = installInfo.getProductType();
+ String name = formatServerName(bindAddress, jnpPort,
context.getSystemInformation().getHostname(),
+ configName, productType, isRhqServer(configDir));
+ String description = productType.NAME +
+ " server that the RHQ Plugin Container is running within";
DiscoveredResourceDetails resource = new
DiscoveredResourceDetails(context.getResourceType(),
- configDir.getAbsolutePath(), resourceName, version,
- "JBoss AS server that the RHQ Plugin Container is running
within", pluginConfiguration, null);
+ configDir.getAbsolutePath(), name, version, description,
pluginConfiguration, null);
return resource;
}
} catch (Exception e) {
- /* JBoss MBean doesn't exist - not a JBoss server. */
- if (log.isDebugEnabled())
- log.debug("Not detected to be embedded in a JBoss AS Server",
e);
+ // JBoss MBean doesn't exist - not a JBoss server.
+ log.debug("Not detected to be embedded in a JBoss AS Server", e);
}
return null;
}
public String formatServerName(String bindingAddress, String jnpPort, String
hostname, String configurationName,
- boolean isRhq) {
+ JBossProductType productType, boolean isRhq) {
+ StringBuilder serverName = new StringBuilder();
+
+ if (!isRhq) {
+ // prefix w/ product name (e.g. "AS" or "EAP")
+ serverName.append(productType.name()).append(' ');
+ }
+
+ String bindingHostname = getBindingHostname(bindingAddress);
+ if (bindingHostname != null) {
+ serverName.append(bindingHostname);
+ } else {
+ serverName.append(hostname);
+ }
+
+ if (jnpPort != null && !jnpPort.equals(CHANGE_ME)) {
+ serverName.append(':').append(jnpPort);
+ }
+ serverName.append(' ');
if (isRhq) {
- return hostname + " RHQ Server";
+ serverName.append("RHQ Server");
} else {
- String hostnameToUse = hostname;
+ serverName.append(configurationName);
+ }
- if (bindingAddress != null) {
- try {
- InetAddress bindAddr = InetAddress.getByName(bindingAddress);
- if (!bindAddr.isAnyLocalAddress()) {
- //if the binding address != 0.0.0.0
- hostnameToUse = bindAddr.getHostName();
- }
- } catch (UnknownHostException e) {
- //this should not happen?
- log.warn("Unknown hostname passed in as the binding address for
JBoss AS server discovery: "
- + bindingAddress);
- }
- }
+ return serverName.toString();
+ }
- if (jnpPort != null && !jnpPort.equals(CHANGE_ME)) {
- hostnameToUse += ":" + jnpPort;
+ private String getBindingHostname(String bindingAddress) {
+ String bindingHostname = null;
+ if (bindingAddress != null) {
+ try {
+ InetAddress bindAddr = InetAddress.getByName(bindingAddress);
+ if (!bindAddr.isAnyLocalAddress()) {
+ //if the binding address != 0.0.0.0
+ bindingHostname = bindAddr.getHostName();
+ }
+ } catch (UnknownHostException e) {
+ //this should not happen?
+ log.warn("Unknown hostname passed in as the binding address for
JBoss AS instance: "
+ + bindingAddress);
}
-
- return hostnameToUse + " " + configurationName;
}
+ return bindingHostname;
}
private static String getJnpURL(JBossInstanceInfo cmdLine, File installHome, File
configDir) {
commit 664dc6e66fc82257d60c51837c495d60f873505f
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Wed Dec 7 13:46:44 2011 -0500
change log messages from INFO to DEBUG to be consistent w/ other plugins
diff --git
a/modules/plugins/rhq-agent/src/main/java/org/rhq/plugins/agent/AgentDiscoveryComponent.java
b/modules/plugins/rhq-agent/src/main/java/org/rhq/plugins/agent/AgentDiscoveryComponent.java
index 0faecf2..bef2731 100644
---
a/modules/plugins/rhq-agent/src/main/java/org/rhq/plugins/agent/AgentDiscoveryComponent.java
+++
b/modules/plugins/rhq-agent/src/main/java/org/rhq/plugins/agent/AgentDiscoveryComponent.java
@@ -64,7 +64,7 @@ public class AgentDiscoveryComponent implements
ResourceDiscoveryComponent, Reso
* @see ResourceDiscoveryComponent#discoverResources(ResourceDiscoveryContext)
*/
public Set<DiscoveredResourceDetails>
discoverResources(ResourceDiscoveryContext context) {
- log.info("Discovering RHQ Agent...");
+ log.debug("Discovering RHQ Agent...");
HashSet<DiscoveredResourceDetails> set = new
HashSet<DiscoveredResourceDetails>();
diff --git
a/modules/plugins/script/src/main/java/org/rhq/plugins/script/ScriptDiscoveryComponent.java
b/modules/plugins/script/src/main/java/org/rhq/plugins/script/ScriptDiscoveryComponent.java
index eb58227..8cbd84e 100644
---
a/modules/plugins/script/src/main/java/org/rhq/plugins/script/ScriptDiscoveryComponent.java
+++
b/modules/plugins/script/src/main/java/org/rhq/plugins/script/ScriptDiscoveryComponent.java
@@ -52,7 +52,7 @@ public class ScriptDiscoveryComponent implements
ResourceDiscoveryComponent, Man
private final Log log = LogFactory.getLog(ScriptDiscoveryComponent.class);
public Set<DiscoveredResourceDetails>
discoverResources(ResourceDiscoveryContext context) {
- log.info("Processing discovered management script resources");
+ log.debug("Processing discovered management script resources...");
HashSet<DiscoveredResourceDetails> details = new
HashSet<DiscoveredResourceDetails>();
commit 30e90184edd8660d613fe9feffc94368e4c832f5
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Dec 7 15:55:20 2011 +0100
Don't put @Stateless on the (abstract) super class.
Fix some fallout from refactoring the creation of the aggregate.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index ce4dd43..ea901d1 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -33,7 +33,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.ejb.Stateless;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@@ -63,7 +62,6 @@ import org.rhq.enterprise.server.rest.domain.ResourceWithType;
* For the cache and its evicion policies see rhq-cache-service.xml
* @author Heiko W. Rupp
*/
-@Stateless
@javax.annotation.Resource(name="cache",type= TreeCacheMBean.class,mappedName =
"RhqCache")
public class AbstractRestBean {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 5934dbe..99dd6fe 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -133,7 +133,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
res.setMaxTimeStamp(maxTime);
res.setMinTimeStamp(minTime);
*/
- res = fill(list,scheduleId,hideEmpty);
+ fill(res, list,scheduleId,hideEmpty);
}
CacheControl cc = new CacheControl();
@@ -156,10 +156,10 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
return builder.build();
}
- private MetricAggregate fill(List<MeasurementDataNumericHighLowComposite>
list,int scheduleId , boolean hideEmpty) {
+ private MetricAggregate fill(MetricAggregate res,
List<MeasurementDataNumericHighLowComposite> list, int scheduleId,
+ boolean hideEmpty) {
long minTime=Long.MAX_VALUE;
long maxTime=0;
- MetricAggregate res = new MetricAggregate();
res.setScheduleId(scheduleId);
for (MeasurementDataNumericHighLowComposite c : list) {
@@ -214,8 +214,9 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
dataManager.findDataForContext(caller,
EntityContext.forResource(sched.getResource().getId()),definitionId,startTime,endTime,dataPoints);
if (!listList.isEmpty()) {
List<MeasurementDataNumericHighLowComposite> list =
listList.get(0);
- MetricAggregate ma = fill(list,scheduleId,hideEmpty);
- resList.add(ma);
+ MetricAggregate res = new MetricAggregate();
+ fill(res, list,scheduleId,hideEmpty);
+ resList.add(res);
}
else
throw new StuffNotFoundException("Metrics for schedule " +
scheduleId);
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 7e908ad..6631ef4 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -22,6 +22,7 @@ import java.util.List;
import javax.ejb.Local;
import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -54,7 +55,7 @@ public interface MetricHandlerLocal {
Response getMetricData(@PathParam("scheduleId") int scheduleId,
@QueryParam("startTime") long startTime,
@QueryParam("endTime") long endTime,
- @QueryParam("dataPoints") int dataPoints,
+ @QueryParam("dataPoints")
@DefaultValue("60") int dataPoints,
@QueryParam("hideEmpty") boolean hideEmpty,
@Context Request request,
@Context HttpHeaders headers);
commit 95a713e15f42a5be3d800b800012482b8ee555c2
Merge: f74b119 bf5446c
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Dec 7 12:04:38 2011 +0100
Merge remote-tracking branch 'origin/master' into feature/rest
commit f74b119c57c4fe7721127b2faa9a5b0b046a8811
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Dec 7 12:04:02 2011 +0100
Initial set of integration tests for the REST api.
diff --git a/modules/integration-tests/rest-api/README.md
b/modules/integration-tests/rest-api/README.md
new file mode 100644
index 0000000..b5551be
--- /dev/null
+++ b/modules/integration-tests/rest-api/README.md
@@ -0,0 +1,13 @@
+# REST-API Integration tests
+
+These require a running server with at least one platform as resource id 10001 in
inventory.
+Also required are two accounts
+
+* rhqadmin with password rhqadmin in the superuser role
+* user with password name23 as a restricted user
+
+The server to connect to can be given on the command line via
`-Drest.server=<server>` as in
+
+ `mvn -Pdev -Drest.server=pintsize test`
+
+If no server is given, localhost is assumed.
\ No newline at end of file
diff --git a/modules/integration-tests/rest-api/pom.xml
b/modules/integration-tests/rest-api/pom.xml
new file mode 100644
index 0000000..4a2c4a6
--- /dev/null
+++ b/modules/integration-tests/rest-api/pom.xml
@@ -0,0 +1,56 @@
+<?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.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-integration-tests</artifactId>
+ <version>4.3.0-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.rhq</groupId>
+ <artifactId>rest-api-tests</artifactId>
+
+ <name>RHQ REST API tests</name>
+
+ <properties>
+ <surefire-plugin.version>2.10</surefire-plugin.version>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>${surefire-plugin.version}</version>
+ <dependencies>
+ <dependency>
+ <!-- explicitly choose the junit4.7+ provider -->
+ <groupId>org.apache.maven.surefire</groupId>
+ <artifactId>surefire-junit47</artifactId>
+ <version>${surefire-plugin.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.restfuse</groupId>
+ <artifactId>com.eclipsesource.restfuse</artifactId>
+ <version>1.0.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git
a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/GetStatusTest.java
b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/GetStatusTest.java
new file mode 100644
index 0000000..b59bd11
--- /dev/null
+++
b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/GetStatusTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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, 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.
+ */
+package org.rhq.modules.integrationTests.restApi;
+
+
+import com.eclipsesource.restfuse.AuthenticationType;
+import com.eclipsesource.restfuse.Destination;
+import com.eclipsesource.restfuse.HttpJUnitRunner;
+import com.eclipsesource.restfuse.Method;
+import com.eclipsesource.restfuse.Response;
+import com.eclipsesource.restfuse.annotation.Authentication;
+import com.eclipsesource.restfuse.annotation.Context;
+import com.eclipsesource.restfuse.annotation.HttpTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test the status part of the API
+ */
+(a)RunWith(HttpJUnitRunner.class)
+public class GetStatusTest {
+
+
+
+ @Rule
+ public Destination destination = new Destination("http://" +
System.getProperty("rest.server","localhost") +
":7080/rest/1");
+
+ @Context
+ private Response response;
+
+
+ @Test
+ @HttpTest( method = Method.GET, path = "/status" )
+ public void testAuthRequired() {
+ com.eclipsesource.restfuse.Assert.assertUnauthorized(response);
+ }
+
+
+ @Test
+ @HttpTest( method = Method.GET, path = "/status" ,authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin")
+ )
+ public void testAuthRhqadmin() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ }
+
+ @Test
+ @HttpTest( method = Method.GET, path = "/status" ,authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "user",
password = "name23")
+ )
+ public void testAuthRestricted() {
+ com.eclipsesource.restfuse.Assert.assertForbidden(response);
+ }
+
+
+}
diff --git
a/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ResourcesTest.java
b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ResourcesTest.java
new file mode 100644
index 0000000..88cfaaf
--- /dev/null
+++
b/modules/integration-tests/rest-api/src/test/java/org/rhq/modules/integrationTests/restApi/ResourcesTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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, 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.
+ */
+package org.rhq.modules.integrationTests.restApi;
+
+import com.eclipsesource.restfuse.AuthenticationType;
+import com.eclipsesource.restfuse.Destination;
+import com.eclipsesource.restfuse.HttpJUnitRunner;
+import com.eclipsesource.restfuse.Method;
+import com.eclipsesource.restfuse.Response;
+import com.eclipsesource.restfuse.annotation.Authentication;
+import com.eclipsesource.restfuse.annotation.Context;
+import com.eclipsesource.restfuse.annotation.Header;
+import com.eclipsesource.restfuse.annotation.HttpTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static junit.framework.Assert.assertTrue;
+
+/**
+ * Test the resources part
+ * @author Heiko W. Rupp
+ */
+(a)RunWith(HttpJUnitRunner.class)
+public class ResourcesTest {
+
+ @Rule
+ public Destination destination = new Destination("http://" +
System.getProperty("rest.server","localhost") +
":7080/rest/1");
+
+ @Context
+ private Response response;
+
+
+ @HttpTest( method = Method.GET, path = "/resource/platforms" )
+ public void testAuthRequired() {
+ com.eclipsesource.restfuse.Assert.assertUnauthorized(response);
+ }
+
+ @HttpTest( method = Method.GET, path =
"/resource/platforms",authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin")
+ )
+ public void testPlatformsPresent() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ assertTrue(response.hasBody());
+ }
+
+ @HttpTest( method = Method.GET, path = "/resource/10001",authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin"),
+ headers = {@Header(name = "Accept",value
="application/json")}
+ )
+ public void testGetPlatformJson() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ assertTrue(response.hasBody());
+ }
+
+ @HttpTest( method = Method.GET, path = "/resource/10001",authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin"),
+ headers = {@Header(name = "Accept",value
="application/xml")}
+ )
+ public void testGetPlatformXml() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ assertTrue(response.hasBody());
+ }
+
+ @HttpTest( method = Method.GET, path =
"/resource/10001/schedules",authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin"),
+ headers = {@Header(name = "Accept",value
="application/json")}
+ )
+ public void testGetPlatformSchedules() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ assertTrue(response.hasBody());
+ }
+
+ @HttpTest( method = Method.GET, path =
"/resource/10001/children",authentications =
+ @Authentication(type = AuthenticationType.BASIC, user = "rhqadmin",
password = "rhqadmin"),
+ headers = {@Header(name = "Accept",value
="application/json")}
+ )
+ public void testGetPlatformChildren() {
+ com.eclipsesource.restfuse.Assert.assertOk(response);
+ assertTrue(response.hasBody());
+ }
+
+}
commit 56eab8fd42ea5c3cfece191a71fef81fd8bf9c54
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Dec 7 12:03:28 2011 +0100
Improve Exception mapping
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
index d951767..58ccf30 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
@@ -18,11 +18,11 @@
*/
package org.rhq.enterprise.server.rest;
-import javax.ejb.EJBException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
+import org.rhq.enterprise.server.authz.PermissionException;
import org.rhq.enterprise.server.resource.ResourceNotFoundException;
/**
@@ -30,31 +30,39 @@ import org.rhq.enterprise.server.resource.ResourceNotFoundException;
* @author Heiko W. Rupp
*/
@Provider
-public class CustomExceptionMapper implements ExceptionMapper<EJBException> {
+public class CustomExceptionMapper implements ExceptionMapper<Exception> {
@Override
- public Response toResponse(EJBException e) {
+ public Response toResponse(Exception e) {
Throwable cause = e.getCause();
+ Response.ResponseBuilder builder;
if (cause !=null) {
+ Response.Status status;
if (cause instanceof StuffNotFoundException)
- return Response.status(Response.Status.NOT_FOUND)
- .entity(cause.getMessage())
- .build();
+ status =Response.Status.NOT_FOUND;
else if (cause instanceof ResourceNotFoundException)
- return Response.status(Response.Status.NOT_FOUND)
- .entity(cause.getMessage())
- .build();
+ status = Response.Status.NOT_FOUND;
else if (cause instanceof ParameterMissingException)
- return Response.status(Response.Status.NOT_ACCEPTABLE)
- .entity(cause.getMessage())
- .build();
+ status = Response.Status.NOT_ACCEPTABLE;
+ else if (cause instanceof PermissionException)
+ status = Response.Status.FORBIDDEN;
else
- return Response.status(Response.Status.SERVICE_UNAVAILABLE)
- .entity(cause.getMessage())
- .build();
+ status = Response.Status.SERVICE_UNAVAILABLE;
+
+ builder = Response.status(status);
+ builder.entity(cause.getMessage());
+ }
+ else {
+ if (e instanceof PermissionException) {
+ builder = Response.status(Response.Status.FORBIDDEN);
+ } else {
+ builder = Response.status(Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ if (e.getMessage()!=null)
+ builder.entity(e.getMessage());
}
- throw e;
+ return builder.build();
}
}
commit 8c1ab57450bc314e7a469ff968d4c24a806905ef
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Dec 7 09:30:45 2011 +0100
Allow to obtain metrics for multiple schedules at once.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 8be8070..5934dbe 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -26,9 +26,14 @@ import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
@@ -38,6 +43,7 @@ import javax.ws.rs.core.UriInfo;
import org.jboss.cache.Fqn;
+import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementSchedule;
@@ -73,7 +79,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
@Context Request request,
@Context HttpHeaders headers) {
- if (dataPoints<0)
+ if (dataPoints<=0)
throw new IllegalArgumentException("dataPoints must be >0 ");
if (startTime==0) {
@@ -103,11 +109,14 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
List<List<MeasurementDataNumericHighLowComposite>> listList =
dataManager.findDataForResource(caller,
schedule.getResource().getId(), new int[]{definitionId}, startTime,
endTime, dataPoints);
+/*
long minTime=Long.MAX_VALUE;
long maxTime=0;
+*/
if (!listList.isEmpty()) {
List<MeasurementDataNumericHighLowComposite> list = listList.get(0);
+/*
for (MeasurementDataNumericHighLowComposite c : list) {
long timestamp = c.getTimestamp();
if (!Double.isNaN(c.getValue()) || !hideEmpty) {
@@ -123,6 +132,9 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
}
res.setMaxTimeStamp(maxTime);
res.setMinTimeStamp(minTime);
+*/
+ res = fill(list,scheduleId,hideEmpty);
+ }
CacheControl cc = new CacheControl();
int maxAge = (int) (schedule.getInterval() / 1000L)/2; // millis ; half of
schedule interval
@@ -144,6 +156,76 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
return builder.build();
}
+ private MetricAggregate fill(List<MeasurementDataNumericHighLowComposite>
list,int scheduleId , boolean hideEmpty) {
+ long minTime=Long.MAX_VALUE;
+ long maxTime=0;
+ MetricAggregate res = new MetricAggregate();
+ res.setScheduleId(scheduleId);
+
+ for (MeasurementDataNumericHighLowComposite c : list) {
+ long timestamp = c.getTimestamp();
+ if (!Double.isNaN(c.getValue()) || !hideEmpty) {
+ MetricAggregate.DataPoint dp = new
MetricAggregate.DataPoint(timestamp,c.getValue(),c.getHighValue(),c.getLowValue());
+ res.addDataPoint(dp);
+ }
+ if (timestamp <minTime)
+ minTime= timestamp;
+ if (timestamp >maxTime)
+ maxTime= timestamp;
+ }
+ res.setNumDataPoints(list.size());
+ res.setMaxTimeStamp(maxTime);
+ res.setMinTimeStamp(minTime);
+
+ return res;
+ }
+
+ @Override
+ @GET
+ @Path("data")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML,
MediaType.TEXT_HTML})
+ public Response getMetricDataMulti(@QueryParam("sid") String schedules,
@QueryParam("startTime") long startTime,
+ @QueryParam("endTime") long endTime,
@QueryParam("dataPoints") int dataPoints,
+ @QueryParam("hideEmpty") boolean
hideEmpty, @Context Request request,
+ @Context HttpHeaders headers) {
+
+ if (startTime==0) {
+ endTime = System.currentTimeMillis();
+ startTime = endTime - EIGHT_HOURS;
+ }
+
+ String[] tmp = schedules.split(",");
+ Integer[] scheduleIds = new Integer[tmp.length];
+ try {
+ for (int i = 0; i < tmp.length ; i++)
+ scheduleIds[i] = Integer.parseInt(tmp[i]);
+ }
+ catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Bad input: " +
nfe.getMessage());
+ }
+
+ List<MetricAggregate> resList = new
ArrayList<MetricAggregate>(scheduleIds.length);
+ for (Integer scheduleId : scheduleIds) {
+ MeasurementSchedule sched =
scheduleManager.getScheduleById(caller,scheduleId);
+ if (sched==null)
+ throw new StuffNotFoundException("Schedule with id " +
scheduleId);
+ int definitionId = sched.getDefinition().getId();
+ List<List<MeasurementDataNumericHighLowComposite>> listList =
+ dataManager.findDataForContext(caller,
EntityContext.forResource(sched.getResource().getId()),definitionId,startTime,endTime,dataPoints);
+ if (!listList.isEmpty()) {
+ List<MeasurementDataNumericHighLowComposite> list =
listList.get(0);
+ MetricAggregate ma = fill(list,scheduleId,hideEmpty);
+ resList.add(ma);
+ }
+ else
+ throw new StuffNotFoundException("Metrics for schedule " +
scheduleId);
+ }
+
+ GenericEntity<List<MetricAggregate>> metAgg = new
GenericEntity<List<MetricAggregate>>(resList) {};
+
+ return Response.ok(metAgg).build();
+
+ }
/**
* Return a metric schedule with the respective status codes for cache validation
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 44844ae..7e908ad 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -18,7 +18,6 @@
*/
package org.rhq.enterprise.server.rest;
-import java.io.InputStream;
import java.util.List;
import javax.ejb.Local;
@@ -60,6 +59,17 @@ public interface MetricHandlerLocal {
@Context Request request,
@Context HttpHeaders headers);
+ @GET
+ @Path("data")
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+ Response getMetricDataMulti(@QueryParam("sid") String scheduleIds,
+ @QueryParam("startTime") long startTime,
+ @QueryParam("endTime") long endTime,
+ @QueryParam("dataPoints") int dataPoints,
+ @QueryParam("hideEmpty") boolean hideEmpty,
+ @Context Request request,
+ @Context HttpHeaders headers);
+
@GET
@Path("data/resource/{resourceId}")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
index d6af76e..16b6d40 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricAggregate.java
@@ -41,15 +41,17 @@ public class MetricAggregate {
long maxTimeStamp;
public MetricAggregate() {
+ dataPoints = new ArrayList<DataPoint>();
}
public MetricAggregate(Integer scheduleId, double min, double avg, double max) {
+ this();
this.scheduleId = scheduleId;
this.min = min;
this.avg = avg;
this.max = max;
- dataPoints = new ArrayList<DataPoint>();
+
}
@XmlElement
commit bf5446c876c5d799a6fb58cf3c6a2e050a0c6390
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Dec 6 17:03:30 2011 -0500
[BZ 755019] sort the deployment table
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleDeploymentListView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleDeploymentListView.java
index 4f78a16..a1f2c13 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleDeploymentListView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleDeploymentListView.java
@@ -26,6 +26,7 @@ import java.util.HashMap;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.widgets.events.DoubleClickEvent;
import com.smartgwt.client.widgets.events.DoubleClickHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
@@ -44,6 +45,7 @@ import
org.rhq.enterprise.gui.coregui.client.components.table.EscapedHtmlCellFor
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.util.StringUtility;
+import org.rhq.enterprise.gui.coregui.client.util.selenium.LocatableListGrid;
/**
* @author Greg Hinkle
@@ -60,6 +62,14 @@ public class BundleDeploymentListView extends
Table<BundleDeploymentDataSource>
}
@Override
+ protected LocatableListGrid createListGrid(String locatorId) {
+ LocatableListGrid grid = super.createListGrid(locatorId);
+ grid.setSortField(BundleDeploymentDataSource.FIELD_DEPLOY_TIME);
+ grid.setSortDirection(SortDirection.DESCENDING);
+ return grid;
+ }
+
+ @Override
protected void configureTable() {
ListGridField idField = new ListGridField(BundleDeploymentDataSource.FIELD_ID,
MSG.common_title_id());
ListGridField nameField = new
ListGridField(BundleDeploymentDataSource.FIELD_NAME, MSG
commit 8a2f8f8caf0c10cfc279dc49a8526da44bb7702b
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Dec 6 16:23:05 2011 -0500
[BZ 755019] sort the deployment audit trail
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleResourceDeploymentHistoryListView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleResourceDeploymentHistoryListView.java
index 749b042..63a10d7 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleResourceDeploymentHistoryListView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/bundle/deployment/BundleResourceDeploymentHistoryListView.java
@@ -32,6 +32,7 @@ import com.smartgwt.client.types.DateDisplayFormat;
import com.smartgwt.client.types.ExpansionMode;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.DoubleClickEvent;
@@ -92,6 +93,8 @@ public class BundleResourceDeploymentHistoryListView extends
LocatableVLayout {
grid.setCanExpandRecords(true);
grid.setExpansionMode(ExpansionMode.DETAIL_FIELD);
grid.setDetailField("message");
+ grid.setSortField("timestamp");
+ grid.setSortDirection(SortDirection.ASCENDING);
ListGridField action = new ListGridField("action",
MSG.view_bundle_deploy_action());
action.setAutoFitWidth(true);
commit 64c7032453ec453246ffb3a9a2bcb664903be88e
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Dec 6 16:11:42 2011 -0500
[Bug 753866 - DB Upgrade needs more work for existing drift records]
This is to support RHQ 4.1 and 4.2 drift upgrades to 4.3. It adds
two Java upgrade tasks and puts them into the existing 2.115 step. (JON
note, JON3.0 already places the db at 2.116 and the previous
version of 2.115 was already sufficient for JON3.0 purposes. We *do not*
want these new changes applied to JON3.0 upgrades and so I did not
create a new schema task for this, on purpose.)
diff --git a/modules/core/dbutils/src/main/java/org/rhq/core/db/DatabaseType.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/DatabaseType.java
index 27fee40..eb2f6a7 100644
--- a/modules/core/dbutils/src/main/java/org/rhq/core/db/DatabaseType.java
+++ b/modules/core/dbutils/src/main/java/org/rhq/core/db/DatabaseType.java
@@ -560,6 +560,20 @@ public abstract class DatabaseType {
public abstract int getNextSequenceValue(Connection conn, String table, String key)
throws SQLException;
/**
+ * A utility that returns the string for sequence use in an Insert statement
+ *
+ * @param conn The connection to use to get the sequence value.
+ * @param sequenceName The sequence name for the table.
+ *
+ * @return the sequence generating string
+ *
+ * @throws SQLException
+ */
+ public String getSequenceInsertValue(Connection conn, String sequenceName) {
+ return "nextval('" + sequenceName + "')";
+ }
+
+ /**
* Creates a sequence with the given name. Its initial value is specified along with
its increment (both are
* specified as Strings).
*
@@ -570,13 +584,15 @@ public abstract class DatabaseType {
* @param seqIdCacheSize
* @throws SQLException
*/
- public void createSequence(Connection conn, String name, String initial, String
increment, String seqIdCacheSize) throws SQLException {
+ public void createSequence(Connection conn, String name, String initial, String
increment, String seqIdCacheSize)
+ throws SQLException {
CreateSequenceExprBuilder builder = CreateSequenceExprBuilder.getBuilder(this);
HashMap<String, Object> terms = new HashMap<String, Object>();
terms.put(CreateSequenceExprBuilder.KEY_SEQ_NAME, name);
terms.put(CreateSequenceExprBuilder.KEY_SEQ_START, initial);
terms.put(CreateSequenceExprBuilder.KEY_SEQ_INCREMENT, increment);
- terms.put(CreateSequenceExprBuilder.KEY_SEQ_CACHE_SIZE,
CreateSequenceExprBuilder.getSafeSequenceCacheSize(builder, seqIdCacheSize));
+ terms.put(CreateSequenceExprBuilder.KEY_SEQ_CACHE_SIZE,
CreateSequenceExprBuilder.getSafeSequenceCacheSize(
+ builder, seqIdCacheSize));
executeSql(conn, builder.build(terms));
}
diff --git a/modules/core/dbutils/src/main/java/org/rhq/core/db/OracleDatabaseType.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/OracleDatabaseType.java
index abf73a3..b4ea5e9 100644
--- a/modules/core/dbutils/src/main/java/org/rhq/core/db/OracleDatabaseType.java
+++ b/modules/core/dbutils/src/main/java/org/rhq/core/db/OracleDatabaseType.java
@@ -95,6 +95,10 @@ public abstract class OracleDatabaseType extends DatabaseType {
}
}
+ public String getSequenceInsertValue(Connection conn, String sequenceName) {
+ return sequenceName + ".NEXTVAL";
+ }
+
/**
* @see DatabaseType#getNextSequenceValue(Connection, String, String)
*/
diff --git
a/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftPathDirectoryDatabaseUpgradeTask.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftPathDirectoryDatabaseUpgradeTask.java
new file mode 100644
index 0000000..6b87331
--- /dev/null
+++
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftPathDirectoryDatabaseUpgradeTask.java
@@ -0,0 +1,71 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.core.db.upgrade;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import mazz.i18n.Logger;
+
+import org.rhq.core.db.DatabaseType;
+import org.rhq.core.db.DbUtilsI18NFactory;
+import org.rhq.core.db.DbUtilsI18NResourceKeys;
+
+/**
+ * Database upgrade task 2.115 adds rhq_drift.path_directory column to store the
directory portion of the path
+ * column. This code sets the value appropriately.
+ *
+ * @author Jay Shaughnessy
+ */
+public class DriftPathDirectoryDatabaseUpgradeTask implements DatabaseUpgradeTask {
+
+ private static final Logger LOG =
DbUtilsI18NFactory.getLogger(DriftPathDirectoryDatabaseUpgradeTask.class);
+
+ public void execute(DatabaseType databaseType, Connection connection) throws
SQLException {
+
+ Statement s = null;
+
+ try {
+ String sql = "select d.ID, d.PATH, d.PATH_DIRECTORY from RHQ_DRIFT
d";
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
+
+ s = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
+ ResultSet rs = s.executeQuery(sql);
+
+ while (rs.next()) {
+ // note, this is guaranteed to be non-null and valid
+ String path = rs.getString(2);
+ String dir = getDir(path);
+ rs.updateString(3, dir);
+ rs.updateRow();
+ }
+ } finally {
+ databaseType.closeStatement(s);
+ }
+ }
+
+ private String getDir(String path) {
+ int i = path.lastIndexOf("/");
+ String result = (i != -1) ? path.substring(0, i) : "./";
+
+ return result;
+ }
+}
diff --git
a/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
new file mode 100644
index 0000000..d060a2a
--- /dev/null
+++
b/modules/core/dbutils/src/main/java/org/rhq/core/db/upgrade/DriftTemplateMapDatabaseUpgradeTask.java
@@ -0,0 +1,65 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.core.db.upgrade;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import mazz.i18n.Logger;
+
+import org.rhq.core.db.DatabaseType;
+import org.rhq.core.db.DbUtilsI18NFactory;
+import org.rhq.core.db.DbUtilsI18NResourceKeys;
+
+/**
+ * Database upgrade task 2.115 adds the rhq_drift_def_template table. The table is
initially populated here
+ * by using data in the to-be-removed rhq_drift_template_map table.
+ *
+ * @author Jay Shaughnessy
+ */
+public class DriftTemplateMapDatabaseUpgradeTask implements DatabaseUpgradeTask {
+
+ private static final Logger LOG =
DbUtilsI18NFactory.getLogger(DriftPathDirectoryDatabaseUpgradeTask.class);
+
+ public void execute(DatabaseType databaseType, Connection connection) throws
SQLException {
+
+ String sql = "" //
+ + " insert into RHQ_DRIFT_DEF_TEMPLATE (" //
+ + " ID," //
+ + " RESOURCE_TYPE_ID," //
+ + " CONFIG_ID," //
+ + " NAME," //
+ + " DESCRIPTION," //
+ + " IS_USER_DEFINED," //
+ + " CTIME )" //
+ + " select " + databaseType.getSequenceInsertValue(connection,
"RHQ_DRIFT_DEF_TEMPLATE_ID_SEQ") + "," //
+ + " tm.RESOURCE_TYPE_ID, " //
+ + " ct.CONFIG_ID, " //
+ + " ct.NAME, " //
+ + " ct.DESCRIPTION, " //
+ + " " + databaseType.getBooleanValue(false) + ","
//
+ + " " + System.currentTimeMillis() //
+ + " from RHQ_DRIFT_TEMPLATE_MAP tm " //
+ + " join RHQ_RESOURCE_TYPE rt on tm.RESOURCE_TYPE_ID = rt.ID" //
+ + " join RHQ_CONFIG_TEMPLATE ct on tm.CONFIG_TEMPLATE_ID =
ct.ID";
+ LOG.debug(DbUtilsI18NResourceKeys.EXECUTING_SQL, sql);
+
+ databaseType.executeSql(connection, sql);
+ }
+}
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 fb2ed85..8f10a3b 100644
--- a/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
+++ b/modules/core/dbutils/src/main/scripts/dbupgrade/db-upgrade.xml
@@ -3781,12 +3781,11 @@
<!-- RHQ_DRIFT -->
<!-- Add new path_directory column -->
<schema-addColumn table="RHQ_DRIFT"
column="PATH_DIRECTORY" columnType="VARCHAR2"
precision="1024" />
- <!-- TODO: This needs to be a java update step! -->
- <schema-directSQL>
- <statement desc="RHQ_DRIFT_DEFINITION: set
drift_handling_mode column (normal)">
- UPDATE RHQ_DRIFT SET PATH_DIRECTORY = PATH
- </statement>
- </schema-directSQL>
+
+ <!-- Set the new path_directory column using the current path column
values -->
+ <schema-javaTask
className="DriftPathDirectoryDatabaseUpgradeTask" />
+
+ <!-- Now it is safe to set the new path_directory column not null
-->
<schema-alterColumn table="RHQ_DRIFT"
column="PATH_DIRECTORY" nullable="FALSE" />
<!-- add new drift_set_id column and FK -->
@@ -3800,8 +3799,10 @@
</statement>
</schema-directSQL>
- <!-- TODO For RHQ 4.3: Java upgrade step to set path_directory from
path in rhq_drift -->
- <!-- TODO For RHQ 4.3: Java upgrade step for rhq_drift_template_map
conversion and removal -->
+ <!-- Morph rhq_drift_template_map rows into rhq_drift_def_template
rows. The rhq_drift_template_map
+ table is obsolete. -->
+ <schema-javaTask
className="DriftTemplateMapDatabaseUpgradeTask" />
+
</schemaSpec>
<schemaSpec version="2.116">
commit 16823b9934ea91e81e0b7102bd464a57875fe033
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Dec 6 14:16:32 2011 -0500
[BZ 752550] remove old downloaded bundle files to help clean out the tmp directory
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/bundle/BundleManager.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/bundle/BundleManager.java
index d240606..561e4a1 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/bundle/BundleManager.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/bundle/BundleManager.java
@@ -23,6 +23,7 @@
package org.rhq.core.pc.bundle;
import java.io.File;
+import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.HashMap;
@@ -47,10 +48,10 @@ import org.rhq.core.domain.bundle.BundleDeploymentStatus;
import org.rhq.core.domain.bundle.BundleDestination;
import org.rhq.core.domain.bundle.BundleResourceDeployment;
import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory;
+import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory.Status;
import org.rhq.core.domain.bundle.BundleType;
import org.rhq.core.domain.bundle.BundleVersion;
import org.rhq.core.domain.bundle.ResourceTypeBundleConfiguration;
-import org.rhq.core.domain.bundle.BundleResourceDeploymentHistory.Status;
import
org.rhq.core.domain.bundle.ResourceTypeBundleConfiguration.BundleDestinationBaseDirectory;
import org.rhq.core.domain.content.PackageVersion;
import org.rhq.core.domain.resource.Resource;
@@ -72,6 +73,7 @@ import org.rhq.core.pluginapi.bundle.BundleManagerProvider;
import org.rhq.core.pluginapi.bundle.BundlePurgeResult;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.core.util.file.FileUtil;
/**
* Manages the bundle subsystem, which allows bundles of content to be installed.
@@ -172,6 +174,12 @@ public class BundleManager extends AgentService implements
BundleAgentService, B
File bundleFilesDir = new File(pluginTmpDir,
"bundle-versions/"
+ bundleDeployment.getBundleVersion().getId());
bundleFilesDir.mkdirs();
+
+ // clean up any old downloads we may have retrieved before. This
helps clean out
+ // our temp directory so we don't unnecessarily fill up our
file system with obsolete files
+ removeOldDownloadedBundleFiles(bundleFilesDir);
+
+ // now download the bundle files we need for the current
deployment
Map<PackageVersion, File> downloadedFiles =
downloadBundleFiles(resourceDeployment,
bundleFilesDir);
@@ -311,6 +319,35 @@ public class BundleManager extends AgentService implements
BundleAgentService, B
}
/**
+ * Given a directory where the current deployment's bundle files will reside,
this method will clear out
+ * any other files located in other peer directories. In other words, the given
directory and all its
+ * subdirectories and child files are left untouched, but all files and directories
found in peer
+ * directories are wiped. This helps clean out our temp directory so we don't
fill up the file system
+ * with old downloaded files that we don't need anymore. See BZ 752550.
+ *
+ * @param currentBundleVersionFilesDir
+ */
+ private void removeOldDownloadedBundleFiles(final File currentBundleVersionFilesDir)
{
+ File parent = null;
+ try {
+ parent = currentBundleVersionFilesDir.getParentFile();
+ File[] doomedFiles = parent.listFiles(new FileFilter() {
+ public boolean accept(File child) {
+ return !currentBundleVersionFilesDir.equals(child);
+ }
+ });
+ for (File doomedFile : doomedFiles) {
+ FileUtil.purge(doomedFile, true);
+ }
+ } catch (Exception e) {
+ log.warn("Failed to clean up old downloaded bundle files in ["
+ + parent
+ + "]. You can ignore this but if the agent is asked to deploy a lot
of bundles, the file system may fill up."
+ + " Cause: " + e);
+ }
+ }
+
+ /**
* Downloads the bundle's files into the bundle plugin's tmp directory and
returns that tmp directory.
*
* @param resourceDeployment access to deployment information, including what bundle
files need to be downloaded
commit 32e06f0466e3a6a440518c4bd72e21c012bd6afa
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Dec 6 12:08:35 2011 -0500
[Bug 760209 - Drift Carousel not filtering the drift entries]
I think this broke when initial criteria handling changed in the
underlying Table implementation. The initial criteria was set correctly
but that was no longer applied directly to the grid, instead we now
manually perform the initial fetch. But that led to an overriden method
getting called to return the current criteria, which had yet to be
determined (it was thought to be needed on refresh only). So, the fix was
to immediately set the current criteria to the initial criteria in the
subclass.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselMemberView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselMemberView.java
index 47b5d3d..991ec84 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselMemberView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselMemberView.java
@@ -71,6 +71,7 @@ public class DriftCarouselMemberView extends DriftHistoryView implements
Carouse
super(locatorId, null, context, hasWriteAccess, initialCriteria);
+ this.carouselCriteria = initialCriteria;
this.changeSet = driftChangeSet;
((DriftCarouselDataSource)
getDataSource()).setChangeSetId(this.changeSet.getId());
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
index 6ccbb17..f941c6f 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
@@ -242,7 +242,7 @@ public class DriftCarouselView extends BookmarkableCarousel implements
DetailsVi
}
changeSetCriteria.addFilterStartVersion(String.valueOf(startVersion));
- // apply the drift-level carousel filters in order to filter out changesets that
have no applicab;e drift
+ // apply the drift-level carousel filters in order to filter out changesets that
have no applicable drift
Criteria criteria = getCurrentCriteria();
DriftCategory[] driftCategoriesFilter = RPCDataSource.getArrayFilter(criteria,
DriftDataSource.FILTER_CATEGORIES, DriftCategory.class);
commit 71db2833a06ad1537d8a27fe5a0399850d6f81bd
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 6 16:48:56 2011 +0100
Allow to limit schedules by name
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 59efeba..64cdea3 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
@@ -195,6 +195,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
public Response getSchedules(int resourceId,
String scheduleType,
boolean enabledOnly,
+ String name,
@Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
@@ -216,26 +217,28 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if ("all".equals(scheduleType) ||
scheduleType.toLowerCase().equals(definition.getDataType().toString().toLowerCase()) ) {
if (!enabledOnly || (enabledOnly && schedule.isEnabled() )) {
- MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
- schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
- definition.getDataType().toString());
- UriBuilder uriBuilder;
- URI uri;
- if (definition.getDataType()== DataType.MEASUREMENT) {
+ if (name==null || (name!=null &&
name.equals(definition.getName()))) {
+ MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
+ schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
+ definition.getDataType().toString());
+ UriBuilder uriBuilder;
+ URI uri;
+ if (definition.getDataType()== DataType.MEASUREMENT) {
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/metric/data/{id}");
+ uri = uriBuilder.build(schedule.getId());
+ Link metricLink = new
Link("metric",uri.toString());
+ ms.addLink(metricLink);
+ }
+ // create link to the resource
uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/metric/data/{id}");
- uri = uriBuilder.build(schedule.getId());
- Link metricLink = new Link("metric",uri.toString());
- ms.addLink(metricLink);
+ uriBuilder.path("resource/" +
schedule.getResource().getId());
+ uri = uriBuilder.build();
+ Link link = new Link("resource",uri.toString());
+ ms.addLink(link);
+
+ ret.add(ms);
}
- // create link to the resource
- uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("resource/" +
schedule.getResource().getId());
- uri = uriBuilder.build();
- Link link = new Link("resource",uri.toString());
- ms.addLink(link);
-
- ret.add(ms);
}
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index ab9ea62..77f268f 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -88,6 +88,7 @@ public interface ResourceHandlerLocal {
Response getSchedules(@PathParam("id") int resourceId,
@QueryParam("type")
@DefaultValue("all") String scheduleType,
@QueryParam("enabledOnly")
@DefaultValue("true") boolean enabledOnly,
+ @QueryParam("name") String name,
@Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo);
commit 01902864918073d86e08d088061ffc85efc593da
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 6 12:16:42 2011 +0100
Make ResourceWithType show up as <resource> in xml output
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
index 132fc34..735efa1 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
@@ -39,7 +39,7 @@ import org.jboss.resteasy.spi.touri.URITemplate;
* A (partial) resource with some type information
* @author Heiko W. Rupp
*/
-@XmlRootElement
+@XmlRootElement(name="resource")
/*
@XmlAccessorType(XmlAccessType.NONE)
@URITemplate("{id}")
commit 83197699d41880beb6266fc47d38cbf201bce7a4
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Dec 5 15:55:31 2011 -0500
[Bug 760145 - Drift snapshot displays wrong directory name on windows]
This is fairly bad as the paths get corrupted for all drift entries when
the basedir is a windows file system root (e.g. c:/ or d:/). There is
no workaround other than not using the file system root as the basedir.
I'm not sure, this may also happen using the a linux root "/" basedir.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
index 0ba5795..71ddf44 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
@@ -538,7 +538,13 @@ public class DriftDetector implements Runnable {
if (basedir.equals(file)) {
return ".";
}
- return file.getAbsolutePath().substring(basedir.getAbsolutePath().length() + 1);
+ String filePath = file.getAbsolutePath();
+ String basedirPath = basedir.getAbsolutePath();
+ int basedirLen = basedirPath.length();
+ if (!basedirPath.endsWith(File.separator)) {
+ ++basedirLen;
+ }
+ return filePath.substring(basedirLen);
}
private String sha256(File file) throws IOException {
commit 6f3d99d160c4910bffe16ada89b625fe251bea44
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Dec 5 15:51:07 2011 -0500
[Bug 760289 - Excessive file scanning in drift detection when using includes filters]
Now, when using includes file paths limit the directory scanning to only
those included directories.
Note that using a "." as an includes path basically translates to using
the base directory, in which case the scan will be as it is now.
A future enhancement may be to analyze the pattern and decide
whether a recursive scan is necessary. Currently. So, using includes
patterns to just look for certain files in the base directory will
expose you to the full scan.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
index 3f32f1f..0ba5795 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
@@ -45,6 +45,7 @@ import org.rhq.common.drift.FileEntry;
import org.rhq.common.drift.Headers;
import org.rhq.core.domain.drift.DriftChangeSetCategory;
import org.rhq.core.domain.drift.DriftDefinition;
+import org.rhq.core.domain.drift.Filter;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.core.util.file.FileVisitor;
@@ -167,13 +168,12 @@ public class DriftDetector implements Runnable {
log.debug("Generating drift change set for " + schedule);
- File currentSnapshot = changeSetMgr.findChangeSet(schedule.getResourceId(),
- schedule.getDriftDefinition().getName(), COVERAGE);
+ File currentSnapshot = changeSetMgr.findChangeSet(schedule.getResourceId(),
schedule.getDriftDefinition()
+ .getName(), COVERAGE);
File snapshotFile = currentSnapshot;
if (schedule.getDriftDefinition().isPinned()) {
snapshotFile = new File(snapshotFile.getParentFile(),
"snapshot.pinned");
-
}
final File basedir = new File(basedir(schedule.getResourceId(),
schedule.getDriftDefinition()));
@@ -203,43 +203,50 @@ public class DriftDetector implements Runnable {
}
// First look for files that have either been modified or deleted
- scanForModifiedOrDeletedFiles(schedule, basedir, processedFiles,
snapshotEntries, deltaEntries, coverageReader);
+ scanForModifiedOrDeletedFiles(schedule, basedir, processedFiles,
snapshotEntries, deltaEntries,
+ coverageReader);
} finally {
coverageReader.close();
}
// If the basedir is still valid we need to do a directory tree scan to look for
newly added files
if (basedir.isDirectory()) {
- forEachFile(basedir, new FilterFileVisitor(basedir,
schedule.getDriftDefinition().getIncludes(), schedule
- .getDriftDefinition().getExcludes(), new FileVisitor() {
- @Override
- public void visit(File file) {
- try {
- if (processedFiles.contains(file)) {
- return;
- }
+ DriftDefinition driftDef = schedule.getDriftDefinition();
+ List<Filter> includes = driftDef.getIncludes();
+ List<Filter> excludes = driftDef.getExcludes();
+
+ for (File dir : getScanDirectories(basedir, includes)) {
+ forEachFile(dir, new FilterFileVisitor(basedir, includes, excludes, new
FileVisitor() {
+ @Override
+ public void visit(File file) {
+ try {
+ if (processedFiles.contains(file)) {
+ return;
+ }
- if (!file.canRead()) {
- if (log.isDebugEnabled()) {
- log.debug("Skipping " + file.getPath() + "
since it is not readable.");
+ if (!file.canRead()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Skipping " + file.getPath() +
" since it is not readable.");
+ }
+ return;
}
- return;
- }
- if (log.isInfoEnabled()) {
- log.info("Detected added file for " + schedule +
" --> " + file.getAbsolutePath());
- }
+ if (log.isInfoEnabled()) {
+ log.info("Detected added file for " + schedule
+ " --> " + file.getAbsolutePath());
+ }
- FileEntry newEntry = addedFileEntry(relativePath(basedir, file),
sha256(file));
- deltaEntries.add(newEntry);
- snapshotEntries.add(newEntry);
- } catch (IOException e) {
- log.error("An error occurred while generating a drift change
set for " + schedule + ": "
- + e.getMessage());
- throw new DriftDetectionException("An error occurred while
generating a drift change set", e);
+ FileEntry newEntry = addedFileEntry(relativePath(basedir,
file), sha256(file));
+ deltaEntries.add(newEntry);
+ snapshotEntries.add(newEntry);
+ } catch (IOException e) {
+ log.error("An error occurred while generating a drift
change set for " + schedule + ": "
+ + e.getMessage());
+ throw new DriftDetectionException("An error occurred
while generating a drift change set",
+ e);
+ }
}
- }
- }));
+ }));
+ }
}
if (deltaEntries.isEmpty()) {
@@ -250,8 +257,8 @@ public class DriftDetector implements Runnable {
// the current snapshot to match the pinned snapshot. Note though
// that we increment the snapshot version in order to let the server
// know about the state change.
- if (schedule.getDriftDefinition().isPinned() && newVersion > 1
&& !isPreviousChangeSetEmpty(
- schedule.getResourceId(), schedule.getDriftDefinition())) {
+ if (schedule.getDriftDefinition().isPinned() && newVersion > 1
+ && !isPreviousChangeSetEmpty(schedule.getResourceId(),
schedule.getDriftDefinition())) {
currentSnapshot.delete();
File newSnapshot = updateCurrentSnapshot(schedule, snapshotEntries,
newVersion);
@@ -259,8 +266,8 @@ public class DriftDetector implements Runnable {
// TODO report back to the server that we are back in compliance
}
} else {
- if (schedule.getDriftDefinition().isPinned() && newVersion > 1
&&
- isSameAsPreviousChangeSet(deltaEntries, currentSnapshot)) {
+ if (schedule.getDriftDefinition().isPinned() && newVersion > 1
+ && isSameAsPreviousChangeSet(deltaEntries, currentSnapshot)) {
// if we are still out of compliance just report, we report a
// repeat change set to indicate no changes but also still out
// of compliance.
@@ -276,6 +283,26 @@ public class DriftDetector implements Runnable {
}
}
+ private Set<File> getScanDirectories(final File basedir, List<Filter>
includes) {
+
+ Set<File> directories = new HashSet<File>();
+
+ if (null == includes || includes.isEmpty()) {
+ directories.add(basedir);
+ } else {
+ for (Filter filter : includes) {
+ String path = filter.getPath();
+ if (".".equals(path)) {
+ directories.add(basedir);
+ } else {
+ directories.add(new File(basedir, path));
+ }
+ }
+ }
+
+ return directories;
+ }
+
private void scanForModifiedOrDeletedFiles(DriftDetectionSchedule schedule, File
basedir, Set<File> processedFiles,
List<FileEntry> snapshotEntries, List<FileEntry> deltaEntries,
ChangeSetReader coverageReader)
throws IOException {
@@ -312,6 +339,7 @@ public class DriftDetector implements Runnable {
}
}
+ @SuppressWarnings("unused")
private boolean isPreviousChangeSetEmpty(int resourceId, DriftDefinition definition)
throws IOException {
File changeSet = changeSetMgr.findChangeSet(resourceId, definition.getName(),
DRIFT);
if (!changeSet.exists()) {
@@ -337,15 +365,14 @@ public class DriftDetector implements Runnable {
}
private void updateDeltaSnapshot(DriftDetectionSummary summary,
DriftDetectionSchedule schedule,
- List<FileEntry> deltaEntries, int newVersion, File oldSnapshot, File
newSnapshot)
- throws IOException {
+ List<FileEntry> deltaEntries, int newVersion, File oldSnapshot, File
newSnapshot) throws IOException {
ChangeSetWriter deltaWriter = null;
try {
Headers deltaHeaders = createHeaders(schedule, DRIFT, newVersion);
- File driftChangeSet = changeSetMgr.findChangeSet(schedule.getResourceId(),
- schedule.getDriftDefinition().getName(), DRIFT);
+ File driftChangeSet = changeSetMgr.findChangeSet(schedule.getResourceId(),
schedule.getDriftDefinition()
+ .getName(), DRIFT);
deltaWriter = changeSetMgr.getChangeSetWriter(driftChangeSet, deltaHeaders);
summary.setDriftChangeSet(driftChangeSet);
@@ -376,10 +403,9 @@ public class DriftDetector implements Runnable {
try {
Headers snapshotHeaders = createHeaders(schedule, COVERAGE, newVersion);
- File newSnapshot = changeSetMgr.findChangeSet(schedule.getResourceId(),
- schedule.getDriftDefinition().getName(), COVERAGE);
- newSnapshotWriter =
changeSetMgr.getChangeSetWriter(schedule.getResourceId(),
- snapshotHeaders);
+ File newSnapshot = changeSetMgr.findChangeSet(schedule.getResourceId(),
schedule.getDriftDefinition()
+ .getName(), COVERAGE);
+ newSnapshotWriter = changeSetMgr.getChangeSetWriter(schedule.getResourceId(),
snapshotHeaders);
for (FileEntry entry : snapshotEntries) {
newSnapshotWriter.write(entry);
@@ -411,19 +437,19 @@ public class DriftDetector implements Runnable {
return false;
}
switch (entry.getType()) {
- case FILE_ADDED:
- if (!entry.getNewSHA().equals(newEntry.getNewSHA())) {
- return false;
- }
- case FILE_CHANGED:
- if (!entry.getNewSHA().equals(newEntry.getNewSHA()) ||
- !entry.getOldSHA().equals(newEntry.getOldSHA())) {
- return false;
- }
- default: // FILE_REMOVED
- if (!entry.getOldSHA().equals(newEntry.getOldSHA())) {
- return false;
- }
+ case FILE_ADDED:
+ if (!entry.getNewSHA().equals(newEntry.getNewSHA())) {
+ return false;
+ }
+ case FILE_CHANGED:
+ if (!entry.getNewSHA().equals(newEntry.getNewSHA())
+ || !entry.getOldSHA().equals(newEntry.getOldSHA())) {
+ return false;
+ }
+ default: // FILE_REMOVED
+ if (!entry.getOldSHA().equals(newEntry.getOldSHA())) {
+ return false;
+ }
}
numEntries++;
}
@@ -443,9 +469,9 @@ public class DriftDetector implements Runnable {
if (!basedir.exists()) {
if (log.isWarnEnabled()) {
- log.warn("The base directory [" + basedir.getAbsolutePath() +
"] for " + schedule + " does not " +
- "exist. You may want review the drift definition and verify that
the value of the base " +
- "directory is in fact correct.");
+ log.warn("The base directory [" + basedir.getAbsolutePath() +
"] for " + schedule + " does not "
+ + "exist. You may want review the drift definition and verify
that the value of the base "
+ + "directory is in fact correct.");
}
summary.setBaseDirExists(false);
return;
@@ -477,9 +503,13 @@ public class DriftDetector implements Runnable {
}
private void doDirectoryScan(final DriftDetectionSchedule schedule, DriftDefinition
driftDef, final File basedir,
- final ChangeSetWriter writer) {
- forEachFile(basedir, new FilterFileVisitor(basedir, driftDef.getIncludes(),
driftDef.getExcludes(),
- new FileVisitor() {
+ final ChangeSetWriter writer) {
+
+ List<Filter> includes = driftDef.getIncludes();
+ List<Filter> excludes = driftDef.getExcludes();
+
+ for (File dir : getScanDirectories(basedir, includes)) {
+ forEachFile(dir, new FilterFileVisitor(basedir, includes, excludes, new
FileVisitor() {
@Override
public void visit(File file) {
try {
@@ -501,6 +531,7 @@ public class DriftDetector implements Runnable {
}
}
}));
+ }
}
private String relativePath(File basedir, File file) {
commit be9d41a3a6e8be965b5d016bea666c38af29b31c
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Dec 5 11:08:25 2011 -0500
[Bug 760105 - Drift: Unchecking the 'File Changed' option in
'Category' drop-down in the carousel view displays Globally uncaught exception]
This had to do with the filtering leaving no eligible snapshots.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
index 544ef81..6ccbb17 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
@@ -167,7 +167,7 @@ public class DriftCarouselView extends BookmarkableCarousel implements
DetailsVi
break;
}
}
- if (null == maxSnapshotVersion || maxSnapshotVersion <
carouselStart) {
+ if (null == maxSnapshotVersion || null == carouselStart ||
maxSnapshotVersion < carouselStart) {
maxSnapshotVersion = carouselStart;
setCarouselStartFilterMax(maxSnapshotVersion);
}
commit bfb46035204786e3e1297a41cdb1eede7e901e77
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Dec 5 16:43:27 2011 +0100
[BZ 760148] - Use the unprivileged method introduced by the commit
0589b9ee8488bd1 for BZ 694892 for obtaining the version info upon
the CLI login.
diff --git
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
index 3ce9598..2f4c640 100644
---
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
+++
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
@@ -115,7 +115,7 @@ public class LoginCommand implements ClientCommand {
Subject subject = remoteClient.login(username, password);
- ProductInfo info =
remoteClient.getSystemManager().getServerDetails(subject).getProductInfo();
+ ProductInfo info = remoteClient.getSystemManager().getProductInfo(subject);
String version = info.getVersion()
+ " (" + info.getBuildNumber() + ")";
client.getPrintWriter().println("Remote server version is: " +
version);
commit 69e2b1d81dc42b0bfc12ee8e32b08d7b2beba38d
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Dec 2 13:52:59 2011 -0500
[Bug 759485 - Error viewing live data in the resource metric graph portlet]
- Fixed the live graph code to throw out metric requests returning no values
- Fixed CpuComponent to return valid data on a live metric request, instead
of null.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/LiveGraphView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/LiveGraphView.java
index 01284cc..5076804 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/LiveGraphView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/resource/detail/monitoring/LiveGraphView.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.gui.coregui.client.inventory.resource.detail.monitoring;
import java.util.Date;
+import java.util.Iterator;
import java.util.Set;
import ca.nanometrics.gflot.client.Axis;
@@ -81,6 +82,7 @@ public class LiveGraphView extends LocatableVLayout {
private SimplePlot plot;
private Timer dataLoader;
+ @SuppressWarnings("unused")
private long min, max;
public LiveGraphView(String locatorId) {
@@ -231,7 +233,12 @@ public class LiveGraphView extends LocatableVLayout {
}
public void onSuccess(Set<MeasurementData> result) {
- MeasurementDataNumeric d = (MeasurementDataNumeric)
result.iterator().next();
+ Iterator<MeasurementData> i = result.iterator();
+ if (!i.hasNext()) {
+ return;
+ }
+
+ MeasurementDataNumeric d = (MeasurementDataNumeric)
i.next();
handler.add(new DataPoint(d.getTimestamp(), d.getValue()));
plot.redraw();
diff --git
a/modules/plugins/platform/src/main/java/org/rhq/plugins/platform/CpuComponent.java
b/modules/plugins/platform/src/main/java/org/rhq/plugins/platform/CpuComponent.java
index d5bc264..f045126 100644
--- a/modules/plugins/platform/src/main/java/org/rhq/plugins/platform/CpuComponent.java
+++ b/modules/plugins/platform/src/main/java/org/rhq/plugins/platform/CpuComponent.java
@@ -124,14 +124,15 @@ public class CpuComponent implements
ResourceComponent<PlatformComponent>, Measu
CpuEntry previousCpu = cpuCache.put(property, currentCpu);
previousCpu = (null == previousCpu) ? startCpuEntry : previousCpu;
- // if for some reason the delta time is excessive then toss
- // the metric, since it depends on a reasonable interval between
- // prev and curr. This can happen due to avail down or a newly
- // activated metric. Allow up to twice the metric interval.
+ // if for some reason the delta time is excessive then toss the
metric, since it depends
+ // on a reasonable interval between prev and curr. This can happen
due to avail down or a newly
+ // activated metric. Allow up to twice the metric interval. If the
metric interval is
+ // 0 (for a live data request) then just use a 10 minute interval.
Number num = null;
long deltaTime = currentCpu.getTimestamp() -
previousCpu.getTimestamp();
+ long metricInterval = (0 < request.getInterval()) ?
request.getInterval() : 600000L;
- if (deltaTime <= (2 * request.getInterval())) {
+ if (deltaTime <= (2 * metricInterval)) {
// Use the same calculation that SIGAR uses to generate the
percentages. The difference is that
// we use a safe "previous" cpu record.
num = getPercentage(previousCpu.getCpu(), cpu, property);
commit 024946c9b0969f316428740a780180e1dfd322ed
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Fri Dec 2 12:31:33 2011 -0500
Utility that examines jar files and/or directories and will print out
packages that are found in more than one jar file. To use it:
java -cp test-utils.jar org.rhq.test.DuplicatePackagesDetector <list of jar
files or directories>
You can optionally set the system prop in -Drhq.verbose for extra messages.
If you do not pass in any arguments, it will default to your local maven
repo. Specifically, it will default to "$HOME/.m2/repository/orq/rhq".
diff --git a/modules/test-utils/src/main/java/org/rhq/test/DuplicatePackagesDetector.java
b/modules/test-utils/src/main/java/org/rhq/test/DuplicatePackagesDetector.java
new file mode 100644
index 0000000..ec6820a
--- /dev/null
+++ b/modules/test-utils/src/main/java/org/rhq/test/DuplicatePackagesDetector.java
@@ -0,0 +1,194 @@
+package org.rhq.test;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * This will walk a set of jar files and directories and print out
+ * packages that are found in more than one jar file.
+ *
+ * @author John Mazzitelli
+ */
+public class DuplicatePackagesDetector {
+
+ static Visitor visitor;
+
+ static boolean verbose = Boolean.getBoolean("rhq.verbose");
+
+ private static void debug(String msg) {
+ if (verbose) {
+ System.out.println(msg);
+ }
+ }
+
+ /**
+ * Each argument is the name of a .jar file or a directory that will
+ * be walked looking for jar files.
+ */
+ public static void main(String[] args) {
+ visitor = new Visitor();
+
+ if (args.length == 0) {
+ // no arguments given, try to use the user's local maven repo of all the
RHQ libraries
+ String homeEnv = System.getenv("HOME");
+ if (homeEnv != null && homeEnv.length() > 0) {
+ String s = File.separator;
+ args = new String[] { homeEnv + s + ".m2" + s +
"repository" + s + "org" + s + "rhq" };
+ }
+ }
+
+ for (String arg : args) {
+ File file = new File(arg);
+ debug("Processing [" + file.getAbsolutePath() + "]...");
+ if (file.exists()) {
+ if (file.isDirectory()) {
+ processDirectory(file);
+ } else {
+ processFile(file);
+ }
+ } else {
+ System.err.println("File does not exist: " + arg);
+ }
+
+ }
+
+ // print out what we've found
+
System.out.println("======================================================================");
+ Map<String, Set<String>> map = visitor.getMap(); // key=dir (pkg)
name; value=list of jars where dir is
+ for (Entry<String, Set<String>> entry : map.entrySet()) {
+ String packageName = entry.getKey();
+ Set<String> jarFiles = entry.getValue();
+ if (jarFiles.size() > 1 || verbose) {
+ System.out.println(packageName);
+ for (String jarFile : jarFiles) {
+ System.out.println("\t" + jarFile);
+ }
+ }
+ }
+ }
+
+ public static void processDirectory(File dir) {
+ File[] dirEntries = dir.listFiles();
+ if (dirEntries != null) {
+ for (File dirEntry : dirEntries) {
+ if (dirEntry.isDirectory()) {
+ processDirectory(dirEntry);
+ } else {
+ processFile(dirEntry);
+ }
+ }
+ }
+ }
+
+ public static void processFile(File file) {
+ // if its not a .jar file, ignore it
+ if (!file.getName().endsWith(".jar")) {
+ debug("Not a jar file, skipping [" + file.getAbsolutePath() +
"]");
+ return;
+ }
+
+ try {
+ walkZipFile(file, visitor);
+ } catch (Exception e) {
+ System.err.println("Cannot process jar file [" + file +
"]:" + e.toString());
+ }
+ }
+
+ /**
+ * Walks the entries of a zip file, allowing a listener to "visit" each
node and perform tasks on
+ * the zip entry.
+ *
+ * @param zipFile the zip file to walk
+ * @param visitor the object that will be notified for each entry in the zip file
+ *
+ * @throws Exception if any errors occur during the reading or visiting
+ */
+ public static void walkZipFile(File zipFile, Visitor visitor) throws Exception {
+ FileInputStream fis = new FileInputStream(zipFile);
+ try {
+ InputStream zipContent = new BufferedInputStream(fis);
+ try {
+ ZipInputStream zis = new ZipInputStream(zipContent);
+
+ ZipEntry e;
+ while ((e = zis.getNextEntry()) != null) {
+ boolean keepGoing = visitor
+ .visit(e, zis, (verbose) ? zipFile.getAbsolutePath() :
zipFile.getName());
+ if (!keepGoing) {
+ break; // visitor told us to stop
+ }
+ }
+ } finally {
+ zipContent.close();
+ }
+ } finally {
+ fis.close();
+ }
+ }
+
+ public static class Visitor {
+ // keyed on directory name - the value is all the jar files that had that
directory in it
+ private Map<String, Set<String>> map = new HashMap<String,
Set<String>>();
+
+ public Map<String, Set<String>> getMap() {
+ return map;
+ }
+
+ /**
+ * Visits a specific zip file entry. Implementations can read the entry content
from the given stream but
+ * must <b>not</b> close the stream - the caller of this method will
handle the lifecycle of the stream.
+ *
+ * @param entry the entry being visited
+ * @param stream the stream containing the zip content
+ * @param zipFilePath the actual zip file that is being walked
+ * @return the visitor should return <code>true</code> if everything
is OK and processing of the zip content
+ * should continue; returning <code>false</code> will tell
the walker to abort further traversing
+ * of the zip content.
+ * @throws Exception if the visitation failed for some reason - this will abort
further walking of the zip content
+ */
+ public boolean visit(ZipEntry entry, ZipInputStream stream, String zipFilePath)
throws Exception {
+ if (!entry.isDirectory()) {
+ String[] dirAndName = splitPathName(entry.getName());
+ if (dirAndName != null) {
+ String dirName = dirAndName[0];
+ Set<String> zipFilePaths = map.get(dirName);
+ if (zipFilePaths == null) {
+ zipFilePaths = new HashSet<String>();
+ map.put(dirName, zipFilePaths);
+ }
+ zipFilePaths.add(zipFilePath);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Splits the path from the filename and returns as a 2-dimension array.
+ * If the full name does NOT end with .class, <code>null</code> is
returned.
+ * Thus, this only processes Java classes.
+ * @param fullname the name to split
+ * @return first element is the path, second element is the filename
+ */
+ private String[] splitPathName(String fullname) {
+ if (fullname.endsWith(".class")) {
+ int lastSlash = fullname.lastIndexOf('/');
+ if (lastSlash >= 0) {
+ return new String[] { fullname.substring(0, lastSlash),
fullname.substring(lastSlash + 1) };
+ } else {
+ return new String[] { "", fullname };
+ }
+ } else {
+ return null;
+ }
+ }
+ }
+}
commit 2b26d4412f3fbc28f4131efa7732f3f5e31e4955
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Dec 1 18:04:18 2011 -0500
[BZ 759173] rename package name so we don't have the same package in different
signed jars
diff --git
a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
index 9b18acb..74f7ab8 100644
---
a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
+++
b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/AbstractPerspectiveResourceUIBean.java
@@ -7,7 +7,7 @@ import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageList;
-import org.rhq.enterprise.client.RemoteClient;
+import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.enterprise.server.resource.ResourceManagerRemote;
/**
diff --git
a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
index 9f91c88..77d8f24 100644
---
a/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
+++
b/modules/enterprise/gui/base-perspective-jar/src/main/java/org/rhq/enterprise/server/perspective/PerspectiveClientUIBean.java
@@ -12,7 +12,7 @@ import org.jboss.seam.annotations.web.RequestParameter;
import org.jboss.seam.core.Conversation;
import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.client.RemoteClient;
+import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.enterprise.server.auth.SubjectManagerRemote;
/**
diff --git
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
index 27af798..191c537 100755
---
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
+++
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/ClientMain.java
@@ -46,6 +46,7 @@ import mazz.i18n.Msg;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.commands.ClientCommand;
import org.rhq.enterprise.client.commands.ScriptCommand;
+import org.rhq.enterprise.clientapi.RemoteClient;
/**
* @author Greg Hinkle
diff --git
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
index abbc496..f51e148 100644
---
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
+++
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/InteractiveJavascriptCompletor.java
@@ -44,6 +44,7 @@ import jline.ConsoleReader;
import org.rhq.core.domain.auth.Subject;
import org.rhq.enterprise.client.utility.ReflectionUtility;
+import org.rhq.enterprise.clientapi.RemoteClientProxy;
/**
* A Contextual JavaScript interactive completor. Not perfect, but
diff --git
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
index cac73a2..3ce9598 100644
---
a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
+++
b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/commands/LoginCommand.java
@@ -30,7 +30,7 @@ import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.common.ProductInfo;
import org.rhq.core.domain.common.ServerDetails;
import org.rhq.enterprise.client.ClientMain;
-import org.rhq.enterprise.client.RemoteClient;
+import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.enterprise.server.system.ServerVersion;
/**
diff --git
a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
index 815629d..4120f2d 100644
---
a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
+++
b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/commands/ScriptCommandTest.java
@@ -11,8 +11,8 @@ import java.util.ArrayList;
import org.testng.annotations.Test;
import org.rhq.enterprise.client.ClientMain;
-import org.rhq.enterprise.client.RemoteClient;
import org.rhq.enterprise.client.commands.ScriptCommand;
+import org.rhq.enterprise.clientapi.RemoteClient;
import org.rhq.enterprise.server.alert.AlertManagerRemote;
import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
diff --git
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java
deleted file mode 100644
index bb0205b..0000000
---
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClient.java
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2008 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.client;
-
-import java.lang.reflect.Method;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.jboss.remoting.Client;
-import org.jboss.remoting.InvokerLocator;
-import org.jboss.remoting.security.SSLSocketBuilder;
-import org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker;
-
-import org.rhq.bindings.client.RhqFacade;
-import org.rhq.bindings.client.RhqManagers;
-import org.rhq.core.domain.auth.Subject;
-import org.rhq.enterprise.communications.util.SecurityUtil;
-import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
-import org.rhq.enterprise.server.alert.AlertManagerRemote;
-import org.rhq.enterprise.server.auth.SubjectManagerRemote;
-import org.rhq.enterprise.server.authz.RoleManagerRemote;
-import org.rhq.enterprise.server.bundle.BundleManagerRemote;
-import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
-import org.rhq.enterprise.server.content.ContentManagerRemote;
-import org.rhq.enterprise.server.content.RepoManagerRemote;
-import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
-import org.rhq.enterprise.server.drift.DriftManagerRemote;
-import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
-import org.rhq.enterprise.server.event.EventManagerRemote;
-import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
-import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
-import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
-import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
-import org.rhq.enterprise.server.operation.OperationManagerRemote;
-import org.rhq.enterprise.server.report.DataAccessManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceManagerRemote;
-import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
-import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
-import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
-import org.rhq.enterprise.server.support.SupportManagerRemote;
-import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
-import org.rhq.enterprise.server.system.SystemManagerRemote;
-import org.rhq.enterprise.server.tagging.TagManagerRemote;
-
-/**
- * A remote access client that provides transparent servlet-based proxies to an RHQ
Server.
- *
- * @author Greg Hinkle
- * @author Simeon Pinder
- * @author Jay Shaughnessy
- * @author John Mazzitelli
- */
-public class RemoteClient implements RhqFacade {
-
- private static final Log LOG = LogFactory.getLog(RemoteClient.class);
-
- public static final String NONSECURE_TRANSPORT = "servlet";
- public static final String SECURE_TRANSPORT = "sslservlet";
-
- private String transport;
- private final String host;
- private final int port;
- private boolean loggedIn;
- private boolean connected;
- private Map<String, Object> managers;
- private Subject subject;
- private Client remotingClient;
- private String subsystem = null;
-
- /**
- * Creates a client that will communicate with the server running on the given host
- * listening on the given port. This constructor will not attempt to connect or
login
- * to the remote server - use {@link #login(String, String)} for that.
- *
- * @param host
- * @param port
- */
- public RemoteClient(String host, int port) {
- this(null, host, port);
- }
-
- /**
- * Creates a client that will communicate with the server running on the given host
- * listening on the given port over the given transport.
- * This constructor will not attempt to connect or login
- * to the remote server - use {@link #login(String, String)} for that.
- *
- * @param transport valid values are "servlet" and "sslservlet" -
if <code>null</code>,
- * sslservlet will be used for ports that end with "443",
servlet otherwise
- * @param host
- * @param port
- */
- public RemoteClient(String transport, String host, int port) {
- this(transport, host, port, null);
- }
-
- public RemoteClient(String transport, String host, int port, String subsystem) {
- this.transport = (transport != null) ? transport : guessTransport(port);
- this.host = host;
- this.port = port;
- this.subsystem = subsystem;
- }
-
- /**
- * Connects to the remote server and logs in with the given credentials.
- * After successfully executing this, {@link #isLoggedIn()} will be
<code>true</code>
- * and {@link #getSubject()} will return the subject that this method returns.
- *
- * @param user
- * @param password
- *
- * @return the logged in user
- *
- * @throws Exception if failed to connect to the server or log in
- */
- public Subject login(String user, String password) throws Exception {
-
- logout();
- doConnect();
-
- this.subject = getSubjectManager().login(user, password);
- this.loggedIn = true;
-
- return this.subject;
- }
-
- /**
- * Logs out from the server and disconnects this client.
- */
- public void logout() {
- try {
- if (this.loggedIn && this.subject != null) {
- getSubjectManager().logout(this.subject);
- }
- } catch (Exception e) {
- // just keep going so we can disconnect this client
- }
-
- doDisconnect();
-
- this.subject = null;
- this.loggedIn = false;
- }
-
- /**
- * Connects to the remote server but does not establish a user session. This can be
used
- * with the limited API that does not require a Subject.
- *
- * After successfully executing this, {@link #isConnected()} will be
<code>true</code>
- * and {@link #getSubject()} will return the subject that this method returns.
- * @throws Exception if failed to connect to the server or log in
- */
- public void connect() throws Exception {
- if (this.loggedIn) {
- String name = (null == this.subject) ? "" :
this.subject.getName();
- throw new IllegalStateException("User " + name + " must log
out before connection can be established.");
- }
-
- doDisconnect();
- doConnect();
- this.connected = true;
- }
-
- /**
- * Disconnect from the server.
- */
- public void disconnect() {
- if (this.loggedIn) {
- String name = (null == this.subject) ? "" :
this.subject.getName();
- throw new IllegalStateException("User " + name + " is logged
in. Call logout() instead of disconnect().");
- }
-
- doDisconnect();
- this.connected = false;
- }
-
- /**
- * Returns <code>true</code> if and only if this client successfully
connected
- * to the remote server and the user successfully logged in.
- *
- * @return if the user was able to connect and log into the server
- */
- public boolean isLoggedIn() {
- return this.loggedIn;
- }
-
- /**
- * Returns <code>true</code> if and only if this client successfully
connected
- * to the remote server.
- *
- * @return if the user was able to connect and log into the server
- */
- public boolean isConnected() {
- return this.connected;
- }
-
- /**
- * Returns the information on the user that is logged in.
- * May be <code>null</code> if the user never logged in successfully.
- *
- * @return user information or <code>null</code>
- */
- public Subject getSubject() {
- return this.subject;
- }
-
- public String getHost() {
- return this.host;
- }
-
- public int getPort() {
- return this.port;
- }
-
- public String getTransport() {
- return transport;
- }
-
- protected String guessTransport(int port) {
- return String.valueOf(port).endsWith("443") ? SECURE_TRANSPORT :
NONSECURE_TRANSPORT;
- }
-
- /**
- * Sets the underlying transport to use to communicate with the server.
- * Available transports are "servlet" and "sslservlet".
- * If you set it to <code>null</code>, then the transport to be used
will
- * be set appropriately for the {@link #getPort()} (e.g. a secure transport
- * will be used for ports that end with 443, a non-secure transport will be
- * used for all other ports).
- *
- * @param transport
- */
- public void setTransport(String transport) {
- this.transport = transport;
- }
-
- public AlertManagerRemote getAlertManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AlertManager);
- }
-
- public AlertDefinitionManagerRemote getAlertDefinitionManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AlertDefinitionManager);
- }
-
- public AvailabilityManagerRemote getAvailabilityManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.AvailabilityManager);
- }
-
- public BundleManagerRemote getBundleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.BundleManager);
- }
-
- public CallTimeDataManagerRemote getCallTimeDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.CallTimeDataManager);
- }
-
- public DriftManagerRemote getDriftManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DriftManager);
- }
-
- public DriftTemplateManagerRemote getDriftTemplateManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DriftTemplateManager);
- }
-
- public RepoManagerRemote getRepoManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RepoManager);
- }
-
- public ConfigurationManagerRemote getConfigurationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ConfigurationManager);
- }
-
- public ContentManagerRemote getContentManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ContentManager);
- }
-
- public DataAccessManagerRemote getDataAccessManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DataAccessManager);
- }
-
- public DiscoveryBossRemote getDiscoveryBoss() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.DiscoveryBoss);
- }
-
- public EventManagerRemote getEventManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.EventManager);
- }
-
- public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
- return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementBaselineManager);
- }
-
- public MeasurementDataManagerRemote getMeasurementDataManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementDataManager);
- }
-
- public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
- return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementDefinitionManager);
- }
-
- public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
- return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementScheduleManager);
- }
-
- public OperationManagerRemote getOperationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.OperationManager);
- }
-
- public ResourceManagerRemote getResourceManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceManager);
- }
-
- public ResourceFactoryManagerRemote getResourceFactoryManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceFactoryManager);
- }
-
- public ResourceGroupManagerRemote getResourceGroupManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceGroupManager);
- }
-
- public ResourceTypeManagerRemote getResourceTypeManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceTypeManager);
- }
-
- public RoleManagerRemote getRoleManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RoleManager);
- }
-
- public SavedSearchManagerRemote getSavedSearchManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SavedSearchManager);
- }
-
- public SubjectManagerRemote getSubjectManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SubjectManager);
- }
-
- public SupportManagerRemote getSupportManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SupportManager);
- }
-
- public SystemManagerRemote getSystemManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SystemManager);
- }
-
- public RemoteInstallManagerRemote getRemoteInstallManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.RemoteInstallManager);
- }
-
- public TagManagerRemote getTagManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.TagManager);
- }
-
- public SynchronizationManagerRemote getSynchronizationManager() {
- return RemoteClientProxy.getProcessor(this, RhqManagers.SynchronizationManager);
- }
-
- /**
- * Returns the map of all remote managers running in the server that this
- * client can talk to.
- *
- * @return Map K=manager name V=remote proxy
- */
- public Map<String, Object> getManagers() {
- if (this.managers == null) {
-
- this.managers = new HashMap<String, Object>();
-
- for (RhqManagers manager : RhqManagers.values()) {
- try {
- Method m = this.getClass().getMethod("get" +
manager.name());
- if (manager.enabled()) {
- this.managers.put(manager.name(), m.invoke(this));
- }
- } catch (Throwable e) {
- LOG.error("Failed to load manager " + manager + " due
to missing class.", e);
- }
- }
- }
-
- return this.managers;
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + "[" + "transport=" +
transport + ", host=" + host + ", port=" + port
- + ", subsystem=" + subsystem + ", connected=" + connected
+ ", loggedIn=" + loggedIn + ", subject="
- + subject + ']';
- }
-
- /**
- * Returns the internal JBoss/Remoting client used to perform the low-level
- * comm with the server.
- *
- * This is package-scoped so the proxy can use it.
- *
- * @return remoting client used to talk to the server
- */
- Client getRemotingClient() {
- return this.remotingClient;
- }
-
- private void doDisconnect() {
- try {
- if (this.remotingClient != null && this.remotingClient.isConnected())
{
- this.remotingClient.disconnect();
- }
- } catch (Exception e) {
- LOG.warn(e); // TODO what to do here?
- } finally {
- this.remotingClient = null;
- }
- }
-
- private void doConnect() throws Exception {
- String locatorURI = this.transport + "://" + this.host + ":"
+ this.port
- + "/jboss-remoting-servlet-invoker/ServerInvokerServlet";
- InvokerLocator locator = new InvokerLocator(locatorURI);
-
- String subsystem = "REMOTEAPI";
- if ((this.subsystem != null) &&
(this.subsystem.trim().equalsIgnoreCase("WSREMOTEAPI"))) {
- subsystem = "WSREMOTEAPI";
- }
- Map<String, String> remotingConfig = buildRemotingConfig(locatorURI);
- this.remotingClient = new Client(locator, subsystem, remotingConfig);
- this.remotingClient.connect();
- }
-
- private Map<String, String> buildRemotingConfig(String locatorURI) {
- Map<String, String> config = new HashMap<String, String>();
- if (SecurityUtil.isTransportSecure(locatorURI)) {
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH,
"data/keystore.dat");
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_ALGORITHM,
"SunX509");
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_TYPE,
"JKS");
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_PASSWORD,
"password");
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_PASSWORD,
"password");
- setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_FILE_PATH,
null);
- setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_ALGORITHM,
null);
- setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_TYPE, null);
- setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_PASSWORD, null);
- setConfigProp(config, SSLSocketBuilder.REMOTING_SSL_PROTOCOL, null);
- setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_ALIAS,
"self");
- setConfigProp(config, SSLSocketBuilder.REMOTING_SERVER_AUTH_MODE,
"false");
- config.put(SSLSocketBuilder.REMOTING_SOCKET_USE_CLIENT_MODE,
"true");
-
- // since we do not know the server's client-auth mode, assume we need a
keystore and let's make sure we have one
- SSLSocketBuilder dummy_sslbuilder = new SSLSocketBuilder(); // just so we can
test finding our keystore
- try {
- // this allows the configured keystore file to be a URL, file path or a
resource relative to our classloader
-
dummy_sslbuilder.setKeyStoreURL(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH));
- } catch (Exception e) {
- // this probably is due to the fact that the keystore doesn't exist
yet - let's prepare one now
-
SecurityUtil.createKeyStore(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH),
- config.get(SSLSocketBuilder.REMOTING_KEY_ALIAS), "CN=RHQ,
OU=RedHat,
O=redhat.com, C=US",
- config.get(SSLSocketBuilder.REMOTING_KEY_STORE_PASSWORD),
- config.get(SSLSocketBuilder.REMOTING_KEY_PASSWORD), "DSA",
36500);
-
- // now try to set it again, if an exception is still thrown, it's an
unrecoverable error
-
dummy_sslbuilder.setKeyStoreURL(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH));
- }
-
- // in case the transport floats over https - we want to make sure a hostname
verifier is installed and allows all hosts
- config.put(HTTPSClientInvoker.IGNORE_HTTPS_HOST, "true");
- }
- return config;
- }
-
- /**
- * Looks up the prop name in system properties and puts the value in the map. If the
property
- * isn't set, the given default is used. If the given default is null and the
property isn't
- * set, then the map is not populated.
- *
- * @param configMap
- * @param propName
- * @param defaultValue
- */
- private void setConfigProp(Map<String, String> configMap, String propName,
String defaultValue) {
- String propValue = System.getProperty(propName, defaultValue);
- if (propValue != null) {
- configMap.put(propName, propValue);
- }
- return;
- }
-}
\ No newline at end of file
diff --git
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClientProxy.java
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClientProxy.java
deleted file mode 100644
index 38dec8b..0000000
---
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/client/RemoteClientProxy.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2008 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.client;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.jboss.remoting.invocation.NameBasedInvocation;
-
-import org.rhq.bindings.client.AbstractRhqFacadeProxy;
-import org.rhq.bindings.client.RhqManagers;
-import org.rhq.bindings.util.InterfaceSimplifier;
-import org.rhq.core.server.ExternalizableStrategy;
-
-/**
- * This class acts as a local SLSB proxy to make remote invocations
- * to SLSB Remotes over a remoting invoker.
- *
- * @author Greg Hinkle
- * @author Lukas Krejci
- */
-public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
-
- private static final Log LOG = LogFactory.getLog(RemoteClientProxy.class);
-
- public RemoteClientProxy(RemoteClient client, RhqManagers manager) {
- super(client, manager);
- }
-
- public Class<?> getRemoteInterface() {
- return this.getManager().remote();
- }
-
- @SuppressWarnings("unchecked")
- public static <T> T getProcessor(RemoteClient remoteClient, RhqManagers
manager) {
- try {
- RemoteClientProxy gpc = new RemoteClientProxy(remoteClient, manager);
-
- Class<?> intf = InterfaceSimplifier.simplify(manager.remote());
-
- return (T) Proxy
- .newProxyInstance(Thread.currentThread().getContextClassLoader(), new
Class<?>[] { intf }, gpc);
- } catch (Exception e) {
- throw new RuntimeException("Failed to get remote connection proxy",
e);
- }
- }
-
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- return super.invoke(proxy, method, args);
- } catch (Throwable t) {
- LOG.debug(t);
- throw t;
- }
- }
-
- protected Object doInvoke(Object proxy, Method originalMethod,
java.lang.Class<?>[] argTypes, Object[] args) throws Throwable {
-
ExternalizableStrategy.setStrategy(ExternalizableStrategy.Subsystem.REFLECTIVE_SERIALIZATION);
-
- String methodName = getManager().beanName() + ":" +
originalMethod.getName();
-
- String[] paramSig = createParamSignature(argTypes);
-
- NameBasedInvocation request = new NameBasedInvocation(methodName, args,
paramSig);
-
- Object response = getRhqFacade().getRemotingClient().invoke(request);
-
- if (response instanceof Throwable) {
- throw (Throwable) response;
- }
-
- return response;
- }
-
- private String[] createParamSignature(Class<?>[] types) {
- String[] paramSig = new String[types.length];
- for (int x = 0; x < types.length; x++) {
- paramSig[x] = types[x].getName();
- }
- return paramSig;
- }
-}
diff --git
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
new file mode 100644
index 0000000..0ec565e
--- /dev/null
+++
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClient.java
@@ -0,0 +1,506 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.clientapi;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jboss.remoting.Client;
+import org.jboss.remoting.InvokerLocator;
+import org.jboss.remoting.security.SSLSocketBuilder;
+import org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker;
+
+import org.rhq.bindings.client.RhqFacade;
+import org.rhq.bindings.client.RhqManagers;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.communications.util.SecurityUtil;
+import org.rhq.enterprise.server.alert.AlertDefinitionManagerRemote;
+import org.rhq.enterprise.server.alert.AlertManagerRemote;
+import org.rhq.enterprise.server.auth.SubjectManagerRemote;
+import org.rhq.enterprise.server.authz.RoleManagerRemote;
+import org.rhq.enterprise.server.bundle.BundleManagerRemote;
+import org.rhq.enterprise.server.configuration.ConfigurationManagerRemote;
+import org.rhq.enterprise.server.content.ContentManagerRemote;
+import org.rhq.enterprise.server.content.RepoManagerRemote;
+import org.rhq.enterprise.server.discovery.DiscoveryBossRemote;
+import org.rhq.enterprise.server.drift.DriftManagerRemote;
+import org.rhq.enterprise.server.drift.DriftTemplateManagerRemote;
+import org.rhq.enterprise.server.event.EventManagerRemote;
+import org.rhq.enterprise.server.install.remote.RemoteInstallManagerRemote;
+import org.rhq.enterprise.server.measurement.AvailabilityManagerRemote;
+import org.rhq.enterprise.server.measurement.CallTimeDataManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementBaselineManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDataManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerRemote;
+import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerRemote;
+import org.rhq.enterprise.server.operation.OperationManagerRemote;
+import org.rhq.enterprise.server.report.DataAccessManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceFactoryManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceManagerRemote;
+import org.rhq.enterprise.server.resource.ResourceTypeManagerRemote;
+import org.rhq.enterprise.server.resource.group.ResourceGroupManagerRemote;
+import org.rhq.enterprise.server.search.SavedSearchManagerRemote;
+import org.rhq.enterprise.server.support.SupportManagerRemote;
+import org.rhq.enterprise.server.sync.SynchronizationManagerRemote;
+import org.rhq.enterprise.server.system.SystemManagerRemote;
+import org.rhq.enterprise.server.tagging.TagManagerRemote;
+
+/**
+ * A remote access client that provides transparent servlet-based proxies to an RHQ
Server.
+ *
+ * @author Greg Hinkle
+ * @author Simeon Pinder
+ * @author Jay Shaughnessy
+ * @author John Mazzitelli
+ */
+public class RemoteClient implements RhqFacade {
+
+ private static final Log LOG = LogFactory.getLog(RemoteClient.class);
+
+ public static final String NONSECURE_TRANSPORT = "servlet";
+ public static final String SECURE_TRANSPORT = "sslservlet";
+
+ private String transport;
+ private final String host;
+ private final int port;
+ private boolean loggedIn;
+ private boolean connected;
+ private Map<String, Object> managers;
+ private Subject subject;
+ private Client remotingClient;
+ private String subsystem = null;
+
+ /**
+ * Creates a client that will communicate with the server running on the given host
+ * listening on the given port. This constructor will not attempt to connect or
login
+ * to the remote server - use {@link #login(String, String)} for that.
+ *
+ * @param host
+ * @param port
+ */
+ public RemoteClient(String host, int port) {
+ this(null, host, port);
+ }
+
+ /**
+ * Creates a client that will communicate with the server running on the given host
+ * listening on the given port over the given transport.
+ * This constructor will not attempt to connect or login
+ * to the remote server - use {@link #login(String, String)} for that.
+ *
+ * @param transport valid values are "servlet" and "sslservlet" -
if <code>null</code>,
+ * sslservlet will be used for ports that end with "443",
servlet otherwise
+ * @param host
+ * @param port
+ */
+ public RemoteClient(String transport, String host, int port) {
+ this(transport, host, port, null);
+ }
+
+ public RemoteClient(String transport, String host, int port, String subsystem) {
+ this.transport = (transport != null) ? transport : guessTransport(port);
+ this.host = host;
+ this.port = port;
+ this.subsystem = subsystem;
+ }
+
+ /**
+ * Connects to the remote server and logs in with the given credentials.
+ * After successfully executing this, {@link #isLoggedIn()} will be
<code>true</code>
+ * and {@link #getSubject()} will return the subject that this method returns.
+ *
+ * @param user
+ * @param password
+ *
+ * @return the logged in user
+ *
+ * @throws Exception if failed to connect to the server or log in
+ */
+ public Subject login(String user, String password) throws Exception {
+
+ logout();
+ doConnect();
+
+ this.subject = getSubjectManager().login(user, password);
+ this.loggedIn = true;
+
+ return this.subject;
+ }
+
+ /**
+ * Logs out from the server and disconnects this client.
+ */
+ public void logout() {
+ try {
+ if (this.loggedIn && this.subject != null) {
+ getSubjectManager().logout(this.subject);
+ }
+ } catch (Exception e) {
+ // just keep going so we can disconnect this client
+ }
+
+ doDisconnect();
+
+ this.subject = null;
+ this.loggedIn = false;
+ }
+
+ /**
+ * Connects to the remote server but does not establish a user session. This can be
used
+ * with the limited API that does not require a Subject.
+ *
+ * After successfully executing this, {@link #isConnected()} will be
<code>true</code>
+ * and {@link #getSubject()} will return the subject that this method returns.
+ * @throws Exception if failed to connect to the server or log in
+ */
+ public void connect() throws Exception {
+ if (this.loggedIn) {
+ String name = (null == this.subject) ? "" :
this.subject.getName();
+ throw new IllegalStateException("User " + name + " must log
out before connection can be established.");
+ }
+
+ doDisconnect();
+ doConnect();
+ this.connected = true;
+ }
+
+ /**
+ * Disconnect from the server.
+ */
+ public void disconnect() {
+ if (this.loggedIn) {
+ String name = (null == this.subject) ? "" :
this.subject.getName();
+ throw new IllegalStateException("User " + name + " is logged
in. Call logout() instead of disconnect().");
+ }
+
+ doDisconnect();
+ this.connected = false;
+ }
+
+ /**
+ * Returns <code>true</code> if and only if this client successfully
connected
+ * to the remote server and the user successfully logged in.
+ *
+ * @return if the user was able to connect and log into the server
+ */
+ public boolean isLoggedIn() {
+ return this.loggedIn;
+ }
+
+ /**
+ * Returns <code>true</code> if and only if this client successfully
connected
+ * to the remote server.
+ *
+ * @return if the user was able to connect and log into the server
+ */
+ public boolean isConnected() {
+ return this.connected;
+ }
+
+ /**
+ * Returns the information on the user that is logged in.
+ * May be <code>null</code> if the user never logged in successfully.
+ *
+ * @return user information or <code>null</code>
+ */
+ public Subject getSubject() {
+ return this.subject;
+ }
+
+ public String getHost() {
+ return this.host;
+ }
+
+ public int getPort() {
+ return this.port;
+ }
+
+ public String getTransport() {
+ return transport;
+ }
+
+ protected String guessTransport(int port) {
+ return String.valueOf(port).endsWith("443") ? SECURE_TRANSPORT :
NONSECURE_TRANSPORT;
+ }
+
+ /**
+ * Sets the underlying transport to use to communicate with the server.
+ * Available transports are "servlet" and "sslservlet".
+ * If you set it to <code>null</code>, then the transport to be used
will
+ * be set appropriately for the {@link #getPort()} (e.g. a secure transport
+ * will be used for ports that end with 443, a non-secure transport will be
+ * used for all other ports).
+ *
+ * @param transport
+ */
+ public void setTransport(String transport) {
+ this.transport = transport;
+ }
+
+ public AlertManagerRemote getAlertManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.AlertManager);
+ }
+
+ public AlertDefinitionManagerRemote getAlertDefinitionManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.AlertDefinitionManager);
+ }
+
+ public AvailabilityManagerRemote getAvailabilityManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.AvailabilityManager);
+ }
+
+ public BundleManagerRemote getBundleManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.BundleManager);
+ }
+
+ public CallTimeDataManagerRemote getCallTimeDataManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.CallTimeDataManager);
+ }
+
+ public DriftManagerRemote getDriftManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.DriftManager);
+ }
+
+ public DriftTemplateManagerRemote getDriftTemplateManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.DriftTemplateManager);
+ }
+
+ public RepoManagerRemote getRepoManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.RepoManager);
+ }
+
+ public ConfigurationManagerRemote getConfigurationManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ConfigurationManager);
+ }
+
+ public ContentManagerRemote getContentManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ContentManager);
+ }
+
+ public DataAccessManagerRemote getDataAccessManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.DataAccessManager);
+ }
+
+ public DiscoveryBossRemote getDiscoveryBoss() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.DiscoveryBoss);
+ }
+
+ public EventManagerRemote getEventManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.EventManager);
+ }
+
+ public MeasurementBaselineManagerRemote getMeasurementBaselineManager() {
+ return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementBaselineManager);
+ }
+
+ public MeasurementDataManagerRemote getMeasurementDataManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.MeasurementDataManager);
+ }
+
+ public MeasurementDefinitionManagerRemote getMeasurementDefinitionManager() {
+ return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementDefinitionManager);
+ }
+
+ public MeasurementScheduleManagerRemote getMeasurementScheduleManager() {
+ return RemoteClientProxy.getProcessor(this,
RhqManagers.MeasurementScheduleManager);
+ }
+
+ public OperationManagerRemote getOperationManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.OperationManager);
+ }
+
+ public ResourceManagerRemote getResourceManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceManager);
+ }
+
+ public ResourceFactoryManagerRemote getResourceFactoryManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceFactoryManager);
+ }
+
+ public ResourceGroupManagerRemote getResourceGroupManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceGroupManager);
+ }
+
+ public ResourceTypeManagerRemote getResourceTypeManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.ResourceTypeManager);
+ }
+
+ public RoleManagerRemote getRoleManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.RoleManager);
+ }
+
+ public SavedSearchManagerRemote getSavedSearchManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.SavedSearchManager);
+ }
+
+ public SubjectManagerRemote getSubjectManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.SubjectManager);
+ }
+
+ public SupportManagerRemote getSupportManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.SupportManager);
+ }
+
+ public SystemManagerRemote getSystemManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.SystemManager);
+ }
+
+ public RemoteInstallManagerRemote getRemoteInstallManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.RemoteInstallManager);
+ }
+
+ public TagManagerRemote getTagManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.TagManager);
+ }
+
+ public SynchronizationManagerRemote getSynchronizationManager() {
+ return RemoteClientProxy.getProcessor(this, RhqManagers.SynchronizationManager);
+ }
+
+ /**
+ * Returns the map of all remote managers running in the server that this
+ * client can talk to.
+ *
+ * @return Map K=manager name V=remote proxy
+ */
+ public Map<String, Object> getManagers() {
+ if (this.managers == null) {
+
+ this.managers = new HashMap<String, Object>();
+
+ for (RhqManagers manager : RhqManagers.values()) {
+ try {
+ Method m = this.getClass().getMethod("get" +
manager.name());
+ if (manager.enabled()) {
+ this.managers.put(manager.name(), m.invoke(this));
+ }
+ } catch (Throwable e) {
+ LOG.error("Failed to load manager " + manager + " due
to missing class.", e);
+ }
+ }
+ }
+
+ return this.managers;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName() + "[" + "transport=" +
transport + ", host=" + host + ", port=" + port
+ + ", subsystem=" + subsystem + ", connected=" + connected
+ ", loggedIn=" + loggedIn + ", subject="
+ + subject + ']';
+ }
+
+ /**
+ * Returns the internal JBoss/Remoting client used to perform the low-level
+ * comm with the server.
+ *
+ * This is package-scoped so the proxy can use it.
+ *
+ * @return remoting client used to talk to the server
+ */
+ Client getRemotingClient() {
+ return this.remotingClient;
+ }
+
+ private void doDisconnect() {
+ try {
+ if (this.remotingClient != null && this.remotingClient.isConnected())
{
+ this.remotingClient.disconnect();
+ }
+ } catch (Exception e) {
+ LOG.warn(e); // TODO what to do here?
+ } finally {
+ this.remotingClient = null;
+ }
+ }
+
+ private void doConnect() throws Exception {
+ String locatorURI = this.transport + "://" + this.host + ":"
+ this.port
+ + "/jboss-remoting-servlet-invoker/ServerInvokerServlet";
+ InvokerLocator locator = new InvokerLocator(locatorURI);
+
+ String subsystem = "REMOTEAPI";
+ if ((this.subsystem != null) &&
(this.subsystem.trim().equalsIgnoreCase("WSREMOTEAPI"))) {
+ subsystem = "WSREMOTEAPI";
+ }
+ Map<String, String> remotingConfig = buildRemotingConfig(locatorURI);
+ this.remotingClient = new Client(locator, subsystem, remotingConfig);
+ this.remotingClient.connect();
+ }
+
+ private Map<String, String> buildRemotingConfig(String locatorURI) {
+ Map<String, String> config = new HashMap<String, String>();
+ if (SecurityUtil.isTransportSecure(locatorURI)) {
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH,
"data/keystore.dat");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_ALGORITHM,
"SunX509");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_TYPE,
"JKS");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_STORE_PASSWORD,
"password");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_PASSWORD,
"password");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_FILE_PATH,
null);
+ setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_ALGORITHM,
null);
+ setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_TYPE, null);
+ setConfigProp(config, SSLSocketBuilder.REMOTING_TRUST_STORE_PASSWORD, null);
+ setConfigProp(config, SSLSocketBuilder.REMOTING_SSL_PROTOCOL, null);
+ setConfigProp(config, SSLSocketBuilder.REMOTING_KEY_ALIAS,
"self");
+ setConfigProp(config, SSLSocketBuilder.REMOTING_SERVER_AUTH_MODE,
"false");
+ config.put(SSLSocketBuilder.REMOTING_SOCKET_USE_CLIENT_MODE,
"true");
+
+ // since we do not know the server's client-auth mode, assume we need a
keystore and let's make sure we have one
+ SSLSocketBuilder dummy_sslbuilder = new SSLSocketBuilder(); // just so we can
test finding our keystore
+ try {
+ // this allows the configured keystore file to be a URL, file path or a
resource relative to our classloader
+
dummy_sslbuilder.setKeyStoreURL(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH));
+ } catch (Exception e) {
+ // this probably is due to the fact that the keystore doesn't exist
yet - let's prepare one now
+
SecurityUtil.createKeyStore(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH),
+ config.get(SSLSocketBuilder.REMOTING_KEY_ALIAS), "CN=RHQ,
OU=RedHat,
O=redhat.com, C=US",
+ config.get(SSLSocketBuilder.REMOTING_KEY_STORE_PASSWORD),
+ config.get(SSLSocketBuilder.REMOTING_KEY_PASSWORD), "DSA",
36500);
+
+ // now try to set it again, if an exception is still thrown, it's an
unrecoverable error
+
dummy_sslbuilder.setKeyStoreURL(config.get(SSLSocketBuilder.REMOTING_KEY_STORE_FILE_PATH));
+ }
+
+ // in case the transport floats over https - we want to make sure a hostname
verifier is installed and allows all hosts
+ config.put(HTTPSClientInvoker.IGNORE_HTTPS_HOST, "true");
+ }
+ return config;
+ }
+
+ /**
+ * Looks up the prop name in system properties and puts the value in the map. If the
property
+ * isn't set, the given default is used. If the given default is null and the
property isn't
+ * set, then the map is not populated.
+ *
+ * @param configMap
+ * @param propName
+ * @param defaultValue
+ */
+ private void setConfigProp(Map<String, String> configMap, String propName,
String defaultValue) {
+ String propValue = System.getProperty(propName, defaultValue);
+ if (propValue != null) {
+ configMap.put(propName, propValue);
+ }
+ return;
+ }
+}
\ No newline at end of file
diff --git
a/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
new file mode 100644
index 0000000..a3daeb7
--- /dev/null
+++
b/modules/enterprise/remoting/client-api/src/main/java/org/rhq/enterprise/clientapi/RemoteClientProxy.java
@@ -0,0 +1,101 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.clientapi;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.jboss.remoting.invocation.NameBasedInvocation;
+
+import org.rhq.bindings.client.AbstractRhqFacadeProxy;
+import org.rhq.bindings.client.RhqManagers;
+import org.rhq.bindings.util.InterfaceSimplifier;
+import org.rhq.core.server.ExternalizableStrategy;
+
+/**
+ * This class acts as a local SLSB proxy to make remote invocations
+ * to SLSB Remotes over a remoting invoker.
+ *
+ * @author Greg Hinkle
+ * @author Lukas Krejci
+ */
+public class RemoteClientProxy extends AbstractRhqFacadeProxy<RemoteClient> {
+
+ private static final Log LOG = LogFactory.getLog(RemoteClientProxy.class);
+
+ public RemoteClientProxy(RemoteClient client, RhqManagers manager) {
+ super(client, manager);
+ }
+
+ public Class<?> getRemoteInterface() {
+ return this.getManager().remote();
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T getProcessor(RemoteClient remoteClient, RhqManagers
manager) {
+ try {
+ RemoteClientProxy gpc = new RemoteClientProxy(remoteClient, manager);
+
+ Class<?> intf = InterfaceSimplifier.simplify(manager.remote());
+
+ return (T) Proxy
+ .newProxyInstance(Thread.currentThread().getContextClassLoader(), new
Class<?>[] { intf }, gpc);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to get remote connection proxy",
e);
+ }
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ try {
+ return super.invoke(proxy, method, args);
+ } catch (Throwable t) {
+ LOG.debug(t);
+ throw t;
+ }
+ }
+
+ protected Object doInvoke(Object proxy, Method originalMethod,
java.lang.Class<?>[] argTypes, Object[] args) throws Throwable {
+
ExternalizableStrategy.setStrategy(ExternalizableStrategy.Subsystem.REFLECTIVE_SERIALIZATION);
+
+ String methodName = getManager().beanName() + ":" +
originalMethod.getName();
+
+ String[] paramSig = createParamSignature(argTypes);
+
+ NameBasedInvocation request = new NameBasedInvocation(methodName, args,
paramSig);
+
+ Object response = getRhqFacade().getRemotingClient().invoke(request);
+
+ if (response instanceof Throwable) {
+ throw (Throwable) response;
+ }
+
+ return response;
+ }
+
+ private String[] createParamSignature(Class<?>[] types) {
+ String[] paramSig = new String[types.length];
+ for (int x = 0; x < types.length; x++) {
+ paramSig[x] = types[x].getName();
+ }
+ return paramSig;
+ }
+}
commit 81960e013533ee848aacc7716bf4bc254df93b2d
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 1 12:17:43 2011 -0500
[Bug 759167 - Client side exception generated when viewing empty compatible group]
This turns out to be something of a happy accident. The offending code
made an assumption that the group would not be empty. But looking more
closely it seems to me that the entire code block is unnecessary. Moreover,
it added a round trip to the DB when displaying the group level dashboard.
It was trying to determine whether to render the bundle deployment
portlet. From what I can see, this can be determined already by looking
at the type's facets. And in fact the code was already in place. So,
- removing unnecessary db round-trip code (was even being done for mixed groups)
- keeping the existing facet-based code, which seems to do the right thing
- fixing a secondary problem that displayed the bundle deployment portlet for mixed
groups.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
index ec71b3f..a50cea5 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/DashboardView.java
@@ -246,8 +246,8 @@ public class DashboardView extends LocatableVLayout {
} else {
// TODO: i18n
Message message = new Message("There is already a dashboard
named '" + trimmedName
- + "'. Please specify a name that is not already in
use.", Message.Severity.Error,
- EnumSet.of(Message.Option.Transient));
+ + "'. Please specify a name that is not already in
use.", Message.Severity.Error, EnumSet
+ .of(Message.Option.Transient));
CoreGUI.getMessageCenter().notify(message);
nameItem.setValue(storedDashboard.getName());
}
@@ -299,8 +299,6 @@ public class DashboardView extends LocatableVLayout {
}
});
-
-
// build the menu of valid portlets for this context, sorted by portlet name
final Menu addPortletMenu = new
LocatableMenu(editForm.extendLocatorId("PortletMenu"));
LinkedHashMap<String, String> valueMap;
@@ -512,6 +510,7 @@ public class DashboardView extends LocatableVLayout {
groupMenuMap.remove(GroupOobsPortlet.KEY);
groupMenuMap.remove(GroupPkgHistoryPortlet.KEY);
groupMenuMap.remove(GroupConfigurationUpdatesPortlet.KEY);
+ groupMenuMap.remove(GroupBundleDeploymentsPortlet.KEY);
} else {
// for compatible may still need to do some pruning.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/summary/ActivityView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/summary/ActivityView.java
index 09abe9d..991098e 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/summary/ActivityView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/inventory/groups/detail/summary/ActivityView.java
@@ -18,11 +18,9 @@
*/
package org.rhq.enterprise.gui.coregui.client.inventory.groups.detail.summary;
-import java.util.HashMap;
import java.util.Map;
import java.util.Set;
-import com.allen_sauer.gwt.log.client.Log;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.util.BooleanCallback;
import com.smartgwt.client.util.SC;
@@ -36,16 +34,11 @@ import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.DashboardCriteria;
-import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.dashboard.Dashboard;
import org.rhq.core.domain.dashboard.DashboardCategory;
import org.rhq.core.domain.dashboard.DashboardPortlet;
-import org.rhq.core.domain.resource.Resource;
-import org.rhq.core.domain.resource.ResourceCategory;
-import org.rhq.core.domain.resource.group.GroupCategory;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
-import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.InitializableView;
@@ -55,7 +48,6 @@ import org.rhq.enterprise.gui.coregui.client.UserSessionManager;
import org.rhq.enterprise.gui.coregui.client.dashboard.DashboardContainer;
import org.rhq.enterprise.gui.coregui.client.dashboard.DashboardView;
import
org.rhq.enterprise.gui.coregui.client.dashboard.portlets.groups.GroupAlertsPortlet;
-import
org.rhq.enterprise.gui.coregui.client.dashboard.portlets.groups.GroupBundleDeploymentsPortlet;
import
org.rhq.enterprise.gui.coregui.client.dashboard.portlets.groups.GroupConfigurationUpdatesPortlet;
import
org.rhq.enterprise.gui.coregui.client.dashboard.portlets.groups.GroupMetricsPortlet;
import
org.rhq.enterprise.gui.coregui.client.dashboard.portlets.groups.GroupOperationsPortlet;
@@ -212,44 +204,6 @@ public class ActivityView extends LocatableVLayout implements
DashboardContainer
//figure out which portlets to display and how
Map<String, String> groupKeyNameMap =
DashboardView.processPortletNameMapForGroup(groupComposite);
- //initiate asynch call to execute and then update the dashboard with bundle
deployment portlet if necessary
- ResourceGroupCriteria criteria = new ResourceGroupCriteria();
- criteria.addFilterId(group.getId());
- criteria.fetchExplicitResources(true);
- criteria.setPageControl(new PageControl(0, 1));
-
GWTServiceLookup.getResourceGroupService().findResourceGroupsByCriteria(criteria,
- new AsyncCallback<PageList<ResourceGroup>>() {
- @Override
- public void onSuccess(PageList<ResourceGroup> results) {
- if (!results.isEmpty()) {
- ResourceGroup grp = results.get(0);
- Set<Resource> explicitMembers =
grp.getExplicitResources();
- Resource[] currentResources = new
Resource[explicitMembers.size()];
- explicitMembers.toArray(currentResources);
- Map<String, String> portletToAdd = new HashMap<String,
String>();
- //membership dynamically determined if all platforms then will be
compatible.
- if (group.getGroupCategory().equals(GroupCategory.COMPATIBLE)) {
- if
(currentResources[0].getResourceType().getCategory().equals(ResourceCategory.PLATFORM)) {
- //this portlet allowed to add bundle portlet monitoring
- portletToAdd.put(GroupBundleDeploymentsPortlet.KEY,
GroupBundleDeploymentsPortlet.NAME);
- }
- if (!portletToAdd.isEmpty()) {
- //update dashboard with that portlet and reload
- updateDashboardWithPortlets(portletToAdd, dashboard,
100);
- //request reload of dashboard widget
- setDashboard(dashboard);
- markForRedraw();
- }
- }
- }
- }
-
- @Override
- public void onFailure(Throwable caught) {
- Log.debug("Error retrieving information for group [" +
group.getId() + "]:" + caught.getMessage());
- }
- });
-
//reset positioning parameters
colLeft = 0;
colRight = 1;
commit 4ef245611000d6b7f72d2e6ed27b8211315d427b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 1 14:50:06 2011 +0100
[BZ 751248] - Make the sample CLI alert scripts downloadable from the server's
/download/cli-alert-scripts location and from the Downloads section of the GUI.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
index 122ccb6..114f335 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
@@ -60,6 +60,7 @@ public class DownloadsView extends LocatableVLayout {
private SectionStackSection cliSection;
private SectionStackSection bundleSection;
private SectionStackSection connectorsSection;
+ private SectionStackSection cliAlertScriptsSection;
private ProductInfo productInfo;
public DownloadsView(String locatorId) {
@@ -83,17 +84,20 @@ public class DownloadsView extends LocatableVLayout {
cliSection = new SectionStackSection(MSG.view_admin_downloads_cliDownload());
bundleSection = new
SectionStackSection(MSG.view_admin_downloads_bundleDownload());
connectorsSection = new
SectionStackSection(MSG.view_admin_downloads_connectorsDownload());
-
+ cliAlertScriptsSection = new
SectionStackSection(MSG.view_admin_downloads_cliAlertScriptsDownload());
+
agentSection.setExpanded(false);
cliSection.setExpanded(false);
bundleSection.setExpanded(false);
connectorsSection.setExpanded(false);
-
+ cliAlertScriptsSection.setExpanded(false);
+
sectionStack.addSection(agentSection);
sectionStack.addSection(cliSection);
sectionStack.addSection(bundleSection);
sectionStack.addSection(connectorsSection);
-
+ sectionStack.addSection(cliAlertScriptsSection);
+
addMember(sectionStack);
}
@@ -105,6 +109,7 @@ public class DownloadsView extends LocatableVLayout {
prepareCLISection();
prepareBundleSection();
prepareConnectorsSection();
+ prepareCliAlertScriptsSection();
}
private void prepareAgentSection() {
@@ -299,4 +304,54 @@ public class DownloadsView extends LocatableVLayout {
}
});
}
+
+ private void prepareCliAlertScriptsSection() {
+ systemManager.getCliAlertScriptDownloads(new AsyncCallback<HashMap<String,
String>>() {
+
+ @Override
+ public void onSuccess(HashMap<String, String> result) {
+ LocatableDynamicForm form = new
LocatableDynamicForm(extendLocatorId("cliAlertScripts"));
+ form.setMargin(10);
+ form.setWidth100();
+
+ if (result != null && !result.isEmpty()) {
+ int i = 0;
+ FormItem[] items = new FormItem[result.size() + 1];
+
+ StaticTextItem helpText = new
StaticTextItem("cliAlertScriptsHelp");
+ helpText.setColSpan(2);
+ helpText.setShowTitle(false);
+ helpText.setValue(MSG.view_admin_downloads_cliAlertScripts_help());
+ items[i] = helpText;
+ i++;
+
+ for (Map.Entry<String, String> entry : result.entrySet()) {
+ LinkItem linkItem = new LinkItem("cliAlertScriptLink" +
i);
+ linkItem.setColSpan(2);
+ linkItem.setShowTitle(false);
+ linkItem.setLinkTitle(entry.getKey());
+ linkItem.setValue(entry.getValue());
+ items[i] = linkItem;
+ i++;
+ }
+ form.setItems(items);
+ } else {
+ StaticTextItem item = new
StaticTextItem("noCliAlertScripts");
+ item.setColSpan(2);
+ item.setShowTitle(false);
+ item.setValue(MSG.view_admin_downloads_cliAlertScripts_none());
+ form.setItems(item);
+ }
+
+ cliAlertScriptsSection.setItems(form);
+ cliAlertScriptsSection.setExpanded(true);
+ sectionStack.markForRedraw();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+
CoreGUI.getErrorHandler().handleError(MSG.view_admin_downloads_cliAlertScripts_loadError(),
caught);
+ }
+ });
+ }
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/SystemGWTService.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/SystemGWTService.java
index 33573d1..a9fd575 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/SystemGWTService.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/gwt/SystemGWTService.java
@@ -57,6 +57,17 @@ public interface SystemGWTService extends RemoteService {
HashMap<String, String> getConnectorDownloads() throws RuntimeException;
/**
+ * Returns the name and download URL (key and value respectively) of all CLI alert
scripts
+ * that are available on the server for download. These are just scripts that users
can
+ * then use to create actual cli scripts for alert notifications. These scripts will
usually
+ * require some tweaking to be usable as alert scripts.
+ *
+ * @return info about the available CLI alert scripts
+ * @throws RuntimeException
+ */
+ HashMap<String, String> getCliAlertScriptDownloads() throws RuntimeException;
+
+ /**
* @return metadata properties about the CLI download available on the server.
*/
HashMap<String, String> getClientVersionProperties() throws RuntimeException;
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
index cf3848e..d4624ef 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
@@ -130,6 +130,26 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
}
@Override
+ public HashMap<String, String> getCliAlertScriptDownloads() throws
RuntimeException {
+ try {
+ File downloadsDir = getCliAlertScriptDownloadsDir();
+ List<File> files = getFiles(downloadsDir);
+ if (files == null) {
+ return new HashMap<String, String>(0);
+ } else {
+ HashMap<String, String> ret = new HashMap<String,
String>(files.size());
+
+ for(File file : files) {
+ ret.put(file.getName(), "/downloads/cli-alert-scripts/" +
file.getName());
+ }
+ return ret;
+ }
+ } catch (Throwable t) {
+ throw getExceptionToThrowToClient(t);
+ }
+ }
+
+ @Override
public HashMap<String, String> getClientVersionProperties() throws
RuntimeException {
File versionFile = new File(getClientDownloadDir(),
"rhq-client-version.properties");
try {
@@ -182,6 +202,15 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
return downloadDir;
}
+ private File getCliAlertScriptDownloadsDir() {
+ File serverHomeDir = getServerHomeDir();
+ File downloadDir = new File(serverHomeDir,
"deploy/rhq.ear/rhq-downloads/cli-alert-scripts");
+ if (!downloadDir.exists()) {
+ throw new RuntimeException("Server is missing connectors download
directory at [" + downloadDir + "]");
+ }
+ return downloadDir;
+ }
+
private File getClientDownloadDir() {
File serverHomeDir = getServerHomeDir();
File downloadDir = new File(serverHomeDir,
"deploy/rhq.ear/rhq-downloads/rhq-client");
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 c8df668..e96c012 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
@@ -506,6 +506,10 @@ view_admin_downloads_bundle_help = <p> This is the Bundle
Deployer tool. It is f
view_admin_downloads_bundle_link_label = Link
view_admin_downloads_bundle_link_value = Download Bundle Deployer {0}
view_admin_downloads_bundle_loadError = Cannot get bundle deployer info
+view_admin_downloads_cliAlertScriptsDownload = CLI Alert Scripts Download
+view_admin_downloads_cliAlertScripts_help = CLI alert scripts are pre-canned scripts that
can be used for creating scripted alert notifications. The scripts will usually require
some tweaking before they can be used as a notification on a particular alert definition.
+view_admin_downloads_cliAlertScripts_loadError = Cannot get info on available CLI alert
scripts for download
+view_admin_downloads_cliAlertScripts_none = No CLI alert scripts are available for
download
view_admin_downloads_cliDownload = Command Line Client Download
view_admin_downloads_cli_buildNumber = CLI Build
view_admin_downloads_cli_help = <p> This is the Command Line Client tool, otherwise
known as the CLI. It is a standalone tool that runs from within a console and provides a
command line interface to the {0} Server. You can invoke commands via the CLI as well as
run scripts to perform automated tasks. See the documentation for more information on how
to install and use the CLI. </p>
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 76bbf6a..a42707d 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
@@ -466,6 +466,10 @@ view_admin_content = Inhalte
view_admin_downloads_agent_buildNumber = Build-Nummer des Agents
view_admin_downloads_agent_md5 = MD5 Prüfsumme des Agents
view_admin_downloads_agent_version = Version des Agents
+##view_admin_downloads_cliAlertScriptsDownload = CLI Alert Scripts Download
+##view_admin_downloads_cliAlertScripts_help = CLI alert scripts are pre-canned scripts
that can be used as templates for creating actual scripted alert notifications. The
scripts will usually require some tweaking before they can be used as a notification on a
particular alert definition.
+##view_admin_downloads_cliAlertScripts_loadError = Cannot get info on available CLI alert
scripts for download
+##view_admin_downloads_cliAlertScripts_none = No CLI alert scripts are available for
download
view_admin_downloads_cli_md5 = MD5 Prüfsumme des CLI
view_admin_downloads_cli_version = Version des CLI
##view_admin_landing = In dieser Sektion können die globalen Einstellungen für RHQ
verwaltet werden. Dies schließt Sicherheitseinstellungen und Plugins ein, sowie die
Verwaltung der RHQ Server- und Agentplugins.
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 1242729..71e3530 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
@@ -505,6 +505,10 @@ view_admin_downloads_bundleDownload = バンドルデプロイヤのダウンロ
view_admin_downloads_bundle_link_label = リンク
view_admin_downloads_bundle_link_value = バンドルデプロイヤー {0} をダウンロード
view_admin_downloads_bundle_loadError = バンドルデプロイヤー情報が取得できません
+##view_admin_downloads_cliAlertScriptsDownload = CLI Alert Scripts Download
+##view_admin_downloads_cliAlertScripts_help = CLI alert scripts are pre-canned scripts
that can be used for creating scripted alert notifications. The scripts will usually
require some tweaking before they can be used as a notification on a particular alert
definition.
+##view_admin_downloads_cliAlertScripts_loadError = Cannot get info on available CLI alert
scripts for download
+##view_admin_downloads_cliAlertScripts_none = No CLI alert scripts are available for
download
view_admin_downloads_cliDownload = コマンドラインクライアントのダウンロード
view_admin_downloads_cli_buildNumber = CLIビルド
##view_admin_downloads_cli_help = <p>
これはコマンドラインツールで、CLIとして知られているものです。コンソールから実行するスタンドアロンツールで、RHQサーバーに対してコマンドラインインタフェースを提供します。自動化されたタスクを実行するためにCLIやrunスクリプトを介してコマンドを呼び出すことが可能です。CLIのインストール方法と使い方についての詳細情報はドキュメントをご覧ください。</p>
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 6acfd8a..3a60da7 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
@@ -514,6 +514,10 @@ view_admin_downloads_bundleDownload = Bundle Deployer Download
##view_admin_downloads_bundle_help = <p> view_admin_downloads_bundle_link_label =
Link
view_admin_downloads_bundle_link_value = Download Bundle Deployer {0}
view_admin_downloads_bundle_loadError = Cannot get bundle deployer info
+##view_admin_downloads_cliAlertScriptsDownload = CLI Alert Scripts Download
+##view_admin_downloads_cliAlertScripts_help = CLI alert scripts are pre-canned scripts
that can be used for creating scripted alert notifications. The scripts will usually
require some tweaking before they can be used as a notification on a particular alert
definition.
+##view_admin_downloads_cliAlertScripts_loadError = Cannot get info on available CLI alert
scripts for download
+##view_admin_downloads_cliAlertScripts_none = No CLI alert scripts are available for
download
view_admin_downloads_cliDownload = Command Line Client Download
view_admin_downloads_cli_buildNumber = CLI Build
##view_admin_downloads_cli_help = <p> view_admin_downloads_cli_link_label = Link
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 89b23e8..7e4da98 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
@@ -503,6 +503,10 @@ view_admin_downloads_bundleDownload =
Bundle\u90e8\u7f72\u5668\u4e0b\u8f7d
view_admin_downloads_bundle_link_label = \u94fe\u63a5
view_admin_downloads_bundle_link_value = Download Bundle Deployer {0}
view_admin_downloads_bundle_loadError = Cannot get bundle deployer info
+##view_admin_downloads_cliAlertScriptsDownload = CLI Alert Scripts Download
+##view_admin_downloads_cliAlertScripts_help = CLI alert scripts are pre-canned scripts
that can be used for creating scripted alert notifications. The scripts will usually
require some tweaking before they can be used as a notification on a particular alert
definition.
+##view_admin_downloads_cliAlertScripts_loadError = Cannot get info on available CLI alert
scripts for download
+##view_admin_downloads_cliAlertScripts_none = No CLI alert scripts are available for
download
view_admin_downloads_cliDownload = \u547d\u4ee4\u884c\u5ba2\u6237\u7aef\u4e0b\u8f7d
view_admin_downloads_cli_buildNumber = CLI Build
##view_admin_downloads_cli_help = <p> This is the Command Line Client tool,
otherwise known as the CLI. It is a standalone tool that runs from within a console and
provides a command line interface to the RHQ Server. You can invoke commands via the CLI
as well as run scripts to perform automated tasks. See the documentation for more
information on how to install and use the CLI. </p>
diff --git
a/modules/enterprise/server/container/src/main/downloadable-resources/cli-alert-scripts/fix-bundle-deployment.js
b/modules/enterprise/server/container/src/main/downloadable-resources/cli-alert-scripts/fix-bundle-deployment.js
new file mode 100644
index 0000000..bc38983
--- /dev/null
+++
b/modules/enterprise/server/container/src/main/downloadable-resources/cli-alert-scripts/fix-bundle-deployment.js
@@ -0,0 +1,467 @@
+/**
+ * Using this script one can automatically reset a bundle deployment to a specified
version.
+ *
+ * This script consists of several utility methods originally included in standalone
files
+ * in the samples of the CLI distribution, which have been included in this file so that
it
+ * is usable on its own even in the server environment.
+ *
+ * The configuration of the bundle and its version to reset to is located at the top of
this file.
+ * Please provide the values to the variables as instructed to make this script work in
+ * a concrete situation.
+ */
+
+//----------------------------- script configuration
+
+/**
+ * Replace the "null" with the ID of the bundle destination you want to reset.
+ *
+ * The ID of the destination can be determined from the URL. If you navigate to
+ * some destination of a bundle, the destination ID is the number at the very
+ * end of the URL in the address bar of your browser.
+ *
+ * It can also be determined programmatically using the remote API of the RHQ
+ * server but that is beyond the scope of this comment.
+ */
+var destinationId = null;
+
+/**
+ * Replace the "null" with the ID of the bundle version the deployment should
be reset to.
+ *
+ * As with the destinations, the ID of a bundle version can be determined in the GUI by
+ * examining the URL. If you navigate to some version of a bundle, the bundle version ID
is
+ * the very last number in the URL in the address bar of the browser.
+ */
+var bundleVersionId = null;
+
+/**
+ * A deployment of the bundle version may require configuration. Normally, this can be
+ * provided as a simple javascript object (using the curly braces notation). Please see
+ * the documentation of the <code>asConfiguration</code> function further
down to understand
+ * the limitations of this approach. Should the simple format using the javascript
object
+ * be not possible, you have to build up a full
org.rhq.core.domain.configuration.Configuration
+ * object.
+ */
+var deploymentConfig = null;
+
+/**
+ * The description of the deployment that will be performed.
+ */
+var description = "redeploy due to drift";
+
+// DO NOT TOUCH THIS UNLESS YOU FULLY UNDERSTAND WHAT YOU ARE DOING.
+// NOTE: It's essential that isCleanDeployment=true, otherwise files that have
drifted will not be replaced with their
+// original versions from the bundle.
+var isCleanDeployment = true;
+
+//-------------------- util.js
+
+/**
+ * If obj is a JS array or a java.util.Collection, each element is passed to
+ * the callback function. If obj is a java.util.Map, each map entry is passed
+ * to the callback function as a key/value pair. If obj is none of the
+ * aforementioned types, it is treated as a generic object and each of its
+ * properties is passed to the callback function as a name/value pair.
+ */
+function foreach(obj, fn) {
+ if (obj instanceof Array) {
+ for (i in obj) {
+ fn(obj[i]);
+ }
+ }
+ else if (obj instanceof java.util.Collection) {
+ var iterator = obj.iterator();
+ while (iterator.hasNext()) {
+ fn(iterator.next());
+ }
+ }
+ else if (obj instanceof java.util.Map) {
+ var iterator = obj.entrySet().iterator()
+ while (iterator.hasNext()) {
+ var entry = iterator.next();
+ fn(entry.key, entry.value);
+ }
+ }
+ else { // assume we have a generic object
+ for (i in obj) {
+ fn(i, obj[i]);
+ }
+ }
+}
+
+/**
+ * Iterates over obj similar to foreach. fn should be a predicate that evaluates
+ * to true or false. The first match that is found is returned.
+ */
+function find(obj, fn) {
+ if (obj instanceof Array) {
+ for (i in obj) {
+ if (fn(obj[i])) {
+ return obj[i]
+ }
+ }
+ }
+ else if (obj instanceof java.util.Collection) {
+ var iterator = obj.iterator();
+ while (iterator.hasNext()) {
+ var next = iterator.next();
+ if (fn(next)) {
+ return next;
+ }
+ }
+ }
+ else if (obj instanceof java.util.Map) {
+ var iterator = obj.entrySet().iterator();
+ while (iterator.hasNext()) {
+ var entry = iterator.next();
+ if (fn(entry.key, entry.value)) {
+ return {key: entry.key, value: entry.value};
+ }
+ }
+ }
+ else {
+ for (i in obj) {
+ if (fn(i, obj[i])) {
+ return {key: i, value: obj[i]};
+ }
+ }
+ }
+ return null;
+}
+
+/**
+ * Iterates over obj similar to foreach. fn should be a predicate that evaluates
+ * to true or false. All of the matches are returned in a java.util.List.
+ */
+function findAll(obj, fn) {
+ var matches = java.util.ArrayList();
+ if ((obj instanceof Array) || (obj instanceof java.util.Collection)) {
+ foreach(obj, function(element) {
+ if (fn(element)) {
+ matches.add(element);
+ }
+ });
+ }
+ else {
+ foreach(obj, function(key, value) {
+ if (fn(theKey, theValue)) {
+ matches.add({key: theKey, value: theValue});
+ }
+ });
+ }
+ return matches;
+}
+
+/**
+ * A convenience function to convert javascript hashes into RHQ's configuration
+ * objects.
+ * <p>
+ * The conversion of individual keys in the hash follows these rules:
+ * <ol>
+ * <li> if a value of a key is a javascript array, it is interpreted as
PropertyList
+ * <li> if a value is a hash, it is interpreted as a PropertyMap
+ * <li> otherwise it is interpreted as a PropertySimple
+ * <li> a null or undefined value is ignored
+ * </ol>
+ * <p>
+ * Note that the conversion isn't perfect, because the hash does not contain enough
+ * information to restore the names of the list members.
+ * <p>
+ * Example: <br/>
+ * <pre><code>
+ * {
+ * simple : "value",
+ * list : [ "value1", "value2"],
+ * listOfMaps : [ { k1 : "value", k2 : "value" }, { k1 :
"value2", k2 : "value2" } ]
+ * }
+ * </code></pre>
+ * gets converted to a configuration object:
+ * Configuration:
+ * <ul>
+ * <li> PropertySimple(name = "simple", value = "value")
+ * <li> PropertyList(name = "list")
+ * <ol>
+ * <li>PropertySimple(name = "list", value = "value1")
+ * <li>PropertySimple(name = "list", value = "value2")
+ * </ol>
+ * <li> PropertyList(name = "listOfMaps")
+ * <ol>
+ * <li> PropertyMap(name = "listOfMaps")
+ * <ul>
+ * <li>PropertySimple(name = "k1", value = "value")
+ * <li>PropertySimple(name = "k2", value = "value")
+ * </ul>
+ * <li> PropertyMap(name = "listOfMaps")
+ * <ul>
+ * <li>PropertySimple(name = "k1", value = "value2")
+ * <li>PropertySimple(name = "k2", value = "value2")
+ * </ul>
+ * </ol>
+ * </ul>
+ * Notice that the members of the list have the same name as the list itself
+ * which generally is not the case.
+ */
+function asConfiguration(hash) {
+
+ config = new Configuration;
+
+ for(key in hash) {
+ value = hash[key];
+
+ if (value == null) {
+ continue;
+ }
+
+ (function(parent, key, value) {
+ function isArray(obj) {
+ return typeof(obj) == 'object' && (obj instanceof Array);
+ }
+
+ function isHash(obj) {
+ return typeof(obj) == 'object' && !(obj instanceof Array);
+ }
+
+ function isPrimitive(obj) {
+ return typeof(obj) != 'object';
+ }
+
+ //this is an anonymous function, so the only way it can call itself
+ //is by getting its reference via argument.callee. Let's just assign
+ //a shorter name for it.
+ var me = arguments.callee;
+
+ var prop = null;
+
+ if (isPrimitive(value)) {
+ prop = new PropertySimple(key, new java.lang.String(value));
+ } else if (isArray(value)) {
+ prop = new PropertyList(key);
+ for(var i = 0; i < value.length; ++i) {
+ var v = value[i];
+ if (v != null) {
+ me(prop, key, v);
+ }
+ }
+ } else if (isHash(value)) {
+ prop = new PropertyMap(key);
+ for(var i in value) {
+ var v = value[i];
+ if (value != null) {
+ me(prop, i, v);
+ }
+ }
+ }
+
+ if (parent instanceof PropertyList) {
+ parent.add(prop);
+ } else {
+ parent.put(prop);
+ }
+ })(config, key, value);
+ }
+
+ return config;
+}
+
+/**
+ * Opposite of <code>asConfiguration</code>. Converts an RHQ's
configuration object
+ * into a javascript hash.
+ *
+ * @param configuration
+ */
+function asHash(configuration) {
+ ret = {}
+
+ iterator = configuration.getMap().values().iterator();
+ while(iterator.hasNext()) {
+ prop = iterator.next();
+
+ (function(parent, prop) {
+ function isArray(obj) {
+ return typeof(obj) == 'object' && (obj instanceof Array);
+ }
+
+ function isHash(obj) {
+ return typeof(obj) == 'object' && !(obj instanceof Array);
+ }
+
+ var me = arguments.callee;
+
+ var representation = null;
+
+ if (prop instanceof PropertySimple) {
+ representation = prop.stringValue;
+ } else if (prop instanceof PropertyList) {
+ representation = [];
+
+ for(var i = 0; i < prop.list.size(); ++i) {
+ var child = prop.list.get(i);
+ me(representation, child);
+ }
+ } else if (prop instanceof PropertyMap) {
+ representation = {};
+
+ var childIterator = prop.getMap().values().iterator();
+ while(childIterator.hasNext()) {
+ var child = childIterator.next();
+
+ me(representation, child);
+ }
+ }
+
+ if (isArray(parent)) {
+ parent.push(representation);
+ } else if (isHash(parent)) {
+ parent[prop.name] = representation;
+ }
+ })(ret, prop);
+ }
+ (function(parent) {
+
+ })(configuration);
+
+ return ret;
+}
+
+//------------------------------ bundles.js
+
+/**
+ * A simple function to create a new bundle version from a zip file containing
+ * the bundle.
+ *
+ * @param pathToBundleZipFile the path to the bundle on the local file system
+ *
+ * @return an instance of BundleVersion class describing what's been created on
+ * the RHQ server.
+ */
+function createBundleVersion(pathToBundleZipFile) {
+ var bytes = getFileBytes(pathToBundleZipFile)
+ return BundleManager.createBundleVersionViaByteArray(bytes)
+}
+
+/**
+ * This is a helper function that one can use to find out what base directories
+ * given resource type defines.
+ * <p>
+ * These base directories then can be used when specifying bundle destinations.
+ *
+ * @param resourceTypeId
+ * @returns a java.util.Set of ResourceTypeBundleConfiguration objects
+ */
+function getAllBaseDirectories(resourceTypeId) {
+ var crit = new ResourceTypeCriteria;
+ crit.addFilterId(resourceTypeId);
+ crit.fetchBundleConfiguration(true);
+
+ var types = ResourceTypeManager.findResourceTypesByCriteria(crit);
+
+ if (types.size() == 0) {
+ throw "Could not find a resource type with id " + resourceTypeId;
+ } else if (types.size() > 1) {
+ throw "More than one resource type found with id " + resourceTypeId + "!
How did that happen!";
+ }
+
+ var type = types.get(0);
+
+ return type.getResourceTypeBundleConfiguration().getBundleDestinationBaseDirectories();
+}
+
+/**
+ * Creates a new destination for given bundle. Once a destination exists,
+ * actual bundle versions can be deployed to it.
+ * <p>
+ * Note that this only differs from the
<code>BundleManager.createBundleDestination</code>
+ * method in the fact that one can provide bundle and resource group names instead of
their
+ * ids.
+ *
+ * @param destinationName the name of the destination to be created
+ * @param description the description for the destination
+ * @param bundleName the name of the bundle to create the destination for
+ * @param groupName name of a group of resources that the destination will handle
+ * @param baseDirName the name of the basedir definition that represents where inside the
+ * deployment of the individual resources the bundle will get
deployed
+ * @param deployDir the specific sub directory of the base dir where the bundles will get
deployed
+ *
+ * @return BundleDestination object
+ */
+function createBundleDestination(destinationName, description, bundleName, groupName,
baseDirName, deployDir) {
+ var groupCrit = new ResourceGroupCriteria;
+ groupCrit.addFilterName(groupName);
+ var groups = ResourceGroupManager.findResourceGroupsByCriteria(groupCrit);
+
+ if (groups.empty) {
+ throw "No group called '" + groupName + "' found.";
+ }
+
+ var group = groups.get(0);
+
+ var bundleCrit = new BundleCriteria;
+ bundleCrit.addFilterName(bundleName);
+ var bundles = BundleManager.findBundlesByCriteria(bundleCrit);
+
+ if (bundles.empty) {
+ throw "No bundle called '" + bundleName + "' found.";
+ }
+
+ var bundle = bundles.get(0);
+
+ return BundleManager.createBundleDestination(bundle.id, destinationName, description,
baseDirName, deployDir, group.id);
+}
+
+/**
+ * Tries to deploy given bundle version to provided destination using given
configuration.
+ * <p>
+ * This method blocks while waiting for the deployment to complete or fail.
+ *
+ * @param destination the bundle destination (or id thereof)
+ * @param bundleVersion the bundle version to deploy (or id thereof)
+ * @param deploymentConfiguration the deployment configuration. This can be an ordinary
+ * javascript object (hash) or an instance of RHQ's Configuration. If it is the
former,
+ * it is converted to a Configuration instance using the
<code>asConfiguration</code>
+ * function from <code>util.js</code>. Please consult the documentation of
that method
+ * to understand the limitations of that approach.
+ * @param description the deployment description
+ * @param isCleanDeployment if true, perform a wipe of the deploy directory prior to the
deployment; if false,
+ * perform as an upgrade to the existing deployment, if any
+ *
+ * @return the BundleDeployment instance describing the deployment
+ */
+function deployBundle(destination, bundleVersion, deploymentConfiguration, description,
isCleanDeployment) {
+ var destinationId = destination;
+ if (typeof(destination) == 'object') {
+ destinationId = destination.id;
+ }
+
+ var bundleVersionId = bundleVersion;
+ if (typeof(bundleVersion) == 'object') {
+ bundleVersionId = bundleVersion.id;
+ }
+
+ var deploymentConfig = deploymentConfiguration;
+ if (!(deploymentConfiguration instanceof Configuration)) {
+ deploymentConfig = asConfiguration(deploymentConfiguration);
+ }
+
+ var deployment = BundleManager.createBundleDeployment(bundleVersionId, destinationId,
description, deploymentConfig);
+
+ deployment = BundleManager.scheduleBundleDeployment(deployment.id, isCleanDeployment);
+
+ var crit = new BundleDeploymentCriteria;
+ crit.addFilterId(deployment.id);
+
+ while (deployment.status == BundleDeploymentStatus.PENDING || deployment.status ==
BundleDeploymentStatus.IN_PROGRESS) {
+ java.lang.Thread.currentThread().sleep(1000);
+ var dps = BundleManager.findBundleDeploymentsByCriteria(crit);
+ if (dps.empty) {
+ throw "The deployment disappeared while we were waiting for it to
complete.";
+ }
+
+ deployment = dps.get(0);
+ }
+
+ return deployment;
+}
+
+//------------------- script body
+
+deployBundle(destinationId, bundleVersionId, deploymentConfig, description,
isCleanDeployment);
+
diff --git a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
index d920ff9..9416b30 100644
--- a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
+++ b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
@@ -360,7 +360,18 @@
<fileset dir="${jboss.home}" includes="*.*" />
</delete>
- <!-- make sure our downloads directories exist -->
+ <!-- make sure the top level downloads directory exists -->
+ <mkdir dir="${jboss.deploy.dir}/${rhq.earName}/rhq-downloads" />
+
+ <!-- deploy all the static downloadable content -->
+ <echo>Deploying static downloadable content</echo>
+ <copy todir="${jboss.deploy.dir}/${rhq.earName}/rhq-downloads"
+ verbose="true"
+ failonerror="false">
+ <fileset dir="${basedir}/src/main/downloadable-resources" />
+ </copy>
+
+ <!-- make sure our downloads directories exist -->
<mkdir
dir="${jboss.deploy.dir}/${rhq.earName}/rhq-downloads/rhq-agent" />
<mkdir
dir="${jboss.deploy.dir}/${rhq.earName}/rhq-downloads/rhq-client" />
<mkdir
dir="${jboss.deploy.dir}/${rhq.earName}/rhq-downloads/rhq-plugins" />
commit c7aa8dbb5e387dadc03fb6c7d615857e82d645f4
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Nov 30 16:37:49 2011 -0500
Adding itests module to build
I discovered earlier today that the itests module is not getting built
in integration builds. It is declared in the list of modules of the
server pom, but the server module itself is not listed in
enterprise/pom.xml.
diff --git a/modules/enterprise/pom.xml b/modules/enterprise/pom.xml
index 22a7c7e..e119090 100644
--- a/modules/enterprise/pom.xml
+++ b/modules/enterprise/pom.xml
@@ -46,6 +46,7 @@
<module>server/ear</module>
<module>binding</module>
<module>server/client-api</module>
+ <module>server/itests</module>
</modules>
</profile>
commit ca50dc7aef3203af9d88227e216d96febbfbe57d
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 15:22:50 2011 -0500
Fix some minor issues in the server itests.
diff --git
a/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/server/drift/DriftTemplateManagerBeanTest.java
b/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/server/drift/DriftTemplateManagerBeanTest.java
index 08c5b4d..895cf9b 100644
---
a/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/server/drift/DriftTemplateManagerBeanTest.java
+++
b/modules/enterprise/server/itests/src/test/java/org/rhq/enterprise/server/drift/DriftTemplateManagerBeanTest.java
@@ -59,7 +59,6 @@ import org.rhq.core.domain.drift.JPADrift;
import org.rhq.core.domain.drift.JPADriftChangeSet;
import org.rhq.core.domain.drift.JPADriftFile;
import org.rhq.core.domain.drift.JPADriftSet;
-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.server.safeinvoker.HibernateDetachUtility;
@@ -68,12 +67,13 @@ import org.rhq.test.TransactionCallback;
public class DriftTemplateManagerBeanTest extends DriftServerTest {
+ private static final String TEST_CREATE_TEMPLATE =
"test-createTemplateForNegativeUpdateTests";
+ private static final String TEST_PIN_TEMPLATE = "test-pinTemplate";
+
private DriftTemplateManagerLocal templateMgr;
private DriftManagerLocal driftMgr;
- private List<Resource> resources = new LinkedList<Resource>();
-
private JPADrift drift1;
private JPADrift drift2;
private JPADriftFile driftFile1;
@@ -117,7 +117,7 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
public void createNewTemplate() {
final DriftDefinition definition = new DriftDefinition(new Configuration());
- definition.setName("test::createNewTemplate");
+ definition.setName("test-createNewTemplate");
definition.setEnabled(true);
definition.setDriftHandlingMode(normal);
definition.setInterval(2400L);
@@ -148,7 +148,7 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
public void createTemplateForNegativeUpdateTests() {
DriftDefinition definition = new DriftDefinition(new Configuration());
- definition.setName("test::createTemplateForNegativeUpdateTests");
+ definition.setName(TEST_CREATE_TEMPLATE);
definition.setEnabled(true);
definition.setDriftHandlingMode(normal);
definition.setInterval(2400L);
@@ -159,48 +159,40 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
assertNotNull("Failed to load template",
loadTemplate(definition.getName()));
}
- @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
- expectedExceptions = EJBException.class,
- expectedExceptionsMessageRegExp = ".*base directory.*cannot be
modified")
+ @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
expectedExceptions = EJBException.class, expectedExceptionsMessageRegExp = ".*base
directory.*cannot be modified")
@InitDB(false)
public void doNotAllowBaseDirToBeUpdated() {
- DriftDefinitionTemplate template =
loadTemplate("test::createTemplateForNegativeUpdateTests");
+ DriftDefinitionTemplate template = loadTemplate(TEST_CREATE_TEMPLATE);
DriftDefinition definition = template.getTemplateDefinition();
definition.setBasedir(new DriftDefinition.BaseDirectory(fileSystem,
"/foo/bar/TEST"));
templateMgr.updateTemplate(getOverlord(), template);
}
- @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
- expectedExceptions = EJBException.class,
- expectedExceptionsMessageRegExp = ".*filters.*cannot be modified")
+ @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
expectedExceptions = EJBException.class, expectedExceptionsMessageRegExp =
".*filters.*cannot be modified")
@InitDB(false)
public void doNotAllowFiltersToBeUpdated() {
- DriftDefinitionTemplate template =
loadTemplate("test::createTemplateForNegativeUpdateTests");
+ DriftDefinitionTemplate template = loadTemplate(TEST_CREATE_TEMPLATE);
DriftDefinition definition = template.getTemplateDefinition();
definition.addExclude(new Filter("/foo/bar/TEST/conf",
"*.xml"));
templateMgr.updateTemplate(getOverlord(), template);
}
- @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
- expectedExceptions = EJBException.class,
- expectedExceptionsMessageRegExp = ".*name.*cannot be modified")
+ @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
expectedExceptions = EJBException.class, expectedExceptionsMessageRegExp =
".*name.*cannot be modified")
@InitDB(false)
public void doNotAllowTemplateNameToBeUpdated() {
- DriftDefinitionTemplate template =
loadTemplate("test::createTemplateForNegativeUpdateTests");
+ DriftDefinitionTemplate template = loadTemplate(TEST_CREATE_TEMPLATE);
template.setName("A new name");
templateMgr.updateTemplate(getOverlord(), template);
}
- @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
- expectedExceptions = EJBException.class,
- expectedExceptionsMessageRegExp = ".*template name must be unique.*")
+ @Test(dependsOnMethods = "createTemplateForNegativeUpdateTests",
expectedExceptions = EJBException.class, expectedExceptionsMessageRegExp =
".*template name must be unique.*")
@InitDB(false)
public void doNotAllowDuplicateTemplateNames() {
DriftDefinition definition = new DriftDefinition(new Configuration());
- definition.setName("test::createTemplateForNegativeUpdateTests");
+ definition.setName(TEST_CREATE_TEMPLATE);
definition.setEnabled(true);
definition.setDriftHandlingMode(normal);
definition.setInterval(2400L);
@@ -212,7 +204,7 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
public void createAndUpdateTemplate() {
// create the template
DriftDefinition definition = new DriftDefinition(new Configuration());
- definition.setName("test::updateTemplate");
+ definition.setName("test-updateTemplate");
definition.setDescription("update template test");
definition.setEnabled(true);
definition.setDriftHandlingMode(normal);
@@ -252,12 +244,11 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
String msg = "Failed to propagate update to attached definition " +
toString(updatedDef) + " - ";
DriftDefinition updatedTemplateDef =
updatedTemplate.getTemplateDefinition();
- assertEquals(msg + "enabled property not updated",
updatedTemplateDef.isEnabled(),
- updatedDef.isEnabled());
- assertEquals(msg + "driftHandlingMode property not updated",
- updatedTemplateDef.getDriftHandlingMode(),
updatedDef.getDriftHandlingMode());
- assertEquals(msg + "interval property not updated",
updatedTemplateDef.getInterval(),
- updatedDef.getInterval());
+ assertEquals(msg + "enabled property not updated",
updatedTemplateDef.isEnabled(), updatedDef.isEnabled());
+ assertEquals(msg + "driftHandlingMode property not updated",
updatedTemplateDef.getDriftHandlingMode(),
+ updatedDef.getDriftHandlingMode());
+ assertEquals(msg + "interval property not updated",
updatedTemplateDef.getInterval(), updatedDef
+ .getInterval());
}
// verify that the detached definitions have not been updated.
@@ -266,25 +257,23 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
String msg = "Detached definition " + toString(def) + " should
not get updated - ";
assertEquals(msg + "enabled property was modified",
def.isEnabled(), defAfterUpdate.isEnabled());
- assertEquals(msg + "driftHandlingMode property was modified",
def.getDriftHandlingMode(),
- defAfterUpdate.getDriftHandlingMode());
+ assertEquals(msg + "driftHandlingMode property was modified",
def.getDriftHandlingMode(), defAfterUpdate
+ .getDriftHandlingMode());
assertEquals(msg + "interval property was modified",
def.getInterval(), defAfterUpdate.getInterval());
}
}
-
- @SuppressWarnings("unchecked")
public void pinTemplate() throws Exception {
// First create the template
final DriftDefinition templateDef = new DriftDefinition(new Configuration());
- templateDef.setName("test::pinTemplate");
+ templateDef.setName(TEST_PIN_TEMPLATE);
templateDef.setEnabled(true);
templateDef.setDriftHandlingMode(normal);
templateDef.setInterval(2400L);
templateDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem,
"/foo/bar/test"));
- final DriftDefinitionTemplate template =
templateMgr.createTemplate(getOverlord(),
- resourceType.getId(), true, templateDef);
+ final DriftDefinitionTemplate template =
templateMgr.createTemplate(getOverlord(), resourceType.getId(), true,
+ templateDef);
// next create some resource level definitions
final DriftDefinition attachedDef1 = createDefinition(template,
"attachedDef1", true);
@@ -338,10 +327,11 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
assertTrue("Template should be marked pinned",
updatedTemplate.isPinned());
}
+ @SuppressWarnings("unchecked")
@Test(dependsOnMethods = "pinTemplate")
@InitDB(false)
public void persistChangeSetWhenTemplateGetsPinned() throws Exception {
- DriftDefinitionTemplate template = loadTemplate("test::pinTemplate");
+ DriftDefinitionTemplate template = loadTemplate(TEST_PIN_TEMPLATE);
GenericDriftChangeSetCriteria criteria = new GenericDriftChangeSetCriteria();
criteria.addFilterId(template.getChangeSetId());
@@ -352,9 +342,8 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
assertEquals("Expected to find change set for pinned template", 1,
changeSets.size());
JPADriftChangeSet expectedChangeSet = new JPADriftChangeSet(resource, 1,
COVERAGE, null);
- List<? extends Drift> expectedDrifts = asList(
- new JPADrift(expectedChangeSet, "drift.1", FILE_ADDED, null,
driftFile1),
- new JPADrift(expectedChangeSet, drift2.getPath(), FILE_ADDED, null,
driftFile2));
+ List<? extends Drift> expectedDrifts = asList(new
JPADrift(expectedChangeSet, "drift.1", FILE_ADDED, null,
+ driftFile1), new JPADrift(expectedChangeSet, drift2.getPath(), FILE_ADDED,
null, driftFile2));
DriftChangeSet<?> actualChangeSet = changeSets.get(0);
List<? extends Drift> actualDrifts = new
ArrayList(actualChangeSet.getDrifts());
@@ -375,7 +364,7 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
@Test(dependsOnMethods = "pinTemplate")
@InitDB(false)
public void updateAttachedDefinitionsWhenTemplateGetsPinned() throws Exception {
- DriftDefinitionTemplate template = loadTemplate("test::pinTemplate");
+ DriftDefinitionTemplate template = loadTemplate(TEST_PIN_TEMPLATE);
// get the attached definitions
List<DriftDefinition> attachedDefs = new
LinkedList<DriftDefinition>();
@@ -392,7 +381,7 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
@Test(dependsOnMethods = "pinTemplate")
@InitDB(false)
public void doNotUpdateDetachedDefinitionsWhenTemplateGetsPinned() throws Exception
{
- DriftDefinitionTemplate template = loadTemplate("test::pinTemplate");
+ DriftDefinitionTemplate template = loadTemplate(TEST_PIN_TEMPLATE);
// get the detached definitions
List<DriftDefinition> detachedDefs = new
LinkedList<DriftDefinition>();
@@ -409,14 +398,14 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
public void deleteTemplate() throws Exception {
// first create the template
final DriftDefinition templateDef = new DriftDefinition(new Configuration());
- templateDef.setName("test::pinTemplate");
+ templateDef.setName(TEST_PIN_TEMPLATE);
templateDef.setEnabled(true);
templateDef.setDriftHandlingMode(normal);
templateDef.setInterval(2400L);
templateDef.setBasedir(new DriftDefinition.BaseDirectory(fileSystem,
"/foo/bar/test"));
- final DriftDefinitionTemplate template =
templateMgr.createTemplate(getOverlord(),
- resourceType.getId(), true, templateDef);
+ final DriftDefinitionTemplate template =
templateMgr.createTemplate(getOverlord(), resourceType.getId(), true,
+ templateDef);
// next create some resource level definitions
final DriftDefinition attachedDef1 = createDefinition(template,
"attachedDef1", true);
@@ -472,22 +461,23 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
// verify that attached definitions along with their change sets have
// been deleted
assertNull("Change sets belonging to attached definitions should be
deleted", loadChangeSet(changeSet0.getId()));
- assertNull("Attached definition " + toString(attachedDef1) + "
should be deleted",
- loadDefinition(attachedDef1.getId()));
- assertNull("Attached definition " + toString(attachedDef2) + "
should be deleted",
- loadDefinition(attachedDef2.getId()));
+ assertNull("Attached definition " + toString(attachedDef1) + "
should be deleted", loadDefinition(attachedDef1
+ .getId()));
+ assertNull("Attached definition " + toString(attachedDef2) + "
should be deleted", loadDefinition(attachedDef2
+ .getId()));
// verify that detached definitions along with their change sets have not been
deleted
- assertNotNull("Change sets belonging to detached definitions should not be
deleted",
- loadChangeSet(changeSet1.getId()));
+ assertNotNull("Change sets belonging to detached definitions should not be
deleted", loadChangeSet(changeSet1
+ .getId()));
assertDetachedDefinitionNotDeleted(detachedDef1.getId());
assertDetachedDefinitionNotDeleted(detachedDef2.getId());
// verify that the template itself has been deleted
- assertNull("The template " + toString(template) + " should have
been deleted",
- loadTemplate(template.getName(), false));
+ assertNull("The template " + toString(template) + " should have
been deleted", loadTemplate(template.getName(),
+ false));
}
+ @SuppressWarnings("unchecked")
private void assertDefinitionIsPinned(DriftDefinition definition) throws Exception {
// verify that the definition is marked as pinned
assertTrue("Expected " + toString(definition) + " to be
pinned", definition.isPinned());
@@ -503,9 +493,8 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
assertEquals("Expected to find one change set", 1, changeSets.size());
JPADriftChangeSet expectedChangeSet = new JPADriftChangeSet(resource, 1,
COVERAGE, null);
- List<? extends Drift> expectedDrifts = asList(
- new JPADrift(expectedChangeSet, drift1.getPath(), FILE_ADDED, null,
driftFile1),
- new JPADrift(expectedChangeSet, drift2.getPath(), FILE_ADDED, null,
driftFile2));
+ List<? extends Drift> expectedDrifts = asList(new
JPADrift(expectedChangeSet, drift1.getPath(), FILE_ADDED,
+ null, driftFile1), new JPADrift(expectedChangeSet, drift2.getPath(),
FILE_ADDED, null, driftFile2));
DriftChangeSet<?> actualChangeSet = changeSets.get(0);
List<? extends Drift> actualDrifts = new
ArrayList(actualChangeSet.getDrifts());
@@ -519,8 +508,8 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
criteria.addFilterStartVersion(1);
criteria.addFilterDriftDefinitionId(definition.getId());
- assertEquals("There should not be any drift change sets", 0,
- driftMgr.findDriftChangeSetsByCriteria(getOverlord(), criteria).size());
+ assertEquals("There should not be any drift change sets", 0,
driftMgr.findDriftChangeSetsByCriteria(
+ getOverlord(), criteria).size());
}
private void assertDefinitionIsNotPinned(DriftDefinition definition) throws Exception
{
@@ -534,15 +523,14 @@ public class DriftTemplateManagerBeanTest extends DriftServerTest {
PageList<? extends DriftChangeSet<?>> changeSets =
driftMgr.findDriftChangeSetsByCriteria(getOverlord(),
criteria);
- assertEquals("Did not expect to find any change sets for " +
toString(definition) + ". Note that this " +
- "assertion method assumes that the definition you are testing is not
supposed to have any change sets.",
+ assertEquals("Did not expect to find any change sets for " +
toString(definition) + ". Note that this "
+ + "assertion method assumes that the definition you are testing is not
supposed to have any change sets.",
0, changeSets.size());
}
private void assertDriftTemplateEquals(String msg, DriftDefinitionTemplate expected,
DriftDefinitionTemplate actual) {
- AssertUtils
- .assertPropertiesMatch(msg + ": basic drift definition template
properties do not match", expected, actual,
- "id", "resourceType", "ctime",
"templateDefinition");
+ AssertUtils.assertPropertiesMatch(msg + ": basic drift definition template
properties do not match", expected,
+ actual, "id", "resourceType", "ctime",
"templateDefinition");
assertDriftDefEquals(msg + ": template definitions do not match",
expected.getTemplateDefinition(), actual
.getTemplateDefinition());
}
commit e705478deae5e5c6e11cb0ca6a54af228d207102
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 14:57:54 2011 -0500
Fix dep group for javassist
diff --git a/modules/enterprise/server/itests/pom.xml
b/modules/enterprise/server/itests/pom.xml
index d9d8677..f269e77 100644
--- a/modules/enterprise/server/itests/pom.xml
+++ b/modules/enterprise/server/itests/pom.xml
@@ -104,7 +104,7 @@
<!-- NOTE: The remaining test deps correspond to the classes contained in
hibernate-all.jar and thirdparty-all.jar. -->
<dependency>
- <groupId>jboss</groupId>
+ <groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<scope>test</scope>
</dependency>
commit b31e3a66a1e75dcad0070b5b78bbd3f8e9005533
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 14:40:55 2011 -0500
[Bug 758724 - Transaction timing can prevent drift file content from being persisted]
Resolves a timing issue where it was possible for the agent to submit
DriftFile content before the DriftFile entity was committed to the
database, thus generating exceptions due to the missing entity, and
a failure to store the required content.
diff --git a/modules/common/drift/src/main/java/org/rhq/common/drift/Headers.java
b/modules/common/drift/src/main/java/org/rhq/common/drift/Headers.java
index fb13e94..a567b75 100644
--- a/modules/common/drift/src/main/java/org/rhq/common/drift/Headers.java
+++ b/modules/common/drift/src/main/java/org/rhq/common/drift/Headers.java
@@ -128,4 +128,10 @@ public class Headers implements Serializable {
this.version = version;
}
+ @Override
+ public String toString() {
+ return "Headers [driftDefinitionName=" + driftDefinitionName + ",
resourceId=" + resourceId + ", version="
+ + version + "]";
+ }
+
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
index 6633c22..c985c1e 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
@@ -19,6 +19,7 @@
*/
package org.rhq.enterprise.server.drift;
+import static javax.ejb.TransactionAttributeType.NOT_SUPPORTED;
import static javax.ejb.TransactionAttributeType.REQUIRES_NEW;
import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
import static org.rhq.core.domain.drift.DriftFileStatus.LOADED;
@@ -300,10 +301,38 @@ public class JPADriftServerBean implements JPADriftServerLocal {
df.setStatus(LOADED);
}
+ // This facade does not start, or participate in, a transaction so that it can
execute its work
+ // in two new transactions. The first transaction ensures all new entities are
committed to the
+ // database. The second transaction can then safely ackknowledge that the changeset
is persisted
+ // and request drift file content, if necessary.
@Override
- @TransactionAttribute(REQUIRES_NEW)
+ @TransactionAttribute(NOT_SUPPORTED)
public DriftChangeSetSummary storeChangeSet(Subject subject, final int resourceId,
final File changeSetZip)
throws Exception {
+
+ // a List to be populated by storeChangeSetInNewTransaction for use in
ackChangeSetInNewTransaction
+ List<JPADriftFile> driftFilesToRequest = new
ArrayList<JPADriftFile>();
+ // a 1 element array so storeChangeSetInNewTransaction can return the Headers for
use in ackChangeSetInNewTransaction
+ Headers[] headers = new Headers[1];
+
+ DriftChangeSetSummary result =
JPADriftServer.storeChangeSetInNewTransaction(subject, resourceId, changeSetZip,
+ driftFilesToRequest, headers);
+
+ if (null == result) {
+ return result;
+ }
+
+ JPADriftServer.ackChangeSetInNewTransaction(subject, resourceId, headers[0],
driftFilesToRequest);
+
+ return result;
+ }
+
+ @Override
+ @TransactionAttribute(REQUIRES_NEW)
+ public DriftChangeSetSummary storeChangeSetInNewTransaction(Subject subject, final
int resourceId,
+ final File changeSetZip, final List<JPADriftFile> driftFilesToRequest,
final Headers[] headers)
+ throws Exception {
+
final Resource resource = getResource(resourceId);
final DriftChangeSetSummary summary = new DriftChangeSetSummary();
final boolean storeBinaryContent = isBinaryContentStorageEnabled();
@@ -313,105 +342,82 @@ public class JPADriftServerBean implements JPADriftServerLocal {
@Override
public boolean visit(ZipEntry zipEntry, ZipInputStream stream) throws
Exception {
- List<JPADriftFile> emptyDriftFiles = new
ArrayList<JPADriftFile>();
+
JPADriftChangeSet driftChangeSet = null;
- try {
- ChangeSetReader reader = new ChangeSetReaderImpl(new
BufferedReader(new InputStreamReader(
- stream)), false);
+ ChangeSetReader reader = new ChangeSetReaderImpl(new
BufferedReader(new InputStreamReader(stream)),
+ false);
- // store the new change set info (not the actual blob)
- DriftDefinition driftDef = findDriftDefinition(resource,
reader.getHeaders());
- if (driftDef == null) {
- log.error("Unable to locate DriftDefinition for Resource
[" + resource
- + "]. Change set cannot be saved.");
- return false;
- }
- // TODO: Commenting out the following line for now. We want to
set the
- // version to the value specified in the headers, but we may want
to also
- // validate it against the latest version we have in the database
so that
- // we can make sure that the agent is in sync with the server.
- //
- //int version = getChangeSetVersion(resource, config);
- int version = reader.getHeaders().getVersion();
-
- DriftChangeSetCategory category = reader.getHeaders().getType();
- driftChangeSet = new JPADriftChangeSet(resource, version,
category, driftDef);
- entityManager.persist(driftChangeSet);
-
- summary.setCategory(category);
- summary.setResourceId(resourceId);
-
summary.setDriftDefinitionName(reader.getHeaders().getDriftDefinitionName());
- summary.setDriftHandlingMode(driftDef.getDriftHandlingMode());
- summary.setCreatedTime(driftChangeSet.getCtime());
-
- if (version > 0) {
- for (FileEntry entry : reader) {
- boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
- JPADriftFile oldDriftFile =
getDriftFile(entry.getOldSHA(), emptyDriftFiles, addToList);
- JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
-
- // TODO Figure out an efficient way to save coverage
change sets.
- // The initial/coverage change set could contain hundreds
or even thousands
- // of entries. We probably want to consider doing some
kind of batch insert
- //
- // jsanda
-
- // use a path with only forward slashing to ensure
consistent paths across reports
- String path = FileUtil.useForwardSlash(entry.getFile());
- JPADrift drift = new JPADrift(driftChangeSet, path,
entry.getType(), oldDriftFile,
- newDriftFile);
- entityManager.persist(drift);
-
- // we are taking advantage of the fact that we know the
summary is only used by the server
- // if the change set is a DRIFT report. If its a coverage
report, it is not used (we do
- // not alert on coverage reports) - so don't waste
memory by collecting all the paths
- // when we know they aren't going to be used anyway.
- if (category == DriftChangeSetCategory.DRIFT) {
- summary.addDriftPathname(path);
- }
- }
- } else {
- summary.setInitialChangeSet(true);
- JPADriftSet driftSet = new JPADriftSet();
- for (FileEntry entry : reader) {
- boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
- JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
- String path = FileUtil.useForwardSlash(entry.getFile());
- // A Drift always has a changeSet. Note that in this code
section the changeset is
- // always going to be set to a DriftDefinition's
changeSet. But that is not always the
- // case, it could also be set to a
DriftDefinitionTemplate's changeSet.
- driftSet.addDrift(new JPADrift(driftChangeSet, path,
entry.getType(), null,
- newDriftFile));
+ // store the new change set info (not the actual blob)
+ DriftDefinition driftDef = findDriftDefinition(resource,
reader.getHeaders());
+ if (driftDef == null) {
+ log.error("Unable to locate DriftDefinition for Resource
[" + resource
+ + "]. Change set cannot be saved.");
+ return false;
+ }
+ // TODO: Commenting out the following line for now. We want to set
the
+ // version to the value specified in the headers, but we may want to
also
+ // validate it against the latest version we have in the database so
that
+ // we can make sure that the agent is in sync with the server.
+ //
+ //int version = getChangeSetVersion(resource, config);
+ int version = reader.getHeaders().getVersion();
+
+ DriftChangeSetCategory category = reader.getHeaders().getType();
+ driftChangeSet = new JPADriftChangeSet(resource, version, category,
driftDef);
+ entityManager.persist(driftChangeSet);
+
+ summary.setCategory(category);
+ summary.setResourceId(resourceId);
+
summary.setDriftDefinitionName(reader.getHeaders().getDriftDefinitionName());
+ summary.setDriftHandlingMode(driftDef.getDriftHandlingMode());
+ summary.setCreatedTime(driftChangeSet.getCtime());
+
+ if (version > 0) {
+ for (FileEntry entry : reader) {
+ boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
+ JPADriftFile oldDriftFile = getDriftFile(entry.getOldSHA(),
driftFilesToRequest, addToList);
+ JPADriftFile newDriftFile = getDriftFile(entry.getNewSHA(),
driftFilesToRequest, addToList);
+
+ // TODO Figure out an efficient way to save coverage change
sets.
+ // The initial/coverage change set could contain hundreds or
even thousands
+ // of entries. We probably want to consider doing some kind
of batch insert
+ //
+ // jsanda
+
+ // use a path with only forward slashing to ensure consistent
paths across reports
+ String path = FileUtil.useForwardSlash(entry.getFile());
+ JPADrift drift = new JPADrift(driftChangeSet, path,
entry.getType(), oldDriftFile,
+ newDriftFile);
+ entityManager.persist(drift);
+
+ // we are taking advantage of the fact that we know the
summary is only used by the server
+ // if the change set is a DRIFT report. If its a coverage
report, it is not used (we do
+ // not alert on coverage reports) - so don't waste memory
by collecting all the paths
+ // when we know they aren't going to be used anyway.
+ if (category == DriftChangeSetCategory.DRIFT) {
+ summary.addDriftPathname(path);
}
- entityManager.persist(driftSet);
- driftChangeSet.setInitialDriftSet(driftSet);
- entityManager.merge(driftChangeSet);
}
-
- AgentClient agentClient =
agentManager.getAgentClient(subjectManager.getOverlord(), resourceId);
- DriftAgentService service = agentClient.getDriftAgentService();
-
- service.ackChangeSet(resourceId,
reader.getHeaders().getDriftDefinitionName());
-
- // send a message to the agent requesting the empty JPADriftFile
content
- if (!emptyDriftFiles.isEmpty()) {
- try {
- if (service.requestDriftFiles(resourceId,
reader.getHeaders(), emptyDriftFiles)) {
- for (DriftFile driftFile : emptyDriftFiles) {
- driftFile.setStatus(DriftFileStatus.REQUESTED);
- }
- }
- } catch (Exception e) {
- log.warn(" Unable to inform agent of drift file
request [" + emptyDriftFiles + "]", e);
- }
+ } else {
+ summary.setInitialChangeSet(true);
+ JPADriftSet driftSet = new JPADriftSet();
+ for (FileEntry entry : reader) {
+ boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
+ JPADriftFile newDriftFile = getDriftFile(entry.getNewSHA(),
driftFilesToRequest, addToList);
+ String path = FileUtil.useForwardSlash(entry.getFile());
+ // A Drift always has a changeSet. Note that in this code
section the changeset is
+ // always going to be set to a DriftDefinition's
changeSet. But that is not always the
+ // case, it could also be set to a
DriftDefinitionTemplate's changeSet.
+ driftSet.addDrift(new JPADrift(driftChangeSet, path,
entry.getType(), null, newDriftFile));
}
- } catch (Exception e) {
- String msg = "Failed to store drift changeset [" +
driftChangeSet + "]";
- log.error(msg, e);
- return false;
+ entityManager.persist(driftSet);
+ driftChangeSet.setInitialDriftSet(driftSet);
+ entityManager.merge(driftChangeSet);
}
+ headers[0] = reader.getHeaders();
+
return true;
}
});
@@ -423,16 +429,43 @@ public class JPADriftServerBean implements JPADriftServerLocal {
if (null != resource) {
msg += resource;
} else {
- msg += ("resourceId " + resourceId);
+ msg += ("resourceId [" + resourceId + "]");
}
log.error(msg, e);
return null;
+
} finally {
// delete the changeSetFile?
}
}
+ @Override
+ @TransactionAttribute(REQUIRES_NEW)
+ public void ackChangeSetInNewTransaction(Subject subject, final int resourceId, final
Headers headers,
+ final List<JPADriftFile> driftFilesToRequest) throws Exception {
+
+ try {
+ AgentClient agentClient =
agentManager.getAgentClient(subjectManager.getOverlord(), resourceId);
+ DriftAgentService service = agentClient.getDriftAgentService();
+
+ service.ackChangeSet(resourceId, headers.getDriftDefinitionName());
+
+ // send a message to the agent requesting the necessary JPADriftFile content.
Note that the
+ // driftFile status has been set to REQUESTED outside of this call.
+ if (!driftFilesToRequest.isEmpty()) {
+ try {
+ service.requestDriftFiles(resourceId, headers, driftFilesToRequest);
+
+ } catch (Exception e) {
+ log.warn("Unable to inform agent of drift file request ["
+ driftFilesToRequest + "]", e);
+ }
+ }
+ } catch (Exception e) {
+ log.warn("Unable to acknowledge changeSet storage with agent for "
+ headers, e);
+ }
+ }
+
private boolean isBinaryContentStorageEnabled() {
String binaryContent =
System.getProperty("rhq.server.drift.store-binary-content", "false");
return binaryContent.equals("true");
@@ -446,9 +479,15 @@ public class JPADriftServerBean implements JPADriftServerLocal {
}
result = entityManager.find(JPADriftFile.class, sha256);
- // if the JPADriftFile is not yet in the db, then it needs to be fetched from the
agent
+ // if the JPADriftFile is not yet in the db then persist it, and mark it
requested if content is to be fetched
+ // note - by immediately setting the initial status to REQUESTED we avoid a
future update and a
+ // potential deadlock scenario where the REQUESTED and LOADED status updates can
happen simultaneously
if (null == result) {
- result = persistDriftFile(new JPADriftFile(sha256));
+ JPADriftFile driftFile = new JPADriftFile(sha256);
+ if (addToList) {
+ driftFile.setStatus(DriftFileStatus.REQUESTED);
+ }
+ result = persistDriftFile(driftFile);
if (addToList) {
emptyDriftFiles.add(result);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerLocal.java
index 0cd1e78..940a54b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerLocal.java
@@ -21,9 +21,11 @@ package org.rhq.enterprise.server.drift;
import java.io.File;
import java.io.InputStream;
+import java.util.List;
import javax.ejb.Local;
+import org.rhq.common.drift.Headers;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.criteria.DriftChangeSetCriteria;
import org.rhq.core.domain.criteria.DriftCriteria;
@@ -103,6 +105,18 @@ public interface JPADriftServerLocal {
DriftChangeSetSummary storeChangeSet(Subject subject, int resourceId, File
changeSetZip) throws Exception;
/**
+ * For transactioning purposes only, part of storeChangeSet impl. Not to be exposed
outside of local interface.
+ */
+ DriftChangeSetSummary storeChangeSetInNewTransaction(Subject subject, int resourceId,
File changeSetZip,
+ List<JPADriftFile> driftFilesToRequest, Headers[] headers) throws
Exception;
+
+ /**
+ * For transactioning purposes only, part of storeChangeSet impl. Not to be exposed
outside of local interface.
+ */
+ void ackChangeSetInNewTransaction(Subject subject, int resourceId, Headers headers,
+ List<JPADriftFile> driftFilesToRequest) throws Exception;
+
+ /**
* This method stores the provided drift files. The files should correspond to
requested drift files.
* The unzipped files will have their sha256 generated. Those not corresponding to
needed content will
* be logged and ignored.
commit 663c3c65527a5aacbcafead08614e4b416d52fa6
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 10:43:54 2011 -0500
[Bug 758565 - Add drift templates to JBAS WAR/EAR resources]
After reverting the changes to support EAR/WAR/Embedded WAR
types for AS-4 and AS-5 plugins, this now adds back drift
templates for EAR and WAR types. The Embedded WAR support will
be added back in later, when the repo is open for the plugin
code changes required to offer that support.
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 32aca99..2729d31 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
@@ -2313,6 +2313,15 @@
&deploymentContentConfigProps;
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
+ <basedir>
+ <value-context>measurementTrait</value-context>
+ <value-name>custom.path</value-name>
+ </basedir>
+ </drift-definition>
+
</service>
<service name="Web Application (WAR)"
@@ -2364,6 +2373,15 @@
&deploymentContentConfigProps;
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>measurementTrait</value-context>
+ <value-name>custom.path</value-name>
+ </basedir>
+ </drift-definition>
+
<service name="Web Application Context"
class="WebApplicationContextComponent"
discovery="WebApplicationContextDiscoveryComponent"
diff --git a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
index db4b8e5..d4d0874 100644
--- a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
@@ -259,8 +259,9 @@
</configuration>
</content>
- <drift-definition name="Template-Base Files"
- description="Monitor base application server files for
drift. It defines monitoring for some standard sub-directories of the HOME directory.
Note, it is not recommeded to monitor all files for an application server. There are many
files, and many temp files.">
+ <drift-definition
+ name="Template-Base Files"
+ description="Monitor base application server files for drift. It defines
monitoring for some standard sub-directories of the HOME directory. Note, it is not
recommeded to monitor all files for an application server. There are many files, and many
temp files.">
<basedir>
<value-context>pluginConfiguration</value-context>
<value-name>jbossHomeDir</value-name>
@@ -2267,6 +2268,15 @@
</configuration>
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
+ <basedir>
+ <value-context>pluginConfiguration</value-context>
+ <value-name>filename</value-name>
+ </basedir>
+ </drift-definition>
+
<service name="Embedded Web Application (WAR)"
class="org.rhq.plugins.jbossas.WarComponent"
discovery="org.rhq.plugins.jbossas.EmbeddedWarDiscoveryComponent"
@@ -2460,55 +2470,55 @@
dataType="calltime" defaultOn="false"
units="milliseconds" destinationType="URL"
description="the minimum, maximum, and average response times for
HTTP requests serviced by this webapp"/>
- <metric property="Servlet.MinResponseTime"
displayName="Min. Servlet Resp. Time"
+ <metric property="Servlet.MinResponseTime" displayName="Min.
Servlet Resp. Time"
units="milliseconds"
description="Minimum response time of a servlet"/>
- <metric property="Servlet.AvgResponseTime"
displayName="Avg. Servlet Resp. Time"
+ <metric property="Servlet.AvgResponseTime" displayName="Avg.
Servlet Resp. Time"
units="milliseconds"
description="Average response time of a servlet"/>
- <metric property="Servlet.MaxResponseTime"
displayName="Max. Servlet Resp. Time"
+ <metric property="Servlet.MaxResponseTime" displayName="Max.
Servlet Resp. Time"
units="milliseconds"
description="Maximum response time of a servlet"/>
- <metric property="Servlet.TotalTime" displayName="Total
processing time"
+ <metric property="Servlet.TotalTime" displayName="Total
processing time"
units="milliseconds"
description="Total processing time of the webapp"
measurementType="trendsup"/>
- <metric property="Servlet.NumRequests"
displayName="Requests served"
+ <metric property="Servlet.NumRequests" displayName="Requests
served"
units="none" description="Number of requests served by
servlets"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Servlet.NumErrors" displayName="Errors
while processing"
+ <metric property="Servlet.NumErrors" displayName="Errors while
processing"
units="none" description="Number of errors while
processing"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
+ <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
units="none" description="Number active sessions for
the webapp right now" />
- <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
+ <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
units="none" description="Maximum number of active
sessions for the webapp" />
- <metric property="Session.sessionCounter"
displayName="Sessions created"
+ <metric property="Session.sessionCounter"
displayName="Sessions created"
units="none" description="Number of sessions created
for the webapp"
measurementType="trendsup" />
- <metric property="Session.expiredSessions"
displayName="Expired Sessions"
+ <metric property="Session.expiredSessions"
displayName="Expired Sessions"
units="none" description="Number of expired sessions
for the webapp"
measurementType="trendsup" />
- <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
+ <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
units="none" description="Number of sessions rejected
for the webapp"
measurementType="trendsup" />
- <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
+ <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
units="seconds" description="Average alive time of a
Sessions" />
- <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
+ <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
units="seconds" description="Maximum alive time of a
Sessions" />
- <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
+ <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
description="Virtual hosts this app runs on"/>
<content name="file" displayName="WAR File"
category="deployable" isCreationType="true">
@@ -2524,6 +2534,15 @@
</configuration>
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>pluginConfiguration</value-context>
+ <value-name>filename</value-name>
+ </basedir>
+ </drift-definition>
+
</service>
<service name="EJB3 Entity Tree Cache"
commit 42c78796a8b115058f4f87f2d9f61f1cdec11428
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 10:38:16 2011 -0500
Revert "Fix issue in AS-4 plugin such that for embedded WAR resources the"
This reverts commit 8c6dcd3817195f9a0f0d9855b3f7b648bd1ada5d.
diff --git
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
index 9ad2fbd..18a6b38 100644
---
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
+++
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
@@ -65,7 +65,7 @@ public class EmbeddedWarDiscoveryComponent extends
MBeanResourceDiscoveryCompone
// Once we've finished making sure the plugin configurations have the data we
need:
// 1) First the stuff generic to all WARs...
- JBossASServerComponent<?> grandparentJBossASComponent =
parentEarComponent.getParentResourceComponent();
+ JBossASServerComponent grandparentJBossASComponent =
parentEarComponent.getParentResourceComponent();
resourceDetails =
WarDiscoveryHelper.initPluginConfigurations(grandparentJBossASComponent, resourceDetails,
parentEarComponent);
@@ -74,7 +74,7 @@ public class EmbeddedWarDiscoveryComponent extends
MBeanResourceDiscoveryCompone
for (DiscoveredResourceDetails resource : resourceDetails) {
Configuration pluginConfiguration = resource.getPluginConfiguration();
pluginConfiguration.put(new PropertySimple(WarComponent.FILE_NAME,
parentEarFullFileName
- + pluginConfiguration.getSimpleValue(WarComponent.NAME_CONFIG_PROP,
"")));
+ + resource.getResourceName()));
}
return resourceDetails;
commit a481dd1c53a035f9a633551ea982bc70d9e0188b
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 10:37:50 2011 -0500
Revert "Add drift templates to EAR/WAR/Embedded War types for AS-4 plugin"
This reverts commit 0ad22b15a3f6d5b700bfec506db74f6a20633f21.
diff --git a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
index b5d4bd1..db4b8e5 100644
--- a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
@@ -259,9 +259,8 @@
</configuration>
</content>
- <drift-definition
- name="Template-Base Files"
- description="Monitor base application server files for drift. It defines
monitoring for some standard sub-directories of the HOME directory. Note, it is not
recommeded to monitor all files for an application server. There are many files, and many
temp files.">
+ <drift-definition name="Template-Base Files"
+ description="Monitor base application server files for
drift. It defines monitoring for some standard sub-directories of the HOME directory.
Note, it is not recommeded to monitor all files for an application server. There are many
files, and many temp files.">
<basedir>
<value-context>pluginConfiguration</value-context>
<value-name>jbossHomeDir</value-name>
@@ -2268,15 +2267,6 @@
</configuration>
</content>
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
- <basedir>
- <value-context>pluginConfiguration</value-context>
- <value-name>filename</value-name>
- </basedir>
- </drift-definition>
-
<service name="Embedded Web Application (WAR)"
class="org.rhq.plugins.jbossas.WarComponent"
discovery="org.rhq.plugins.jbossas.EmbeddedWarDiscoveryComponent"
@@ -2394,15 +2384,6 @@
<metric property="Vhost.name" displayName="Vhost"
dataType="trait"
description="Virtual hosts this app runs on"/>
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the embedded web application
archive (WAR). Use filters for more fine-grained monitoring.">
- <basedir>
- <value-context>pluginConfiguration</value-context>
- <value-name>filename</value-name>
- </basedir>
- </drift-definition>
-
</service>
</service>
@@ -2479,55 +2460,55 @@
dataType="calltime" defaultOn="false"
units="milliseconds" destinationType="URL"
description="the minimum, maximum, and average response times for
HTTP requests serviced by this webapp"/>
- <metric property="Servlet.MinResponseTime" displayName="Min.
Servlet Resp. Time"
+ <metric property="Servlet.MinResponseTime"
displayName="Min. Servlet Resp. Time"
units="milliseconds"
description="Minimum response time of a servlet"/>
- <metric property="Servlet.AvgResponseTime" displayName="Avg.
Servlet Resp. Time"
+ <metric property="Servlet.AvgResponseTime"
displayName="Avg. Servlet Resp. Time"
units="milliseconds"
description="Average response time of a servlet"/>
- <metric property="Servlet.MaxResponseTime" displayName="Max.
Servlet Resp. Time"
+ <metric property="Servlet.MaxResponseTime"
displayName="Max. Servlet Resp. Time"
units="milliseconds"
description="Maximum response time of a servlet"/>
- <metric property="Servlet.TotalTime" displayName="Total
processing time"
+ <metric property="Servlet.TotalTime" displayName="Total
processing time"
units="milliseconds"
description="Total processing time of the webapp"
measurementType="trendsup"/>
- <metric property="Servlet.NumRequests" displayName="Requests
served"
+ <metric property="Servlet.NumRequests"
displayName="Requests served"
units="none" description="Number of requests served by
servlets"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Servlet.NumErrors" displayName="Errors while
processing"
+ <metric property="Servlet.NumErrors" displayName="Errors
while processing"
units="none" description="Number of errors while
processing"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
+ <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
units="none" description="Number active sessions for
the webapp right now" />
- <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
+ <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
units="none" description="Maximum number of active
sessions for the webapp" />
- <metric property="Session.sessionCounter"
displayName="Sessions created"
+ <metric property="Session.sessionCounter"
displayName="Sessions created"
units="none" description="Number of sessions created
for the webapp"
measurementType="trendsup" />
- <metric property="Session.expiredSessions"
displayName="Expired Sessions"
+ <metric property="Session.expiredSessions"
displayName="Expired Sessions"
units="none" description="Number of expired sessions
for the webapp"
measurementType="trendsup" />
- <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
+ <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
units="none" description="Number of sessions rejected
for the webapp"
measurementType="trendsup" />
- <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
+ <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
units="seconds" description="Average alive time of a
Sessions" />
- <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
+ <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
units="seconds" description="Maximum alive time of a
Sessions" />
- <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
+ <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
description="Virtual hosts this app runs on"/>
<content name="file" displayName="WAR File"
category="deployable" isCreationType="true">
@@ -2543,15 +2524,6 @@
</configuration>
</content>
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
- <basedir>
- <value-context>pluginConfiguration</value-context>
- <value-name>filename</value-name>
- </basedir>
- </drift-definition>
-
</service>
<service name="EJB3 Entity Tree Cache"
commit aa79d1e533ca7a3e216719c933493d8ffef64b6b
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Nov 30 10:36:24 2011 -0500
Revert "Add drift templates to EAR/WAR/Embedded War types for AS-4 plugin"
This reverts commit 72d6aa5c440900dacaf8f97914ea2e17dea07112.
diff --git
a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
index ea8d467..2ed3159 100644
---
a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
+++
b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
@@ -33,28 +33,28 @@ import org.rhq.core.pluginapi.measurement.MeasurementFacet;
/**
* @author Ian Springer
*/
-public class EmbeddedManagedDeploymentComponent extends
AbstractManagedDeploymentComponent implements MeasurementFacet {
+public class EmbeddedManagedDeploymentComponent extends
AbstractManagedDeploymentComponent
+ implements MeasurementFacet
+{
private static final String CUSTOM_PARENT_TRAIT = "custom.parent";
- private static final String CUSTOM_PATH_TRAIT = "custom.path";
// ------------ MeasurementFacet Implementation ------------
- public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest>
requests) throws Exception {
- Set<MeasurementScheduleRequest> remainingRequests = new
HashSet<MeasurementScheduleRequest>();
- for (MeasurementScheduleRequest request : requests) {
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest>
requests)
+ throws Exception
+ {
+ Set<MeasurementScheduleRequest> remainingRequests = new HashSet();
+ for (MeasurementScheduleRequest request : requests)
+ {
String metricName = request.getName();
- if (metricName.equals(CUSTOM_PARENT_TRAIT)) {
+ if (metricName.equals(CUSTOM_PARENT_TRAIT))
+ {
String parentDeploymentName =
getManagedDeployment().getParent().getName();
MeasurementDataTrait trait = new MeasurementDataTrait(request,
parentDeploymentName);
report.addData(trait);
-
- } else if (metricName.equals(CUSTOM_PATH_TRAIT)) {
- boolean exploded = this.deploymentFile.isDirectory();
- if (exploded) {
- MeasurementDataTrait trait = new MeasurementDataTrait(request,
this.deploymentFile.getPath());
- report.addData(trait);
- }
- } else {
+ }
+ else
+ {
remainingRequests.add(request);
}
}
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 470b0f6..32aca99 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
@@ -2313,15 +2313,6 @@
&deploymentContentConfigProps;
</content>
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
- <basedir>
- <value-context>measurementTrait</value-context>
- <value-name>custom.path</value-name>
- </basedir>
- </drift-definition>
-
</service>
<service name="Web Application (WAR)"
@@ -2373,15 +2364,6 @@
&deploymentContentConfigProps;
</content>
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
- <basedir>
- <value-context>measurementTrait</value-context>
- <value-name>custom.path</value-name>
- </basedir>
- </drift-definition>
-
<service name="Web Application Context"
class="WebApplicationContextComponent"
discovery="WebApplicationContextDiscoveryComponent"
@@ -2444,21 +2426,9 @@
<metric property="custom.parent" displayName="Parent"
dataType="trait" displayType="summary"
description="the name of this WAR's parent
deployment"/>
- <metric property="custom.path" displayName="Path"
- dataType="trait" displayType="summary"
- description="the absolute path of this WAR directory (not
supported for WAR's embedded in non-exploded EARs)"/>
&webApplicationMetrics;
- <drift-definition
- name="Template-Files"
- description="Monitor files comprising the embedded web application
archive (WAR). Use filters for more fine-grained monitoring.">
- <basedir>
- <value-context>measurementTrait</value-context>
- <value-name>custom.path</value-name>
- </basedir>
- </drift-definition>
-
<service name="Embedded Web Application Context"
class="WebApplicationContextComponent"
discovery="WebApplicationContextDiscoveryComponent"
commit 72d6aa5c440900dacaf8f97914ea2e17dea07112
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 29 17:54:29 2011 -0500
Add drift templates to EAR/WAR/Embedded War types for AS-4 plugin
- To support this, add new Path trait (via Ian) for embedded war resources
diff --git
a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
index 2ed3159..ea8d467 100644
---
a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
+++
b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/EmbeddedManagedDeploymentComponent.java
@@ -33,28 +33,28 @@ import org.rhq.core.pluginapi.measurement.MeasurementFacet;
/**
* @author Ian Springer
*/
-public class EmbeddedManagedDeploymentComponent extends
AbstractManagedDeploymentComponent
- implements MeasurementFacet
-{
+public class EmbeddedManagedDeploymentComponent extends
AbstractManagedDeploymentComponent implements MeasurementFacet {
private static final String CUSTOM_PARENT_TRAIT = "custom.parent";
+ private static final String CUSTOM_PATH_TRAIT = "custom.path";
// ------------ MeasurementFacet Implementation ------------
- public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest>
requests)
- throws Exception
- {
- Set<MeasurementScheduleRequest> remainingRequests = new HashSet();
- for (MeasurementScheduleRequest request : requests)
- {
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest>
requests) throws Exception {
+ Set<MeasurementScheduleRequest> remainingRequests = new
HashSet<MeasurementScheduleRequest>();
+ for (MeasurementScheduleRequest request : requests) {
String metricName = request.getName();
- if (metricName.equals(CUSTOM_PARENT_TRAIT))
- {
+ if (metricName.equals(CUSTOM_PARENT_TRAIT)) {
String parentDeploymentName =
getManagedDeployment().getParent().getName();
MeasurementDataTrait trait = new MeasurementDataTrait(request,
parentDeploymentName);
report.addData(trait);
- }
- else
- {
+
+ } else if (metricName.equals(CUSTOM_PATH_TRAIT)) {
+ boolean exploded = this.deploymentFile.isDirectory();
+ if (exploded) {
+ MeasurementDataTrait trait = new MeasurementDataTrait(request,
this.deploymentFile.getPath());
+ report.addData(trait);
+ }
+ } else {
remainingRequests.add(request);
}
}
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 32aca99..470b0f6 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
@@ -2313,6 +2313,15 @@
&deploymentContentConfigProps;
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
+ <basedir>
+ <value-context>measurementTrait</value-context>
+ <value-name>custom.path</value-name>
+ </basedir>
+ </drift-definition>
+
</service>
<service name="Web Application (WAR)"
@@ -2364,6 +2373,15 @@
&deploymentContentConfigProps;
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>measurementTrait</value-context>
+ <value-name>custom.path</value-name>
+ </basedir>
+ </drift-definition>
+
<service name="Web Application Context"
class="WebApplicationContextComponent"
discovery="WebApplicationContextDiscoveryComponent"
@@ -2426,9 +2444,21 @@
<metric property="custom.parent" displayName="Parent"
dataType="trait" displayType="summary"
description="the name of this WAR's parent
deployment"/>
+ <metric property="custom.path" displayName="Path"
+ dataType="trait" displayType="summary"
+ description="the absolute path of this WAR directory (not
supported for WAR's embedded in non-exploded EARs)"/>
&webApplicationMetrics;
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the embedded web application
archive (WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>measurementTrait</value-context>
+ <value-name>custom.path</value-name>
+ </basedir>
+ </drift-definition>
+
<service name="Embedded Web Application Context"
class="WebApplicationContextComponent"
discovery="WebApplicationContextDiscoveryComponent"
commit 0ad22b15a3f6d5b700bfec506db74f6a20633f21
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 29 17:53:32 2011 -0500
Add drift templates to EAR/WAR/Embedded War types for AS-4 plugin
diff --git a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
index db4b8e5..b5d4bd1 100644
--- a/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as/src/main/resources/META-INF/rhq-plugin.xml
@@ -259,8 +259,9 @@
</configuration>
</content>
- <drift-definition name="Template-Base Files"
- description="Monitor base application server files for
drift. It defines monitoring for some standard sub-directories of the HOME directory.
Note, it is not recommeded to monitor all files for an application server. There are many
files, and many temp files.">
+ <drift-definition
+ name="Template-Base Files"
+ description="Monitor base application server files for drift. It defines
monitoring for some standard sub-directories of the HOME directory. Note, it is not
recommeded to monitor all files for an application server. There are many files, and many
temp files.">
<basedir>
<value-context>pluginConfiguration</value-context>
<value-name>jbossHomeDir</value-name>
@@ -2267,6 +2268,15 @@
</configuration>
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the enterprise application
archive (EAR). Use filters for more fine-grained monitoring. Or, create definitions only
on the embedded WAR resources themselves.">
+ <basedir>
+ <value-context>pluginConfiguration</value-context>
+ <value-name>filename</value-name>
+ </basedir>
+ </drift-definition>
+
<service name="Embedded Web Application (WAR)"
class="org.rhq.plugins.jbossas.WarComponent"
discovery="org.rhq.plugins.jbossas.EmbeddedWarDiscoveryComponent"
@@ -2384,6 +2394,15 @@
<metric property="Vhost.name" displayName="Vhost"
dataType="trait"
description="Virtual hosts this app runs on"/>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the embedded web application
archive (WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>pluginConfiguration</value-context>
+ <value-name>filename</value-name>
+ </basedir>
+ </drift-definition>
+
</service>
</service>
@@ -2460,55 +2479,55 @@
dataType="calltime" defaultOn="false"
units="milliseconds" destinationType="URL"
description="the minimum, maximum, and average response times for
HTTP requests serviced by this webapp"/>
- <metric property="Servlet.MinResponseTime"
displayName="Min. Servlet Resp. Time"
+ <metric property="Servlet.MinResponseTime" displayName="Min.
Servlet Resp. Time"
units="milliseconds"
description="Minimum response time of a servlet"/>
- <metric property="Servlet.AvgResponseTime"
displayName="Avg. Servlet Resp. Time"
+ <metric property="Servlet.AvgResponseTime" displayName="Avg.
Servlet Resp. Time"
units="milliseconds"
description="Average response time of a servlet"/>
- <metric property="Servlet.MaxResponseTime"
displayName="Max. Servlet Resp. Time"
+ <metric property="Servlet.MaxResponseTime" displayName="Max.
Servlet Resp. Time"
units="milliseconds"
description="Maximum response time of a servlet"/>
- <metric property="Servlet.TotalTime" displayName="Total
processing time"
+ <metric property="Servlet.TotalTime" displayName="Total
processing time"
units="milliseconds"
description="Total processing time of the webapp"
measurementType="trendsup"/>
- <metric property="Servlet.NumRequests"
displayName="Requests served"
+ <metric property="Servlet.NumRequests" displayName="Requests
served"
units="none" description="Number of requests served by
servlets"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Servlet.NumErrors" displayName="Errors
while processing"
+ <metric property="Servlet.NumErrors" displayName="Errors while
processing"
units="none" description="Number of errors while
processing"
measurementType="trendsup"
displayType="summary"/>
- <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
+ <metric property="Session.activeSessions"
displayName="Currently Active Sessions"
units="none" description="Number active sessions for
the webapp right now" />
- <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
+ <metric property="Session.maxActive" displayName="Maximum
number of Active Sessions"
units="none" description="Maximum number of active
sessions for the webapp" />
- <metric property="Session.sessionCounter"
displayName="Sessions created"
+ <metric property="Session.sessionCounter"
displayName="Sessions created"
units="none" description="Number of sessions created
for the webapp"
measurementType="trendsup" />
- <metric property="Session.expiredSessions"
displayName="Expired Sessions"
+ <metric property="Session.expiredSessions"
displayName="Expired Sessions"
units="none" description="Number of expired sessions
for the webapp"
measurementType="trendsup" />
- <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
+ <metric property="Session.rejectedSessions"
displayName="Rejected Sessions"
units="none" description="Number of sessions rejected
for the webapp"
measurementType="trendsup" />
- <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
+ <metric property="Session.sessionAverageAliveTime"
displayName="Session Average alive time"
units="seconds" description="Average alive time of a
Sessions" />
- <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
+ <metric property="Session.sessionMaxAliveTime"
displayName="Max Session alive time"
units="seconds" description="Maximum alive time of a
Sessions" />
- <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
+ <metric property="Vhost.name" displayName="Vhost"
dataType="trait"
description="Virtual hosts this app runs on"/>
<content name="file" displayName="WAR File"
category="deployable" isCreationType="true">
@@ -2524,6 +2543,15 @@
</configuration>
</content>
+ <drift-definition
+ name="Template-Files"
+ description="Monitor files comprising the web application archive
(WAR). Use filters for more fine-grained monitoring.">
+ <basedir>
+ <value-context>pluginConfiguration</value-context>
+ <value-name>filename</value-name>
+ </basedir>
+ </drift-definition>
+
</service>
<service name="EJB3 Entity Tree Cache"
commit 8c6dcd3817195f9a0f0d9855b3f7b648bd1ada5d
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 29 17:50:22 2011 -0500
Fix issue in AS-4 plugin such that for embedded WAR resources the
"filename" plugin config prop and the "Path" trait are set to an
actual
path (do not use the resource name when constructing the value)
diff --git
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
index 18a6b38..9ad2fbd 100644
---
a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
+++
b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/EmbeddedWarDiscoveryComponent.java
@@ -65,7 +65,7 @@ public class EmbeddedWarDiscoveryComponent extends
MBeanResourceDiscoveryCompone
// Once we've finished making sure the plugin configurations have the data we
need:
// 1) First the stuff generic to all WARs...
- JBossASServerComponent grandparentJBossASComponent =
parentEarComponent.getParentResourceComponent();
+ JBossASServerComponent<?> grandparentJBossASComponent =
parentEarComponent.getParentResourceComponent();
resourceDetails =
WarDiscoveryHelper.initPluginConfigurations(grandparentJBossASComponent, resourceDetails,
parentEarComponent);
@@ -74,7 +74,7 @@ public class EmbeddedWarDiscoveryComponent extends
MBeanResourceDiscoveryCompone
for (DiscoveredResourceDetails resource : resourceDetails) {
Configuration pluginConfiguration = resource.getPluginConfiguration();
pluginConfiguration.put(new PropertySimple(WarComponent.FILE_NAME,
parentEarFullFileName
- + resource.getResourceName()));
+ + pluginConfiguration.getSimpleValue(WarComponent.NAME_CONFIG_PROP,
"")));
}
return resourceDetails;
commit 8939b50bf8bee6f4b0037885026abdcd2f79946a
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 29 12:18:17 2011 -0500
[Bug 758261 - A bad drift definition can prevent agent startup]
Add more aggressive catching (Throwable) because it's better to skip
detection for a problematic definition than it is to prevent agent
startup.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
index c713297..2790f13 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
@@ -152,9 +152,11 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
try {
syncWithServer(r, d);
schedulesQueue.addSchedule(new DriftDetectionSchedule(r.getId(), d));
- } catch (IOException e) {
+
+ } catch (Throwable t) {
+ // catch throwable, don't prevent agent startup just due to a bad
definition
log.error("Failed to sync with server for " +
toString(r.getId(), d) + ". Drift detection will not be "
- + "scheduled.", e);
+ + "scheduled.", t);
}
}
@@ -691,7 +693,8 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
// find out the type of base location that is specified by the drift def
DriftDefinition.BaseDirectory baseDir = driftDefinition.getBasedir();
if (baseDir == null) {
- throw new IllegalArgumentException("Missing basedir in drift
definition");
+ throw new IllegalArgumentException("Base directory is null for drift
definition ["
+ + driftDefinition.getName() + "]");
}
// based on the type of base location, determine the root base directory
commit f80179210e18065e5d32850726f9b79826dd669b
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Nov 29 17:18:02 2011 -0500
[BZ 756086] allow CLI to support printing of arrays (primitive and object). This only
supports single-dimension arrays but none of our APIs return multi-dim arrays AFAIK
diff --git
a/modules/enterprise/binding/src/main/java/org/rhq/bindings/output/TabularWriter.java
b/modules/enterprise/binding/src/main/java/org/rhq/bindings/output/TabularWriter.java
index 382ce8c..6ead45c 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/output/TabularWriter.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/output/TabularWriter.java
@@ -79,6 +79,8 @@ public class TabularWriter {
static {
SIMPLE_TYPES.add(Byte.class);
SIMPLE_TYPES.add(Byte.TYPE);
+ SIMPLE_TYPES.add(Character.class);
+ SIMPLE_TYPES.add(Character.TYPE);
SIMPLE_TYPES.add(Short.class);
SIMPLE_TYPES.add(Short.TYPE);
SIMPLE_TYPES.add(Integer.class);
@@ -130,7 +132,48 @@ public class TabularWriter {
}
if (object != null && object.getClass().isArray()) {
- print((Object[]) object);
+ if (!object.getClass().getComponentType().isPrimitive()) {
+ print((Object[]) object);
+ } else {
+ Class<?> oClass = object.getClass();
+ // note: we assume single-dimension arrays!
+ out.println("Array of " +
(oClass.getComponentType().getName()));
+ if (oClass == byte[].class) {
+ for (byte i : (byte[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == short[].class) {
+ for (short i : (short[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == int[].class) {
+ for (int i : (int[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == long[].class) {
+ for (long i : (long[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == char[].class) {
+ for (char i : (char[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == float[].class) {
+ for (float i : (float[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == double[].class) {
+ for (double i : (double[]) object) {
+ this.out.println(i);
+ }
+ } else if (oClass == boolean[].class) {
+ for (boolean i : (boolean[]) object) {
+ this.out.println(i);
+ }
+ } else {
+ this.out.println("*Printing of this data type is not
supported*");
+ }
+ }
return;
}
@@ -285,7 +328,7 @@ public class TabularWriter {
for (Object object : list) {
data[i++][0] = (String) object;
}
-
+ this.print(data);
} else {
if (consistentMaps(list)) {
@@ -318,30 +361,33 @@ public class TabularWriter {
}
}
- headers = new String[pdList.size()];
- data = new String[list.size()][pdList.size()];
+ if (pdList.isEmpty()) {
+ printStrings(list);
+ } else {
+ headers = new String[pdList.size()];
+ data = new String[list.size()][pdList.size()];
- for (PropertyDescriptor pd : pdList) {
- headers[i++] = pd.getName();
- }
- i = 0;
- for (Object row : list) {
- int j = 0;
for (PropertyDescriptor pd : pdList) {
-
- Object val = "?";
- val = invoke(row, pd.getReadMethod());
- if (val == null) {
- data[i][j++] = "";
- } else {
- data[i][j++] = shortVersion(val);
+ headers[i++] = pd.getName();
+ }
+ i = 0;
+ for (Object row : list) {
+ int j = 0;
+ for (PropertyDescriptor pd : pdList) {
+
+ Object val = "?";
+ val = invoke(row, pd.getReadMethod());
+ if (val == null) {
+ data[i][j++] = "";
+ } else {
+ data[i][j++] = shortVersion(val);
+ }
}
+ i++;
}
- i++;
- }
-
- this.print(data);
+ this.print(data);
+ }
}
}
} catch (Exception e) {
@@ -530,13 +576,11 @@ public class TabularWriter {
for (Integer col : columns) {
actualColumnWidths[col] = maxColumnWidth;
}
- }
- else if (columns.size() == 0) {
+ } else if (columns.size() == 0) {
// If the columns list is empty then that means that there is enough
available
// space for each column so we are done.
return;
- }
- else if (extraSpace > 0) {
+ } else if (extraSpace > 0) {
// Since we have extra space, we will go ahead and recalculate the widths
for
// those columns still needing space
int newMaxColumnWidth = (maxColumnWidth + extraSpace) / columns.size();
diff --git
a/modules/enterprise/binding/src/test/java/org/rhq/bindings/TabularWriterTest.java
b/modules/enterprise/binding/src/test/java/org/rhq/bindings/TabularWriterTest.java
index a80d2c2..a69f7bf 100644
--- a/modules/enterprise/binding/src/test/java/org/rhq/bindings/TabularWriterTest.java
+++ b/modules/enterprise/binding/src/test/java/org/rhq/bindings/TabularWriterTest.java
@@ -23,6 +23,8 @@
package org.rhq.bindings;
+import static org.testng.Assert.assertEquals;
+
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -42,8 +44,6 @@ import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.shared.ResourceBuilder;
-import static org.testng.Assert.*;
-
public class TabularWriterTest {
StringWriter stringWriter;
@@ -57,6 +57,60 @@ public class TabularWriterTest {
}
@Test
+ public void testPrintEntityArray() {
+ User[] users = new User[] { new User(1, "rhqadmin",
"rhqadmin"), new User(222, "name", "pw") };
+
+ writer.print(users);
+
+ assertLineEquals(1, "id password username", "Cannot print entity
array header");
+ // skip line 2 - its the separator "--------------"
+ assertLineEquals(3, "1 rhqadmin rhqadmin", "Cannot print first
entity array item");
+ assertLineEquals(4, "222 pw name ", "Cannot print second
entity array item");
+ }
+
+ /**
+ * This tests printing String arrays via the print(Object) API
+ */
+ @Test
+ public void testPrintStringArray() {
+ String[] objArray = new String[] { new String("first string"), new
String("second string") };
+ Object obj = objArray;
+ writer.print(obj);
+
+ // first two lines are the header and "----" separator
+ assertLineEquals(3, "first string ", "Cannot print first string
array item");
+ assertLineEquals(4, "second string", "Cannot print first string
array item");
+ }
+
+ /**
+ * This tests printing "primitive object" arrays via the print(Object) API
+ */
+ @Test
+ public void testPrintObjectArray() {
+ Integer[] objArray = new Integer[] { new Integer("123"), new
Integer("789") };
+ Object obj = objArray;
+ writer.print(obj);
+
+ String expected = "Array of java.lang.Integer\n123\n789\n";
+ String actual = stringWriter.toString();
+ assertEquals(actual, expected, "Could not print an object Integer
array");
+ }
+
+ /**
+ * This tests printing primitive arrays via the print(Object) API
+ */
+ @Test
+ public void testPrintPrimaryArray() {
+ byte[] primitiveArray = "abc".getBytes();
+ Object obj = primitiveArray;
+ writer.print(obj);
+
+ String expected = "Array of byte\n" + (int) 'a' + '\n'
+ (int) 'b' + '\n' + (int) 'c' + '\n';
+ String actual = stringWriter.toString();
+ assertEquals(actual, expected, "Could not print a primitive byte
array");
+ }
+
+ @Test
public void aByteShouldPrintUnchanged() {
byte value = 1;
commit 4a30174e4cb99e7290b7f8d7b21be19572ed57e6
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Nov 29 17:15:48 2011 -0500
add bindings test module to eclipse classpath
diff --git a/.classpath b/.classpath
index 29ff1d4..24fb54d 100644
--- a/.classpath
+++ b/.classpath
@@ -167,6 +167,7 @@
<classpathentry kind="src"
path="modules/enterprise/server/jar/src/main/filtered-sources/java"/>
<classpathentry kind="src"
path="modules/enterprise/server/jar/target/generated-sources/antlr3"/>
<classpathentry kind="src"
path="modules/enterprise/binding/src/main/java"/>
+ <classpathentry kind="src"
path="modules/enterprise/binding/src/test/java"/>
<classpathentry kind="src"
path="modules/enterprise/gui/coregui/src/main/java"/>
<classpathentry kind="src"
path="modules/enterprise/gui/coregui/target/generated-sources/gwt"/>
<classpathentry kind="src"
path="modules/enterprise/gui/portal-war/src/main/java"/>
commit 96fb4fd93fa197a0487674eea9b083db198cd632
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Tue Nov 29 14:03:51 2011 -0500
minor - fix javadoc typos
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossRemote.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossRemote.java
index b77c025..ee0a1f6 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossRemote.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossRemote.java
@@ -39,8 +39,9 @@ import org.rhq.enterprise.server.system.ServerVersion;
public interface DiscoveryBossRemote {
/**
- * Analogous to the GUI feature Import Resources in the auto discovery queue. Note,
to query for
- * Resources with a specific InnventryStatus see {@link
ResourceManagerRemote.findResourcesByCriteria(ResourceCriteria)}
+ * Analogous to the GUI feature Import Resources in the auto discovery queue.
+ * Note, to query for Resources with a specific InventoryStatus, see
+ * {@link
org.rhq.enterprise.server.resource.ResourceManagerRemote#findResourcesByCriteria}.
*
* @param subject
* @param resourceIds
@@ -50,8 +51,9 @@ public interface DiscoveryBossRemote {
@WebParam(name = "resourceIds") int[] resourceIds);
/**
- * Analogous to the GUI feature Ignore Resources in the auto discovery queue. Note,
to query for
- * Resources with a specific InnventryStatus see {@link
ResourceManagerRemote.findResourcesByCriteria(ResourceCriteria)}
+ * Analogous to the GUI feature Ignore Resources in the auto discovery queue.
+ * Note, to query for Resources with a specific InventoryStatus, see
+ * {@link
org.rhq.enterprise.server.resource.ResourceManagerRemote#findResourcesByCriteria}.
*
* @param subject
* @param resourceIds
@@ -61,8 +63,9 @@ public interface DiscoveryBossRemote {
@WebParam(name = "resourceIds") int[] resourceIds);
/**
- * Analogous to the GUI feature Unignore Resources in the auto discovery queue. Note,
to query for
- * Resources with a specific InnventryStatus see {@link
ResourceManagerRemote.findResourcesByCriteria(ResourceCriteria)}
+ * Analogous to the GUI feature Unignore Resources in the auto discovery queue.
+ * Note, to query for Resources with a specific InventoryStatus, see
+ * {@link
org.rhq.enterprise.server.resource.ResourceManagerRemote#findResourcesByCriteria}.
*
* @param subject
* @param resourceIds
commit 6f563d3e58bc41f2a97ac42619acd912abf49521
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Nov 29 12:20:51 2011 -0500
[BZ 756205] installer should go to /coregui explicitly
diff --git a/modules/enterprise/gui/installer-war/src/main/webapp/header.jsp
b/modules/enterprise/gui/installer-war/src/main/webapp/header.jsp
index 54a35be..673b018 100644
--- a/modules/enterprise/gui/installer-war/src/main/webapp/header.jsp
+++ b/modules/enterprise/gui/installer-war/src/main/webapp/header.jsp
@@ -63,7 +63,7 @@
{
linkText = "${bundle.alreadyInstalledStartedLink}";
document.getElementById('pleasewait-image').src="/images/finished.gif";
-
document.getElementById('progressBarMessage').innerHTML='<a
class="small" href="/">' + linkText + '<\/a>';
+
document.getElementById('progressBarMessage').innerHTML='<a
class="small" href="/coregui">' + linkText +
'<\/a>';
// this will immediately forward to the start page, uncomment if we
want that functionality
// window.location.replace( startPage );
}
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/index.html
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/index.html
index 9778784..5da4047 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/index.html
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/index.html
@@ -2,7 +2,7 @@
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
-<title>Welcome to RHQ!</title>
+<title>The Server Is Installed!</title>
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="-1" />
@@ -11,7 +11,7 @@
</head>
<body>
<p> </p>
- <h1 align="center">RHQ Is Installed!</h1>
- <p align="center"><a href="/">Click here to get
started!</a></p>
+ <h1 align="center">The Server Is Installed!</h1>
+ <p align="center"><a href="/coregui">Click here to get
started!</a></p>
</body>
</html>
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/start.jsf
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/start.jsf
index 4ce54a0..4e5a220 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/start.jsf
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/start.jsf
@@ -8,6 +8,6 @@
<body>
<p> </p>
<h1 align="center">The Server Is Installed!</h1>
- <p align="center"><a href="/">Click here to get
started!</a></p>
+ <p align="center"><a href="/coregui">Click here to get
started!</a></p>
</body>
</html>
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/welcome.jsf
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/welcome.jsf
index 4ce54a0..4e5a220 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/welcome.jsf
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-postinstaller.war.rej/welcome.jsf
@@ -8,6 +8,6 @@
<body>
<p> </p>
<h1 align="center">The Server Is Installed!</h1>
- <p align="center"><a href="/">Click here to get
started!</a></p>
+ <p align="center"><a href="/coregui">Click here to get
started!</a></p>
</body>
</html>
commit cc717fc0ac9b891306738842910b026d3a485085
Author: John Sanda <jsanda(a)redhat.com>
Date: Tue Nov 29 11:01:13 2011 -0500
[BZ 757958] updating script to work with current APIs
diff --git a/modules/enterprise/remoting/cli/src/main/samples/drift.js
b/modules/enterprise/remoting/cli/src/main/samples/drift.js
index ff7c31a..132d5bf 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/drift.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/drift.js
@@ -21,7 +21,7 @@
/**
* description: Generates a snapshot of changes sets belonging to a particular
- * drift configuration of a specific resource. By default all change sets are
+ * drift definition of a specific resource. By default all change sets are
* included in the snapshot. An optional third argument can be specified to
* limit or control the number of change sets included in the snapshot.
*
@@ -32,10 +32,11 @@
* arguments:
* rid: A resource id, expected to be an integer
*
- * cname: The drift configuration name as a string
+ * defName: The drift definition name as a string
*
- * filters: (optional) A map or JS object that limits the number of
- * change sets included in the snapshot
+ * filter: (optional) A map or JS object that limits the number of
+ * change sets included in the snapshot. It should specify two
+ * properties - startVersion and endVersion.
*
* return: A org.rhq.core.domamin.drift.DriftSnapshot object
*
@@ -44,48 +45,33 @@
* // mydrift drift configuration.
* createSnapshot(123, 'mydrift')
*
- * // generates a snapshot starting a change set version 3 and including
- * // everything more recent than it.
- * createSnapshot(123, 'mydrift', {"startVersion": 3})
- *
* // generates a snapshot that includes change sets 3 through 5 inclusive.
- * createSnapshot(123, 'mydrift', {"startVersion": 3,
"endVersion": 5})
- *
- * // generates a snaphot that includes everything after the specified
- * // timestamp. Note that the timestamp should be specified in milliseconds.
- * createSnapshot(123, 'mydrift', {"createdAfter": 1314811734365})
- *
- * // generates a snapshot that includes all change sets that were created
- * // between the two timestamps.
- * createSnapshot(123, 'mydrift', {"createdAfter": 1314811734365,
"createdBefore": 9994811734365})
+ * createSnapshot(123, 'mydrift', {startVersion: 3, endVersion: 5})
*/
-function createSnapshot(rid, cname) {
- var config = findDriftConfig(rid, function(c) { return cname.equals(c.name) });
-
- var criteria = GenericDriftChangeSetCriteria();
- criteria.addFilterResourceId(rid);
- criteria.addFilterDriftConfigurationId(config.id);
+function createSnapshot(rid, defName) {
+ var driftDef = findDriftDefinition(rid, function(d) { return defName.equals(d.name)
});
+ var snapshotRequest;
if (arguments.length > 2) {
var filters = arguments[2];
- for (key in filters) {
- criteria['addFilter' + key[0].toUpperCase() +
key.substring(1)](filters[key]);
- }
+ snapshotRequest = DriftSnapshotRequest(driftDef.id, filters.startVersion,
filters.endVersion);
+ } else {
+ snapshotRequest = DriftSnapshotRequest(driftDef.id);
}
- return DriftManager.createSnapshot(criteria);
+ return DriftManager.getSnapshot(snapshotRequest);
}
-function findDriftConfig(rid, filter) {
+function findDriftDefinition(rid, filter) {
var criteria = ResourceCriteria();
criteria.addFilterId(rid);
- criteria.fetchDriftConfigurations(true);
+ criteria.fetchDriftDefinitions(true);
var resources = ResourceManager.findResourcesByCriteria(criteria);
var resource = resources.get(0);
- return find(resource.driftConfigurations, function(config) {
- return filter(DriftConfiguration(config));
+ return find(resource.driftDefinitions, function(config) {
+ return filter(DriftDefinition(config));
});
}
@@ -111,7 +97,7 @@ function findDriftConfig(rid, filter) {
* s2.
*
* return: This function does not return a useful value. It may be an empty
- * string or it could be null. Instead of returns its results, the function
+ * string or it could be null. Instead of returning its results, the function
* prints them to the CLI console. As such, this function is intended for
* use from the interactive CLI shell.
*
@@ -137,8 +123,8 @@ function diff(s1, s2) {
}
var pathFilter = function(entry) { return entry.path == path; };
- var e1 = find(s1.entries, pathFilter);
- var e2 = find(s2.entries, pathFilter);
+ var e1 = find(s1.driftInstances, pathFilter);
+ var e2 = find(s2.driftInstances, pathFilter);
var fileDiff = DriftManager.generateUnifiedDiff(e1, e2);
foreach(fileDiff.diff, println);
@@ -190,7 +176,7 @@ function diff(s1, s2) {
* $ history.view(1) // returns the full contents of version 1 of the file
* $ history.compare(1, 2) // generates and prints a unified diff of versions 1 and 2
*/
-function fetchHistory(rid, configName, path) {
+function fetchHistory(rid, driftDefName, path) {
function History() {
var entries = [];
@@ -210,7 +196,7 @@ function fetchHistory(rid, configName, path) {
var drifts = DriftManager.findDriftsByCriteria(criteria);
foreach(drifts, function(drift) {
- if (drift.changeSet.driftConfiguration.name == configName &&
+ if (drift.changeSet.driftDefinition.name == driftDefName &&
drift.path == path) {
entries.push(drift);
}
@@ -234,7 +220,7 @@ function fetchHistory(rid, configName, path) {
if (drift == null) {
return "Could not find version " + version;
}
- return DriftManager.getDriftFileBits(drift.newDriftFile.hashId);
+ return
java.lang.String(DriftManager.getDriftFileAsByteArray(drift.newDriftFile.hashId));
}
this.compare = function(v1, v2) {
commit 127ba9e50f4a75d67cf021d0f688c2a064dc3824
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 29 09:04:50 2011 -0500
Make forEachFile() more efficient.
diff --git a/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
b/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
index c4ac737..14ed3af 100644
--- a/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
+++ b/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
@@ -353,8 +353,9 @@ public class FileUtil {
while (!directories.isEmpty()) {
File dir = directories.pop();
- if (dir.listFiles()!=null) {
- for (File file : dir.listFiles()) {
+ File[] files = dir.listFiles();
+ if (files != null) {
+ for (File file : files) {
if (file.isDirectory()) {
directories.push(file);
} else {
commit 4a6ec5f97390cc5b6a1956b0b904162ea7b2b319
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Nov 28 15:05:53 2011 -0500
[Bug 757201 - NPE in Server with drift report processing]
Not so much a fix as this was not reproducible, but some added
null protection and logging to try and ensure that a failure in
the "Ack" call does not cause a server-side failure. And to
capture more information if this happens again.
- note, some formatting changes were applied. the real changes are
lines 635-648.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
index c70b546..c713297 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftManager.java
@@ -166,8 +166,8 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
private void syncWithServer(Resource resource, DriftDefinition driftDefinition)
throws IOException {
Headers headers = createHeaders(resource.getId(), driftDefinition);
if (!changeSetMgr.changeSetExists(resource.getId(), headers)) {
- log.info("No snapshot found for " + toString(resource.getId(),
driftDefinition) +
- ". Downloading snapshot from server");
+ log.info("No snapshot found for " + toString(resource.getId(),
driftDefinition)
+ + ". Downloading snapshot from server");
DriftServerService driftServer =
pluginContainerConfiguration.getServerServices().getDriftServerService();
DriftSnapshot snapshot =
driftServer.getCurrentSnapshot(driftDefinition.getId());
@@ -179,18 +179,18 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
// this point we just return and allow the agent to generate the
// initial snapshot file.
if (log.isDebugEnabled()) {
- log.debug("The server does not have any change sets for " +
toString(resource.getId(), driftDefinition)
- + ". An initial snapshot needs to be generated.");
+ log.debug("The server does not have any change sets for "
+ + toString(resource.getId(), driftDefinition) + ". An
initial snapshot needs to be generated.");
}
return;
}
headers.setVersion(snapshot.getVersion());
- log.info("Preparing to write snapshot at version " +
snapshot.getVersion() + " to disk for " +
- toString(resource.getId(), driftDefinition));
- File currentSnapshotFile = changeSetMgr.findChangeSet(resource.getId(),
driftDefinition.getName(),
- COVERAGE);
+ log.info("Preparing to write snapshot at version " +
snapshot.getVersion() + " to disk for "
+ + toString(resource.getId(), driftDefinition));
+ File currentSnapshotFile = changeSetMgr
+ .findChangeSet(resource.getId(), driftDefinition.getName(), COVERAGE);
writeSnapshotToFile(snapshot, currentSnapshotFile, headers);
if (driftDefinition.isPinned()) {
@@ -200,15 +200,16 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
DriftSnapshot pinnedSnapshot =
driftServer.getSnapshot(driftDefinition.getId(), 0, 0);
Headers pinnedHeaders = createHeaders(resource.getId(),
driftDefinition);
File pinnedSnapshotFile = new File(currentSnapshotFile.getParent(),
"snapshot.pinned");
- log.info("Preparing to write pinned snapshot to disk for " +
toString(resource.getId(), driftDefinition));
+ log.info("Preparing to write pinned snapshot to disk for "
+ + toString(resource.getId(), driftDefinition));
writeSnapshotToFile(pinnedSnapshot, pinnedSnapshotFile, pinnedHeaders);
if (snapshot.getVersion() > 0) {
// Drift was previously reported. We will fetch a snapshot of the
// latest change set and write that to disk so that we avoid
reporting
// drift that has already been reported to the server.
- DriftSnapshot deltaSnapshot =
driftServer.getSnapshot(driftDefinition.getId(),
- snapshot.getVersion(), snapshot.getVersion());
+ DriftSnapshot deltaSnapshot =
driftServer.getSnapshot(driftDefinition.getId(), snapshot
+ .getVersion(), snapshot.getVersion());
File deltaFile = new File(currentSnapshotFile.getParentFile(),
"drift-changeset.txt");
Headers deltaHeaders = createHeaders(resource.getId(),
driftDefinition);
deltaHeaders.setVersion(snapshot.getVersion());
@@ -317,14 +318,14 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
} catch (IOException e) {
zipFile.delete();
throw new DriftDetectionException("Failed to create change set zip file
" + zipFile.getPath(), e);
-// } finally {
-// try {
-// if (stream != null) {
-// stream.close();
-// }
-// } catch (IOException e) {
-// log.warn("An error occurred while trying to close change set zip
file output stream", e);
-// }
+ // } finally {
+ // try {
+ // if (stream != null) {
+ // stream.close();
+ // }
+ // } catch (IOException e) {
+ // log.warn("An error occurred while trying to close
change set zip file output stream", e);
+ // }
}
try {
@@ -541,8 +542,8 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
schedulesQueue.removeAndExecute(schedule.getResourceId(),
schedule.getDriftDefinition(), new Runnable() {
@Override
public void run() {
- File currentSnapshot =
changeSetMgr.findChangeSet(schedule.getResourceId(),
- schedule.getDriftDefinition().getName(), COVERAGE);
+ File currentSnapshot =
changeSetMgr.findChangeSet(schedule.getResourceId(), schedule
+ .getDriftDefinition().getName(), COVERAGE);
File pinnedSnapshot = new File(currentSnapshot.getParentFile(),
"snapshot.pinned");
pinnedSnapshot.delete();
@@ -557,8 +558,7 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
@Override
public void updateDriftDetection(int resourceId, DriftDefinition driftDef,
DriftSnapshot driftSnapshot) {
- File currentSnapshot = changeSetMgr.findChangeSet(resourceId,
driftDef.getName(),
- COVERAGE);
+ File currentSnapshot = changeSetMgr.findChangeSet(resourceId, driftDef.getName(),
COVERAGE);
File pinnedSnapshot = new File(currentSnapshot.getParentFile(),
"snapshot.pinned");
Headers headers = createHeaders(resourceId, driftDef);
@@ -606,8 +606,8 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
final DriftDetectionSchedule schedule = schedulesQueue.find(resourceId,
defName);
if (schedule == null) {
if (log.isDebugEnabled()) {
- log.debug("Unable to pin snapshot for " + toString(resourceId,
defName) + " - no detection schedule " +
- "found.");
+ log.debug("Unable to pin snapshot for " + toString(resourceId,
defName) + " - no detection schedule "
+ + "found.");
}
return;
}
@@ -619,8 +619,9 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
@Override
public void ackChangeSet(int resourceId, String defName) {
- log.info("Received server change set ack for [resourceId: " +
resourceId + ", driftDefinition:" +
- defName + "]");
+ log
+ .info("Received server change set ack for [resourceId: " +
resourceId + ", driftDefinition:" + defName
+ + "]");
File resourceDir = new File(changeSetsDir, Integer.toString(resourceId));
File changeSetDir = new File(resourceDir, defName);
@@ -631,11 +632,20 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
return;
}
- File snapshot = changeSetMgr.findChangeSet(resourceId, defName, COVERAGE);
- File previousSnapshot = new File(snapshot.getParentFile(), snapshot.getName() +
".previous");
+ try {
+ File snapshot = changeSetMgr.findChangeSet(resourceId, defName, COVERAGE);
+ if (null == snapshot) {
+ log.warn("Cannot complete acknowledgement. Could not find coverage
changeset for [" + resourceId + ","
+ + defName + "].");
+ return;
+ }
- previousSnapshot.delete();
- deleteZipFiles(changeSetDir, "changeset_");
+ File previousSnapshot = new File(snapshot.getParentFile(), snapshot.getName()
+ ".previous");
+ previousSnapshot.delete();
+
+ } finally {
+ deleteZipFiles(changeSetDir, "changeset_");
+ }
}
@Override
@@ -758,16 +768,16 @@ public class DriftManager extends AgentService implements
DriftAgentService, Dri
private void writeSnapshotToFile(DriftSnapshot snapshot, File file, Headers headers)
throws IOException {
ChangeSetWriter writer = changeSetMgr.getChangeSetWriter(file, headers);
try {
- for (Drift drift : snapshot.getDriftInstances()) {
+ for (Drift<?, ?> drift : snapshot.getDriftInstances()) {
switch (drift.getCategory()) {
case FILE_ADDED:
writer.write(addedFileEntry(drift.getPath(),
drift.getNewDriftFile().getHashId()));
break;
case FILE_CHANGED:
- writer.write(changedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId(),
- drift.getNewDriftFile().getHashId()));
+ writer.write(changedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId(), drift
+ .getNewDriftFile().getHashId()));
break;
- default: // FILE_REMOVED
+ default: // FILE_REMOVED
writer.write(removedFileEntry(drift.getPath(),
drift.getOldDriftFile().getHashId()));
}
}
commit f6eba2a3f0d31a45918664109034182de01ec970
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Nov 28 14:58:12 2011 -0500
Convert back to unix line terminators
diff --git
a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
index f4e62d5..b284dcc 100644
---
a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
+++
b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
@@ -1,87 +1,87 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2008 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.
- */
-package org.rhq.core.clientapi.server.core;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-
-import org.rhq.core.domain.cloud.composite.FailoverListComposite;
-import org.rhq.core.domain.cloud.composite.FailoverListComposite.ServerEntry;
-
-/**
- * These are the results of a successful agent registration. After the agent has been
registered by the server, this
- * object will contain the identification information assigned to the agent by the
server. It will also contain the
- * list of servers this agent can fail over to in a high availability setup.
- *
- * @author John Mazzitelli
- */
-public class AgentRegistrationResults implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private String agentToken;
- private FailoverListComposite failoverList;
-
- /**
- * The agent token that the agent must use in order to communicate with the server in
the future.
- *
- * @return the agent's token assigned to it by the server
- */
- public String getAgentToken() {
- return agentToken;
- }
-
- /**
- * See {@link #getAgentToken()}.
- *
- * @param token
- */
- public void setAgentToken(String token) {
- agentToken = token;
- }
-
- /**
- * The FailoverListComposite that the agent must use in order to communicate with the
server in the future.
- *
- * @return the FailoverListComposite assigned to it by the server (will not be
<code>null</code> but may be empty)
- */
- public FailoverListComposite getFailoverList() {
- return failoverList != null ? failoverList : new FailoverListComposite(new
ArrayList<ServerEntry>());
- }
-
- /**
- * See {@link #getFailoverList()}.
- *
- * @param failoverList
- */
- public void setFailoverList(FailoverListComposite failoverList) {
- this.failoverList = failoverList;
- }
-
- /**
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "AgentRegistrationResults: [agent-token=" + this.agentToken +
"]";
- }
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.
+ */
+package org.rhq.core.clientapi.server.core;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.rhq.core.domain.cloud.composite.FailoverListComposite;
+import org.rhq.core.domain.cloud.composite.FailoverListComposite.ServerEntry;
+
+/**
+ * These are the results of a successful agent registration. After the agent has been
registered by the server, this
+ * object will contain the identification information assigned to the agent by the
server. It will also contain the
+ * list of servers this agent can fail over to in a high availability setup.
+ *
+ * @author John Mazzitelli
+ */
+public class AgentRegistrationResults implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String agentToken;
+ private FailoverListComposite failoverList;
+
+ /**
+ * The agent token that the agent must use in order to communicate with the server in
the future.
+ *
+ * @return the agent's token assigned to it by the server
+ */
+ public String getAgentToken() {
+ return agentToken;
+ }
+
+ /**
+ * See {@link #getAgentToken()}.
+ *
+ * @param token
+ */
+ public void setAgentToken(String token) {
+ agentToken = token;
+ }
+
+ /**
+ * The FailoverListComposite that the agent must use in order to communicate with the
server in the future.
+ *
+ * @return the FailoverListComposite assigned to it by the server (will not be
<code>null</code> but may be empty)
+ */
+ public FailoverListComposite getFailoverList() {
+ return failoverList != null ? failoverList : new FailoverListComposite(new
ArrayList<ServerEntry>());
+ }
+
+ /**
+ * See {@link #getFailoverList()}.
+ *
+ * @param failoverList
+ */
+ public void setFailoverList(FailoverListComposite failoverList) {
+ this.failoverList = failoverList;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "AgentRegistrationResults: [agent-token=" + this.agentToken +
"]";
+ }
}
\ No newline at end of file
commit 99803343b9014aa9399ef2444cc214061bf0bdb9
Author: John Sanda <jsanda(a)redhat.com>
Date: Mon Nov 28 14:10:55 2011 -0500
[BZ 755073] Do not display the view link for binary files
We were displaying the view link for a drift entry where the type of
drift was a file change. The logic has been modified so that the view
link is not displayed.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDetailsView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDetailsView.java
index 1dcf23b..0cd08a1 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDetailsView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDetailsView.java
@@ -145,8 +145,7 @@ public class DriftDetailsView extends LocatableVLayout {
case FILE_CHANGED:
category.setValue(DriftDataSource.CATEGORY_ICON_CHANGE);
oldFile.setValue(driftDetails.getDrift().getOldDriftFile().getHashId());
- oldFileLink =
createViewFileLink(driftDetails.getDrift().getOldDriftFile().getHashId(), driftDetails
- .getDrift().getPath(),
driftDetails.getPreviousChangeSet().getVersion());
+ oldFileLink = spacer;
newFile.setValue(driftDetails.getDrift().getNewDriftFile().getHashId());
newFileLink = spacer;
break;
commit 4443f017ce764353f41e4c4ca17d334af5742234
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Nov 28 13:50:13 2011 -0500
[Bug 754197 - When agent runs on loopback address, the advisory text is incorrect.
Administration > High Availability does not exist]
- Trivial - Fix the text
- Also, fix some jdoc text in AgentRegistrationResults (reformatted as well). No code
changes.
diff --git
a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
index f1d58f3..f4e62d5 100644
---
a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
+++
b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/core/AgentRegistrationResults.java
@@ -1,87 +1,87 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2008 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.
- */
-package org.rhq.core.clientapi.server.core;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-
-import org.rhq.core.domain.cloud.composite.FailoverListComposite;
-import org.rhq.core.domain.cloud.composite.FailoverListComposite.ServerEntry;
-
-/**
- * These are the results of a successful agent registration. After the agent has been
registered by the server, this
- * object will contain the identification information assigned to the agent by the
server. It will also contain the
- * list of servers this agent can fail over to an in high availability setup.
- *
- * @author John Mazzitelli
- */
-public class AgentRegistrationResults implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private String agentToken;
- private FailoverListComposite failoverList;
-
- /**
- * The agent token that the agent must use in order to communicate with the server in
the future.
- *
- * @return the agent's token assigned to it by the server
- */
- public String getAgentToken() {
- return agentToken;
- }
-
- /**
- * See {@link #getAgentToken()}.
- *
- * @param token
- */
- public void setAgentToken(String token) {
- agentToken = token;
- }
-
- /**
- * The FailoverListComposite that the agent must use in order to communicate with the
server in the future.
- *
- * @return the FailoverListComposite assigned to it by the server (will not be
<code>null</code> but may be empty)
- */
- public FailoverListComposite getFailoverList() {
- return failoverList != null ? failoverList : new FailoverListComposite(new
ArrayList<ServerEntry>());
- }
-
- /**
- * See {@link #getFailoverList()}.
- *
- * @param failoverList
- */
- public void setFailoverList(FailoverListComposite failoverList) {
- this.failoverList = failoverList;
- }
-
- /**
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "AgentRegistrationResults: [agent-token=" + this.agentToken +
"]";
- }
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2008 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.
+ */
+package org.rhq.core.clientapi.server.core;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.rhq.core.domain.cloud.composite.FailoverListComposite;
+import org.rhq.core.domain.cloud.composite.FailoverListComposite.ServerEntry;
+
+/**
+ * These are the results of a successful agent registration. After the agent has been
registered by the server, this
+ * object will contain the identification information assigned to the agent by the
server. It will also contain the
+ * list of servers this agent can fail over to in a high availability setup.
+ *
+ * @author John Mazzitelli
+ */
+public class AgentRegistrationResults implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String agentToken;
+ private FailoverListComposite failoverList;
+
+ /**
+ * The agent token that the agent must use in order to communicate with the server in
the future.
+ *
+ * @return the agent's token assigned to it by the server
+ */
+ public String getAgentToken() {
+ return agentToken;
+ }
+
+ /**
+ * See {@link #getAgentToken()}.
+ *
+ * @param token
+ */
+ public void setAgentToken(String token) {
+ agentToken = token;
+ }
+
+ /**
+ * The FailoverListComposite that the agent must use in order to communicate with the
server in the future.
+ *
+ * @return the FailoverListComposite assigned to it by the server (will not be
<code>null</code> but may be empty)
+ */
+ public FailoverListComposite getFailoverList() {
+ return failoverList != null ? failoverList : new FailoverListComposite(new
ArrayList<ServerEntry>());
+ }
+
+ /**
+ * See {@link #getFailoverList()}.
+ *
+ * @param failoverList
+ */
+ public void setFailoverList(FailoverListComposite failoverList) {
+ this.failoverList = failoverList;
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "AgentRegistrationResults: [agent-token=" + this.agentToken +
"]";
+ }
}
\ No newline at end of file
diff --git
a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
index b0608c2..59702fc 100644
---
a/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
+++
b/modules/enterprise/agent/src/main/java/org/rhq/enterprise/agent/i18n/AgentI18NResourceKeys.java
@@ -212,7 +212,7 @@ public interface AgentI18NResourceKeys {
@I18NMessage("!!! A server has registered under a loopback address [{0}] - this
should only be done for testing and demo purposes. "
+ "Only agents running on the same machine as that server will be able to
interact with that server successfully. "
+ "Please double check that you really want your server to have a public
endpoint of [{0}]. "
- + "See the Administration > High Availability > Servers menu in the
server GUI to change the public endpoint of the server.")
+ + "See the Administration (Topology) > Servers menu in the server GUI to
change the public endpoint of the server.")
String FAILOVER_LIST_HAS_LOCALHOST =
"AgentMain.failover-list-has-localhost";
@I18NMessage("The server failover list has been loaded from [{0}] - there are
[{1}] servers in the list")
@@ -249,7 +249,7 @@ public interface AgentI18NResourceKeys {
+ "Please double check all public endpoints of your servers and
ensure\\n\\\n"
+ "they are all reachable by this agent. The failed server endpoints
are:\\n\\\n" //
+ "{1}\\n\\\n" //
- + "See the Administration > High Availability > Servers in the server
GUI\\n\\\n"
+ + "See the Administration (Topology) > Servers in the server
GUI\\n\\\n"
+ "to change the public endpoint of a server.\\n\\\n"
+ "THIS AGENT WILL WAIT UNTIL ONE OF ITS SERVERS BECOMES REACHABLE!")
String FAILOVER_LIST_CHECK_FAILED =
"AgentMain.failover-list-check-failed";
commit 1495d03f65e30eef1f533d90ee4c8b434282f790
Author: John Sanda <jsanda(a)redhat.com>
Date: Mon Nov 28 12:03:06 2011 -0500
[BZ 757758] make property name/values consistent with other server properties
diff --git a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
index 3a18691..d920ff9 100644
--- a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
+++ b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
@@ -22,7 +22,7 @@
<property name="rhq.sync.endpoint-address" value="false"/>
- <property name="default.rhq.server.drift.binary.content"
value="0"/>
+ <property name="default.rhq.server.drift.binary.content"
value="false"/>
<target name="set-predeploy-prop">
<condition property="predeploy" value="true">
@@ -48,6 +48,7 @@
<property name="rhq.server.quartz.driverDelegateClass"
value="${rhq.dev.quartz.driverDelegateClass}" />
<property name="rhq.server.quartz.selectWithLockSQL"
value="${rhq.dev.quartz.selectWithLockSQL}" />
<property name="rhq.server.quartz.lockHandlerClass"
value="${rhq.dev.quartz.lockHandlerClass}" />
+ <property name="rhq.server.drift.store-binary-content"
value="${default.rhq.server.drift.binary.content}"/>
</target>
<target name="set-default-server-props" unless="predeploy">
@@ -65,6 +66,7 @@
<property name="rhq.server.quartz.driverDelegateClass"
value="${default.rhq.server.quartz.driverDelegateClass}" />
<property name="rhq.server.quartz.selectWithLockSQL"
value="${default.rhq.server.quartz.selectWithLockSQL}" />
<property name="rhq.server.quartz.lockHandlerClass"
value="${default.rhq.server.quartz.lockHandlerClass}" />
+ <property name="rhq.server.drift.store-binary-content"
value="${default.rhq.server.drift.binary.content}"/>
</target>
<target name="initialize" depends="set-predeploy-prop,
set-dev-server-props, set-default-server-props">
@@ -610,12 +612,13 @@
rhq.server.plugin-scan-period-ms=${rhq.server.plugin-scan-period-ms}
# you have to deal with IP address changes.
rhq.sync.endpoint-address=false
-# Enabling this property has two effects. First, it will cause the server to
-# request that agents send binary content for files being monitored for drift.
-# Secondly, the server will persist that binary content. The property is
-# enabled when it is to set a value of 1. Any other value will cause this
-# feature to be disabled.
-# default.rhq.server.drift.binary.content=0
+# When this is enabled, the server will request that agents send content for
+# for binary files that are being monitored for drift. The content for those
+# files will then be persisted on the server. By default, the server does not
+# persist content for well known binary file types like .ear, .war, .zip, .so,
+# etc. The server does however persist content for text files or any other
+# files that are not known binary types.
+# rhq.server.drift.store-binary-content=${rhq.server.drift.store-binary-content}
# Auto-Install Pre-Configuration Settings
# If you wish to circumvent the installer webapp and auto-install the server
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
index ddcdace..6633c22 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
@@ -306,6 +306,7 @@ public class JPADriftServerBean implements JPADriftServerLocal {
throws Exception {
final Resource resource = getResource(resourceId);
final DriftChangeSetSummary summary = new DriftChangeSetSummary();
+ final boolean storeBinaryContent = isBinaryContentStorageEnabled();
try {
ZipUtil.walkZipFile(changeSetZip, new ChangeSetFileVisitor() {
@@ -346,8 +347,7 @@ public class JPADriftServerBean implements JPADriftServerLocal {
if (version > 0) {
for (FileEntry entry : reader) {
- boolean addToList =
- isBinaryContentStorageEnabled() ||
!DriftUtil.isBinaryFile(entry.getFile());
+ boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
JPADriftFile oldDriftFile =
getDriftFile(entry.getOldSHA(), emptyDriftFiles, addToList);
JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
@@ -375,8 +375,7 @@ public class JPADriftServerBean implements JPADriftServerLocal {
summary.setInitialChangeSet(true);
JPADriftSet driftSet = new JPADriftSet();
for (FileEntry entry : reader) {
- boolean addToList =
- isBinaryContentStorageEnabled() ||
!DriftUtil.isBinaryFile(entry.getFile());
+ boolean addToList = storeBinaryContent ||
!DriftUtil.isBinaryFile(entry.getFile());
JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
String path = FileUtil.useForwardSlash(entry.getFile());
// A Drift always has a changeSet. Note that in this code
section the changeset is
@@ -435,8 +434,8 @@ public class JPADriftServerBean implements JPADriftServerLocal {
}
private boolean isBinaryContentStorageEnabled() {
- String binaryContent =
System.getProperty("default.rhq.server.drift.binary.content", "0");
- return binaryContent.equals("1");
+ String binaryContent =
System.getProperty("rhq.server.drift.store-binary-content", "false");
+ return binaryContent.equals("true");
}
private JPADriftFile getDriftFile(String sha256, List<JPADriftFile>
emptyDriftFiles, boolean addToList) {
commit 12766f0f87c4922faa8d59b2a962beabd6933c09
Author: John Sanda <jsanda(a)redhat.com>
Date: Mon Nov 28 11:08:19 2011 -0500
[BZ 757758] Adding logic to filter out binary content
By default, the server will not store content for binary files that are
being monitored for drift. It can be enabled by setting the system
property default.rhq.server.drift.binary.content to a value of 1. The
property can be set in rhq-server.properties.
diff --git a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
index 9dbae53..3a18691 100644
--- a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
+++ b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
@@ -22,6 +22,8 @@
<property name="rhq.sync.endpoint-address" value="false"/>
+ <property name="default.rhq.server.drift.binary.content"
value="0"/>
+
<target name="set-predeploy-prop">
<condition property="predeploy" value="true">
<or>
@@ -608,6 +610,13 @@ rhq.server.plugin-scan-period-ms=${rhq.server.plugin-scan-period-ms}
# you have to deal with IP address changes.
rhq.sync.endpoint-address=false
+# Enabling this property has two effects. First, it will cause the server to
+# request that agents send binary content for files being monitored for drift.
+# Secondly, the server will persist that binary content. The property is
+# enabled when it is to set a value of 1. Any other value will cause this
+# feature to be disabled.
+# default.rhq.server.drift.binary.content=0
+
# Auto-Install Pre-Configuration Settings
# If you wish to circumvent the installer webapp and auto-install the server
# immediately upon first startup, make sure all of the settings above are
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
index 7c1d3e1..6af05d9 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
@@ -119,48 +119,6 @@ import org.rhq.enterprise.server.util.LookupUtil;
@Stateless
public class DriftManagerBean implements DriftManagerLocal, DriftManagerRemote {
- private static Set<String> binaryFileTypes = new HashSet<String>();
-
- static {
- binaryFileTypes.add("jar");
- binaryFileTypes.add("war");
- binaryFileTypes.add("ear");
- binaryFileTypes.add("sar"); // jboss service
- binaryFileTypes.add("har"); // hibernate archive
- binaryFileTypes.add("rar"); // resource adapter
- binaryFileTypes.add("wsr"); // jboss web service archive
- binaryFileTypes.add("zip");
- binaryFileTypes.add("tar");
- binaryFileTypes.add("bz2");
- binaryFileTypes.add("gz");
- binaryFileTypes.add("rpm");
- binaryFileTypes.add("so");
- binaryFileTypes.add("dll");
- binaryFileTypes.add("exe");
- binaryFileTypes.add("jpg");
- binaryFileTypes.add("png");
- binaryFileTypes.add("jpeg");
- binaryFileTypes.add("gif");
- binaryFileTypes.add("pdf");
- binaryFileTypes.add("swf");
- binaryFileTypes.add("bpm");
- binaryFileTypes.add("tiff");
- binaryFileTypes.add("svg");
- binaryFileTypes.add("doc");
- binaryFileTypes.add("mp3");
- binaryFileTypes.add("wav");
- binaryFileTypes.add("m4a");
- binaryFileTypes.add("mov");
- binaryFileTypes.add("mpeg");
- binaryFileTypes.add("avi");
- binaryFileTypes.add("mp4");
- binaryFileTypes.add("wmv");
- binaryFileTypes.add("deb");
- binaryFileTypes.add("sit");
- binaryFileTypes.add("iso");
- binaryFileTypes.add("dmg");
- }
-
// TODO Should security checks be handled here instead of delegating to the drift
plugin?
// Currently any security checks that need to be performed are delegated to the
plugin.
// This is fine *so far* since the only plugin is the default which is our existing
SLSB
@@ -869,14 +827,7 @@ public class DriftManagerBean implements DriftManagerLocal,
DriftManagerRemote {
@Override
public boolean isBinaryFile(Subject subject, Drift<?, ?> drift) {
- String path = drift.getPath();
- int index = path.lastIndexOf('.');
-
- if (index == -1 || index == path.length() - 1) {
- return false;
- }
-
- return binaryFileTypes.contains(path.substring(index + 1, path.length()));
+ return DriftUtil.isBinaryFile(drift);
}
@Override
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftUtil.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftUtil.java
index 16cb479..34a3c69 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftUtil.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftUtil.java
@@ -21,9 +21,12 @@ package org.rhq.enterprise.server.drift;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.HashSet;
+import java.util.Set;
import org.apache.commons.logging.LogFactory;
+import org.rhq.core.domain.drift.Drift;
import org.rhq.enterprise.communications.ServiceContainer;
import org.rhq.enterprise.communications.command.client.ClientCommandSender;
import
org.rhq.enterprise.communications.command.client.ClientCommandSenderConfiguration;
@@ -36,6 +39,62 @@ import
org.rhq.enterprise.server.core.comm.ServerCommunicationsServiceUtil;
*/
public class DriftUtil {
+ private static Set<String> binaryFileTypes = new HashSet<String>();
+
+ static {
+ binaryFileTypes.add("jar");
+ binaryFileTypes.add("war");
+ binaryFileTypes.add("ear");
+ binaryFileTypes.add("sar"); // jboss service
+ binaryFileTypes.add("har"); // hibernate archive
+ binaryFileTypes.add("rar"); // resource adapter
+ binaryFileTypes.add("wsr"); // jboss web service archive
+ binaryFileTypes.add("zip");
+ binaryFileTypes.add("tar");
+ binaryFileTypes.add("bz2");
+ binaryFileTypes.add("gz");
+ binaryFileTypes.add("rpm");
+ binaryFileTypes.add("so");
+ binaryFileTypes.add("dll");
+ binaryFileTypes.add("exe");
+ binaryFileTypes.add("jpg");
+ binaryFileTypes.add("png");
+ binaryFileTypes.add("jpeg");
+ binaryFileTypes.add("gif");
+ binaryFileTypes.add("pdf");
+ binaryFileTypes.add("swf");
+ binaryFileTypes.add("bpm");
+ binaryFileTypes.add("tiff");
+ binaryFileTypes.add("svg");
+ binaryFileTypes.add("doc");
+ binaryFileTypes.add("mp3");
+ binaryFileTypes.add("wav");
+ binaryFileTypes.add("m4a");
+ binaryFileTypes.add("mov");
+ binaryFileTypes.add("mpeg");
+ binaryFileTypes.add("avi");
+ binaryFileTypes.add("mp4");
+ binaryFileTypes.add("wmv");
+ binaryFileTypes.add("deb");
+ binaryFileTypes.add("sit");
+ binaryFileTypes.add("iso");
+ binaryFileTypes.add("dmg");
+ }
+
+ static boolean isBinaryFile(Drift<?, ?> drift) {
+ return isBinaryFile(drift.getPath());
+ }
+
+ static boolean isBinaryFile(String path) {
+ int index = path.lastIndexOf('.');
+
+ if (index == -1 || index == path.length() - 1) {
+ return false;
+ }
+
+ return binaryFileTypes.contains(path.substring(index + 1, path.length()));
+ }
+
static InputStream remoteStream(InputStream stream) {
RemoteInputStream remoteStream = (RemoteInputStream) stream;
ServiceContainer serviceContainer =
ServerCommunicationsServiceUtil.getService().getServiceContainer();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
index 142e959..ddcdace 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/JPADriftServerBean.java
@@ -346,8 +346,10 @@ public class JPADriftServerBean implements JPADriftServerLocal {
if (version > 0) {
for (FileEntry entry : reader) {
- JPADriftFile oldDriftFile =
getDriftFile(entry.getOldSHA(), emptyDriftFiles);
- JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles);
+ boolean addToList =
+ isBinaryContentStorageEnabled() ||
!DriftUtil.isBinaryFile(entry.getFile());
+ JPADriftFile oldDriftFile =
getDriftFile(entry.getOldSHA(), emptyDriftFiles, addToList);
+ JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
// TODO Figure out an efficient way to save coverage
change sets.
// The initial/coverage change set could contain hundreds
or even thousands
@@ -373,7 +375,9 @@ public class JPADriftServerBean implements JPADriftServerLocal {
summary.setInitialChangeSet(true);
JPADriftSet driftSet = new JPADriftSet();
for (FileEntry entry : reader) {
- JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles);
+ boolean addToList =
+ isBinaryContentStorageEnabled() ||
!DriftUtil.isBinaryFile(entry.getFile());
+ JPADriftFile newDriftFile =
getDriftFile(entry.getNewSHA(), emptyDriftFiles, addToList);
String path = FileUtil.useForwardSlash(entry.getFile());
// A Drift always has a changeSet. Note that in this code
section the changeset is
// always going to be set to a DriftDefinition's
changeSet. But that is not always the
@@ -430,7 +434,12 @@ public class JPADriftServerBean implements JPADriftServerLocal {
}
}
- private JPADriftFile getDriftFile(String sha256, List<JPADriftFile>
emptyDriftFiles) {
+ private boolean isBinaryContentStorageEnabled() {
+ String binaryContent =
System.getProperty("default.rhq.server.drift.binary.content", "0");
+ return binaryContent.equals("1");
+ }
+
+ private JPADriftFile getDriftFile(String sha256, List<JPADriftFile>
emptyDriftFiles, boolean addToList) {
JPADriftFile result = null;
if (null == sha256 || "0".equals(sha256)) {
@@ -441,7 +450,9 @@ public class JPADriftServerBean implements JPADriftServerLocal {
// if the JPADriftFile is not yet in the db, then it needs to be fetched from the
agent
if (null == result) {
result = persistDriftFile(new JPADriftFile(sha256));
- emptyDriftFiles.add(result);
+ if (addToList) {
+ emptyDriftFiles.add(result);
+ }
}
return result;
commit f1475dbbc68e537e73727b38a717837c57ce05b7
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Tue Nov 22 16:59:23 2011 -0500
[BZ 620603] turn log level down from WARN to ERROR for
org.hibernate.hql.ast.QueryTranslatorImpl
to suppress "firstResult/maxResults specified with collection fetch; applying in
memory!"
warning messages (
https://bugzilla.redhat.com/show_bug.cgi?id=620603)
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-log4j.xml
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-log4j.xml
index 16b0384..936c5b9 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-log4j.xml
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-log4j.xml
@@ -315,6 +315,12 @@
<priority value="OFF"/>
</category>
+ <!-- Suppress "firstResult/maxResults specified with collection fetch;
applying in memory!" WARN messages
+ (see
https://bugzilla.redhat.com/show_bug.cgi?id=620603). -->
+ <category name="org.hibernate.hql.ast.QueryTranslatorImpl">
+ <priority value="ERROR"/>
+ </category>
+
<!-- log SQL statements -->
<!--
<category name="org.hibernate.SQL">
commit 49eaa2bfe7d58efad3881522715f288d2ee01763
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sun Nov 27 21:32:54 2011 +0100
Adapt the examples to the latest changes in the API.
Make the simple examples more stable by selecting active schedules via ajax
callbacks.
Move whisker() into the rhq namespace.
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/bars_simple.html
b/modules/enterprise/gui/rest-war/src/main/webapp/bars_simple.html
index 710b19b..e4e96d9 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/bars_simple.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/bars_simple.html
@@ -3,6 +3,7 @@
<head>
<title>Bar Chart</title>
<script type="text/javascript"
src="js/d3.js"></script>
+ <script type="text/javascript"
src="js/jquery.js"></script>
<style type="text/css">
body {
@@ -16,107 +17,114 @@ svg {
</style>
</head>
<body>
- <h2>Schedule id 10003</h2>
+ <h1 id="resId">Resource</h1>
+ <h2 id="header">Schedule</h2>
<script type="text/javascript">
-//var data = d3.range(60).map(Math.random);
-
-d3.json(
- '/rest/1/metric/data/10003.json',
- function (jsondata) {
-
- // create the chart here with
- // the returned data
-
-// console.log(jsondata);
- var points = jsondata.metricAggregate.dataPoints;
- var min = jsondata.metricAggregate.min;
- var max = jsondata.metricAggregate.max;
-
- console.log(min);
- console.log(max);
-
- console.log(points);
-
- var data = points.map(function(d) {
- var t = d.value - min;
- if (t<0)
- t=0;
- return t;
- });
- console.log(data);
-
-
-
-var w = 430,
- h = 630,
- x = d3.scale.linear().domain([0, max-min]).range([0, w]),
- y = d3.scale.ordinal().domain(d3.range(data.length)).rangeBands([0, h], .2);
-
-var vis = d3.select("body")
- .append("svg:svg")
- .attr("width", w + 40)
- .attr("height", h + 20)
- .append("svg:g")
- .attr("transform", "translate(20,0)");
-
-var bars = vis.selectAll("g.bar")
- .data(data)
- .enter().append("svg:g")
- .attr("class", "bar")
- .attr("transform", function(d, i) { return "translate(0," + y(i)
+ ")"; });
-
-bars.append("svg:rect")
- .attr("fill", "steelblue")
- .attr("width", x)
- .attr("height", y.rangeBand());
-
-bars.append("svg:text")
- .attr("x", x)
- .attr("y", y.rangeBand() / 2)
- .attr("dx", -6)
- .attr("dy", ".35em")
- .attr("fill", "white")
- .attr("text-anchor", "end")
- .text(x.tickFormat(100));
-
-bars.append("svg:text")
- .attr("x", 0)
- .attr("y", y.rangeBand() / 2)
- .attr("dx", -6)
- .attr("dy", ".35em")
- .attr("text-anchor", "end")
- .text(function(d, i) { return String.fromCharCode(65 + i); });
-
-var rules = vis.selectAll("g.rule")
- .data(x.ticks(10))
- .enter().append("svg:g")
- .attr("class", "rule")
- .attr("transform", function(d) { return "translate(" + x(d) +
",0)"; });
-
-rules.append("svg:line")
- .attr("y1", h)
- .attr("y2", h + 6)
- .attr("stroke", "black");
-
-rules.append("svg:line")
- .attr("y1", 0)
- .attr("y2", h)
- .attr("stroke", "white")
- .attr("stroke-opacity", .3);
-
-rules.append("svg:text")
- .attr("y", h + 9)
- .attr("dy", ".71em")
- .attr("text-anchor", "middle")
- .text(x.tickFormat(10));
-
-vis.append("svg:line")
- .attr("y1", 0)
- .attr("y2", h)
- .attr("stroke", "black");
-
- });
+ // Get the platforms and then the first one
+ $.getJSON('/rest/1/resource/platforms.json', function (json) {
+ var res = json[0];
+ var rid = res.resourceId;
+ $("#resId").text("Resource " + rid + " (" +
res.resourceName + ")");
+
+ // Now get the metrical schedules of the platform
+ $.getJSON("/rest/1/resource/"+rid+"/schedules.json?type=metric",
function (json2) {
+
+ // Take the first
+ var schId = json2[0].scheduleId;
+ $("#header").text("Schedule " + schId + " (" +
json2[0].scheduleName + ")");
+
+ // And call d3 graphing on it
+ d3.json(
+ '/rest/1/metric/data/'+schId+'.json',
+ function (jsondata) {
+
+ // create the chart here with
+ // the returned data
+
+ var points = jsondata.dataPoints;
+ var min = jsondata.min;
+ var max = jsondata.max;
+
+ var data = points.map(function(d) {
+ var t = d.value - min;
+ if (t<0)
+ t=0;
+ return t;
+ });
+
+ var w = 430,
+ h = 630,
+ x = d3.scale.linear().domain([0, max-min]).range([0, w]),
+ y =
d3.scale.ordinal().domain(d3.range(data.length)).rangeBands([0, h], .2);
+
+ var vis = d3.select("body")
+ .append("svg:svg")
+ .attr("width", w + 40)
+ .attr("height", h + 20)
+ .append("svg:g")
+ .attr("transform", "translate(20,0)");
+
+ var bars = vis.selectAll("g.bar")
+ .data(data)
+ .enter().append("svg:g")
+ .attr("class", "bar")
+ .attr("transform", function(d, i) { return
"translate(0," + y(i) + ")"; });
+
+ bars.append("svg:rect")
+ .attr("fill", "steelblue")
+ .attr("width", x)
+ .attr("height", y.rangeBand());
+
+ bars.append("svg:text")
+ .attr("x", x)
+ .attr("y", y.rangeBand() / 2)
+ .attr("dx", -6)
+ .attr("dy", ".35em")
+ .attr("fill", "white")
+ .attr("text-anchor", "end")
+ .text(x.tickFormat(100));
+
+ bars.append("svg:text")
+ .attr("x", 0)
+ .attr("y", y.rangeBand() / 2)
+ .attr("dx", -6)
+ .attr("dy", ".35em")
+ .attr("text-anchor", "end")
+ .text(function(d, i) { return String.fromCharCode(65 + i);
});
+
+ var rules = vis.selectAll("g.rule")
+ .data(x.ticks(10))
+ .enter().append("svg:g")
+ .attr("class", "rule")
+ .attr("transform", function(d) { return
"translate(" + x(d) + ",0)"; });
+
+ rules.append("svg:line")
+ .attr("y1", h)
+ .attr("y2", h + 6)
+ .attr("stroke", "black");
+
+ rules.append("svg:line")
+ .attr("y1", 0)
+ .attr("y2", h)
+ .attr("stroke", "white")
+ .attr("stroke-opacity", .3);
+
+ rules.append("svg:text")
+ .attr("y", h + 9)
+ .attr("dy", ".71em")
+ .attr("text-anchor", "middle")
+ .text(x.tickFormat(10));
+
+ vis.append("svg:line")
+ .attr("y1", 0)
+ .attr("y2", h)
+ .attr("stroke", "black");
+
+ });
+
+ })
+ })
</script>
</body>
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/js/jquery.js
b/modules/enterprise/gui/rest-war/src/main/webapp/js/jquery.js
new file mode 100644
index 0000000..8ccd0ea
--- /dev/null
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/js/jquery.js
@@ -0,0 +1,9266 @@
+/*!
+ * jQuery JavaScript Library v1.7.1
+ *
http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ *
http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ *
http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Mon Nov 21 21:11:03 2011 -0500
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens =
/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Matches dashed string for camelizing
+ rdashAlpha = /-([a-z]|[0-9])/ig,
+ rmsPrefix = /^-ms-/,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt(
selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex
check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context ? context.ownerDocument || context : document );
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment
).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.7.1",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) +
selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector +
")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.add( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray =
jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true &&
!jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket
#5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.fireWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).off( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery.Callbacks( "once memory" );
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ // A crude way of determining if an object is a window
+ isWindow: function( obj ) {
+ return obj && typeof obj === "object" &&
"setInterval" in obj;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType ||
jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from
http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ var xml, tmp;
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror"
).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ //
http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-...
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, ""
);
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type ===
"function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array, i ) {
+ var len;
+
+ if ( array ) {
+ if ( indexOf ) {
+ return indexOf.call( array, elem, i );
+ }
+
+ len = array.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in array && array[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length ===
"number" && ( ( length > 0 && elems[ 0 ] && elems[
length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be
removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, key, value, exec, fn, pass ) {
+ var length = elems.length;
+
+ // Setting many attributes
+ if ( typeof key === "object" ) {
+ for ( var k in key ) {
+ jQuery.access( elems, k, key[k], exec, fn, value );
+ }
+ return elems;
+ }
+
+ // Setting one attribute
+ if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = !pass && exec && jQuery.isFunction(value);
+
+ for ( var i = 0; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value,
pass );
+ }
+
+ return elems;
+ }
+
+ // Getting an attribute
+ return length ? fn( elems[0], key ) : undefined;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details:
http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof
jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp
Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket
#5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ //
http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+// String to Object flags format cache
+var flagsCache = {};
+
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+ var object = flagsCache[ flags ] = {},
+ i, length;
+ flags = flags.split( /\s+/ );
+ for ( i = 0, length = flags.length; i < length; i++ ) {
+ object[ flags[i] ] = true;
+ }
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * flags: an optional list of space-separated flags that will change how
+ * the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+ // Convert flags from String-formatted to Object-formatted
+ // (we check in cache first)
+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+ var // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = [],
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Add one or several callbacks to the list
+ add = function( args ) {
+ var i,
+ length,
+ elem,
+ type,
+ actual;
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ // Inspect recursively
+ add( elem );
+ } else if ( type === "function" ) {
+ // Add if not in unique mode and callback is not in
+ if ( !flags.unique || !self.has( elem ) ) {
+ list.push( elem );
+ }
+ }
+ }
+ },
+ // Fire callbacks
+ fire = function( context, args ) {
+ args = args || [];
+ memory = !flags.memory || [ context, args ];
+ firing = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( context, args ) === false &&
flags.stopOnFalse ) {
+ memory = true; // Mark as halted
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( !flags.once ) {
+ if ( stack && stack.length ) {
+ memory = stack.shift();
+ self.fireWith( memory[ 0 ], memory[ 1 ] );
+ }
+ } else if ( memory === true ) {
+ self.disable();
+ } else {
+ list = [];
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ var length = list.length;
+ add( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away, unless previous
+ // firing was halted (stopOnFalse)
+ } else if ( memory && memory !== true ) {
+ firingStart = length;
+ fire( memory[ 0 ], memory[ 1 ] );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ var args = arguments,
+ argIndex = 0,
+ argLength = args.length;
+ for ( ; argIndex < argLength ; argIndex++ ) {
+ for ( var i = 0; i < list.length; i++ ) {
+ if ( args[ argIndex ] === list[ i ] ) {
+ // Handle firingIndex and firingLength
+ if ( firing ) {
+ if ( i <= firingLength ) {
+ firingLength--;
+ if ( i <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ // Remove the element
+ list.splice( i--, 1 );
+ // If we have some unicity property then
+ // we only need to do this once
+ if ( flags.unique ) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ if ( list ) {
+ var i = 0,
+ length = list.length;
+ for ( ; i < length; i++ ) {
+ if ( fn === list[ i ] ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory || memory === true ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( stack ) {
+ if ( firing ) {
+ if ( !flags.once ) {
+ stack.push( [ context, args ] );
+ }
+ } else if ( !( flags.once && memory ) ) {
+ fire( context, args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!memory;
+ }
+ };
+
+ return self;
+};
+
+
+
+
+var // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var doneList = jQuery.Callbacks( "once memory" ),
+ failList = jQuery.Callbacks( "once memory" ),
+ progressList = jQuery.Callbacks( "memory" ),
+ state = "pending",
+ lists = {
+ resolve: doneList,
+ reject: failList,
+ notify: progressList
+ },
+ promise = {
+ done: doneList.add,
+ fail: failList.add,
+ progress: progressList.add,
+
+ state: function() {
+ return state;
+ },
+
+ // Deprecated
+ isResolved: doneList.fired,
+ isRejected: failList.fired,
+
+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+ return this;
+ },
+ always: function() {
+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ return this;
+ },
+ pipe: function( fnDone, fnFail, fnProgress ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ],
+ progress: [ fnProgress, "notify" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [
returned ] );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ obj = promise;
+ } else {
+ for ( var key in promise ) {
+ obj[ key ] = promise[ key ];
+ }
+ }
+ return obj;
+ }
+ },
+ deferred = promise.promise({}),
+ key;
+
+ for ( key in lists ) {
+ deferred[ key ] = lists[ key ].fire;
+ deferred[ key + "With" ] = lists[ key ].fireWith;
+ }
+
+ // Handle state
+ deferred.done( function() {
+ state = "resolved";
+ }, failList.disable, progressList.lock ).fail( function() {
+ state = "rejected";
+ }, doneList.disable, progressList.lock );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = sliceDeferred.call( arguments, 0 ),
+ i = 0,
+ length = args.length,
+ pValues = new Array( length ),
+ count = length,
+ pCount = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction(
firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred(),
+ promise = deferred.promise();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ deferred.resolveWith( deferred, args );
+ }
+ };
+ }
+ function progressFunc( i ) {
+ return function( value ) {
+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ deferred.notifyWith( promise, pValues );
+ };
+ }
+ if ( length > 1 ) {
+ for ( ; i < length; i++ ) {
+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i
].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return promise;
+ }
+});
+
+
+
+
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ marginDiv,
+ fragment,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = " <link/><table></table><a href='/a'
style='top:1px;float:left;opacity:.55;'>a</a><input
type='checkbox'/>";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing
get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form(#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !==
"<:nav></:nav>",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true
+ };
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ div.innerHTML = "";
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( window.getComputedStyle ) {
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 }
).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ // Technique from Juriy Zaytsev
+ //
http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See:
https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ fragment.removeChild( div );
+
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = marginDiv = div = input = null;
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, outer, inner, table, td, offsetSupport,
+ conMarginTop, ptlm, vb, style, html,
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ conMarginTop = 1;
+ ptlm = "position:absolute;top:0;left:0;width:1px;height:1px;margin:0;";
+ vb = "visibility:hidden;border:0;";
+ style = "style='" + ptlm + "border:5px solid
#000;padding:0;'";
+ html = "<div " + style +
"><div></div></div>" +
+ "<table " + style + " cellpadding='0'
cellspacing='0'>" +
+ "<tr><td></td></tr></table>";
+
+ container = document.createElement("div");
+ container.style.cssText = vb +
"width:0;height:0;position:static;top:0;margin-top:" + conMarginTop +
"px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td
style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0
);
+
+ // Figure out if the W3C box model works as expected
+ div.innerHTML = "";
+ div.style.width = div.style.paddingLeft = "1px";
+ jQuery.boxModel = support.boxModel = div.offsetWidth === 2;
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.style.cssText = ptlm + vb;
+ div.innerHTML = html;
+
+ outer = div.firstChild;
+ inner = outer.firstChild;
+ td = outer.nextSibling.firstChild.firstChild;
+
+ offsetSupport = {
+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+ };
+
+ inner.style.position = "fixed";
+ inner.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+ inner.style.position = inner.style.top = "";
+
+ outer.style.overflow = "hidden";
+ outer.style.position = "relative";
+
+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+ body.removeChild( container );
+ div = container = null;
+
+ jQuery.extend( support, offsetSupport );
+ });
+
+ return support;
+})();
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g,
"" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var privateCache, thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+ isEvents = name === "events";
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data))
&& getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = ++jQuery.uuid;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ privateCache = thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Users should not attempt to inspect the internal events object using jQuery.data,
+ // it is undocumented and subject to change. But does anyone listen? No.
+ if ( isEvents && !thisCache[ name ] ) {
+ return privateCache.events;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ // Reference to internal data cache key
+ internalKey = jQuery.expando,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ internalKey ] : internalKey;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ // Ensure that `cache` is not a window object #10080
+ if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the cache and need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ internalKey ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+ } else {
+ elem[ internalKey ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, attr, name,
+ data = null;
+
+ if ( typeof key === "undefined" ) {
+ if ( this.length ) {
+ data = jQuery.data( this[0] );
+
+ if ( this[0].nodeType === 1 && !jQuery._data( this[0],
"parsedAttrs" ) ) {
+ attr = this[0].attributes;
+ for ( var i = 0, l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( this[0], name, data[ name ] );
+ }
+ }
+ jQuery._data( this[0], "parsedAttrs", true );
+ }
+ }
+
+ return data;
+
+ } else if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split(".");
+ parts[1] = parts[1] ? "." + parts[1] : "";
+
+ if ( value === undefined ) {
+ data = this.triggerHandler("getData" + parts[1] + "!",
[parts[0]]);
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && this.length ) {
+ data = jQuery.data( this[0], key );
+ data = dataAttr( this[0], key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+
+ } else {
+ return this.each(function() {
+ var self = jQuery( this ),
+ args = [ parts[0], value ];
+
+ self.triggerHandler( "setData" + parts[1] + "!", args );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + parts[1] + "!", args );
+ });
+ }
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1"
).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ jQuery.isNumeric( data ) ? parseFloat( data ) :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery._data( elem, deferDataKey );
+ if ( defer &&
+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery._data( elem, queueDataKey ) &&
+ !jQuery._data( elem, markDataKey ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.fire();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = ( type || "fx" ) + "mark";
+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
+ if ( count ) {
+ jQuery._data( elem, key, count );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ var q;
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ q = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ hooks = {};
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ jQuery._data( elem, type + ".run", hooks );
+ fn.call( elem, function() {
+ jQuery.dequeue( elem, type );
+ }, hooks );
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ }
+
+ if ( data === undefined ) {
+ return jQuery.queue( this[0], type );
+ }
+ return this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ //
http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory"
), true ) )) {
+ count++;
+ tmp.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise();
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean =
/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ nodeHook, boolHook, fixSpecified;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.attr );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, name, value, true, jQuery.prop );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classNames, i, l, elem, className, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ classNames = ( value || "" ).split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ className = (" " + elem.className + " ").replace( rclass,
" " );
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[ c ] + " ",
" ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal
);
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data(
this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + "
").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.nodeName.toLowerCase() ] || jQuery.valHooks[ elem.type
];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem,
"value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type
];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value"
) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, i, max, option,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ i = one ? index : 0;
+ max = one ? index + 1 : options.length;
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled :
option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode,
"optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml &&
(ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret
= hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, l,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+ attrNames = value.toLowerCase().split( rspace );
+ l = attrNames.length;
+
+ for ( ; i < l; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ jQuery.attr( elem, name, "" );
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( rboolean.test( name ) && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in
IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio"
&& jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem,
value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name
)) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been
explicitly set
+ //
http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabi...
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) &&
elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Add the tabIndex propHook to attrHooks for back-compat (different case is
intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && (
attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" :
ret.specified ) ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.nodeValue = value + "" );
+ }
+ };
+
+ // Apply the nodeHook to tabindex
+ jQuery.attrHooks.tabindex.set = nodeHook.set;
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height"
], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = "" + value );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on"
if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+
+
+
+
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+ rhoverHack = /\bhover(\.\S+)?\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+ quickParse = function( selector ) {
+ var quick = rquickIs.exec( selector );
+ if ( quick ) {
+ // 0 1 2 3
+ // [ _, tag, id, class ]
+ quick[1] = ( quick[1] || "" ).toLowerCase();
+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] +
"(?:\\s|$)" );
+ }
+ return quick;
+ },
+ quickIs = function( elem, m ) {
+ var attrs = elem.attributes || {};
+ return (
+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+ (!m[2] || (attrs.id || {}).value === m[2]) &&
+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+ );
+ },
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack,
"mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, quick, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData =
jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e ||
jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native
events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ quick: quickParse( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) ===
false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ t, tns, type, origType, namespaces, origCount,
+ j, events, special, handle, eventType, handleObj;
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" +
namespaces.split(".").sort().join("\\.(?:.*\\.)?") +
"(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**"
&& handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, [ "events", "handle" ], true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type
] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" +
namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to
document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) )
{
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ old = null;
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old && old === elem.ownerDocument ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] &&
jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data )
=== false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) ===
false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" ))
&& jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be
(#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus"
&& type !== "blur") || event.target.offsetWidth !== 0) &&
!jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] ||
[]),
+ delegateCount = handlers.delegateCount,
+ args = [].slice.call( arguments, 0 ),
+ run_all = !event.exclusive && !event.namespace,
+ handlerQueue = [],
+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid disabled elements in IE (#6911) and non-left-click bubbling in Firefox
(#3861)
+ if ( delegateCount && !event.target.disabled && !(event.button
&& event.type === "click") ) {
+
+ // Pregenerate a single jQuery object for reuse with .is()
+ jqcur = jQuery(this);
+ jqcur.context = this.ownerDocument || this;
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+ selMatch = {};
+ matches = [];
+ jqcur[0] = cur;
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = (
+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+ );
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ )
{
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length &&
!event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have
no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) ||
event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle ||
handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C,
deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable
ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view
which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY
screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body &&
body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body &&
body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body &&
body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop
|| 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement :
fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0
) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+ if ( event.metaKey === undefined ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady
+ },
+
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue :
returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+//
http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-bind...
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector,
+ ret;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) )
{
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem,
"button" ) ? elem.form : undefined;
+ if ( form && !form._submit_attached ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ });
+ form._submit_attached = true;
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached
above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ jQuery.event.simulate( "change", this, event, true );
+ }
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ elem._change_attached = true;
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !==
"radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig,
fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) {
+ // ( types-Object, data )
+ data = selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on.call( this, types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ var handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace? handleObj.type + "." + handleObj.namespace :
handleObj.type,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( var type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types,
selector, fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) %
i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick
" +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split("
"), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information:
http://sizzlejs.com/
+ */
+(function(){
+
+var chunker =
/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^
>+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache" + (Math.random() + '').replace('.',
''),
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rReturn = /\r\n/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context, seed );
+
+ } else {
+ set = Expr.relative[ parts[0] ] ?
+ [ context ] :
+ Sizzle( parts.shift(), context );
+
+ while ( parts.length ) {
+ selector = parts.shift();
+
+ if ( Expr.relative[ selector ] ) {
+ selector += parts.shift();
+ }
+
+ set = posProcess( selector, set, seed );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 &&
!contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1])
) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ ret = seed ?
+ { expr: parts.pop(), set: makeArray(seed) } :
+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~"
|| parts[0] === "+") && context.parentNode ? context.parentNode :
context, contextXML );
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
+
+ if ( parts.length > 0 ) {
+ checkSet = makeArray( set );
+
+ } else {
+ prune = false;
+ }
+
+ while ( parts.length ) {
+ cur = parts.pop();
+ pop = cur;
+
+ if ( !Expr.relative[ cur ] ) {
+ cur = "";
+ } else {
+ pop = parts.pop();
+ }
+
+ if ( pop == null ) {
+ pop = context;
+ }
+
+ Expr.relative[ cur ]( checkSet, pop, contextXML );
+ }
+
+ } else {
+ checkSet = parts = [];
+ }
+ }
+
+ if ( !checkSet ) {
+ checkSet = set;
+ }
+
+ if ( !checkSet ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1
&& Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
+ results.push( set[i] );
+ }
+ }
+ }
+
+ } else {
+ makeArray( checkSet, results );
+ }
+
+ if ( extra ) {
+ Sizzle( extra, origContext, results, seed );
+ Sizzle.uniqueSort( results );
+ }
+
+ return results;
+};
+
+Sizzle.uniqueSort = function( results ) {
+ if ( sortOrder ) {
+ hasDuplicate = baseHasDuplicate;
+ results.sort( sortOrder );
+
+ if ( hasDuplicate ) {
+ for ( var i = 1; i < results.length; i++ ) {
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
+ }
+ }
+ }
+ }
+
+ return results;
+};
+
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set, i, len, match, type, left;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ filter = Expr.filter[ type ];
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ if ( curLoop === result ) {
+ result = [];
+ }
+
+ if ( Expr.preFilter[ type ] ) {
+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter
);
+
+ if ( !match ) {
+ anyFound = found = true;
+
+ } else if ( match === true ) {
+ continue;
+ }
+ }
+
+ if ( match ) {
+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ pass = not ^ found;
+
+ if ( inplace && found != null ) {
+ if ( pass ) {
+ anyFound = true;
+
+ } else {
+ curLoop[i] = false;
+ }
+
+ } else if ( pass ) {
+ result.push( item );
+ anyFound = true;
+ }
+ }
+ }
+ }
+
+ if ( found !== undefined ) {
+ if ( !inplace ) {
+ curLoop = result;
+ }
+
+ expr = expr.replace( Expr.match[ type ], "" );
+
+ if ( !anyFound ) {
+ return [];
+ }
+
+ break;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
+};
+
+var Expr = Sizzle.selectors = {
+ order: [ "ID", "NAME", "TAG" ],
+
+ match: {
+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
+ ATTR:
/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD:
/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO:
/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
+ if ( (elem = checkSet[i]) ) {
+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
+
+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() ===
part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ checkSet[i] = isPartStr ?
+ elem.parentNode :
+ elem.parentNode === part;
+ }
+ }
+
+ if ( isPartStr ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ }
+ },
+
+ "": function(checkSet, part, isXML){
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
+ }
+ },
+
+ find: {
+ ID: function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ if ( typeof context.getElementsByName !== "undefined" ) {
+ var ret = [],
+ results = context.getElementsByName( match[1] );
+
+ for ( var i = 0, l = results.length; i < l; i++ ) {
+ if ( results[i].getAttribute("name") === match[1] ) {
+ ret.push( results[i] );
+ }
+ }
+
+ return ret.length === 0 ? null : ret;
+ }
+ },
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + "
").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n',
'3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] ===
"odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, ""
);
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3])
) {
+ match[3] = Sizzle(match[3], null, null, curLoop);
+
+ } else {
+ var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
+ if ( !inplace ) {
+ result.push.apply( result, ret );
+ }
+
+ return false;
+ }
+
+ } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
+ return true;
+ }
+
+ return match;
+ },
+
+ POS: function( match ) {
+ match.unshift( true );
+
+ return match;
+ }
+ },
+
+ filters: {
+ enabled: function( elem ) {
+ return elem.disabled === false && elem.type !== "hidden";
+ },
+
+ disabled: function( elem ) {
+ return elem.disabled === true;
+ },
+
+ checked: function( elem ) {
+ return elem.checked === true;
+ },
+
+ selected: function( elem ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
+ return elem.selected === true;
+ },
+
+ parent: function( elem ) {
+ return !!elem.firstChild;
+ },
+
+ empty: function( elem ) {
+ return !elem.firstChild;
+ },
+
+ has: function( elem, i, match ) {
+ return !!Sizzle( match[3], elem ).length;
+ },
+
+ header: function( elem ) {
+ return (/h\d/i).test( elem.nodeName );
+ },
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text"
=== type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio"
=== elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" &&
"checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file"
=== elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" &&
"password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") &&
"submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image"
=== elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") &&
"reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name
=== "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ setFilters: {
+ first: function( elem, i ) {
+ return i === 0;
+ },
+
+ last: function( elem, i, match, array ) {
+ return i === array.length - 1;
+ },
+
+ even: function( elem, i ) {
+ return i % 2 === 0;
+ },
+
+ odd: function( elem, i ) {
+ return i % 2 === 1;
+ },
+
+ lt: function( elem, i, match ) {
+ return i < match[3] - 0;
+ },
+
+ gt: function( elem, i, match ) {
+ return i > match[3] - 0;
+ },
+
+ nth: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ },
+
+ eq: function( elem, i, match ) {
+ return match[3] - 0 === i;
+ }
+ },
+ filter: {
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+
+ } else if ( name === "contains" ) {
+ return (elem.textContent || elem.innerText || getText([ elem ]) ||
"").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ if ( type === "first" ) {
+ return true;
+ }
+
+ node = elem;
+
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ first = match[2];
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ doneName = match[0];
+ parent = elem.parentNode;
+
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent[ expando ] = doneName;
+ }
+
+ diff = elem.nodeIndex - last;
+
+ if ( first === 0 ) {
+ return diff === 0;
+
+ } else {
+ return ( diff % first === 0 && diff / first >= 0 );
+ }
+ }
+ },
+
+ ID: function( elem, match ) {
+ return elem.nodeType === 1 && elem.getAttribute("id") === match;
+ },
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || !!elem.nodeName
&& elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) +
" ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ Expr.attrHandle[ name ] ?
+ Expr.attrHandle[ name ]( elem ) :
+ elem[ name ] != null ?
+ elem[ name ] :
+ elem.getAttribute( name ),
+ value = result + "",
+ type = match[2],
+ check = match[4];
+
+ return result == null ?
+ type === "!=" :
+ !type && Sizzle.attr ?
+ result != null :
+ type === "=" ?
+ value === check :
+ type === "*=" ?
+ value.indexOf(check) >= 0 :
+ type === "~=" ?
+ (" " + value + " ").indexOf(check) >= 0 :
+ !check ?
+ value && result !== false :
+ type === "!=" ?
+ value !== check :
+ type === "^=" ?
+ value.indexOf(check) === 0 :
+ type === "$=" ?
+ value.substr(value.length - check.length) === check :
+ type === "|=" ?
+ value === check || value.substr(0, check.length + 1) === check + "-" :
+ false;
+ },
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
+
+ if ( filter ) {
+ return filter( elem, i, match, array );
+ }
+ }
+ }
+};
+
+var origPOS = Expr.match.POS,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+for ( var type in Expr.match ) {
+ Expr.match[ type ] = new RegExp( Expr.match[ type ].source +
(/(?![^\[]*\])(?![^\(]*\))/.source) );
+ Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type
].source.replace(/\\(\d+)/g, fescape) );
+}
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "<a name='" + id + "'/>";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ if ( document.getElementById( id ) ) {
+ Expr.find.ID = function( match, context, isXML ) {
+ if ( typeof context.getElementById !== "undefined" && !isXML ) {
+ var m = context.getElementById(match[1]);
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" &&
m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
+ }
+ };
+
+ Expr.filter.ID = function( elem, match ) {
+ var node = typeof elem.getAttributeNode !== "undefined" &&
elem.getAttributeNode("id");
+
+ return elem.nodeType === 1 && node && node.nodeValue === match;
+ };
+ }
+
+ root.removeChild( form );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ if ( match[1] === "*" ) {
+ var tmp = [];
+
+ for ( var i = 0; results[i]; i++ ) {
+ if ( results[i].nodeType === 1 ) {
+ tmp.push( results[i] );
+ }
+ }
+
+ results = tmp;
+ }
+
+ return results;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "<a href='#'></a>";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !==
"undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "<p class='TEST'></p>";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length ===
0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS &&
context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !==
"object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid +
"'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector
|| html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ),
"div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) )
{
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "<div class='test e'></div><div
class='test'></div>";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length ===
0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ div.lastChild.className = "e";
+
+ if ( div.getElementsByClassName("e").length === 1 ) {
+ return;
+ }
+
+ Expr.order.splice(1, 0, "CLASS");
+ Expr.find.CLASS = function( match, context, isXML ) {
+ if ( typeof context.getElementsByClassName !== "undefined" && !isXML
) {
+ return context.getElementsByClassName(match[1]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( typeof cur !== "string" ) {
+ if ( elem === cur ) {
+ match = true;
+ break;
+ }
+
+ } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
+ match = elem;
+ break;
+ }
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
+ later += match[0];
+ selector = selector.replace( Expr.match.PSEUDO, "" );
+ }
+
+ selector = Expr.relative[selector] ? selector + "*" : selector;
+
+ for ( var i = 0, l = root.length; i < l; i++ ) {
+ Sizzle( selector, root[i], tmpSet, seed );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.POS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc
with two "p".
+ POS.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array (deprecated as of jQuery 1.7)
+ if ( jQuery.isArray( selectors ) ) {
+ var level = 1;
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( i = 0; i < selectors.length; i++ ) {
+
+ if ( jQuery( cur ).is( selectors[ i ] ) ) {
+ ret.push({ selector: selectors[ i ], elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( elem.parentNode.firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) :
ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) &&
rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined ||
cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+
+
+
+
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames =
"abbr|article|aside|audio|canvas|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag =
/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /<tbody/i,
+ rhtml = /<|&#?\w+;/,
+ rnoInnerhtml = /<(?:script|style)/i,
+ rnocache = /<(?:script|object|embed|option|style)/i,
+ rnoshimcache = new RegExp("<(?:" + nodeNames + ")",
"i"),
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/,
+ wrapMap = {
+ option: [ 1, "<select multiple='multiple'>",
"</select>" ],
+ legend: [ 1, "<fieldset>", "</fieldset>" ],
+ thead: [ 1, "<table>", "</table>" ],
+ tr: [ 2, "<table><tbody>",
"</tbody></table>" ],
+ td: [ 3, "<table><tbody><tr>",
"</tr></tbody></table>" ],
+ col: [ 2, "<table><tbody></tbody><colgroup>",
"</colgroup></table>" ],
+ area: [ 1, "<map>", "</map>" ],
+ _default: [ 0, "", "" ]
+ },
+ safeFragment = createSafeFragment( document );
+
+wrapMap.optgroup = wrapMap.option;
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// IE can't serialize <link> and <script> tags normally
+if ( !jQuery.support.htmlSerialize ) {
+ wrapMap._default = [ 1, "div<div>", "</div>" ];
+}
+
+jQuery.fn.extend({
+ text: function( text ) {
+ if ( jQuery.isFunction(text) ) {
+ return this.each(function(i) {
+ var self = jQuery( this );
+
+ self.text( text.call(this, i, self.text()) );
+ });
+ }
+
+ if ( typeof text !== "object" && text !== undefined ) {
+ return this.empty().append( (this[0] && this[0].ownerDocument ||
document).createTextNode( text ) );
+ }
+
+ return jQuery.text( this );
+ },
+
+ wrapAll: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapAll( html.call(this, i) );
+ });
+ }
+
+ if ( this[0] ) {
+ // The elements to wrap the target around
+ var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
+
+ if ( this[0].parentNode ) {
+ wrap.insertBefore( this[0] );
+ }
+
+ wrap.map(function() {
+ var elem = this;
+
+ while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
+ elem = elem.firstChild;
+ }
+
+ return elem;
+ }).append( this );
+ }
+
+ return this;
+ },
+
+ wrapInner: function( html ) {
+ if ( jQuery.isFunction( html ) ) {
+ return this.each(function(i) {
+ jQuery(this).wrapInner( html.call(this, i) );
+ });
+ }
+
+ return this.each(function() {
+ var self = jQuery( this ),
+ contents = self.contents();
+
+ if ( contents.length ) {
+ contents.wrapAll( html );
+
+ } else {
+ self.append( html );
+ }
+ });
+ },
+
+ wrap: function( html ) {
+ var isFunction = jQuery.isFunction( html );
+
+ return this.each(function(i) {
+ jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+ });
+ },
+
+ unwrap: function() {
+ return this.parent().each(function() {
+ if ( !jQuery.nodeName( this, "body" ) ) {
+ jQuery( this ).replaceWith( this.childNodes );
+ }
+ }).end();
+ },
+
+ append: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.appendChild( elem );
+ }
+ });
+ },
+
+ prepend: function() {
+ return this.domManip(arguments, true, function( elem ) {
+ if ( this.nodeType === 1 ) {
+ this.insertBefore( elem, this.firstChild );
+ }
+ });
+ },
+
+ before: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this );
+ });
+ } else if ( arguments.length ) {
+ var set = jQuery.clean( arguments );
+ set.push.apply( set, this.toArray() );
+ return this.pushStack( set, "before", arguments );
+ }
+ },
+
+ after: function() {
+ if ( this[0] && this[0].parentNode ) {
+ return this.domManip(arguments, false, function( elem ) {
+ this.parentNode.insertBefore( elem, this.nextSibling );
+ });
+ } else if ( arguments.length ) {
+ var set = this.pushStack( this, "after", arguments );
+ set.push.apply( set, jQuery.clean(arguments) );
+ return set;
+ }
+ },
+
+ // keepData is for internal use only--do not document
+ remove: function( selector, keepData ) {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
+ if ( !keepData && elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ jQuery.cleanData( [ elem ] );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+ }
+ }
+
+ return this;
+ },
+
+ empty: function() {
+ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( elem.nodeType === 1 ) {
+ jQuery.cleanData( elem.getElementsByTagName("*") );
+ }
+
+ // Remove any remaining nodes
+ while ( elem.firstChild ) {
+ elem.removeChild( elem.firstChild );
+ }
+ }
+
+ return this;
+ },
+
+ clone: function( dataAndEvents, deepDataAndEvents ) {
+ dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+ deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+ return this.map( function () {
+ return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+ });
+ },
+
+ html: function( value ) {
+ if ( value === undefined ) {
+ return this[0] && this[0].nodeType === 1 ?
+ this[0].innerHTML.replace(rinlinejQuery, "") :
+ null;
+
+ // See if we can take a shortcut and just use innerHTML
+ } else if ( typeof value === "string" && !rnoInnerhtml.test( value )
&&
+ (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
+ !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ]
) {
+
+ value = value.replace(rxhtmlTag, "<$1></$2>");
+
+ try {
+ for ( var i = 0, l = this.length; i < l; i++ ) {
+ // Remove element nodes and prevent memory leaks
+ if ( this[i].nodeType === 1 ) {
+ jQuery.cleanData( this[i].getElementsByTagName("*") );
+ this[i].innerHTML = value;
+ }
+ }
+
+ // If using innerHTML throws an exception, use the fallback method
+ } catch(e) {
+ this.empty().append( value );
+ }
+
+ } else if ( jQuery.isFunction( value ) ) {
+ this.each(function(i){
+ var self = jQuery( this );
+
+ self.html( value.call(this, i, self.html()) );
+ });
+
+ } else {
+ this.empty().append( value );
+ }
+
+ return this;
+ },
+
+ replaceWith: function( value ) {
+ if ( this[0] && this[0].parentNode ) {
+ // Make sure that the elements are removed from the DOM before they are inserted
+ // this can help fix replacing a parent with child elements
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function(i) {
+ var self = jQuery(this), old = self.html();
+ self.replaceWith( value.call( this, i, old ) );
+ });
+ }
+
+ if ( typeof value !== "string" ) {
+ value = jQuery( value ).detach();
+ }
+
+ return this.each(function() {
+ var next = this.nextSibling,
+ parent = this.parentNode;
+
+ jQuery( this ).remove();
+
+ if ( next ) {
+ jQuery(next).before( value );
+ } else {
+ jQuery(parent).append( value );
+ }
+ });
+ } else {
+ return this.length ?
+ this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value),
"replaceWith", value ) :
+ this;
+ }
+ },
+
+ detach: function( selector ) {
+ return this.remove( selector, true );
+ },
+
+ domManip: function( args, table, callback ) {
+ var results, first, fragment, parent,
+ value = args[0],
+ scripts = [];
+
+ // We can't cloneNode fragments that contain checked, in WebKit
+ if ( !jQuery.support.checkClone && arguments.length === 3 && typeof
value === "string" && rchecked.test( value ) ) {
+ return this.each(function() {
+ jQuery(this).domManip( args, table, callback, true );
+ });
+ }
+
+ if ( jQuery.isFunction(value) ) {
+ return this.each(function(i) {
+ var self = jQuery(this);
+ args[0] = value.call(this, i, table ? self.html() : undefined);
+ self.domManip( args, table, callback );
+ });
+ }
+
+ if ( this[0] ) {
+ parent = value && value.parentNode;
+
+ // If we're in a fragment, just use that instead of building a new one
+ if ( jQuery.support.parentNode && parent && parent.nodeType === 11
&& parent.childNodes.length === this.length ) {
+ results = { fragment: parent };
+
+ } else {
+ results = jQuery.buildFragment( args, this, scripts );
+ }
+
+ fragment = results.fragment;
+
+ if ( fragment.childNodes.length === 1 ) {
+ first = fragment = fragment.firstChild;
+ } else {
+ first = fragment.firstChild;
+ }
+
+ if ( first ) {
+ table = table && jQuery.nodeName( first, "tr" );
+
+ for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) {
+ callback.call(
+ table ?
+ root(this[i], first) :
+ this[i],
+ // Make sure that we do not leak memory by inadvertently discarding
+ // the original fragment (which might have attached data) instead of
+ // using it; in addition, use the original fragment object for the last
+ // item instead of first because it can end up being emptied incorrectly
+ // in certain situations (Bug #8070).
+ // Fragments from the fragment cache must always be cloned and never used
+ // in place.
+ results.cacheable || ( l > 1 && i < lastIndex ) ?
+ jQuery.clone( fragment, true, true ) :
+ fragment
+ );
+ }
+ }
+
+ if ( scripts.length ) {
+ jQuery.each( scripts, evalScript );
+ }
+ }
+
+ return this;
+ }
+});
+
+function root( elem, cur ) {
+ return jQuery.nodeName(elem, "table") ?
+ (elem.getElementsByTagName("tbody")[0] ||
+ elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
+ elem;
+}
+
+function cloneCopyEvent( src, dest ) {
+
+ if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
+ return;
+ }
+
+ var type, i, l,
+ oldData = jQuery._data( src ),
+ curData = jQuery._data( dest, oldData ),
+ events = oldData.events;
+
+ if ( events ) {
+ delete curData.handle;
+ curData.events = {};
+
+ for ( type in events ) {
+ for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+ jQuery.event.add( dest, type + ( events[ type ][ i ].namespace ? "." :
"" ) + events[ type ][ i ].namespace, events[ type ][ i ], events[ type ][ i
].data );
+ }
+ }
+ }
+
+ // make the cloned public data object a copy from the original
+ if ( curData.data ) {
+ curData.data = jQuery.extend( {}, curData.data );
+ }
+}
+
+function cloneFixAttributes( src, dest ) {
+ var nodeName;
+
+ // We do not need to do anything for non-Elements
+ if ( dest.nodeType !== 1 ) {
+ return;
+ }
+
+ // clearAttributes removes the attributes, which we don't want,
+ // but also removes the attachEvent events, which we *do* want
+ if ( dest.clearAttributes ) {
+ dest.clearAttributes();
+ }
+
+ // mergeAttributes, in contrast, only merges back on the
+ // original attributes, not the events
+ if ( dest.mergeAttributes ) {
+ dest.mergeAttributes( src );
+ }
+
+ nodeName = dest.nodeName.toLowerCase();
+
+ // IE6-8 fail to clone children inside object elements that use
+ // the proprietary classid attribute value (rather than the type
+ // attribute) to identify the type of content to display
+ if ( nodeName === "object" ) {
+ dest.outerHTML = src.outerHTML;
+
+ } else if ( nodeName === "input" && (src.type === "checkbox"
|| src.type === "radio") ) {
+ // IE6-8 fails to persist the checked state of a cloned checkbox
+ // or radio button. Worse, IE6-7 fail to give the cloned element
+ // a checked appearance if the defaultChecked value isn't also set
+ if ( src.checked ) {
+ dest.defaultChecked = dest.checked = src.checked;
+ }
+
+ // IE6-7 get confused and end up setting the value of a cloned
+ // checkbox/radio button to an empty string instead of "on"
+ if ( dest.value !== src.value ) {
+ dest.value = src.value;
+ }
+
+ // IE6-8 fails to return the selected option to the default selected
+ // state when cloning options
+ } else if ( nodeName === "option" ) {
+ dest.selected = src.defaultSelected;
+
+ // IE6-8 fails to set the defaultValue to the correct value when
+ // cloning other types of input fields
+ } else if ( nodeName === "input" || nodeName === "textarea" ) {
+ dest.defaultValue = src.defaultValue;
+ }
+
+ // Event data gets referenced instead of copied if the expando
+ // gets copied too
+ dest.removeAttribute( jQuery.expando );
+}
+
+jQuery.buildFragment = function( args, nodes, scripts ) {
+ var fragment, cacheable, cacheresults, doc,
+ first = args[ 0 ];
+
+ // nodes may contain either an explicit document object,
+ // a jQuery collection or context object.
+ // If nodes[0] contains a valid object to assign to doc
+ if ( nodes && nodes[0] ) {
+ doc = nodes[0].ownerDocument || nodes[0];
+ }
+
+ // Ensure that an attr object doesn't incorrectly stand in as a document object
+ // Chrome and Firefox seem to allow this to occur and will throw exception
+ // Fixes #8950
+ if ( !doc.createDocumentFragment ) {
+ doc = document;
+ }
+
+ // Only cache "small" (1/2 KB) HTML strings that are associated with the main
document
+ // Cloning options loses the selected state, so don't cache them
+ // IE 6 doesn't like it when you put <object> or <embed> elements in a
fragment
+ // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't
cache
+ // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from
unknown elems #10501
+ if ( args.length === 1 && typeof first === "string" &&
first.length < 512 && doc === document &&
+ first.charAt(0) === "<" && !rnocache.test( first ) &&
+ (jQuery.support.checkClone || !rchecked.test( first )) &&
+ (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
+
+ cacheable = true;
+
+ cacheresults = jQuery.fragments[ first ];
+ if ( cacheresults && cacheresults !== 1 ) {
+ fragment = cacheresults;
+ }
+ }
+
+ if ( !fragment ) {
+ fragment = doc.createDocumentFragment();
+ jQuery.clean( args, doc, fragment, scripts );
+ }
+
+ if ( cacheable ) {
+ jQuery.fragments[ first ] = cacheresults ? fragment : 1;
+ }
+
+ return { fragment: fragment, cacheable: cacheable };
+};
+
+jQuery.fragments = {};
+
+jQuery.each({
+ appendTo: "append",
+ prependTo: "prepend",
+ insertBefore: "before",
+ insertAfter: "after",
+ replaceAll: "replaceWith"
+}, function( name, original ) {
+ jQuery.fn[ name ] = function( selector ) {
+ var ret = [],
+ insert = jQuery( selector ),
+ parent = this.length === 1 && this[0].parentNode;
+
+ if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1
&& insert.length === 1 ) {
+ insert[ original ]( this[0] );
+ return this;
+
+ } else {
+ for ( var i = 0, l = insert.length; i < l; i++ ) {
+ var elems = ( i > 0 ? this.clone(true) : this ).get();
+ jQuery( insert[i] )[ original ]( elems );
+ ret = ret.concat( elems );
+ }
+
+ return this.pushStack( ret, name, insert.selector );
+ }
+ };
+});
+
+function getAll( elem ) {
+ if ( typeof elem.getElementsByTagName !== "undefined" ) {
+ return elem.getElementsByTagName( "*" );
+
+ } else if ( typeof elem.querySelectorAll !== "undefined" ) {
+ return elem.querySelectorAll( "*" );
+
+ } else {
+ return [];
+ }
+}
+
+// Used in clean, fixes the defaultChecked property
+function fixDefaultChecked( elem ) {
+ if ( elem.type === "checkbox" || elem.type === "radio" ) {
+ elem.defaultChecked = elem.checked;
+ }
+}
+// Finds all inputs and passes them to fixDefaultChecked
+function findInputs( elem ) {
+ var nodeName = ( elem.nodeName || "" ).toLowerCase();
+ if ( nodeName === "input" ) {
+ fixDefaultChecked( elem );
+ // Skip scripts, get other children
+ } else if ( nodeName !== "script" && typeof elem.getElementsByTagName
!== "undefined" ) {
+ jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
+ }
+}
+
+// Derived From:
http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js
+function shimCloneNode( elem ) {
+ var div = document.createElement( "div" );
+ safeFragment.appendChild( div );
+
+ div.innerHTML = elem.outerHTML;
+ return div.firstChild;
+}
+
+jQuery.extend({
+ clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+ var srcElements,
+ destElements,
+ i,
+ // IE<=8 does not properly clone detached, unknown element nodes
+ clone = jQuery.support.html5Clone || !rnoshimcache.test( "<" +
elem.nodeName ) ?
+ elem.cloneNode( true ) :
+ shimCloneNode( elem );
+
+ if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
+ (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
+ // IE copies events bound via attachEvent when using cloneNode.
+ // Calling detachEvent on the clone will also remove the events
+ // from the original. In order to get around this, we use some
+ // proprietary methods to clear the events. Thanks to MooTools
+ // guys for this hotness.
+
+ cloneFixAttributes( elem, clone );
+
+ // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ // Weird iteration because IE will replace the length property
+ // with an element if you are cloning the body and one of the
+ // elements on the page has a name or id of "length"
+ for ( i = 0; srcElements[i]; ++i ) {
+ // Ensure that the destination node is not null; Fixes #9587
+ if ( destElements[i] ) {
+ cloneFixAttributes( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ // Copy the events from the original to the clone
+ if ( dataAndEvents ) {
+ cloneCopyEvent( elem, clone );
+
+ if ( deepDataAndEvents ) {
+ srcElements = getAll( elem );
+ destElements = getAll( clone );
+
+ for ( i = 0; srcElements[i]; ++i ) {
+ cloneCopyEvent( srcElements[i], destElements[i] );
+ }
+ }
+ }
+
+ srcElements = destElements = null;
+
+ // Return the cloned set
+ return clone;
+ },
+
+ clean: function( elems, context, fragment, scripts ) {
+ var checkScriptType;
+
+ context = context || document;
+
+ // !context.createElement fails in IE with an error but returns typeof
'object'
+ if ( typeof context.createElement === "undefined" ) {
+ context = context.ownerDocument || context[0] && context[0].ownerDocument ||
document;
+ }
+
+ var ret = [], j;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( typeof elem === "number" ) {
+ elem += "";
+ }
+
+ if ( !elem ) {
+ continue;
+ }
+
+ // Convert html string into DOM nodes
+ if ( typeof elem === "string" ) {
+ if ( !rhtml.test( elem ) ) {
+ elem = context.createTextNode( elem );
+ } else {
+ // Fix "XHTML"-style tags in all browsers
+ elem = elem.replace(rxhtmlTag, "<$1></$2>");
+
+ // Trim whitespace, otherwise indexOf won't work as expected
+ var tag = ( rtagName.exec( elem ) || ["", ""]
)[1].toLowerCase(),
+ wrap = wrapMap[ tag ] || wrapMap._default,
+ depth = wrap[0],
+ div = context.createElement("div");
+
+ // Append wrapper element to unknown element safe doc fragment
+ if ( context === document ) {
+ // Use the fragment we've already created for this document
+ safeFragment.appendChild( div );
+ } else {
+ // Use a fragment created with the owner document
+ createSafeFragment( context ).appendChild( div );
+ }
+
+ // Go to html and back, then peel off extra wrappers
+ div.innerHTML = wrap[1] + elem + wrap[2];
+
+ // Move to the right depth
+ while ( depth-- ) {
+ div = div.lastChild;
+ }
+
+ // Remove IE's autoinserted <tbody> from table fragments
+ if ( !jQuery.support.tbody ) {
+
+ // String was a <table>, *may* have spurious <tbody>
+ var hasBody = rtbody.test(elem),
+ tbody = tag === "table" && !hasBody ?
+ div.firstChild && div.firstChild.childNodes :
+
+ // String was a bare <thead> or <tfoot>
+ wrap[1] === "<table>" && !hasBody ?
+ div.childNodes :
+ [];
+
+ for ( j = tbody.length - 1; j >= 0 ; --j ) {
+ if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j
].childNodes.length ) {
+ tbody[ j ].parentNode.removeChild( tbody[ j ] );
+ }
+ }
+ }
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) )
{
+ div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ),
div.firstChild );
+ }
+
+ elem = div.childNodes;
+ }
+ }
+
+ // Resets defaultChecked for any radios and checkboxes
+ // about to be appended to the DOM in IE 6/7 (#8060)
+ var len;
+ if ( !jQuery.support.appendChecked ) {
+ if ( elem[0] && typeof (len = elem.length) === "number" ) {
+ for ( j = 0; j < len; j++ ) {
+ findInputs( elem[j] );
+ }
+ } else {
+ findInputs( elem );
+ }
+ }
+
+ if ( elem.nodeType ) {
+ ret.push( elem );
+ } else {
+ ret = jQuery.merge( ret, elem );
+ }
+ }
+
+ if ( fragment ) {
+ checkScriptType = function( elem ) {
+ return !elem.type || rscriptType.test( elem.type );
+ };
+ for ( i = 0; ret[i]; i++ ) {
+ if ( scripts && jQuery.nodeName( ret[i], "script" ) &&
(!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
+ scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i]
);
+
+ } else {
+ if ( ret[i].nodeType === 1 ) {
+ var jsTags = jQuery.grep( ret[i].getElementsByTagName( "script" ),
checkScriptType );
+
+ ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
+ }
+ fragment.appendChild( ret[i] );
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ cleanData: function( elems ) {
+ var data, id,
+ cache = jQuery.cache,
+ special = jQuery.event.special,
+ deleteExpando = jQuery.support.deleteExpando;
+
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
+ continue;
+ }
+
+ id = elem[ jQuery.expando ];
+
+ if ( id ) {
+ data = cache[ id ];
+
+ if ( data && data.events ) {
+ for ( var type in data.events ) {
+ if ( special[ type ] ) {
+ jQuery.event.remove( elem, type );
+
+ // This is a shortcut to avoid jQuery.event.remove's overhead
+ } else {
+ jQuery.removeEvent( elem, type, data.handle );
+ }
+ }
+
+ // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+ if ( data.handle ) {
+ data.handle.elem = null;
+ }
+ }
+
+ if ( deleteExpando ) {
+ delete elem[ jQuery.expando ];
+
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( jQuery.expando );
+ }
+
+ delete cache[ id ];
+ }
+ }
+ }
+});
+
+function evalScript( i, elem ) {
+ if ( elem.src ) {
+ jQuery.ajax({
+ url: elem.src,
+ async: false,
+ dataType: "script"
+ });
+ } else {
+ jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || ""
).replace( rcleanScript, "/*$0*/" ) );
+ }
+
+ if ( elem.parentNode ) {
+ elem.parentNode.removeChild( elem );
+ }
+}
+
+
+
+
+var ralpha = /alpha\([^)]*\)/i,
+ ropacity = /opacity=([^)]*)/,
+ // fixed for IE9, see #8346
+ rupper = /([A-Z]|^ms)/g,
+ rnumpx = /^-?\d+(?:px)?$/i,
+ rnum = /^-?\d/,
+ rrelNum = /^([\-+])=([\-+.\de]+)/,
+
+ cssShow = { position: "absolute", visibility: "hidden", display:
"block" },
+ cssWidth = [ "Left", "Right" ],
+ cssHeight = [ "Top", "Bottom" ],
+ curCSS,
+
+ getComputedStyle,
+ currentStyle;
+
+jQuery.fn.css = function( name, value ) {
+ // Setting 'undefined' is a no-op
+ if ( arguments.length === 2 && value === undefined ) {
+ return this;
+ }
+
+ return jQuery.access( this, name, value, true, function( elem, name, value ) {
+ return value !== undefined ?
+ jQuery.style( elem, name, value ) :
+ jQuery.css( elem, name );
+ });
+};
+
+jQuery.extend({
+ // Add in style property hooks for overriding the default
+ // behavior of getting and setting a style property
+ cssHooks: {
+ opacity: {
+ get: function( elem, computed ) {
+ if ( computed ) {
+ // We should always get a number back from opacity
+ var ret = curCSS( elem, "opacity", "opacity" );
+ return ret === "" ? "1" : ret;
+
+ } else {
+ return elem.style.opacity;
+ }
+ }
+ }
+ },
+
+ // Exclude the following css properties to add px
+ cssNumber: {
+ "fillOpacity": true,
+ "fontWeight": true,
+ "lineHeight": true,
+ "opacity": true,
+ "orphans": true,
+ "widows": true,
+ "zIndex": true,
+ "zoom": true
+ },
+
+ // Add in properties whose names you wish to fix before
+ // setting or getting the value
+ cssProps: {
+ // normalize float css property
+ "float": jQuery.support.cssFloat ? "cssFloat" :
"styleFloat"
+ },
+
+ // Get and set the style property on a DOM Node
+ style: function( elem, name, value, extra ) {
+ // Don't set styles on text and comment nodes
+ if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+ return;
+ }
+
+ // Make sure that we're working with the right name
+ var ret, type, origName = jQuery.camelCase( name ),
+ style = elem.style, hooks = jQuery.cssHooks[ origName ];
+
+ name = jQuery.cssProps[ origName ] || origName;
+
+ // Check if we're setting a value
+ if ( value !== undefined ) {
+ type = typeof value;
+
+ // convert relative number strings (+= or -=) to relative numbers. #7345
+ if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+ value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) );
+ // Fixes bug #9237
+ type = "number";
+ }
+
+ // Make sure that NaN and null values aren't set. See: #7116
+ if ( value == null || type === "number" && isNaN( value ) ) {
+ return;
+ }
+
+ // If a number was passed in, add 'px' to the (except for certain CSS
properties)
+ if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+ value += "px";
+ }
+
+ // If a hook was provided, use that value, otherwise just set the specified value
+ if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !==
undefined ) {
+ // Wrapped to prevent IE from throwing errors when 'invalid' values are
provided
+ // Fixes bug #5509
+ try {
+ style[ name ] = value;
+ } catch(e) {}
+ }
+
+ } else {
+ // If a hook was provided get the non-computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem,
false, extra )) !== undefined ) {
+ return ret;
+ }
+
+ // Otherwise just get the value from the style object
+ return style[ name ];
+ }
+ },
+
+ css: function( elem, name, extra ) {
+ var ret, hooks;
+
+ // Make sure that we're working with the right name
+ name = jQuery.camelCase( name );
+ hooks = jQuery.cssHooks[ name ];
+ name = jQuery.cssProps[ name ] || name;
+
+ // cssFloat needs a special treatment
+ if ( name === "cssFloat" ) {
+ name = "float";
+ }
+
+ // If a hook was provided get the computed value from there
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, true,
extra )) !== undefined ) {
+ return ret;
+
+ // Otherwise, if a way to get the computed value exists, use that
+ } else if ( curCSS ) {
+ return curCSS( elem, name );
+ }
+ },
+
+ // A method for quickly swapping in/out CSS properties to get correct calculations
+ swap: function( elem, options, callback ) {
+ var old = {};
+
+ // Remember the old values, and insert the new ones
+ for ( var name in options ) {
+ old[ name ] = elem.style[ name ];
+ elem.style[ name ] = options[ name ];
+ }
+
+ callback.call( elem );
+
+ // Revert the old values
+ for ( name in options ) {
+ elem.style[ name ] = old[ name ];
+ }
+ }
+});
+
+// DEPRECATED, Use jQuery.css() instead
+jQuery.curCSS = jQuery.css;
+
+jQuery.each(["height", "width"], function( i, name ) {
+ jQuery.cssHooks[ name ] = {
+ get: function( elem, computed, extra ) {
+ var val;
+
+ if ( computed ) {
+ if ( elem.offsetWidth !== 0 ) {
+ return getWH( elem, name, extra );
+ } else {
+ jQuery.swap( elem, cssShow, function() {
+ val = getWH( elem, name, extra );
+ });
+ }
+
+ return val;
+ }
+ },
+
+ set: function( elem, value ) {
+ if ( rnumpx.test( value ) ) {
+ // ignore negative width and height values #1599
+ value = parseFloat( value );
+
+ if ( value >= 0 ) {
+ return value + "px";
+ }
+
+ } else {
+ return value;
+ }
+ }
+ };
+});
+
+if ( !jQuery.support.opacity ) {
+ jQuery.cssHooks.opacity = {
+ get: function( elem, computed ) {
+ // IE uses filters for opacity
+ return ropacity.test( (computed && elem.currentStyle ?
elem.currentStyle.filter : elem.style.filter) || "" ) ?
+ ( parseFloat( RegExp.$1 ) / 100 ) + "" :
+ computed ? "1" : "";
+ },
+
+ set: function( elem, value ) {
+ var style = elem.style,
+ currentStyle = elem.currentStyle,
+ opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 +
")" : "",
+ filter = currentStyle && currentStyle.filter || style.filter ||
"";
+
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ style.zoom = 1;
+
+ // if setting opacity to 1, and no other filters exist - attempt to remove filter
attribute #6652
+ if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) )
=== "" ) {
+
+ // Setting style.filter to null, "" & " " still leave
"filter:" in the cssText
+ // if "filter:" is present at all, clearType is disabled, we want to avoid
this
+ // style.removeAttribute is IE Only, but so apparently is this code path...
+ style.removeAttribute( "filter" );
+
+ // if there there is no filter style applied in a css rule, we are done
+ if ( currentStyle && !currentStyle.filter ) {
+ return;
+ }
+ }
+
+ // otherwise, set new filter values
+ style.filter = ralpha.test( filter ) ?
+ filter.replace( ralpha, opacity ) :
+ filter + " " + opacity;
+ }
+ };
+}
+
+jQuery(function() {
+ // This hook cannot be added until DOM ready because the support test
+ // for it is not run until after DOM ready
+ if ( !jQuery.support.reliableMarginRight ) {
+ jQuery.cssHooks.marginRight = {
+ get: function( elem, computed ) {
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ // Work around by temporarily setting element display to inline-block
+ var ret;
+ jQuery.swap( elem, { "display": "inline-block" }, function() {
+ if ( computed ) {
+ ret = curCSS( elem, "margin-right", "marginRight" );
+ } else {
+ ret = elem.style.marginRight;
+ }
+ });
+ return ret;
+ }
+ };
+ }
+});
+
+if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ getComputedStyle = function( elem, name ) {
+ var ret, defaultView, computedStyle;
+
+ name = name.replace( rupper, "-$1" ).toLowerCase();
+
+ if ( (defaultView = elem.ownerDocument.defaultView) &&
+ (computedStyle = defaultView.getComputedStyle( elem, null )) ) {
+ ret = computedStyle.getPropertyValue( name );
+ if ( ret === "" && !jQuery.contains(
elem.ownerDocument.documentElement, elem ) ) {
+ ret = jQuery.style( elem, name );
+ }
+ }
+
+ return ret;
+ };
+}
+
+if ( document.documentElement.currentStyle ) {
+ currentStyle = function( elem, name ) {
+ var left, rsLeft, uncomputed,
+ ret = elem.currentStyle && elem.currentStyle[ name ],
+ style = elem.style;
+
+ // Avoid setting ret to empty string here
+ // so we don't default to auto
+ if ( ret === null && style && (uncomputed = style[ name ]) ) {
+ ret = uncomputed;
+ }
+
+ // From the awesome hack by Dean Edwards
+ //
http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
+
+ // If we're not dealing with a regular pixel number
+ // but a number that has a weird ending, we need to convert it to pixels
+ if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
+
+ // Remember the original values
+ left = style.left;
+ rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
+
+ // Put in the new values to get a computed value out
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = elem.currentStyle.left;
+ }
+ style.left = name === "fontSize" ? "1em" : ( ret || 0 );
+ ret = style.pixelLeft + "px";
+
+ // Revert the changed values
+ style.left = left;
+ if ( rsLeft ) {
+ elem.runtimeStyle.left = rsLeft;
+ }
+ }
+
+ return ret === "" ? "auto" : ret;
+ };
+}
+
+curCSS = getComputedStyle || currentStyle;
+
+function getWH( elem, name, extra ) {
+
+ // Start with offset property
+ var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+ which = name === "width" ? cssWidth : cssHeight,
+ i = 0,
+ len = which.length;
+
+ if ( val > 0 ) {
+ if ( extra !== "border" ) {
+ for ( ; i < len; i++ ) {
+ if ( !extra ) {
+ val -= parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
+ } else {
+ val -= parseFloat( jQuery.css( elem, "border" + which[ i ] +
"Width" ) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+ }
+
+ // Fall back to computed then uncomputed css if necessary
+ val = curCSS( elem, name, name );
+ if ( val < 0 || val == null ) {
+ val = elem.style[ name ] || 0;
+ }
+ // Normalize "", auto, and prepare for extra
+ val = parseFloat( val ) || 0;
+
+ // Add padding, border, margin
+ if ( extra ) {
+ for ( ; i < len; i++ ) {
+ val += parseFloat( jQuery.css( elem, "padding" + which[ i ] ) ) || 0;
+ if ( extra !== "padding" ) {
+ val += parseFloat( jQuery.css( elem, "border" + which[ i ] +
"Width" ) ) || 0;
+ }
+ if ( extra === "margin" ) {
+ val += parseFloat( jQuery.css( elem, extra + which[ i ] ) ) || 0;
+ }
+ }
+ }
+
+ return val + "px";
+}
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.hidden = function( elem ) {
+ var width = elem.offsetWidth,
+ height = elem.offsetHeight;
+
+ return ( width === 0 && height === 0 ) ||
(!jQuery.support.reliableHiddenOffsets && ((elem.style &&
elem.style.display) || jQuery.css( elem, "display" )) === "none");
+ };
+
+ jQuery.expr.filters.visible = function( elem ) {
+ return !jQuery.expr.filters.hidden( elem );
+ };
+}
+
+
+
+
+var r20 = /%20/g,
+ rbracket = /\[\]$/,
+ rCRLF = /\r?\n/g,
+ rhash = /#.*$/,
+ rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
+ rinput =
/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+ // #7653, #8125, #8152: local protocol detection
+ rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
+ rnoContent = /^(?:GET|HEAD)$/,
+ rprotocol = /^\/\//,
+ rquery = /\?/,
+ rscript =
/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+ rselectTextarea = /^(?:select|textarea)/i,
+ rspacesAjax = /\s+/,
+ rts = /([?&])_=[^&]*/,
+ rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,
+
+ // Keep a copy of the old load method
+ _load = jQuery.fn.load,
+
+ /* Prefilters
+ * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+ * 2) These are called:
+ * - BEFORE asking for a transport
+ * - AFTER param serialization (s.data is a string if s.processData is true)
+ * 3) key is the dataType
+ * 4) the catchall symbol "*" can be used
+ * 5) execution will start with transport dataType and THEN continue down to
"*" if needed
+ */
+ prefilters = {},
+
+ /* Transports bindings
+ * 1) key is the dataType
+ * 2) the catchall symbol "*" can be used
+ * 3) selection will start with transport dataType and THEN go to "*" if
needed
+ */
+ transports = {},
+
+ // Document location
+ ajaxLocation,
+
+ // Document location segments
+ ajaxLocParts,
+
+ // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+ allTypes = ["*/"] + ["*"];
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+ ajaxLocation = location.href;
+} catch( e ) {
+ // Use the href attribute of an A element
+ // since IE will modify it given document.location
+ ajaxLocation = document.createElement( "a" );
+ ajaxLocation.href = "";
+ ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+ // dataTypeExpression is optional and defaults to "*"
+ return function( dataTypeExpression, func ) {
+
+ if ( typeof dataTypeExpression !== "string" ) {
+ func = dataTypeExpression;
+ dataTypeExpression = "*";
+ }
+
+ if ( jQuery.isFunction( func ) ) {
+ var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ),
+ i = 0,
+ length = dataTypes.length,
+ dataType,
+ list,
+ placeBefore;
+
+ // For each dataType in the dataTypeExpression
+ for ( ; i < length; i++ ) {
+ dataType = dataTypes[ i ];
+ // We control if we're asked to add before
+ // any existing element
+ placeBefore = /^\+/.test( dataType );
+ if ( placeBefore ) {
+ dataType = dataType.substr( 1 ) || "*";
+ }
+ list = structure[ dataType ] = structure[ dataType ] || [];
+ // then we add to the structure accordingly
+ list[ placeBefore ? "unshift" : "push" ]( func );
+ }
+ }
+ };
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
+ dataType /* internal */, inspected /* internal */ ) {
+
+ dataType = dataType || options.dataTypes[ 0 ];
+ inspected = inspected || {};
+
+ inspected[ dataType ] = true;
+
+ var list = structure[ dataType ],
+ i = 0,
+ length = list ? list.length : 0,
+ executeOnly = ( structure === prefilters ),
+ selection;
+
+ for ( ; i < length && ( executeOnly || !selection ); i++ ) {
+ selection = list[ i ]( options, originalOptions, jqXHR );
+ // If we got redirected to another dataType
+ // we try there if executing only and not done already
+ if ( typeof selection === "string" ) {
+ if ( !executeOnly || inspected[ selection ] ) {
+ selection = undefined;
+ } else {
+ options.dataTypes.unshift( selection );
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, selection, inspected );
+ }
+ }
+ }
+ // If we're only executing or nothing was selected
+ // we try the catchall dataType if not done already
+ if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
+ selection = inspectPrefiltersOrTransports(
+ structure, options, originalOptions, jqXHR, "*", inspected );
+ }
+ // unnecessary when only executing (prefilters)
+ // but it'll be ignored by the caller in that case
+ return selection;
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+ var key, deep,
+ flatOptions = jQuery.ajaxSettings.flatOptions || {};
+ for ( key in src ) {
+ if ( src[ key ] !== undefined ) {
+ ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
+ }
+ }
+ if ( deep ) {
+ jQuery.extend( true, target, deep );
+ }
+}
+
+jQuery.fn.extend({
+ load: function( url, params, callback ) {
+ if ( typeof url !== "string" && _load ) {
+ return _load.apply( this, arguments );
+
+ // Don't do a request if no elements are being requested
+ } else if ( !this.length ) {
+ return this;
+ }
+
+ var off = url.indexOf( " " );
+ if ( off >= 0 ) {
+ var selector = url.slice( off, url.length );
+ url = url.slice( 0, off );
+ }
+
+ // Default to a GET request
+ var type = "GET";
+
+ // If the second parameter was provided
+ if ( params ) {
+ // If it's a function
+ if ( jQuery.isFunction( params ) ) {
+ // We assume that it's the callback
+ callback = params;
+ params = undefined;
+
+ // Otherwise, build a param string
+ } else if ( typeof params === "object" ) {
+ params = jQuery.param( params, jQuery.ajaxSettings.traditional );
+ type = "POST";
+ }
+ }
+
+ var self = this;
+
+ // Request the remote document
+ jQuery.ajax({
+ url: url,
+ type: type,
+ dataType: "html",
+ data: params,
+ // Complete callback (responseText is used internally)
+ complete: function( jqXHR, status, responseText ) {
+ // Store the response as specified by the jqXHR object
+ responseText = jqXHR.responseText;
+ // If successful, inject the HTML into all the matched elements
+ if ( jqXHR.isResolved() ) {
+ // #4825: Get the actual response in case
+ // a dataFilter is present in ajaxSettings
+ jqXHR.done(function( r ) {
+ responseText = r;
+ });
+ // See if a selector was specified
+ self.html( selector ?
+ // Create a dummy div to hold the results
+ jQuery("<div>")
+ // inject the contents of the document in, removing the scripts
+ // to avoid any 'Permission Denied' errors in IE
+ .append(responseText.replace(rscript, ""))
+
+ // Locate the specified elements
+ .find(selector) :
+
+ // If not, just inject the full result
+ responseText );
+ }
+
+ if ( callback ) {
+ self.each( callback, [ responseText, status, jqXHR ] );
+ }
+ }
+ });
+
+ return this;
+ },
+
+ serialize: function() {
+ return jQuery.param( this.serializeArray() );
+ },
+
+ serializeArray: function() {
+ return this.map(function(){
+ return this.elements ? jQuery.makeArray( this.elements ) : this;
+ })
+ .filter(function(){
+ return this.name && !this.disabled &&
+ ( this.checked || rselectTextarea.test( this.nodeName ) ||
+ rinput.test( this.type ) );
+ })
+ .map(function( i, elem ){
+ var val = jQuery( this ).val();
+
+ return val == null ?
+ null :
+ jQuery.isArray( val ) ?
+ jQuery.map( val, function( val, i ){
+ return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }) :
+ { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+ }).get();
+ }
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess
ajaxSend".split( " " ), function( i, o ){
+ jQuery.fn[ o ] = function( f ){
+ return this.on( o, f );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ type: method,
+ url: url,
+ data: data,
+ success: callback,
+ dataType: type
+ });
+ };
+});
+
+jQuery.extend({
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ if ( settings ) {
+ // Building a settings object
+ ajaxExtend( target, jQuery.ajaxSettings );
+ } else {
+ // Extending ajaxSettings
+ settings = target;
+ target = jQuery.ajaxSettings;
+ }
+ ajaxExtend( target, settings );
+ return target;
+ },
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ type: "GET",
+ contentType: "application/x-www-form-urlencoded",
+ processData: true,
+ async: true,
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ xml: "application/xml, text/xml",
+ html: "text/html",
+ text: "text/plain",
+ json: "application/json, text/javascript",
+ "*": allTypes
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // List of data converters
+ // 1) key format is "source_type destination_type" (a single space
in-between)
+ // 2) the catchall symbol "*" can be used for source_type
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ context: true,
+ url: true
+ }
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events
+ // It's the callbackContext if one was provided in the options
+ // and if it's a DOM node or a jQuery collection
+ globalEventContext = callbackContext !== s &&
+ ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
+ jQuery( callbackContext ) : jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks( "once memory" ),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // ifModified key
+ ifModifiedKey,
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // Response headers
+ responseHeadersString,
+ responseHeaders,
+ // transport
+ transport,
+ // timeout handle
+ timeoutTimer,
+ // Cross-domain detection vars
+ parts,
+ // The jqXHR state
+ state = 0,
+ // To know if global events are to be dispatched
+ fireGlobals,
+ // Loop variable
+ i,
+ // Fake xhr
+ jqXHR = {
+
+ readyState: 0,
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ if ( !state ) {
+ var lname = name.toLowerCase();
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match === undefined ? null : match;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ statusText = statusText || "abort";
+ if ( transport ) {
+ transport.abort( statusText );
+ }
+ done( 0, statusText );
+ return this;
+ }
+ };
+
+ // Callback for when everything is done
+ // It is defined here because jslint complains if it is declared
+ // at the end of the function (which would be more logical and readable)
+ function done( status, nativeStatusText, responses, headers ) {
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ var isSuccess,
+ success,
+ error,
+ statusText = nativeStatusText,
+ response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined,
+ lastModified,
+ etag;
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+
+ if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) {
+ jQuery.lastModified[ ifModifiedKey ] = lastModified;
+ }
+ if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) {
+ jQuery.etag[ ifModifiedKey ] = etag;
+ }
+ }
+
+ // If not modified
+ if ( status === 304 ) {
+
+ statusText = "notmodified";
+ isSuccess = true;
+
+ // If we have data
+ } else {
+
+ try {
+ success = ajaxConvert( s, response );
+ statusText = "success";
+ isSuccess = true;
+ } catch(e) {
+ // We have a parsererror
+ statusText = "parsererror";
+ error = e;
+ }
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( !statusText || status ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = "" + ( nativeStatusText || statusText );
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" :
"Error" ),
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger( "ajaxStop" );
+ }
+ }
+ }
+
+ // Attach deferreds
+ deferred.promise( jqXHR );
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+ jqXHR.complete = completeDeferred.add;
+
+ // Status-dependent callbacks
+ jqXHR.statusCode = function( map ) {
+ if ( map ) {
+ var tmp;
+ if ( state < 2 ) {
+ for ( tmp in map ) {
+ statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
+ }
+ } else {
+ tmp = map[ jqXHR.status ];
+ jqXHR.then( tmp, tmp );
+ }
+ }
+ return this;
+ };
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace(
rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split(
rspacesAjax );
+
+ // Determine if a cross-domain request is in order
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" )
{
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefiler, stop there
+ if ( state === 2 ) {
+ return false;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger( "ajaxStart" );
+ }
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Get ifModifiedKey before adding the anti-cache parameter
+ ifModifiedKey = s.url;
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+
+ var ts = jQuery.now(),
+ // try replacing _= if it is there
+ ret = s.url.replace( rts, "$1_=" + ts );
+
+ // if nothing was replaced, add timestamp to the end
+ s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" :
"?" ) + "_=" + ts : "" );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false ||
options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ ifModifiedKey = ifModifiedKey || s.url;
+ if ( jQuery.lastModified[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[
ifModifiedKey ] );
+ }
+ if ( jQuery.etag[ ifModifiedKey ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
+ }
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " +
allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false
|| state === 2 ) ) {
+ // Abort if not done already
+ jqXHR.abort();
+ return false;
+
+ }
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout( function(){
+ jqXHR.abort( "timeout" );
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch (e) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ // Serialize an array of form elements or a set of
+ // key/values into a query string
+ param: function( a, traditional ) {
+ var s = [],
+ add = function( key, value ) {
+ // If value is a function, invoke it and return its value
+ value = jQuery.isFunction( value ) ? value() : value;
+ s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value
);
+ };
+
+ // Set traditional to true for jQuery <= 1.3.2 behavior.
+ if ( traditional === undefined ) {
+ traditional = jQuery.ajaxSettings.traditional;
+ }
+
+ // If an array was passed in, assume that it is an array of form elements.
+ if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+ // Serialize the form elements
+ jQuery.each( a, function() {
+ add( this.name, this.value );
+ });
+
+ } else {
+ // If traditional, encode the "old" way (the way 1.3.2 or older
+ // did it), otherwise encode params recursively.
+ for ( var prefix in a ) {
+ buildParams( prefix, a[ prefix ], traditional, add );
+ }
+ }
+
+ // Return the resulting serialization
+ return s.join( "&" ).replace( r20, "+" );
+ }
+});
+
+function buildParams( prefix, obj, traditional, add ) {
+ if ( jQuery.isArray( obj ) ) {
+ // Serialize array item.
+ jQuery.each( obj, function( i, v ) {
+ if ( traditional || rbracket.test( prefix ) ) {
+ // Treat each array item as a scalar.
+ add( prefix, v );
+
+ } else {
+ // If array item is non-scalar (array or object), encode its
+ // numeric index to resolve deserialization ambiguity issues.
+ // Note that rack (as of 1.0.0) can't currently deserialize
+ // nested arrays properly, and attempting to do so may cause
+ // a server error. Possible fixes are to modify rack's
+ // deserialization algorithm or to provide an option or flag
+ // to force array serialization to be shallow.
+ buildParams( prefix + "[" + ( typeof v === "object" ||
jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
+ }
+ });
+
+ } else if ( !traditional && obj != null && typeof obj ===
"object" ) {
+ // Serialize object item.
+ for ( var name in obj ) {
+ buildParams( prefix + "[" + name + "]", obj[ name ], traditional,
add );
+ }
+
+ } else {
+ // Serialize scalar item.
+ add( prefix, obj );
+ }
+}
+
+// This is still on the jQuery object... for now
+// Want to move this to jQuery.ajax some day
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {}
+
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+ var contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields,
+ ct,
+ type,
+ finalDataType,
+ firstDataType;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ var dataTypes = s.dataTypes,
+ converters = {},
+ i,
+ key,
+ length = dataTypes.length,
+ tmp,
+ // Current and previous dataTypes
+ current = dataTypes[ 0 ],
+ prev,
+ // Conversion expression
+ conversion,
+ // Conversion function
+ conv,
+ // Conversion functions (transitive conversion)
+ conv1,
+ conv2;
+
+ // For each dataType in the chain
+ for ( i = 1; i < length; i++ ) {
+
+ // Create converters map
+ // with lowercased keys
+ if ( i === 1 ) {
+ for ( key in s.converters ) {
+ if ( typeof key === "string" ) {
+ converters[ key.toLowerCase() ] = s.converters[ key ];
+ }
+ }
+ }
+
+ // Get the dataTypes
+ prev = current;
+ current = dataTypes[ i ];
+
+ // If current is auto dataType, update it to prev
+ if ( current === "*" ) {
+ current = prev;
+ // If no auto and dataTypes are actually different
+ } else if ( prev !== "*" && prev !== current ) {
+
+ // Get the converter
+ conversion = prev + " " + current;
+ conv = converters[ conversion ] || converters[ "* " + current ];
+
+ // If there is no direct converter, search transitively
+ if ( !conv ) {
+ conv2 = undefined;
+ for ( conv1 in converters ) {
+ tmp = conv1.split( " " );
+ if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) {
+ conv2 = converters[ tmp[1] + " " + current ];
+ if ( conv2 ) {
+ conv1 = converters[ conv1 ];
+ if ( conv1 === true ) {
+ conv = conv2;
+ } else if ( conv2 === true ) {
+ conv = conv1;
+ }
+ break;
+ }
+ }
+ }
+ }
+ // If we found no converter, dispatch an error
+ if ( !( conv || conv2 ) ) {
+ jQuery.error( "No conversion from " + conversion.replace("
"," to ") );
+ }
+ // If found converter is not an equivalence
+ if ( conv !== true ) {
+ // Convert with 1 or 2 converters accordingly
+ response = conv ? conv( response ) : conv2( conv1(response) );
+ }
+ }
+ }
+ return response;
+}
+
+
+
+
+var jsc = jQuery.now(),
+ jsre = /(\=)\?(&|$)|\?\?/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ return jQuery.expando + "_" + ( jsc++ );
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var inspectData = s.contentType === "application/x-www-form-urlencoded"
&&
+ ( typeof s.data === "string" );
+
+ if ( s.dataTypes[ 0 ] === "jsonp" ||
+ s.jsonp !== false && ( jsre.test( s.url ) ||
+ inspectData && jsre.test( s.data ) ) ) {
+
+ var responseContainer,
+ jsonpCallback = s.jsonpCallback =
+ jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
+ previous = window[ jsonpCallback ],
+ url = s.url,
+ data = s.data,
+ replace = "$1" + jsonpCallback + "$2";
+
+ if ( s.jsonp !== false ) {
+ url = url.replace( jsre, replace );
+ if ( s.url === url ) {
+ if ( inspectData ) {
+ data = data.replace( jsre, replace );
+ }
+ if ( s.data === data ) {
+ // Add callback manually
+ url += (/\?/.test( url ) ? "&" : "?") + s.jsonp +
"=" + jsonpCallback;
+ }
+ }
+ }
+
+ s.url = url;
+ s.data = data;
+
+ // Install callback
+ window[ jsonpCallback ] = function( response ) {
+ responseContainer = [ response ];
+ };
+
+ // Clean-up function
+ jqXHR.always(function() {
+ // Set callback back to previous value
+ window[ jsonpCallback ] = previous;
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( previous ) ) {
+ window[ jsonpCallback ]( responseContainer[ 0 ] );
+ }
+ });
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( jsonpCallback + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Delegate to script
+ return "script";
+ }
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript,
application/x-ecmascript"
+ },
+ contents: {
+ script: /javascript|ecmascript/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || document.getElementsByTagName( "head" )[0] ||
document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement( "script" );
+
+ script.async = "async";
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) )
{
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( head && script.parentNode ) {
+ head.removeChild( script );
+ }
+
+ // Dereference the script
+ script = undefined;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+ // Use insertBefore instead of appendChild to circumvent an IE6 bug.
+ // This arises when a base node is used (#2709 and #4378).
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( 0, 1 );
+ }
+ }
+ };
+ }
+});
+
+
+
+
+var // #5280: Internet Explorer will keep connections alive if we don't abort on
unload
+ xhrOnUnloadAbort = window.ActiveXObject ? function() {
+ // Abort all pending requests
+ for ( var key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( 0, 1 );
+ }
+ } : false,
+ xhrId = 0,
+ xhrCallbacks;
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject( "Microsoft.XMLHTTP" );
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+(function( xhr ) {
+ jQuery.extend( jQuery.support, {
+ ajax: !!xhr,
+ cors: !!xhr && ( "withCredentials" in xhr )
+ });
+})( jQuery.ajaxSettings.xhr() );
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var xhr = s.xhr(),
+ handle,
+ i;
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers[ "X-Requested-With" ] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( _ ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+
+ var status,
+ statusText,
+ responseHeaders,
+ responses,
+ xml;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occured
+ //
http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:...
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+ responses = {};
+ xml = xhr.responseXML;
+
+ // Construct response list
+ if ( xml && xml.documentElement /* #4958 */ ) {
+ responses.xml = xml;
+ }
+ responses.text = xhr.responseText;
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ // if we're in sync mode or it's in cache
+ // and has been retrieved directly (IE6 & IE7)
+ // we need to manually fire the callback
+ if ( !s.async || xhr.readyState === 4 ) {
+ callback();
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback(0,1);
+ }
+ }
+ };
+ }
+ });
+}
+
+
+
+
+var elemdisplay = {},
+ iframe, iframeDoc,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,
+ timerId,
+ fxAttrs = [
+ // height animations
+ [ "height", "marginTop", "marginBottom",
"paddingTop", "paddingBottom" ],
+ // width animations
+ [ "width", "marginLeft", "marginRight",
"paddingLeft", "paddingRight" ],
+ // opacity animations
+ [ "opacity" ]
+ ],
+ fxNow;
+
+jQuery.fn.extend({
+ show: function( speed, easing, callback ) {
+ var elem, display;
+
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("show", 3), speed, easing, callback );
+
+ } else {
+ for ( var i = 0, j = this.length; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ // Reset the inline display of this element to learn if it is
+ // being hidden by cascaded rules or not
+ if ( !jQuery._data(elem, "olddisplay") && display ===
"none" ) {
+ display = elem.style.display = "";
+ }
+
+ // Set elements which have been overridden with display: none
+ // in a stylesheet to whatever the default browser style is
+ // for such an element
+ if ( display === "" && jQuery.css(elem, "display") ===
"none" ) {
+ jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+ }
+ }
+ }
+
+ // Set the display of most of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.style ) {
+ display = elem.style.display;
+
+ if ( display === "" || display === "none" ) {
+ elem.style.display = jQuery._data( elem, "olddisplay" ) || "";
+ }
+ }
+ }
+
+ return this;
+ }
+ },
+
+ hide: function( speed, easing, callback ) {
+ if ( speed || speed === 0 ) {
+ return this.animate( genFx("hide", 3), speed, easing, callback);
+
+ } else {
+ var elem, display,
+ i = 0,
+ j = this.length;
+
+ for ( ; i < j; i++ ) {
+ elem = this[i];
+ if ( elem.style ) {
+ display = jQuery.css( elem, "display" );
+
+ if ( display !== "none" && !jQuery._data( elem,
"olddisplay" ) ) {
+ jQuery._data( elem, "olddisplay", display );
+ }
+ }
+ }
+
+ // Set the display of the elements in a second loop
+ // to avoid the constant reflow
+ for ( i = 0; i < j; i++ ) {
+ if ( this[i].style ) {
+ this[i].style.display = "none";
+ }
+ }
+
+ return this;
+ }
+ },
+
+ // Save the old toggle function
+ _toggle: jQuery.fn.toggle,
+
+ toggle: function( fn, fn2, callback ) {
+ var bool = typeof fn === "boolean";
+
+ if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
+ this._toggle.apply( this, arguments );
+
+ } else if ( fn == null || bool ) {
+ this.each(function() {
+ var state = bool ? fn : jQuery(this).is(":hidden");
+ jQuery(this)[ state ? "show" : "hide" ]();
+ });
+
+ } else {
+ this.animate(genFx("toggle", 3), fn, fn2, callback);
+ }
+
+ return this;
+ },
+
+ fadeTo: function( speed, to, easing, callback ) {
+ return this.filter(":hidden").css("opacity", 0).show().end()
+ .animate({opacity: to}, speed, easing, callback);
+ },
+
+ animate: function( prop, speed, easing, callback ) {
+ var optall = jQuery.speed( speed, easing, callback );
+
+ if ( jQuery.isEmptyObject( prop ) ) {
+ return this.each( optall.complete, [ false ] );
+ }
+
+ // Do not change referenced properties as per-property easing will be lost
+ prop = jQuery.extend( {}, prop );
+
+ function doAnimation() {
+ // XXX 'this' does not always have a nodeName when running the
+ // test suite
+
+ if ( optall.queue === false ) {
+ jQuery._mark( this );
+ }
+
+ var opt = jQuery.extend( {}, optall ),
+ isElement = this.nodeType === 1,
+ hidden = isElement && jQuery(this).is(":hidden"),
+ name, val, p, e,
+ parts, start, end, unit,
+ method;
+
+ // will store per property easing and be used to determine when an animation is
complete
+ opt.animatedProperties = {};
+
+ for ( p in prop ) {
+
+ // property name normalization
+ name = jQuery.camelCase( p );
+ if ( p !== name ) {
+ prop[ name ] = prop[ p ];
+ delete prop[ p ];
+ }
+
+ val = prop[ name ];
+
+ // easing resolution: per property > opt.specialEasing > opt.easing >
'swing' (default)
+ if ( jQuery.isArray( val ) ) {
+ opt.animatedProperties[ name ] = val[ 1 ];
+ val = prop[ name ] = val[ 0 ];
+ } else {
+ opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[
name ] || opt.easing || 'swing';
+ }
+
+ if ( val === "hide" && hidden || val === "show"
&& !hidden ) {
+ return opt.complete.call( this );
+ }
+
+ if ( isElement && ( name === "height" || name === "width"
) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( this, "display" ) === "inline" &&
+ jQuery.css( this, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) ===
"inline" ) {
+ this.style.display = "inline-block";
+
+ } else {
+ this.style.zoom = 1;
+ }
+ }
+ }
+ }
+
+ if ( opt.overflow != null ) {
+ this.style.overflow = "hidden";
+ }
+
+ for ( p in prop ) {
+ e = new jQuery.fx( this, opt, p );
+ val = prop[ p ];
+
+ if ( rfxtypes.test( val ) ) {
+
+ // Tracks whether to show or hide based on private
+ // data attached to the element
+ method = jQuery._data( this, "toggle" + p ) || ( val ===
"toggle" ? hidden ? "show" : "hide" : 0 );
+ if ( method ) {
+ jQuery._data( this, "toggle" + p, method === "show" ?
"hide" : "show" );
+ e[ method ]();
+ } else {
+ e[ val ]();
+ }
+
+ } else {
+ parts = rfxnum.exec( val );
+ start = e.cur();
+
+ if ( parts ) {
+ end = parseFloat( parts[2] );
+ unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" ) {
+ jQuery.style( this, p, (end || 1) + unit);
+ start = ( (end || 1) / e.cur() ) * start;
+ jQuery.style( this, p, start + unit);
+ }
+
+ // If a +=/-= token was provided, we're doing a relative animation
+ if ( parts[1] ) {
+ end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start;
+ }
+
+ e.custom( start, end, unit );
+
+ } else {
+ e.custom( start, val, "" );
+ }
+ }
+ }
+
+ // For JS strict compliance
+ return true;
+ }
+
+ return optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+
+ stop: function( type, clearQueue, gotoEnd ) {
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var index,
+ hadTimers = false,
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ // clear marker counters if we know they won't be
+ if ( !gotoEnd ) {
+ jQuery._unmark( true, this );
+ }
+
+ function stopQueue( elem, data, index ) {
+ var hooks = data[ index ];
+ jQuery.removeData( elem, index, true );
+ hooks.stop( gotoEnd );
+ }
+
+ if ( type == null ) {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop &&
index.indexOf(".run") === index.length - 4 ) {
+ stopQueue( this, data, index );
+ }
+ }
+ } else if ( data[ index = type + ".run" ] && data[ index ].stop ){
+ stopQueue( this, data, index );
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue
=== type) ) {
+ if ( gotoEnd ) {
+
+ // force the next step to be the last
+ timers[ index ]( true );
+ } else {
+ timers[ index ].saveState();
+ }
+ hadTimers = true;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( !( gotoEnd && hadTimers ) ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ }
+
+});
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout( clearFxNow, 0 );
+ return ( fxNow = jQuery.now() );
+}
+
+function clearFxNow() {
+ fxNow = undefined;
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, num ) {
+ var obj = {};
+
+ jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() {
+ obj[ this ] = type;
+ });
+
+ return obj;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx( "show", 1 ),
+ slideUp: genFx( "hide", 1 ),
+ slideToggle: genFx( "toggle", 1 ),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.extend({
+ speed: function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {},
speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing )
&& easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ?
opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] :
jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function( noUnmark ) {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ } else if ( noUnmark !== false ) {
+ jQuery._unmark( this );
+ }
+ };
+
+ return opt;
+ },
+
+ easing: {
+ linear: function( p, n, firstNum, diff ) {
+ return firstNum + diff * p;
+ },
+ swing: function( p, n, firstNum, diff ) {
+ return ( ( -Math.cos( p*Math.PI ) / 2 ) + 0.5 ) * diff + firstNum;
+ }
+ },
+
+ timers: [],
+
+ fx: function( elem, options, prop ) {
+ this.options = options;
+ this.elem = elem;
+ this.prop = prop;
+
+ options.orig = options.orig || {};
+ }
+
+});
+
+jQuery.fx.prototype = {
+ // Simple function for setting a style value
+ update: function() {
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this );
+ },
+
+ // Get the current size
+ cur: function() {
+ if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[
this.prop ] == null) ) {
+ return this.elem[ this.prop ];
+ }
+
+ var parsed,
+ r = jQuery.css( this.elem, this.prop );
+ // Empty strings, null, undefined and "auto" are converted to 0,
+ // complex values such as "rotate(1rad)" are returned as is,
+ // simple values such as "10px" are parsed to Float.
+ return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r :
parsed;
+ },
+
+ // Start an animation from one number to another
+ custom: function( from, to, unit ) {
+ var self = this,
+ fx = jQuery.fx;
+
+ this.startTime = fxNow || createFxNow();
+ this.end = to;
+ this.now = this.start = from;
+ this.pos = this.state = 0;
+ this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" :
"px" );
+
+ function t( gotoEnd ) {
+ return self.step( gotoEnd );
+ }
+
+ t.queue = this.options.queue;
+ t.elem = this.elem;
+ t.saveState = function() {
+ if ( self.options.hide && jQuery._data( self.elem, "fxshow" +
self.prop ) === undefined ) {
+ jQuery._data( self.elem, "fxshow" + self.prop, self.start );
+ }
+ };
+
+ if ( t() && jQuery.timers.push(t) && !timerId ) {
+ timerId = setInterval( fx.tick, fx.interval );
+ }
+ },
+
+ // Simple 'show' function
+ show: function() {
+ var dataShow = jQuery._data( this.elem, "fxshow" + this.prop );
+
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop );
+ this.options.show = true;
+
+ // Begin the animation
+ // Make sure that we start at a small width/height to avoid any flash of content
+ if ( dataShow !== undefined ) {
+ // This show is picking up where a previous hide or show left off
+ this.custom( this.cur(), dataShow );
+ } else {
+ this.custom( this.prop === "width" || this.prop === "height" ? 1 :
0, this.cur() );
+ }
+
+ // Start by showing the element
+ jQuery( this.elem ).show();
+ },
+
+ // Simple 'hide' function
+ hide: function() {
+ // Remember where we started, so that we can go back to it later
+ this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" +
this.prop ) || jQuery.style( this.elem, this.prop );
+ this.options.hide = true;
+
+ // Begin the animation
+ this.custom( this.cur(), 0 );
+ },
+
+ // Each step of an animation
+ step: function( gotoEnd ) {
+ var p, n, complete,
+ t = fxNow || createFxNow(),
+ done = true,
+ elem = this.elem,
+ options = this.options;
+
+ if ( gotoEnd || t >= options.duration + this.startTime ) {
+ this.now = this.end;
+ this.pos = this.state = 1;
+ this.update();
+
+ options.animatedProperties[ this.prop ] = true;
+
+ for ( p in options.animatedProperties ) {
+ if ( options.animatedProperties[ p ] !== true ) {
+ done = false;
+ }
+ }
+
+ if ( done ) {
+ // Reset the overflow
+ if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) {
+
+ jQuery.each( [ "", "X", "Y" ], function( index, value
) {
+ elem.style[ "overflow" + value ] = options.overflow[ index ];
+ });
+ }
+
+ // Hide the element if the "hide" operation was done
+ if ( options.hide ) {
+ jQuery( elem ).hide();
+ }
+
+ // Reset the properties, if the item has been hidden or shown
+ if ( options.hide || options.show ) {
+ for ( p in options.animatedProperties ) {
+ jQuery.style( elem, p, options.orig[ p ] );
+ jQuery.removeData( elem, "fxshow" + p, true );
+ // Toggle data is no longer needed
+ jQuery.removeData( elem, "toggle" + p, true );
+ }
+ }
+
+ // Execute the complete function
+ // in the event that the complete function throws an exception
+ // we must ensure it won't be called twice. #5684
+
+ complete = options.complete;
+ if ( complete ) {
+
+ options.complete = false;
+ complete.call( elem );
+ }
+ }
+
+ return false;
+
+ } else {
+ // classical easing cannot be used with an Infinity duration
+ if ( options.duration == Infinity ) {
+ this.now = t;
+ } else {
+ n = t - this.startTime;
+ this.state = n / options.duration;
+
+ // Perform the easing function, defaults to swing
+ this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0,
1, options.duration );
+ this.now = this.start + ( (this.end - this.start) * this.pos );
+ }
+ // Perform the next step of the animation
+ this.update();
+ }
+
+ return true;
+ }
+};
+
+jQuery.extend( jQuery.fx, {
+ tick: function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ },
+
+ interval: 13,
+
+ stop: function() {
+ clearInterval( timerId );
+ timerId = null;
+ },
+
+ speeds: {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+ },
+
+ step: {
+ opacity: function( fx ) {
+ jQuery.style( fx.elem, "opacity", fx.now );
+ },
+
+ _default: function( fx ) {
+ if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
+ fx.elem.style[ fx.prop ] = fx.now + fx.unit;
+ } else {
+ fx.elem[ fx.prop ] = fx.now;
+ }
+ }
+ }
+});
+
+// Adds width/height step functions
+// Do not set anything below 0
+jQuery.each([ "width", "height" ], function( i, prop ) {
+ jQuery.fx.step[ prop ] = function( fx ) {
+ jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit );
+ };
+});
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+
+// Try to restore the default display value of an element
+function defaultDisplay( nodeName ) {
+
+ if ( !elemdisplay[ nodeName ] ) {
+
+ var body = document.body,
+ elem = jQuery( "<" + nodeName + ">" ).appendTo( body ),
+ display = elem.css( "display" );
+ elem.remove();
+
+ // If the simple way fails,
+ // get element's real default display by attaching it to a temp iframe
+ if ( display === "none" || display === "" ) {
+ // No iframe to use yet, so create it
+ if ( !iframe ) {
+ iframe = document.createElement( "iframe" );
+ iframe.frameBorder = iframe.width = iframe.height = 0;
+ }
+
+ body.appendChild( iframe );
+
+ // Create a cacheable copy of the iframe document on first call.
+ // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
+ // document to it; WebKit & Firefox won't allow reusing the iframe document.
+ if ( !iframeDoc || !iframe.createElement ) {
+ iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
+ iframeDoc.write( ( document.compatMode === "CSS1Compat" ?
"<!doctype html>" : "" ) + "<html><body>"
);
+ iframeDoc.close();
+ }
+
+ elem = iframeDoc.createElement( nodeName );
+
+ iframeDoc.body.appendChild( elem );
+
+ display = jQuery.css( elem, "display" );
+ body.removeChild( iframe );
+ }
+
+ // Store the correct default display
+ elemdisplay[ nodeName ] = display;
+ }
+
+ return elemdisplay[ nodeName ];
+}
+
+
+
+
+var rtable = /^t(?:able|d|h)$/i,
+ rroot = /^(?:body|html)$/i;
+
+if ( "getBoundingClientRect" in document.documentElement ) {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0], box;
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ try {
+ box = elem.getBoundingClientRect();
+ } catch(e) {}
+
+ var doc = elem.ownerDocument,
+ docElem = doc.documentElement;
+
+ // Make sure we're not dealing with a disconnected DOM node
+ if ( !box || !jQuery.contains( docElem, elem ) ) {
+ return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
+ }
+
+ var body = doc.body,
+ win = getWindow(doc),
+ clientTop = docElem.clientTop || body.clientTop || 0,
+ clientLeft = docElem.clientLeft || body.clientLeft || 0,
+ scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop
|| body.scrollTop,
+ scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft
|| body.scrollLeft,
+ top = box.top + scrollTop - clientTop,
+ left = box.left + scrollLeft - clientLeft;
+
+ return { top: top, left: left };
+ };
+
+} else {
+ jQuery.fn.offset = function( options ) {
+ var elem = this[0];
+
+ if ( options ) {
+ return this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ if ( !elem || !elem.ownerDocument ) {
+ return null;
+ }
+
+ if ( elem === elem.ownerDocument.body ) {
+ return jQuery.offset.bodyOffset( elem );
+ }
+
+ var computedStyle,
+ offsetParent = elem.offsetParent,
+ prevOffsetParent = elem,
+ doc = elem.ownerDocument,
+ docElem = doc.documentElement,
+ body = doc.body,
+ defaultView = doc.defaultView,
+ prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) :
elem.currentStyle,
+ top = elem.offsetTop,
+ left = elem.offsetLeft;
+
+ while ( (elem = elem.parentNode) && elem !== body && elem !== docElem )
{
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position ===
"fixed" ) {
+ break;
+ }
+
+ computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) :
elem.currentStyle;
+ top -= elem.scrollTop;
+ left -= elem.scrollLeft;
+
+ if ( elem === offsetParent ) {
+ top += elem.offsetTop;
+ left += elem.offsetLeft;
+
+ if ( jQuery.support.doesNotAddBorder &&
!(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevOffsetParent = offsetParent;
+ offsetParent = elem.offsetParent;
+ }
+
+ if ( jQuery.support.subtractsBorderForOverflowNotVisible &&
computedStyle.overflow !== "visible" ) {
+ top += parseFloat( computedStyle.borderTopWidth ) || 0;
+ left += parseFloat( computedStyle.borderLeftWidth ) || 0;
+ }
+
+ prevComputedStyle = computedStyle;
+ }
+
+ if ( prevComputedStyle.position === "relative" || prevComputedStyle.position
=== "static" ) {
+ top += body.offsetTop;
+ left += body.offsetLeft;
+ }
+
+ if ( jQuery.support.fixedPosition && prevComputedStyle.position ===
"fixed" ) {
+ top += Math.max( docElem.scrollTop, body.scrollTop );
+ left += Math.max( docElem.scrollLeft, body.scrollLeft );
+ }
+
+ return { top: top, left: left };
+ };
+}
+
+jQuery.offset = {
+
+ bodyOffset: function( body ) {
+ var top = body.offsetTop,
+ left = body.offsetLeft;
+
+ if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
+ top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
+ left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
+ }
+
+ return { top: top, left: left };
+ },
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position ===
"fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft])
> -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is
either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[0] ) {
+ return null;
+ }
+
+ var elem = this[0],
+
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent(),
+
+ // Get correct offsets
+ offset = this.offset(),
+ parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } :
offsetParent.offset();
+
+ // Subtract element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
+ offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
+
+ // Add offsetParent borders
+ parentOffset.top += parseFloat( jQuery.css(offsetParent[0],
"borderTopWidth") ) || 0;
+ parentOffset.left += parseFloat( jQuery.css(offsetParent[0],
"borderLeftWidth") ) || 0;
+
+ // Subtract the two offsets
+ return {
+ top: offset.top - parentOffset.top,
+ left: offset.left - parentOffset.left
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.body;
+ while ( offsetParent && (!rroot.test(offsetParent.nodeName) &&
jQuery.css(offsetParent, "position") === "static") ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( ["Left", "Top"], function( i, name ) {
+ var method = "scroll" + name;
+
+ jQuery.fn[ method ] = function( val ) {
+ var elem, win;
+
+ if ( val === undefined ) {
+ elem = this[ 0 ];
+
+ if ( !elem ) {
+ return null;
+ }
+
+ win = getWindow( elem );
+
+ // Return the scroll offset
+ return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" :
"pageXOffset" ] :
+ jQuery.support.boxModel && win.document.documentElement[ method ] ||
+ win.document.body[ method ] :
+ elem[ method ];
+ }
+
+ // Set the scroll offset
+ return this.each(function() {
+ win = getWindow( this );
+
+ if ( win ) {
+ win.scrollTo(
+ !i ? val : jQuery( win ).scrollLeft(),
+ i ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ this[ method ] = val;
+ }
+ });
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+
+
+
+
+// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods
+jQuery.each([ "Height", "Width" ], function( i, name ) {
+
+ var type = name.toLowerCase();
+
+ // innerHeight and innerWidth
+ jQuery.fn[ "inner" + name ] = function() {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, "padding" ) ) :
+ this[ type ]() :
+ null;
+ };
+
+ // outerHeight and outerWidth
+ jQuery.fn[ "outer" + name ] = function( margin ) {
+ var elem = this[0];
+ return elem ?
+ elem.style ?
+ parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" )
) :
+ this[ type ]() :
+ null;
+ };
+
+ jQuery.fn[ type ] = function( size ) {
+ // Get window width or height
+ var elem = this[0];
+ if ( !elem ) {
+ return size == null ? null : this;
+ }
+
+ if ( jQuery.isFunction( size ) ) {
+ return this.each(function( i ) {
+ var self = jQuery( this );
+ self[ type ]( size.call( this, i, self[ type ]() ) );
+ });
+ }
+
+ if ( jQuery.isWindow( elem ) ) {
+ // Everyone else use document.documentElement or document.body depending on Quirks vs
Standards mode
+ // 3rd condition allows Nokia support, as it supports the docElem prop but not
CSS1Compat
+ var docElemProp = elem.document.documentElement[ "client" + name ],
+ body = elem.document.body;
+ return elem.document.compatMode === "CSS1Compat" && docElemProp ||
+ body && body[ "client" + name ] || docElemProp;
+
+ // Get document width or height
+ } else if ( elem.nodeType === 9 ) {
+ // Either scroll[Width/Height] or offset[Width/Height], whichever is greater
+ return Math.max(
+ elem.documentElement["client" + name],
+ elem.body["scroll" + name], elem.documentElement["scroll" +
name],
+ elem.body["offset" + name], elem.documentElement["offset" +
name]
+ );
+
+ // Get or set width or height on the element
+ } else if ( size === undefined ) {
+ var orig = jQuery.css( elem, type ),
+ ret = parseFloat( orig );
+
+ return jQuery.isNumeric( ret ) ? ret : orig;
+
+ // Set the width or height on the element (default to pixels if value is unitless)
+ } else {
+ return this.css( type, typeof size === "string" ? size : size +
"px" );
+ }
+ };
+
+});
+
+
+
+
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd &&
define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+
+
+})( window );
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/js/resource_tree.js
b/modules/enterprise/gui/rest-war/src/main/webapp/js/resource_tree.js
index b8f570b..a43eee5 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/js/resource_tree.js
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/js/resource_tree.js
@@ -17,8 +17,7 @@ var r = 960 / 2;
// d3.json("/rest/1/resource/10702/hierarchy.json", function(json) {
d3.json("/rest/1/resource/" + resourceId + "/hierarchy.json",
function(json) {
- var inner = json.resourceWithChildren;
- var nodes = tree.nodes(inner);
+ var nodes = tree.nodes(json);
var link = vis.selectAll("path.link")
.data(tree.links(nodes))
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/js/rhq.js
b/modules/enterprise/gui/rest-war/src/main/webapp/js/rhq.js
new file mode 100644
index 0000000..0f1c5e6
--- /dev/null
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/js/rhq.js
@@ -0,0 +1,120 @@
+var rhq = {
+
+dashboard : function() {
+ // get all platforms
+ $.get('/rest/1/resource/platforms', function(data) {
+
+ var table = $("#table");
+ for (var i = 0; i < data.length ; i++ ) {
+ var resource = data[i];
+
+ var rowId = "tr" + i ;
+ table.append('<tr id="'+ rowId + '">');
+
+ // Now get the schedules for this resource
+ var resId = resource.resourceId;
+ console.log(resId);
+
+
$.getJSON("/rest/1/resource/"+resId+"/schedules.json?type=metric&enabledOnly=true",
function (json2) {
+
+ var tr = $("#" + rowId);
+ for ( var n = 0 ; n < json2.length ; n ++ ) {
+ var tdid = "td_" + i + "_" + n;
+ var schedule = json2[n];
+ var td = tr.append('<td id="'+tdid+'">');
+ $("#"+tdid).html(schedule.displayName + " (" +
schedule.scheduleId + ')<br/><div id="d' + tdid
+'"/>');
+ rhq.whisker(schedule.scheduleId,tdid,180,90);
+ }
+ })
+ }
+ })
+ },
+whisker: function(scheduleId,divId,w,h) {
+
+ d3.json(
+ '/rest/1/metric/data/'+scheduleId+'.json?hideEmpty=true',
+ function (jsondata) {
+// console.log(scheduleId +", "+ divId);
+ var svg = d3.select("body").select("#"+divId);
+ svg=svg.append("svg:svg")
+ .attr("width", w)
+ .attr("height", h);
+
+ var points = jsondata.dataPoints;
+ var minVal = jsondata.min;
+ var maxVal = jsondata.max;
+ var avgVal = jsondata.avg;
+ var minTs = jsondata.minTimeStamp;
+ var maxTs = jsondata.maxTimeStamp;
+
+ var minTsD = new Date(minTs);
+ var maxTsD = new Date(maxTs);
+
+ // X axis goes from lowest to highest timestamp
+ var x = d3.time.scale().domain([minTsD,maxTsD]).range([0,w]);
+ // Y axis goes from lowest to highest value
+ var y = d3.scale.linear().domain([minVal, maxVal]).rangeRound([0,h]);
+
+
+ // Line for the average
+ var avgLine = svg.append("svg:line")
+ .attr("x1", x(new Date(minTs)))
+ .attr("y1", h - y(avgVal))
+ .attr("x2", x(new Date(maxTs)))
+ .attr("y2", h - y(avgVal))
+ .attr("stroke", "lightgrey")
+ .attr("stroke-dasharray", "2,4");
+
+ // TODO if present add lines for baselines
+
+ // data() loops over all entries in 'points' above for the
remaining statements
+ var bars = svg.selectAll("bars").data(points);
+
+ var currX = function(d) {
+ return x(new Date(d.timeStamp))
+ };
+
+ var group = bars.enter().append("svg:g");
+
+ var line = group.append("svg:line")
+ .attr("x1", currX)
+ .attr("x2", currX)
+ .attr("y1", function(d) {
+ return h - y(d.low)
+ })
+ .attr("y2", function(d) {
+ return h - y(d.high)
+ })
+ .attr("stroke", "lightblue");
+
+ var circleLow = group.append("svg:circle")
+ .attr("cx", currX)
+ .attr("cy", function(d) {
+ return h - y(d.low)
+ })
+ .attr("r", 1)
+ .attr("stroke", "green")
+ .attr("fill", "lightgreen");
+
+ var circleHigh = group.append("svg:circle")
+ .attr("cx", currX)
+ .attr("cy", function(d) {
+ return h - y(d.high)
+ })
+ .attr("r", 1)
+ .attr("stroke", "green")
+ .attr("fill", "lightblue");
+
+ var circleVal = group.append("svg:circle")
+ .attr("cx", currX)
+ .attr("cy", function(d) {
+ return h - y(d.value)
+ })
+ .attr("r", 1.5)
+ .attr("stroke", "blue")
+ .attr("fill", "lightpink");
+
+ });
+} // whisker
+
+};//var rhq
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
b/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
deleted file mode 100644
index 6f23669..0000000
--- a/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
+++ /dev/null
@@ -1,92 +0,0 @@
-function whisker(scheduleId,divId) {
-// Width and height of the chart to print
- var w = 240,
- h = 125;
-
-// div#one is the div with the id 'one' ( <div id="one"/>)
- var svg =
d3.select("body").select("div#"+divId).append("svg:svg")
- .attr("width", w)
- .attr("height", h);
-// .append("svg:g");
-// .attr("transform", "translate(" + p[3] + "," + (h -
p[2]) + ")");
- d3.json(
- '/rest/1/metric/data/'+scheduleId+'.json?hideEmpty=true',
- function (jsondata) {
-
- var points = jsondata.dataPoints;
- var minVal = jsondata.min;
- var maxVal = jsondata.max;
- var avgVal = jsondata.avg;
- var minTs = jsondata.minTimeStamp;
- var maxTs = jsondata.maxTimeStamp;
-
- var minTsD = new Date(minTs);
- var maxTsD = new Date(maxTs);
- console.log(minTs, minTsD, maxTs, maxTsD);
-
- // X axis goes from lowest to highest timestamp
- var x = d3.time.scale().domain([minTsD,maxTsD]).range([0,w]);
- // Y axis goes from lowest to highest value
- var y = d3.scale.linear().domain([minVal, maxVal]).rangeRound([0,h]);
-
-
- // Line for the average
- var avgLine = svg.append("svg:line")
- .attr("x1", x(new Date(minTs)))
- .attr("y1", h - y(avgVal))
- .attr("x2", x(new Date(maxTs)))
- .attr("y2", h - y(avgVal))
- .attr("stroke", "lightgrey")
- .attr("stroke-dasharray", "2,4");
-
- // TODO if present add lines for baselines
-
- // data() loops over all entries in 'points' above for the
remaining statements
- var bars = svg.selectAll("bars").data(points);
-
- var currX = function(d) {
- return x(new Date(d.timeStamp))
- };
-
- var group = bars.enter().append("svg:g");
-
- var line = group.append("svg:line")
- .attr("x1", currX)
- .attr("x2", currX)
- .attr("y1", function(d) {
- return h - y(d.low)
- })
- .attr("y2", function(d) {
- return h - y(d.high)
- })
- .attr("stroke", "lightblue");
-
- var circleLow = group.append("svg:circle")
- .attr("cx", currX)
- .attr("cy", function(d) {
- return h - y(d.low)
- })
- .attr("r", 1)
- .attr("stroke", "green")
- .attr("fill", "lightgreen");
-
- var circleHigh = group.append("svg:circle")
- .attr("cx", currX)
- .attr("cy", function(d) {
- return h - y(d.high)
- })
- .attr("r", 1)
- .attr("stroke", "green")
- .attr("fill", "lightblue");
-
- var circleVal = group.append("svg:circle")
- .attr("cx", currX)
- .attr("cy", function(d) {
- return h - y(d.value)
- })
- .attr("r", 1.5)
- .attr("stroke", "blue")
- .attr("fill", "lightpink");
-
- });
-}
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/stacked1.html
b/modules/enterprise/gui/rest-war/src/main/webapp/stacked1.html
index 549a3ff..9413b70 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/stacked1.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/stacked1.html
@@ -5,6 +5,7 @@
<script type="text/javascript"
src="js/d3.js"></script>
<script type="text/javascript"
src="js/d3.layout.js"></script>
<script type="text/javascript"
src="js/d3.time.js"></script>
+ <script type="text/javascript"
src="js/jquery.js"></script>
<style type="text/css">
svg {
@@ -18,33 +19,50 @@ svg {
</style>
</head>
<body>
- <h2>Stacked metrics (schedule 10003)</h2>
- Upper edge of green is low, upper edge of blue is the value and upper edge of pink is
high.
- <br/>
- <script type="text/javascript">
-
-var w = 960,
- h = 500,
- p = [20, 50, 30, 20],
- x = d3.scale.ordinal().rangeRoundBands([0, w - p[1] - p[3]]),
- y = d3.scale.linear().range([0, h - p[0] - p[2]]),
- z = d3.scale.ordinal().range(["lightgreen", "darkblue",
"lightpink"]),
- format = d3.time.format("%H:%M");
-
-var svg = d3.select("body").append("svg:svg")
- .attr("width", w)
- .attr("height", h)
- .append("svg:g")
- .attr("transform", "translate(" + p[3] + "," + (h -
p[2]) + ")");
-
-
-d3.json(
- '/rest/1/metric/data/10003.json',
- function (jsondata) {
-
- var points = jsondata.metricAggregate.dataPoints;
- var min = jsondata.metricAggregate.min;
- var max = jsondata.metricAggregate.max;
+
+
+<h1 id="resId">Resource</h1>
+Upper edge of green is low, upper edge of blue is the value and upper edge of pink is
high.
+<br/>
+<h2 id="header">Schedule</h2>
+ <script type="text/javascript">
+
+// Get the platforms and then the first one
+$.getJSON('/rest/1/resource/platforms.json', function (json) {
+ var res = json[0];
+ var rid = res.resourceId;
+ $("#resId").text("Resource " + rid + " (" +
res.resourceName + ")");
+
+ // Now get the metrical schedules of the platform
+ $.getJSON("/rest/1/resource/"+rid+"/schedules.json?type=metric",
function (json2) {
+
+ // Take the first
+ var schId = json2[0].scheduleId;
+ $("#header").text("Schedule " + schId + " (" +
json2[0].scheduleName + ")");
+
+ // And call d3 graphing on it
+ d3.json(
+ '/rest/1/metric/data/'+schId+'.json',
+ function (jsondata) {
+
+ var w = 960,
+ h = 500,
+ p = [20, 50, 30, 20],
+ x = d3.scale.ordinal().rangeRoundBands([0, w - p[1] - p[3]]),
+ y = d3.scale.linear().range([0, h - p[0] - p[2]]),
+ z = d3.scale.ordinal().range(["lightgreen",
"darkblue", "lightpink"]),
+ format = d3.time.format("%H:%M");
+
+ var svg = d3.select("body").append("svg:svg")
+ .attr("width", w)
+ .attr("height", h)
+ .append("svg:g")
+ .attr("transform", "translate(" + p[3] +
"," + (h - p[2]) + ")");
+
+
+ var points = jsondata.dataPoints;
+ var min = jsondata.min;
+ var max = jsondata.max;
var metrics =
d3.layout.stack()(["low","value","high"].map(function
(point) {
return points.map(function (d) {
@@ -119,6 +137,8 @@ d3.json(
.attr("dy", ".35em")
.text(d3.format(",d"));
});
+ });
+});
</script>
</body>
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/stacked2.html
b/modules/enterprise/gui/rest-war/src/main/webapp/stacked2.html
index d3f8b4d..e622f2c 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/stacked2.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/stacked2.html
@@ -5,6 +5,7 @@
<script type="text/javascript"
src="js/d3.js"></script>
<script type="text/javascript"
src="js/d3.layout.js"></script>
<script type="text/javascript"
src="js/d3.time.js"></script>
+ <script type="text/javascript"
src="js/jquery.js"></script>
<style type="text/css">
svg {
@@ -18,9 +19,10 @@ svg {
</style>
</head>
<body>
- <h2>Stacked metrics (schedule 10003)</h2>
+ <h1 id="resId">Resource</h1>
Upper edge of green is low, upper edge of blue is the value and upper edge of pink is
high.
<br/>
+ <h2 id="header">Schedule</h2>
<script type="text/javascript">
var w = 960,
@@ -37,89 +39,101 @@ var svg = d3.select("body").append("svg:svg")
.append("svg:g")
.attr("transform", "translate(" + p[3] + "," + (h -
p[2]) + ")");
-
-d3.json(
- '/rest/1/metric/data/10003.json',
- function (jsondata) {
-
- var points = jsondata.metricAggregate.dataPoints;
- var min = jsondata.metricAggregate.min;
- var max = jsondata.metricAggregate.max;
-
- var metrics =
d3.layout.stack()(["low","value","high"].map(function
(point) {
- return points.map(function (d) {
- var tmp;
- if (point == "low")
- tmp = d["low"] - min;
- if (point == "value") {
- var va = d["value"];
- var lo = d["low"];
- tmp = va-lo;
- }
- if (point == "high")
- tmp = (d["high"]-d["value"]);
-
- if (tmp < 0 ) {
-// console.log(point);
- tmp=0;
- }
-
- return {x: new Date(d.timeStamp), y: tmp};
- });
- }));
-
-// console.log(metrics);
-
-
- // Compute the x-domain (by date) and y-domain (by top).
- x.domain(metrics[0].map(function(d) { return d.x; }));
- y.domain([0, d3.max(metrics[metrics.length - 1], function(d) { return d.y0 + d.y;
})]);
-
- // Add a group for each cause.
- var cause = svg.selectAll("g.cause")
- .data(metrics)
- .enter().append("svg:g")
- .attr("class", "cause")
- .style("fill", function(d, i) { return z(i); })
- .style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
-
- // Add a rect for each date.
- var rect = cause.selectAll("rect")
- .data(Object)
- .enter().append("svg:rect")
- .attr("x", function(d) { return x(d.x); })
- .attr("y", function(d) { return -y(d.y0) - y(d.y); })
- .attr("height", function(d) { return 5; }) // <<<--- HERE
- .attr("width", x.rangeBand());
-
- // Add a label per date.
- var label = svg.selectAll("text")
- .data(x.domain())
- .enter().append("svg:text")
- .attr("x", function(d) { return x(d) + x.rangeBand() / 2; })
- .attr("y", 6)
- .attr("text-anchor", "middle")
- .attr("dy", ".71em")
- .text(format);
-
- // Add y-axis rules.
- var rule = svg.selectAll("g.rule")
- .data(y.ticks(5))
- .enter().append("svg:g")
- .attr("class", "rule")
- .attr("transform", function(d) { return "translate(0," + -y(d)
+ ")"; });
-
- rule.append("svg:line")
- .attr("x2", w - p[1] - p[3])
- .style("stroke", function(d) { return d ? "#fff" :
"#000"; })
- .style("stroke-opacity", function(d) { return d ? .7 : null; });
-
- rule.append("svg:text")
- .attr("x", w - p[1] - p[3] + 6)
- .attr("dy", ".35em")
- .text(d3.format(",d"));
-});
-
+// Get the platforms and then the first one
+$.getJSON('/rest/1/resource/platforms.json', function (json) {
+ var res = json[0];
+ var rid = res.resourceId;
+ $("#resId").text("Resource " + rid + " (" +
res.resourceName + ")");
+
+ // Now get the metrical schedules of the platform
+ $.getJSON("/rest/1/resource/"+rid+"/schedules.json?type=metric",
function (json2) {
+
+ // Take the first
+ var schId = json2[0].scheduleId;
+ $("#header").text("Schedule " + schId + " (" +
json2[0].scheduleName + ")");
+
+ // And call d3 graphing on it
+ d3.json(
+ '/rest/1/metric/data/'+schId+'.json',
+ function (jsondata) {
+
+ var points = jsondata.dataPoints;
+ var min = jsondata.min;
+ var max = jsondata.max;
+
+ var metrics =
d3.layout.stack()(["low","value","high"].map(function
(point) {
+ return points.map(function (d) {
+ var tmp;
+ if (point == "low")
+ tmp = d["low"] - min;
+ if (point == "value") {
+ var va = d["value"];
+ var lo = d["low"];
+ tmp = va-lo;
+ }
+ if (point == "high")
+ tmp = (d["high"]-d["value"]);
+
+ if (tmp < 0 ) {
+ tmp=0;
+ }
+
+ return {x: new Date(d.timeStamp), y: tmp};
+ });
+ }));
+
+
+ // Compute the x-domain (by date) and y-domain (by top).
+ x.domain(metrics[0].map(function(d) { return d.x; }));
+ y.domain([0, d3.max(metrics[metrics.length - 1], function(d) { return
d.y0 + d.y; })]);
+
+ // Add a group for each cause.
+ var cause = svg.selectAll("g.cause")
+ .data(metrics)
+ .enter().append("svg:g")
+ .attr("class", "cause")
+ .style("fill", function(d, i) { return z(i); })
+ .style("stroke", function(d, i) { return
d3.rgb(z(i)).darker(); });
+
+ // Add a rect for each date.
+ var rect = cause.selectAll("rect")
+ .data(Object)
+ .enter().append("svg:rect")
+ .attr("x", function(d) { return x(d.x); })
+ .attr("y", function(d) { return -y(d.y0) - y(d.y);
})
+ .attr("height", function(d) { return 5; }) //
<<<--- HERE
+ .attr("width", x.rangeBand());
+
+ // Add a label per date.
+ var label = svg.selectAll("text")
+ .data(x.domain())
+ .enter().append("svg:text")
+ .attr("x", function(d) { return x(d) +
x.rangeBand() / 2; })
+ .attr("y", 6)
+ .attr("text-anchor", "middle")
+ .attr("dy", ".71em")
+ .text(format);
+
+ // Add y-axis rules.
+ var rule = svg.selectAll("g.rule")
+ .data(y.ticks(5))
+ .enter().append("svg:g")
+ .attr("class", "rule")
+ .attr("transform", function(d) { return
"translate(0," + -y(d) + ")"; });
+
+ rule.append("svg:line")
+ .attr("x2", w - p[1] - p[3])
+ .style("stroke", function(d) { return d ?
"#fff" : "#000"; })
+ .style("stroke-opacity", function(d) { return d ?
.7 : null; });
+
+ rule.append("svg:text")
+ .attr("x", w - p[1] - p[3] + 6)
+ .attr("dy", ".35em")
+ .text(d3.format(",d"));
+ });
+
+ })
+})
</script>
</body>
</html>
\ No newline at end of file
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/whisker.html
b/modules/enterprise/gui/rest-war/src/main/webapp/whisker.html
index dc37e35..2b03349 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/whisker.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/whisker.html
@@ -5,6 +5,8 @@
<script type="text/javascript"
src="js/d3.js"></script>
<script type="text/javascript"
src="js/d3.layout.js"></script>
<script type="text/javascript"
src="js/d3.time.js"></script>
+ <script type="text/javascript"
src="js/jquery.js"></script>
+ <script type="text/javascript"
src="js/rhq.js"></script>
<style type="text/css">
svg {
@@ -18,91 +20,33 @@ svg {
</style>
</head>
<body>
- <h2> (schedule 10003)</h2>
+ <h1 id="resId">Resource</h1>
+ <h2 id="header">Schedule</h2>
- <br/>
- <div id="one"/>
+ <div id="one"></div>
<script type="text/javascript">
// Width and height of the chart to print
var w = 800,
h = 600;
-// div#one is the div with the id 'one' ( <div id="one"/>)
-var svg =
d3.select("body").select("div#one").append("svg:svg")
- .attr("width", w)
- .attr("height", h);
-// .append("svg:g");
-// .attr("transform", "translate(" + p[3] + "," + (h -
p[2]) + ")");
+// Get the platforms and then the first one
+$.getJSON('/rest/1/resource/platforms.json', function (json) {
+ var res = json[0];
+ var rid = res.resourceId;
+ $("#resId").text("Resource " + rid + " (" +
res.resourceName + ")");
+ // Now get the metrical schedules of the platform
+ $.getJSON("/rest/1/resource/"+rid+"/schedules.json?type=metric",
function (json2) {
-d3.json(
- '/rest/1/metric/data/10003.json?hideEmpty=true',
- function (jsondata) {
+ // Take the first and render it
+ var schId = json2[0].scheduleId;
+ $("#header").text("Schedule " + schId + " (" +
json2[0].scheduleName + ")");
+ rhq.whisker(schId,"one",w,h);
+ })
+})
- var points = jsondata.metricAggregate.dataPoints;
- var minVal = jsondata.metricAggregate.min;
- var maxVal = jsondata.metricAggregate.max;
- var avgVal = jsondata.metricAggregate.avg;
- var minTs = jsondata.metricAggregate.minTimeStamp;
- var maxTs = jsondata.metricAggregate.maxTimeStamp;
- var minTsD = new Date(minTs);
- var maxTsD = new Date(maxTs);
- console.log(minTs, minTsD, maxTs, maxTsD);
-
- // X axis goes from lowest to highest timestamp
- var x = d3.time.scale().domain([minTsD,maxTsD]).range([0,w]);
- // Y axis goes from lowest to highest value
- var y = d3.scale.linear().domain([minVal, maxVal]).rangeRound([0,h]);
-
-// console.log(jsondata.metricAggregate);
-
-
- // Line for the average
- var avgLine = svg.append("svg:line")
- .attr("x1",x(new Date(minTs)))
- .attr("y1",h-y(avgVal))
- .attr("x2",x(new Date(maxTs)))
- .attr("y2",h-y(avgVal))
- .attr("stroke","lighgrey")
- .attr("stroke-dasharray","2,2");
-
- // TODO if present add lines for baselines
-
- // data() loops over all entries in 'points' above for the remaining
statements
- var bars = svg.selectAll("bars").data(points);
-
- var currX = function(d) { return x(new Date(d.timeStamp)) };
-
- var circleLow = bars.enter().append("svg:circle")
- .attr("cx",currX)
- .attr("cy",function(d) {return h-y(d.low)})
- .attr("r",2)
- .attr("stroke","lightgreen")
- .attr("fill","lightgreen");
-
- var circleHigh = bars.enter().append("svg:circle")
- .attr("cx",currX)
- .attr("cy",function(d) {return h-y(d.high)})
- .attr("r",2)
- .attr("stroke","lightgreen")
- .attr("fill","lightgreen");
-
- var circleVal = bars.enter().append("svg:circle")
- .attr("cx",currX)
- .attr("cy",function(d) {return h-y(d.value)})
- .attr("r",3)
- .attr("stroke","lightpink")
- .attr("fill","lightpink");
-
- var line = bars.enter().append("svg:line")
- .attr("x1",currX)
- .attr("x2",currX)
- .attr("y1",function(d) {return h-y(d.low)})
- .attr("y2",function(d) {return h-y(d.high)})
- .attr("stroke","blue");
-});
</script>
</body>
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/whisker2.html
b/modules/enterprise/gui/rest-war/src/main/webapp/whisker2.html
index d523acd..67340d5 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/whisker2.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/whisker2.html
@@ -1,17 +1,18 @@
<!DOCTYPE html>
<html>
<head>
- <title>(free mem, schedule 10003)</title>
+ <title>Dashboard</title>
<script type="text/javascript"
src="js/d3.js"></script>
<script type="text/javascript"
src="js/d3.layout.js"></script>
<script type="text/javascript"
src="js/d3.time.js"></script>
- <script type="text/javascript"
src="js/whisker.js"></script>
+ <script type="text/javascript"
src="js/jquery.js"></script>
+ <script type="text/javascript"
src="js/rhq.js"></script>
<style type="text/css">
svg {
- width: 240px;
- height: 125px;
+ width: 182px;
+ height: 92px;
border: solid 1px #ccc;
font: 10px sans-serif;
shape-rendering: crispEdges;
@@ -20,51 +21,15 @@ svg {
</style>
</head>
<body>
- <h2>Various metrics</h2>
- <table>
- <tr>
- <td>
- Schedule 10003<br/>
- <div id="two">
- <script>whisker(10003,'two')</script>
- </div>
- </td>
- <td>
- Schedule 10004<br/>
- <div id="three">
- <script>whisker(10004,'three')</script>
- </div>
- </td>
-
- <td>
- Schedule 10007<br/>
- <div id="four">
- <script>whisker(10007,'four')</script>
- </div>
- </td>
- </tr>
- <tr>
- <td>
- Schedule 10011<br/>
- <div id="five">
- <script>whisker(10011,'five')</script>
- </div>
- </td>
- <td>
- Schedule 10009<br/>
- <div id="six">
- <script>whisker(10009,'six')</script>
- </div>
- </td>
- <td>
- Schedule 10010<br/>
- <div id="seven">
- <script>whisker(10010,'seven')</script>
- </div>
- </td>
- </tr>
+ <h2>Dashboard</h2>
+<script>
+ $(document).ready(function() {
+ rhq.dashboard();
+ })
+</script>
+ <table id="table">
</table>
</body>
-</html>
\ No newline at end of file
+</html>
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 91ed23a..59efeba 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
@@ -31,6 +31,8 @@ import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.GenericEntity;
@@ -192,6 +194,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
public Response getSchedules(int resourceId,
String scheduleType,
+ boolean enabledOnly,
@Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo) {
@@ -200,7 +203,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if (scheduleType.equals("metric"))
scheduleType=DataType.MEASUREMENT.toString().toLowerCase();
- Resource res = resMgr.getResource(caller,resourceId); // Don't fetch(), as
this would yield a LazyLoadException
+ Resource res = resMgr.getResource(caller, resourceId); // Don't fetch(), as
this would yield a LazyLoadException
Set<MeasurementSchedule> schedules = res.getSchedules();
List<MetricSchedule> ret = new
ArrayList<MetricSchedule>(schedules.size());
@@ -212,26 +215,28 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if ("all".equals(scheduleType) ||
scheduleType.toLowerCase().equals(definition.getDataType().toString().toLowerCase()) ) {
- MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
- schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
- definition.getDataType().toString());
- UriBuilder uriBuilder;
- URI uri;
- if (definition.getDataType()== DataType.MEASUREMENT) {
+ if (!enabledOnly || (enabledOnly && schedule.isEnabled() )) {
+ MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
+ schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
+ definition.getDataType().toString());
+ UriBuilder uriBuilder;
+ URI uri;
+ if (definition.getDataType()== DataType.MEASUREMENT) {
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/metric/data/{id}");
+ uri = uriBuilder.build(schedule.getId());
+ Link metricLink = new Link("metric",uri.toString());
+ ms.addLink(metricLink);
+ }
+ // create link to the resource
uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/metric/data/{id}");
- uri = uriBuilder.build(schedule.getId());
- Link metricLink = new Link("metric",uri.toString());
- ms.addLink(metricLink);
+ uriBuilder.path("resource/" +
schedule.getResource().getId());
+ uri = uriBuilder.build();
+ Link link = new Link("resource",uri.toString());
+ ms.addLink(link);
+
+ ret.add(ms);
}
- // create link to the resource
- uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("resource/" + schedule.getResource().getId());
- uri = uriBuilder.build();
- Link link = new Link("resource",uri.toString());
- ms.addLink(link);
-
- ret.add(ms);
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index 08a1905..ab9ea62 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -87,6 +87,7 @@ public interface ResourceHandlerLocal {
@Cache(isPrivate = true,maxAge = 60)
Response getSchedules(@PathParam("id") int resourceId,
@QueryParam("type")
@DefaultValue("all") String scheduleType,
+ @QueryParam("enabledOnly")
@DefaultValue("true") boolean enabledOnly,
@Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo);
diff --git
a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
index d426e06..58af399 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricData.ftl
@@ -24,7 +24,7 @@
<script type="text/javascript"
src="/rest/js/d3.js"></script>
<script type="text/javascript"
src="/rest/js/d3.layout.js"></script>
<script type="text/javascript"
src="/rest/js/d3.time.js"></script>
- <script type="text/javascript"
src="/rest/js/whisker.js"></script>
+ <script type="text/javascript"
src="/rest/js/rhq.js"></script>
<table border=1>
<thead>
<tr>
@@ -44,7 +44,7 @@
<td>Max</td><td>${var.max}</td>
</tr>
<tr>
- <td><a align="top"
href="javascript:whisker(${sched?c},'one')">DataPoints</a></td><td>
+ <td><a align="top"
href="javascript:rhq.whisker(${sched?c},'one',400,200)">DataPoints</a></td><td>
<table>
<thead>
<tr>
commit ffa3c7f3e181f62107036844dacc4aef413ade80
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sat Nov 26 19:27:37 2011 +0100
Get system status from the SystemInfoManager
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
index 0f2a0ed..a6fce0c 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
@@ -18,9 +18,12 @@
*/
package org.rhq.enterprise.server.rest;
+import java.util.Map;
+
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
+import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@@ -39,6 +42,7 @@ import org.rhq.enterprise.server.alert.AlertManagerLocal;
import org.rhq.enterprise.server.measurement.MeasurementScheduleManagerLocal;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.rest.domain.Status;
+import org.rhq.enterprise.server.system.SystemInfoManagerLocal;
/**
* Return system status
@@ -50,48 +54,14 @@ public class StatusHandlerBean extends AbstractRestBean implements
StatusHandler
@EJB
- MeasurementScheduleManagerLocal scheduleManager;
- @EJB
- ResourceManagerLocal resourceManager;
- @EJB
- AlertManagerLocal alertManager;
- @EJB
- AlertDefinitionManagerLocal alertDefinitionManager;
+ SystemInfoManagerLocal infoMgr;
@Override
public Response getStatus(HttpHeaders httpHeaders) {
+ Map<String,String> statusMap = infoMgr.getSystemInformation (caller);
Status status = new Status();
-
- ResourceCriteria criteria = new ResourceCriteria();
- criteria.addFilterResourceCategories(ResourceCategory.PLATFORM);
- criteria.setRestriction(Criteria.Restriction.COUNT_ONLY);
- PageList<Resource> resList =
resourceManager.findResourcesByCriteria(caller,criteria);
- status.setPlatforms(resList.getTotalSize());
- criteria = new ResourceCriteria();
- criteria.addFilterResourceCategories(ResourceCategory.SERVER);
- criteria.setRestriction(Criteria.Restriction.COUNT_ONLY);
- resList = resourceManager.findResourcesByCriteria(caller,criteria);
- status.setServers(resList.getTotalSize());
- criteria = new ResourceCriteria();
- criteria.addFilterResourceCategories(ResourceCategory.SERVICE);
- criteria.setRestriction(Criteria.Restriction.COUNT_ONLY);
- resList = resourceManager.findResourcesByCriteria(caller,criteria);
- status.setServices(resList.getTotalSize());
-
- AlertCriteria alertCriteria = new AlertCriteria();
- alertCriteria.setRestriction(Criteria.Restriction.COUNT_ONLY);
- PageList<Alert> alertList =
alertManager.findAlertsByCriteria(caller,alertCriteria);
- status.setAlerts(alertList.getTotalSize());
-
- AlertDefinitionCriteria alertDefinitionCriteria = new AlertDefinitionCriteria();
- alertDefinitionCriteria.setRestriction(Criteria.Restriction.COUNT_ONLY);
- PageList<AlertDefinition> defList =
alertDefinitionManager.findAlertDefinitionsByCriteria(caller,alertDefinitionCriteria);
- status.setAlertDefinitions(defList.getTotalSize());
-
- status.setSchedules(-1); // TODO
-
- status.setMetricsMin(scheduleManager.getScheduledMeasurementsPerMinute());
+ status.setValues(statusMap);
MediaType mediaType = httpHeaders.getAcceptableMediaTypes().get(0);
Response.ResponseBuilder builder;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Status.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Status.java
index c0a4e10..dfb64ab 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Status.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Status.java
@@ -18,6 +18,9 @@
*/
package org.rhq.enterprise.server.rest.domain;
+import java.util.HashMap;
+import java.util.Map;
+
import javax.xml.bind.annotation.XmlRootElement;
/**
@@ -27,70 +30,15 @@ import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Status {
- int platforms;
- int servers;
- int services;
- int alerts;
- int alertDefinitions;
- int metricsMin;
- int schedules;
-
+ Map<String,String> values = new HashMap<String, String>();
public Status() {
}
- public int getPlatforms() {
- return platforms;
- }
-
- public void setPlatforms(int platforms) {
- this.platforms = platforms;
- }
-
- public int getServers() {
- return servers;
- }
-
- public void setServers(int servers) {
- this.servers = servers;
- }
-
- public int getServices() {
- return services;
- }
-
- public void setServices(int services) {
- this.services = services;
- }
-
- public int getAlerts() {
- return alerts;
- }
-
- public void setAlerts(int alerts) {
- this.alerts = alerts;
- }
-
- public int getAlertDefinitions() {
- return alertDefinitions;
- }
-
- public void setAlertDefinitions(int alertDefinitions) {
- this.alertDefinitions = alertDefinitions;
- }
-
- public int getMetricsMin() {
- return metricsMin;
- }
-
- public void setMetricsMin(int metricsMin) {
- this.metricsMin = metricsMin;
- }
-
- public int getSchedules() {
- return schedules;
+ public Map<String, String> getValues() {
+ return values;
}
- public void setSchedules(int schedules) {
- this.schedules = schedules;
+ public void setValues(Map<String, String> values) {
+ this.values = values;
}
}
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/status.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/status.ftl
index f4adb30..6569373 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/status.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/status.ftl
@@ -2,7 +2,7 @@
<#--
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -27,23 +27,10 @@
<td>Name</td><td>Value</td>
</tr>
</thead>
+ <#list var.values?keys as key>
<tr>
- <td>Alert
definitions</td><td>${var.alertDefinitions}</td>
- </tr>
- <tr>
- <td>Alerts</td><td>${var.alerts}</td>
- </tr>
- <tr>
- <td>Platforms</td><td>${var.platforms}</td>
- </tr>
- <tr>
- <td>Servers</td><td>${var.servers}</td>
- </tr>
- <tr>
- <td>Services</td><td>${var.services}</td>
- </tr>
- <tr>
- <td>Metrics/min</td><td>${var.metricsMin}</td>
+ <td>${key}</td><td>${var.values[key]}</td>
</tr>
+ </#list>
</table>
</html>
commit 4ab4f01ace074d2d0ed1457175173523988cd5ea
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sat Nov 26 17:34:07 2011 +0100
Improve history handling and parameter passing.
Add link to self for the history item.
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 536274a..be39ede 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
@@ -1,8 +1,27 @@
+/*
+ * 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.server.rest;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.ejb.EJB;
@@ -14,6 +33,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
@@ -24,6 +44,8 @@ import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.Property;
+import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.criteria.OperationHistoryCriteria;
@@ -40,6 +62,7 @@ import org.rhq.enterprise.server.operation.OperationManagerLocal;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.OperationDefinitionRest;
+import org.rhq.enterprise.server.rest.domain.OperationHistoryRest;
import org.rhq.enterprise.server.rest.domain.OperationRest;
/**
@@ -204,7 +227,13 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
// todo check params
// submit
- ResourceOperationSchedule sched =
opsManager.scheduleResourceOperation(caller,operation.getResourceId(),operation.getName(),0,0,0,-1,new
Configuration(),"TEst");
+
+ Configuration parameters = new Configuration();
+ for (Map.Entry<String,Object> entry : operation.getParams().entrySet())
{
+ parameters.put(new PropertySimple(entry.getKey(),entry.getValue())); //
TODO honor more types
+ }
+ ResourceOperationSchedule sched =
opsManager.scheduleResourceOperation(caller,operation.getResourceId(),operation.getName(),0,0,0,-1,
+ parameters,"TEst");
JobId jobId = new JobId(sched.getJobName(),sched.getJobGroup());
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/operation/history/{id}");
@@ -242,27 +271,45 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
@Override
@GET
@Path("history/{id}")
- public Response outcome(@PathParam("id") String jobName) {
+ public Response outcome(@PathParam("id") String jobName, @Context UriInfo
uriInfo) {
-/*
- ResourceOperationSchedule schedule =
opsManager.getResourceOperationSchedule(caller,operationHistoryId);
- if (schedule==null)
- throw new StuffNotFoundException("OperationSchedule with id " +
operationHistoryId);
-*/
ResourceOperationHistoryCriteria criteria = new
ResourceOperationHistoryCriteria();
criteria.addFilterJobId(new JobId(jobName));
- OperationHistory history ;//=
opsManager.getOperationHistoryByJobId(caller,jobName);
+ ResourceOperationHistory history ;//=
opsManager.getOperationHistoryByJobId(caller,jobName);
List<ResourceOperationHistory> list =
opsManager.findResourceOperationHistoriesByCriteria(caller,criteria);
- if (list==null || list.isEmpty())
+ if (list==null || list.isEmpty()) {
+ log.info("No history with id " + new HistoryJobId(jobName) + "
found");
throw new StuffNotFoundException("OperationHistory with id " + new
HistoryJobId(jobName));
+ }
history = list.get(0);
- String ret = history.getStatus().getDisplayName();
- // TODO get history.results with the help of the property definition
+ String status;
+ if (history.getStatus()==null)
+ status = " - no infomation yet -";
+ else
+ status = history.getStatus().getDisplayName();
+
+ OperationHistoryRest hist = new OperationHistoryRest();
+ hist.setStatus(status);
+ if (history.getErrorMessage()!=null)
+ hist.setErrorMessage(history.getErrorMessage());
+ if (history.getResults()!=null) {
+ Configuration results = history.getResults();
+ for (Property p : results.getProperties()) {
+ hist.getResult().put(p.getName(),p.toString());
+ }
+ }
+
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/history/{id}");
+ URI url = uriBuilder.build(new JobId(jobName));
+ Link self = new Link("self",url.toString());
+ hist.getLinks().add(self);
+
- return Response.ok(ret).build();
+ return Response.ok(hist).build();
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
index 1d2a9ed..79c20bb 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
@@ -1,3 +1,21 @@
+/*
+ * 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.server.rest;
import java.util.List;
@@ -72,5 +90,5 @@ public interface OperationsHandlerLocal {
@GET
@Path("history/{id}")
- public Response outcome(@PathParam("id") String jobName);
+ public Response outcome(@PathParam("id") String jobName, @Context UriInfo
uriInfo);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationHistoryRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationHistoryRest.java
new file mode 100644
index 0000000..8249db6
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationHistoryRest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.rest.domain;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * An operations history item
+ * @author Heiko W. Rupp
+ */
+@XmlRootElement
+public class OperationHistoryRest {
+
+ String status;
+ String errorMessage;
+
+ Map<String,Object> result = new HashMap<String, Object>();
+ List<Link> links = new ArrayList<Link>();
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Map<String, Object> getResult() {
+ return result;
+ }
+
+ public void setResult(Map<String, Object> result) {
+ this.result = result;
+ }
+
+ public String getErrorMessage() {
+ return errorMessage;
+ }
+
+ public void setErrorMessage(String errorMessage) {
+ this.errorMessage = errorMessage;
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ this.links = links;
+ }
+}
commit 7eed6555906199687c5f9f5d6c8cf50c15970d0d
Merge: 096cb99 b9ef6c8
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sat Nov 26 11:31:39 2011 +0100
Merge branch 'master' into feature/rest
commit b9ef6c8f5c67d257dc63ea56ef9e56c39bb22b47
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 19:53:54 2011 +0100
BZ 757178 If we can't read the file return to prevent an Exception later.
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
index e0f7509..3f32f1f 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftDetector.java
@@ -222,8 +222,8 @@ public class DriftDetector implements Runnable {
if (!file.canRead()) {
if (log.isDebugEnabled()) {
log.debug("Skipping " + file.getPath() + "
since it is not readable.");
- return;
}
+ return;
}
if (log.isInfoEnabled()) {
commit ee3319453f65791975cdd753a5d9f1757ae483cc
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 18:19:30 2011 +0100
BZ 757175 Prevent a NPE by missing directory listing rights.
diff --git a/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
b/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
index 7cfac87..c4ac737 100644
--- a/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
+++ b/modules/core/util/src/main/java/org/rhq/core/util/file/FileUtil.java
@@ -68,9 +68,9 @@ public class FileUtil {
/**
* Given a directory, this will recursively purge all child directories and files.
* If dir is actually a normal file, it will be deleted but only if deleteIt is
true.
- *
+ *
* If deleteIt is true, the directory itself will be deleted, otherwise it will
remain (albeit empty).
- *
+ *
* @param dir the directory to purge (if <code>null</code>, this method
does nothing and returns normally)
* @param deleteIt if <code>true</code> delete the directory itself,
otherwise leave it but purge its children
*/
@@ -297,7 +297,7 @@ public class FileUtil {
/**
* Strips the drive letter from the given Windows path. The drive letter is returned
* or <code>null</code> is returned if there was no drive letter in the
path.
- *
+ *
* @param path the path string that will be altered to have its drive letter
stripped.
* @return if there was a drive letter, it will be returned. If no drive letter was
in path, null is returned
*/
@@ -326,7 +326,7 @@ public class FileUtil {
* Return just the filename portion (the portion right of the last path separator
string)
* @param path
* @param separator
- * @return null if path is null, otherwise the trimmed filename
+ * @return null if path is null, otherwise the trimmed filename
*/
public static String getFileName(String path, String separator) {
if (null == path) {
@@ -353,11 +353,13 @@ public class FileUtil {
while (!directories.isEmpty()) {
File dir = directories.pop();
- for (File file : dir.listFiles()) {
- if (file.isDirectory()) {
- directories.push(file);
- } else {
- visitor.visit(file);
+ if (dir.listFiles()!=null) {
+ for (File file : dir.listFiles()) {
+ if (file.isDirectory()) {
+ directories.push(file);
+ } else {
+ visitor.visit(file);
+ }
}
}
}
@@ -444,7 +446,7 @@ public class FileUtil {
regex.append('.');
} else if (c == '*') {
- // ? match zero or more characters
+ // ? match zero or more characters
if (i + 1 < pattern.length()) {
char c2 = pattern.charAt(i + 1);
if (c2 == '*') {
commit 096cb99394259a950b92c81781405ad87fb206f6
Merge: 123ac05 90081e7
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 17:56:43 2011 +0100
Merge branch 'master' into feature/rest
Conflicts:
modules/enterprise/server/container/pom.xml
modules/enterprise/server/ear/pom.xml
diff --cc modules/enterprise/server/ear/pom.xml
index b792ab8,258a946e..941c88a
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@@ -95,7 -89,11 +89,11 @@@
<groupId>org.rhq</groupId>
<artifactId>rhq-core-domain</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </exclusion>
- </exclusions>
+ </exclusions>
</dependency>
<dependency>
diff --cc modules/jopr/modules-plugins-pom.xml
index dc1fa28,3b602ea..1bb52cf
--- a/modules/jopr/modules-plugins-pom.xml
+++ b/modules/jopr/modules-plugins-pom.xml
@@@ -1,83 -1,74 +1,74 @@@
--<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
-- <modelVersion>4.0.0</modelVersion>
--
-- <parent>
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-modules-parent</artifactId>
-- <version>2.4.0-SNAPSHOT</version>
-- </parent>
--
-- <!-- Plugin poms should not set their parent pom to this artifact. Instead, set to
"jopr-modules-parent". This is due to
-- the fact that this pom runs integration tests *on* the plugins. This pom must process
in the reactor after the
-- plugins themselves, as such, it can't be their parent. -->
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-plugins-parent</artifactId>
-- <version>2.3.0.EmbJopr.1.3.0-1</version>
-- <packaging>pom</packaging>
-- <name>Jopr Plugins</name>
-- <description>parent POM for all Jopr plugins</description>
--
- <scm>
-
<
connection>scm:svn:http://svn.jboss.org/repos/jopr/tags/Jopr_2_3_0_Emb...
-
<
developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/tags/Jopr...
- </scm>
-
- <properties>
- <scm.module.path>modules/plugins/</scm.module.path>
- </properties>
-
-- <dependencies>
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-jmx-plugin</artifactId>
-- <version>${rhq.version}</version>
-- <scope>test</scope>
-- </dependency>
-- </dependencies>
--
-- <!-- plugins - order is important -->
-- <modules>
-- <module>tomcat</module>
-- <module>jboss-as</module>
-- <module>hibernate</module>
-- <module>rhq-server</module>
-- <module>jboss-cache</module>
-- <module>jboss-as-5</module>
-- <module>jboss-cache-v3</module>
-- </modules>
--
-- <build>
-- <plugins>
-- <plugin>
-- <artifactId>maven-antrun-plugin</artifactId>
-- <executions>
-- <execution>
-- <phase>integration-test</phase>
-- <configuration>
-- <tasks>
-- <property name="test.classpath"
refid="maven.test.classpath" />
-- <echo>Validating plugins...</echo>
-- <java classname="org.rhq.core.pc.plugin.PluginValidator"
failonerror="true" fork="true">
-- <classpath>
-- <pathelement path="${test.classpath}" />
-- <pathelement
location="hibernate/target/jopr-hibernate-plugin-${project.version}.jar" />
-- <pathelement
location="tomcat/target/jopr-tomcat-plugin-${project.version}.jar" />
-- <pathelement
location="jboss-as/target/jopr-jboss-as-plugin-${project.version}.jar" />
-- <!-- TODO: Figure out why this is causing NoClassDefFoundErrors and fix it.
-->
-- <!--<pathelement
location="jboss-as-5/target/jopr-jboss-as-5-plugin-${project.version}.jar"
/>-->
-- <pathelement
location="rhq-server/target/jopr-rhq-server-plugin-${project.version}.jar"
/>
-- </classpath>
-- <sysproperty key="org.apache.commons.logging.Log"
value="org.apache.commons.logging.impl.SimpleLog" />
-- </java>
-- </tasks>
-- </configuration>
-- <goals>
-- <goal>run</goal>
-- </goals>
-- </execution>
-- </executions>
-- </plugin>
-- </plugins>
-- </build>
--
--</project>
++<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
++ <modelVersion>4.0.0</modelVersion>
++
++ <parent>
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-modules-parent</artifactId>
++ <version>2.4.0-SNAPSHOT</version>
++ </parent>
++
++ <!-- Plugin poms should not set their parent pom to this artifact. Instead, set to
"jopr-modules-parent". This is due to
++ the fact that this pom runs integration tests *on* the plugins. This pom must process
in the reactor after the
++ plugins themselves, as such, it can't be their parent. -->
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-plugins-parent</artifactId>
++ <version>2.3.0.EmbJopr.1.3.0-1</version>
++ <packaging>pom</packaging>
++ <name>Jopr Plugins</name>
++ <description>parent POM for all Jopr plugins</description>
++
++ <dependencies>
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-jmx-plugin</artifactId>
++ <version>${rhq.version}</version>
++ <scope>test</scope>
++ </dependency>
++ </dependencies>
++
++ <!-- plugins - order is important -->
++ <modules>
++ <module>tomcat</module>
++ <module>jboss-as</module>
++ <module>hibernate</module>
++ <module>rhq-server</module>
++ <module>jboss-cache</module>
++ <module>jboss-as-5</module>
++ <module>jboss-cache-v3</module>
++ </modules>
++
++ <build>
++ <plugins>
++ <plugin>
++ <artifactId>maven-antrun-plugin</artifactId>
++ <executions>
++ <execution>
++ <phase>integration-test</phase>
++ <configuration>
++ <tasks>
++ <property name="test.classpath"
refid="maven.test.classpath" />
++ <echo>Validating plugins...</echo>
++ <java classname="org.rhq.core.pc.plugin.PluginValidator"
failonerror="true" fork="true">
++ <classpath>
++ <pathelement path="${test.classpath}" />
++ <pathelement
location="hibernate/target/jopr-hibernate-plugin-${project.version}.jar" />
++ <pathelement
location="tomcat/target/jopr-tomcat-plugin-${project.version}.jar" />
++ <pathelement
location="jboss-as/target/jopr-jboss-as-plugin-${project.version}.jar" />
++ <!-- TODO: Figure out why this is causing NoClassDefFoundErrors and fix it.
-->
++ <!--<pathelement
location="jboss-as-5/target/jopr-jboss-as-5-plugin-${project.version}.jar"
/>-->
++ <pathelement
location="rhq-server/target/jopr-rhq-server-plugin-${project.version}.jar"
/>
++ </classpath>
++ <sysproperty key="org.apache.commons.logging.Log"
value="org.apache.commons.logging.impl.SimpleLog" />
++ </java>
++ </tasks>
++ </configuration>
++ <goals>
++ <goal>run</goal>
++ </goals>
++ </execution>
++ </executions>
++ </plugin>
++ </plugins>
++ </build>
++
++</project>
diff --cc modules/jopr/modules-pom.xml
index 7571c50,67e763b..de9ca10
--- a/modules/jopr/modules-pom.xml
+++ b/modules/jopr/modules-pom.xml
@@@ -1,131 -1,124 +1,124 @@@
--<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
--
-- <modelVersion>4.0.0</modelVersion>
--
-- <parent>
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-parent</artifactId>
-- <version>2.4.0-SNAPSHOT</version>
-- </parent>
--
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-modules-parent</artifactId>
-- <packaging>pom</packaging>
--
-- <name>Jopr Modules</name>
-- <description>parent POM for all Jopr modules</description>
--
- <scm>
-
<
connection>scm:svn:http://svn.jboss.org/repos/jopr/trunk/modules/</...
-
<
developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/trunk/mod...
- </scm>
-
-- <properties>
- <scm.module.path>modules/</scm.module.path>
-
-- <!-- dependency versions -->
-- <hibernate-all.version>1.0.0.Alpha9</hibernate-all.version>
-- <persistence-api.version>1.0</persistence-api.version>
-- </properties>
--
-- <!-- These dependencies are (mostly) common to the plugin artifacts built under
the
-- Jopr umbrella. They are not specific to the build a one particular artifact but
-- placed here to simplify the child poms. -->
-- <dependencies>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-util</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-domain</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-plugin-api</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-native-system</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-plugin-container</artifactId>
-- <version>${rhq.version}</version>
-- <scope>test</scope>
-- </dependency>
--
-- <!-- Third-party libs -->
-- <dependency>
-- <groupId>javax.xml.bind</groupId>
-- <artifactId>jaxb-api</artifactId>
-- <scope>provided</scope>
-- </dependency>
--
-- <dependency>
-- <groupId>com.sun.xml.bind</groupId>
-- <artifactId>jaxb-impl</artifactId>
-- <!-- NOTE: The version is defined in the root POM's dependencyManagement
section. -->
-- <scope>provided</scope>
-- </dependency>
--
-- <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes. It is fixed in JDK 6. -->
-- <dependency>
-- <groupId>javax.persistence</groupId>
-- <artifactId>persistence-api</artifactId>
-- <version>${persistence-api.version}</version>
-- <scope>provided</scope> <!-- by ON container -->
-- </dependency>
--
-- <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes; it is fixed in JDK 6. -->
-- <dependency>
-- <groupId>jboss.jboss-embeddable-ejb3</groupId>
-- <artifactId>hibernate-all</artifactId>
-- <version>${hibernate-all.version}</version>
-- <scope>provided</scope> <!-- by ON container -->
-- </dependency>
--
-- </dependencies>
--
-- <modules>
-- <module>common</module>
-- <module>plugins</module>
-- </modules>
--
-- <profiles>
--
-- <!-- enterprise-only stuff -->
-- <profile>
-- <id>enterprise</id>
-- <activation>
-- <activeByDefault>true</activeByDefault>
-- </activation>
-- <modules>
-- <module>serverplugins</module>
-- </modules>
-- </profile>
--
-- <!-- same as enterprise profile, except includes dist -->
-- <profile>
-- <id>dist</id>
-- <modules>
-- <module>serverplugins</module>
-- <module>dist</module>
-- </modules>
-- </profile>
--
-- </profiles>
--</project>
++<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
++
++ <modelVersion>4.0.0</modelVersion>
++
++ <parent>
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-parent</artifactId>
++ <version>2.4.0-SNAPSHOT</version>
++ </parent>
++
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-modules-parent</artifactId>
++ <packaging>pom</packaging>
++
++ <name>Jopr Modules</name>
++ <description>parent POM for all Jopr modules</description>
++
++ <properties>
++ <!-- dependency versions -->
++ <hibernate-all.version>1.0.0.Alpha9</hibernate-all.version>
++ <persistence-api.version>1.0</persistence-api.version>
++ </properties>
++
++ <!-- These dependencies are (mostly) common to the plugin artifacts built under
the
++ Jopr umbrella. They are not specific to the build a one particular artifact but
++ placed here to simplify the child poms. -->
++ <dependencies>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-util</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-domain</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-plugin-api</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-native-system</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-plugin-container</artifactId>
++ <version>${rhq.version}</version>
++ <scope>test</scope>
++ </dependency>
++
++ <!-- Third-party libs -->
++ <dependency>
++ <groupId>javax.xml.bind</groupId>
++ <artifactId>jaxb-api</artifactId>
++ <scope>provided</scope>
++ </dependency>
++
++ <dependency>
++ <groupId>com.sun.xml.bind</groupId>
++ <artifactId>jaxb-impl</artifactId>
++ <!-- NOTE: The version is defined in the root POM's dependencyManagement
section. -->
++ <scope>provided</scope>
++ </dependency>
++
++ <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes. It is fixed in JDK 6. -->
++ <dependency>
++ <groupId>javax.persistence</groupId>
++ <artifactId>persistence-api</artifactId>
++ <version>${persistence-api.version}</version>
++ <scope>provided</scope> <!-- by ON container -->
++ </dependency>
++
++ <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes; it is fixed in JDK 6. -->
++ <dependency>
++ <groupId>jboss.jboss-embeddable-ejb3</groupId>
++ <artifactId>hibernate-all</artifactId>
++ <version>${hibernate-all.version}</version>
++ <scope>provided</scope> <!-- by ON container -->
++ </dependency>
++
++ </dependencies>
++
++ <modules>
++ <module>common</module>
++ <module>plugins</module>
++ </modules>
++
++ <profiles>
++
++ <!-- enterprise-only stuff -->
++ <profile>
++ <id>enterprise</id>
++ <activation>
++ <activeByDefault>true</activeByDefault>
++ </activation>
++ <modules>
++ <module>serverplugins</module>
++ </modules>
++ </profile>
++
++ <!-- same as enterprise profile, except includes dist -->
++ <profile>
++ <id>dist</id>
++ <modules>
++ <module>serverplugins</module>
++ <module>dist</module>
++ </modules>
++ </profile>
++
++ </profiles>
++</project>
commit 123ac05d5c93d5f66961e435ccfb67d907c86287
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 10:23:05 2011 +0100
Turn down logging. Prevent a LazyLoadException for schedules.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index e670afb..ce4dd43 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -146,7 +146,7 @@ public class AbstractRestBean {
protected <T>T getFromCache(Fqn fqn,Class<T> clazz) {
Object o=null;
if (treeCache.exists(fqn)) {
- log.info("Hit for " + fqn.toString());
+ log.debug("Hit for " + fqn.toString());
try{
Node n = treeCache.get(fqn);
Set<Integer> readers= (Set<Integer>)
n.get("readers");
@@ -154,11 +154,10 @@ public class AbstractRestBean {
o = n.get("item");
}
else {
- log.info("No object for caller " +caller.toString() +
" found");
+ log.debug("No object for caller " +caller.toString() +
" found");
}
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
- log.info("Miss for " + fqn.toString());
+ log.debug("Miss for " + fqn.toString());
}
}
return (T)o;
@@ -187,9 +186,9 @@ public class AbstractRestBean {
treeCache.put(fqn,"readers",readers);
treeCache.put(fqn,"item",o);
success = true;
- log.info("Put " + fqn);
+ log.debug("Put " + fqn);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
return success;
}
@@ -235,7 +234,7 @@ public class AbstractRestBean {
childParentMap.put(res.getId(), pid);
treeCache.put(resourceMeta,"childParentMap",childParentMap);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -269,7 +268,7 @@ public class AbstractRestBean {
ret.add(getFromCache(resId, Resource.class));
}
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -287,7 +286,7 @@ public class AbstractRestBean {
if (visibleResources.contains(resourceid))
res = getFromCache(resourceid,Resource.class);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -316,7 +315,7 @@ public class AbstractRestBean {
if (treeCache.exists(fqn)) {
try {
treeCache.remove(fqn);
- log.info("Cancel " + fqn);
+ log.debug("Cancel " + fqn);
return true;
} catch (CacheException e) {
return false;
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 e500a33..91ed23a 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
@@ -200,7 +200,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if (scheduleType.equals("metric"))
scheduleType=DataType.MEASUREMENT.toString().toLowerCase();
- Resource res = fetchResource(resourceId);
+ Resource res = resMgr.getResource(caller,resourceId); // Don't fetch(), as
this would yield a LazyLoadException
Set<MeasurementSchedule> schedules = res.getSchedules();
List<MetricSchedule> ret = new
ArrayList<MetricSchedule>(schedules.size());
@@ -290,6 +290,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
Link link = new Link();
link.setRel("alert");
link.setHref("/alert/" + al.getId());
+ links.add(link);
}
return links;
}
commit 90081e71f34681d194767d00705d485a393ee133
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Nov 24 12:56:28 2011 +0100
[BZ 755653] - Make sure role membership is not updated through
the updateResourceGroup() methods.
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/group/ResourceGroup.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/group/ResourceGroup.java
index 564cef3..156253c 100644
---
a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/group/ResourceGroup.java
+++
b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/group/ResourceGroup.java
@@ -409,6 +409,10 @@ public class ResourceGroup extends Group {
@ManyToMany
private Set<Resource> implicitResources;
+ //TODO ResourceGroup probably isn't the right owning side of this relationship
+ //this means that any update of a group can update the role membership,
+ //which is not something one normally does. Maybe the more logical owner of this
+ //relationship would be the Role.
@JoinTable(name = "RHQ_ROLE_RESOURCE_GROUP_MAP", joinColumns = {
@JoinColumn(name = "RESOURCE_GROUP_ID") }, inverseJoinColumns = {
@JoinColumn(name = "ROLE_ID") })
@ManyToMany
private Set<Role> roles = new HashSet<Role>();
@@ -562,15 +566,22 @@ public class ResourceGroup extends Group {
}
public Set<Role> getRoles() {
+ //even though the field has an initializer, it can end up
+ //being null after deserialization. Because there is no
+ //setter and there are some other public methods depending
+ //on this not being null, let's make it lazy init.
+ if (roles == null) {
+ roles = new HashSet<Role>();
+ }
return roles;
}
public void addRole(Role role) {
- this.roles.add(role);
+ getRoles().add(role);
}
public void removeRole(Role role) {
- this.roles.remove(role);
+ getRoles().remove(role);
}
@NotNull
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/ResourceGroupManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/ResourceGroupManagerBean.java
index b8e52c4..82a91dc 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/ResourceGroupManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/group/ResourceGroupManagerBean.java
@@ -206,6 +206,11 @@ public class ResourceGroupManagerBean implements
ResourceGroupManagerLocal, Reso
+ "] does not have permission to modify Resource group with id
[" + groupId + "].");
}
+ //roles are not to be updated by this call but the group entity
+ //owns the relationship. Let's make sure we don't change the assigned
roles here.
+ group.getRoles().clear();
+ group.getRoles().addAll(attachedGroup.getRoles());
+
if (changeType == null) {
changeType = RecursivityChangeType.None;
if (attachedGroup.isRecursive() == true && group.isRecursive() ==
false) {
commit 18d73b987d8023c52c00d6fc99b73cab14528e44
Author: Jan Martiska <jmartisk(a)redhat.com>
Date: Thu Nov 24 13:50:58 2011 +0100
changed maven profiles for integration tests
diff --git a/modules/integration-tests/pom.xml b/modules/integration-tests/pom.xml
index 0341e53..0290729 100644
--- a/modules/integration-tests/pom.xml
+++ b/modules/integration-tests/pom.xml
@@ -36,10 +36,11 @@
It is fixed in JDK 6.
-->
<dependency>
- <groupId>javax.persistence</groupId>
- <artifactId>persistence-api</artifactId>
- <version>1.0</version>
- <scope>provided</scope> <!-- by RHQ container -->
+ <groupId>javax.persistence</groupId>
+ <artifactId>persistence-api</artifactId>
+ <version>1.0</version>
+ <scope>provided</scope>
+ <!-- by RHQ container -->
</dependency>
<!--
@@ -47,27 +48,30 @@
it is fixed in JDK 6.
-->
<dependency>
- <groupId>jboss.jboss-embeddable-ejb3</groupId>
- <artifactId>hibernate-all</artifactId>
- <version>1.0.0.Alpha9</version>
- <scope>provided</scope> <!-- by RHQ container -->
+ <groupId>jboss.jboss-embeddable-ejb3</groupId>
+ <artifactId>hibernate-all</artifactId>
+ <version>1.0.0.Alpha9</version>
+ <scope>provided</scope>
+ <!-- by RHQ container -->
</dependency>
</dependencies>
<profiles>
<profile>
- <id>as7-integration-test</id>
- <modules>
- <module>jboss-as-7-plugin-test</module>
- </modules>
- </profile>
- <profile>
- <id>apache-plugin-test</id>
- <modules>
- <module>apache-plugin-test</module>
- </modules>
- </profile>
+ <id>integration-tests</id>
+ <modules>
+ <module>apache-plugin-test</module>
+ <!--<module>mod_cluster-plugin-test</module>-->
+ </modules>
+ </profile>
+ <profile>
+ <id>as7-integration-test</id>
+ <!-- this one is separate from others, requires special setup -->
+ <modules>
+ <module>jboss-as-7-plugin-test</module>
+ </modules>
+ </profile>
</profiles>
commit 884eb4f2a4a609ff9541d9d8693133f79518a520
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Nov 23 15:26:04 2011 +0100
[BZ 690957] - EWS Tomcat is identical to "normal" tomcat when installed
through RPM (at least wrt system services installed)
Make sure that the operation execution logic doesn't get confused by not being
able to distinguish between them.
diff --git
a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatDiscoveryComponent.java
b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatDiscoveryComponent.java
index 7709b86..542f212 100644
---
a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatDiscoveryComponent.java
+++
b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatDiscoveryComponent.java
@@ -575,6 +575,14 @@ public class TomcatDiscoveryComponent implements
ResourceDiscoveryComponent, Man
return (isEWS(catalinaHome) && catalinaHome.endsWith(EWS_TOMCAT_6));
}
+ public static boolean isRPMTomcat5(String catalinaHome) {
+ return catalinaHome.endsWith(EWS_TOMCAT_5);
+ }
+
+ public static boolean isRPMTomcat6(String catalinaHome) {
+ return catalinaHome.endsWith(EWS_TOMCAT_6);
+ }
+
private void populateJMXConfiguration(Configuration configuration, String[]
commandLine) {
// null for manual add, properties will already be set
if (null == commandLine) {
diff --git
a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatServerOperationsDelegate.java
b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatServerOperationsDelegate.java
index ce542a9..f1e78f4 100644
---
a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatServerOperationsDelegate.java
+++
b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatServerOperationsDelegate.java
@@ -252,7 +252,7 @@ public class TomcatServerOperationsDelegate {
private ProcessExecution getRpmStart(Configuration pluginConfiguration) {
ProcessExecution processExecution;
String catalinaHome = this.serverComponent.getCatalinaHome().getPath();
- String rpm = TomcatDiscoveryComponent.isEWSTomcat6(catalinaHome) ?
TomcatDiscoveryComponent.EWS_TOMCAT_6
+ String rpm = TomcatDiscoveryComponent.isRPMTomcat6(catalinaHome) ?
TomcatDiscoveryComponent.EWS_TOMCAT_6
: TomcatDiscoveryComponent.EWS_TOMCAT_5;
processExecution = new ProcessExecution("service");
@@ -335,7 +335,7 @@ public class TomcatServerOperationsDelegate {
private ProcessExecution getRpmShutdown() {
ProcessExecution processExecution;
String catalinaHome = this.serverComponent.getCatalinaHome().getPath();
- String rpm = TomcatDiscoveryComponent.isEWSTomcat6(catalinaHome) ?
TomcatDiscoveryComponent.EWS_TOMCAT_6
+ String rpm = TomcatDiscoveryComponent.isRPMTomcat6(catalinaHome) ?
TomcatDiscoveryComponent.EWS_TOMCAT_6
: TomcatDiscoveryComponent.EWS_TOMCAT_5;
processExecution = new ProcessExecution("service");
commit f6cee08b05e8cebd4c28c6d7cb6de52ec0c5d180
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Nov 23 13:49:39 2011 +0100
BZ 755544 prevent NPE when no resource or group or metric were selected.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/groups/graph/ResourceGroupGraphPortlet.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/groups/graph/ResourceGroupGraphPortlet.java
index 87d1199..d91fc3b 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/groups/graph/ResourceGroupGraphPortlet.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/groups/graph/ResourceGroupGraphPortlet.java
@@ -54,10 +54,10 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
// A non-displayed, persisted identifier for the portlet
public static final String KEY = "ResourceGroupMetric";
- // A default displayed, persisted name for the portlet
+ // A default displayed, persisted name for the portlet
public static final String NAME = MSG.view_portlet_defaultName_groupMetric();
- // set on initial configuration, the window for this portlet view.
+ // set on initial configuration, the window for this portlet view.
private PortletWindow portletWindow;
public static final String CFG_RESOURCE_GROUP_ID = "resourceGroupId";
@@ -79,8 +79,13 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
}
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
-
setEntityId(storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID).getIntegerValue());
-
setDefinitionId(storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID).getIntegerValue());
+ Integer integerValue =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID).getIntegerValue();
+ if (integerValue!=null)
+ setEntityId(integerValue);
+
+ PropertySimple propertySimple =
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
+ if (propertySimple!=null && propertySimple.getIntegerValue()!=null)
+ setDefinitionId(propertySimple.getIntegerValue());
}
}
@@ -92,11 +97,12 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
protected void onDraw() {
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
- if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
- super.onDraw();
- } else {
+ PropertySimple simple =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID);
+ if (simple == null || simple.getIntegerValue()==null) {
removeMembers(getMembers());
addMember(new Label("<i>" +
MSG.view_portlet_configure_needed() + "</i>"));
+ } else {
+ super.onDraw();
}
}
@@ -113,7 +119,7 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
.extendLocatorId("Selector"), GroupCategory.COMPATIBLE, false);
resourceGroupSelector.setWidth(700);
resourceGroupSelector.setHeight(300);
- //TODO, would probaby be nice to find a way to seed assigned with the current
group
+ //TODO, would probaby be nice to find a way to seed assigned with the current
group
//ListGridRecord rec = new ListGridRecord();
//rec.setAttribute("id", getEntityId());
//rec.setAttribute("name", "current");
@@ -152,10 +158,14 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
final DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
- form.setValue(CFG_RESOURCE_GROUP_ID,
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID)
- .getIntegerValue());
- form.setValue(CFG_DEFINITION_ID,
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID)
- .getIntegerValue());
+ Integer integerValue =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID)
+ .getIntegerValue();
+ if (integerValue!=null)
+ form.setValue(CFG_RESOURCE_GROUP_ID, integerValue);
+
+ PropertySimple propertySimple =
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
+ if (propertySimple!=null && propertySimple.getIntegerValue()!=null)
+ form.setValue(CFG_DEFINITION_ID, propertySimple.getIntegerValue());
}
selectorItem.setCanvas(resourceGroupSelector);
@@ -184,10 +194,11 @@ public class ResourceGroupGraphPortlet extends
ResourceGroupMetricGraphView impl
removeMembers(getMembers());
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
- if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
- renderGraph();
- } else {
+ PropertySimple simple =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID);
+ if (simple == null | simple.getIntegerValue()==null) {
addMember(new Label("<i>" +
MSG.view_portlet_configure_needed() + "</i>"));
+ } else {
+ renderGraph();
}
}
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/resource/graph/ResourceGraphPortlet.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/resource/graph/ResourceGraphPortlet.java
index ad18fcb..7187066 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/resource/graph/ResourceGraphPortlet.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/dashboard/portlets/inventory/resource/graph/ResourceGraphPortlet.java
@@ -48,10 +48,10 @@ public class ResourceGraphPortlet extends ResourceMetricGraphView
implements Cus
// A non-displayed, persisted identifier for the portlet
public static final String KEY = "ResourceMetric";
- // A default displayed, persisted name for the portlet
+ // A default displayed, persisted name for the portlet
public static final String NAME = MSG.view_portlet_defaultName_resourceMetric();
- // set on initial configuration, the window for this portlet view.
+ // set on initial configuration, the window for this portlet view.
private PortletWindow portletWindow;
public static final String CFG_RESOURCE_ID = "resourceId";
@@ -73,8 +73,19 @@ public class ResourceGraphPortlet extends ResourceMetricGraphView
implements Cus
}
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID) != null) {
-
setEntityId(storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID).getIntegerValue());
-
setDefinitionId(storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID).getIntegerValue());
+ PropertySimple propertySimple =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID);
+ if (propertySimple!=null) {
+ Integer integerValue = propertySimple.getIntegerValue();
+ if (integerValue!=null)
+ setEntityId(integerValue);
+ }
+ propertySimple =
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
+ if (propertySimple!=null) {
+
+ Integer integerValue = propertySimple.getIntegerValue();
+ if (integerValue!=null)
+ setDefinitionId(integerValue);
+ }
}
}
@@ -86,11 +97,12 @@ public class ResourceGraphPortlet extends ResourceMetricGraphView
implements Cus
protected void onDraw() {
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
- if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID) != null) {
- super.onDraw();
- } else {
+ PropertySimple simple =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID);
+ if (simple == null || simple.getIntegerValue()==null) {
removeMembers(getMembers());
addMember(new Label("<i>" +
MSG.view_portlet_configure_needed() + "</i>"));
+ } else {
+ super.onDraw();
}
}
@@ -133,10 +145,15 @@ public class ResourceGraphPortlet extends ResourceMetricGraphView
implements Cus
});
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID) != null) {
- form.setValue(CFG_RESOURCE_ID,
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID)
- .getIntegerValue());
- form.setValue(CFG_DEFINITION_ID,
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID)
- .getIntegerValue());
+ Integer integerValue =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID).getIntegerValue();
+ if (integerValue!=null) {
+ form.setValue(CFG_RESOURCE_ID, integerValue);
+ }
+
+ PropertySimple propertySimple =
storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
+ if (propertySimple!=null && propertySimple.getIntegerValue()!=null)
{
+ form.setValue(CFG_DEFINITION_ID, propertySimple.getIntegerValue());
+ }
}
form.setFields(resourceLookupComboBoxItem, metric);
@@ -164,10 +181,11 @@ public class ResourceGraphPortlet extends ResourceMetricGraphView
implements Cus
removeMembers(getMembers());
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
- if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID) != null) {
- renderGraph();
- } else {
+ PropertySimple simple =
storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_ID);
+ if (simple == null || simple.getIntegerValue()==null) {
addMember(new Label("<i>" +
MSG.view_portlet_configure_needed() + "</i>"));
+ } else {
+ renderGraph();
}
}
commit bbb3015a3d7f7cbf8238853e262c64cf1740d381
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 22 16:31:21 2011 -0500
[Bug 756171 - The UI does not have a way to see the template from which a drift
definition is derived]
Add template and attached info to the definitions view. This implements
#2 of the three proposed enhancements and should be enough for the
first release. It adds two new columns, "Attached?" and
"Template",
which indicate whether the definition is attached or not,and to what
template. Note that detached definitions still have an associated template,
and may be re-attached.
Users with manage_settings can link to the template, for others the field
is not a link.
Given a lack of real-estate, the interval and drift handling columns have
been removed from the list row, and are now viewable only from the edit
view.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
index 90440dd..b29e9a6 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
@@ -21,6 +21,8 @@ package org.rhq.enterprise.gui.coregui.client.drift;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSRequest;
@@ -36,6 +38,7 @@ import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+import org.rhq.core.domain.authz.Permission;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.criteria.DriftDefinitionCriteria;
import org.rhq.core.domain.drift.DriftChangeSet;
@@ -49,6 +52,8 @@ 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.PermissionsLoadedListener;
+import org.rhq.enterprise.gui.coregui.client.PermissionsLoader;
import org.rhq.enterprise.gui.coregui.client.components.table.TimestampCellFormatter;
import org.rhq.enterprise.gui.coregui.client.gwt.DriftGWTServiceAsync;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
@@ -76,12 +81,15 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
public static final String ATTR_CHANGE_SET_VERSION = "changesetVersion";
public static final String ATTR_COMPLIANCE = "compliance";
public static final String ATTR_COMPLIANCE_ICON = "complianceIcon";
+ public static final String ATTR_ATTACHED = "attached";
+ public static final String ATTR_TEMPLATE = "template";
public static final String DRIFT_HANDLING_MODE_NORMAL =
MSG.view_drift_table_driftHandlingMode_normal();
public static final String DRIFT_HANDLING_MODE_PLANNED =
MSG.view_drift_table_driftHandlingMode_plannedChanges();
private DriftGWTServiceAsync driftService = GWTServiceLookup.getDriftService();
private EntityContext entityContext;
+ private Set<Permission> globalPermissions;
public DriftDefinitionDataSource() {
this(EntityContext.forSubsystemView());
@@ -168,17 +176,42 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
});
fields.add(inComplianceField);
+ /*
ListGridField driftHandlingModeField = new
ListGridField(ATTR_DRIFT_HANDLING_MODE, MSG
.view_drift_table_driftHandlingMode());
fields.add(driftHandlingModeField);
+ */
+ /*
ListGridField intervalField = new ListGridField(ATTR_INTERVAL,
MSG.common_title_interval());
fields.add(intervalField);
+ */
ListGridField baseDirField = new ListGridField(ATTR_BASE_DIR_STRING,
MSG.view_drift_table_baseDir());
changeSetTimeField.setCanSortClientOnly(true);
fields.add(baseDirField);
+ ListGridField attachedField = new ListGridField(ATTR_ATTACHED,
MSG.view_drift_table_attached());
+ fields.add(attachedField);
+
+ ListGridField templateField = new ListGridField(ATTR_TEMPLATE,
MSG.view_drift_table_template());
+ templateField.setCellFormatter(new CellFormatter() {
+ public String format(Object o, ListGridRecord listGridRecord, int i, int i1)
{
+ DriftDefinition def = (DriftDefinition)
listGridRecord.getAttributeAsObject(ATTR_ENTITY);
+ if (null != globalPermissions &&
globalPermissions.contains(Permission.MANAGE_SETTINGS)) {
+ int typeId = def.getResource().getResourceType().getId();
+ int templateId = def.getTemplate().getId();
+ String link = LinkManager.getDriftTemplateLink(typeId, templateId);
+ return SeleniumUtility.getLocatableHref(link, o.toString(), null);
+
+ } else {
+ return o.toString();
+ }
+ }
+ });
+ templateField.setCanSortClientOnly(true);
+ fields.add(templateField);
+
ListGridField editField = new ListGridField(ATTR_EDIT, MSG.common_title_edit());
editField.setType(ListGridFieldType.IMAGE);
editField.setAlign(Alignment.CENTER);
@@ -229,9 +262,11 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
nameField.setWidth("20%");
enabledField.setWidth(60);
inComplianceField.setWidth(100);
- driftHandlingModeField.setWidth("10%");
- intervalField.setWidth(100);
+ //driftHandlingModeField.setWidth("10%");
+ //intervalField.setWidth(100);
baseDirField.setWidth("*");
+ attachedField.setWidth(70);
+ templateField.setWidth("25%");
pinnedField.setWidth(70);
changeSetField.setWidth(70);
changeSetTimeField.setWidth(100);
@@ -244,9 +279,11 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
changeSetTimeField.setWidth(100);
enabledField.setWidth(60);
inComplianceField.setWidth(100);
- driftHandlingModeField.setWidth("15%");
- intervalField.setWidth(70);
+ //driftHandlingModeField.setWidth("15%");
+ //intervalField.setWidth(70);
baseDirField.setWidth("*");
+ attachedField.setWidth(70);
+ templateField.setWidth("25%");
pinnedField.setWidth(70);
editField.setWidth(70);
}
@@ -265,8 +302,21 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
processResponse(request.getRequestId(), response);
}
- public void onSuccess(PageList<DriftDefinitionComposite> result) {
- dataRetrieved(result, response, request);
+ public void onSuccess(final PageList<DriftDefinitionComposite>
result) {
+
+ // if we don't have them grab the global perms to see later if we
can link to to templates.
+ if (null == globalPermissions) {
+ new PermissionsLoader().loadExplicitGlobalPermissions(new
PermissionsLoadedListener() {
+ @Override
+ public void onPermissionsLoaded(Set<Permission>
permissions) {
+ globalPermissions = (permissions != null) ? permissions :
new HashSet<Permission>();
+ dataRetrieved(result, response, request);
+ }
+ });
+
+ } else {
+ dataRetrieved(result, response, request);
+ }
}
});
}
@@ -326,12 +376,14 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
}
criteria.fetchConfiguration(true);
+ criteria.fetchTemplate(true);
// filter out unsortable fields (i.e. fields sorted client-side only)
PageControl pageControl = getPageControl(request);
pageControl.removeOrderingField(ATTR_BASE_DIR_STRING);
pageControl.removeOrderingField(ATTR_CHANGE_SET_CTIME);
pageControl.removeOrderingField(ATTR_CHANGE_SET_VERSION);
+ pageControl.removeOrderingField(ATTR_TEMPLATE);
criteria.setPageControl(pageControl);
return criteria;
@@ -353,7 +405,7 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
ListGridRecord record = new ListGridRecord();
// We need this for Detect Now support
- record.setAttribute(ATTR_ENTITY, from.getDriftDefinition());
+ record.setAttribute(ATTR_ENTITY, def);
record.setAttribute(ATTR_ID, def.getId());
record.setAttribute(ATTR_NAME, def.getName());
@@ -369,6 +421,8 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
record.setAttribute(ATTR_EDIT, ImageManager.getEditIcon());
record.setAttribute(ATTR_IS_PINNED, def.isPinned() ? ImageManager.getPinnedIcon()
: ImageManager
.getUnpinnedIcon());
+ record.setAttribute(ATTR_ATTACHED, def.isAttached() ? MSG.common_val_yes() :
MSG.common_val_no());
+ record.setAttribute(ATTR_TEMPLATE, def.getTemplate().getName());
record.setAttribute(ATTR_CHANGE_SET_VERSION, (null != changeSet) ?
String.valueOf(changeSet.getVersion()) : MSG
.common_label_none());
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 5a5f154..c8df668 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
@@ -1206,6 +1206,7 @@ view_drift_success_deleteTemplate = Successfully deleted {0} drift
templates
view_drift_success_detectNow = Successfully submitted drift detection run request
view_drift_success_pinToDef = Successfully pinned snapshot {0} to drift definition.
view_drift_success_templateUpdated = Drift template updated and changes pushed to
attached definitions.
+view_drift_table_attached = Attached?
view_drift_table_baseDir = Base Directory
view_drift_table_driftHandlingMode = Drift Handling
view_drift_table_driftHandlingMode_normal = normal
@@ -1224,6 +1225,7 @@ view_drift_table_resourceDef = Resource Drift Detection Definition
view_drift_table_resourceHistory = Resource Drift History
view_drift_table_snapshot = Snapshot
view_drift_table_snapshotTime = Snapshot Time
+view_drift_table_template = Template
view_drift_table_title_initialSnapshot = Initial Snapshot for Definition [{0}] : Pinned =
[{1}]
view_drift_table_title_snapshot = Snapshot [{0}] for Definition [{1}]
view_drift_table_title_templateSnapshot = Pinned Snapshot for Template [{0}]
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 c5d56d5..76bbf6a 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
@@ -1054,6 +1054,7 @@ view_dashboards_title = Dashboard
##view_drift_success_detectNow = Successfully submitted drift detection run request
##view_drift_success_pinToDef = Successfully pinned snapshot {0} to drift definition.
##view_drift_success_templateUpdated = Drift template updated and changes pushed to
attached definitions.
+##view_drift_table_attached = Attached?
##view_drift_table_baseDir = Base Directory
##view_drift_table_driftHandlingMode = Drift Handling
##view_drift_table_driftHandlingMode_normal = normal
@@ -1072,6 +1073,7 @@ view_dashboards_title = Dashboard
##view_drift_table_resourceHistory = Resource Drift History
##view_drift_table_snapshot = Snapshot
##view_drift_table_snapshotTime = Snapshot Time
+##view_drift_table_template = Template
##view_drift_table_title_initialSnapshot = Initial Snapshot for Definition [{0}] : Pinned
= [{1}]
##view_drift_table_title_snapshot = Snapshot [{0}] for Definition [{1}]
##view_drift_table_title_templateSnapshot = Pinned Snapshot for Template [{0}]
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 d673c42..1242729 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
@@ -1196,6 +1196,7 @@ view_dashboards_title = ダッシュボード
##view_drift_success_detectNow = Successfully submitted drift detection run request
##view_drift_success_pinToDef = Successfully pinned snapshot {0} to drift definition.
##view_drift_success_templateUpdated = Drift template updated and changes pushed to
attached definitions.
+##view_drift_table_attached = Attached?
##view_drift_table_baseDir = Base Directory
##view_drift_table_driftHandlingMode = Drift Handling
##view_drift_table_driftHandlingMode_normal = normal
@@ -1214,6 +1215,7 @@ view_dashboards_title = ダッシュボード
##view_drift_table_resourceHistory = Resource Drift History
##view_drift_table_snapshot = Snapshot
##view_drift_table_snapshotTime = Snapshot Time
+##view_drift_table_template = Template
##view_drift_table_title_initialSnapshot = Initial Snapshot for Definition [{0}] : Pinned
= [{1}]
##view_drift_table_title_snapshot = Snapshot [{0}] for Definition [{1}]
##view_drift_table_title_templateSnapshot = Pinned Snapshot for Template [{0}]
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 56e46b1..6acfd8a 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
@@ -1225,6 +1225,7 @@ view_drift_success_delete = Drifts {0} exclu�dos com sucesso!
view_drift_success_detectNow = Requisi��o de detec��o de drift submetida com sucesso.
##view_drift_success_pinToDef = Successfully pinned snapshot {0} to drift definition.
##view_drift_success_templateUpdated = Drift template updated and changes pushed to
attached definitions.
+##view_drift_table_attached = Attached?
##view_drift_table_baseDir = Diret�rio Base
##view_drift_table_driftHandlingMode = Drift Handling
##view_drift_table_driftHandlingMode_normal = normal
@@ -1243,6 +1244,7 @@ view_drift_success_detectNow = Requisi��o de detec��o de drift
submetida
##view_drift_table_resourceHistory = Hist�rico de Drift do Recurso
##view_drift_table_snapshot = Snapshot
##view_drift_table_snapshotTime = Snapshot Time
+##view_drift_table_template = Template
##view_drift_table_title_initialSnapshot = Initial Snapshot for Definition [{0}] : Pinned
= [{1}]
##view_drift_table_title_snapshot = Snapshot [{0}] for Definition [{1}]
##view_drift_table_title_templateSnapshot = Pinned Snapshot for Template [{0}]
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 670769e..89b23e8 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
@@ -1205,6 +1205,7 @@ view_dashboards_title = \u7edf\u8ba1\u8868\u76d8
##view_drift_success_detectNow = Successfully submitted drift detection run request
##view_drift_success_pinToDef = Successfully pinned snapshot {0} to drift definition.
##view_drift_success_templateUpdated = Drift template updated and changes pushed to
attached definitions.
+##view_drift_table_attached = Attached?
##view_drift_table_baseDir = Base Directory
##view_drift_table_driftHandlingMode = Drift Handling
##view_drift_table_driftHandlingMode_normal = normal
@@ -1223,6 +1224,7 @@ view_dashboards_title = \u7edf\u8ba1\u8868\u76d8
##view_drift_table_resourceHistory = Resource Drift History
##view_drift_table_snapshot = Snapshot
##view_drift_table_snapshotTime = Snapshot Time
+##view_drift_table_template = Template
##view_drift_table_title_initialSnapshot = Initial Snapshot for Definition [{0}] : Pinned
= [{1}]
##view_drift_table_title_snapshot = Snapshot [{0}] for Definition [{1}]
##view_drift_table_title_templateSnapshot = Pinned Snapshot for Template [{0}]
commit b33a34080654eb5305c5a2e46ad18d730b95ed85
Author: Ian Springer <ian.springer(a)redhat.com>
Date: Tue Nov 22 16:27:16 2011 -0500
[BZ 756106] ensure resource classloader is initialized in processSyncInfo() prior to
making any resource component facet
calls (
https://bugzilla.redhat.com/show_bug.cgi?id=756106)
diff --git
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index e815315..2a5bd2f 100644
---
a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++
b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2010 Red Hat, Inc.
+ * Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -2494,6 +2494,9 @@ public class InventoryManager extends AgentService implements
ContainerService,
log.info("Got unknown resource: " + syncInfo.getId());
} else {
Resource resource = container.getResource();
+ // Ensure the Resource classloader is initialized on the Resource
container.
+ initResourceContainer(resource);
+
if (log.isDebugEnabled()) {
log.debug("Local Resource: id=" + resource.getId() +
", status=" + resource.getInventoryStatus()
+ ", mtime=" + resource.getMtime());
commit d211435be4d13204d34c3e3c90fea7f0b7c645e6
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 22 13:41:02 2011 -0500
Add new required method to mongo drift server impl
diff --git
a/modules/enterprise/server/plugins/drift-mongodb/src/main/java/org/rhq/enterprise/server/plugins/drift/mongodb/MongoDBDriftServer.java
b/modules/enterprise/server/plugins/drift-mongodb/src/main/java/org/rhq/enterprise/server/plugins/drift/mongodb/MongoDBDriftServer.java
index 028c1dd..a98ff59 100644
---
a/modules/enterprise/server/plugins/drift-mongodb/src/main/java/org/rhq/enterprise/server/plugins/drift/mongodb/MongoDBDriftServer.java
+++
b/modules/enterprise/server/plugins/drift-mongodb/src/main/java/org/rhq/enterprise/server/plugins/drift/mongodb/MongoDBDriftServer.java
@@ -359,4 +359,10 @@ public class MongoDBDriftServer implements DriftServerPluginFacet,
ServerPluginC
// TODO Auto-generated method stub
return null;
}
+
+ @Override
+ public byte[] getDriftFileAsByteArray(Subject subject, String hash) {
+ // TODO Auto-generated method stub
+ return null;
+ }
}
commit 77d9e38f36a5401ecb287ffea42335fc5c9e9463
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Tue Nov 22 13:40:42 2011 -0500
[Bug 753659 - Exception thrown when clicking Back to List after pinning snapshot to
template]
A subtle issue having to do with the fact that pinning can remove
existing entities therefore invalidating previous views. A couple of
changes to address this and make things, hopefully, more intuitive:
- When navigating from the deifinitions list to the initial snapshot the
"Back to List" button now brings you directly back to the defs list. It
previously would bring you to the carousel, which was confusing because
that's not where you navigated from.
- When pinning a snapshot to a template the wizard will, if successful,
navigate you back to the definitions list view automatically. This
higher level view is guaranteed to be valid, and will reflect any new
pinning-related state changes. On cancel or failure the use will
remain on the snapshot view.
will
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/LinkManager.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/LinkManager.java
index d3ef464..ef99372 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/LinkManager.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/LinkManager.java
@@ -28,6 +28,7 @@ import org.rhq.enterprise.gui.coregui.client.admin.roles.RolesView;
import
org.rhq.enterprise.gui.coregui.client.admin.templates.DriftDefinitionTemplateTypeView;
import org.rhq.enterprise.gui.coregui.client.admin.users.UsersView;
import org.rhq.enterprise.gui.coregui.client.components.table.StringIDTableSection;
+import org.rhq.enterprise.gui.coregui.client.drift.DriftDefinitionsView;
/**
* @author Greg Hinkle
@@ -514,7 +515,13 @@ public class LinkManager {
}
public static String getDriftDefinitionEditLink(int resourceId, int driftDefId) {
- return "#Resource/" + resourceId + "/Drift/Definitions/" +
driftDefId + "/Edit";
+ return "#Resource/" + resourceId + "/Drift/Definitions/" +
driftDefId + "/"
+ + DriftDefinitionsView.DetailView.Edit.name();
+ }
+
+ public static String getDriftDefinitionInitialSnapshotLink(int resourceId, int
driftDefId) {
+ return "#Resource/" + resourceId + "/Drift/Definitions/" +
driftDefId + "/"
+ + DriftDefinitionsView.DetailView.InitialSnapshot.name();
}
public static String getDriftCarouselDriftLink(int resourceId, int driftDefId, String
driftId) {
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
index 0781f22..90440dd 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionDataSource.java
@@ -112,8 +112,8 @@ public class DriftDefinitionDataSource extends
RPCDataSource<DriftDefinitionComp
public void onRecordClick(RecordClickEvent event) {
switch (entityContext.getType()) {
case Resource:
-
CoreGUI.goToView(LinkManager.getDriftCarouselSnapshotLink(entityContext.getResourceId(),
event
- .getRecord().getAttributeAsInt(ATTR_ID), 0));
+
CoreGUI.goToView(LinkManager.getDriftDefinitionInitialSnapshotLink(entityContext.getResourceId(),
+ event.getRecord().getAttributeAsInt(ATTR_ID)));
break;
default:
throw new IllegalArgumentException("Entity Type not
supported");
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionsView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionsView.java
index 118e074..619fba3 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionsView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftDefinitionsView.java
@@ -65,10 +65,14 @@ public class DriftDefinitionsView extends
TableSection<DriftDefinitionDataSource
private static final Criteria INITIAL_CRITERIA = new Criteria();
+ public enum DetailView {
+ Carousel, Edit, InitialSnapshot
+ };
+
private EntityContext context;
private boolean hasWriteAccess;
private DriftDefinitionDataSource dataSource;
- private boolean useEditDetailsView;
+ private DetailView detailsView = DetailView.Carousel;
static {
DriftCategory[] categoryValues = DriftCategory.values();
@@ -243,10 +247,17 @@ public class DriftDefinitionsView extends
TableSection<DriftDefinitionDataSource
@Override
public void renderView(ViewPath viewPath) {
- // we have two detail views for drift defs, the config editor and the carousel.
figure out which one we're
- // dealing with. The default is the carousel, anything further in the path we
assume to be /Edit
- if (!viewPath.isEnd()) {
- this.useEditDetailsView = !viewPath.isNextEnd() &&
"Edit".equals(viewPath.getNext().getPath());
+ // we have three detail views for drift defs, the carousel, the the config
editor, and the initial snapshot.
+ // Figure out which one we're dealing with. The default is the carousel.
+ this.detailsView = DetailView.Carousel;
+
+ if (!viewPath.isEnd() && !viewPath.isNextEnd()) {
+ if (DetailView.Edit.name().equals(viewPath.getNext().getPath())) {
+ this.detailsView = DetailView.Edit;
+
+ } else if
(DetailView.InitialSnapshot.name().equals(viewPath.getNext().getPath())) {
+ this.detailsView = DetailView.InitialSnapshot;
+ }
}
super.renderView(viewPath);
@@ -254,11 +265,17 @@ public class DriftDefinitionsView extends
TableSection<DriftDefinitionDataSource
@Override
public Canvas getDetailsView(Integer driftDefId) {
- if (this.useEditDetailsView) {
- return new
DriftDefinitionEditView(extendLocatorId("DefinitionEdit"), context, driftDefId,
hasWriteAccess);
- }
+ switch (detailsView) {
+ case Edit:
+ return new DriftDefinitionEditView(extendLocatorId("Edit"),
context, driftDefId, hasWriteAccess);
+
+ case InitialSnapshot:
+ return new DriftSnapshotView(extendLocatorId("InitialSnapshot"),
null, context.getResourceId(), driftDefId,
+ 0, hasWriteAccess);
- return new DriftCarouselView(extendLocatorId("Carousel"), context,
driftDefId, hasWriteAccess);
+ default:
+ return new DriftCarouselView(extendLocatorId("Carousel"), context,
driftDefId, hasWriteAccess);
+ }
}
public EntityContext getContext() {
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftSnapshotView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftSnapshotView.java
index 7d9a782..5ad8311 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftSnapshotView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftSnapshotView.java
@@ -239,7 +239,7 @@ public class DriftSnapshotView extends
Table<DriftSnapshotDataSource> {
}
private void pinToTemplate() {
- DriftPinTemplateWizard.showWizard(driftDefId, version, DriftSnapshotView.this);
+ DriftPinTemplateWizard.showWizard(driftDefId, version);
// we can refresh the table buttons immediately since the wizard is a dialog,
the
// user can't access enabled buttons anyway.
DriftSnapshotView.this.refreshTableInfo();
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftPinTemplateWizard.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftPinTemplateWizard.java
index 7f6c683..46b69b6 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftPinTemplateWizard.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftPinTemplateWizard.java
@@ -30,7 +30,7 @@ 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.components.table.Table;
+import org.rhq.enterprise.gui.coregui.client.LinkManager;
import org.rhq.enterprise.gui.coregui.client.components.wizard.WizardStep;
import org.rhq.enterprise.gui.coregui.client.gwt.GWTServiceLookup;
import
org.rhq.enterprise.gui.coregui.client.inventory.resource.type.ResourceTypeRepository;
@@ -41,13 +41,9 @@ import org.rhq.enterprise.gui.coregui.client.util.message.Message;
*/
public class DriftPinTemplateWizard extends AbstractDriftPinTemplateWizard {
- private Table<?> table;
-
- public DriftPinTemplateWizard(ResourceType resourceType, DriftDefinition driftDef,
int snapshotVersion,
- Table<?> table) {
+ public DriftPinTemplateWizard(ResourceType resourceType, DriftDefinition driftDef,
int snapshotVersion) {
super(resourceType, driftDef, snapshotVersion);
- this.table = table;
final ArrayList<WizardStep> steps = new ArrayList<WizardStep>();
@@ -108,19 +104,20 @@ public class DriftPinTemplateWizard extends
AbstractDriftPinTemplateWizard {
new
Message(MSG.view_drift_wizard_pinTemplate_success(template.getName()),
Message.Severity.Info));
getView().closeDialog();
- DriftPinTemplateWizard.this.table.refresh();
+
CoreGUI.goToView(LinkManager.getDriftDefinitionsLink(getSnapshotDriftDef().getResource().getId()),
+ true);
}
public void onFailure(Throwable caught) {
-
CoreGUI.getErrorHandler().handleError(MSG.view_drift_wizard_pinTemplate_failure(template.getName()),
- caught);
+ CoreGUI.getErrorHandler().handleError(
+ MSG.view_drift_wizard_pinTemplate_failure(template.getName()),
caught);
getView().closeDialog();
}
});
}
- public static void showWizard(final int snapshpotDriftDefId, final int
snapshotVersion, final Table<?> table) {
+ public static void showWizard(final int snapshpotDriftDefId, final int
snapshotVersion) {
// get the relevant DriftDefinition
DriftDefinitionCriteria ddc = new DriftDefinitionCriteria();
@@ -153,7 +150,7 @@ public class DriftPinTemplateWizard extends
AbstractDriftPinTemplateWizard {
public void onTypesLoaded(ResourceType type) {
DriftPinTemplateWizard wizard = new
DriftPinTemplateWizard(type, driftDef,
- snapshotVersion, table);
+ snapshotVersion);
wizard.startWizard();
}
});
commit 6fd3fb7dd614593adb01affb91a9ac97cc9d0633
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Tue Nov 22 10:42:38 2011 -0500
[BZ 755564] null check to avoid NPE. summary must be non-null as per data model
diff --git
a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceError.java
b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceError.java
index 5738e74..725ab53 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceError.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceError.java
@@ -141,7 +141,9 @@ public class ResourceError implements Serializable {
}
public void setSummary(String summary) {
- if (summary != null && summary.length() > MAX_SUMMARY_LENGTH) {
+ if (summary == null) {
+ summary = "An error occurred.";
+ } else if (summary.length() > MAX_SUMMARY_LENGTH) {
summary = summary.substring(0, MAX_SUMMARY_LENGTH - 3) + "...";
}
this.summary = summary;
commit 4917e09cd2575b093be2753d88be0176cfca551b
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Mon Nov 21 15:38:50 2011 -0500
[Bug 755712 - Drift carousel view previous button generates exception]
Fix some issues with carousel filtering/next/previous buttons. There
were a few issues in play.
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/carousel/Carousel.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/carousel/Carousel.java
index 8503487..55e3cbc 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/carousel/Carousel.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/components/carousel/Carousel.java
@@ -433,8 +433,11 @@ public abstract class Carousel extends LocatableHLayout implements
RefreshableVi
if (null != carouselStartFilter) {
int newEnd = carouselStartFilter + 1;
setCarouselEndFilter(newEnd);
+
+ // it's ok if this is higher than the current max, the actual fetch will
make sure the
+ // values are sane.
+ setCarouselStartFilter(carouselStartFilter + carouselSizeFilter);
}
- setCarouselStartFilter(null);
buildCarousel(true);
}
@@ -449,6 +452,7 @@ public abstract class Carousel extends LocatableHLayout implements
RefreshableVi
newStart = (newStart < carouselSizeFilter) ? carouselSizeFilter :
newStart;
setCarouselStartFilter(newStart);
}
+
setCarouselEndFilter(null);
buildCarousel(true);
@@ -803,7 +807,7 @@ public abstract class Carousel extends LocatableHLayout implements
RefreshableVi
}
try {
- carouselStartFilter = Integer.valueOf((String)
criteriaMap.get(FILTER_CAROUSEL_START));
+ carouselStartFilter = (Integer) criteriaMap.get(FILTER_CAROUSEL_START);
} catch (Exception e) {
carouselStartFilter = null;
}
@@ -1054,12 +1058,6 @@ public abstract class Carousel extends LocatableHLayout implements
RefreshableVi
}
protected void setCarouselStartFilter(Integer carouselStartFilter) {
- // bump up the start filter max if need be
- Integer max = getCarouselStartFilterMax();
- if (null != max && max < carouselStartFilter) {
- setCarouselStartFilterMax(carouselStartFilter);
- }
-
this.carouselStartFilter = carouselStartFilter;
if (null != carouselStartFilter) {
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
index 4489472..544ef81 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
@@ -70,7 +70,7 @@ public class DriftCarouselView extends BookmarkableCarousel implements
DetailsVi
private int driftDefId;
private EntityContext context;
private boolean hasWriteAccess;
- private Integer maxCarouselEndFilter;
+ private Integer maxSnapshotVersion;
private ArrayList<Record> selectedRecords = new ArrayList<Record>();
private boolean useDriftDetailsView;
@@ -157,11 +157,9 @@ public class DriftCarouselView extends BookmarkableCarousel
implements DetailsVi
}
});
+ // descending order, so highest changeset version first
if (null == carouselStart) {
carouselStart = changeSet.getVersion();
- if (null == maxCarouselEndFilter) {
- maxCarouselEndFilter = carouselStart;
- }
}
carouselEnd = changeSet.getVersion();
@@ -169,20 +167,20 @@ public class DriftCarouselView extends BookmarkableCarousel
implements DetailsVi
break;
}
}
+ if (null == maxSnapshotVersion || maxSnapshotVersion <
carouselStart) {
+ maxSnapshotVersion = carouselStart;
+ setCarouselStartFilterMax(maxSnapshotVersion);
+ }
+
+ setCarouselStartFilter(carouselStart);
+ setCarouselEndFilter(carouselEnd);
+ setCarouselSizeFilter(carouselSize);
if (!isRefresh) {
DriftCarouselView.super.onDraw();
} else {
DriftCarouselView.this.refreshCarouselInfo();
}
-
- Integer currentStartFilterMax = getCarouselStartFilterMax();
- if (null == currentStartFilterMax || currentStartFilterMax <
carouselStart) {
- setCarouselStartFilterMax(carouselStart);
- }
- setCarouselStartFilter(carouselStart);
- setCarouselEndFilter(carouselEnd);
- setCarouselSizeFilter(carouselSize);
}
public void onFailure(Throwable caught) {
@@ -220,30 +218,29 @@ public class DriftCarouselView extends BookmarkableCarousel
implements DetailsVi
* @param changeSetCriteria
*/
private void addCarouselCriteria(GenericDriftChangeSetCriteria changeSetCriteria) {
- Integer carouselStartFilter;
- Integer carouselEndFilter;
-
- // if no startFilter is set then include changesets up to the most recent
- try {
- carouselStartFilter = Integer.valueOf(getCarouselStartFilter());
- if (carouselStartFilter > 0) {
-
changeSetCriteria.addFilterEndVersion(String.valueOf(carouselStartFilter));
- }
- } catch (Exception e) {
- carouselStartFilter = null;
+ Integer startVersion; // low snapshot version (carouselEndFilter)
+ Integer endVersion; // high snapshot version (carouselStartFilter)
+
+ // if no startFilter is set then don't limit the endVersion
+ endVersion = getCarouselStartFilter();
+ if (null != endVersion) {
+ changeSetCriteria.addFilterEndVersion(String.valueOf(endVersion));
}
// if no endFilter is set then include changesets greater than 0 (never include
0, the initial snapshot)
- try {
- carouselEndFilter = Integer.valueOf(getCarouselEndFilter());
- if (carouselEndFilter < 1) {
- carouselEndFilter = 1;
- } else if (carouselEndFilter <= maxCarouselEndFilter) {
-
changeSetCriteria.addFilterStartVersion(String.valueOf(carouselEndFilter));
- }
- } catch (Exception e) {
- carouselEndFilter = null;
+ // else ensure endFilter is not greater than makes sense
+ startVersion = getCarouselEndFilter();
+ if (null == startVersion || 1 > startVersion) {
+ startVersion = 1;
+
+ } else if (null != endVersion && startVersion > endVersion) {
+ startVersion = endVersion;
+ }
+
+ if (null != maxSnapshotVersion && startVersion > maxSnapshotVersion)
{
+ startVersion = maxSnapshotVersion;
}
+ changeSetCriteria.addFilterStartVersion(String.valueOf(startVersion));
// apply the drift-level carousel filters in order to filter out changesets that
have no applicab;e drift
Criteria criteria = getCurrentCriteria();
commit cfbb12d5a736b1f01415ef33fbe7a0b0f8078380
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 21 10:46:18 2011 +0100
Tried to do some stuff wrt caching, simple operations can now be scheduled.
Fix operation history retrieval
Tiny cleanup.
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
index f458a8e..4381fbb 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
@@ -123,8 +123,8 @@
<attribute
name="wakeUpIntervalSeconds">5</attribute>
<!-- Cache wide default -->
<region name="/_default_">
- <attribute name="maxNodes">5000</attribute>
- <attribute
name="timeToLiveSeconds">1000</attribute>
+ <attribute name="maxNodes">50000</attribute>
+ <attribute
name="timeToLiveSeconds">3600</attribute>
</region>
<region name="/schedules">
<attribute name="maxNodes">5000</attribute>
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index 1be4db0..e670afb 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -25,12 +25,14 @@ package org.rhq.enterprise.server.rest;
import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@@ -51,6 +53,7 @@ import org.jboss.cache.Node;
import org.jboss.cache.TreeCacheMBean;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
@@ -61,7 +64,7 @@ import org.rhq.enterprise.server.rest.domain.ResourceWithType;
* @author Heiko W. Rupp
*/
@Stateless
-@Resource(name="cache",type= TreeCacheMBean.class,mappedName =
"RhqCache")
+(a)javax.annotation.Resource(name="cache",type= TreeCacheMBean.class,mappedName =
"RhqCache")
public class AbstractRestBean {
Log log = LogFactory.getLog(getClass().getName());
@@ -70,7 +73,7 @@ public class AbstractRestBean {
Subject caller;
/** The cache to use */
- @Resource(name="cache")
+ @javax.annotation.Resource(name="cache")
TreeCacheMBean treeCache;
/**
@@ -204,6 +207,94 @@ public class AbstractRestBean {
return putToCache(fqn,o);
}
+ protected void putResourceToCache(Resource res) {
+ putToCache(res.getId(),Resource.class,res);
+
+ Fqn callerFqn = new Fqn(new String[]{"user",
String.valueOf(caller.getId()),"resources"});
+ Set<Integer> visibleResources;
+ try {
+ if (treeCache.exists(callerFqn)) {
+ Node n = treeCache.get(callerFqn);
+ visibleResources = (Set<Integer>)
n.get("visibleResources");
+ }
+ else {
+ visibleResources = new HashSet<Integer>();
+ }
+ visibleResources.add(res.getId());
+ treeCache.put(callerFqn,"visibleResources",visibleResources);
+
+ Fqn resourceMeta = new Fqn(new String[] { "resourceMeta"});
+ Map<Integer,Integer> childParentMap;
+ if (treeCache.exists(resourceMeta)) {
+ childParentMap = (Map<Integer, Integer>)
treeCache.get(resourceMeta,"childParentMap");
+ }
+ else {
+ childParentMap = new HashMap<Integer,Integer>();
+ }
+ int pid = res.getParentResource() == null ? 0 :
res.getParentResource().getId();
+ childParentMap.put(res.getId(), pid);
+ treeCache.put(resourceMeta,"childParentMap",childParentMap);
+ } catch (CacheException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ }
+ }
+
+ protected List<Resource> getResourcesFromCacheByParentId(int pid) {
+ List<Integer> candidateIds = new ArrayList<Integer>();
+ List<Resource> ret = new ArrayList<Resource>();
+
+ // First determine candidate children
+ Map<Integer,Integer> childParentMap;
+ Fqn resourceMeta = new Fqn(new String[] { "resourceMeta"});
+ if (treeCache.exists(resourceMeta)) {
+ try {
+ childParentMap = (Map<Integer, Integer>)
treeCache.get(resourceMeta,"childParentMap");
+ for (Map.Entry<Integer,Integer> entry : childParentMap.entrySet())
{
+ if (entry.getValue() == pid)
+ candidateIds.add(entry.getKey());
+ }
+ // then see if the current user can see them
+ Fqn callerFqn = new Fqn(new String[]{"user",
String.valueOf(caller.getId()),"resources"});
+ Set<Integer> visibleResources = (Set<Integer>)
treeCache.get(callerFqn,"visibleResources");
+ Iterator<Integer> iter = candidateIds.iterator();
+ while (iter.hasNext()) {
+ Integer resId = iter.next();
+ if (!visibleResources.contains(resId)) {
+ iter.remove();
+ }
+ }
+
+ // Last but not least, get the resources and return them
+ for (Integer resId : candidateIds) {
+ ret.add(getFromCache(resId, Resource.class));
+ }
+ } catch (CacheException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ }
+
+ }
+ return ret;
+ }
+
+ protected Resource getResourceFromCache(int resourceid) {
+
+ Resource res = null;
+ // check if the current user can see the resource
+ Fqn callerFqn = new Fqn(new String[]{"user",
String.valueOf(caller.getId()),"resources"});
+ if (treeCache.exists(callerFqn)) {
+ try {
+ Set<Integer> visibleResources = (Set<Integer>)
treeCache.get(callerFqn,"visibleResources");
+ if (visibleResources.contains(resourceid))
+ res = getFromCache(resourceid,Resource.class);
+ } catch (CacheException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ }
+ }
+
+ return res;
+ }
+
+
/**
* Construct a Fqn object from the passed data
* @param id Id of the target object
@@ -244,6 +335,8 @@ public class AbstractRestBean {
if (parent!=null) {
rwt.setParentId(parent.getId());
}
+ else
+ rwt.setParentId(0);
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/operation/definitions");
uriBuilder.queryParam("resourceId",res.getId());
@@ -251,6 +344,38 @@ public class AbstractRestBean {
Link link = new Link("operationDefinitions",uri.toString());
rwt.addLink(link);
+/*
+ for (Resource child : getResourcesFromCacheByParentId(res.getId())) {
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/resource/{id}");
+ uri = uriBuilder.build(child.getId());
+ link = new Link("child",uri.toString());
+ rwt.addLink(link);
+ }
+*/
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/resource/{id}");
+ uri = uriBuilder.build(res.getId());
+ link = new Link("self",uri.toString());
+ rwt.addLink(link);
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/resource/{id}/schedules");
+ uri = uriBuilder.build(res.getId());
+ link = new Link("schedules",uri.toString());
+ rwt.addLink(link);
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/resource/{id}/children");
+ uri = uriBuilder.build(res.getId());
+ link = new Link("children",uri.toString());
+ rwt.addLink(link);
+ if (parent!=null) {
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/resource/{id}/");
+ uri = uriBuilder.build(parent.getId());
+ link = new Link("parent",uri.toString());
+ rwt.addLink(link);
+ }
+
return rwt;
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
index 627c22b..d742139 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -1,7 +1,5 @@
package org.rhq.enterprise.server.rest;
-import java.util.List;
-
import javax.ejb.Local;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
@@ -10,7 +8,6 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -18,9 +15,9 @@ import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.jboss.resteasy.annotations.cache.Cache;
+
import org.rhq.enterprise.server.rest.domain.GroupRest;
-import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
* Handler for Group related things
@@ -36,6 +33,7 @@ public interface GroupHandlerLocal {
public Response getGroups(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo);
+ @Cache(isPrivate = true,maxAge = 60)
@GET
@Path("{id}")
public Response getGroup(@PathParam("id") int id,@Context Request request,
@Context HttpHeaders headers,
@@ -61,6 +59,7 @@ public interface GroupHandlerLocal {
@GET
@Path("{id}/resources")
+ @Cache(isPrivate = true,maxAge = 60)
public Response getResources(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
@Context UriInfo uriInfo);
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 2a4f7df..536274a 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
@@ -9,12 +9,13 @@ import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
@@ -22,9 +23,17 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.configuration.definition.PropertyDefinition;
+import org.rhq.core.domain.criteria.OperationHistoryCriteria;
+import org.rhq.core.domain.criteria.ResourceOperationHistoryCriteria;
+import org.rhq.core.domain.operation.HistoryJobId;
+import org.rhq.core.domain.operation.JobId;
import org.rhq.core.domain.operation.OperationDefinition;
+import org.rhq.core.domain.operation.OperationHistory;
+import org.rhq.core.domain.operation.ResourceOperationHistory;
+import org.rhq.core.domain.operation.bean.ResourceOperationSchedule;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.operation.OperationManagerLocal;
@@ -86,19 +95,14 @@ public class OperationsHandlerBean extends AbstractRestBean implements
Operation
}
- CacheControl cc = new CacheControl();
- cc.setMaxAge(1200); // Definitions are very stable, so caching for 20mins is ok
- cc.setPrivate(false);
builder.tag(eTag);
- builder.cacheControl(cc);
return builder.build();
}
@Override
- // TODO find out why a return type of Response lets this fail for XML (even with
@Wrapped annotation
- public List<OperationDefinitionRest> getOperationDefinitions(Integer
resourceId, UriInfo uriInfo, Request request) {
+ public Response getOperationDefinitions(Integer resourceId, UriInfo uriInfo, Request
request) {
if (resourceId == null)
throw new ParameterMissingException("resourceId");
@@ -109,16 +113,15 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
ResourceType resourceType = res.getResourceType();
-/*
EntityTag eTag = new EntityTag(Integer.toHexString(resourceType.hashCode()));
Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
if (builder==null) {
-*/
Set<OperationDefinition> opDefList =
resourceType.getOperationDefinitions();
List<OperationDefinitionRest> resultList = new
ArrayList<OperationDefinitionRest>(opDefList.size());
for (OperationDefinition def : opDefList) {
+ putToCache(def.getId(),OperationDefinition.class,def);
OperationDefinitionRest odr = new OperationDefinitionRest();
odr.setId(def.getId());
odr.setName(def.getName());
@@ -130,21 +133,13 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
resultList.add(odr);
}
- return resultList;
-
-/*
- builder = Response.ok(resultList);
+ GenericEntity<List<OperationDefinitionRest>> entity = new
GenericEntity<List<OperationDefinitionRest>>(resultList){};
+ builder = Response.ok(entity);
}
- CacheControl cc = new CacheControl();
- cc.setMaxAge(1200); // Definitions are very stable, so caching for 20mins is ok
- cc.setPrivate(false);
builder.tag(eTag);
- builder.cacheControl(cc);
-
return builder.build();
-*/
}
@@ -203,19 +198,30 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
if (operation.getState().equals("creating") &&
operation.getDefinitionId()>0 && !operation.getName().isEmpty()) {
// TODO check all the required parameters for persence before allowing to
submit
- Link link = new Link("submit","TODO");
- operation.addLink(link);
-
+ operation.setState("ready");
}
- UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/operation/{id}");
- URI uri = uriBuilder.build(operation.getId());
- Link editLink = new Link("edit",uri.toString());
- operation.addLink(editLink);
+ if (operation.getState().equals("ready")) {
+ // todo check params
+
+ // submit
+ ResourceOperationSchedule sched =
opsManager.scheduleResourceOperation(caller,operation.getResourceId(),operation.getName(),0,0,0,-1,new
Configuration(),"TEst");
+ JobId jobId = new JobId(sched.getJobName(),sched.getJobGroup());
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/history/{id}");
+ URI uri = uriBuilder.build(jobId);
+ Link histLink = new Link("history",uri.toString());
+ operation.addLink(histLink);
+ }
+ else {
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/{id}");
+ URI uri = uriBuilder.build(operation.getId());
+ Link editLink = new Link("edit",uri.toString());
+ operation.addLink(editLink);
+ }
// Update item in cache
putToCache(operation.getId(),OperationRest.class,operation);
-
Response.ResponseBuilder builder = Response.ok(operation);
return builder.build();
}
@@ -233,4 +239,30 @@ public class OperationsHandlerBean extends AbstractRestBean
implements Operation
return null; // TODO: Customise this generated block
}
+ @Override
+ @GET
+ @Path("history/{id}")
+ public Response outcome(@PathParam("id") String jobName) {
+
+/*
+ ResourceOperationSchedule schedule =
opsManager.getResourceOperationSchedule(caller,operationHistoryId);
+ if (schedule==null)
+ throw new StuffNotFoundException("OperationSchedule with id " +
operationHistoryId);
+*/
+
+ ResourceOperationHistoryCriteria criteria = new
ResourceOperationHistoryCriteria();
+ criteria.addFilterJobId(new JobId(jobName));
+
+ OperationHistory history ;//=
opsManager.getOperationHistoryByJobId(caller,jobName);
+ List<ResourceOperationHistory> list =
opsManager.findResourceOperationHistoriesByCriteria(caller,criteria);
+ if (list==null || list.isEmpty())
+ throw new StuffNotFoundException("OperationHistory with id " + new
HistoryJobId(jobName));
+
+ history = list.get(0);
+ String ret = history.getStatus().getDisplayName();
+ // TODO get history.results with the help of the property definition
+
+ return Response.ok(ret).build();
+
+ }
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
index 5ab7366..1d2a9ed 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
@@ -19,6 +19,8 @@ import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.jboss.resteasy.annotations.cache.Cache;
+
import org.rhq.enterprise.server.rest.domain.OperationDefinitionRest;
import org.rhq.enterprise.server.rest.domain.OperationRest;
@@ -28,12 +30,13 @@ import org.rhq.enterprise.server.rest.domain.OperationRest;
*/
@Local
@Path("/operation")
+(a)Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public interface OperationsHandlerLocal {
@GET
@Path("definition/{id}")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ @Cache(maxAge = 1200)
public Response getOperationDefinition(@PathParam("id") int definitionId,
@QueryParam("resourceId") Integer
resourceId,
@Context UriInfo uriInfo,
@@ -42,33 +45,32 @@ public interface OperationsHandlerLocal {
@GET
@Path("definitions")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
- public List<OperationDefinitionRest>
getOperationDefinitions(@QueryParam("resourceId") Integer resourceId,
+ @Cache(maxAge = 1200)
+ public Response getOperationDefinitions(@QueryParam("resourceId") Integer
resourceId,
@Context UriInfo uriInfo,
@Context Request request
);
@POST
@Path("definition/{id}")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response createOperation(@PathParam("id") int definitionId,
@QueryParam("resourceId") Integer
resourceId,
@Context UriInfo uriInfo);
@GET
@Path("{id}")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response getOperation(@PathParam("id") int operationId);
@PUT
@Path("{id}")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateOperation(@PathParam("id") int operationId,
OperationRest operation, @Context UriInfo uriInfo);
@DELETE
@Path("{id}")
- @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response cancelOperation(@PathParam("id") int operationId);
+ @GET
+ @Path("history/{id}")
+ public Response outcome(@PathParam("id") String jobName);
}
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 f90143a..e500a33 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
@@ -31,7 +31,6 @@ import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
-import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.GenericEntity;
@@ -42,8 +41,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
-import com.arjuna.ats.internal.jdbc.drivers.modifiers.list;
-
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.measurement.Availability;
@@ -85,29 +82,14 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
public Response getResource(int id, @Context Request request, @Context HttpHeaders
headers,
@Context UriInfo uriInfo) {
- // Create a cache control
- CacheControl cc = new CacheControl();
- cc.setMaxAge(300); // Resources are valid for 5 mins
- cc.setPrivate(false); // Proxies may cache this
-
Resource res;
- res = getFromCache(id,Resource.class);
- if (res==null) {
- res = resMgr.getResource(caller, id);
- if (res!=null)
- putToCache(id, Resource.class,res);
- else
- throw new StuffNotFoundException("Resource with id " + id);
- }
-
- Response.ResponseBuilder builder=null;
+ res = fetchResource(id);
long mtime = res.getMtime();
EntityTag eTag = new EntityTag(Long.toOctalString(res.hashCode()+ mtime)); //
factor in mtime in etag
- builder = request.evaluatePreconditions(new Date(mtime),eTag);
+ Response.ResponseBuilder builder = request.evaluatePreconditions(new
Date(mtime),eTag);
if (builder!=null) {
- builder.cacheControl(cc);
return builder.build();
}
@@ -218,7 +200,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if (scheduleType.equals("metric"))
scheduleType=DataType.MEASUREMENT.toString().toLowerCase();
- Resource res = resMgr.getResource(caller, resourceId);
+ Resource res = fetchResource(resourceId);
Set<MeasurementSchedule> schedules = res.getSchedules();
List<MetricSchedule> ret = new
ArrayList<MetricSchedule>(schedules.size());
@@ -273,18 +255,10 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
@Context UriInfo uriInfo) {
PageControl pc = new PageControl();
Resource parent;
- parent = getFromCache(id,Resource.class);
- if (parent==null) {
- parent = resMgr.getResource(caller,id);
- if (parent==null)
- throw new StuffNotFoundException("Resource with id " + id);
- else
- putToCache(id,Resource.class,parent);
- }
+ parent = fetchResource(id);
List<Resource> ret =
resMgr.findResourceByParentAndInventoryStatus(caller,parent,InventoryStatus.COMMITTED,pc);
List<ResourceWithType> rwtList = new
ArrayList<ResourceWithType>(ret.size());
for (Resource r: ret) {
- putToCache(r.getId(),Resource.class,r);
ResourceWithType rwt = fillRWT(r, uriInfo);
rwtList.add(rwt);
}
@@ -319,4 +293,18 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
}
return links;
}
+
+ private Resource fetchResource(int resourceId) {
+ Resource res;
+ res = getFromCache(resourceId,Resource.class);
+ if (res==null) {
+ res = resMgr.getResource(caller, resourceId);
+ if (res!=null)
+ putToCache(resourceId, Resource.class,res);
+ else
+ throw new StuffNotFoundException("Resource with id " +
resourceId);
+ }
+ return res;
+ }
+
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index 412b347..08a1905 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -34,6 +34,7 @@ import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
+import org.jboss.resteasy.annotations.cache.Cache;
import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;
import org.jboss.resteasy.links.LinkResources;
@@ -59,12 +60,13 @@ public interface ResourceHandlerLocal {
})
@GET
@Path("/{id}")
-
+ @Cache(isPrivate = true,maxAge = 120)
Response getResource(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo);
@GET
@Path("/platforms")
+ @Cache(isPrivate = true,maxAge = 300)
Response getPlatforms(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo);
@@ -82,6 +84,7 @@ public interface ResourceHandlerLocal {
@GET
@Path("/{id}/schedules")
@LinkResource(rel="schedules",value = MetricSchedule.class)
+ @Cache(isPrivate = true,maxAge = 60)
Response getSchedules(@PathParam("id") int resourceId,
@QueryParam("type")
@DefaultValue("all") String scheduleType,
@Context Request request,
commit 1527222e6a0c560401e29300ffc26e1ac807a1e9
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Nov 15 10:22:42 2011 +0100
Use the correct data type as input for group creation. Add some more attributes to a
group.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index 5ecbb86..76180d9 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -28,8 +28,11 @@ import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceTypeManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
import org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.rest.domain.GroupRest;
@@ -52,6 +55,9 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
@EJB
ResourceManagerLocal resourceManager;
+ @EJB
+ ResourceTypeManagerLocal resourceTypeManager;
+
public Response getGroups(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroupCriteria criteria = new ResourceGroupCriteria();
@@ -104,19 +110,33 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
@Override
@POST
@Path("/")
- public Response createGroup(ResourceGroup group, @Context Request request, @Context
HttpHeaders headers, @Context UriInfo uriInfo) {
+ public Response createGroup(GroupRest group, @Context Request request, @Context
HttpHeaders headers, @Context UriInfo uriInfo) {
+
+ ResourceGroup newGroup = new ResourceGroup(group.getName());
+ if (group.getResourceTypeId()!=null) {
+
+ ResourceType resourceType = null;
+ try {
+ resourceType =
resourceTypeManager.getResourceTypeById(caller,group.getResourceTypeId());
+ newGroup.setResourceType(resourceType);
+ } catch (ResourceTypeNotFoundException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ throw new StuffNotFoundException("ResourceType with id " +
group.getResourceTypeId());
+ }
+ }
Response.ResponseBuilder builder;
try {
- ResourceGroup newGroup =
resourceGroupManager.createResourceGroup(caller,group);
+ newGroup = resourceGroupManager.createResourceGroup(caller,newGroup);
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/{id}");
URI uri = uriBuilder.build(newGroup.getId());
builder=Response.created(uri);
+ putToCache(newGroup.getId(),ResourceGroup.class,newGroup);
} catch (Exception e) {
e.printStackTrace(); // TODO: Customise this generated block
- builder=Response.serverError();
+ builder=Response.status(Response.Status.NOT_ACCEPTABLE);
}
return builder.build();
}
@@ -135,6 +155,7 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
try {
resourceGroup =
resourceGroupManager.updateResourceGroup(caller,resourceGroup);
builder=Response.ok(fillGroup(resourceGroup,uriInfo));
+ putToCache(resourceGroup.getId(),ResourceGroup.class,resourceGroup);
}
catch (Exception e) {
builder = Response.status(Response.Status.NOT_ACCEPTABLE); // TODO correct?
@@ -150,6 +171,7 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
try {
resourceGroupManager.deleteResourceGroup(caller,id);
+ removeFromCache(id,ResourceGroup.class);
return Response.ok().build();
} catch (ResourceGroupDeleteException e) {
e.printStackTrace(); // TODO: Customise this generated block
@@ -196,8 +218,13 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
if (res==null)
throw new StuffNotFoundException("Resource with id " +
resourceId);
- // TODO do we want/ need to check that the group category stays the same?
+ // A resource type is set for the group, so only allow to add resources with the
same type.
+ if (resourceGroup.getResourceType()!=null) {
+ if (!res.getResourceType().equals(resourceGroup.getResourceType()))
+ return Response.status(Response.Status.NOT_ACCEPTABLE).build();
+ }
+ // TODO if comp group and no resourceTypeId set, shall we allow to have it change
to a mixed group?
resourceGroup.addExplicitResource(res);
return Response.ok().build(); // TODO right code?
@@ -246,6 +273,7 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
GroupRest gr = new GroupRest(group.getName());
gr.setId(group.getId());
gr.setCategory(group.getGroupCategory());
+ gr.setRecursive(group.isRecursive());
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("/group/{id}");
URI uri = uriBuilder.build(group.getId());
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
index 88a3610..627c22b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -44,7 +44,7 @@ public interface GroupHandlerLocal {
@POST
@Path("/")
- public Response createGroup(ResourceGroup group, @Context Request request, @Context
HttpHeaders headers,
+ public Response createGroup(GroupRest group, @Context Request request, @Context
HttpHeaders headers,
@Context UriInfo uriInfo);
@PUT
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
index c55223d..4c9079b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
@@ -18,6 +18,8 @@ public class GroupRest {
int id;
private String name;
+ private Integer resourceTypeId;
+ private boolean recursive;
private GroupCategory category;
List<Link> links = new ArrayList<Link>();
@@ -45,6 +47,22 @@ public class GroupRest {
this.name = name;
}
+ public Integer getResourceTypeId() {
+ return resourceTypeId;
+ }
+
+ public void setResourceTypeId(Integer resourceTypeId) {
+ this.resourceTypeId = resourceTypeId;
+ }
+
+ public boolean isRecursive() {
+ return recursive;
+ }
+
+ public void setRecursive(boolean recursive) {
+ this.recursive = recursive;
+ }
+
public GroupCategory getCategory() {
return category;
}
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
index 90bbfac..6edaf99 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
@@ -36,6 +36,14 @@
<tr>
<td>Category</td><td>${var.category}</td>
</tr>
+ <#if var.resourceTypeId??>
+ <tr>
+ <td>ResourceType id</td><td></td>
+ </tr>
+ </#if>
+ <tr>
+
<td>Recursive</td><td>${var.recursive?string("Yes","No")}</td>
+ </tr>
</table>
<a
href="/rest/1/group/${var.id?c}/resources.html">Resources</a><br/>
</html>
commit 7317b864fa22275d1784edb410d37ef65c84c6d2
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 14 22:55:17 2011 +0100
Do some caching of the group.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index ea3d2ec..5ecbb86 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -228,9 +228,14 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
* @throws StuffNotFoundException if the group is not found (or not accessible by the
caller)
*/
private ResourceGroup fetchGroup(int groupId) {
- ResourceGroup resourceGroup = resourceGroupManager.getResourceGroup(caller,
groupId);
- if (resourceGroup==null)
- throw new StuffNotFoundException("Group with id " + groupId);
+ ResourceGroup resourceGroup;
+ resourceGroup = getFromCache(groupId,ResourceGroup.class);
+ if (resourceGroup==null) {
+ resourceGroup = resourceGroupManager.getResourceGroup(caller, groupId);
+ if (resourceGroup==null)
+ throw new StuffNotFoundException("Group with id " + groupId);
+ putToCache(groupId,ResourceGroup.class,resourceGroup);
+ }
return resourceGroup;
}
commit 2920cb4967c49b083198111032d62bc8e8426b4e
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 14 21:55:30 2011 +0100
Allow updates to the group name.
Remove non-needed method.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index a227a18..ea3d2ec 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -124,9 +124,22 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
@Override
@PUT
@Path("{id}")
- public Response updateGroup(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
+ public Response updateGroup(@PathParam("id") int id, GroupRest in, @Context
Request request,
+ @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
- return null; // TODO: Customise this generated block
+
+ ResourceGroup resourceGroup = fetchGroup(id);
+ resourceGroup.setName(in.getName());
+ Response.ResponseBuilder builder;
+
+ try {
+ resourceGroup =
resourceGroupManager.updateResourceGroup(caller,resourceGroup);
+ builder=Response.ok(fillGroup(resourceGroup,uriInfo));
+ }
+ catch (Exception e) {
+ builder = Response.status(Response.Status.NOT_ACCEPTABLE); // TODO correct?
+ }
+ return builder.build();
}
@Override
@@ -172,18 +185,6 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
}
-
- @Override
- @GET
- @Path("{id}/resource/{resourceId}")
- public Response getResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
- @Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
-
-
-
- return null; // TODO: Customise this generated block
- }
-
@Override
@PUT
@Path("{id}/resource/{resourceId}")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
index 6c91fbe..88a3610 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -19,6 +19,7 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.enterprise.server.rest.domain.GroupRest;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
@@ -48,8 +49,9 @@ public interface GroupHandlerLocal {
@PUT
@Path("{id}")
- public Response updateGroup(@PathParam("id") int id,@Context Request
request, @Context HttpHeaders headers,
- @Context UriInfo uriInfo);
+ public Response updateGroup(@PathParam("id") int id, GroupRest in, @Context
Request request,
+ @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
@DELETE
@Path("{id}")
@@ -63,12 +65,6 @@ public interface GroupHandlerLocal {
@Context UriInfo uriInfo);
- @GET
- @Path("{id}/resource/{resourceId}")
- public Response getResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
- @Context Request request, @Context HttpHeaders headers,
- @Context UriInfo uriInfo);
-
@PUT
@Path("{id}/resource/{resourceId}")
public Response addResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
commit 6f207bc8f0ec6126d94342f4f76ae6817b426320
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 14 21:36:05 2011 +0100
Improvements in group handling.
Make resource id an element and not an attribute.
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/index.html
b/modules/enterprise/gui/rest-war/src/main/webapp/index.html
index 7c28583..76aa220 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/index.html
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/index.html
@@ -10,6 +10,7 @@ See also <a
href="http://rhq-project.org/display/RHQ/Design-REST">the RHQ wiki</
mobile client (Android) of the API.
<ul>
<li><a
href="1/resource/platforms.html">Platforms</a></li>
+ <li><a href="1/group/">Groups</a></li>
<li><a href="1/alert/">Alerts</a></li>
<li><a href="1/status.html">System status</a>
</li>
</ul>
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
index 6f2c6b0..a227a18 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -15,7 +15,9 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
@@ -24,13 +26,14 @@ import javax.ws.rs.core.UriInfo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.rhq.core.domain.authz.Role;
+import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
import org.rhq.enterprise.server.rest.domain.GroupRest;
+import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
@@ -49,15 +52,28 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
@EJB
ResourceManagerLocal resourceManager;
- @Override
- @GET
- @Path("/")
public Response getGroups(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
- Set<Role> roles = caller.getRoles();
+ ResourceGroupCriteria criteria = new ResourceGroupCriteria();
+ List<ResourceGroup> groups =
resourceGroupManager.findResourceGroupsByCriteria(caller,criteria);
+
+ List<GroupRest> list = new ArrayList<GroupRest>(groups.size());
+ for (ResourceGroup group : groups) {
+ list.add(fillGroup(group, uriInfo));
+ }
+
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
+
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listGroup", list),
mediaType);
+ }
+ else {
+ GenericEntity<List<GroupRest>> ret = new
GenericEntity<List<GroupRest>>(list) {};
+ builder = Response.ok(ret);
+ }
-// resourceGroupManager.findAvailableResourceGroupsForRole(caller,roleId,new
int[]{}, PageControl.SIZE_UNLIMITED);
- return null; // TODO
+ return builder.build();
}
@@ -69,8 +85,18 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
ResourceGroup group = fetchGroup(id);
- GroupRest groupRest = fillGroup(group);
- Response.ResponseBuilder builder = Response.ok(groupRest);
+ GroupRest groupRest = fillGroup(group, uriInfo);
+
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+
+ Response.ResponseBuilder builder;
+
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("group", groupRest),
mediaType);
+ }
+ else {
+ builder = Response.ok(groupRest,mediaType);
+ }
return builder.build();
}
@@ -121,7 +147,7 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
@Override
@GET
@Path("{id}/resources") // TODO introduce paging through the list
- public List<ResourceWithType> getResources(@PathParam("id") int id,
@Context Request request, @Context HttpHeaders headers,
+ public Response getResources(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
ResourceGroup resourceGroup = fetchGroup(id);
@@ -131,10 +157,19 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
for (Resource res: resources) {
rwtList.add(fillRWT(res,uriInfo));
}
- Response.ResponseBuilder builder = Response.ok(rwtList);
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
+
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listResourceWithType",
rwtList), mediaType);
+ }
+ else {
+ GenericEntity<List<ResourceWithType>> list = new
GenericEntity<List<ResourceWithType>>(rwtList){};
+ builder = Response.ok(list);
+ }
+
+ return builder.build();
-// return builder.build();
- return rwtList;
}
@@ -200,10 +235,17 @@ public class GroupHandlerBean extends AbstractRestBean implements
GroupHandlerLo
- private GroupRest fillGroup(ResourceGroup group) {
+ private GroupRest fillGroup(ResourceGroup group, UriInfo uriInfo) {
GroupRest gr = new GroupRest(group.getName());
+ gr.setId(group.getId());
gr.setCategory(group.getGroupCategory());
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/group/{id}");
+ URI uri = uriBuilder.build(group.getId());
+
+ Link link = new Link("edit",uri.toASCIIString());
+ gr.getLinks().add(link);
return gr;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
index 79052f5..6c91fbe 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -59,8 +59,8 @@ public interface GroupHandlerLocal {
@GET
@Path("{id}/resources")
- public List<ResourceWithType> getResources(@PathParam("id") int
id,@Context Request request, @Context HttpHeaders headers,
- @Context UriInfo uriInfo);
+ public Response getResources(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
@GET
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 8b02ad8..f90143a 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
@@ -34,6 +34,7 @@ import javax.interceptor.Interceptors;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
@@ -41,6 +42,8 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+import com.arjuna.ats.internal.jdbc.drivers.modifiers.list;
+
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.measurement.Availability;
@@ -145,7 +148,8 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
builder = Response.ok(renderTemplate("listResourceWithType",
rwtList), mediaType);
}
else {
- builder = Response.ok(rwtList);
+ GenericEntity<List<ResourceWithType>> list = new
GenericEntity<List<ResourceWithType>>(rwtList){};
+ builder = Response.ok(list);
}
return builder.build();
@@ -257,7 +261,8 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
builder = Response.ok(renderTemplate("listMetricSchedule", ret),
mediaType);
}
else {
- builder = Response.ok(ret,mediaType);
+ GenericEntity<List<MetricSchedule>> list = new
GenericEntity<List<MetricSchedule>>(ret){};
+ builder = Response.ok(list,mediaType);
}
return builder.build();
@@ -292,7 +297,8 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
builder = Response.ok(renderTemplate("listResourceWithType",
rwtList), mediaType);
}
else {
- builder = Response.ok(rwtList);
+ GenericEntity<List<ResourceWithType>> list = new
GenericEntity<List<ResourceWithType>>(rwtList){};
+ builder = Response.ok(list);
}
return builder.build();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
index 3192ea2..c55223d 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
@@ -16,6 +16,7 @@ import org.rhq.core.domain.resource.group.GroupCategory;
public class GroupRest {
+ int id;
private String name;
private GroupCategory category;
@@ -28,6 +29,14 @@ public class GroupRest {
this.name = name;
}
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
public String getName() {
return name;
}
@@ -44,6 +53,7 @@ public class GroupRest {
this.category = category;
}
+ @XmlElementRef
public List<Link> getLinks() {
return links;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
index b7ee10a..132fc34 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
@@ -76,7 +76,6 @@ public class ResourceWithType {
this.resourceName = resourceName;
}
- @XmlAttribute
@XmlID
public String getResourceId() {
return String.valueOf(resourceId);
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
new file mode 100644
index 0000000..90bbfac
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/group.ftl
@@ -0,0 +1,41 @@
+<#ftl >
+<#--
+/*
+ * 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.
+ */
+-->
+<#-- @ftlvariable name="var"
type="org.rhq.enterprise.server.rest.domain.GroupRest" -->
+<html>
+ <table>
+ <thead>
+ <tr>
+ <td>Name</td><td>Value</td>
+ </tr>
+ </thead>
+ <tr>
+ <td>Name</td><td>${var.name}</td>
+ </tr>
+ <tr>
+ <td>Id</td><td>${var.id}</td>
+ </tr>
+ <tr>
+ <td>Category</td><td>${var.category}</td>
+ </tr>
+ </table>
+ <a
href="/rest/1/group/${var.id?c}/resources.html">Resources</a><br/>
+</html>
diff --git a/modules/enterprise/server/jar/src/main/resources/rest_templates/listGroup.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/listGroup.ftl
new file mode 100644
index 0000000..6f38801
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/listGroup.ftl
@@ -0,0 +1,32 @@
+<#ftl >
+<#--
+/*
+ * 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.
+ */
+-->
+<#-- @ftlvariable name="var"
type="java.util.List<org.rhq.enterprise.server.rest.domain.GroupRest>"
-->
+<html>
+<ul>
+ <#-- the next looks odd, but the incoming var is a list -->
+ <#list var as var>
+ <li>
+ <#include "group.ftl"/>
+ </li>
+ </#list>
+</ul>
+<html>
\ No newline at end of file
diff --git
a/modules/enterprise/server/jar/src/main/resources/rest_templates/resourceWithType.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/resourceWithType.ftl
index 9c3e435..f1ab4d2 100644
---
a/modules/enterprise/server/jar/src/main/resources/rest_templates/resourceWithType.ftl
+++
b/modules/enterprise/server/jar/src/main/resources/rest_templates/resourceWithType.ftl
@@ -2,7 +2,7 @@
<#--
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2011 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
commit 02a84e46915dfb47500233db99e534d4ef7973f8
Merge: a43fccf fdf91e1
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 14 20:34:45 2011 +0100
Merge branch 'master' into feature/rest
commit a43fccf8118da8af2017933cbcbcd6733105b6fd
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Mon Nov 14 08:19:50 2011 +0100
Unify get schedules code, allow to limit the returned values to all,metric,trait
Fix return type for schedule update and only include the link to data for metric
schedules.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index f21a90c..8be8070 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -138,7 +138,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
builder = Response.ok(htmlString,mediaType);
}
else
- builder= Response.ok(res);
+ builder= Response.ok(res,mediaType);
builder.cacheControl(cc);
return builder.build();
@@ -178,8 +178,6 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
builder.cacheControl(cc);
return builder.build();
}
-
-
}
if (schedule==null) {
@@ -210,12 +208,17 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
if (builder==null) {
// preconditions not met, we need to send the resource
- // create link to metrics
- UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("metric/data/" + scheduleId);
- URI uri = uriBuilder.build();
- Link link = new Link("metrics",uri.toString());
- metricSchedule.addLink(link);
+ UriBuilder uriBuilder;
+ URI uri;
+ Link link;
+ if (definition.getDataType()==DataType.MEASUREMENT) {
+ // create link to metrics
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("metric/data/" + scheduleId);
+ uri = uriBuilder.build();
+ link = new Link("metric",uri.toString());
+ metricSchedule.addLink(link);
+ }
// create link to the resource
uriBuilder = uriInfo.getBaseUriBuilder();
@@ -237,7 +240,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
builder = Response.ok(renderTemplate("metricSchedule",
metricSchedule), mediaType);
}
else {
- builder = Response.ok(metricSchedule);
+ builder = Response.ok(metricSchedule,mediaType);
}
}
@@ -271,7 +274,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
}
@Override
- public MetricSchedule updateSchedule(int scheduleId, MetricSchedule in) {
+ public Response updateSchedule(int scheduleId, MetricSchedule in,HttpHeaders
httpHeaders) {
if (in==null)
throw new StuffNotFoundException("Input is null"); // TODO other
type of exception
if (in.getScheduleId()==null)
@@ -294,6 +297,6 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
MetricSchedule ret = new
MetricSchedule(scheduleId,def.getName(),def.getDisplayName(),
schedule.isEnabled(),schedule.getInterval(),def.getUnits().toString(),def.getDataType().toString());
- return ret;
+ return Response.ok(ret,httpHeaders.getAcceptableMediaTypes().get(0)).build();
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 368ef36..44844ae 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -75,6 +75,6 @@ public interface MetricHandlerLocal {
@Path("/schedule/{id}")
@Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
- MetricSchedule updateSchedule(@PathParam("id") int scheduleId,
MetricSchedule in);
+ Response updateSchedule(@PathParam("id") int scheduleId, MetricSchedule
in,@Context HttpHeaders headers);
}
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 4ae74f4..8b02ad8 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
@@ -44,6 +44,7 @@ import javax.ws.rs.core.UriInfo;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.measurement.Availability;
+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.InventoryStatus;
@@ -203,7 +204,15 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
return availabilityRest;
}
- public List<MetricSchedule> getSchedules(int resourceId, UriInfo uriInfo) {
+ public Response getSchedules(int resourceId,
+ String scheduleType,
+ @Context Request request,
+ @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+
+ // allow metric as input
+ if (scheduleType.equals("metric"))
+ scheduleType=DataType.MEASUREMENT.toString().toLowerCase();
Resource res = resMgr.getResource(caller, resourceId);
@@ -212,30 +221,46 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
for (MeasurementSchedule schedule : schedules) {
putToCache(schedule.getId(),MeasurementSchedule.class,schedule);
MeasurementDefinition definition = schedule.getDefinition();
- MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
- schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
- definition.getDataType().toString());
- UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/metric/data/{id}");
- URI uri = uriBuilder.build(schedule.getId());
- Link metricLink = new Link("metric",uri.toString());
- ms.addLink(metricLink);
- // create link to the resource
- uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("resource/" + schedule.getResource().getId());
- uri = uriBuilder.build();
- Link link = new Link("resource",uri.toString());
- ms.addLink(link);
-
- ret.add(ms);
+
+ // user can opt to e.g. only get "measurement" or "trait"
metrics
+
+ if ("all".equals(scheduleType) ||
+
scheduleType.toLowerCase().equals(definition.getDataType().toString().toLowerCase()) ) {
+ MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
+ schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
+ definition.getDataType().toString());
+ UriBuilder uriBuilder;
+ URI uri;
+ if (definition.getDataType()== DataType.MEASUREMENT) {
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/metric/data/{id}");
+ uri = uriBuilder.build(schedule.getId());
+ Link metricLink = new Link("metric",uri.toString());
+ ms.addLink(metricLink);
+ }
+ // create link to the resource
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("resource/" + schedule.getResource().getId());
+ uri = uriBuilder.build();
+ Link link = new Link("resource",uri.toString());
+ ms.addLink(link);
+
+ ret.add(ms);
+ }
}
- return ret;
- }
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
- public String getSchedulesHtml(int resourceId, UriInfo uriInfo) {
- List<MetricSchedule> list = getSchedules(resourceId, uriInfo);
- return renderTemplate("listMetricSchedule", list);
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listMetricSchedule", ret),
mediaType);
+ }
+ else {
+ builder = Response.ok(ret,mediaType);
+ }
+
+ return builder.build();
}
@Override
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index 9d9a293..412b347 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -21,10 +21,12 @@ package org.rhq.enterprise.server.rest;
import java.util.List;
import javax.ejb.Local;
+import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -36,7 +38,6 @@ import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;
import org.jboss.resteasy.links.LinkResources;
-import org.rhq.core.domain.resource.Resource;
import org.rhq.enterprise.server.rest.domain.AvailabilityRest;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
@@ -80,18 +81,13 @@ public interface ResourceHandlerLocal {
@GET
@Path("/{id}/schedules")
- @Produces({"application/json","application/xml"})
@LinkResource(rel="schedules",value = MetricSchedule.class)
- List<MetricSchedule> getSchedules(@PathParam("id") int resourceId,
+ Response getSchedules(@PathParam("id") int resourceId,
+ @QueryParam("type")
@DefaultValue("all") String scheduleType,
+ @Context Request request,
+ @Context HttpHeaders headers,
@Context UriInfo uriInfo);
- @GET
- @Path("/{id}/schedules")
- @Produces("text/html")
- @LinkResource(rel="schedules",value = MetricSchedule.class)
- // TODO move to ScheduleManager, link from resource and pass the resource as
query param
- String getSchedulesHtml(@PathParam("id") int resourceId,
- @Context UriInfo uriInfo);
@GET
@Path("/{id}/children")
commit 805c8af249272c4e93f06bb3710d74949ba5e6b3
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sun Nov 13 22:53:54 2011 +0100
Update RESTeasy to 2.2.3
Start with Group related things
Use the jackson provider for nicer json
Cleanup getting the status
Fix compile errors
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
b/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
index 5d39d99..d607ea2 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
@@ -7,7 +7,7 @@
<context-param>
<param-name>resteasy.jndi.resources</param-name>
-
<param-value>rhq/ResourceHandlerBean/local,rhq/MetricHandlerBean/local,rhq/UserHandlerBean/local,rhq/StatusHandlerBean/local,rhq/AlertHandlerBean/local,rhq/OperationsHandlerBean/local</param-value>
+
<param-value>rhq/ResourceHandlerBean/local,rhq/MetricHandlerBean/local,rhq/UserHandlerBean/local,rhq/StatusHandlerBean/local,rhq/AlertHandlerBean/local,rhq/OperationsHandlerBean/local,rhq/GroupHandlerBean/local</param-value>
<description>List of jndi names of EJBs local interfaces that define REST
stuff</description>
</context-param>
<context-param>
@@ -22,7 +22,7 @@
</context-param>
<context-param>
<param-name>resteasy.media.type.mappings</param-name>
- <param-value>html : text/html, json : application/json, xml :
application/xml</param-value>
+ <param-value>html : text/html, json : application/json, xml :
application/xml, yaml: application/yaml</param-value>
</context-param>
<!--
diff --git a/modules/enterprise/server/container/pom.xml
b/modules/enterprise/server/container/pom.xml
index e6fa762..4890488 100644
--- a/modules/enterprise/server/container/pom.xml
+++ b/modules/enterprise/server/container/pom.xml
@@ -117,18 +117,6 @@
<version>1.7.3</version>
</dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jaxrs-all</artifactId>
- <scope>provided</scope>
- <version>${resteasy.version}</version>
- <type>pom</type>
- </dependency>
- <dependency>
- <groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jettison-provider</artifactId>
- </dependency>
-
</dependencies>
<build>
diff --git a/modules/enterprise/server/ear/pom.xml
b/modules/enterprise/server/ear/pom.xml
index a913b44..b792ab8 100644
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@ -150,7 +150,7 @@
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jettison-provider</artifactId>
+ <artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
@@ -164,6 +164,32 @@
<version>${freemarker.version}</version>
<type>jar</type>
</dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-yaml-provider</artifactId>
+ <version>${resteasy.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>1.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxb-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-impl</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.xml.stream</groupId>
+ <artifactId>sjsxp</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
</dependencies>
diff --git a/modules/enterprise/server/jar/pom.xml
b/modules/enterprise/server/jar/pom.xml
index 7206726..f14c91d 100644
--- a/modules/enterprise/server/jar/pom.xml
+++ b/modules/enterprise/server/jar/pom.xml
@@ -477,7 +477,7 @@
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
- <artifactId>resteasy-jettison-provider</artifactId>
+ <artifactId>resteasy-jackson-provider</artifactId>
<version>${resteasy.version}</version>
<scope>provided</scope> <!-- by container -->
</dependency>
@@ -488,6 +488,34 @@
<scope>provided</scope> <!-- by container -->
</dependency>
<dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-yaml-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxb-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>provided</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>com.sun.xml.bind</groupId>
+ <artifactId>jaxb-impl</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.sun.xml.stream</groupId>
+ <artifactId>sjsxp</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.yaml</groupId>
+ <artifactId>snakeyaml</artifactId>
+ <version>1.8</version>
+ </dependency>
+ <dependency>
<groupId>org.jboss.el</groupId>
<artifactId>jboss-el</artifactId>
<version>2.0.1.GA</version>
@@ -806,7 +834,7 @@ Build-OS-Version=${os.version}
<property name="javadoc.outputDirectory"
value="${javadoc.outputDirectory}" />
<property name="project.dir"
value="./src/main/java/org/rhq/enterprise/server" />
<property name="maven.compile.classpath"
refid="maven.compile.classpath" />
-
+
<mkdir
dir="${javadoc.outputDirectory}/remote-api" />
<javadoc
destdir="${javadoc.outputDirectory}/remote-api" author="false"
version="true" windowtitle="RHQ ${project.version} Remote API"
noindex="false">
<classpath>
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index 124e12f..1be4db0 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -24,6 +24,7 @@ package org.rhq.enterprise.server.rest;
import java.io.IOException;
import java.io.StringWriter;
+import java.net.URI;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -31,6 +32,8 @@ import java.util.Set;
import javax.annotation.Resource;
import javax.ejb.Stateless;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
@@ -48,6 +51,9 @@ import org.jboss.cache.Node;
import org.jboss.cache.TreeCacheMBean;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.enterprise.server.rest.domain.Link;
+import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
* Abstract base class for EJB classes that implement REST methods.
@@ -227,4 +233,24 @@ public class AbstractRestBean {
}
return true;
}
+
+ public ResourceWithType fillRWT(org.rhq.core.domain.resource.Resource res, UriInfo
uriInfo) {
+ ResourceType resourceType = res.getResourceType();
+ ResourceWithType rwt = new ResourceWithType(res.getName(),res.getId());
+ rwt.setTypeName(resourceType.getName());
+ rwt.setTypeId(resourceType.getId());
+ rwt.setPluginName(resourceType.getPlugin());
+ org.rhq.core.domain.resource.Resource parent = res.getParentResource();
+ if (parent!=null) {
+ rwt.setParentId(parent.getId());
+ }
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/definitions");
+ uriBuilder.queryParam("resourceId",res.getId());
+ URI uri = uriBuilder.build();
+ Link link = new Link("operationDefinitions",uri.toString());
+ rwt.addLink(link);
+
+ return rwt;
+ }
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
new file mode 100644
index 0000000..6f2c6b0
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerBean.java
@@ -0,0 +1,210 @@
+package org.rhq.enterprise.server.rest;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.interceptor.Interceptors;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.authz.Role;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.resource.group.ResourceGroupDeleteException;
+import org.rhq.enterprise.server.resource.group.ResourceGroupManagerLocal;
+import org.rhq.enterprise.server.rest.domain.GroupRest;
+import org.rhq.enterprise.server.rest.domain.ResourceWithType;
+
+/**
+ * Deal with group related things.
+ * @author Heiko W. Rupp
+ */
+@Stateless
+(a)Interceptors(SetCallerInterceptor.class)
+public class GroupHandlerBean extends AbstractRestBean implements GroupHandlerLocal {
+
+ private final Log log = LogFactory.getLog(GroupHandlerBean.class);
+
+ @EJB
+ ResourceGroupManagerLocal resourceGroupManager;
+
+ @EJB
+ ResourceManagerLocal resourceManager;
+
+ @Override
+ @GET
+ @Path("/")
+ public Response getGroups(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
+
+ Set<Role> roles = caller.getRoles();
+
+// resourceGroupManager.findAvailableResourceGroupsForRole(caller,roleId,new
int[]{}, PageControl.SIZE_UNLIMITED);
+ return null; // TODO
+
+ }
+
+ @Override
+ @GET
+ @Path("{id}")
+ public Response getGroup(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+
+ ResourceGroup group = fetchGroup(id);
+
+ GroupRest groupRest = fillGroup(group);
+ Response.ResponseBuilder builder = Response.ok(groupRest);
+
+ return builder.build();
+ }
+
+ @Override
+ @POST
+ @Path("/")
+ public Response createGroup(ResourceGroup group, @Context Request request, @Context
HttpHeaders headers, @Context UriInfo uriInfo) {
+
+ Response.ResponseBuilder builder;
+ try {
+ ResourceGroup newGroup =
resourceGroupManager.createResourceGroup(caller,group);
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/group/{id}");
+ URI uri = uriBuilder.build(newGroup.getId());
+
+ builder=Response.created(uri);
+ } catch (Exception e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ builder=Response.serverError();
+ }
+ return builder.build();
+ }
+
+ @Override
+ @PUT
+ @Path("{id}")
+ public Response updateGroup(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+ return null; // TODO: Customise this generated block
+ }
+
+ @Override
+ @DELETE
+ @Path("{id}")
+ public Response deleteGroup(@PathParam("id") int id, @Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+
+ try {
+ resourceGroupManager.deleteResourceGroup(caller,id);
+ return Response.ok().build();
+ } catch (ResourceGroupDeleteException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ return Response.serverError().build(); // TODO what exactly ?
+ }
+ }
+
+ @Override
+ @GET
+ @Path("{id}/resources") // TODO introduce paging through the list
+ public List<ResourceWithType> getResources(@PathParam("id") int id,
@Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
+
+ ResourceGroup resourceGroup = fetchGroup(id);
+
+ Set<Resource> resources = resourceGroup.getExplicitResources();
+ List<ResourceWithType> rwtList = new
ArrayList<ResourceWithType>(resources.size());
+ for (Resource res: resources) {
+ rwtList.add(fillRWT(res,uriInfo));
+ }
+ Response.ResponseBuilder builder = Response.ok(rwtList);
+
+// return builder.build();
+ return rwtList;
+ }
+
+
+ @Override
+ @GET
+ @Path("{id}/resource/{resourceId}")
+ public Response getResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
+
+
+
+ return null; // TODO: Customise this generated block
+ }
+
+ @Override
+ @PUT
+ @Path("{id}/resource/{resourceId}")
+ public Response addResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo) {
+
+ ResourceGroup resourceGroup = fetchGroup(id);
+ Resource res = resourceManager.getResource(caller,resourceId);
+ if (res==null)
+ throw new StuffNotFoundException("Resource with id " +
resourceId);
+
+ // TODO do we want/ need to check that the group category stays the same?
+
+ resourceGroup.addExplicitResource(res);
+
+ return Response.ok().build(); // TODO right code?
+
+ }
+
+ @Override
+ @DELETE
+ @Path("{id}/resource/{resourceId}")
+ public Response removeResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders
headers, @Context UriInfo uriInfo) {
+
+ ResourceGroup resourceGroup = fetchGroup(id);
+ Resource res = resourceManager.getResource(caller,resourceId);
+ if (res==null)
+ throw new StuffNotFoundException("Resource with id " +
resourceId);
+
+ resourceGroup.removeExplicitResource(res);
+
+ return Response.ok().build(); // TODO right code?
+
+ }
+
+ /**
+ * Fetch the group with the passed id
+ * @param groupId id of the resource group
+ * @return the group object if found
+ * @throws StuffNotFoundException if the group is not found (or not accessible by the
caller)
+ */
+ private ResourceGroup fetchGroup(int groupId) {
+ ResourceGroup resourceGroup = resourceGroupManager.getResourceGroup(caller,
groupId);
+ if (resourceGroup==null)
+ throw new StuffNotFoundException("Group with id " + groupId);
+ return resourceGroup;
+ }
+
+
+
+ private GroupRest fillGroup(ResourceGroup group) {
+
+ GroupRest gr = new GroupRest(group.getName());
+ gr.setCategory(group.getGroupCategory());
+
+ return gr;
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
new file mode 100644
index 0000000..79052f5
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/GroupHandlerLocal.java
@@ -0,0 +1,83 @@
+package org.rhq.enterprise.server.rest;
+
+import java.util.List;
+
+import javax.ejb.Local;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.enterprise.server.rest.domain.ResourceWithType;
+
+/**
+ * Handler for Group related things
+ * @author Heiko W. Rupp
+ */
+@Local
+@Path("/group/")
+(a)Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML,
"application/yaml"})
+public interface GroupHandlerLocal {
+
+ @GET
+ @Path("/")
+ public Response getGroups(@Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+ @GET
+ @Path("{id}")
+ public Response getGroup(@PathParam("id") int id,@Context Request request,
@Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+
+ @POST
+ @Path("/")
+ public Response createGroup(ResourceGroup group, @Context Request request, @Context
HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+ @PUT
+ @Path("{id}")
+ public Response updateGroup(@PathParam("id") int id,@Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+ @DELETE
+ @Path("{id}")
+ public Response deleteGroup(@PathParam("id") int id,@Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+
+ @GET
+ @Path("{id}/resources")
+ public List<ResourceWithType> getResources(@PathParam("id") int
id,@Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+
+ @GET
+ @Path("{id}/resource/{resourceId}")
+ public Response getResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+ @PUT
+ @Path("{id}/resource/{resourceId}")
+ public Response addResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
+
+ @DELETE
+ @Path("{id}/resource/{resourceId}")
+ public Response removeResource(@PathParam("id") int id,
@PathParam("resourceId") int resourceId,
+ @Context Request request, @Context HttpHeaders
headers,
+ @Context UriInfo uriInfo);
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
index 46a7f86..5ab7366 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
@@ -19,8 +19,6 @@ import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
-import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
-
import org.rhq.enterprise.server.rest.domain.OperationDefinitionRest;
import org.rhq.enterprise.server.rest.domain.OperationRest;
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 e656c24..4ae74f4 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
@@ -49,7 +49,6 @@ import org.rhq.core.domain.measurement.MeasurementSchedule;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceCategory;
-import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.alert.AlertManagerLocal;
import org.rhq.enterprise.server.rest.domain.AvailabilityRest;
import org.rhq.enterprise.server.rest.domain.Link;
@@ -204,26 +203,6 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
return availabilityRest;
}
- private ResourceWithType fillRWT(Resource res, UriInfo uriInfo) {
- ResourceType resourceType = res.getResourceType();
- ResourceWithType rwt = new ResourceWithType(res.getName(),res.getId());
- rwt.setTypeName(resourceType.getName());
- rwt.setTypeId(resourceType.getId());
- rwt.setPluginName(resourceType.getPlugin());
- Resource parent = res.getParentResource();
- if (parent!=null) {
- rwt.setParentId(parent.getId());
- }
- UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
- uriBuilder.path("/operation/definitions");
- uriBuilder.queryParam("resourceId",res.getId());
- URI uri = uriBuilder.build();
- Link link = new Link("operationDefinitions",uri.toString());
- rwt.addLink(link);
-
- return rwt;
- }
-
public List<MetricSchedule> getSchedules(int resourceId, UriInfo uriInfo) {
Resource res = resMgr.getResource(caller, resourceId);
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index d1cb0f2..9d9a293 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -36,6 +36,7 @@ import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;
import org.jboss.resteasy.links.LinkResources;
+import org.rhq.core.domain.resource.Resource;
import org.rhq.enterprise.server.rest.domain.AvailabilityRest;
import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
@@ -46,7 +47,7 @@ import org.rhq.enterprise.server.rest.domain.ResourceWithType;
* Interface class that describes the REST interface
* @author Heiko W. Rupp
*/
-@Produces({"application/json","application/xml","text/html"})
+(a)Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
@Path("/resource")
@Local
public interface ResourceHandlerLocal {
@@ -57,13 +58,12 @@ public interface ResourceHandlerLocal {
})
@GET
@Path("/{id}")
-
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+
Response getResource(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo);
@GET
@Path("/platforms")
-
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
Response getPlatforms(@Context Request request, @Context HttpHeaders headers,
@Context UriInfo uriInfo);
@@ -95,7 +95,6 @@ public interface ResourceHandlerLocal {
@GET
@Path("/{id}/children")
-
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
@LinkResource(rel="children", value = ResourceWithType.class)
Response getChildren(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
@Context UriInfo uriInfo);
@@ -104,7 +103,6 @@ public interface ResourceHandlerLocal {
@AddLinks
@GET
@Path(("/{id}/alerts"))
-// @LinkResource(rel="alerts", value = AlertRest.class)
List<Link> getAlertsForResource(@PathParam("id") int resourceId); //
TODO paging + status
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
index cd68a72..0f2a0ed 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerBean.java
@@ -21,13 +21,12 @@ package org.rhq.enterprise.server.rest;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
-import javax.ws.rs.GET;
-import javax.ws.rs.Path;
-import javax.ws.rs.Produces;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.alert.AlertDefinition;
-import org.rhq.core.domain.content.composite.PackageListItemComposite;
import org.rhq.core.domain.criteria.AlertCriteria;
import org.rhq.core.domain.criteria.AlertDefinitionCriteria;
import org.rhq.core.domain.criteria.Criteria;
@@ -60,7 +59,7 @@ public class StatusHandlerBean extends AbstractRestBean implements
StatusHandler
AlertDefinitionManagerLocal alertDefinitionManager;
@Override
- public Status getStatus() {
+ public Response getStatus(HttpHeaders httpHeaders) {
Status status = new Status();
@@ -94,12 +93,15 @@ public class StatusHandlerBean extends AbstractRestBean implements
StatusHandler
status.setMetricsMin(scheduleManager.getScheduledMeasurementsPerMinute());
- return status;
- }
+ MediaType mediaType = httpHeaders.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ String htmlString = renderTemplate("status",status);
+ builder = Response.ok(htmlString,mediaType);
+ } else {
+ builder = Response.ok(status, httpHeaders.getAcceptableMediaTypes().get(0));
+ }
- @Override
- public String getStatusHtml() {
- Status status = getStatus();
- return renderTemplate("status",status);
+ return builder.build();
}
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
index 5e1c3f4..838fbad 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/StatusHandlerLocal.java
@@ -22,8 +22,9 @@ import javax.ejb.Local;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
-
-import org.rhq.enterprise.server.rest.domain.Status;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
/**
* Return some status information about the system
@@ -36,11 +37,5 @@ public interface StatusHandlerLocal {
@GET
@Path("/")
- @Produces({"application/json","application/xml"})
- Status getStatus();
-
- @GET
- @Path("/")
- @Produces("text/html")
- String getStatusHtml();
+ Response getStatus(@Context HttpHeaders httpHeaders);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerBean.java
index d8b0c39..b644cd6 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerBean.java
@@ -32,10 +32,13 @@ import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
+import javax.ws.rs.core.UriInfo;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.resource.Resource;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceManagerLocal;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
/**
@@ -66,15 +69,20 @@ public class UserHandlerBean extends AbstractRestBean implements
UserHandlerLoca
@EJB
ResourceHandlerLocal resourceHandler;
+ @EJB
+ ResourceManagerLocal resourceManager;
+
@Override
@GET
@Path("favorites/r")
- public List<ResourceWithType> getFavorites() {
+ public List<ResourceWithType> getFavorites(UriInfo uriInfo) {
Set<Integer> favIds = getResourceIdsForFavorites();
List<ResourceWithType> ret = new ArrayList<ResourceWithType>();
for (Integer id : favIds) {
- ResourceWithType rwt = resourceHandler.getResource(id);
+ Resource res = resourceManager.getResource(caller,id);
+
+ ResourceWithType rwt = fillRWT(res,uriInfo);
ret.add(rwt);
}
return ret;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerLocal.java
index f80eb9e..9073f32 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/UserHandlerLocal.java
@@ -28,6 +28,8 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
@@ -42,7 +44,7 @@ public interface UserHandlerLocal {
@GET
@Path("favorites/resource")
- public List<ResourceWithType> getFavorites();
+ public List<ResourceWithType> getFavorites(@Context UriInfo uriInfo);
@PUT
@Path("favorites/resource/{id}")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertDefinitionRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertDefinitionRest.java
index 073cab8..f8d1f88 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertDefinitionRest.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertDefinitionRest.java
@@ -24,8 +24,6 @@ import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
-import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
-import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
import org.jboss.resteasy.links.RESTServiceDiscovery;
import org.jboss.resteasy.spi.touri.URITemplate;
@@ -35,7 +33,6 @@ import org.jboss.resteasy.spi.touri.URITemplate;
*/
@URITemplate("/alert/definition/{id}")
@XmlRootElement
-@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace =
"http://www.w3.org/2005/Atom"))
public class AlertDefinitionRest {
int id;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertRest.java
index c540434..90fe925 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertRest.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AlertRest.java
@@ -26,8 +26,6 @@ import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
-import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
-import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;
import org.jboss.resteasy.links.RESTServiceDiscovery;
@@ -39,9 +37,7 @@ import org.jboss.resteasy.spi.touri.URITemplate;
*/
@URITemplate("/alert/{id}")
@XmlRootElement
-//@XmlType(propOrder =
{"id","name","alertTime","description","alertDefinitionId","definitionEnabled","resourceId","resourceName","ackBy","ackTime","rest"})
@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
-@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace =
"http://www.w3.org/2005/Atom"))
public class AlertRest {
private int id;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AvailabilityRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AvailabilityRest.java
index a0763dd..9f64f4b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AvailabilityRest.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/AvailabilityRest.java
@@ -22,8 +22,6 @@ import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
-import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
-import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
import org.jboss.resteasy.links.RESTServiceDiscovery;
import org.rhq.core.domain.measurement.AvailabilityType;
@@ -33,7 +31,6 @@ import org.rhq.core.domain.measurement.AvailabilityType;
* @author Heiko W. Rupp
*/
@XmlRootElement(name = "availability")
-@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace =
"http://www.w3.org/2005/Atom"))
public class AvailabilityRest {
long since;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
new file mode 100644
index 0000000..3192ea2
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/GroupRest.java
@@ -0,0 +1,54 @@
+package org.rhq.enterprise.server.rest.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.rhq.core.domain.resource.group.GroupCategory;
+
+/**
+ * Representation of a resource group
+ * @author Heiko W. Rupp
+ */
+@XmlRootElement
+public class GroupRest {
+
+
+ private String name;
+ private GroupCategory category;
+
+ List<Link> links = new ArrayList<Link>();
+
+ public GroupRest() {
+ }
+
+ public GroupRest(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public GroupCategory getCategory() {
+ return category;
+ }
+
+ public void setCategory(GroupCategory category) {
+ this.category = category;
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ this.links = links;
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
index 4333530..30a006b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
@@ -27,8 +27,6 @@ import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
-import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
-import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
import org.jboss.resteasy.spi.touri.URITemplate;
/**
@@ -36,8 +34,6 @@ import org.jboss.resteasy.spi.touri.URITemplate;
* @author Heiko W. Rupp
*/
@XmlRootElement
-//@XmlType(propOrder =
{"scheduleId","scheduleName","displayName","enabled","collectionInterval","unit","type"})
-@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace =
"http://www.w3.org/2005/Atom"))
@URITemplate("/metric/schedule/{id}")
public class MetricSchedule {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
index 1e5971c..b7ee10a 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
@@ -33,19 +33,17 @@ import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
-import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
-import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
import org.jboss.resteasy.spi.touri.URITemplate;
/**
* A (partial) resource with some type information
* @author Heiko W. Rupp
*/
-@Mapped(namespaceMap = @XmlNsMap(jsonName = "atom", namespace =
"http://www.w3.org/2005/Atom"))
@XmlRootElement
+/*
@XmlAccessorType(XmlAccessType.NONE)
-//@XmlType(propOrder =
{"resourceId","resourceName","parentId","parentName","typeId","typeName","pluginId","pluginName","rest"})
@URITemplate("{id}")
+*/
public class ResourceWithType {
String resourceName;
diff --git a/pom.xml b/pom.xml
index 9eae894..c01cf60 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,7 +107,7 @@
<augeas.zip.location>${settings.localRepository}/net/augeas/augeas-native/${augeas.version}</augeas.zip.location>
<augeas.zip.mask>*-${augeas.classifier}.zip</augeas.zip.mask>
<ant.contrib.version>1.0b3</ant.contrib.version>
- <resteasy.version>2.2.2.GA</resteasy.version>
+ <resteasy.version>2.2.3.GA</resteasy.version>
<freemarker.version>2.3.18</freemarker.version>
<rhq.db.admin.username>rhqadmin</rhq.db.admin.username>
commit e23db61ad37d962ce5ba746a2864854d1ce4fc78
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Sat Nov 5 21:40:57 2011 +0100
More REST work, consisting of:
Introduce a cache (at the moment for schedules) to reduce database load.
Map the cache int JNDI and have it injected by the container.
Check the caller when accessing cache entries, so that only entries are returned that
the caller has access to.
Clean up some methods, add support for links to operation definitions for resources.
First cut at triggering operations.
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
b/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
index 9652232..5d39d99 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/WEB-INF/web.xml
@@ -7,7 +7,7 @@
<context-param>
<param-name>resteasy.jndi.resources</param-name>
-
<param-value>rhq/ResourceHandlerBean/local,rhq/MetricHandlerBean/local,rhq/UserHandlerBean/local,rhq/StatusHandlerBean/local,rhq/AlertHandlerBean/local</param-value>
+
<param-value>rhq/ResourceHandlerBean/local,rhq/MetricHandlerBean/local,rhq/UserHandlerBean/local,rhq/StatusHandlerBean/local,rhq/AlertHandlerBean/local,rhq/OperationsHandlerBean/local</param-value>
<description>List of jndi names of EJBs local interfaces that define REST
stuff</description>
</context-param>
<context-param>
@@ -64,6 +64,7 @@
<web-resource-collection>
<web-resource-name>REST</web-resource-name>
<url-pattern>/*</url-pattern>
+<!--
<http-method>DELETE</http-method>
<http-method>PUT</http-method>
<http-method>HEAD</http-method>
@@ -71,6 +72,7 @@
<http-method>TRACE</http-method>
<http-method>GET</http-method>
<http-method>POST</http-method>
+-->
</web-resource-collection>
<auth-constraint>
<role-name>all</role-name>
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
new file mode 100644
index 0000000..f458a8e
--- /dev/null
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<server>
+
+ <mbean code="org.jboss.cache.TreeCache"
+ name="rhq.cache:service=cache">
+ <depends>jboss:service=TransactionManager</depends>
+
+ <!-- Configure the TransactionManager -->
+ <attribute
name="TransactionManagerLookupClass">org.jboss.cache.JBossTransactionManagerLookup</attribute>
+
+ <!-- Isolation level : SERIALIZABLE
+ REPEATABLE_READ (default)
+ READ_COMMITTED
+ READ_UNCOMMITTED
+ NONE
+ -->
+ <attribute name="IsolationLevel">REPEATABLE_READ</attribute>
+
+ <!--
+ Node locking scheme:
+ OPTIMISTIC
+ PESSIMISTIC (default)
+ -->
+ <attribute name="NodeLockingScheme">OPTIMISTIC</attribute>
+
+ <!-- Valid modes are LOCAL, REPL_ASYNC and REPL_SYNC -->
+ <attribute name="CacheMode">REPL_ASYNC</attribute>
+
+ <!-- Just used for async repl: use a replication queue -->
+ <attribute name="UseReplQueue">false</attribute>
+
+ <!-- Replication interval for replication queue (in ms) -->
+ <attribute name="ReplQueueInterval">100</attribute>
+
+ <!-- Max number of elements which trigger replication -->
+ <attribute name="ReplQueueMaxElements">10</attribute>
+
+
+ <!-- Name of cluster. Needs to be the same for all clusters, in order
+ to find each other
+ -->
+ <attribute
name="ClusterName">${jboss.partition.name:DefaultPartition}-RHQ-Cache</attribute>
+
+ <!-- JGroups protocol stack properties. Can also be a URL,
+ e.g. file:/home/bela/default.xml
+ <attribute name="ClusterProperties"></attribute>
+ -->
+ <attribute name="ClusterConfig">
+
+ <config>
+ <!-- UDP: if you have a multihomed machine,
+ set the bind_addr attribute to the appropriate NIC IP address, e.g
bind_addr="192.168.0.2"
+ -->
+ <!-- UDP: On Windows machines, because of the media sense feature
+ being broken with multicast (even after disabling media sense)
+ set the loopback attribute to true
+
+ TODO need to parametrize and add to installer
+
+ -->
+ <UDP
+ mcast_addr="${jgroups.udp.mcast_addr:230.1.2.3}"
+ mcast_port="${jboss.alertcachepartition.mcast_port:48899}"
+ ip_ttl="${jgroups.udp.ip_ttl:2}"
+ bind_addr="${jgroups.bind_addr}"
+ ip_mcast="true"
+ tos="8"
+ mcast_send_buf_size="150000"
+ mcast_recv_buf_size="80000"
+ ucast_send_buf_size="150000"
+ ucast_recv_buf_size="80000"
+ loopback="${rhq.server.startup.partition.udpLoopback:false}"
/>
+ <PING timeout="2000"
+ num_initial_members="3"
+ up_thread="false"
+ down_thread="false" />
+ <MERGE2 min_interval="10000"
+ max_interval="20000" />
+ <FD_SOCK />
+ <VERIFY_SUSPECT timeout="1500"
+ up_thread="false"
+ down_thread="false" />
+ <pbcast.NAKACK gc_lag="50"
+ retransmit_timeout="600,1200,2400,4800"
+ max_xmit_size="8192"
+ up_thread="false"
+ down_thread="false" />
+ <UNICAST timeout="600,1200,2400"
+ down_thread="false" />
+ <pbcast.STABLE desired_avg_gossip="20000"
+ up_thread="false"
+ down_thread="false" />
+ <FRAG frag_size="8192" down_thread="false"
+ up_thread="false" />
+ <pbcast.GMS join_timeout="5000"
join_retry_timeout="2000"
+ shun="true" print_local_addr="true" />
+ <pbcast.STATE_TRANSFER up_thread="true"
down_thread="true" />
+ </config>
+ </attribute>
+
+ <!-- Whether or not to fetch state on joining a cluster -->
+ <attribute name="FetchStateOnStartup">true</attribute>
+
+ <!-- The max amount of time (in milliseconds) we wait until the
+ initial state (ie. the contents of the cache) are retrieved from
+ existing members in a clustered environment
+
+ -->
+ <attribute
name="InitialStateRetrievalTimeout">5000</attribute>
+
+ <!-- Number of milliseconds to wait until all responses for a
+ synchronous call have been received.
+ -->
+ <attribute name="SyncReplTimeout">15000</attribute>
+
+ <!-- Max number of milliseconds to wait for a lock acquisition -->
+ <attribute name="LockAcquisitionTimeout">10000</attribute>
+
+ <attribute
name="EvictionPolicyClass">org.jboss.cache.eviction.LRUPolicy</attribute>
+ <!-- Specific eviction policy configurations. This is LRU -->
+ <attribute name="EvictionPolicyConfig">
+ <config>
+ <attribute
name="wakeUpIntervalSeconds">5</attribute>
+ <!-- Cache wide default -->
+ <region name="/_default_">
+ <attribute name="maxNodes">5000</attribute>
+ <attribute
name="timeToLiveSeconds">1000</attribute>
+ </region>
+ <region name="/schedules">
+ <attribute name="maxNodes">5000</attribute>
+ <attribute
name="timeToLiveSeconds">1000</attribute>
+ </region>
+ <region name="/org/jboss/test/data">
+ <attribute name="maxNodes">5</attribute>
+ <attribute
name="timeToLiveSeconds">4</attribute>
+ </region>
+ <region name="/test/">
+ <attribute name="maxNodes">10000</attribute>
+ <attribute
name="timeToLiveSeconds">5</attribute>
+ </region>
+ <region name="/maxAgeTest/">
+ <attribute name="maxNodes">10000</attribute>
+ <attribute
name="timeToLiveSeconds">8</attribute>
+ <attribute name="maxAgeSeconds">10</attribute>
+ </region>
+ </config>
+ </attribute>
+ </mbean>
+
+
+ <!-- Map the cache into JNDI -->
+ <mbean
+ code="org.jboss.invocation.jrmp.server.JRMPProxyFactory"
+ name="mydomain:service=proxyFactory,type=jrmp,target=factory">
+ <attribute
name="InvokerName">jboss:service=invoker,type=jrmp</attribute>
+ <attribute
name="TargetName">rhq.cache:service=cache</attribute>
+ <attribute name="JndiName">RhqCache</attribute>
+ <attribute name="InvokeTargetMethod">true</attribute>
+ <attribute
name="ExportedInterface">org.jboss.cache.TreeCacheMBean</attribute>
+ <attribute name="ClientInterceptors">
+ <iterceptors>
+
<interceptor>org.jboss.proxy.ClientMethodInterceptor</interceptor>
+
<interceptor>org.jboss.proxy.SecurityInterceptor</interceptor>
+
<interceptor>org.jboss.invocation.InvokerInterceptor</interceptor>
+ </iterceptors>
+ </attribute>
+ <depends>jboss:service=invoker,type=jrmp</depends>
+ <depends>rhq.cache:service=cache</depends>
+ </mbean>
+</server>
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index d1d3c89..124e12f 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -25,7 +25,12 @@ package org.rhq.enterprise.server.rest;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.ejb.Stateless;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
@@ -37,12 +42,20 @@ import freemarker.template.TemplateException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.CacheException;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.Node;
+import org.jboss.cache.TreeCacheMBean;
+
import org.rhq.core.domain.auth.Subject;
/**
* Abstract base class for EJB classes that implement REST methods.
+ * For the cache and its evicion policies see rhq-cache-service.xml
* @author Heiko W. Rupp
*/
+@Stateless
+@Resource(name="cache",type= TreeCacheMBean.class,mappedName =
"RhqCache")
public class AbstractRestBean {
Log log = LogFactory.getLog(getClass().getName());
@@ -50,6 +63,10 @@ public class AbstractRestBean {
/** Subject of the caller that gets injected via {@link SetCallerInterceptor} */
Subject caller;
+ /** The cache to use */
+ @Resource(name="cache")
+ TreeCacheMBean treeCache;
+
/**
* Renders the passed object with the help of a freemarker template into a string.
Freemarket templates
* are searched in the class path in a directory called "/rest_templates".
In the usual Maven tree structure,
@@ -92,4 +109,122 @@ public class AbstractRestBean {
}
return null;
}
+
+ /**
+ * Retrieve an object from the cache. This is identified by its class and id
+ * @param id Id of the object to load.
+ * @param clazz Wanted return type
+ * @return Object if found and the caller has access to it.
+ * @see #getFqn(int, Class)
+ */
+ protected <T>T getFromCache(int id,Class<T> clazz) {
+ Fqn fqn = getFqn(id, clazz);
+ return getFromCache(fqn,clazz);
+ }
+
+
+ /**
+ * Retrieve an object from the cache if present or null otherwise.
+ * We need to be careful here as we must not return objects the current
+ * caller has no access to. We do this by checking the "readers" attribute
+ * of the selected node to see if the caller has put the object there
+ * @param fqn FullyQualified name (=path in cache) ot the object to retrieve
+ * @param clazz Return type
+ * @return The desired object if found and valid for the current caller. Null
otherwise.
+ * @see #putToCache(org.jboss.cache.Fqn, Object)
+ */
+ @SuppressWarnings("unchecked")
+ protected <T>T getFromCache(Fqn fqn,Class<T> clazz) {
+ Object o=null;
+ if (treeCache.exists(fqn)) {
+ log.info("Hit for " + fqn.toString());
+ try{
+ Node n = treeCache.get(fqn);
+ Set<Integer> readers= (Set<Integer>)
n.get("readers");
+ if (readers.contains(caller.getId())) {
+ o = n.get("item");
+ }
+ else {
+ log.info("No object for caller " +caller.toString() +
" found");
+ }
+ } catch (CacheException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ log.info("Miss for " + fqn.toString());
+ }
+ }
+ return (T)o;
+ }
+
+ /**
+ * Put an object into the cache. We need to record the caller so that we can later
+ * check if the caller can access that object or not.
+ * @param fqn Fully qualified name (=path to object)
+ * @param o Object to put
+ * @return true if put was successful
+ * @see #getFromCache(org.jboss.cache.Fqn, Class)
+ */
+ @SuppressWarnings("unchecked")
+ protected <T>boolean putToCache(Fqn fqn,T o) {
+ boolean success = false;
+ try {
+ Set<Integer> readers;
+ if (treeCache.exists(fqn)) {
+ Node n = treeCache.get(fqn);
+ readers = (Set<Integer>) n.get("readers");
+ } else {
+ readers = new HashSet<Integer>();
+ }
+ readers.add(caller.getId());
+ treeCache.put(fqn,"readers",readers);
+ treeCache.put(fqn,"item",o);
+ success = true;
+ log.info("Put " + fqn);
+ } catch (CacheException e) {
+ e.printStackTrace(); // TODO: Customise this generated block
+ }
+ return success;
+ }
+
+ /**
+ * Put an object into the cache identified by its type and id
+ * @param id Id of the object to put
+ * @param clazz Type to put in
+ * @param o Object to put
+ * @return true if put was successful
+ * @see #putToCache(org.jboss.cache.Fqn, Object)
+ */
+ protected <T>boolean putToCache(int id,Class<T> clazz,T o) {
+ Fqn fqn = getFqn(id, clazz);
+ return putToCache(fqn,o);
+ }
+
+ /**
+ * Construct a Fqn object from the passed data
+ * @param id Id of the target object
+ * @param clazz Type of object for that node
+ * @return Fqn object
+ */
+ protected <T> Fqn getFqn(int id, Class<T> clazz) {
+ return new Fqn(new Object[]{clazz.getName(), String.valueOf(id)});
+ }
+
+ /**
+ * Remove an item from the cache
+ * @param operationId Id of the item
+ * @param clazz Type of object for that node
+ * @return true if object is no longer in cache
+ */
+ protected <T> boolean removeFromCache(int operationId, Class<T> clazz) {
+ Fqn fqn = getFqn(operationId,clazz);
+ if (treeCache.exists(fqn)) {
+ try {
+ treeCache.remove(fqn);
+ log.info("Cancel " + fqn);
+ return true;
+ } catch (CacheException e) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 9908b7d..f21a90c 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -36,6 +36,8 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
+import org.jboss.cache.Fqn;
+
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementSchedule;
@@ -80,7 +82,17 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
}
- MeasurementSchedule schedule =
scheduleManager.getScheduleById(caller,scheduleId);
+ MeasurementSchedule schedule;
+ schedule = getFromCache(scheduleId,MeasurementSchedule.class);
+ if (schedule==null) {
+ schedule = scheduleManager.getScheduleById(caller,scheduleId);
+ if (schedule==null) {
+ throw new StuffNotFoundException("Schedule with id " +
scheduleId);
+ }
+ else
+ putToCache(scheduleId,MeasurementSchedule.class,schedule);
+ }
+
if (schedule.getDefinition().getDataType()!= DataType.MEASUREMENT)
throw new IllegalArgumentException("Schedule [" + scheduleId +
"] is not a (numerical) metric");
@@ -140,14 +152,43 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
* @param scheduleId ID of the schedule
* @param request the REST request - injected by the REST framework
* @param headers the REST request http headers - injected by the REST framework
- * @param uriInfo
+ * @param uriInfo info about the called uri to build links
* @return Schedule with respective headers
*/
public Response getSchedule(int scheduleId, Request request, HttpHeaders headers,
UriInfo uriInfo) {
- MeasurementSchedule schedule = scheduleManager.getScheduleById(caller,
scheduleId);
- if (schedule==null)
- throw new StuffNotFoundException("Schedule with id " +
scheduleId);
+ MeasurementSchedule schedule=null;
+ Response.ResponseBuilder builder=null;
+
+ // Create a cache control
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(300); // Schedules are valid for 5 mins
+ cc.setPrivate(false); // Proxies may cache this
+
+
+ Fqn fqn = getFqn(scheduleId,MeasurementSchedule.class);
+ schedule = getFromCache(fqn,MeasurementSchedule.class);
+ if (schedule!=null) {
+ // If it is on cache, quickly return if match
+ long tim = schedule.getMtime() != null ? schedule.getMtime() : 0;
+ EntityTag eTag = new EntityTag(Long.toOctalString(schedule.hashCode()+tim));
// factor in mtime in etag
+ builder = request.evaluatePreconditions(new Date(tim),eTag);
+
+ if (builder!=null) {
+ builder.cacheControl(cc);
+ return builder.build();
+ }
+
+
+ }
+
+ if (schedule==null) {
+ schedule = scheduleManager.getScheduleById(caller, scheduleId);
+ if (schedule==null)
+ throw new StuffNotFoundException("Schedule with id " +
scheduleId);
+ else
+ putToCache(fqn,schedule);
+ }
MeasurementDefinition definition = schedule.getDefinition();
MetricSchedule metricSchedule = new MetricSchedule(schedule.getId(),
definition.getName(),
@@ -157,14 +198,14 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
if (schedule.getMtime()!=null)
metricSchedule.setMtime(schedule.getMtime());
- // What media type does the user request?
- MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
- // Check for conditional get
+ // Check for conditional get again
// Interestingly computing the hashCode of the original schedule is slower, as it
also
// pulls in data from the definition and the resource
- EntityTag eTag = new
EntityTag(Integer.toOctalString(metricSchedule.hashCode()));
- Response.ResponseBuilder builder = request.evaluatePreconditions(new
Date(metricSchedule.getMtime()),eTag);
+ long tim = schedule.getMtime() != null ? schedule.getMtime() : 0;
+ EntityTag eTag = new EntityTag(Long.toOctalString(schedule.hashCode()+tim));
+ builder = request.evaluatePreconditions(new Date(tim),eTag); // factor in mtime
in etag
+
if (builder==null) {
// preconditions not met, we need to send the resource
@@ -173,16 +214,24 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
uriBuilder.path("metric/data/" + scheduleId);
URI uri = uriBuilder.build();
-
Link link = new Link("metrics",uri.toString());
metricSchedule.addLink(link);
+ // create link to the resource
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("resource/" + schedule.getResource().getId());
+ uri = uriBuilder.build();
+ link = new Link("resource",uri.toString());
+ metricSchedule.addLink(link);
+
// Link for updates
uriBuilder = uriInfo.getAbsolutePathBuilder();
uri = uriBuilder.build();
Link updateLink = new Link("edit",uri.toString());
metricSchedule.addLink(updateLink);
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("metricSchedule",
metricSchedule), mediaType);
@@ -192,10 +241,6 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
}
}
- // Create a cache control
- CacheControl cc = new CacheControl();
- cc.setMaxAge(300); // Schedules are valid for 5 mins
- cc.setPrivate(false); // Proxies may cache this
builder.cacheControl(cc);
builder.tag(eTag);
@@ -208,6 +253,9 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
List<MeasurementSchedule> schedules =
scheduleManager.findSchedulesForResourceAndType(caller,
resourceId, DataType.MEASUREMENT, null,false);
+ for (MeasurementSchedule sched: schedules) {
+ putToCache(sched.getId(),MeasurementSchedule.class,sched);
+ }
List<MetricAggregate> ret = new
ArrayList<MetricAggregate>(schedules.size());
long now = System.currentTimeMillis();
@@ -239,6 +287,8 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
scheduleManager.updateSchedule(caller, schedule);
schedule = scheduleManager.getScheduleById(caller,scheduleId);
+ Fqn fqn = getFqn(scheduleId,MeasurementSchedule.class);
+ putToCache(fqn,schedule);
MeasurementDefinition def = schedule.getDefinition();
MetricSchedule ret = new
MetricSchedule(scheduleId,def.getName(),def.getDisplayName(),
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 76c3f9d..368ef36 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -73,8 +73,8 @@ public interface MetricHandlerLocal {
@PUT
@Path("/schedule/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
+ @Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
MetricSchedule updateSchedule(@PathParam("id") int scheduleId,
MetricSchedule in);
}
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
new file mode 100644
index 0000000..2a4f7df
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerBean.java
@@ -0,0 +1,236 @@
+package org.rhq.enterprise.server.rest;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.interceptor.Interceptors;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
+import org.rhq.core.domain.configuration.definition.PropertyDefinition;
+import org.rhq.core.domain.operation.OperationDefinition;
+import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.enterprise.server.operation.OperationManagerLocal;
+import org.rhq.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.rest.domain.Link;
+import org.rhq.enterprise.server.rest.domain.OperationDefinitionRest;
+import org.rhq.enterprise.server.rest.domain.OperationRest;
+
+/**
+ * Deal with operations
+ * @author Heiko W. Rupp
+ */
+@Stateless
+(a)Interceptors(SetCallerInterceptor.class)
+public class OperationsHandlerBean extends AbstractRestBean implements
OperationsHandlerLocal {
+
+ @EJB
+ OperationManagerLocal opsManager;
+
+ @EJB
+ ResourceManagerLocal resourceManager;
+
+ @Override
+ public Response getOperationDefinition(int definitionId,
+ Integer resourceId,
+ UriInfo uriInfo,
+ Request request, HttpHeaders httpHeaders) {
+
+
+
+ OperationDefinition def;
+ def = getFromCache(definitionId, OperationDefinition.class);
+ if (def==null) {
+ def = opsManager.getOperationDefinition(caller,definitionId);
+ if (def==null)
+ throw new StuffNotFoundException("OperationDefinition with id "
+ definitionId);
+ else
+ putToCache(definitionId,OperationDefinition.class,def);
+ }
+
+ EntityTag eTag = new EntityTag(Integer.toHexString(def.hashCode()));
+ Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
+ if (builder==null) {
+
+ OperationDefinitionRest odr = new OperationDefinitionRest();
+ odr.setId(def.getId());
+ odr.setName(def.getName());
+
+ builder=Response.ok(odr);
+
+ // Add some links
+ if (resourceId!=null) {
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/definition/{id}");
+ uriBuilder.queryParam("resourceId",resourceId);
+ Link createLink = new
Link("create",uriBuilder.build(definitionId).toString());
+ odr.addLink(createLink);
+ }
+
+ }
+
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(1200); // Definitions are very stable, so caching for 20mins is ok
+ cc.setPrivate(false);
+ builder.tag(eTag);
+ builder.cacheControl(cc);
+
+ return builder.build();
+
+ }
+
+ @Override
+ // TODO find out why a return type of Response lets this fail for XML (even with
@Wrapped annotation
+ public List<OperationDefinitionRest> getOperationDefinitions(Integer
resourceId, UriInfo uriInfo, Request request) {
+
+ if (resourceId == null)
+ throw new ParameterMissingException("resourceId");
+
+ Resource res =resourceManager.getResource(caller,resourceId);
+ if(res==null)
+ throw new StuffNotFoundException("resource with id " +
resourceId);
+
+ ResourceType resourceType = res.getResourceType();
+
+/*
+ EntityTag eTag = new EntityTag(Integer.toHexString(resourceType.hashCode()));
+ Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
+ if (builder==null) {
+*/
+
+ Set<OperationDefinition> opDefList =
resourceType.getOperationDefinitions();
+ List<OperationDefinitionRest> resultList = new
ArrayList<OperationDefinitionRest>(opDefList.size());
+
+ for (OperationDefinition def : opDefList) {
+ OperationDefinitionRest odr = new OperationDefinitionRest();
+ odr.setId(def.getId());
+ odr.setName(def.getName());
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/definition/{id}");
+ uriBuilder.queryParam("resourceId",resourceId);
+ Link createLink = new
Link("create",uriBuilder.build(def.getId()).toString());
+ odr.addLink(createLink);
+
+ resultList.add(odr);
+ }
+ return resultList;
+
+/*
+
+ builder = Response.ok(resultList);
+ }
+
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(1200); // Definitions are very stable, so caching for 20mins is ok
+ cc.setPrivate(false);
+ builder.tag(eTag);
+ builder.cacheControl(cc);
+
+ return builder.build();
+*/
+
+ }
+
+ @Override
+ public Response createOperation(@PathParam("id") int definitionId,
+ @QueryParam("resourceId") Integer
resourceId,
+ UriInfo uriInfo) {
+
+ if (resourceId == null)
+ throw new ParameterMissingException("resourceId");
+
+ Resource res =resourceManager.getResource(caller,resourceId);
+ if(res==null)
+ throw new StuffNotFoundException("resource with id " +
resourceId);
+
+
+ OperationDefinition opDef =
opsManager.getOperationDefinition(caller,definitionId);
+ if (opDef==null) {
+ throw new StuffNotFoundException("Operation definition with id " +
definitionId);
+ }
+ OperationRest operationRest = new OperationRest(resourceId,definitionId);
+ operationRest.setId((int)System.currentTimeMillis()); // TODO better id (?)(we
need one for pUT later on)
+ operationRest.setState("creating");
+ operationRest.setName(opDef.getName());
+ ConfigurationDefinition paramDefinition =
opDef.getParametersConfigurationDefinition();
+ if (paramDefinition != null) {
+ for (PropertyDefinition propDefs : paramDefinition.getNonGroupedProperties())
{ // TODO extend to all properties ?
+ operationRest.getParams().put(propDefs.getName(),"TODO"); //
TODO type and value of the value
+ }
+ }
+
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/{id}");
+ URI uri = uriBuilder.build(operationRest.getId());
+ Link editLink = new Link("edit",uri.toString());
+ operationRest.addLink(editLink);
+ Response.ResponseBuilder builder = Response.ok(operationRest);
+
+ putToCache(operationRest.getId(),OperationRest.class,operationRest);
+
+ return builder.build();
+
+ }
+
+ @Override
+ public Response getOperation(@PathParam("id") int operationId) {
+ OperationRest op = getFromCache(operationId,OperationRest.class);
+ if (op==null)
+ throw new StuffNotFoundException("Operation with id " +
operationId);
+
+ return Response.ok(op).build();
+ }
+
+ @Override
+ public Response updateOperation(@PathParam("id") int operationId,
OperationRest operation, UriInfo uriInfo) {
+
+ if (operation.getState().equals("creating") &&
operation.getDefinitionId()>0 && !operation.getName().isEmpty()) {
+ // TODO check all the required parameters for persence before allowing to
submit
+ Link link = new Link("submit","TODO");
+ operation.addLink(link);
+
+ }
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/{id}");
+ URI uri = uriBuilder.build(operation.getId());
+ Link editLink = new Link("edit",uri.toString());
+ operation.addLink(editLink);
+
+ // Update item in cache
+ putToCache(operation.getId(),OperationRest.class,operation);
+
+ Response.ResponseBuilder builder = Response.ok(operation);
+ return builder.build();
+ }
+
+ @Override
+ @DELETE
+ @Path("{id}")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
+ public Response cancelOperation(@PathParam("id") int operationId) {
+
+ log.info("Cancel called");
+
+ removeFromCache(operationId,OperationRest.class);
+
+ return null; // TODO: Customise this generated block
+ }
+
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
new file mode 100644
index 0000000..46a7f86
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/OperationsHandlerLocal.java
@@ -0,0 +1,76 @@
+package org.rhq.enterprise.server.rest;
+
+import java.util.List;
+
+import javax.ejb.Local;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+import org.jboss.resteasy.annotations.providers.jaxb.Wrapped;
+
+import org.rhq.enterprise.server.rest.domain.OperationDefinitionRest;
+import org.rhq.enterprise.server.rest.domain.OperationRest;
+
+/**
+ * Service that deals with operations
+ * @author Heiko W. Rupp
+ */
+@Local
+@Path("/operation")
+public interface OperationsHandlerLocal {
+
+
+ @GET
+ @Path("definition/{id}")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public Response getOperationDefinition(@PathParam("id") int definitionId,
+ @QueryParam("resourceId") Integer
resourceId,
+ @Context UriInfo uriInfo,
+ @Context Request request,
+ @Context HttpHeaders httpHeaders);
+
+ @GET
+ @Path("definitions")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public List<OperationDefinitionRest>
getOperationDefinitions(@QueryParam("resourceId") Integer resourceId,
+ @Context UriInfo uriInfo,
+ @Context Request request
+ );
+
+ @POST
+ @Path("definition/{id}")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public Response createOperation(@PathParam("id") int definitionId,
+ @QueryParam("resourceId") Integer
resourceId,
+ @Context UriInfo uriInfo);
+
+ @GET
+ @Path("{id}")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public Response getOperation(@PathParam("id") int operationId);
+
+ @PUT
+ @Path("{id}")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ @Consumes({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public Response updateOperation(@PathParam("id") int operationId,
OperationRest operation, @Context UriInfo uriInfo);
+
+ @DELETE
+ @Path("{id}")
+ @Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
+ public Response cancelOperation(@PathParam("id") int operationId);
+
+}
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 368efc0..e656c24 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
@@ -24,12 +24,20 @@ package org.rhq.enterprise.server.rest;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
@@ -71,44 +79,88 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
AlertManagerLocal alertManager;
@Override
- public ResourceWithType getResource(int id) {
+ public Response getResource(int id, @Context Request request, @Context HttpHeaders
headers,
+ @Context UriInfo uriInfo) {
- Resource res = resMgr.getResource(caller, id);
+ // Create a cache control
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(300); // Resources are valid for 5 mins
+ cc.setPrivate(false); // Proxies may cache this
- ResourceWithType rwt = fillRWT(res);
+ Resource res;
+ res = getFromCache(id,Resource.class);
+ if (res==null) {
+ res = resMgr.getResource(caller, id);
+ if (res!=null)
+ putToCache(id, Resource.class,res);
+ else
+ throw new StuffNotFoundException("Resource with id " + id);
+ }
- return rwt;
- }
+ Response.ResponseBuilder builder=null;
- @Override
- public String getResourceHtml(int id) {
- ResourceWithType rwt = getResource(id);
- return renderTemplate("resourceWithType.ftl", rwt);
+ long mtime = res.getMtime();
+ EntityTag eTag = new EntityTag(Long.toOctalString(res.hashCode()+ mtime)); //
factor in mtime in etag
+ builder = request.evaluatePreconditions(new Date(mtime),eTag);
+
+ if (builder!=null) {
+ builder.cacheControl(cc);
+ return builder.build();
+ }
+
+
+ ResourceWithType rwt = fillRWT(res, uriInfo);
+
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("resourceWithType", rwt),
mediaType);
+ }
+ else {
+ builder = Response.ok(rwt);
+ }
+
+ return builder.build();
}
+
@Override
- public List<ResourceWithType> getPlatforms() {
+ public Response getPlatforms(@Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo) {
PageControl pc = new PageControl();
List<Resource> ret = resMgr.findResourcesByCategory(caller,
ResourceCategory.PLATFORM, InventoryStatus.COMMITTED, pc) ;
List<ResourceWithType> rwtList = new
ArrayList<ResourceWithType>(ret.size());
for (Resource r: ret) {
- ResourceWithType rwt = fillRWT(r);
+ putToCache(r.getId(),Resource.class,r);
+ ResourceWithType rwt = fillRWT(r, uriInfo);
rwtList.add(rwt);
}
- return rwtList;
- }
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
- @Override
- public String getPlatformsHtml() {
- List<ResourceWithType> list = getPlatforms();
- return renderTemplate("listResourceWithType", list);
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listResourceWithType",
rwtList), mediaType);
+ }
+ else {
+ builder = Response.ok(rwtList);
+ }
+
+ return builder.build();
}
@Override
public ResourceWithChildren getHierarchy(int baseResourceId) {
// TODO optimize to do less recursion
- Resource start = resMgr.getResource(caller,baseResourceId);
+ Resource start;
+ start = getFromCache(baseResourceId,Resource.class);
+ if (start==null) {
+ start = resMgr.getResource(caller,baseResourceId);
+ if (start!=null)
+ putToCache(start.getId(),Resource.class,start);
+ }
ResourceWithChildren rwc = getHierarchy(start);
/*new ResourceWithChildren(""+start.getId(),start.getName());
@@ -135,6 +187,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
for (Resource res : ret) {
ResourceWithChildren child = getHierarchy(res);
resList.add(child);
+ putToCache(res.getId(),Resource.class,res);
}
if (!resList.isEmpty())
rwc.setChildren(resList);
@@ -151,7 +204,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
return availabilityRest;
}
- private ResourceWithType fillRWT(Resource res) {
+ private ResourceWithType fillRWT(Resource res, UriInfo uriInfo) {
ResourceType resourceType = res.getResourceType();
ResourceWithType rwt = new ResourceWithType(res.getName(),res.getId());
rwt.setTypeName(resourceType.getName());
@@ -161,6 +214,13 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if (parent!=null) {
rwt.setParentId(parent.getId());
}
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/operation/definitions");
+ uriBuilder.queryParam("resourceId",res.getId());
+ URI uri = uriBuilder.build();
+ Link link = new Link("operationDefinitions",uri.toString());
+ rwt.addLink(link);
+
return rwt;
}
@@ -171,6 +231,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
Set<MeasurementSchedule> schedules = res.getSchedules();
List<MetricSchedule> ret = new
ArrayList<MetricSchedule>(schedules.size());
for (MeasurementSchedule schedule : schedules) {
+ putToCache(schedule.getId(),MeasurementSchedule.class,schedule);
MeasurementDefinition definition = schedule.getDefinition();
MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
@@ -180,6 +241,13 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
URI uri = uriBuilder.build(schedule.getId());
Link metricLink = new Link("metric",uri.toString());
ms.addLink(metricLink);
+ // create link to the resource
+ uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("resource/" + schedule.getResource().getId());
+ uri = uriBuilder.build();
+ Link link = new Link("resource",uri.toString());
+ ms.addLink(link);
+
ret.add(ms);
}
@@ -192,24 +260,42 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
}
@Override
- public List<ResourceWithType> getChildren(int id) {
+ public Response getChildren(int id, @Context Request request, @Context HttpHeaders
headers,
+ @Context UriInfo uriInfo) {
PageControl pc = new PageControl();
- Resource parent = resMgr.getResource(caller,id);
+ Resource parent;
+ parent = getFromCache(id,Resource.class);
+ if (parent==null) {
+ parent = resMgr.getResource(caller,id);
+ if (parent==null)
+ throw new StuffNotFoundException("Resource with id " + id);
+ else
+ putToCache(id,Resource.class,parent);
+ }
List<Resource> ret =
resMgr.findResourceByParentAndInventoryStatus(caller,parent,InventoryStatus.COMMITTED,pc);
List<ResourceWithType> rwtList = new
ArrayList<ResourceWithType>(ret.size());
for (Resource r: ret) {
- ResourceWithType rwt = fillRWT(r);
+ putToCache(r.getId(),Resource.class,r);
+ ResourceWithType rwt = fillRWT(r, uriInfo);
rwtList.add(rwt);
}
- return rwtList;
- }
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ Response.ResponseBuilder builder;
+
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ builder = Response.ok(renderTemplate("listResourceWithType",
rwtList), mediaType);
+ }
+ else {
+ builder = Response.ok(rwtList);
+ }
+
+ return builder.build();
- public String getChildrenHtml(int id) {
- List<ResourceWithType> list = getChildren(id);
- return renderTemplate("listResourceWithType", list);
}
+
@Override
public List<Link> getAlertsForResource(int resourceId) {
AlertCriteria criteria = new AlertCriteria();
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index c2e8698..d1cb0f2 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -26,6 +26,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.links.AddLinks;
@@ -53,23 +57,15 @@ public interface ResourceHandlerLocal {
})
@GET
@Path("/{id}")
- @Produces({"application/json","application/xml"})
- ResourceWithType getResource(@PathParam("id") int id);
-
- @GET
- @Path("/{id}")
- @Produces("text/html")
- String getResourceHtml(@PathParam("id") int id);
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+ Response getResource(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
@GET
@Path("/platforms")
- @Produces({"application/json","application/xml"})
- List<ResourceWithType> getPlatforms();
-
- @GET
- @Path("/platforms")
- @Produces("text/html")
- String getPlatformsHtml();
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+ Response getPlatforms(@Context Request request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
@GET
@@ -93,20 +89,17 @@ public interface ResourceHandlerLocal {
@Path("/{id}/schedules")
@Produces("text/html")
@LinkResource(rel="schedules",value = MetricSchedule.class)
+ // TODO move to ScheduleManager, link from resource and pass the resource as
query param
String getSchedulesHtml(@PathParam("id") int resourceId,
@Context UriInfo uriInfo);
@GET
@Path("/{id}/children")
- @Produces({"application/json","application/xml"})
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
@LinkResource(rel="children", value = ResourceWithType.class)
- List<ResourceWithType> getChildren(@PathParam("id") int id);
+ Response getChildren(@PathParam("id") int id, @Context Request request,
@Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
- @GET
- @Path("/{id}/children")
- @Produces("text/html")
- @LinkResource(rel="children", value = ResourceWithType.class)
- String getChildrenHtml(@PathParam("id") int id);
@AddLinks
@GET
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationDefinitionRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationDefinitionRest.java
new file mode 100644
index 0000000..70f1a97
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationDefinitionRest.java
@@ -0,0 +1,52 @@
+package org.rhq.enterprise.server.rest.domain;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Heiko W. Rupp
+ */
+@XmlRootElement
+public class OperationDefinitionRest {
+
+ public OperationDefinitionRest() {
+ }
+
+ String name;
+ int id;
+ List<Link> links = new ArrayList<Link>();
+ // TODO add parameters
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @XmlElementRef
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ this.links = links;
+ }
+
+ public void addLink(Link link) {
+ links.add(link);
+ }
+
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationRest.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationRest.java
new file mode 100644
index 0000000..50332ae
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/OperationRest.java
@@ -0,0 +1,117 @@
+package org.rhq.enterprise.server.rest.domain;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.annotation.XmlElementRef;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * @author Heiko W. Rupp
+ */
+@XmlRootElement
+public class OperationRest {
+
+ int id;
+ String name;
+ String state; // TODO Convert to enum
+ int resourceId;
+ int definitionId;
+ Map<String,Object> params = new HashMap<String, Object>();
+ List<Link> links = new ArrayList<Link>();
+
+ public OperationRest() {
+ }
+
+ public OperationRest(int resourceId, int definitionId) {
+ this.resourceId = resourceId;
+ this.definitionId = definitionId;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public void setState(String state) {
+ this.state = state;
+ }
+
+ public int getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(int resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public int getDefinitionId() {
+ return definitionId;
+ }
+
+ public void setDefinitionId(int definitionId) {
+ this.definitionId = definitionId;
+ }
+
+ public Map<String, Object> getParams() {
+ return params;
+ }
+
+ public void setParams(Map<String, Object> params) {
+ this.params = params;
+ }
+
+ @XmlElementRef
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ this.links = links;
+ }
+
+ public void addLink(Link link) {
+ links.add(link);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ OperationRest that = (OperationRest) o;
+
+ if (definitionId != that.definitionId) return false;
+ if (id != that.id) return false;
+ if (resourceId != that.resourceId) return false;
+ if (name != null ? !name.equals(that.name) : that.name != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (int) (id ^ (id >>> 32));
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + resourceId;
+ result = 31 * result + definitionId;
+ return result;
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricSchedule.ftl
b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricSchedule.ftl
index 26360e5..0a0f187 100644
--- a/modules/enterprise/server/jar/src/main/resources/rest_templates/metricSchedule.ftl
+++ b/modules/enterprise/server/jar/src/main/resources/rest_templates/metricSchedule.ftl
@@ -46,7 +46,10 @@
<td>Units</td><td>${var.unit}</td>
</tr>
</table>
- <a href="/rest/1/metric/data/${var.scheduleId}.html">Metric
data</a>
- <br/>
-
+ Links<br/>
+ <ul>
+ <#list var.links as link>
+ <li><a href="${link.href}.html">${link.rel}</a>
</li>
+ </#list>
+ </ul>
</html>
\ No newline at end of file
commit 001a8e84ccb27d43979c10ce2103a6106440dfea
Author: Heiko W. Rupp <pilhuhn(a)fedoraproject.org>
Date: Wed Nov 2 17:58:59 2011 +0100
Remove bogus import.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
index ff160e2..d951767 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
@@ -23,8 +23,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
-import sun.security.provider.certpath.OCSPResponse;
-
import org.rhq.enterprise.server.resource.ResourceNotFoundException;
/**
commit c6be98cee4043752d110f191a039a11bfe869465
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Nov 2 08:51:54 2011 +0100
Introduce a ParamMissing exception for missing query params
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
index 57385cd..ff160e2 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/CustomExceptionMapper.java
@@ -23,6 +23,10 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
+import sun.security.provider.certpath.OCSPResponse;
+
+import org.rhq.enterprise.server.resource.ResourceNotFoundException;
+
/**
* Map a NotFoundException to a HTTP response with respective error message
* @author Heiko W. Rupp
@@ -40,6 +44,14 @@ public class CustomExceptionMapper implements
ExceptionMapper<EJBException> {
return Response.status(Response.Status.NOT_FOUND)
.entity(cause.getMessage())
.build();
+ else if (cause instanceof ResourceNotFoundException)
+ return Response.status(Response.Status.NOT_FOUND)
+ .entity(cause.getMessage())
+ .build();
+ else if (cause instanceof ParameterMissingException)
+ return Response.status(Response.Status.NOT_ACCEPTABLE)
+ .entity(cause.getMessage())
+ .build();
else
return Response.status(Response.Status.SERVICE_UNAVAILABLE)
.entity(cause.getMessage())
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ParameterMissingException.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ParameterMissingException.java
new file mode 100644
index 0000000..16fd62f
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ParameterMissingException.java
@@ -0,0 +1,12 @@
+package org.rhq.enterprise.server.rest;
+
+/**
+ * Exception thrown if (Query) Parameters are missing
+ * @author Heiko W. Rupp
+ */
+public class ParameterMissingException extends RuntimeException {
+
+ public ParameterMissingException(String what) {
+ super("(Query)param " + what + " is missing");
+ }
+}
commit 47a3371266a2607681cd7ee05f9028ef03bb86ed
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Nov 2 08:48:34 2011 +0100
Add hyperlinks from resource/schedules to their metrics
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 5251805..368efc0 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
@@ -22,6 +22,7 @@
*/
package org.rhq.enterprise.server.rest;
+import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -29,8 +30,8 @@ import java.util.Set;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
-
-import org.jboss.resteasy.spi.Link;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
import org.rhq.core.domain.alert.Alert;
import org.rhq.core.domain.criteria.AlertCriteria;
@@ -42,8 +43,8 @@ import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.alert.AlertManagerLocal;
-import org.rhq.enterprise.server.plugin.pc.content.RepoSource;
import org.rhq.enterprise.server.rest.domain.AvailabilityRest;
+import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
import org.rhq.enterprise.server.rest.domain.ResourceWithChildren;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
@@ -163,7 +164,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
return rwt;
}
- public List<MetricSchedule> getSchedules(int resourceId) {
+ public List<MetricSchedule> getSchedules(int resourceId, UriInfo uriInfo) {
Resource res = resMgr.getResource(caller, resourceId);
@@ -174,14 +175,19 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
MetricSchedule ms = new MetricSchedule(schedule.getId(),
definition.getName(), definition.getDisplayName(),
schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
definition.getDataType().toString());
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("/metric/data/{id}");
+ URI uri = uriBuilder.build(schedule.getId());
+ Link metricLink = new Link("metric",uri.toString());
+ ms.addLink(metricLink);
ret.add(ms);
}
return ret;
}
- public String getSchedulesHtml(int resourceId) {
- List<MetricSchedule> list = getSchedules(resourceId);
+ public String getSchedulesHtml(int resourceId, UriInfo uriInfo) {
+ List<MetricSchedule> list = getSchedules(resourceId, uriInfo);
return renderTemplate("listMetricSchedule", list);
}
@@ -212,7 +218,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
List<Link> links = new ArrayList<Link>(alerts.size());
for (Alert al: alerts) {
Link link = new Link();
- link.setRelationship("alert");
+ link.setRel("alert");
link.setHref("/alert/" + al.getId());
}
return links;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
index a36d4fb..c2e8698 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerLocal.java
@@ -25,13 +25,15 @@ import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.UriInfo;
import org.jboss.resteasy.links.AddLinks;
import org.jboss.resteasy.links.LinkResource;
import org.jboss.resteasy.links.LinkResources;
-import org.jboss.resteasy.spi.Link;
import org.rhq.enterprise.server.rest.domain.AvailabilityRest;
+import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
import org.rhq.enterprise.server.rest.domain.ResourceWithChildren;
import org.rhq.enterprise.server.rest.domain.ResourceWithType;
@@ -84,13 +86,15 @@ public interface ResourceHandlerLocal {
@Path("/{id}/schedules")
@Produces({"application/json","application/xml"})
@LinkResource(rel="schedules",value = MetricSchedule.class)
- List<MetricSchedule> getSchedules(@PathParam("id") int resourceId);
+ List<MetricSchedule> getSchedules(@PathParam("id") int resourceId,
+ @Context UriInfo uriInfo);
@GET
@Path("/{id}/schedules")
@Produces("text/html")
@LinkResource(rel="schedules",value = MetricSchedule.class)
- String getSchedulesHtml(@PathParam("id") int resourceId);
+ String getSchedulesHtml(@PathParam("id") int resourceId,
+ @Context UriInfo uriInfo);
@GET
@Path("/{id}/children")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
index 894277a..1e5971c 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/ResourceWithType.java
@@ -22,6 +22,9 @@
*/
package org.rhq.enterprise.server.rest.domain;
+import java.util.ArrayList;
+import java.util.List;
+
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
@@ -32,9 +35,6 @@ import javax.xml.bind.annotation.XmlRootElement;
import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
-import org.jboss.resteasy.links.AddLinks;
-import org.jboss.resteasy.links.LinkResource;
-import org.jboss.resteasy.links.RESTServiceDiscovery;
import org.jboss.resteasy.spi.touri.URITemplate;
/**
@@ -54,8 +54,8 @@ public class ResourceWithType {
Integer typeId;
String pluginName;
Integer parentId;
+ List<Link> links = new ArrayList<Link>();
- private RESTServiceDiscovery rest;
public ResourceWithType() {
}
@@ -124,12 +124,15 @@ public class ResourceWithType {
}
@XmlElementRef
- public RESTServiceDiscovery getRest() {
- return rest;
+ public List<Link> getLinks() {
+ return links;
}
- public void setRest(RESTServiceDiscovery rest) {
- this.rest = rest;
+ public void setLinks(List<Link> links) {
+ this.links = links;
}
+ public void addLink(Link link) {
+ links.add(link);
+ }
}
commit 691dc9c0cce937f4c1b979145bfa6458382c29c0
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Oct 28 11:01:52 2011 +0200
Add support for hyperlinking between schedule and metrics and to the edit schedule
method.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 0f0c9a9..9908b7d 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -18,6 +18,7 @@
*/
package org.rhq.enterprise.server.rest;
+import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@@ -32,12 +33,15 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
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.enterprise.server.resource.ResourceManagerLocal;
+import org.rhq.enterprise.server.rest.domain.Link;
import org.rhq.enterprise.server.rest.domain.MetricAggregate;
import org.rhq.enterprise.server.measurement.MeasurementAggregate;
import org.rhq.enterprise.server.measurement.MeasurementDataManagerLocal;
@@ -132,12 +136,14 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
/**
* Return a metric schedule with the respective status codes for cache validation
*
+ *
* @param scheduleId ID of the schedule
* @param request the REST request - injected by the REST framework
* @param headers the REST request http headers - injected by the REST framework
+ * @param uriInfo
* @return Schedule with respective headers
*/
- public Response getSchedule(int scheduleId, Request request, HttpHeaders headers) {
+ public Response getSchedule(int scheduleId, Request request, HttpHeaders headers,
UriInfo uriInfo) {
MeasurementSchedule schedule = scheduleManager.getScheduleById(caller,
scheduleId);
if (schedule==null)
@@ -155,11 +161,29 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
// Check for conditional get
+ // Interestingly computing the hashCode of the original schedule is slower, as it
also
+ // pulls in data from the definition and the resource
EntityTag eTag = new
EntityTag(Integer.toOctalString(metricSchedule.hashCode()));
Response.ResponseBuilder builder = request.evaluatePreconditions(new
Date(metricSchedule.getMtime()),eTag);
if (builder==null) {
// preconditions not met, we need to send the resource
+
+ // create link to metrics
+ UriBuilder uriBuilder = uriInfo.getBaseUriBuilder();
+ uriBuilder.path("metric/data/" + scheduleId);
+ URI uri = uriBuilder.build();
+
+ Link link = new Link("metrics",uri.toString());
+ metricSchedule.addLink(link);
+
+ // Link for updates
+ uriBuilder = uriInfo.getAbsolutePathBuilder();
+ uri = uriBuilder.build();
+ Link updateLink = new Link("edit",uri.toString());
+ metricSchedule.addLink(updateLink);
+
+
if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("metricSchedule",
metricSchedule), mediaType);
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 053005b..76c3f9d 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -34,6 +34,7 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
import org.rhq.enterprise.server.rest.domain.MetricAggregate;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
@@ -67,7 +68,8 @@ public interface MetricHandlerLocal {
@GET
@Path("/schedule/{id}")
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
- Response getSchedule(@PathParam("id") int scheduleId, @Context Request
request, @Context HttpHeaders headers);
+ Response getSchedule(@PathParam("id") int scheduleId, @Context Request
request, @Context HttpHeaders headers,
+ @Context UriInfo uriInfo);
@PUT
@Path("/schedule/{id}")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Link.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Link.java
new file mode 100644
index 0000000..1936425
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/Link.java
@@ -0,0 +1,50 @@
+package org.rhq.enterprise.server.rest.domain;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * A link between two resources
+ * @author Heiko W. Rupp
+ */
+@XmlRootElement
+public class Link {
+
+ private String rel;
+ private String href;
+
+ public Link() {
+ }
+
+ public Link(String rel, String href) {
+ this.rel = rel;
+ this.href = href;
+ }
+
+
+ @XmlAttribute
+ public String getRel() {
+ return rel;
+ }
+
+ @XmlAttribute
+ public String getHref() {
+ return href;
+ }
+
+ public void setRel(String rel) {
+ this.rel = rel;
+ }
+
+ public void setHref(String href) {
+ this.href = href;
+ }
+
+ @Override
+ public String toString() {
+ return "Link{" +
+ "rel='" + rel + '\'' +
+ ", href='" + href + '\'' +
+ '}';
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
index 377ca87..4333530 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
@@ -18,6 +18,11 @@
*/
package org.rhq.enterprise.server.rest.domain;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
@@ -44,6 +49,7 @@ public class MetricSchedule {
String unit;
String type;
transient long mtime;
+ List<Link> links = new ArrayList<Link>();
@SuppressWarnings("unused")
public MetricSchedule() {
@@ -129,7 +135,14 @@ public class MetricSchedule {
this.mtime = mtime;
}
+ public void addLink(Link link) {
+ links.add(link);
+ }
+ @XmlElementRef
+ public List<Link> getLinks() {
+ return links;
+ }
@Override
public boolean equals(Object o) {
@@ -156,4 +169,5 @@ public class MetricSchedule {
result = 31 * result + (int) (mtime ^ (mtime >>> 32));
return result;
}
+
}
commit 9e080614d6b101bd3a86905583d4e4de002a89c7
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Oct 27 22:53:51 2011 +0200
API cleanup for getting metrics and schedules. Also fix updating of schedules.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 5beee77..0f0c9a9 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -26,6 +26,7 @@ import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.EntityTag;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
@@ -61,8 +62,10 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
private static final long EIGHT_HOURS = 8 * 3600L * 1000L;
@Override
- public MetricAggregate getMetricData(int scheduleId, long startTime, long endTime,
- int dataPoints,boolean hideEmpty) {
+ public Response getMetricData(int scheduleId, long startTime, long endTime,
+ int dataPoints,boolean hideEmpty,
+ @Context Request request,
+ @Context HttpHeaders headers) {
if (dataPoints<0)
throw new IllegalArgumentException("dataPoints must be >0 ");
@@ -105,19 +108,27 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
res.setMaxTimeStamp(maxTime);
res.setMinTimeStamp(minTime);
+ CacheControl cc = new CacheControl();
+ int maxAge = (int) (schedule.getInterval() / 1000L)/2; // millis ; half of
schedule interval
+ cc.setMaxAge(maxAge); // these are seconds
+ cc.setPrivate(false);
+ cc.setNoCache(false);
- return res;
- }
- public String getMetricDataHtml(int scheduleId,
- long startTime,
- long endTime,
- int dataPoints,
- boolean hideEmpty) {
- MetricAggregate agg =
getMetricData(scheduleId,startTime,endTime,dataPoints,hideEmpty);
- return renderTemplate("metricData", agg);
+ Response.ResponseBuilder builder;
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
+ String htmlString = renderTemplate("metricData", res);
+ builder = Response.ok(htmlString,mediaType);
+ }
+ else
+ builder= Response.ok(res);
+ builder.cacheControl(cc);
+
+ return builder.build();
}
+
/**
* Return a metric schedule with the respective status codes for cache validation
*
@@ -149,7 +160,7 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
if (builder==null) {
// preconditions not met, we need to send the resource
- if (mediaType == MediaType.TEXT_HTML_TYPE) {
+ if (mediaType.equals(MediaType.TEXT_HTML_TYPE)) {
builder = Response.ok(renderTemplate("metricSchedule",
metricSchedule), mediaType);
}
else {
@@ -191,7 +202,6 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
public MetricSchedule updateSchedule(int scheduleId, MetricSchedule in) {
if (in==null)
throw new StuffNotFoundException("Input is null"); // TODO other
type of exception
-
if (in.getScheduleId()==null)
throw new StuffNotFoundException("Invalid input data");
@@ -205,8 +215,10 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
scheduleManager.updateSchedule(caller, schedule);
schedule = scheduleManager.getScheduleById(caller,scheduleId);
+ MeasurementDefinition def = schedule.getDefinition();
- MetricSchedule ret = new
MetricSchedule(scheduleId,in.getScheduleName(),in.getDisplayName(),schedule.isEnabled(),schedule.getInterval(),in.getUnit(),in.getType());
+ MetricSchedule ret = new
MetricSchedule(scheduleId,def.getName(),def.getDisplayName(),
+
schedule.isEnabled(),schedule.getInterval(),def.getUnits().toString(),def.getDataType().toString());
return ret;
}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index f7802d3..053005b 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -18,6 +18,7 @@
*/
package org.rhq.enterprise.server.rest;
+import java.io.InputStream;
import java.util.List;
import javax.ejb.Local;
@@ -30,6 +31,7 @@ import javax.ws.rs.PUT;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
@@ -48,21 +50,15 @@ public interface MetricHandlerLocal {
@GET
@Path("data/{scheduleId}")
- @Produces({"application/json","application/xml"})
- MetricAggregate getMetricData(@PathParam("scheduleId") int scheduleId,
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
+ Response getMetricData(@PathParam("scheduleId") int scheduleId,
@QueryParam("startTime") long startTime,
@QueryParam("endTime") long endTime,
@QueryParam("dataPoints") int dataPoints,
- @QueryParam("hideEmpty") boolean hideEmpty);
+ @QueryParam("hideEmpty") boolean hideEmpty,
+ @Context Request request,
+ @Context HttpHeaders headers);
- @GET
- @Path("data/{scheduleId}")
- @Produces("text/html")
- String getMetricDataHtml(@PathParam("scheduleId") int scheduleId,
- @QueryParam("startTime") long startTime,
- @QueryParam("endTime") long endTime,
- @QueryParam("dataPoints") int dataPoints,
- @QueryParam("hideEmpty") boolean hideEmpty);
@GET
@Path("data/resource/{resourceId}")
@@ -70,12 +66,13 @@ public interface MetricHandlerLocal {
@GET
@Path("/schedule/{id}")
-
@Produces({"application/json","application/xml","text/html"})
+
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML,MediaType.TEXT_HTML})
Response getSchedule(@PathParam("id") int scheduleId, @Context Request
request, @Context HttpHeaders headers);
@PUT
@Path("/schedule/{id}")
- @Consumes("application/json")
- MetricSchedule updateSchedule(@PathParam("id") int scheduleId,
MetricSchedule in);
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ MetricSchedule updateSchedule(@PathParam("id") int scheduleId,
MetricSchedule in);
}
commit 8e40cd43b2ca814b43fcd17140d144ae6523cafb
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Oct 26 15:01:54 2011 +0200
Add cache control and merge all media types into one method
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
index 3ff403d..5beee77 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerBean.java
@@ -19,11 +19,18 @@
package org.rhq.enterprise.server.rest;
import java.util.ArrayList;
+import java.util.Date;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
+import javax.ws.rs.core.CacheControl;
+import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
@@ -111,23 +118,54 @@ public class MetricHandlerBean extends AbstractRestBean implements
MetricHandle
return renderTemplate("metricData", agg);
}
- public MetricSchedule getSchedule(int scheduleId) {
+ /**
+ * Return a metric schedule with the respective status codes for cache validation
+ *
+ * @param scheduleId ID of the schedule
+ * @param request the REST request - injected by the REST framework
+ * @param headers the REST request http headers - injected by the REST framework
+ * @return Schedule with respective headers
+ */
+ public Response getSchedule(int scheduleId, Request request, HttpHeaders headers) {
- MeasurementSchedule schedule =
scheduleManager.getScheduleById(caller,scheduleId);
+ MeasurementSchedule schedule = scheduleManager.getScheduleById(caller,
scheduleId);
if (schedule==null)
throw new StuffNotFoundException("Schedule with id " +
scheduleId);
+
MeasurementDefinition definition = schedule.getDefinition();
- MetricSchedule ms = new MetricSchedule(schedule.getId(), definition.getName(),
definition.getDisplayName(),
- schedule.isEnabled(),schedule.getInterval(),
definition.getUnits().toString(),
+ MetricSchedule metricSchedule = new MetricSchedule(schedule.getId(),
definition.getName(),
+ definition.getDisplayName(),
+ schedule.isEnabled(), schedule.getInterval(),
definition.getUnits().toString(),
definition.getDataType().toString());
+ if (schedule.getMtime()!=null)
+ metricSchedule.setMtime(schedule.getMtime());
- return ms;
- }
+ // What media type does the user request?
+ MediaType mediaType = headers.getAcceptableMediaTypes().get(0);
- @Override
- public String getScheduleHtml(int scheduleId) {
- MetricSchedule ms = getSchedule(scheduleId);
- return renderTemplate("metricSchedule", ms);
+ // Check for conditional get
+ EntityTag eTag = new
EntityTag(Integer.toOctalString(metricSchedule.hashCode()));
+ Response.ResponseBuilder builder = request.evaluatePreconditions(new
Date(metricSchedule.getMtime()),eTag);
+
+ if (builder==null) {
+ // preconditions not met, we need to send the resource
+ if (mediaType == MediaType.TEXT_HTML_TYPE) {
+ builder = Response.ok(renderTemplate("metricSchedule",
metricSchedule), mediaType);
+ }
+ else {
+ builder = Response.ok(metricSchedule);
+ }
+ }
+
+ // Create a cache control
+ CacheControl cc = new CacheControl();
+ cc.setMaxAge(300); // Schedules are valid for 5 mins
+ cc.setPrivate(false); // Proxies may cache this
+
+ builder.cacheControl(cc);
+ builder.tag(eTag);
+
+ return builder.build();
}
@Override
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
index 572fe5b..f7802d3 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/MetricHandlerLocal.java
@@ -28,6 +28,10 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.PUT;
import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Request;
+import javax.ws.rs.core.Response;
import org.rhq.enterprise.server.rest.domain.MetricAggregate;
import org.rhq.enterprise.server.rest.domain.MetricSchedule;
@@ -66,13 +70,8 @@ public interface MetricHandlerLocal {
@GET
@Path("/schedule/{id}")
- @Produces({"application/json","application/xml"})
- MetricSchedule getSchedule(@PathParam("id") int scheduleId);
-
- @GET
- @Path("/schedule/{id}")
- @Produces("text/html")
- String getScheduleHtml(@PathParam("id") int scheduleId);
+
@Produces({"application/json","application/xml","text/html"})
+ Response getSchedule(@PathParam("id") int scheduleId, @Context Request
request, @Context HttpHeaders headers);
@PUT
@Path("/schedule/{id}")
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
index efa6361..377ca87 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/domain/MetricSchedule.java
@@ -20,7 +20,7 @@ package org.rhq.enterprise.server.rest.domain;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlRootElement;
-import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.XmlTransient;
import org.jboss.resteasy.annotations.providers.jaxb.json.Mapped;
import org.jboss.resteasy.annotations.providers.jaxb.json.XmlNsMap;
@@ -43,6 +43,7 @@ public class MetricSchedule {
String displayName;
String unit;
String type;
+ transient long mtime;
@SuppressWarnings("unused")
public MetricSchedule() {
@@ -118,4 +119,41 @@ public class MetricSchedule {
// public String getEnabled() {
// return String.valueOf(enabled);
// }
+
+ @XmlTransient
+ public long getMtime() {
+ return mtime;
+ }
+
+ public void setMtime(long mtime) {
+ this.mtime = mtime;
+ }
+
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MetricSchedule that = (MetricSchedule) o;
+
+ if (collectionInterval != that.collectionInterval) return false;
+ if (mtime != that.mtime) return false;
+ if (scheduleId != that.scheduleId) return false;
+ if (enabled != null ? !enabled.equals(that.enabled) : that.enabled != null)
return false;
+ if (scheduleName != null ? !scheduleName.equals(that.scheduleName) :
that.scheduleName != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = scheduleId;
+ result = 31 * result + (scheduleName != null ? scheduleName.hashCode() : 0);
+ result = 31 * result + (enabled != null ? enabled.hashCode() : 0);
+ result = 31 * result + (int) (collectionInterval ^ (collectionInterval
>>> 32));
+ result = 31 * result + (int) (mtime ^ (mtime >>> 32));
+ return result;
+ }
}
commit abd6af308975aab38766afa51c733923d3df23cf
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Wed Oct 26 14:59:08 2011 +0200
Update to RESTeasy 2.2 and move artifacs into rhq.ear, so future updates do noot need
a container build.
diff --git a/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
b/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
index b07ec2e..6f23669 100644
--- a/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
+++ b/modules/enterprise/gui/rest-war/src/main/webapp/js/whisker.js
@@ -9,17 +9,16 @@ function whisker(scheduleId,divId) {
.attr("height", h);
// .append("svg:g");
// .attr("transform", "translate(" + p[3] + "," + (h -
p[2]) + ")");
-console.log(scheduleId);
d3.json(
'/rest/1/metric/data/'+scheduleId+'.json?hideEmpty=true',
function (jsondata) {
- var points = jsondata.metricAggregate.dataPoints;
- var minVal = jsondata.metricAggregate.min;
- var maxVal = jsondata.metricAggregate.max;
- var avgVal = jsondata.metricAggregate.avg;
- var minTs = jsondata.metricAggregate.minTimeStamp;
- var maxTs = jsondata.metricAggregate.maxTimeStamp;
+ var points = jsondata.dataPoints;
+ var minVal = jsondata.min;
+ var maxVal = jsondata.max;
+ var avgVal = jsondata.avg;
+ var minTs = jsondata.minTimeStamp;
+ var maxTs = jsondata.maxTimeStamp;
var minTsD = new Date(minTs);
var maxTsD = new Date(maxTs);
@@ -30,7 +29,6 @@ console.log(scheduleId);
// Y axis goes from lowest to highest value
var y = d3.scale.linear().domain([minVal, maxVal]).rangeRound([0,h]);
-// console.log(jsondata.metricAggregate);
// Line for the average
var avgLine = svg.append("svg:line")
@@ -50,7 +48,9 @@ console.log(scheduleId);
return x(new Date(d.timeStamp))
};
- var line = bars.enter().append("svg:line")
+ var group = bars.enter().append("svg:g");
+
+ var line = group.append("svg:line")
.attr("x1", currX)
.attr("x2", currX)
.attr("y1", function(d) {
@@ -61,7 +61,7 @@ console.log(scheduleId);
})
.attr("stroke", "lightblue");
- var circleLow = bars.enter().append("svg:circle")
+ var circleLow = group.append("svg:circle")
.attr("cx", currX)
.attr("cy", function(d) {
return h - y(d.low)
@@ -70,7 +70,7 @@ console.log(scheduleId);
.attr("stroke", "green")
.attr("fill", "lightgreen");
- var circleHigh = bars.enter().append("svg:circle")
+ var circleHigh = group.append("svg:circle")
.attr("cx", currX)
.attr("cy", function(d) {
return h - y(d.high)
@@ -79,7 +79,7 @@ console.log(scheduleId);
.attr("stroke", "green")
.attr("fill", "lightblue");
- var circleVal = bars.enter().append("svg:circle")
+ var circleVal = group.append("svg:circle")
.attr("cx", currX)
.attr("cy", function(d) {
return h - y(d.value)
diff --git a/modules/enterprise/server/container/pom.xml
b/modules/enterprise/server/container/pom.xml
index 1e55e08..e6fa762 100644
--- a/modules/enterprise/server/container/pom.xml
+++ b/modules/enterprise/server/container/pom.xml
@@ -194,7 +194,6 @@
<property name="jsf-impl.version"
value="${jsf-impl.version}" />
<property name="hibernate3.version"
value="${hibernate3.version}" />
<property name="quartz.version"
value="${quartz.version}" />
- <property name="resteasy.version"
value="${resteasy.version}" />
<!-- dev DB settings (used to init rhq-server.properties if dev
profile is active or developer prop is set) -->
<property name="rhq.dev.ds.connection-url"
value="${rhq.dev.ds.connection-url}" />
diff --git a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
index 1f8b8b2..e2f3663 100644
--- a/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
+++ b/modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
@@ -408,27 +408,6 @@
</fileset>
</move>
- <echo>Adding REST support ...</echo>
- <copy
file="${settings.localRepository}/org/jboss/resteasy/resteasy-jaxrs/${resteasy.version}/resteasy-jaxrs-${resteasy.version}.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/jboss/resteasy/resteasy-jettison-provider/${resteasy.version}/resteasy-jettison-provider-${resteasy.version}.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/jboss/resteasy/resteasy-jaxb-provider/${resteasy.version}/resteasy-jaxb-provider-${resteasy.version}.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/jboss/resteasy/resteasy-links/${resteasy.version}/resteasy-links-${resteasy.version}.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/jboss/resteasy/jaxrs-api/${resteasy.version}/jaxrs-api-${resteasy.version}.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
-
- <!-- TODO define versions of the following in the root pom -->
- <copy
file="${settings.localRepository}/org/scannotation/scannotation/1.0.3/scannotation-1.0.3.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/codehaus/jettison/jettison/1.2/jettison-1.2.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
- <copy
file="${settings.localRepository}/org/freemarker/freemarker/2.3.18/freemarker-2.3.18.jar"
- todir="${jboss.home}/server/default/lib" verbose="true"
failonerror="true"/>
</target>
diff --git a/modules/enterprise/server/ear/pom.xml
b/modules/enterprise/server/ear/pom.xml
index 32ce3d3..a913b44 100644
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@ -95,7 +95,7 @@
<groupId>org.rhq</groupId>
<artifactId>rhq-core-domain</artifactId>
</exclusion>
- </exclusions>
+ </exclusions>
</dependency>
<dependency>
@@ -128,6 +128,43 @@
</dependency>
-->
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>jar</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpcore</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jettison-provider</artifactId>
+ <version>${resteasy.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-links</artifactId>
+ <version>${resteasy.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>${freemarker.version}</version>
+ <type>jar</type>
+ </dependency>
+
</dependencies>
<build>
@@ -351,13 +388,13 @@
<artifactId>packagetype-cli</artifactId>
<version>${project.version}</version>
</artifactItem>
-
+
<artifactItem>
<groupId>org.rhq</groupId>
<artifactId>rhq-serverplugin-drift</artifactId>
<version>${project.version}</version>
</artifactItem>
-
+
</artifactItems>
<outputDirectory>${earDirectory}/rhq-serverplugins</outputDirectory>
</configuration>
diff --git a/pom.xml b/pom.xml
index 040e7e6..9eae894 100644
--- a/pom.xml
+++ b/pom.xml
@@ -107,7 +107,8 @@
<augeas.zip.location>${settings.localRepository}/net/augeas/augeas-native/${augeas.version}</augeas.zip.location>
<augeas.zip.mask>*-${augeas.classifier}.zip</augeas.zip.mask>
<ant.contrib.version>1.0b3</ant.contrib.version>
- <resteasy.version>2.2.1.GA</resteasy.version>
+ <resteasy.version>2.2.2.GA</resteasy.version>
+ <freemarker.version>2.3.18</freemarker.version>
<rhq.db.admin.username>rhqadmin</rhq.db.admin.username>
<rhq.db.admin.password>rhqadmin</rhq.db.admin.password>