[rhq] Branch 'stefan/backingcontent' - modules/plugins
by snegrea
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java | 37 ---
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java | 103 ++++++----
2 files changed, 77 insertions(+), 63 deletions(-)
New commits:
commit 59550355b8059df1ad63587309426032c2dd108a
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Tue Dec 13 11:15:45 2011 -0600
[BZ 761593] Move all the code for computation and retrieval of SHA256 to file content delegate. This way the manifest and digest code is in one location only.
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
index 27591ba..9a60a57 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
@@ -34,7 +34,6 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
-import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -71,7 +70,6 @@ import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.core.pluginapi.util.FileUtils;
import org.rhq.core.pluginapi.util.ResponseTimeConfiguration;
import org.rhq.core.pluginapi.util.ResponseTimeLogParser;
-import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.core.util.ZipUtil;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.plugins.jmx.MBeanResourceComponent;
@@ -633,43 +631,18 @@ public class TomcatWarComponent extends MBeanResourceComponent<TomcatVHostCompon
}
/**
- * Retrieve SHA 256 for a deployed app.
- * 1) If the app is exploded then look into the manifest file for the RHQ-Sha265 attribute.
- * The SHA should have been computed and the attribute added during package deployment.
- * 2) If the app is not exploded then compute the SHA256 for the archive.
+ * Retrieve SHA256 for a deployed app.
*
* @param file application file
- * @return the SHA 256
+ * @return SHA256 of the content
*/
private String getSHA256(File file) {
-
String sha256 = null;
try {
- //if the filesize of discovered package is null then it's an exploded and deployed war/ear.
- File app = new File(file.getPath());
- if (app.isDirectory()) {
- File manifestFile = new File(app.getAbsolutePath(), "META-INF/MANIFEST.MF");
- if (manifestFile.exists()) {
- InputStream manifestStream = new FileInputStream(manifestFile);
- Manifest manifest = null;
- try {
- manifest = new Manifest(manifestStream);
- sha256 = manifest.getMainAttributes().getValue("RHQ-Sha256");
- } finally {
- manifestStream.close();
- }
- }
-
- if (sha256 == null || sha256.trim().isEmpty()) {
- FileContentDelegate fileContentDelegate = new FileContentDelegate(app);
- sha256 = fileContentDelegate.calculateSHAForCotent(app);
- }
- } else {
- sha256 = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(app);
- }
- } catch (IOException iex) {
- //log exception but move on, discovery happens often. No reason to hold up anything.
+ FileContentDelegate fileContentDelegate = new FileContentDelegate(file);
+ sha256 = fileContentDelegate.getSHA(file);
+ } catch (Exception iex) {
if (log.isDebugEnabled()) {
log.debug("Problem calculating digest of package [" + file.getPath() + "]." + iex.getMessage());
}
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
index 5699ab4..6f914db 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
@@ -51,6 +51,9 @@ import org.rhq.core.util.file.FileUtil;
*/
public class FileContentDelegate {
+ private static final String RHQ_SHA_256 = "RHQ-Sha256";
+ private static final String MANIFEST_RELATIVE_PATH = "META-INF/MANIFEST.MF";
+
private File deployDirectory;
public FileContentDelegate(File directory) {
@@ -92,44 +95,38 @@ public class FileContentDelegate {
}
/**
- * Calculate SHA256 for the content of an exploded war deployment. This method should be used to
- * compute the SHA256 for content deployed outside RHQ or for the initial content delivered
- * with the server.
+ * Retrieves the SHA256 for a deployed application.
+ * 1) If the app is exploded then return RHQ-Sha256 manifest attribute.
+ * 1.1) If RHQ-Sha256 is missing then compute it, save it and return the result.
+ * 2) If the app is an archive then compute SHA256 on fly and return it.
*
- * @param deploymentDirectory app deployment folder
+ * @param deploymentFile deployment file
* @return
*/
- public String calculateSHAForCotent(File deploymentDirectory) {
+ public String getSHA(File deploymentFile) {
String sha = null;
try {
- if (deploymentDirectory.isDirectory()) {
- MessageDigestGenerator messageDigest = new MessageDigestGenerator(MessageDigestGenerator.SHA_256);
-
- Stack<File> unvisitedFolders = new Stack<File>();
- unvisitedFolders.add(deploymentDirectory);
- while (!unvisitedFolders.empty()) {
- for (File file : unvisitedFolders.pop().listFiles()) {
- if (file.isDirectory()) {
- unvisitedFolders.add(file);
- } else {
- FileInputStream inputStream = null;
- try {
- inputStream = new FileInputStream(file);
- messageDigest.add(inputStream);
- } finally {
- if (inputStream != null) {
- inputStream.close();
- }
- }
- }
+ if (deploymentFile.isDirectory()) {
+ File manifestFile = new File(deploymentFile.getAbsolutePath(), MANIFEST_RELATIVE_PATH);
+ if (manifestFile.exists()) {
+ InputStream manifestStream = new FileInputStream(manifestFile);
+ Manifest manifest = null;
+ try {
+ manifest = new Manifest(manifestStream);
+ sha = manifest.getMainAttributes().getValue(RHQ_SHA_256);
+ } finally {
+ manifestStream.close();
}
}
- sha = messageDigest.getDigestString();
- writeSHAToManifest(deploymentDirectory, sha);
+ if (sha == null || sha.trim().isEmpty()) {
+ sha = computeAndSaveSHA(deploymentFile);
+ }
+ } else {
+ sha = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(deploymentFile);
}
- } catch (IOException e) {
- throw new RuntimeException("Error creating artifact for contentFile: " + deploymentDirectory, e);
+ } catch (IOException ex) {
+ throw new RuntimeException("Problem calculating digest of package [" + deploymentFile.getPath() + "].", ex);
}
return sha;
@@ -200,6 +197,50 @@ public class FileContentDelegate {
}
/**
+ * Compute SHA256 for the content of an exploded war deployment. This method should be used to
+ * compute the SHA256 for content deployed outside RHQ or for the initial content delivered
+ * with the server.
+ *
+ * @param deploymentDirectory app deployment folder
+ * @return
+ */
+ private String computeAndSaveSHA(File deploymentDirectory) {
+ String sha = null;
+ try {
+ if (deploymentDirectory.isDirectory()) {
+ MessageDigestGenerator messageDigest = new MessageDigestGenerator(MessageDigestGenerator.SHA_256);
+
+ Stack<File> unvisitedFolders = new Stack<File>();
+ unvisitedFolders.add(deploymentDirectory);
+ while (!unvisitedFolders.empty()) {
+ for (File file : unvisitedFolders.pop().listFiles()) {
+ if (file.isDirectory()) {
+ unvisitedFolders.add(file);
+ } else {
+ FileInputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(file);
+ messageDigest.add(inputStream);
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+ }
+ }
+
+ sha = messageDigest.getDigestString();
+ writeSHAToManifest(deploymentDirectory, sha);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error creating artifact for contentFile: " + deploymentDirectory, e);
+ }
+
+ return sha;
+ }
+
+ /**
* Write the SHA256 to the manifest using the RHQ-Sha256 attribute tag.
*
* @param deploymentFolder app deployment folder
@@ -207,7 +248,7 @@ public class FileContentDelegate {
* @throws IOException
*/
private void writeSHAToManifest(File deploymentFolder, String sha) throws IOException {
- File manifestFile = new File(deploymentFolder, "META-INF/MANIFEST.MF");
+ File manifestFile = new File(deploymentFolder, MANIFEST_RELATIVE_PATH);
Manifest manifest;
if (manifestFile.exists()) {
FileInputStream inputStream = new FileInputStream(manifestFile);
@@ -231,7 +272,7 @@ public class FileContentDelegate {
attribs.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
}
- attribs.putValue("RHQ-Sha256", sha);
+ attribs.putValue(RHQ_SHA_256, sha);
FileOutputStream outputStream = new FileOutputStream(manifestFile);
try {
12 years, 5 months
[rhq] Branch 'stefan/backingcontent' - modules/plugins
by snegrea
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatVHostComponent.java | 2
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java | 23 +
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java | 129 +++++++---
3 files changed, 110 insertions(+), 44 deletions(-)
New commits:
commit ab1896c5792445fae10ea0b503f3dfe5d61180a0
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Tue Dec 13 09:40:15 2011 -0600
[BZ 761593] Compute SHA256 for content that was deployed outside RHQ or for default content delivered with the server.
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatVHostComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatVHostComponent.java
index f605e2d..0024cf8 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatVHostComponent.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatVHostComponent.java
@@ -302,7 +302,7 @@ public class TomcatVHostComponent extends MBeanResourceComponent<TomcatServerCom
return;
}
- FileContentDelegate fileContent = new FileContentDelegate(deployDir, details.getPackageTypeName());
+ FileContentDelegate fileContent = new FileContentDelegate(deployDir);
fileContent.createContent(path, tempFile, explodeOnDeploy);
// Resource key is a canonical objectName similar to:
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
index 504403b..27591ba 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
@@ -508,7 +508,7 @@ public class TomcatWarComponent extends MBeanResourceComponent<TomcatVHostCompon
log.warn("Failed to create app backup but proceeding with redeploy of " + appFile.getPath() + ": " + e);
}
- FileContentDelegate contentDelegate = new FileContentDelegate(appFile.getParentFile(), packageDetails.getName());
+ FileContentDelegate contentDelegate = new FileContentDelegate(appFile.getParentFile());
try {
// Write the new bits for the application. If successful Tomcat will pick it up and complete the deploy.
@@ -650,13 +650,20 @@ public class TomcatWarComponent extends MBeanResourceComponent<TomcatVHostCompon
File app = new File(file.getPath());
if (app.isDirectory()) {
File manifestFile = new File(app.getAbsolutePath(), "META-INF/MANIFEST.MF");
- InputStream manifestStream = new FileInputStream(manifestFile);
- Manifest manifest = null;
- try {
- manifest = new Manifest(manifestStream);
- sha256 = manifest.getMainAttributes().getValue("RHQ-Sha256");
- } finally {
- manifestStream.close();
+ if (manifestFile.exists()) {
+ InputStream manifestStream = new FileInputStream(manifestFile);
+ Manifest manifest = null;
+ try {
+ manifest = new Manifest(manifestStream);
+ sha256 = manifest.getMainAttributes().getValue("RHQ-Sha256");
+ } finally {
+ manifestStream.close();
+ }
+ }
+
+ if (sha256 == null || sha256.trim().isEmpty()) {
+ FileContentDelegate fileContentDelegate = new FileContentDelegate(app);
+ sha256 = fileContentDelegate.calculateSHAForCotent(app);
}
} else {
sha256 = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(app);
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
index a93eddf..5699ab4 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/helper/FileContentDelegate.java
@@ -30,6 +30,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
+import java.util.Stack;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
@@ -49,24 +50,18 @@ import org.rhq.core.util.file.FileUtil;
* @author Jason Dobies
*/
public class FileContentDelegate {
- // Attributes --------------------------------------------
private File deployDirectory;
- private String packageTypeName;
- // Constructors --------------------------------------------
-
- public FileContentDelegate(File directory, String packageTypeName) {
+ public FileContentDelegate(File directory) {
this.deployDirectory = directory;
- this.packageTypeName = packageTypeName;
- }
-
- // Public --------------------------------------------
-
- public String getPackageTypeName() {
- return packageTypeName;
}
+ /**
+ * Getter for deployment directory.
+ *
+ * @return deploy directory
+ */
public File getDirectory() {
return deployDirectory;
}
@@ -85,30 +80,8 @@ public class FileContentDelegate {
try {
if (unzip) {
ZipUtil.unzipFile(content, destination);
-
- File manifestFile = new File(destination, "META-INF/MANIFEST.MF");
- Manifest manifest;
- if (manifestFile.exists()) {
- FileInputStream inputStream = new FileInputStream(manifestFile);
- try {
- manifest = new Manifest(inputStream);
- } finally {
- inputStream.close();
- }
- } else {
- manifest = new Manifest();
- }
-
- Attributes attribs = manifest.getMainAttributes();
String sha = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(content);
- attribs.putValue("RHQ-Sha256", sha);
-
- FileOutputStream outputStream = new FileOutputStream(manifestFile);
- try {
- manifest.write(outputStream);
- } finally {
- outputStream.close();
- }
+ writeSHAToManifest(destination, sha);
} else {
InputStream contentStream = new BufferedInputStream(new FileInputStream(content));
FileUtil.writeFile(contentStream, destination);
@@ -118,6 +91,50 @@ public class FileContentDelegate {
}
}
+ /**
+ * Calculate SHA256 for the content of an exploded war deployment. This method should be used to
+ * compute the SHA256 for content deployed outside RHQ or for the initial content delivered
+ * with the server.
+ *
+ * @param deploymentDirectory app deployment folder
+ * @return
+ */
+ public String calculateSHAForCotent(File deploymentDirectory) {
+ String sha = null;
+ try {
+ if (deploymentDirectory.isDirectory()) {
+ MessageDigestGenerator messageDigest = new MessageDigestGenerator(MessageDigestGenerator.SHA_256);
+
+ Stack<File> unvisitedFolders = new Stack<File>();
+ unvisitedFolders.add(deploymentDirectory);
+ while (!unvisitedFolders.empty()) {
+ for (File file : unvisitedFolders.pop().listFiles()) {
+ if (file.isDirectory()) {
+ unvisitedFolders.add(file);
+ } else {
+ FileInputStream inputStream = null;
+ try {
+ inputStream = new FileInputStream(file);
+ messageDigest.add(inputStream);
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ }
+ }
+ }
+ }
+
+ sha = messageDigest.getDigestString();
+ writeSHAToManifest(deploymentDirectory, sha);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Error creating artifact for contentFile: " + deploymentDirectory, e);
+ }
+
+ return sha;
+ }
+
public File getPath(PackageDetails details) {
/* JBNADM-2022 - It still needs to be determined if it is the responsibility of the plugin container or the
* plugin to be concerned with path information in the package name. For now, it's the plugin's
@@ -181,4 +198,46 @@ public class FileContentDelegate {
*/
return null;
}
+
+ /**
+ * Write the SHA256 to the manifest using the RHQ-Sha256 attribute tag.
+ *
+ * @param deploymentFolder app deployment folder
+ * @param sha SHA256
+ * @throws IOException
+ */
+ private void writeSHAToManifest(File deploymentFolder, String sha) throws IOException {
+ File manifestFile = new File(deploymentFolder, "META-INF/MANIFEST.MF");
+ Manifest manifest;
+ if (manifestFile.exists()) {
+ FileInputStream inputStream = new FileInputStream(manifestFile);
+ try {
+ manifest = new Manifest(inputStream);
+ } finally {
+ inputStream.close();
+ }
+ } else {
+ manifest = new Manifest();
+ manifestFile.getParentFile().mkdirs();
+ manifestFile.createNewFile();
+ }
+
+ Attributes attribs = manifest.getMainAttributes();
+
+ //The main section of the manifest file does not get saved if both of
+ //these two attributes are missing. Please see Attributes implementation.
+ if (!attribs.containsKey(Attributes.Name.MANIFEST_VERSION.toString())
+ && !attribs.containsKey(Attributes.Name.SIGNATURE_VERSION.toString())) {
+ attribs.putValue(Attributes.Name.MANIFEST_VERSION.toString(), "1.0");
+ }
+
+ attribs.putValue("RHQ-Sha256", sha);
+
+ FileOutputStream outputStream = new FileOutputStream(manifestFile);
+ try {
+ manifest.write(outputStream);
+ } finally {
+ outputStream.close();
+ }
+ }
}
\ No newline at end of file
12 years, 5 months
[rhq] Branch 'stefan/backingcontent' - modules/enterprise modules/plugins
by snegrea
modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java | 31 +++-------
modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java | 22 -------
2 files changed, 15 insertions(+), 38 deletions(-)
New commits:
commit 33dada117a564f49e28c061c419a6af3df49fca0
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Dec 12 13:51:12 2011 -0600
[BZ 758753] Use SHA as the version for the package; the format is consistent with the existing versioning scheme.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
index 25068bd..eade28b 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/client/ResourceClientProxy.java
@@ -66,6 +66,7 @@ import org.rhq.core.domain.util.PageList;
import org.rhq.core.domain.util.PageOrdering;
import org.rhq.core.domain.util.Summary;
import org.rhq.core.server.MeasurementConverter;
+import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.enterprise.server.content.ContentManagerRemote;
import org.rhq.enterprise.server.resource.ResourceTypeNotFoundException;
@@ -513,32 +514,26 @@ public class ResourceClientProxy {
throw new IllegalArgumentException("File expected, found directory: " + file.getAbsolutePath());
}
-
- InstalledPackage oldPackage = getBackingContent();
-
-
- String oldVersion = oldPackage.getPackageVersion().getVersion();
- String newVersion = "1.0";
- if (oldVersion != null && oldVersion.length() != 0) {
- String[] parts = oldVersion.split("[^a-zA-Z0-9]");
- String lastPart = parts[parts.length-1];
- try {
- int lastNumber = Integer.parseInt(lastPart);
- newVersion = oldVersion.substring(0, oldVersion.length() - lastPart.length()) + (lastNumber + 1);
- } catch (NumberFormatException nfe) {
- newVersion = oldVersion + ".1";
- }
+ byte[] fileContents = new ScriptUtil(remoteClient).getFileBytes(filename);
+ String sha = null;
+ try {
+ sha = new MessageDigestGenerator(MessageDigestGenerator.SHA_256).calcDigestString(fileContents);
+ }
+ catch(Exception e){
+ //do nothing because the sha will remain null.
+ LOG.error("Message digest for the package bits failed.", e);
}
- byte[] fileContents = new ScriptUtil(remoteClient).getFileBytes(filename);
+ String version = "[sha256="+sha+"]";
+ InstalledPackage oldPackage = getBackingContent();
PackageVersion pv =
remoteClient.getContentManager().createPackageVersion(
remoteClient.getSubject(),
oldPackage.getPackageVersion().getGeneralPackage().getName(),
oldPackage.getPackageVersion().getGeneralPackage().getPackageType().getId(),
- newVersion,
+ version,
oldPackage.getPackageVersion().getArchitecture().getId(),
fileContents);
@@ -547,8 +542,6 @@ public class ResourceClientProxy {
new int[] { resourceClientProxy.getId()},
new int[] {pv.getId()},
"CLI deployment request");
-
-
}
public void retrieveBackingContent(String fileName) throws IOException {
diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
index 933792f..504403b 100644
--- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
+++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java
@@ -74,7 +74,6 @@ import org.rhq.core.pluginapi.util.ResponseTimeLogParser;
import org.rhq.core.util.MessageDigestGenerator;
import org.rhq.core.util.ZipUtil;
import org.rhq.core.util.exception.ThrowableUtil;
-import org.rhq.core.util.file.JarContentFileInfo;
import org.rhq.plugins.jmx.MBeanResourceComponent;
import org.rhq.plugins.jmx.ObjectNameQueryUtility;
@@ -615,8 +614,7 @@ public class TomcatWarComponent extends MBeanResourceComponent<TomcatVHostCompon
// Package name and file name of the application are the same
String fileName = new File(fullFileName).getName();
String sha256 = getSHA256(file);
- JarContentFileInfo info = new JarContentFileInfo(file);
- String version = getVersion(info, sha256);
+ String version = getVersion(sha256);
PackageDetailsKey key = new PackageDetailsKey(fileName, version, PKG_TYPE_FILE, ARCHITECTURE);
ResourcePackageDetails details = new ResourcePackageDetails(key);
@@ -673,22 +671,8 @@ public class TomcatWarComponent extends MBeanResourceComponent<TomcatVHostCompon
return sha256;
}
- private String getVersion(JarContentFileInfo fileInfo, String sha256) {
- // Version string in order of preference
- // manifestVersion + sha256, sha256, manifestVersion, "0"
- String version = "0";
- String manifestVersion = fileInfo.getVersion(null);
-
- if ((null != manifestVersion) && (null != sha256)) {
- // this protects against the occasional differing binaries with poor manifest maintenance
- version = manifestVersion + " [sha256=" + sha256 + "]";
- } else if (null != sha256) {
- version = "[sha256=" + sha256 + "]";
- } else if (null != manifestVersion) {
- version = manifestVersion;
- }
-
- return version;
+ private String getVersion(String sha256) {
+ return "[sha256=" + sha256 + "]";
}
public List<DeployPackageStep> generateInstallationSteps(ResourcePackageDetails packageDetails) {
12 years, 5 months
[rhq] modules/core
by John Sanda
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java | 37 ++++++---
modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java | 11 ++
modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftFilesSenderTest.java | 40 ++++++++--
3 files changed, 68 insertions(+), 20 deletions(-)
New commits:
commit 35fcafbd977ca41cb2cbff7be81787543c5224e8
Author: John Sanda <jsanda(a)redhat.com>
Date: Mon Dec 12 13:39:37 2011 -0500
[BZ 760263] Adding check to avoid generating empty content zip file
When the server requests drift content from the agent, it is possible
that the requested files may not exist. DriftFilesSender now checks to
make sure that there is actually content before making the request to
zip it up and send it to the server.
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java
index d8d2b46..4181472 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java
@@ -1,22 +1,16 @@
package org.rhq.core.pc.drift;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.List;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.rhq.common.drift.ChangeSetReader;
import org.rhq.common.drift.FileEntry;
import org.rhq.common.drift.Headers;
import org.rhq.core.domain.drift.DriftFile;
import org.rhq.core.util.stream.StreamUtil;
+import java.io.*;
+import java.util.List;
+
public class DriftFilesSender implements Runnable {
private Log log = LogFactory.getLog(DriftFilesSender.class);
@@ -54,6 +48,10 @@ public class DriftFilesSender implements Runnable {
@Override
public void run() {
try {
+ if (log.isInfoEnabled()) {
+ log.info("Preparing to send content to server for " + defToString());
+ }
+
File changeSet = changeSetMgr.findChangeSet(resourceId, headers.getDriftDefinitionName());
File contentDir = new File(changeSet.getParentFile(), "content");
contentDir.mkdir();
@@ -63,11 +61,25 @@ public class DriftFilesSender implements Runnable {
if (file == null || !file.exists()) {
log.warn("Unable to find file for " + driftFile);
} else {
+ if (log.isDebugEnabled()) {
+ log.debug("Copying " + file.getPath() + " to " + contentDir.getPath());
+ }
StreamUtil.copy(new BufferedInputStream(new FileInputStream(file)), new BufferedOutputStream(
new FileOutputStream(new File(contentDir, driftFile.getHashId()))), true);
}
}
- driftClient.sendChangeSetContentToServer(resourceId, headers.getDriftDefinitionName(), contentDir);
+
+ File[] content = contentDir.listFiles();
+ if (content == null || content.length == 0) {
+ if (log.isDebugEnabled()) {
+ log.debug("Could not find any of the requested content for " + defToString());
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Sending " + content.length + " files to the server for " + defToString());
+ }
+ driftClient.sendChangeSetContentToServer(resourceId, headers.getDriftDefinitionName(), contentDir);
+ }
} catch (IOException e) {
log.error("Failed to send drift files.", e);
}
@@ -87,4 +99,9 @@ public class DriftFilesSender implements Runnable {
reader.close();
}
}
+
+ private String defToString() {
+ return "[resourceId: " + resourceId + ", driftDefinitionId: " + headers.getDriftDefinitionId() +
+ ", driftDefinitionName: " + headers.getDriftDefinitionName() + "]";
+ }
}
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java
index 209ba61..0f0c26a 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java
@@ -1,9 +1,9 @@
package org.rhq.core.pc.drift;
-import java.io.File;
-
import org.rhq.core.domain.drift.DriftDefinition;
+import java.io.File;
+
class DriftClientTestStub implements DriftClient {
private File basedir;
@@ -14,6 +14,8 @@ class DriftClientTestStub implements DriftClient {
private int reportMissingBaseDirInvocationCount;
+ private int sendChangeSetContentInvocationCount;
+
@Override
public void sendChangeSetToServer(DriftDetectionSummary detectionSummary) {
++sendChangeSetInvocationCount;
@@ -28,6 +30,11 @@ class DriftClientTestStub implements DriftClient {
@Override
public void sendChangeSetContentToServer(int resourceId, String driftDefinitionName, File contentDir) {
+ ++sendChangeSetContentInvocationCount;
+ }
+
+ public int getSendChangeSetContentInvocationCount() {
+ return sendChangeSetContentInvocationCount;
}
@Override
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftFilesSenderTest.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftFilesSenderTest.java
index 5fd6528..77db6b2 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftFilesSenderTest.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftFilesSenderTest.java
@@ -19,18 +19,17 @@
package org.rhq.core.pc.drift;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
import org.rhq.common.drift.ChangeSetWriter;
import org.rhq.common.drift.Headers;
import org.rhq.core.domain.drift.DriftChangeSetCategory;
import org.rhq.core.domain.drift.DriftFile;
import org.rhq.core.domain.drift.JPADriftFile;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import static org.rhq.common.drift.FileEntry.addedFileEntry;
import static org.rhq.core.domain.drift.DriftChangeSetCategory.COVERAGE;
@@ -39,7 +38,7 @@ import static org.testng.Assert.assertTrue;
public class DriftFilesSenderTest extends DriftTest {
- DriftClient driftClient;
+ DriftClientTestStub driftClient;
DriftFilesSender sender;
@@ -161,6 +160,31 @@ public class DriftFilesSenderTest extends DriftTest {
assertFileCopiedToContentDir(contentDir, server2ConfHash);
}
+ @Test
+ public void doNotSendEmptyZipFileToServer() throws Exception {
+ String driftDefName = "empty_content_test";
+
+ File confDir = mkdir(resourceDir, "conf");
+ File serverConf = createRandomFile(confDir, "server.conf");
+
+ String serverConfHash = sha256(serverConf);
+
+ Headers headers = createHeaders(driftDefName, COVERAGE);
+
+ ChangeSetWriter writer = changeSetMgr.getChangeSetWriter(resourceId(), headers);
+ writer.write(addedFileEntry("conf/server.conf", serverConfHash));
+ writer.close();
+
+ serverConf.delete();
+
+ sender.setDriftFiles(driftFiles(serverConfHash));
+ sender.setHeaders(headers);
+ sender.run();
+
+ assertEquals(driftClient.getSendChangeSetContentInvocationCount(), 0,
+ "Do not call DriftClient to send content to server when there is no content to send.");
+ }
+
/**
* This method only verifies that a file having a particular hash or SHA-256 has been
* copied to the specified content directory.
12 years, 5 months
[rhq] Branch 'stefan/backingcontent' - modules/core modules/enterprise
by snegrea
modules/core/domain/src/main/java/org/rhq/core/domain/content/PackageVersion.java | 8 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerBean.java | 41 ----------
2 files changed, 8 insertions(+), 41 deletions(-)
New commits:
commit 9db052a47faf5837892d3fed1e8cfa74ed28791b
Author: Stefan Negrea <snegrea(a)redhat.com>
Date: Mon Dec 12 10:03:40 2011 -0600
[BZ 758753] Use SHA as the identifier for package discovery. The version is unreliable and not correctly updated between server and agent.
Also, populate the installed package during discovery; the complete method is not the correct place because it does not necessarly mean that the package was fully deployed even if copied.
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/content/PackageVersion.java b/modules/core/domain/src/main/java/org/rhq/core/domain/content/PackageVersion.java
index d64d8a5..7096645 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/content/PackageVersion.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/content/PackageVersion.java
@@ -57,7 +57,7 @@ import org.rhq.core.domain.util.OSGiVersionComparator;
* @author Jason Dobies
*/
@Entity
-@NamedQueries( {
+@NamedQueries({
@NamedQuery(name = PackageVersion.QUERY_FIND_BY_PACKAGE_DETAILS, query = "" //
+ "SELECT pv FROM PackageVersion AS pv " //
+ " WHERE pv.generalPackage.name = :packageName " //
@@ -66,6 +66,12 @@ import org.rhq.core.domain.util.OSGiVersionComparator;
+ " AND ( ( NOT pv.sha256 IS NULL AND pv.sha256 = :sha ) OR "
+ " ( pv.sha256 IS NULL AND pv.architecture.name = :architectureName AND pv.version = :version ) "
+ " ) "),
+ @NamedQuery(name = PackageVersion.QUERY_FIND_BY_PACKAGE_SHA, query = "" //
+ + "SELECT pv FROM PackageVersion AS pv " //
+ + " WHERE pv.generalPackage.name = :packageName " //
+ + " AND pv.generalPackage.packageType.name = :packageTypeName " //
+ + " AND pv.generalPackage.packageType.resourceType.id = :resourceTypeId " //
+ + " AND pv.sha256 = :sha "),
@NamedQuery(name = PackageVersion.QUERY_FIND_BY_PACKAGE_VER_ARCH, query = "SELECT pv FROM PackageVersion AS pv "
+ " WHERE pv.generalPackage.name = :name " + " AND pv.generalPackage.packageType.id = :packageTypeId "
+ " AND pv.architecture.id = :architectureId " + " AND pv.version = :version "),
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerBean.java
index 70f5c0c..29c3c85 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/content/ContentManagerBean.java
@@ -203,13 +203,11 @@ public class ContentManagerBean implements ContentManagerLocal, ContentManagerRe
// If the package exists see if package version already exists
if (null != generalPackage) {
Query packageVersionQuery = entityManager
- .createNamedQuery(PackageVersion.QUERY_FIND_BY_PACKAGE_DETAILS);
+ .createNamedQuery(PackageVersion.QUERY_FIND_BY_PACKAGE_SHA);
packageVersionQuery.setFlushMode(FlushModeType.COMMIT);
packageVersionQuery.setParameter("packageName", discoveredPackage.getName());
packageVersionQuery.setParameter("packageTypeName", discoveredPackage.getPackageTypeName());
packageVersionQuery.setParameter("resourceTypeId", resource.getResourceType().getId());
- packageVersionQuery.setParameter("architectureName", discoveredPackage.getArchitectureName());
- packageVersionQuery.setParameter("version", discoveredPackage.getVersion());
packageVersionQuery.setParameter("sha", discoveredPackage.getSHA256());
List<PackageVersion> resultPackageVersions = packageVersionQuery.getResultList();
if (resultPackageVersions.size() > 0) {
@@ -546,10 +544,6 @@ public class ContentManagerBean implements ContentManagerLocal, ContentManagerRe
// We're closing out the package request for this package version, so remove it from the cache of entries
// that need to be closed
inProgressEntries.remove(packageVersion);
-
- if (singleResponse.getResult() == ContentResponseResult.SUCCESS) {
- updateInstalledPackages(resource, packageVersion);
- }
}
// For any entries that were not closed, add closing entries
@@ -2077,37 +2071,4 @@ public class ContentManagerBean implements ContentManagerLocal, ContentManagerRe
ex.printStackTrace();
}
}
-
- /**
- * Update the set of installed packages for a content type resource.
- * Existing installed packages are purged and a new set of installed
- * packages is created. Currently, a content type resource has only
- * and only one installed package.
- *
- * @param resource resource
- * @param packageVersion package version
- */
- @SuppressWarnings("unchecked")
- private void updateInstalledPackages(Resource resource, PackageVersion packageVersion) {
- if (resource.getResourceType().getCreationDataType() == ResourceCreationDataType.CONTENT) {
- Query installedPackageQuery = entityManager.createNamedQuery(InstalledPackage.QUERY_FIND_BY_RESOURCE_ID);
- installedPackageQuery.setParameter("resourceId", resource.getId());
-
- List<InstalledPackage> installedPackages = installedPackageQuery.getResultList();
- if ((installedPackages != null) && (installedPackages.size() > 0)) {
- for (InstalledPackage installedPackage : installedPackages) {
- entityManager.refresh(installedPackage);
- entityManager.remove(installedPackage);
- }
- }
-
- InstalledPackage installedPackage = new InstalledPackage();
- installedPackage.setInstallationDate(System.currentTimeMillis());
- installedPackage.setPackageVersion(packageVersion);
- installedPackage.setResource(resource);
- entityManager.persist(installedPackage);
-
- entityManager.flush();
- }
- }
}
\ No newline at end of file
12 years, 5 months
[rhq] Branch 'jsanda/drift' - 91 commits - .classpath modules/common modules/core modules/enterprise modules/integration-tests modules/jopr modules/plugins modules/test-utils pom.xml
by John Sanda
.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_EmbJopr_1_3_0-1/m...</connection>
- <developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/tags/Jopr_2_3_0_EmbJopr_1_3_0-1/...</developerConnection>
- </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/</connection>
- <developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/trunk/modules/</developerConnection>
- </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>
12 years, 5 months
[rhq] modules/enterprise
by Heiko W. Rupp
modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties | 508 ++++------
1 file changed, 253 insertions(+), 255 deletions(-)
New commits:
commit 48a945ccd90e18205f216f42729c3043dc12189d
Author: Fusayuki Minamoto <fminamot(a)redhat.com>
Date: Mon Dec 12 14:29:22 2011 +0100
Updated Japanese translations.
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 71e3530..67d4a7f 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
@@ -24,23 +24,22 @@ common_button_create_child = 子を作成
common_button_delete = 削除
common_button_delete_all = すべてを削除
common_button_disable = 無効
-##common_button_dump_sysInfo_to_log = Dump system info to log file
common_button_edit = 編集
common_button_enable = 有効
-##common_button_execute = Execute
+common_button_execute = 実行
common_button_finish = 完了
-##common_button_fixedWidth = Fixed Width
+common_button_fixedWidth = 固定幅
common_button_import = インポート
common_button_new = 新規
common_button_next = 次へ
common_button_ok = OK
common_button_previous = 前へ
-##common_button_purge = Purge
+common_button_purge = パージ
common_button_purgeAll = すべてをパージ
common_button_refresh = リフレッシュ
common_button_reset = リセット
common_button_save = 保存
-##common_button_scaleToFit = Scale To Fit
+common_button_scaleToFit = 適合させるスケール
common_button_schedule = スケジュール
common_button_search = 検索
common_button_set = 設定
@@ -62,8 +61,8 @@ common_label_ago = 前
common_label_all = すべての
common_label_all_resources = すべてのリソース
common_label_day = 日
-##common_label_error = Error
-##common_label_filters = Filters
+common_label_error = エラー
+common_label_filters = フィルター
common_label_hour = 時間
common_label_item = 項目
common_label_items = 項目
@@ -79,20 +78,20 @@ common_label_users = ユーザー
common_label_week = 週
common_label_yesterday = 昨日
common_msg_areYouSure = よろしいですか?
-##common_msg_asyncTimeout = {0}. This occurred because the server is taking a long time to complete this request. Please be aware that the server may still be processing your request and it may complete shortly. You can check the server logs to see if any abnormal errors occurred.
+common_msg_asyncTimeout = {0}. これが発生したのはサーバーがこのリクエストを完了するのに長時間経過したからです。サーバーがそのリクエストをまだ処理中で短時間で完了可能かどうか注意してください。サーバーログを調べることで異常なエラーが発生したかどうかがわかります。
common_msg_changeAutoDetected = 変更が自動検出されました
-common_msg_deleteConfirm = を削除してもよろしいですか # selected {0}?
+common_msg_deleteConfirm = 選択された {0} を削除してもよろしいですか
common_msg_emphasizedNotePrefix = 注意:
common_msg_loading = ロード中...
common_msg_noItemsToShow = 表示する項目はありません
-##common_msg_notYetImplemented = Not Yet Implemented
+common_msg_notYetImplemented = 実装されていません
common_msg_see_more = 詳細...
common_msg_step_x_of_y = ステップ {1} 中 {0} 番め
-##common_severity_debug = Debug
+common_severity_debug = デバッグ
common_severity_error = エラー
-##common_severity_fatal = Fatal
+common_severity_fatal = 致命的
common_severity_info = 情報
-##common_severity_warn = Warn
+common_severity_warn = 警告
common_status_canceled = キャンセル
common_status_deferred = 延期
common_status_failed = 失敗
@@ -103,6 +102,7 @@ common_status_success = 成功
common_status_timedOut = タイムアウト
common_status_unknown = 未知
common_title_add_column = カラム追加
+common_title_add_graph_to_view = モニタービューへのグラフの追加
common_title_add_portlet = ポートレット追加
common_title_address = アドレス
common_title_alerts = アラート
@@ -115,7 +115,7 @@ common_title_background = バックグラウンド
common_title_bundle = バンドル
common_title_bundles = バンドル
common_title_category = カテゴリ
-common_title_change_refresh_time = インターバルのリフレッシュ
+common_title_change_refresh_time = リフレッシュ間隔
common_title_columns = カラム
common_title_compare_metrics = メトリックスの比較
common_title_compatibleGroups = 互換グループ
@@ -124,19 +124,20 @@ common_title_component_errors = コンポーネントエラー
common_title_config_update_status = 更新状態
common_title_configuration = 構成
common_title_count = 数
+common_title_createTime = 作成時刻
common_title_custom = カスタム
common_title_dashboard_name = ダッシュボード名
common_title_dateCreated = 作成日
common_title_dateRange = 日付の範囲
common_title_default = デフォルト
-##common_title_definition = Definition
+common_title_definition = 定義
common_title_definitions = 定義
common_title_description = 説明
common_title_details = 詳細
common_title_display = 表示
common_title_display_name = 表示名
common_title_duration = 期間
-##common_title_edit = Edit?
+common_title_edit = 編集?
common_title_edit_mode = 編集モード
common_title_enabled = 有効?
common_title_end = 終了
@@ -149,9 +150,10 @@ common_title_help = ヘルプ
common_title_host = ホスト
common_title_id = ID
common_title_id_parent = 親のID
+common_title_interval = 間隔
common_title_inventory = インベントリ
common_title_inventorySummary = インベントリサマリ
-##common_title_kind = Kind
+common_title_kind = 種類
common_title_lastUpdated = 最終更新
common_title_lastUpdatedBy = 最終更新(による)
common_title_ldapGroups = LDAPグループ
@@ -170,7 +172,7 @@ common_title_operation = オペレーション
common_title_operation_status = オペレーションの状態
common_title_operations = オペレーション
common_title_operations_range = オペレーションの範囲
-common_title_over = Over
+##common_title_over = Over
common_title_password = パスワード
common_title_path = パス
common_title_permissions = パーミッション
@@ -182,7 +184,7 @@ common_title_providers = プロバイダ
common_title_recent_alerts = 最近のアラート
common_title_recent_bundle_deployments = 最近のバンドルのデプロイ
common_title_recent_configuration_updates = 最近の構成の更新
-##common_title_recent_drifts = Recent Drift
+common_title_recent_drifts = 最近のドリフト
common_title_recent_event_counts = 最近のイベント数
common_title_recent_measurements = 最近の測定
common_title_recent_oob_metrics = 最近の境界外のメトリックス
@@ -390,12 +392,12 @@ view_aboutBox_homepage = ホームページ
view_aboutBox_jbossByRedHat = JBoss by Red Hat
view_aboutBox_title = {0}について
view_aboutBox_version = バージョン:
-##view_adminConfig_agentPlugins = Agent Plugins
-##view_adminConfig_alertDefTemplates = Alert Definition Templates
+view_adminConfig_agentPlugins = エージェントプラグイン
+view_adminConfig_alertDefTemplates = エージェント定義テンプレート
view_adminConfig_downloads = ダウンロード
-##view_adminConfig_driftDefTemplates = Drift Definition Templates
-##view_adminConfig_metricTemplates = Metric Collection Templates
-##view_adminConfig_serverPlugins = Server Plugins
+view_adminConfig_driftDefTemplates = ドリフト定義テンプレート
+view_adminConfig_metricTemplates = メトリック収集テンプレート
+view_adminConfig_serverPlugins = サーバープラグイン
view_adminConfig_systemSettings = システム設定
view_adminContent_contentSources = コンテントソース
view_adminRoles_assignedGroups = 割り当てられたリソースグループ
@@ -423,7 +425,7 @@ view_adminRoles_permissions_permDesc_manageBundles = (誰でも閲覧可能な)
view_adminRoles_permissions_permDesc_manageInventory = すべてのリソースについて、以下に示すように、すべてのリソース権限を持ちます。つまり、グループの作成、更新、削除、そして自動検出された、または手動で検出されたリソースのインポートができます
view_adminRoles_permissions_permDesc_manageRepositories = (自分自身のリポジトリを作成可能な)ユーザーのリポジトリの作成、更新、削除が可能です。
view_adminRoles_permissions_permDesc_manageSecurity = (誰でも閲覧可能な)ユーザーとロールの作成、更新、削除
-##view_adminRoles_permissions_permDesc_manageSettings = RHQサーバー構成の修正と任意のサーバー関連の機能の実行ができます
+## view_adminRoles_permissions_permDesc_manageSettings = RHQサーバー構成の修正と任意のサーバー関連の機能の実行ができます
view_adminRoles_permissions_permReadDesc_configure = リソース構成とリソース構成リビジョン履歴の閲覧
view_adminRoles_permissions_permReadDesc_control = 利用可能オペレーションとオペレーション実行履歴の(暗黙的な)閲覧
view_adminRoles_permissions_permReadDesc_createChildResources = (暗黙的な)子リソースの作成履歴の閲覧
@@ -431,7 +433,7 @@ view_adminRoles_permissions_permReadDesc_deleteChildResources = (暗黙的な)
view_adminRoles_permissions_permReadDesc_inventory = リソースプロパティ(名前、説明、バージョンなど)、コネクション設定、コネクション設定履歴の(暗黙的な)閲覧
view_adminRoles_permissions_permReadDesc_manageAlerts = アラート定義とアラート履歴の(暗黙的な)閲覧
view_adminRoles_permissions_permReadDesc_manageContent = (暗黙的な)インストールされて利用可能なパッケージ。パッケージインストール履歴の閲覧。
-##view_adminRoles_permissions_permReadDesc_manageDrift = (IMPLIED) view drift definitions and drift history
+view_adminRoles_permissions_permReadDesc_manageDrift = (暗黙的な) ドリフト定義とドリフト履歴の閲覧
view_adminRoles_permissions_permReadDesc_manageEvents = (暗黙的な)イベントの閲覧
view_adminRoles_permissions_permReadDesc_manageMeasurements = メトリックデータと収集スケジュールの(暗黙的な)閲覧
view_adminRoles_permissions_permWriteDesc_configure = リソース構成の更新。リソース構成リビジョン履歴の削除。
@@ -441,7 +443,7 @@ view_adminRoles_permissions_permWriteDesc_deleteChildResources = リソースの
view_adminRoles_permissions_permWriteDesc_inventory = リソースの名前、バージョン、説明、コネクション設定の更新。コネクション設定の履歴の削除。
view_adminRoles_permissions_permWriteDesc_manageAlerts = アラート定義の作成、更新、削除。アラート履歴の確認と削除
view_adminRoles_permissions_permWriteDesc_manageContent = コンテントソースへの登録。パッケージのインストールとアンインストール。
-##view_adminRoles_permissions_permWriteDesc_manageDrift = create, update, and delete drift definitions; and manage drift instances
+view_adminRoles_permissions_permWriteDesc_manageDrift = ドリフト定義の作成、更新、削除、そして、ドリフトインスタンスの管理
view_adminRoles_permissions_permWriteDesc_manageEvents = イベントの削除
view_adminRoles_permissions_permWriteDesc_manageMeasurements = メトリック収集スケジュールの更新
view_adminRoles_permissions_perm_configure = 構成
@@ -452,7 +454,7 @@ view_adminRoles_permissions_perm_inventory = インベントリ
view_adminRoles_permissions_perm_manageAlerts = アラートの管理
view_adminRoles_permissions_perm_manageBundles = バンドルの管理
view_adminRoles_permissions_perm_manageContent = コンテント管理
-##view_adminRoles_permissions_perm_manageDrift = Manage Drift
+view_adminRoles_permissions_perm_manageDrift = ドリフト管理
view_adminRoles_permissions_perm_manageEvents = イベント管理
view_adminRoles_permissions_perm_manageInventory = インベントリの管理
view_adminRoles_permissions_perm_manageMeasurements = 測定の管理
@@ -469,20 +471,20 @@ view_adminRoles_roleDeleteFailed = ロール [{0}] の削除に失敗しまし
view_adminRoles_roleDeleted = ロール [{0}] が削除されました
view_adminRoles_roleUpdateFailed = ロール [{0}] の更新に失敗しました.
view_adminRoles_roleUpdated = ロール [{0}] が更新されました
-##view_adminTemplates_definedBy = Defined By
-##view_adminTemplates_disabledTemplates = Disabled Templates
+view_adminTemplates_definedBy = 定義
+view_adminTemplates_disabledTemplates = 無効化されたテンプレート
view_adminTemplates_editAlertTemplate = アラートテンプレートの編集
-##view_adminTemplates_editTemplates = Edit Templates
-##view_adminTemplates_enabledTemplates = Enabled Templates
+view_adminTemplates_editTemplates = テンプレートの編集
+view_adminTemplates_enabledTemplates = 有効化されたテンプレート
view_adminTemplates_platformServices = プラットフォームサービス
view_adminTemplates_platforms = プラットフォーム
-##view_adminTemplates_pluginTemplates = Plugin Templates
+view_adminTemplates_pluginTemplates = プラグインテンプレート
view_adminTemplates_prompt_disabledAlertTemplates = このリソースタイプに関して作成されたけれど無効にされたアラートテンプレートの数
view_adminTemplates_prompt_disabledMetricTemplates = このリソースタイプに関してデフォルトで無効にされたメトリックスケジュールの数
view_adminTemplates_prompt_enabledAlertTemplates = このリソースタイプに関して有効にされたアラートテンプレートの数
view_adminTemplates_prompt_enabledMetricTemplates = このリソースタイプに関してデフォルトで有効にされたメトリックスケジュールの数
view_adminTemplates_servers = サービス
-##view_adminTemplates_userTemplates = User Templates
+view_adminTemplates_userTemplates = ユーザーテンプレート
view_adminTopology_affinityGroups = アフィニティグループ
view_adminTopology_agents = エージェント
view_adminTopology_partitionEvents = イベントの分類
@@ -494,106 +496,102 @@ view_admin_configuration = 構成
view_admin_content = コンテント
view_admin_downloads_agentDownload = エージェントのダウンロード
view_admin_downloads_agent_buildNumber = エージェントビルド
-##view_admin_downloads_agent_help = <p> これはRHQ Agentの更新バイナリJARファイルです。このJARファイルの目的はユーザーがエージェントがまだ存在しないマシン上に新しいエージェントをインストールできるようにすることと、すでにインストール済みのエージェントを更新できるようにするためです。詳細は、このエージェントダウンロードJARで次のように--helpコマンドラインオプションをつけて実行してください。<br/> <b>java -jar <agent-download.jar> --help</b> </p> <h3>エージェントのインストール</h3> <p> <b>java -jar <agent-download.jar> --install[=<新しいエージェントディレクトリ>]</b><br/> このコマンドは新しいエージェントをインストールします。もし新しいディレクトリを指定しなければ、デフォルトは"."となります。</p> <h3>エージェントの更新</h3> <p> <b>java -jar <agent-download.jar> --update[=<古いエージェントディレクトリ>]</b><br/> これはすでにインストールされた既存のエージェントを更新します。もし古い、既存のエージェントがインストールされたディレクトリを指定しなかったなら、"rhq-agent"が仮定されます。</p>
+## view_admin_downloads_agent_help = <p> これはRHQ Agentの更新バイナリJARファイルです。このJARファイルの目的はユーザーがエージェントがまだ存在しないマシン上に新しいエージェントをインストールできるようにすることと、すでにインストール済みのエージェントを更新できるようにするためです。詳細は、このエージェントダウンロードJARで次のように--helpコマンドラインオプションをつけて実行してください。<br/> <b>java -jar <agent-download.jar> --help</b> </p> <h3>エージェントのインストール</h3> <p> <b>java -jar <agent-download.jar> --install[=<新しいエージェントディレクトリ>]</b><br/> このコマンドは新しいエージェントをインストールします。もし新しいディレクトリを指定しなければ、デフォルトは"."となります。</p> <h3>エージェントの更新</h3> <p> <b>java -jar <agent-download.jar> --update[=<古いエージェントディレクトリ>]</b><br/> これはすでにインストールされた既存のエージェントを更新します。もし古い、既存のエージェントがインストールされたディレクトリを指定しなかったなら、"rhq-agent"が仮定されます。</p>
view_admin_downloads_agent_link_label = リンク
view_admin_downloads_agent_link_value = エージェント {0} ({1}) のダウンロード
view_admin_downloads_agent_loadError = エージェントバージョン情報が取得できません
view_admin_downloads_agent_md5 = エージェント MD5
view_admin_downloads_agent_version = エージェントバージョン
view_admin_downloads_bundleDownload = バンドルデプロイヤのダウンロード
-##view_admin_downloads_bundle_help = <p> これはバンドルデプロイヤツールで、開発者とRHQバンドルによって使われます。このスタンドアロンツールを使うとバンドルやコンソールからのレシピをテストできるようにします。</p>
+## view_admin_downloads_bundle_help = <p> これはバンドルデプロイヤツールで、開発者とRHQバンドルによって使われます。このスタンドアロンツールを使うとバンドルやコンソールからのレシピをテストできるようにします。</p>
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>
+## view_admin_downloads_cli_help = <p> これはコマンドラインツールで、CLIとして知られているものです。コンソールから実行するスタンドアロンツールで、RHQサーバーに対してコマンドラインインタフェースを提供します。自動化されたタスクを実行するためにCLIやrunスクリプトを介してコマンドを呼び出すことが可能です。CLIのインストール方法と使い方についての詳細情報はドキュメントをご覧ください。</p>
view_admin_downloads_cli_link_label = リンク
view_admin_downloads_cli_link_value = CLI {0} ({1}) をダウンロード
view_admin_downloads_cli_loadError = CLIバージョン情報が取得できません
view_admin_downloads_cli_md5 = CLI MD5
view_admin_downloads_cli_version = CLIバージョン
view_admin_downloads_connectorsDownload = コネクタのダウンロード
-##view_admin_downloads_connectors_help = コネクターは製品がRHQによって管理可能であるために必要となるソフトウェアです。RHQエージェントが通信できるように、管理製品にコネクターをインストールします。詳細はドキュメントをご覧ください。
+## view_admin_downloads_connectors_help = コネクターは製品がRHQによって管理可能であるために必要となるソフトウェアです。RHQエージェントが通信できるように、管理製品にコネクターをインストールします。詳細はドキュメントをご覧ください。
view_admin_downloads_connectors_loadError = コネクター情報を取得できません
view_admin_downloads_connectors_none = ダウンロードのためのコネクターが見つかりません
-##view_admin_landing = このセクションにおいて、RHQグローバル設定が管理されます。これはセキュリティ構成、プラグイン設定、RHQサーバーとエージェントの管理を含みます。
+## view_admin_landing = このセクションにおいて、RHQグローバル設定が管理されます。これはセキュリティ構成、プラグイン設定、RHQサーバーとエージェントの管理を含みます。
view_admin_measTemplates_updateExisting_title = 既存スケジュールの更新
view_admin_measTemplates_updateExisting_tooltip = このボックスをチェックすると、このタイプのすべてのリソースを対象に選択したメトリックスの収集スケジュールを更新します。もしこれがチェックされていなければ、テンプレートスケジュールが将来的にインベントリに追加されるこのタイプの新規リソースに適用されるだけです。
-##view_admin_plugins_agent = Agent
-##view_admin_plugins_agentDeleteConfirm = <b>Warning!</b><br/>\nThe following agent plugins are about to be deleted:<br/>\n{0}<br/>\nAre you sure you want to delete these?
-##view_admin_plugins_agentDisableConfirm = <b>Warning!</b><br/>\nThe following agent plugins are about to be disabled:<br/>\n{0}<br/>\nAre you sure you want to disable these?
-##view_admin_plugins_deletedAgentPlugins = Deleted agent plugins: {0}
-##view_admin_plugins_deletedAgentPluginsFailure = Failed to delete agent plugins.
-##view_admin_plugins_deployed = Deployed?
-##view_admin_plugins_disabledAgentPlugins = Disabled agent plugins: {0}
-##view_admin_plugins_disabledAgentPluginsFailure = Failed to disable agent plugins.
-##view_admin_plugins_disabledServerPlugins = Disabled server plugins: {0}
-##view_admin_plugins_disabledServerPluginsFailure = Failed to disable server plugins.
-##view_admin_plugins_enabledAgentPlugins = Enabled agent plugins: {0}
-##view_admin_plugins_enabledAgentPluginsFailure = Failed to enable agent plugins.
-##view_admin_plugins_enabledServerPlugins = Enabled server plugins: {0}
-##view_admin_plugins_enabledServerPluginsFailure = Failed to enable server plugins.
-##view_admin_plugins_hideDeleted = Hide Deleted
-##view_admin_plugins_hideUndeployed = Hide Undeployed
-##view_admin_plugins_loadFailure = Failed to load plugin data
-##view_admin_plugins_purgedAgentPlugins = Preparing to purge agent plugins {0}. This may take a few minutes since all type definitions from the plugins must first be purged from the system. The plugins will still be visible on this page until they have been purged. Please note that you must not re-install the plugin while the purge is running, otherwise failures will occur. Therefore, please wait before attempting to re-install the plugin into the system until this purge is complete.
-##view_admin_plugins_purgedAgentPluginsFailure = Failed to purge agent plugins.
-##view_admin_plugins_purgedServerPlugins = Purged server plugins: {0}.
-##view_admin_plugins_purgedServerPluginsFailure = Failed to purge server plugins.
-##view_admin_plugins_restartMasterPC = Restart Master Plugin Container
-##view_admin_plugins_restartMasterPCComplete = Master plugin container has been restarted.
-##view_admin_plugins_restartMasterPCFailure = Failed to restart the master plugin container
-##view_admin_plugins_restartMasterPCStarted = Restarting the master plugin container...
-##view_admin_plugins_scan = Scan For Updates
-##view_admin_plugins_scanComplete = Finished scanning for updated plugins
-##view_admin_plugins_scanFailure = Failed to scan for updated plugins.
+view_admin_plugins_agent = エージェント
+view_admin_plugins_agentDeleteConfirm = <b>警告!</b><br/>\n次のエージェントプラグインは削除されようとしています:<br/>\n{0}<br/>\n本当にこれらを削除してもよろしいですか?
+view_admin_plugins_agentDisableConfirm = <b>警告!</b><br/>\n次のエージェントプラグインは無効にされようとしています:<br/>\n{0}<br/>\n本当にこれらを無効にしてもよろしいですか?
+view_admin_plugins_deletedAgentPlugins = 削除されたエージェントプラグイン: {0}
+view_admin_plugins_deletedAgentPluginsFailure = エージェントプラグインの削除に失敗しました
+view_admin_plugins_deployed = デプロイしましたか?
+view_admin_plugins_disabledAgentPlugins = 無効化されたエージェントプラグイン: {0}
+view_admin_plugins_disabledAgentPluginsFailure = エージェントプラグインを無効化するのに失敗しました
+view_admin_plugins_disabledServerPlugins = 無効化されたサーバープラグイン: {0}
+view_admin_plugins_disabledServerPluginsFailure = サーバープラグインを無効化するのに失敗しました
+view_admin_plugins_enabledAgentPlugins = 有効化されたエージェントプラグイン: {0}
+view_admin_plugins_enabledAgentPluginsFailure = エージェントプラグインを有効化するのに失敗しました
+view_admin_plugins_enabledServerPlugins = 有効化されたサーバープラグイン: {0}
+view_admin_plugins_enabledServerPluginsFailure = サーバープラグインの有効化に失敗しました
+view_admin_plugins_hideDeleted = 削除済みを隠す
+view_admin_plugins_hideUndeployed = アンデプロイを隠す
+view_admin_plugins_loadFailure = プラグインデータをロードするのに失敗しました
+view_admin_plugins_purgedAgentPlugins = エージェントプラグイン {0} をパージするための準備をしています。これは数分かかるかもしれません。なぜなら、そのプラグインからのすべてのタイプ定義は最初にシステムからパージされなければならないからです。それらのプラグインはパージされるまでの間このページで見え続けます。パージが実行中はそのプラグインを再インストールしてはいけないことに留意してください。さもなければ、失敗することでしょう。したがって、このパージが完了するまでシステムへプラグインの再インストールをお待ちください。
+view_admin_plugins_purgedAgentPluginsFailure = エージェントプラグインのパージに失敗しました
+view_admin_plugins_purgedServerPlugins = パージされたサーバープラグイン: {0}.
+view_admin_plugins_purgedServerPluginsFailure = サーバープラグインのパージに失敗しました
+view_admin_plugins_restartMasterPC = マスタープラグインコンテナの再起動
+view_admin_plugins_restartMasterPCComplete = マスタープラグインコンテナが再起動されました
+view_admin_plugins_restartMasterPCFailure = マスタープラグインコンテナの再起動に失敗しました
+view_admin_plugins_restartMasterPCStarted = マスタープラグインコンテナの再起動中...
+view_admin_plugins_scan = 更新のためのスキャン
+view_admin_plugins_scanComplete = 更新されたプラグインのためのスキャンを完了しました
+view_admin_plugins_scanFailure = 更新されたプラグインのためのスキャンが失敗しました
view_admin_plugins_server = サーバー
-##view_admin_plugins_serverConfig = Plugin Configuration
-##view_admin_plugins_serverConfig_badSettings = Please enter valid settings
-##view_admin_plugins_serverConfig_saveFailed = Failed to save the settings
-##view_admin_plugins_serverConfig_settingsSaved = The settings have been saved.
-##view_admin_plugins_serverControls = Controls
-##view_admin_plugins_serverControls_badParams = Please enter valid parameters
-##view_admin_plugins_serverControls_clickForError = Click for the error message
-##view_admin_plugins_serverControls_invokeFailure = Failed to invoke the control
-##view_admin_plugins_serverControls_name = Control
-##view_admin_plugins_serverControls_parameters = Parameters
-##view_admin_plugins_serverControls_results = Results
-##view_admin_plugins_serverDisableConfirm = <b>Warning!</b><br/>\nThe following server plugins are about to be disabled:<br/>\n{0}<br/>\nAre you sure you want to disable these?
-##view_admin_plugins_serverScheduleJobs = Scheduled Jobs
-##view_admin_plugins_serverUndeployConfirm = <b>Warning!</b><br/>\nThe following server plugins are about to be undeployed:<br/>\n{0}<br/>\nAre you sure you want to undeploy these?
-##view_admin_plugins_showDeleted = Show Deleted
-##view_admin_plugins_showUndeployed = Show Undeployed
-##view_admin_plugins_undeploy = Undeploy
-##view_admin_plugins_undeployedServerPlugins = Undeployed server plugins: {0}
-##view_admin_plugins_undeployedServerPluginsFailure = Failed to undeploy server plugins.
-##view_admin_plugins_upload = Upload Plugin
+view_admin_plugins_serverConfig = プラグイン構成
+view_admin_plugins_serverConfig_badSettings = 正しい設定を入力してください
+view_admin_plugins_serverConfig_saveFailed = 設定の保存に失敗しました
+view_admin_plugins_serverConfig_settingsSaved = 設定が保存されました
+view_admin_plugins_serverControls = コントロール
+view_admin_plugins_serverControls_badParams = 正しいパラメーターを入力してください
+view_admin_plugins_serverControls_clickForError = エラーメッセージをクリックしてください
+view_admin_plugins_serverControls_invokeFailure = コントロールの呼び出しに失敗しました
+view_admin_plugins_serverControls_name = コントロール
+view_admin_plugins_serverControls_parameters = パラメーター
+view_admin_plugins_serverControls_results = 結果
+view_admin_plugins_serverDisableConfirm = <b>警告!</b><br/>\n次のサーバープラグインは無効にされようとしています:<br/>\n{0}<br/>\n本当にこれらを無効にしてもよろしいですか?
+view_admin_plugins_serverScheduleJobs = スケジュールされたジョブ
+view_admin_plugins_serverUndeployConfirm = <b>警告!</b><br/>\n次のサーバープラグインはアンデプロイされようとしています:<br/>\n{0}<br/>\n本当にこれらをアンデプロイにしてもよろしいですか?
+view_admin_plugins_showDeleted = 削除済みを表示
+view_admin_plugins_showUndeployed = アンデプロイを表示
+view_admin_plugins_undeploy = アンデプロイ
+view_admin_plugins_undeployedServerPlugins = アンデプロイ済みのサーバープラグイン: {0}
+view_admin_plugins_undeployedServerPluginsFailure = サーバープラグインのアンデプロイに失敗しました
+view_admin_plugins_upload = プラグインのアップロード
view_admin_security = セキュリティ
-##view_admin_systemSettings_ActiveDriftServerPlugin_desc = The drift server plugin that manages the persistence of drift-related entities and content.
-##view_admin_systemSettings_ActiveDriftServerPlugin_name = Active Drift Server Plugin
+view_admin_systemSettings_ActiveDriftServerPlugin_desc = ドリフト関連のエンティティとコンテントの永続性を管理するドリフトサーバープラグイン
+view_admin_systemSettings_ActiveDriftServerPlugin_name = アクティブドリフトサーバープラグイン
view_admin_systemSettings_AgentMaxQuietTimeAllowed_desc = もしエージェントから何も受信しないでこの時間が経過したならば、そのエージェントはダウンしたものと見なされます。この値は分単位で指定されます。
view_admin_systemSettings_AgentMaxQuietTimeAllowed_name = 許容される最大無通信時間
view_admin_systemSettings_AlertPurge_desc = データベースから除去される前のアラートヒストリの保持時間。これは日単位で指定されます。
-view_admin_systemSettings_AlertPurge_name = 次より古いアラートを削除
+view_admin_systemSettings_AlertPurge_name = 古いアラートを削除
view_admin_systemSettings_AvailabilityPurge_desc = データベースから除去される前の可用性データの保持時間。これは日単位で指定されます。
-view_admin_systemSettings_AvailabilityPurge_name = 次より古い可用性データの削除
+view_admin_systemSettings_AvailabilityPurge_name = 古い可用性データの削除
view_admin_systemSettings_BaseURL_desc = サーバーGUIへのURL。主にアラートE-メール通知の使われます。
view_admin_systemSettings_BaseURL_name = GUIコンソールURL
view_admin_systemSettings_BaselineDataSet_desc = ベースラインを決定するために使われる過去の測定データの量。これは日単位で指定されます。
view_admin_systemSettings_BaselineDataSet_name = ベースラインデータセット
-view_admin_systemSettings_BaselineFrequency_desc = ベースラインの自動景作の頻度が計算されます。もし0なら、ベースラインの自動景作は無効にされます。これは日単位で指定されます。
+view_admin_systemSettings_BaselineFrequency_desc = ベースラインの自動計算の頻度が計算されます。もし0なら、ベースラインの自動計算は無効にされます。これは日単位で指定されます。
view_admin_systemSettings_BaselineFrequency_name = ベースライン計算の頻度
view_admin_systemSettings_DataMaintenance_desc = データベースの保守が実行される頻度(例えば、Postgresを使っているのであればであればバキュームなど)。これは時間単位で指定されます。
view_admin_systemSettings_DataMaintenance_name = データベース保守期間
-view_admin_systemSettings_DataReindex_desc = もし有効であれば、データベーステーブルは定期的にインデックスが再生成されます。
-view_admin_systemSettings_DataReindex_name = データテーブルの夜間でのインデックス再生成
-##view_admin_systemSettings_DriftFilePurge_desc = How old unused and orphaned drift files must be before being purged from backend storage. This is specified in days.
-##view_admin_systemSettings_DriftFilePurge_name = Delete Unused Drift Files Older Than
+view_admin_systemSettings_DataReindex_desc = もし有効であれば、データベーステーブルは定期的にインデックスが再作成されます。
+view_admin_systemSettings_DataReindex_name = データテーブルの夜間でのインデックス再作成
+view_admin_systemSettings_DriftFilePurge_desc = バックエンドからパージされる対象となる未使用かつ親がいないドリフトファイルの経過時間。これは日数で指定されます。
+view_admin_systemSettings_DriftFilePurge_name = 未使用ドリフトファイルの削除
view_admin_systemSettings_EnableAgentAutoUpdate_desc = サーバーがエージェントが自動更新することを許すかどうかを決定します。もしこれが無効にされているのであれば、サーバーからエージェントのディストリビューションをダウンロードすることはできません。
view_admin_systemSettings_EnableAgentAutoUpdate_name = エージェントの自動更新を有効にする
view_admin_systemSettings_EnableDebugMode_desc = 有効であれば、サーバーはデバッグモードに入ります
@@ -601,8 +599,8 @@ view_admin_systemSettings_EnableDebugMode_name = デバッグモードを有効
view_admin_systemSettings_EnableExperimentalFeatures_desc = 有効であれば、現在の製品に含まれる任意の実験的機能が利用可能になります
view_admin_systemSettings_EnableExperimentalFeatures_name = 実験的機能を有効にする
view_admin_systemSettings_EventPurge_desc = データベースから除去される前のイベントデータの保持時間。これは日単位で指定されます。
-view_admin_systemSettings_EventPurge_name = 次より古いイベント削除
-view_admin_systemSettings_JAASProvider_desc = ユーザーIDを欠点するのにLDAPが使われるべきですか?
+view_admin_systemSettings_EventPurge_name = 古いイベント削除
+view_admin_systemSettings_JAASProvider_desc = ユーザーIDを決定するのにLDAPが使われるべきですか?
view_admin_systemSettings_JAASProvider_name = LDAPの有効化
view_admin_systemSettings_LDAPBaseDN_desc = ユーザ認証の間にユーザー名とパスワードを検索するディレクトリツリーのベース。例: ou=People,dc=redhat,dc=com
view_admin_systemSettings_LDAPBaseDN_name = サーチベース
@@ -623,17 +621,15 @@ view_admin_systemSettings_LDAPProtocol_name = SSL
view_admin_systemSettings_LDAPUrl_desc = LDAPサーバーへのURL
view_admin_systemSettings_LDAPUrl_name = LDAP URL
view_admin_systemSettings_RtDataPurge_desc = データベースから除去される前の応答時間の保持時間。これは日単位で指定されます。
-view_admin_systemSettings_RtDataPurge_name = 次より古い応答時間の削除
+view_admin_systemSettings_RtDataPurge_name = 古い応答時間の削除
view_admin_systemSettings_TraitPurge_desc = データベースから除去される前の測定トレイトデータの保持時間。これは日単位で指定されます。
-view_admin_systemSettings_TraitPurge_name = 次より古い測定トレイトの削除
+view_admin_systemSettings_TraitPurge_name = 古い測定トレイトの削除
view_admin_systemSettings_cannotLoadServerDetails = サーバー詳細をロードできません
view_admin_systemSettings_cannotLoadSettings = 現在のシステム設定を取得できません
-##view_admin_systemSettings_dumpToLogFailed = Writing of system info to server log file failed
-##view_admin_systemSettings_dumpedToLog = System info was successfully written to server log file
view_admin_systemSettings_fixBeforeSaving = 保存前に無効な値を修正してください
view_admin_systemSettings_group_baseline = 自動ベースライン構成プロパティ
view_admin_systemSettings_group_dataMgr = データマネージャー構成プロパティ
-##view_admin_systemSettings_group_drift = Drift Server Configuration Settings
+view_admin_systemSettings_group_drift = ドリフトサーバー構成設定
view_admin_systemSettings_group_general = 全体構成プロパティ
view_admin_systemSettings_group_ldap = LDAP構成プロパティ
view_admin_systemSettings_saveFailure = システム設定の保存に失敗しました
@@ -661,26 +657,26 @@ view_alert_common_tab_conditions_text = 条件
view_alert_common_tab_conditions_type_availability = アベイラビリティの変化
view_alert_common_tab_conditions_type_availability_down = 下降
view_alert_common_tab_conditions_type_availability_up = 上昇
-##view_alert_common_tab_conditions_type_drift = Drift Detection
-##view_alert_common_tab_conditions_type_drift_configpaths = Drift Detection for files that match "{0}" and for drift definition [{1}]
-##view_alert_common_tab_conditions_type_drift_onlyconfig = Drift Detection for drift definition [{0}]
-##view_alert_common_tab_conditions_type_drift_onlypaths = Drift Detection for files that match "{0}"
+view_alert_common_tab_conditions_type_drift = ドリフトの検出
+view_alert_common_tab_conditions_type_drift_configpaths = "{0}" に一致するファイルとドリフト定義 [{1}] のためのドリフトの検出
+view_alert_common_tab_conditions_type_drift_onlyconfig = ドリフト定義 [{0}] のためのドリフト検出
+view_alert_common_tab_conditions_type_drift_onlypaths = "{0}" に一致するファイルのためのドリフト検出
view_alert_common_tab_conditions_type_event = イベントの検出
view_alert_common_tab_conditions_type_event_matching = イベントソースが一致
view_alert_common_tab_conditions_type_metric_baseline = メトリック値のベースライン
view_alert_common_tab_conditions_type_metric_baseline_verb = of
view_alert_common_tab_conditions_type_metric_calltime_change = 呼び出し時間の変化
-view_alert_common_tab_conditions_type_metric_calltime_change_verb = by at least
+##view_alert_common_tab_conditions_type_metric_calltime_change_verb = by at least
view_alert_common_tab_conditions_type_metric_calltime_delta_grows = 上昇
view_alert_common_tab_conditions_type_metric_calltime_delta_other = 変化
view_alert_common_tab_conditions_type_metric_calltime_delta_shrinks = 縮小
view_alert_common_tab_conditions_type_metric_calltime_destination = 呼び出しの宛先が一致
view_alert_common_tab_conditions_type_metric_calltime_threshold = 呼び出し時間の閾値
view_alert_common_tab_conditions_type_metric_change = メトリック値の変換
-##view_alert_common_tab_conditions_type_metric_range_inside_exclusive = Metric Value Range: [{0}] between [{1}] and [{2}], exclusive
-##view_alert_common_tab_conditions_type_metric_range_inside_inclusive = Metric Value Range: [{0}] between [{1}] and [{2}], inclusive
-##view_alert_common_tab_conditions_type_metric_range_outside_exclusive = Metric Value Range: [{0}] outside [{1}] and [{2}], exclusive
-##view_alert_common_tab_conditions_type_metric_range_outside_inclusive = Metric Value Range: [{0}] outside [{1}] and [{2}], inclusive
+view_alert_common_tab_conditions_type_metric_range_inside_exclusive = メトリック値の範囲 [{0}] : [{1}] と [{2}] の間 (境界値を含まない)
+view_alert_common_tab_conditions_type_metric_range_inside_inclusive = メトリック値の範囲 [{0}] : [{1}] と [{2}] の間 (境界値を含む)
+view_alert_common_tab_conditions_type_metric_range_outside_exclusive = メトリック値の範囲 [{0}] : [{1}] と [{2}] の外側(境界値を含まない)
+view_alert_common_tab_conditions_type_metric_range_outside_inclusive = メトリック値の範囲 [{0}] : [{1}] と [{2}] の外側(境界値を含む)
view_alert_common_tab_conditions_type_metric_threshold = メトリック値の閾値
view_alert_common_tab_conditions_type_metric_trait_change = トレイトの変化
view_alert_common_tab_conditions_type_operation = オペレーションの実行
@@ -714,25 +710,25 @@ view_alert_common_tab_notifications_message = メッセージ
view_alert_common_tab_notifications_sender = センダー
view_alert_common_tab_notifications_status = ステータス
view_alert_common_tab_recovery = リカバリ
-view_alert_definition_condition_editor_avilability_option_down = 下降する
-view_alert_definition_condition_editor_avilability_option_up = 上昇する
+view_alert_definition_condition_editor_avilability_option_down = 停止
+view_alert_definition_condition_editor_avilability_option_up = 起動
view_alert_definition_condition_editor_avilability_tooltip = 条件のトリガーとなるアベイラビリティ状態の変化を指定します
view_alert_definition_condition_editor_avilability_value = アベイラビリティ
view_alert_definition_condition_editor_common_avg = 平均
view_alert_definition_condition_editor_common_max = 最大
view_alert_definition_condition_editor_common_min = 最小
view_alert_definition_condition_editor_delete_confirm = 選択されたアラート定義を削除しますか?
-##view_alert_definition_condition_editor_drift_configname_regex = Drift Definition Name
-##view_alert_definition_condition_editor_drift_configname_regex_tooltip = If specified, this is the drift definition name that was responsible for the drift that was detected. This can optionally be a regular expression if you wish to match multiple drift definition names.
-##view_alert_definition_condition_editor_drift_pathname_regex = Pathname Regular Expression
-##view_alert_definition_condition_editor_drift_pathname_regex_tooltip = If specified, this is a regular expression that must match the pathnames of those files that drifted.
-##view_alert_definition_condition_editor_drift_tooltip = This condition is triggered when drift has been detected.
+view_alert_definition_condition_editor_drift_configname_regex = ドリフト定義名
+view_alert_definition_condition_editor_drift_configname_regex_tooltip = もし指定されたなら、これは検出されたドリフトのための責任を持ったドリフト定義名です。これは複数のドリフト定義名に一致させたいのならオプションとして正規表現にすることもできます。
+view_alert_definition_condition_editor_drift_pathname_regex = パス名正規表現
+view_alert_definition_condition_editor_drift_pathname_regex_tooltip = もし指定されたなら、これはドリフトされたファイルのパス名に一致しなければならない正規表現になります。
+view_alert_definition_condition_editor_drift_tooltip = この条件はドリフトが検出されたときがトリガーになります。
view_alert_definition_condition_editor_event_regex = 正規表現
view_alert_definition_condition_editor_event_regex_tooltip = もし指定されたなら、これは正規表現であり、この条件を引き起こすために収集されたイベントメッセージに一致しなければなりません。
view_alert_definition_condition_editor_event_severity = イベント重大度
view_alert_definition_condition_editor_event_tooltip = この条件を引き起こすためにイベントメッセージが報告される必要があるイベント重大度を指定します。もしオプションとなる正規表現を指定したなら、イベントメッセージはその条件が引き起こされるためにイベントメッセージも正規表現に一致しなければなりません。
view_alert_definition_condition_editor_metric_baseline_percentage = ベースラインパーセンテージ
-view_alert_definition_condition_editor_metric_baseline_percentage_tooltip = 選択された比較子(comparator)を使用して選択されたベースライン値のパーセンテージを比較するときに収集されたメトリック値はこの条件を引き起こします。
+view_alert_definition_condition_editor_metric_baseline_percentage_tooltip = 選択された比較演算子を使用して選択されたベースライン値のパーセンテージを比較するときに収集されたメトリック値はこの条件を引き起こします。
view_alert_definition_condition_editor_metric_baseline_tooltip = 条件を引き起こすために違反されることが必要となるベースライン値を指定します。指定する値は、与えられたベースライン値のパーセンテージになります。
view_alert_definition_condition_editor_metric_baseline_value = ベースライン
view_alert_definition_condition_editor_metric_calltime_change_percentage = パーセンテージの変化
@@ -753,19 +749,19 @@ view_alert_definition_condition_editor_metric_calltime_threshold_value = 呼び
view_alert_definition_condition_editor_metric_calltime_threshold_value_tooltip = 選択された比較子(comparator)を使って比較するとき条件が引き起こされるメトリックの閾値
view_alert_definition_condition_editor_metric_change_tooltip = 条件を引き起こすために変更しなければならない値を持つメトリックを指定します
view_alert_definition_condition_editor_metric_common_definition_not_found = メトリック定義が見つかるべきです - 何かがおかしいです
-##view_alert_definition_condition_editor_metric_nometrics = When using the ALL conjunction, you cannot use the same metric in multiple conditions and this alert uses all available metrics in currently existing conditions.
-view_alert_definition_condition_editor_metric_range_comparator = コンパレータ
-##view_alert_definition_condition_editor_metric_range_comparator_inside_exclusive = Inside, exclusive
-##view_alert_definition_condition_editor_metric_range_comparator_inside_inclusive = Inside, inclusive
-##view_alert_definition_condition_editor_metric_range_comparator_outside_exclusive = Outside, exclusive
-##view_alert_definition_condition_editor_metric_range_comparator_outside_inclusive = Outside, inclusive
-##view_alert_definition_condition_editor_metric_range_comparator_tooltip = Determines if a metric value should trigger this condition when inside the range or outside of it.
-##view_alert_definition_condition_editor_metric_range_hivalue = High Value
-##view_alert_definition_condition_editor_metric_range_hivalue_tooltip = The high threshold value of the range.
-##view_alert_definition_condition_editor_metric_range_lovalue = Low Value
-##view_alert_definition_condition_editor_metric_range_lovalue_tooltip = The high threshold value of the range.
-##view_alert_definition_condition_editor_metric_range_tooltip = Compares a metric value to a given low-high value range.
-view_alert_definition_condition_editor_metric_threshold_comparator = コンパレータ
+view_alert_definition_condition_editor_metric_nometrics = すべてを連結して使うとき、複数条件内に同じメトリックを使うことはできません。この警告は現在の条件内でのすべての利用可能なメトリックスを使います。
+view_alert_definition_condition_editor_metric_range_comparator = 比較演算子
+view_alert_definition_condition_editor_metric_range_comparator_inside_exclusive = 内部で、境界値を含まない
+view_alert_definition_condition_editor_metric_range_comparator_inside_inclusive = 内部で、境界値を含む
+view_alert_definition_condition_editor_metric_range_comparator_outside_exclusive = 外部で、 境界値を含まない
+view_alert_definition_condition_editor_metric_range_comparator_outside_inclusive = 外部で、 境界値を含む
+view_alert_definition_condition_editor_metric_range_comparator_tooltip = メトリック値が、範囲内か範囲外のとき、この条件のトリガーになるかどうかを決定します。
+view_alert_definition_condition_editor_metric_range_hivalue = 高値
+view_alert_definition_condition_editor_metric_range_hivalue_tooltip = 範囲での高値の閾値
+view_alert_definition_condition_editor_metric_range_lovalue = 低値
+view_alert_definition_condition_editor_metric_range_lovalue_tooltip = 範囲における低値の閾値
+view_alert_definition_condition_editor_metric_range_tooltip = メトリック値と与えられた値の範囲(低値から高値)を比較します。
+view_alert_definition_condition_editor_metric_threshold_comparator = 比較演算子
view_alert_definition_condition_editor_metric_threshold_comparator_equal = 等しい
view_alert_definition_condition_editor_metric_threshold_comparator_greater = より大きい
view_alert_definition_condition_editor_metric_threshold_comparator_less = より少ない
@@ -776,18 +772,18 @@ view_alert_definition_condition_editor_metric_threshold_value = メトリック
view_alert_definition_condition_editor_metric_threshold_value_tooltip = メトリックの閾値は、選択されたコンパレータを使って比較されたときの条件のトリガーになります
view_alert_definition_condition_editor_metric_trait_change_tooltip = 条件を引き起こすために変更しなければならない値を持つトレイとを指定します
view_alert_definition_condition_editor_metric_trait_change_value = トレイト
-##view_alert_definition_condition_editor_metricswarning = You cannot have multiple conditions that use the same metric when using the ALL conjunction. This alert definition has multiple conditions that use the metric [{0}].
+view_alert_definition_condition_editor_metricswarning = すべての連結を使うときは同じメトリックを使う複数の条件を指定することはできません。この警告定義はそのメトリック [{0}] を使う複数の条件を持ちます。
view_alert_definition_condition_editor_operation_status = オペレーションステータス
view_alert_definition_condition_editor_operation_tooltip = 条件を引き起こすために選択されたが実行されるとき発生しなければならない結果を指定します
view_alert_definition_condition_editor_option_availability = アベイラビリティの変化
-##view_alert_definition_condition_editor_option_drift = Drift Detection
+view_alert_definition_condition_editor_option_drift = ドリフトの検出
view_alert_definition_condition_editor_option_event = イベントの検出
view_alert_definition_condition_editor_option_label = 条件タイプ
view_alert_definition_condition_editor_option_metric_baseline = 測定ベースラインの閾値
view_alert_definition_condition_editor_option_metric_calltime_change = 呼び出し時間の変化
view_alert_definition_condition_editor_option_metric_calltime_threshold = 呼び出し時間の閾値
view_alert_definition_condition_editor_option_metric_change = 測定値の変化
-##view_alert_definition_condition_editor_option_metric_range = Measurement Value Range
+view_alert_definition_condition_editor_option_metric_range = 測定値の範囲
view_alert_definition_condition_editor_option_metric_threshold = 測定絶対値の閾値
view_alert_definition_condition_editor_option_metric_trait_change = トレイト値の変化
view_alert_definition_condition_editor_option_operation = オペレーションの実行
@@ -826,7 +822,7 @@ view_alert_definition_notification_operation_editor_mode_this = このリソー
view_alert_definition_notification_operation_editor_mode_title = リソース選択モード
view_alert_definition_notification_operation_editor_mode_unknown = 未知のオプション - これはバグです
view_alert_definition_notification_operation_editor_operations_loadFailed = 利用可能なオペレーションのリストをロードするのに失敗しました
-view_alert_definition_notification_operation_editor_operations_no_parameters = この操作は一つもパラメータを取りません
+view_alert_definition_notification_operation_editor_operations_no_parameters = この操作は一つもパラメーターを取りません
view_alert_definition_notification_operation_editor_relative_ancestor = 検索を開始
view_alert_definition_notification_operation_editor_relative_ancestor_loadFailed = タイプの祖先を取得できません
view_alert_definition_notification_operation_editor_relative_ancestor_root = ルートの祖先タイプ
@@ -914,23 +910,27 @@ view_autoDiscoveryQ_field_inventoryStatus = インベントリステータス
view_autoDiscoveryQ_field_parentId = 親ID
view_autoDiscoveryQ_ignore = 無視
view_autoDiscoveryQ_ignoreFailure = リソースを無視するのに失敗しました
+view_autoDiscoveryQ_ignoreInProgress = 選択されたリソースを無視している途中です...
view_autoDiscoveryQ_ignoreSuccessful = 選択したリソースを無視するのに成功しました
view_autoDiscoveryQ_ignored = 無視済み
view_autoDiscoveryQ_importFailure = リソースのインポートに失敗しました
+view_autoDiscoveryQ_importInProgress = 選択されたリソースをインポート中です...
view_autoDiscoveryQ_importSuccessful = 選択したリソースのインポートに成功しました。
view_autoDiscoveryQ_loadFailure = インベントリディスカバリキューをロードするのに失敗しました
view_autoDiscoveryQ_newAndIgnored = 新規かつ無視済み
view_autoDiscoveryQ_noperm = (自動ディスカバリキューを閲覧する権限がありません)
+view_autoDiscoveryQ_selectAll = すべてを選択
view_autoDiscoveryQ_showStatus = 表示
view_autoDiscoveryQ_title = 自動リカバリキュー
view_autoDiscoveryQ_unignore = 無視解除
view_autoDiscoveryQ_unignoreFailure = リソースを無視解除のに失敗しました
+view_autoDiscoveryQ_unignoreInProgress = 選択されたリソースの無視を解除中です...
view_autoDiscoveryQ_unignoreSuccessful = 選択したリソースの無視解除に成功しました
view_autoDiscoveryQ_uninventoried = インベントリ登録をしない
view_bundleVersion_loadFailure = バンドルバージョンデータをロードするのに失敗しました
view_bundle_bundle = バンドル
-view_bundle_bundleDeployment = バンドルデプロイ
-view_bundle_bundleDeployments = バンドルデプロイ
+view_bundle_bundleDeployment = バンドルデプロイメント
+view_bundle_bundleDeployments = バンドルデプロイメント
view_bundle_bundleDestinations = バンドル宛先
view_bundle_bundleFiles = バンドルファイル
view_bundle_bundleType = バンドルタイプ
@@ -954,7 +954,7 @@ view_bundle_createWizard_noBundleTypesSupported = バンドルタイプは一つ
view_bundle_createWizard_provideBundleDistro = バンドル配布の提供
view_bundle_createWizard_recipeOption = レシピ
view_bundle_createWizard_title = バンドル作成
-view_bundle_createWizard_uploadInProgress = アップロード中です... この操作は大きいファイルの場合には数分かかることもありません
+view_bundle_createWizard_uploadInProgress = アップロード中です... この操作は大きいファイルの場合には数分かかることもあります
view_bundle_createWizard_uploadOption = アップロード
view_bundle_createWizard_uploadStepName = バンドルファイルのアップロード
view_bundle_createWizard_urlOption = URL
@@ -985,12 +985,17 @@ view_bundle_deployWizard_error_6 = デプロイメントの作成に失敗しま
view_bundle_deployWizard_error_7 = デプロイメント名の取得に失敗しました
view_bundle_deployWizard_error_8 = ドロップダウンから正しいリソースグループを選択しなければなりません
view_bundle_deployWizard_error_9 = 次ページでの新しい宛先の削除に失敗しました
+view_bundle_deployWizard_error_noBundleConfig = バンドルターゲット情報の取得に失敗しました。選択したグループはバンドルデプロイメントのターゲットに成り得る正当な互換グループですか?
view_bundle_deployWizard_getConfigSkip = このバンドルバージョンには構成は必要ありません
view_bundle_deployWizard_getConfigStep = デプロイメント構成の設定
view_bundle_deployWizard_getDestStep = 新しい宛先
view_bundle_deployWizard_getDest_deployDir = ルートデプロイディレクトリ (宛先プラットフォーム上での)
+view_bundle_deployWizard_getDest_deployDir_help = そのバンドルがデプロイされるディレクトリ。このディレクトリはすべてのリソースに関するすべてのデプロイ用として同じものが使われますが、宛先の基本ディレクトリの場所に対する相対ディレクトリになります。このことは、異なるターゲットリソースについて基本ディレクトリの配置がどこにあるかということに依存して、その絶対ディレクトリは異なるターゲットリソースによって異なるパスになる可能性があることを意味しています。
view_bundle_deployWizard_getDest_desc = 宛先の説明
+view_bundle_deployWizard_getDest_destBaseDirName = 基本の配置
+view_bundle_deployWizard_getDest_group_help = メンバーがすべてのバンドルデプロイのための宛先ターゲットとなるようなグループ。バンドルデプロイをサポートするリソースを含む互換グループのみが選択可能です。
view_bundle_deployWizard_getDest_name = 宛先名
+view_bundle_deployWizard_getDest_name_help = 宛先名。それはリソースの特定のグループと特定のバンドルのリソースに関する宛先ディレクトリを識別します。
view_bundle_deployWizard_getInfoStep = デプロイ情報の提供
view_bundle_deployWizard_getInfo_clean = クリーンデプロイですか? (宛先プラットフォーム上でのデプロイディレクトリを掃除する)
view_bundle_deployWizard_getInfo_deploymentDesc = デプロイの説明
@@ -1019,6 +1024,7 @@ view_bundle_deploy_loadBundleFailure = バンドルの検索に失敗しまし
view_bundle_deploy_loadDeployFailure = バンドルデプロイのロードに失敗しました
view_bundle_deploy_loadFailure = バンドルデプロイのロードに失敗しました
view_bundle_deploy_name = デプロイ名
+view_bundle_deploy_operatingSystem = オペレーティングシステム
view_bundle_deploy_selectARow = インストールの詳細を表示するために行を選択してください
view_bundle_deploy_tagUpdateFailure = バンドルデプロイタブの更新に失敗しました
view_bundle_deploy_tagUpdateSuccessful = バンドルデプロイタグの更新に成功しました
@@ -1026,6 +1032,7 @@ view_bundle_deploy_time = デプロイ名
view_bundle_deployed = デプロイ
view_bundle_deployments = デプロイ
view_bundle_dest_backToBundle = バンドルへ戻る
+view_bundle_dest_baseDirName = 基本の配置
view_bundle_dest_created = 作成された
view_bundle_dest_deleteConfirm = このバンドル宛先を削除してもよろしいですか? これはデータベースから削除するだけです。つまり、リモートマシンのこの宛先にデプロイされたすべてのバンドルの内容は残り続けます
view_bundle_dest_deleteFailure = バンドル宛先 [{0}] の削除に失敗しました
@@ -1128,7 +1135,7 @@ view_configurationDetails_configNotUpdatedDueToNoChange = 構成は更新され
view_configurationDetails_error_updateFailure = 構成の更新に失敗しました
view_configurationDetails_messageConcise = 構成は更新されました - 現在のバージョンは {0} です
view_configurationDetails_messageDetailed = リソース [{1}] の構成はバージョン {0} へ更新されました
-##view_configurationDetails_noConfigurationFetched = No configuration was retrieved. This means that either the plugin failed to load the configuration or that configuration collection is merely switched off in the connection settings.
+view_configurationDetails_noConfigurationFetched = 構成が取り出せませんでした。これはプラグインが構成をロードできなかったか、または構成コレクションが接続設定において単にスイッチがオフにされているかのいずれかを意味します。
view_configurationDetails_noPermission = このリソースの構成を編集する権限がありません
view_configurationDetails_somePropertiesInvalid = 次の構成プロパティは不正な値: {0} です。この値は構成が保存される前に訂正されなければなりません。
view_configurationHistoryDetails_error_loadFailure = 構成履歴をロードできません
@@ -1151,11 +1158,11 @@ view_connectionSettingsDetails_messageConcise_updateSuccess = コネクション
view_connectionSettingsDetails_messageDetailed_updateSuccess = リソース [{0} のためのコネクション設定が更新されました
view_connectionSettingsDetails_noPermission = このリソースのコネクション設定を編集する権限がありません
view_connectionSettingsDetails_somePropertiesInvalid = 次のコネクション設定は不正な値: {0} です。設定を保存する前に訂正しなければなりません。
-#view_core_loggedInAs = Logged in as {0}
+view_core_loggedInAs = {0} としてログイン
view_core_loggedOut = ログアウト
view_core_noRecentAlerts = レポートすべき最近のアラートは存在しません
view_core_recentAlerts = [{0}] 個の最近のアラートがありました - 最近のアラートレポートへクリックしてください
-##view_core_serverUnreachable = The server is unreachable and may be down
+view_core_serverUnreachable = サーバーに到達できません。ダウンしている可能性があります。
view_core_uncaught = グローバルにキャッチされない例外
view_dashboardManager_deleteFail = ダッシュボードの削除に失敗しました
view_dashboardManager_deleted = ダッシュボード {0} の削除に成功しました
@@ -1164,7 +1171,7 @@ view_dashboardManager_saved = ダッシュボード {0} をサーバーへ保存
view_dashboardManager_success = ダッシュボードを保存しました
view_dashboard_favorites_error1 = 好みのリソースをロードするのに失敗しました
view_dashboardsManager_error1 = 新しいダッシュボードを追加するのに失敗しました
-##view_dashboardsManager_message_title_details = <h1>RHQへようこそ</h1>\n<p>RHQプロジェクトは抽象化されたプラグインベースのシステム管理スイートで、複数のプロダクトやプラットフォームに対してコア機能を横断的に適用できる拡張可能で、統合化されたシステム管理を提供します。このプロジェクトはレイヤ化されたモジュールによって設計されていて、デプロイのための柔軟なアーキテクチャを提供します。それは企業全体に対する監査と履歴の管理を提供するコアとなるユーザーインタフェースを導入します。サーバー/エージェントアーキテクチャはリモートでの管理と特定プロダクトのサポートを実現するプラグインを提供します。 </p>\n <p>このデフォルトのダッシュボードは上の「編集モード」ボタンをすることで編集可能です。</p>
+## view_dashboardsManager_message_title_details = <h1>RHQへようこそ</h1>\n<p>RHQプロジェクトは抽象化されたプラグインベースのシステム管理スイートで、複数のプロダクトやプラットフォームに対してコア機能を横断的に適用できる拡張可能で、統合化されたシステム管理を提供します。このプロジェクトはレイヤ化されたモジュールによって設計されていて、デプロイのための柔軟なアーキテクチャを提供します。それは企業全体に対する監査と履歴の管理を提供するコアとなるユーザーインタフェースを導入します。サーバー/エージェントアーキテクチャはリモートでの管理と特定プロダクトのサポートを実現するプラグインを提供します。 </p>\n <p>このデフォルトのダッシュボードは上の「編集モード」ボタンをすることで編集可能です。</p>
view_dashboards_confirm1 = 削除してもよろしいですか
view_dashboards_portlets_refresh_fail1 = 自動リフレッシュするポートレットのリフレッシュ間隔の更新に失敗しました
view_dashboards_portlets_refresh_fail2 = 自動リフレッシュするポートレットのリロードの無効化に失敗しました
@@ -1174,85 +1181,76 @@ view_dashboards_portlets_refresh_one_min = 1 分
view_dashboards_portlets_refresh_success1 = ポートレットの自動リフレッシュ間隔を更新しました
view_dashboards_portlets_refresh_success2 = 自動リフレッシュするポートレットのリロードを停止しています
view_dashboards_title = ダッシュボード
-##view_drift_button_detectNow = Detect Now
-##view_drift_button_pinToDef = Pin to Definition
-##view_drift_button_pinToDef_confirm = Pinning will set this snapshot as snapshot 0 for the definition. All other existing snapshots will be removed from the definition. The definition will be be marked as pinned and subsequent drift will always be reported against the pinned snapshot. Pin this snapshot to the definition?
-##view_drift_button_pinToTemplate = Pin to Template
-##view_drift_button_pinToTemplate_confirm = After pinning, this snapshot will be set to the initial snapshot for all definitions created using the template. If pinned to an existing template then the template''s existing definitions will be pinned to the new initial snapshot and their existing snapshots will be removed. Continue to template selection?
-##view_drift_carousel_sizeFilterLabel = Snapshot Display Max
-##view_drift_carousel_startFilterLabel = Snapshot Start
-##view_drift_category_fileAdded = File Added
-##view_drift_category_fileChanged = File Changed
-##view_drift_category_fileNew = Newly Detected
-##view_drift_category_fileRemoved = File Removed
-##view_drift_confirm_deleteAllDefs = Delete all drift detection definitions?
-##view_drift_confirm_deleteDefs = Delete the selected drift detection definition(s)?
-##view_drift_confirm_deleteTemplate = Warning! Deleting this template will also cause all attached drift definitions to be deleted as well. Those attached definitions along with all of their snapshots will be permanently removed from the system. Detached definitions will not be removed. Are you sure you want to continue?
-##view_drift_failure_deleteDefs = Failed to delete some or all drift detection definitions.
-##view_drift_failure_deleteTemplates = Failed to delete some or all of the drift templates
-##view_drift_failure_detectNow = Failed to submit drift detection run request
-##view_drift_failure_load = Failed to fetch drift instances
-##view_drift_failure_pinToDef = Failed to pin snapshot to definition
-##view_drift_success_defUpdated = Drift detection definition updated and will affect the next detection run as applicable.
-##view_drift_success_delete = Successfully deleted {0} drift instances
-##view_drift_success_deleteDefs = Successfully deleted {0} drift detection definitions
-##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
-##view_drift_table_driftHandlingMode_plannedChanges = planned changes
-##view_drift_table_hover_defNotPinned = The drift definition is not pinned Click to view the initial snapshot.
-##view_drift_table_hover_defPinned = The drift definition is pinned to its initial snapshot. Click to view the initial snapshot.
-##view_drift_table_hover_edit = Click to view or edit the drift definition or template properties.
-##view_drift_table_hover_outOfCompliance_drift = There is drift
-##view_drift_table_hover_outOfCompliance_noBaseDir = The base directory does not exist
-##view_drift_table_hover_templateNotPinned = The drift template is not pinned to a snapshot.
-##view_drift_table_hover_templatePinned = The drift template is pinned to a snapshot. Click to view the pinned snapshot.
-##view_drift_table_newFile = New File
-##view_drift_table_oldFile = Old File
-##view_drift_table_pinned = Pinned?
-##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}]
-##view_drift_wizard_addDef_failure = Failed to add new drift detection definition [{0}]
-##view_drift_wizard_addDef_infoStepHelp = Each drift detection definition describes a set of files for which drift monitoring will be performed. The definition can be enabled and disabled, defines the detection run interval, and specifies a base directory and optional file filters. For each resource type offering drift detection there will be one or more predefined templates to use as a starting definition, which can then be edited.
-##view_drift_wizard_addDef_infoStepName = Select the Template for the new Drift Detection Definition
-##view_drift_wizard_addDef_success = Successfully added new drift detection definition [{0}]. Agent(s) will be updated.
-##view_drift_wizard_addDef_templatePrompt = Drift Definition Templates
-##view_drift_wizard_addDef_title = Add Drift Detection Definition for Resource of Type [{0}]
-##view_drift_wizard_addDef_windowTitle = Add Drift Detection Definition Wizard
-##view_drift_wizard_addTemplate_failure = Failed to add new drift detection template [{0}]
-##view_drift_wizard_addTemplate_infoStepHelp = Each drift template is derived from an existing template. This provides a quick way to build new templates that are similar to existing templates, or originate with plugin defined templates. Like a drift definition, the template describes a set of files for which drift monitoring will be performed. Depending on the situation a definition derived from the template may or may not be allowed to alter the file set, or change other settings. Template names must be unique within a resource type.
-##view_drift_wizard_addTemplate_infoStepName = Select the Starting Template
-##view_drift_wizard_addTemplate_success = Successfully added new drift template [{0}].
-##view_drift_wizard_addTemplate_title = Add Drift Definition Template for Type [{0}]
-##view_drift_wizard_addTemplate_windowTitle = Add Drift Definition Template Wizard
-##view_drift_wizard_pinTemplate_confirmNotPinned = Once pinned each current and future drift definition for the template will have its initial snapshot set to the template''s pinned snapshot. Existing definitions for this template will be reset to the new initial snapshot, and all exsiting snapshots will be removed. Continue pinning the template to the snapshot?
-##view_drift_wizard_pinTemplate_confirmPinned = Warning! This template is already pinned. The template can be re-pinned to this new snapshot. Once re-pinned each current and future drift definition for the template will have its initial snapshot set to the template''s pinned snapshot. Existing definitions for this template will be reset to the new initial snapshot, and all exsiting snapshots will be removed. Continue re-pinning the template with this new snapshot?
-##view_drift_wizard_pinTemplate_duplicate_name_error = Template name must be unique
-##view_drift_wizard_pinTemplate_failure = Failed to pin snapshot to drift template [{0}]
-##view_drift_wizard_pinTemplate_infoStepExistingTemplate = Pin to ExistingTemplate
-##view_drift_wizard_pinTemplate_infoStepHelp = Select the template to pin. Once pinned each current and future drift definition for that template will have its initial snapshot set to the template''s pinned snapshot. And the definition itself will be marked as pinned. This is used to detect drift from an expected file set. Note that existing definitions for this template will be reset to the new initial snapshot, and all exsiting snapshots will be removed.
-##view_drift_wizard_pinTemplate_infoStepName = Select the Template to be Pinned
-##view_drift_wizard_pinTemplate_infoStepNewTemplate = Pin to New Template (derived from the snapshot''s Drift Definition)
-##view_drift_wizard_pinTemplate_infoStepRadioHelp = The snapshot can be pinned to a new or existing drift template. The ''New Template" option allows the user to promote a trusted definition and snapshot, at the resource level, to the type level. The new template can then be applied to members of the type. The new template is initially a copy of the snapshot''s drift definition but can then be edited in the next step. The name should be changed and must be a unique drift template name for the type. The "Existing Template" option allows the user to pin, or re-pin, an existing template with the selected snapshot. To be valid, the existing template must monitor the same directories as the snapshot''s definition. The selection box displays only valid existing templates. If there are no valid existing templates this option can not be selected.
-##view_drift_wizard_pinTemplate_infoStepRadioTitle = Template Selection
-##view_drift_wizard_pinTemplate_infoStepSelectBlocked = There are no existing templates that monitor the same directories as the snapshot''s definition. Select the "New Template" option to continue.
-##view_drift_wizard_pinTemplate_infoStepSelectTitle = Existing Templates
-##view_drift_wizard_pinTemplate_success = Successfully pinned the drift template [{0}].
-##view_drift_wizard_pinTemplate_title = Pin Snapshot [{0}] of Definition [{1}] to a Drift Template for type [{2}]
-##view_drift_wizard_pinTemplate_windowTitle = Pin Drift Definition Template Wizard
+view_drift_button_detectNow = すぐに検出
+view_drift_button_pinToDef = 定義としてピン留め
+view_drift_button_pinToDef_confirm = ピン留めはこのスナップショットを定義のスナップショット 0 として設定します。その他すべてのスナップショットはその定義から削除されます。その定義はピン留めされたとマークされ、以後のドリフトは常にそのピン留めされたスナップショットに対して報告されます。このスナップショットをその定義にピン留めしますか?
+view_drift_button_pinToTemplate = テンプレートとしてピン留め
+view_drift_button_pinToTemplate_confirm = ピン留めした後は、このスナップショットはそのテンプレートを使って作成されたすべての定義のために初期スナップショットに設定されます。もし既存テンプレートにピン留めされるなら、テンプレートの既存の定義は新規の初期スナップショットに対してピン留めされ、それら既存のスナップショットは削除されます。選択したものをテンプレートにするために継続しますか?
+view_drift_carousel_sizeFilterLabel = スナップショットの最大表示数
+view_drift_carousel_startFilterLabel = スナップショット開始
+view_drift_category_fileAdded = 追加ファイル
+view_drift_category_fileChanged = 変更ファイル
+view_drift_category_fileNew = 新規に検出されたファイル
+view_drift_category_fileRemoved = 削除ファイル
+view_drift_delete_defConfirm = 選択されたドリフトの検出定義を削除しますか?
+view_drift_delete_defConfirmAll = すべてのドリフト検出定義を削除しますか?
+view_drift_failure_deleteDefs = いくつか、あるいはすべてのドリフト検出定義の削除に失敗しました
+view_drift_failure_detectNow = ドリフト検出実行リクエストの送信に失敗しました
+view_drift_failure_load = ドリフトインスタンスの取り出しに失敗しました
+view_drift_failure_pinToDef = 定義へのスナップショットのピン留めに失敗しました
+view_drift_success_defUpdated = ドリフト検出定義が更新されました。これは規定通りに次回の検出実行に影響があるでしょう。
+view_drift_success_delete = ドリフトインスタンス {0} の削除が成功しました
+view_drift_success_deleteDefs = ドリフト定義 {0} の削除が成功しました
+view_drift_success_detectNow = ドリフト検出実行リクエストの送信に成功しました
+view_drift_success_pinToDef = ドリフト定義へのスナップショット {0} のピン留めに成功しました
+view_drift_table_baseDir = 基本ディレクトリ
+view_drift_table_driftHandlingMode = ドリフト処理
+view_drift_table_driftHandlingMode_normal = 通常
+view_drift_table_driftHandlingMode_plannedChanges = 計画変更
+view_drift_table_hover_defNotPinned = そのドリフト定義はピン留めされていません。クリックして初期スナップショットを見てください。
+view_drift_table_hover_defPinned = そのドリフト定義は初期スナップショットへピン留めされています。クリックして初期スナップショットを見てください。
+view_drift_table_hover_edit = クリックしてそのドリフト定義またはテンプレートプロパティを閲覧する、または編集してください
+view_drift_table_hover_templateNotPinned = そのドリフトテンプレートはどのスナップショットにもピン留めされていません
+view_drift_table_hover_templatePinned = そのドリフトテンプレートはあるスナップショットにピン留めされています。クリックしてピン留めされたスナップショットを見てください
+view_drift_table_newFile = 新ファイル
+view_drift_table_oldFile = 旧ファイル
+view_drift_table_pinned = ピン留め?
+view_drift_table_resourceDef = リソースドリフト検出定義
+view_drift_table_resourceHistory = リソースドリフト履歴
+view_drift_table_snapshot = スナップショット
+view_drift_table_snapshotTime = スナップショット時間
+view_drift_table_title_initialSnapshot = 定義 [{0}] のための初期スナップショット: ピン留め = [{1}]
+view_drift_table_title_snapshot = 定義 [{1}] のためのスナップショット [{0}]
+view_drift_table_title_templateSnapshot = テンプレート [{0}] のためのピン留めされたスナップショット
+view_drift_wizard_addDef_failure = ドリフト検出定義 [{0}] の新規追加に失敗しました
+view_drift_wizard_addDef_infoStepHelp = 各ドリフト検出定義はドリフトモニタリングが実行される一式のファイルを記述します。その定義は有効または無効にしたり、検出の実行間隔を定義したり、基本ディレクトリとファイルフィルタ(オプション)を指定することができます。ドリフト検出を提供するリソースタイプごとに、開始する定義として使用するための一つ以上の既定のテンプレートが存在します。
+view_drift_wizard_addDef_infoStepName = その新規ドリフト検出定義のテンプレートを選択してください
+view_drift_wizard_addDef_success = 新規ドリフト検出定義 [{0}] の追加に成功しました。エージェントは更新されます。
+view_drift_wizard_addDef_templatePrompt = ドリフト定義テンプレート
+view_drift_wizard_addDef_title = タイプのリソース [{0}] のためのドリフト検出定義を追加してください
+view_drift_wizard_addDef_windowTitle = ドリフト検出定義ウィザードの追加
+view_drift_wizard_addTemplate_failure = 新規ドリフト検出テンプレート [{0}] の追加に失敗しました
+view_drift_wizard_addTemplate_infoStepHelp = 各ドリフトテンプレートは既存のテンプレートから派生して作成できます。これは既存テンプレートに類似したものや、プラグインが定義したテンプレートから新規テンプレートを構築する素早い方法を提供します。ドリフト定義のように、そのテンプレートはドリフトを監視を実行する一式のファイルを記述します。状況によっては、そのテンプレートから導出された定義はそのファイルセットの変更や、他の設定の変更を許したり、許さなかったりします。テンプレート名は一つのリソースタイプ内でユニークでなければなりません。
+view_drift_wizard_addTemplate_infoStepName = 開始テンプレートの選択
+view_drift_wizard_addTemplate_success = 新規ドリフトテンプレート [{0}] の追加に成功しました
+view_drift_wizard_addTemplate_title = タイプ [{0}] のドリフト定義テンプレートを追加してください
+view_drift_wizard_addTemplate_windowTitle = ドリフト定義テンプレート定義テンプレートウィザードの追加
+view_drift_wizard_pinTemplate_confirmNotPinned = そのテンプレートのための現在と将来の各ドリフト定義がいったんピン留めされると、そのテンプレートはその初期スナップショットがテンプレートのピン留めされたスナップショットとして設定されるようにします。このテンプレートの既存の定義は新規の初期スナップショットへリセットされます。このテンプレートの既存定義は新規初期スナップショットへリセットされ、すべての既存スナップショットは削除されます。そのスナップショットへのテンプレートのピン留めを続けますか?
+view_drift_wizard_pinTemplate_confirmPinned = 警告! このテンプレートはすでにピン留めされています。そのテンプレートはこの新規スナップショットに再度ピン留めすることが可能です。そのテンプレートのための現在と将来の各ドリフト定義がいったんピン留めされると、そのテンプレートは初期スナップショットがそのテンプレートが持つピン留めされたスナップショットとして設定されるようにします。このテンプレートの既存定義は新規初期スナップショットへリセットされ、すべての既存スナップショットは削除されます。この新規スナップショットを持つテンプレートのピン留めを続けますか?
+view_drift_wizard_pinTemplate_failure = ドリフトテンプレート [{0}] へのスナップショットをピン留めするのに失敗しました
+view_drift_wizard_pinTemplate_infoStepExistingTemplate = 現在のテンプレートへのピン留め
+view_drift_wizard_pinTemplate_infoStepHelp = ピン留めをするテンプレートを選択してください。そのテンプレートのための現在と将来の各ドリフト定義がいったんピン留めされると、そのテンプレートは初期スナップショットがそのテンプレートが持つピン留めされたスナップショットとして設定された持つようになりまするようにします。そしてその定義自身はピン留めされたものとしてマークされます。これは期待されるファイルセットからドリフトを検出するのに使われます。このテンプレートのための既存定義は新規の初期スナップショットにリセットされ、すべての既存スナップショットは削除されることに注意してください。
+view_drift_wizard_pinTemplate_infoStepName = ピン留めされるテンプレートの選択
+view_drift_wizard_pinTemplate_infoStepNewTemplate = 新規テンプレートへのピン留め (スナップショットのドリフト定義から導出されたもの)
+view_drift_wizard_pinTemplate_infoStepRadioHelp = そのスナップショットは新規または既存のドリフトテンプレートにピン留めすることが可能です。その''新規テンプレート''オプションは、ユーザーの手によって信頼する定義やスナップショットを、リソースレベルやタイプレベルで、テンプレート化できるようにします。その新規テンプレートは、次に、そのタイプのメンバーに対して適用できます。その新規テンプレートは初期時はスナップショットのドリフト定義のコピーですが、次のステップで編集することができます。その名前は変更すべきで、そのタイプのユニークなドリフトテンプレート名でなければなりません。''既存テンプレート''オプションは、ユーザーが選択したスナップショットを持つ既存テンプレートに対して、ピン留めまたは再度ピン留めができるようにします。正しく設定するには、その既存テンプレートは同じディレクトリをそのスナップショット定義として監視できなければなりません。セレクションボックスは正当な既存のテンプレートだけを表示します。もし正当でない既存テンプレートが存在したとしてもこのオプションでは選択されません。
+view_drift_wizard_pinTemplate_infoStepRadioTitle = テンプレートの選択
+view_drift_wizard_pinTemplate_infoStepSelectBlocked = 既存テンプレートでスナップショットの定義と同じディレクトリを監視するものは存在しません。継続するに''新規テンプレート''オプションを選択してください。
+view_drift_wizard_pinTemplate_infoStepSelectTitle = 既存テンプレート
+view_drift_wizard_pinTemplate_success = ドリフトテンプレート [{0}] のピン留めに成功しました
+view_drift_wizard_pinTemplate_title = タイプ [{2}] のドリフトテンプレートへの定義 [{1}] のスナップショット [{0}] をピン留め
+view_drift_wizard_pinTemplate_windowTitle = ドリフト定義テンプレートウィザードのピン留め
view_dynagroup_children = 動的グループの子
-view_dynagroup_compatible = Compatible
+view_dynagroup_compatible = 互換
view_dynagroup_definitionAlreadyExists = すでにこの名前のグループ定義が存在します
view_dynagroup_definitionCreated = グループ定義名 [{0}] の作成に成功しました
view_dynagroup_definitionLoadFailure = グループ定義のロードに失敗しました
@@ -1299,6 +1297,7 @@ view_dynagroup_exprBuilder_resource_greatGreatGrandparent = 曾祖父母の親
view_dynagroup_exprBuilder_resource_parent = 親
view_dynagroup_exprBuilder_resource_resource = リソース
view_dynagroup_exprBuilder_resource_tooltip = リソースのレベルを望みのものに選択してください。例えば、"親"を選択すると、親リソースが式の残りの部分に一致するようなリソースを発見できます。
+view_dynagroup_exprBuilder_savedExpression = 保存された式
view_dynagroup_exprBuilder_title = 式ビルダー
view_dynagroup_exprBuilder_unset = 設定解除
view_dynagroup_exprBuilder_unset_tooltip = 設定解除はデータベース内のnull値を持つ値をのすべてを発見します。これはデータベースストアと問い合わせデータの理由により"="オペレータを使うことは可能ではありません。
@@ -1336,17 +1335,16 @@ view_dynagroup_template_platforms = インベントリ内のプラットフォ
view_dynagroup_template_uniqueResourceTypes = インベントリ内のユニークリソースタイプ
view_groupConfigEdit_member = メンバー
view_groupConfigEdit_noListProps = グループ構成に現在サポートされていないプロパティのリスト
-##view_groupConfigEdit_saveReminder = You changed some settings - do not forget to save those changes or they will be lost.\n
+view_groupConfigEdit_saveReminder = 設定が変更されています - これらの変更を保存するのを忘れないでください。さもないと失われます。
view_groupConfigEdit_setAll = すべての値を設定:
view_groupConfigEdit_tooltip_1 = メンバー値の違い - 編集するためにアイコンをクリックします
view_groupConfigEdit_unset = 設定解除
view_groupConfigEdit_valsDiff = メンバー値の違い
view_groupConfigEdit_valsDiffForProp = プロパティ [{0}] のためのメンバー値
-view_groupCreateWizard_createFailure = リソースグループの作成に失敗しました [{0}] : {1}
+## view_groupCreateWizard_createFailure = リソースグループの作成に失敗しました
view_groupCreateWizard_createStepName = グループ設定
-##view_groupCreateWizard_createStep_group_exists = Group with name [{0}] already exists
view_groupCreateWizard_createStep_recursive = 再帰
-##view_groupCreateWizard_createSuccessful_concise = 新規のリソースグループを作成しました。[{0}]
+## view_groupCreateWizard_createSuccessful_concise = 新規のリソースグループを作成しました。
view_groupCreateWizard_createSuccessful_full = [{2}] メンバーリソースを含む 名前 [{1}] の新規 [{0}] リソースグループを作成しました。
view_groupCreateWizard_membersStepName = メンバーの選択
view_groupCreateWizard_title = グループ作成
@@ -1399,7 +1397,7 @@ view_group_pluginConfig_table_title = グループコネクション設定履歴
view_group_pluginConfig_table_viewMemberHistory = メンバー履歴の閲覧
view_group_pluginConfig_table_viewSettings = 設定の閲覧
view_group_pluginConfig_view_groupProperties = グループプロパティ
-view_group_pluginConfig_view_noperm = コネクション設定を見る権限がありません
+view_group_pluginConfig_view_noperm = コネクション設定の閲覧権限がありません
view_group_resConfig_edit_invalid = 次の構成プロパティは不正な値を含むのでその構成を保存する前に修正されなければなりません: [{0}]
view_group_resConfig_edit_loadFail = [{0}] のためのメンバーリソース構成を取り出すのに失敗しました
view_group_resConfig_edit_noperm = このグループ構成を編集する権限がありません
@@ -1410,7 +1408,7 @@ view_group_resConfig_edit_saveTooltip = すべてのグループメンバーの
view_group_resConfig_edit_valid = すべての構成プロパティは正しい値ですので、その構成は保存可能です
view_group_resConfig_members_fetchFailure = グループ [{0}] のメンバーのためのリソース構成更新履歴の取得に失敗しました
view_group_resConfig_members_fetchFailureConfig = [{0}] のためのメンバーリソース構成設定の取り出しに失敗しました
-view_group_resConfig_members_fetchFailureConfigInProgress = グループリソース構成の更新が現在進行中です。グループ設定を見る前に更新が終了するのをお待ちください
+view_group_resConfig_members_fetchFailureConfigInProgress = グループリソース構成の更新が現在進行中です。グループ設定を閲覧する前に更新が終了するのをお待ちください
view_group_resConfig_members_statusDetails = ステータスの詳細
view_group_resConfig_members_statusFailure = この構成の更新は未知の理由で失敗しました。
view_group_resConfig_members_statusInprogress = この構成の更新はまだ進行中です
@@ -1431,7 +1429,7 @@ view_group_resConfig_table_title = グループリソース構成履歴
view_group_resConfig_table_viewMemberHistory = メンバー履歴の閲覧
view_group_resConfig_table_viewSettings = 閲覧設定
view_group_resConfig_view_groupProperties = グループプロパティ
-view_group_resConfig_view_noperm = リソース構成設定を見るための権限はありません
+view_group_resConfig_view_noperm = リソース構成設定を閲覧するための権限はありません
view_group_summary_compatible = 互換性
view_group_summary_descUpdateFailure = ID [{0}] を持つリソースグループの説明を変更できませんでした
view_group_summary_descUpdateSuccessful = このリソースグループの説明を変更しました
@@ -1475,10 +1473,10 @@ view_inventory_groups_loadFailed = グループ合成データのロードに失
view_inventory_mixed = 混成
view_inventory_platforms = プラットフォーム
view_inventory_problemGroups = 問題グループ
-view_inventory_resource_loadFailed = ID [{0}] をT持つリソースは存在しないか、アクセスできません
+view_inventory_resource_loadFailed = ID [{0}] を持つリソースは存在しないか、アクセスできません
view_inventory_resources_deleteConfirm = 選択されたリソースを削除したいですか?
view_inventory_resources_deleteFailed = 選択されたリソースの削除に失敗しました
-##view_inventory_resources_deleteSuccessful = A request to perform the resource deletion has been submitted successfully to the agent(s).
+view_inventory_resources_deleteSuccessful = そのリソースを削除するリクエストは(複数の)エージェントに正しく送信されました。
view_inventory_resources_loadFailed = リソース合成データのロードに失敗しました
view_inventory_resources_members = メンバーリソース
view_inventory_resources_uninventoryConfirm = 選択したリソースをアンインベントリしてもよろしいですか? もし選択したリソースがまだ存在しているなら、そのエージェントの次回のディスカバリスキャンの間にそれは再発見されます。
@@ -1489,7 +1487,7 @@ view_inventory_servers = サーバー
view_inventory_services = サービス
view_inventory_summary_agent_error1 = エージェント管理リソースIDの発見に失敗しました
view_inventory_summary_agent_error2 = エージェントリソースIDのpingに失敗しました
-view_inventory_summary_agent_error3 = このエージェントのための詳細を見る権限がありません
+view_inventory_summary_agent_error3 = このエージェントのための詳細を閲覧する権限がありません
view_inventory_summary_agent_fullEnpoint = フルエンドポイント
view_inventory_summary_agent_fullEnpoint_err1 = !このリソースに関連するリモートエンドポイントはありません!
view_inventory_summary_agent_last_title = 最後に受信したアベイラビリティリポート
@@ -1507,7 +1505,7 @@ view_login_prompt = ログインしてください
view_login_registerLater = (キャンセル - 登録を後で完成させてください)
view_login_registerLdapSuccess = 新しいLDAPユーザーの登録に成功しました
view_login_registerUser = ユーザの登録
-##view_login_welcomeMsg = RHQへようこそ! <br/><br/> 登録を完成させるために次のフィールドを入力/更新してください。<br/> "OK"をクリックしたら、ログインします。<br/><br/>
+## view_login_welcomeMsg = RHQへようこそ! <br/><br/> 登録を完成させるために次のフィールドを入力/更新してください。<br/> "OK"をクリックしたら、ログインします。<br/><br/>
view_measureRange_last = 時間の範囲 - 直前
view_measureRange_simple = シンプル...
view_measureRange_start = 時間の範囲 - 開始
@@ -1535,7 +1533,7 @@ view_operationHistoryDetails_dateSubmitted = 発行日
view_operationHistoryDetails_error_fetchFailure = オペレーション履歴のロードに失敗
view_operationHistoryDetails_noResults = このオペレーションは値を返しません
view_operationHistoryDetails_operation = オペレーション
-view_operationHistoryDetails_parameters = パラメータ
+view_operationHistoryDetails_parameters = パラメーター
view_operationHistoryDetails_requestor = 要求者
view_operationHistoryDetails_results = 結果
view_operationHistoryDetails_status = ステータス
@@ -1545,11 +1543,11 @@ view_operationHistoryList_notYetStarted = まだ開始していません
view_operationHistoryList_title = オペレーション履歴
view_operationScheduleDetails_enterParametersBelow = 以下にパラメーターを入力してください...
view_operationScheduleDetails_fieldDefault_description = 説明を見るとためオペレーションを選択してください
-view_operationScheduleDetails_fieldDefault_parameters = パラメーターを見るためオペレーションを選択してください
+view_operationScheduleDetails_fieldDefault_parameters = パラメーターを見るためにオペレーションを選択してください
view_operationScheduleDetails_fieldHelp_description = このスケジュールされたオペレーションのための任意の説明 (例、夜間保守のためのアプリサーバー再起動)
-##view_operationScheduleDetails_fieldHelp_timeout = 時間間隔 - もし指定されていて、その間隔がスケジュールされたオペレーション実行が完了する前に経過してしまったならば、RHQサーバーはそのオペレーションをタイムアウトにし、それを失敗させることを検討します。注意:通常はすでに開始させられた根本の管理リソースオペレーションをアボートすることは不可能です。
+## view_operationScheduleDetails_fieldHelp_timeout = 時間間隔 - もし指定されていて、その間隔がスケジュールされたオペレーション実行が完了する前に経過してしまったならば、RHQサーバーはそのオペレーションをタイムアウトにし、それを失敗させることを検討します。注意:通常はすでに開始させられた根本の管理リソースオペレーションをアボートすることは不可能です。
view_operationScheduleDetails_field_description = 説明
-view_operationScheduleDetails_field_parameters = パラメータ
+view_operationScheduleDetails_field_parameters = パラメーター
view_operationScheduleDetails_field_timeout = タイムアウト
view_operationScheduleDetails_noParameters = このオペレーションはパラメーターを取りません
view_operationScheduleDetails_operationSchedule = オペレーションスケジュール
@@ -1668,7 +1666,6 @@ view_reportsTop_title = レポート
view_reports_alertDefinitions = アラート定義
view_reports_alertDefinitions_parentHover = クリックすると親のアラート定義へ行きます
view_reports_alertDefinitions_resTypeLoadError = テンプレートリソースタイプを取得できません - アラートテンプレートの閲覧ができません
-##view_reports_driftCompliance = Drift Compliance
view_reports_inventorySummary_failFetch = インベントリサマリーの取得に失敗しました
view_reports_platforms = プラットフォーム利用率
view_reports_subsystems = サブシステム
@@ -1708,22 +1705,22 @@ view_resource_monitor_graphs_lookupFailed = グラフのためのリソースの
view_resource_monitor_graphs_noneAvailable = 利用できるグラフはありません
view_resource_monitor_schedules_title = リソースメトリック収集スケジュール
view_resource_monitor_table_avg = 平均
-view_resource_monitor_table_last = 最後
+view_resource_monitor_table_last = 最新
view_resource_monitor_table_max = 最大
view_resource_monitor_table_min = 最小
view_resource_title_component_errors_tooltip = 管理コンポーネントエラーの表示。詳細はクリックしてください
view_resource_title_tagUpdateFailed = リソースタグの更新に失敗しました
view_searchBar_defaultPattern = パターンを名前を付けてください
-##view_searchBar_instructional_failSuggest = Failed to generate suggestions, see server log for possible errors
-view_searchBar_instructional_noSuggest = No matches, enter a different pattern
-##view_searchBar_instructional_refresh = Failed to retrieve saved searches, please refresh page
-##view_searchBar_savedSearch_delete = Successfully deleted saved search [{0}]
-##view_searchBar_savedSearch_failDelete = Failed to delete saved search with name [{0}]
-##view_searchBar_savedSearch_failFind = Failed to find saved search with name [{0}]
-##view_searchBar_savedSearch_failRename = Failed to rename saved search with name [{0}]
-##view_searchBar_savedSearch_failSave = Failed to create saved search with name [{0}]
-##view_searchBar_savedSearch_rename = Successfully renamed saved search to [{0}]
-##view_searchBar_savedSearch_save = Successfully created saved search [{0}]
+view_searchBar_instructional_failSuggest = サジェッションの作成に失敗しました。可能性のあるエラーをサーバーログから調べてください。
+view_searchBar_instructional_noSuggest = 一件も一致しませんでした。異なるパターンを入力してください。
+view_searchBar_instructional_refresh = 保存済みの検索の取り出しに失敗しました。ページをリフレッシュしてください。
+view_searchBar_savedSearch_delete = 保存済みの検索 [{0}] を削除するのに失敗しました
+view_searchBar_savedSearch_failDelete = [{0}]という名前の保存済み検索を削除するのに失敗しました
+view_searchBar_savedSearch_failFind = [{0}]という名前の保存済み検索を検索するのに失敗しました
+view_searchBar_savedSearch_failRename = [{0}]という名前の保存済みの検索の名前を変更するのに失敗しました
+view_searchBar_savedSearch_failSave = [{0}]という名前の保存済みの検索を作成するのに失敗しました
+view_searchBar_savedSearch_rename = 保存済み検索の名前を [{0}] へ変更するのに成功しました
+view_searchBar_savedSearch_save = [{0}] という名前の保存済み検索を作成するのに成功しました
view_searchGUI_loginStatus = ログインステータスが確定できません。サーバーの状態を調べてください
view_selector_assigned = 割り当てられた {0}
view_selector_available = 利用可能な {0}
@@ -1752,6 +1749,7 @@ view_tableSection_error_noId = テーブル [{0}] のレコードがで 'id' 属
view_table_drawFail = テーブル [{0}] の描画に失敗しました.
view_table_matchingRows = 一致した行: {0} (選択済み: {1})
view_table_totalRows = 合計行: {0} (選択済み: {1})
+view_table_totalRowsUnknown = 行の合計: ?
view_tabs_common_activity = アクティビティ
view_tabs_common_agent = エージェント
view_tabs_common_availability = アベイラビリティ
@@ -1764,7 +1762,7 @@ view_tabs_common_content = コンテント
view_tabs_common_current = 現在
view_tabs_common_dashboard = ダッシュボード
view_tabs_common_deployed = デプロイ済み
-##view_tabs_common_drift = Drift
+view_tabs_common_drift = ドリフト
view_tabs_common_events = イベント
view_tabs_common_graphs = グラフ
view_tabs_common_group_members = グループメンバー
@@ -1921,7 +1919,7 @@ widget_resourceFactoryWizard_infoStepName = リソース情報
widget_resourceFactoryWizard_infoStep_loadFail = アーキテクチャーを利用可能にするのに失敗しました
widget_resourceFactoryWizard_namePrompt = 新規リソース名
widget_resourceFactoryWizard_templatePrompt = コネクション設定テンプレート
-##widget_resourceFactoryWizard_timeoutHelp = xxxxxxx
+## widget_resourceFactoryWizard_timeoutHelp = タイムアウト間隔。もし指定されたならば(RHQエージェント上での)子のリソース作成のためのデフォルトタイムアウトを上書きします。大きなアプリケーションのデプロイのような長時間かかる作成アクションに特に有用です。通常、前回の試みのときにタイムアウトの失敗だった場合に使われます。
widget_resourceFactoryWizard_uploadFailure = ファイルアップロードの失敗
widget_resourceFactoryWizard_uploadFileStepName = リソースコンテントファイルのアップロード
widget_resourceFactoryWizard_uploadInProgress = アップロードは処理中です... 大きなファイルを配布するには数分かかることもあります。
12 years, 5 months
[rhq] Branch 'lkrejci/dissalow-alert-scripts-from-accessing-local-slsbs' - 4 commits - modules/enterprise
by lkrejci
modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardScriptPermissions.java | 6
modules/enterprise/server/client-api/pom.xml | 333 +++++++++-
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java | 4
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessInterceptor.java | 45 -
modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessPermission.java | 36 -
modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/security/test/EjbAccessTest.java | 175 +++++
modules/enterprise/server/client-api/src/test/resources/META-INF/ejb-jar.xml | 34 +
modules/enterprise/server/client-api/src/test/resources/META-INF/jboss.xml | 168 +++++
modules/enterprise/server/client-api/src/test/resources/hibernate.properties | 26
modules/enterprise/server/client-api/src/test/resources/jms-ra.rar |binary
modules/enterprise/server/client-api/src/test/resources/security.policy | 10
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java | 36 -
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessInterceptor.java | 46 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessPermission.java | 36 +
modules/enterprise/server/jar/src/main/resources/META-INF/ejb-jar.xml | 2
15 files changed, 860 insertions(+), 97 deletions(-)
New commits:
commit ca4757776c008f12a053c816d7b9cf8c6a310596
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Sat Dec 10 01:19:40 2011 +0100
Adding EjbAccessTest to make sure the CLI scripts can access the business
layer only through the designated interface. No direct local or remote
EJB access is possible.
diff --git a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardScriptPermissions.java b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardScriptPermissions.java
index 894c953..5822d44 100644
--- a/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardScriptPermissions.java
+++ b/modules/enterprise/binding/src/main/java/org/rhq/bindings/StandardScriptPermissions.java
@@ -20,6 +20,7 @@
package org.rhq.bindings;
import java.io.FilePermission;
+import java.io.SerializablePermission;
import java.lang.reflect.ReflectPermission;
import java.net.SocketPermission;
import java.security.Permission;
@@ -83,6 +84,11 @@ public class StandardScriptPermissions extends PermissionCollection {
add(new PropertyPermission("*", "read"));
add(new ReflectPermission("suppressAccessChecks"));
+
+ //these 2 are required for server-side scripts to be able to
+ //invoke remote EJBs.
+ add(new SerializablePermission("enableSubclassImplementation"));
+ add(new RuntimePermission("reflectionFactoryAccess"));
}
public void add(Permission permission) {
diff --git a/modules/enterprise/server/client-api/pom.xml b/modules/enterprise/server/client-api/pom.xml
index b125b3b..6932b60 100644
--- a/modules/enterprise/server/client-api/pom.xml
+++ b/modules/enterprise/server/client-api/pom.xml
@@ -17,9 +17,28 @@
<properties>
<persistence-api.version>1.0</persistence-api.version>
+ <rhq.server.datasource>java:/RHQDS</rhq.server.datasource>
+ <rhq.server.ds-mapping>PostgreSQL</rhq.server.ds-mapping>
+
+ <!-- dependency versions -->
+ <jboss-embeddable-ejb3.version>1.0.0.Alpha9</jboss-embeddable-ejb3.version>
+
+ <clean.db>true</clean.db>
</properties>
<dependencies>
+
+ <!-- Note, the test deps are intentionally placed above the other scoped deps because of classpath
+ reasons. Maven orders the [test] classpath in the order listed in the pom. We specifically
+ need the embeddable-ejb3 jar above the standard ejb3 jars because we need the embeddble packages
+ loaded when testing. -->
+ <dependency>
+ <groupId>jboss.jboss-embeddable-ejb3</groupId>
+ <artifactId>jboss-ejb3-all</artifactId>
+ <version>${jboss-embeddable-ejb3.version}</version>
+ <scope>test</scope>
+ </dependency>
+
<dependency>
<groupId>org.rhq</groupId>
<artifactId>rhq-script-bindings</artifactId>
@@ -48,21 +67,319 @@
<!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
<scope>provided</scope> <!-- by JBossAS -->
</dependency>
-
+
+ <!-- Test deps - this insane list of deps is needed to get the embedded JBoss server with RHQ server deployed running -->
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>test-utils</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-enterprise-server</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-enterprise-server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>hibernate</groupId>
+ <artifactId>hibernate3</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>hibernate-entitymanager</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-javamail_1.3.1_spec</artifactId>
+ <!-- The Sun javamail jar isn't available from a public repo due to licensing issues,
+ so use the Geronimo one instead. -->
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.4</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>jsp-api</artifactId>
+ <version>2.0</version>
+ <scope>test</scope>
+ </dependency>
+
+
+ <dependency>
+ <groupId>org.opensymphony.quartz</groupId>
+ <artifactId>quartz</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.opensymphony.quartz</groupId>
+ <artifactId>quartz-oracle</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-annotations-ejb3</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-cache</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-common</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <!-- includes the org.jboss.ejb3.StrictMaxPool class, which is needed by the PoolClass annotation used on some
+ of our SLSB's -->
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-ejb3</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-j2ee</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-jmx</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jboss-system</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jbosssx</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jbpm</artifactId>
+ <version>3.1.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>antlr</groupId>
+ <artifactId>antlr</artifactId>
+ <version>2.7.7</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>trove</groupId>
+ <artifactId>trove</artifactId>
+ <version>1.0.2</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>xerces</groupId>
+ <artifactId>xercesImpl</artifactId>
+ <version>2.8.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>net.sf.opencsv</groupId>
+ <artifactId>opencsv</artifactId>
+ <version>1.8</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>commons-jxpath</groupId>
+ <artifactId>commons-jxpath</artifactId>
+ <version>1.3</version>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- for the transaction interrupt EJB3 interceptor -->
+ <dependency>
+ <groupId>org.jboss.transaction</groupId>
+ <artifactId>jboss-jta</artifactId>
+ <!-- NOTE: The version is defined in the root POM's dependencyManagement section. -->
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>tomcat</groupId>
+ <artifactId>catalina</artifactId>
+ <version>5.5.20</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>tomcat</groupId>
+ <artifactId>tomcat-jk</artifactId>
+ <version>4.1.31</version>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- Needed by com.jboss.jbossnetwork.apl.actions.xml.XPathProcessor; TODO: Remove once APL has been excised. -->
+ <dependency>
+ <groupId>xalan</groupId>
+ <artifactId>xalan</artifactId>
+ <version>2.5.1</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>com.jcraft</groupId>
+ <artifactId>jsch</artifactId>
+ <version>0.1.29</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jettison-provider</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-links</artifactId>
+ <version>${resteasy.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.el</groupId>
+ <artifactId>jboss-el</artifactId>
+ <version>2.0.1.GA</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>2.3.18</version>
+ <scope>test</scope>
+ </dependency>
+
</dependencies>
<build>
+ <testResources>
+ <testResource>
+ <directory>src/test/resources</directory>
+ <filtering>true</filtering>
+ </testResource>
+ </testResources>
+
<plugins>
- <plugin>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
- <!-- <argLine>-Xdebug -Xnoagent -Djava.compiler=NONE
- -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y</argLine> -->
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <!-- Everything but the web service tests, this is the standard test execution -->
+ <configuration>
+ <excludedGroups>${rhq.testng.excludedGroups}</excludedGroups>
+ <groups>${rhq.testng.includedGroups}</groups>
+ <systemPropertyVariables>
+ <embeddedDeployment>true</embeddedDeployment>
+ <deploymentDirectory>target/test-classes</deploymentDirectory>
+ <hibernate.dialect>${rhq.test.ds.hibernate-dialect}</hibernate.dialect>
+ <clean.db>${clean.db}</clean.db>
+ </systemPropertyVariables>
+ <argLine>-Djava.security.manager -Djava.security.policy==target/test-classes/security.policy</argLine>
+ <additionalClasspathElements>
+ <!-- The below is required for tests to run against Oracle. -->
+ <additionalClasspathElement>${settings.localRepository}/com/oracle/ojdbc5/${ojdbc5.version}/ojdbc5-${ojdbc5.version}.jar</additionalClasspathElement>
+ </additionalClasspathElements>
</configuration>
- </plugin>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+
+ <!-- in order to get JMS to work properly in embedded test container, extract jms-rs.rar classes -->
+ <execution>
+ <id>Extract JMS classes from RAR needed for JMS tests</id>
+ <phase>process-classes</phase>
+ <configuration>
+ <tasks>
+ <unzip src="src/test/resources/jms-ra.rar" dest="target">
+ <patternset>
+ <include name="jms-ra.jar"/>
+ </patternset>
+ </unzip>
+ <unzip src="target/jms-ra.jar" dest="target/test-classes">
+ <patternset>
+ <include name="org/**"/>
+ </patternset>
+ </unzip>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
</plugins>
</build>
diff --git a/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/security/test/EjbAccessTest.java b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/security/test/EjbAccessTest.java
new file mode 100644
index 0000000..9c9d1bb
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/security/test/EjbAccessTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.client.security.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.SerializablePermission;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.security.PermissionCollection;
+import java.util.Collections;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import org.rhq.bindings.SandboxedScriptEngine;
+import org.rhq.bindings.ScriptEngineFactory;
+import org.rhq.bindings.StandardBindings;
+import org.rhq.bindings.StandardScriptPermissions;
+import org.rhq.bindings.util.PackageFinder;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.client.LocalClient;
+import org.rhq.enterprise.server.security.AllowEjbAccessPermission;
+import org.rhq.enterprise.server.test.AbstractEJB3Test;
+import org.rhq.enterprise.server.util.LookupUtil;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+@Test
+public class EjbAccessTest extends AbstractEJB3Test {
+
+ public void testEjbsAccessibleThroughPrivilegedCode() {
+ LookupUtil.getSubjectManager().getOverlord();
+ }
+
+ public void testEjbsAccessibleThroughLocalClient() throws ScriptException, IOException {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ engine.eval("SubjectManager.getSubjectByName('rhqadmin');");
+ }
+
+ public void testLocalEjbsInaccessibleThroughJndiLookup() throws ScriptException, IOException {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ try {
+ engine.eval(""
+ + "context = new javax.naming.InitialContext();\n"
+ + "subjectManager = context.lookup('SubjectManagerBean/local');\n"
+ + "subjectManager.getOverlord();");
+
+ Assert.fail("The script shouldn't have been able to call local SLSB method.");
+ } catch (ScriptException e) {
+ assert e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ }
+ }
+
+ public void testRemoteEjbsInaccessibleThroughJndiLookup() throws ScriptException, IOException {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ try {
+ engine.eval(""
+ + "context = new javax.naming.InitialContext();\n"
+ + "subjectManager = context.lookup('SubjectManagerBean/remote');\n"
+ + "subjectManager.getSubjectByName('rhqadmin');");
+
+ Assert.fail("The script shouldn't have been able to call remote SLSB method directly.");
+ } catch (ScriptException e) {
+ //TODO java.io.IOException: access denied (java.io.SerializablePermission enableSubclassImplementation)
+ assert e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ }
+ }
+
+ public void testScriptCantUseSessionManager() throws ScriptException, IOException {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ try {
+ engine.eval("org.rhq.enterprise.server.auth.SessionManager.getInstance();");
+
+ Assert.fail("The script shouldn't have been able to get instance of SessionManager.");
+ } catch (ScriptException e) {
+ assert e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ }
+ }
+
+ @SuppressWarnings("unused")
+ public void testScriptCantUseSessionManagerMethods() throws Exception {
+
+ //The code below cannot work in Rhino because as of now, Rhino modifies
+ //the return value of Class.getDeclaredField() to be null when there
+ //is a security manager installed.
+ //This has been filed as a bug at Oracle (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7119959)
+
+ if (true) {
+ return;
+ }
+
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ final ScriptEngine engine = getEngine(overlord);
+
+ class G {
+ private String obtainSessionManagerUsingReflection = ""
+ + "var sessionManagerClass = java.lang.Class.forName(\"org.rhq.enterprise.server.auth.SessionManager\");\n"
+ + "println(sessionManagerClass);\n"
+ + "var managerField = sessionManagerClass.getDeclaredField(\"_manager\");\n"
+ + "println(managerField);\n"
+ + "managerField.setAccessible(true);\n"
+ + "var manager = managerField.get(null);\n"
+ + "println(manager);\n"
+ + "manager.";
+
+ public void testInvoke(String methodCall) throws ScriptException {
+ String code = obtainSessionManagerUsingReflection + methodCall;
+
+ try {
+ engine.eval(code);
+ Assert.fail("The script shouldn't have been able to a method on a SessionManager.");
+ } catch (ScriptException e) {
+ assert e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ }
+ }
+ };
+ G manager = new G();
+
+ manager.testInvoke("getLastAccess(0);");
+ manager.testInvoke("getOverlord()");
+ manager.testInvoke("getSubject(2);");
+ manager.testInvoke("invalidate(0);");
+ manager.testInvoke("invalidate(\"\");");
+ manager.testInvoke("put(new org.rhq.core.domain.auth.Subject());");
+ manager.testInvoke("put(new org.rhq.core.domain.auth.Subject(), 0);");
+ }
+
+ private ScriptEngine getEngine(Subject subject) throws ScriptException, IOException {
+ StandardBindings bindings = new StandardBindings(new PrintWriter(System.out), new LocalClient(subject));
+ ScriptEngine engine = ScriptEngineFactory.getScriptEngine("JavaScript", new PackageFinder(Collections.<File>emptyList()), bindings);
+
+ PermissionCollection perms = new StandardScriptPermissions();
+ perms.add(new SerializablePermission("enableSubclassImplementation"));
+
+ return new SandboxedScriptEngine(engine, perms);
+ }
+}
diff --git a/modules/enterprise/server/client-api/src/test/resources/META-INF/ejb-jar.xml b/modules/enterprise/server/client-api/src/test/resources/META-INF/ejb-jar.xml
new file mode 100644
index 0000000..caa7513
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/test/resources/META-INF/ejb-jar.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
+ version="3.0">
+
+ <description>RHQ</description>
+ <display-name>RHQ</display-name>
+
+ <assembly-descriptor>
+
+ <!-- This interceptor checks that the alert CLI scripts don't try to circumvent our access objects by accessing the local SLSB beans directly. -->
+ <interceptor-binding>
+ <ejb-name>*</ejb-name>
+ <interceptor-class>org.rhq.enterprise.server.security.AllowEjbAccessInterceptor</interceptor-class>
+ </interceptor-binding>
+
+ <!-- all our EJB3 SLSBs can define custom @RequiredPermissions annotations for authorization checks-->
+ <interceptor-binding>
+ <ejb-name>*</ejb-name>
+ <interceptor-class>org.rhq.enterprise.server.authz.RequiredPermissionsInterceptor</interceptor-class>
+ </interceptor-binding>
+ <interceptor-binding>
+ <ejb-name>*</ejb-name>
+ <interceptor-class>org.rhq.enterprise.server.common.TransactionInterruptInterceptor</interceptor-class>
+ </interceptor-binding>
+ <interceptor-binding>
+ <ejb-name>*</ejb-name>
+ <interceptor-class>org.rhq.enterprise.server.common.PerformanceMonitorInterceptor</interceptor-class>
+ </interceptor-binding>
+
+ </assembly-descriptor>
+
+</ejb-jar>
diff --git a/modules/enterprise/server/client-api/src/test/resources/META-INF/jboss.xml b/modules/enterprise/server/client-api/src/test/resources/META-INF/jboss.xml
new file mode 100644
index 0000000..b7c5c66
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/test/resources/META-INF/jboss.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<jboss xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss_5_0.xsd"
+ version="3.0">
+
+ <enterprise-beans>
+
+ <!--
+ The AlertConditionLogManagerBean, AlertConditionManagerBean, AlertDampeningManagerBean,
+ AlertDefinitionManagerBean, AlertManagerBean, AuthorizationManagerBean, CachedConditionManagerBean, and
+ SubjectManager SLSB's are all invoked, either directly or indirectly, by the AlertConditionConsumerBean MDB.
+ Since MDB invocations are always done in new threads, using the default SLSB pool impl (ThreadlocalPool) would
+ cause a new instance of these SLSB's to be created every time they were invoked by AlertConditionConsumerBean.
+ This would be bad if only because existing instances would
+ not be reused, but it is really bad because the instances would also never get destroyed, causing heap space to
+ gradually leak until the Server eventually ran out of memory. Hence, we must use a StrictMaxPool, which
+ will use a fixed pool of instances of this SLSB, instead of a ThreadlocalPool. Because lots of alert
+ definitions could result in many concurrent calls to AlertConditionConsumerBean, and because most of these
+ SLSB's are also invoked by other callers (i.e. Agents, GUI's, or CLI's), we set the max pool size to 100 to
+ minimize the chances of AlertConditionConsumerBean invocations, which are the most critical, from having to
+ block and potentially getting backed up in the queue. We set the pool timeout extremely high to essentially
+ prevent queued callers from timing out. Note, MDB's are configured in ejb3-interceptors-aop.xml to use a strict
+ max pool with a max of 15 and a timeout of 10000, so AlertConditionConsumerBean might become a bottleneck for
+ systems with a lot of alerts defined. (ips, 05/10/11)
+
+ For more details, see https://bugzilla.redhat.com/show_bug.cgi?id=693232 .
+ -->
+
+ <session>
+ <ejb-name>AlertConditionLogManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>AlertConditionManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>AlertDampeningManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>AlertDefinitionManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>AlertManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>AuthorizationManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>CachedConditionManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>SubjectManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>100</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+
+ <!--
+ The CacheConsistencyManagerBean, CloudManagerBean, ServerManagerBean, StatusManagerBean, and SystemManagerBean
+ SLSB's are all invoked, either directly or indirectly, by EJB timers. Since EJB timer invocations are always
+ done in new threads, using the default SLSB pool impl (ThreadlocalPool) would cause a new instance of this
+ SLSB to be created every time it was invoked by an EJB timer. This would be bad if only because an existing
+ instance would not be reused, but it is really bad because the instance would also never get destroyed, causing
+ heap space to gradually leak until the Server eventually ran out of memory. Hence, we must use a
+ StrictMaxPool, which will use a fixed pool of instances of this SLSB, instead of a ThreadlocalPool.
+ The timer invocations will only require one or two instances at any given time, but because most of these
+ SLSB's are also invoked by other callers (i.e. Agents, GUI's, or CLI's, we set the max pool size to 30, to
+ minimize the chances of EJB timer invocations, which are the most critical, from having to block and
+ potentially getting backed up in the queue. We set the pool timeout extremely high to essentially prevent
+ queued callers from timing out. (ips, 05/10/11)
+
+ For more details, see https://bugzilla.redhat.com/show_bug.cgi?id=693232 .
+ -->
+
+ <session>
+ <ejb-name>CacheConsistencyManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>30</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>CloudManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>30</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>ServerManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>30</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>StatusManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>30</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ <session>
+ <ejb-name>SystemManagerBean</ejb-name>
+ <pool-config>
+ <pool-class>org.jboss.ejb3.StrictMaxPool</pool-class>
+ <pool-max-size>30</pool-max-size>
+ <pool-timeout>1000000000</pool-timeout>
+ </pool-config>
+ </session>
+
+ </enterprise-beans>
+
+</jboss>
diff --git a/modules/enterprise/server/client-api/src/test/resources/hibernate.properties b/modules/enterprise/server/client-api/src/test/resources/hibernate.properties
new file mode 100644
index 0000000..1951b84
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/test/resources/hibernate.properties
@@ -0,0 +1,26 @@
+# FOR SOME STRANGE REASON, THIS FILE NEEDS TO BE HERE FOR THE HIBERNATE TO CORRECTLY
+# INITIALIZE. I DON'T KNOW WHY THE STANDARD default.persistence.properties FILE DOESN'T
+# WORK IN THIS MODULE.
+
+hibernate.transaction.manager_lookup_class=org.hibernate.transaction.JBossTransactionManagerLookup
+#hibernate.connection.release_mode=after_statement
+#hibernate.transaction.flush_before_completion=false
+#hibernate.transaction.auto_close_session=false
+#hibernate.query.factory_class=org.hibernate.hql.ast.ASTQueryTranslatorFactory
+#hibernate.hbm2ddl.auto=create-drop
+#hibernate.hbm2ddl.auto=create
+hibernate.cache.provider_class=org.hibernate.cache.HashtableCacheProvider
+# Clustered cache with TreeCache
+#hibernate.cache.provider_class=org.jboss.ejb3.entity.TreeCacheProviderHook
+#hibernate.treecache.mbean.object_name=jboss.cache:service=EJB3EntityTreeCache
+#hibernate.dialect=org.hibernate.dialect.HSQLDialect
+hibernate.jndi.java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
+hibernate.jndi.java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
+hibernate.bytecode.use_reflection_optimizer=false
+# I don't think this is honored, but EJB3Deployer uses it
+hibernate.bytecode.provider=javassist
+hibernate.jdbc.use_streams_for_binary=true
+hibernate.show_sql=false
+hibernate.format_sql=true
+hibernate.default_batch_fetch_size=16
+hibernate.jdbc.batch_size=20
diff --git a/modules/enterprise/server/client-api/src/test/resources/jms-ra.rar b/modules/enterprise/server/client-api/src/test/resources/jms-ra.rar
new file mode 100644
index 0000000..c4807c6
Binary files /dev/null and b/modules/enterprise/server/client-api/src/test/resources/jms-ra.rar differ
diff --git a/modules/enterprise/server/client-api/src/test/resources/security.policy b/modules/enterprise/server/client-api/src/test/resources/security.policy
new file mode 100644
index 0000000..8860b47
--- /dev/null
+++ b/modules/enterprise/server/client-api/src/test/resources/security.policy
@@ -0,0 +1,10 @@
+// We need the SecurityManager installed to enable sandboxing of CLI scripts
+// but we don't define any other security measures on the RHQ server itself.
+//
+// Granting all permissions allows us to run the RHQ server as if no security
+// manager was in place (which is assumed by default by JBoss AS) but be able
+// to use it when we need it for our own purposes.
+
+grant {
+ permission java.security.AllPermission;
+};
commit ef5673fb4482fd08cdec672740afc2f12a3d2b0b
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Sat Dec 10 01:17:50 2011 +0100
Making SessionManager behave the same as EJBs wrt the AccessEjbPermission.
It is very security-sensitive so we shouldn't allow scripts to just access
it and cause havoc in user sessions.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java
index 39b6076..b4a19b7 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java
@@ -25,30 +25,42 @@ import java.util.Map;
import java.util.Random;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.enterprise.server.security.AllowEjbAccessPermission;
import org.rhq.enterprise.server.util.LookupUtil;
/**
* This is the JON Server's own session ID generator. It is outside any container-provided session mechanism. Its sole
* purpose is to provide session IDs to logged in {@link Subject}s. It will timeout those sessions regardless of any
* container-provided session-timeout mechanism.
- *
+ * <p>
+ * Because this is a very security-sensitive class, any public method requires the caller to
+ * have the {@link AllowEjbAccessPermission} as any other calls to the EJB layer. This is so that the
+ * malicious users can't trick the EJB layer into thinking that some users are logged in or log out other
+ * users.
+ * <p>
+ * Also, for security reasons, this class is final so that malicious code can't subclass it and modify its
+ * behavior.
+ *
* <p>This object is a {@link #getInstance() singleton}.</p>
*/
-public class SessionManager {
+public final class SessionManager {
+
+ private static final AllowEjbAccessPermission ACCESS_PERMISSION = new AllowEjbAccessPermission();
+
/**
* Our source for random session IDs.
*/
- private static Random _random = new Random();
+ private static final Random _random = new Random();
/**
* Our session cache that is keyed on the session ID.
*/
- private static Map<Integer, AuthSession> _cache = new HashMap<Integer, AuthSession>();
+ private static final Map<Integer, AuthSession> _cache = new HashMap<Integer, AuthSession>();
/**
* The singleton instance
*/
- private static SessionManager _manager = new SessionManager();
+ private static final SessionManager _manager = new SessionManager();
/**
* The timeout for all user sessions.
@@ -78,7 +90,7 @@ public class SessionManager {
/**
* Return the singleton object.
- *
+ *
* @return the {@link SessionManager}
*/
public static SessionManager getInstance() {
@@ -95,6 +107,7 @@ public class SessionManager {
* sessionId will be assigned.
*/
public Subject put(Subject subject) {
+ checkPermission();
return put(subject, DEFAULT_TIMEOUT);
}
@@ -108,6 +121,7 @@ public class SessionManager {
* that Subject is overlord). The sessionId will be assigned.
*/
public synchronized Subject put(Subject subject, long timeout) {
+ checkPermission();
Integer key;
do {
@@ -141,6 +155,7 @@ public class SessionManager {
* @throws SessionTimeoutException
*/
public synchronized Subject getSubject(int sessionId) throws SessionNotFoundException, SessionTimeoutException {
+ checkPermission();
Integer id = new Integer(sessionId);
AuthSession session = _cache.get(id);
@@ -162,6 +177,7 @@ public class SessionManager {
* @param sessionId session id to invalidate
*/
public synchronized void invalidate(int sessionId) {
+ checkPermission();
_cache.remove(new Integer(sessionId));
// while we are here, let's go through the entire session cache and remove expired sessions
@@ -187,6 +203,7 @@ public class SessionManager {
* @param username username for the sessions to be invalidated
*/
public synchronized void invalidate(String username) {
+ checkPermission();
List<Integer> doomedSessionIds = new ArrayList<Integer>(_cache.size());
for (AuthSession s : _cache.values()) {
if (username.equals(s.getSubject(false).getName())) {
@@ -201,6 +218,7 @@ public class SessionManager {
}
public long getlastAccess(int sessionId) {
+ checkPermission();
AuthSession session = _cache.get(sessionId);
if (session == null) {
return -1;
@@ -209,6 +227,7 @@ public class SessionManager {
}
public Subject getOverlord() {
+ checkPermission();
if (overlordSubject == null) {
overlordSubject = LookupUtil.getSubjectManager().getSubjectById(OVERLORD_SUBJECT_ID);
@@ -256,4 +275,9 @@ public class SessionManager {
return copy;
}
+
+ private static void checkPermission() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) sm.checkPermission(ACCESS_PERMISSION);
+ }
}
\ No newline at end of file
commit 0b0632e5641e91f3549e5addbc2c18bb27132c5a
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Sat Dec 10 01:16:26 2011 +0100
moving the EJB access permission and the interceptor back to the server
jar.
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessInterceptor.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessInterceptor.java
deleted file mode 100644
index d3dcdb2..0000000
--- a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessInterceptor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.enterprise.client.security;
-
-import java.security.AccessController;
-import java.security.Permission;
-
-import javax.interceptor.AroundInvoke;
-import javax.interceptor.InvocationContext;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class AllowEjbAccessInterceptor {
-
- private static final Permission PERM = new AllowEjbAccessPermission();
-
- @AroundInvoke
- public Object intercept(InvocationContext invocationContext) throws Exception {
- //check that the caller has permissions to access the EJBs.
- //normal code does, only alert CLI scripts that try to circumvent our
- //manager proxies don't.
- AccessController.checkPermission(PERM);
- return invocationContext.proceed();
- }
-}
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessPermission.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessPermission.java
deleted file mode 100644
index 19ee165..0000000
--- a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/security/AllowEjbAccessPermission.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.enterprise.client.security;
-
-import java.security.BasicPermission;
-
-/**
- *
- *
- * @author Lukas Krejci
- */
-public class AllowEjbAccessPermission extends BasicPermission {
-
- private static final long serialVersionUID = 1L;
-
- public AllowEjbAccessPermission() {
- super("rhq.allow.ejb.access");
- }
-}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessInterceptor.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessInterceptor.java
new file mode 100644
index 0000000..350453fb
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessInterceptor.java
@@ -0,0 +1,46 @@
+/*
+ * 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.security;
+
+import java.security.Permission;
+
+import javax.interceptor.AroundInvoke;
+import javax.interceptor.InvocationContext;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class AllowEjbAccessInterceptor {
+
+ private static final Permission PERM = new AllowEjbAccessPermission();
+
+ @AroundInvoke
+ public Object intercept(InvocationContext invocationContext) throws Exception {
+ //check that the caller has permissions to access the EJBs.
+ //normal code does, only alert CLI scripts that try to circumvent our
+ //manager proxies don't.
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) sm.checkPermission(PERM);
+
+ return invocationContext.proceed();
+ }
+}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessPermission.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessPermission.java
new file mode 100644
index 0000000..67f418d
--- /dev/null
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessPermission.java
@@ -0,0 +1,36 @@
+/*
+ * 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.security;
+
+import java.security.BasicPermission;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class AllowEjbAccessPermission extends BasicPermission {
+
+ private static final long serialVersionUID = 1L;
+
+ public AllowEjbAccessPermission() {
+ super("rhq.allow.ejb.access");
+ }
+}
diff --git a/modules/enterprise/server/jar/src/main/resources/META-INF/ejb-jar.xml b/modules/enterprise/server/jar/src/main/resources/META-INF/ejb-jar.xml
index 2700ae3..caa7513 100644
--- a/modules/enterprise/server/jar/src/main/resources/META-INF/ejb-jar.xml
+++ b/modules/enterprise/server/jar/src/main/resources/META-INF/ejb-jar.xml
@@ -12,7 +12,7 @@
<!-- This interceptor checks that the alert CLI scripts don't try to circumvent our access objects by accessing the local SLSB beans directly. -->
<interceptor-binding>
<ejb-name>*</ejb-name>
- <interceptor-class>org.rhq.enterprise.client.security.AllowEjbAccessInterceptor</interceptor-class>
+ <interceptor-class>org.rhq.enterprise.server.security.AllowEjbAccessInterceptor</interceptor-class>
</interceptor-binding>
<!-- all our EJB3 SLSBs can define custom @RequiredPermissions annotations for authorization checks-->
commit 248f6d5a2e08b7367490a47b892ccf8080fa1545
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Wed Dec 7 17:50:25 2011 +0100
be paranoid even in the comments ;)
diff --git a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
index 7fcdac3..e9f1e4d 100644
--- a/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
+++ b/modules/enterprise/server/client-api/src/main/java/org/rhq/enterprise/client/LocalClientProxy.java
@@ -62,7 +62,9 @@ public class LocalClientProxy extends AbstractRhqFacadeProxy<LocalClient> {
//run this through the privileged block to elevate the privs of the script
//the scripts don't have the AllowEjbAccessPermission but this code has
- //all perms.
+ //all perms (or at least all perms assigned to it by the current context,
+ //which at the time of writing is defined by the rhq-server.policy file
+ //which gives all code all permissions).
return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
12 years, 5 months
[rhq] modules/enterprise
by Simeon Pinder
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java | 28 +++++++---
1 file changed, 21 insertions(+), 7 deletions(-)
New commits:
commit 0753aae5cab0a3abd3997d093dad1d4866091619
Author: Simeon Pinder <spinder(a)redhat.com>
Date: Fri Dec 9 16:37:56 2011 -0500
[BZ 754693] ensure load of lazy references and update LDAP role relationship purge.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java
index ec3e998..6413b89 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java
@@ -22,7 +22,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -183,18 +182,27 @@ public class RoleManagerBean implements RoleManagerLocal, RoleManagerRemote {
for (int roleId : doomedRoleIds) {
Role doomedRole = entityManager.find(Role.class, roleId);
+ //remove attached Subjects
Set<Subject> subjectsToUnhook = new HashSet<Subject>(doomedRole.getSubjects()); // avoid concurrent mod exception
for (Subject doomedSubjectRelationship : subjectsToUnhook) {
doomedRole.removeSubject(doomedSubjectRelationship);
entityManager.merge(doomedSubjectRelationship);
}
+ //remove attached ResourceGroups
Set<ResourceGroup> groupsToUnhook = new HashSet<ResourceGroup>(doomedRole.getResourceGroups()); // avoid concurrent mod exception
for (ResourceGroup doomedResourceGroupRelationship : groupsToUnhook) {
doomedRole.removeResourceGroup(doomedResourceGroupRelationship);
entityManager.merge(doomedResourceGroupRelationship);
}
+ //remove attached LDAP Subjects
+ Set<Subject> ldapSubjectsToUnhook = new HashSet<Subject>(doomedRole.getLdapSubjects()); // avoid concurrent mod exception
+ for (Subject doomedLdapSubjectRelationship : ldapSubjectsToUnhook) {
+ doomedRole.removeLdapSubject(doomedLdapSubjectRelationship);
+ entityManager.merge(doomedLdapSubjectRelationship);
+ }
+
doomedRole = entityManager.merge(doomedRole);
if (doomedRole.getFsystem()) {
@@ -202,7 +210,13 @@ public class RoleManagerBean implements RoleManagerLocal, RoleManagerRemote {
}
alertNotificationManager.cleanseAlertNotificationByRole(doomedRole.getId());
- entityManager.remove(doomedRole); // there should not be any cascading - this does not touch subjects/groups
+ // Fetch the lazy Sets on the Role to be returned.
+ //[BZ 754693:] we must fetch the lazy sets of LDAP Groups here for correct cascade removal
+ doomedRole.getResourceGroups().size();
+ doomedRole.getSubjects().size();
+ doomedRole.getLdapGroups().size();
+
+ entityManager.remove(doomedRole);
}
}
@@ -432,8 +446,8 @@ public class RoleManagerBean implements RoleManagerLocal, RoleManagerRemote {
}
for (LdapGroup ldapGroup : newLdapGroups) {
- LdapGroup attachedLdapGroup = (ldapGroup.getId() != 0) ?
- entityManager.find(LdapGroup.class, ldapGroup.getId()) : null;
+ LdapGroup attachedLdapGroup = (ldapGroup.getId() != 0) ? entityManager.find(LdapGroup.class,
+ ldapGroup.getId()) : null;
if (attachedLdapGroup == null) {
ldapGroup.setRole(attachedRole);
entityManager.persist(ldapGroup);
@@ -669,8 +683,8 @@ public class RoleManagerBean implements RoleManagerLocal, RoleManagerRemote {
+ "], but subject was not found");
}
if (doomedSubject.getFsystem() || (authorizationManager.isSystemSuperuser(doomedSubject))) {
- throw new PermissionException("You cannot remove user[" + doomedSubject.getName()
- + "] from role[" + roleId + "] - roles are fixed for this user");
+ throw new PermissionException("You cannot remove user[" + doomedSubject.getName() + "] from role["
+ + roleId + "] - roles are fixed for this user");
}
role.removeSubject(doomedSubject);
}
@@ -768,7 +782,7 @@ public class RoleManagerBean implements RoleManagerLocal, RoleManagerRemote {
CriteriaQueryGenerator generator = new CriteriaQueryGenerator(subject, criteria);
CriteriaQueryRunner<Role> queryRunner = new CriteriaQueryRunner<Role>(criteria, generator, entityManager);
- @SuppressWarnings({"UnnecessaryLocalVariable"})
+ @SuppressWarnings({ "UnnecessaryLocalVariable" })
PageList<Role> roles = queryRunner.execute();
return roles;
12 years, 5 months
[rhq] 3 commits - modules/core modules/enterprise
by Jay Shaughnessy
modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftConfigurationDefinition.java | 7 ++--
modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftDefinition.java | 4 +-
modules/core/domain/src/main/java/org/rhq/core/domain/drift/Filter.java | 2 -
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java | 17 +++++++---
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java | 7 +++-
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java | 9 ++---
6 files changed, 30 insertions(+), 16 deletions(-)
New commits:
commit cfe30b134dd06cca8d22c0eacdc994a6021a73f4
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Dec 9 14:21:28 2011 -0500
[Bug 755580 - Unable to add 2nd file-system drift]
The drift definition name and description are no longer preset to the
template name and description. The user will now need to enter a
name. This should help prevent duplicate names, or mistakenly using the
template's name. Also, the exception thrown on duplicate naming has
been updated to indicate that that may in fact be the problem.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java
index c7ad7ed..c2139c1 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java
@@ -36,6 +36,7 @@ import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.definition.ConfigurationTemplate;
+import org.rhq.core.domain.drift.DriftConfigurationDefinition;
import org.rhq.core.domain.drift.DriftDefinitionTemplate;
import org.rhq.enterprise.gui.coregui.client.components.wizard.AbstractWizardStep;
import org.rhq.enterprise.gui.coregui.client.util.FormUtility;
@@ -130,7 +131,11 @@ public class DriftAddDefinitionWizardInfoStep extends AbstractWizardStep {
DriftDefinitionTemplate selectedTemplate = templatesMap.get(key);
wizard.setSelectedTemplate(selectedTemplate);
- wizard.setNewStartingConfiguration(selectedTemplate.createConfiguration());
+ Configuration startingConfig = selectedTemplate.createConfiguration();
+ // don't propogate the template name or desc to the def, force the user to enter a name, and optionally, a def
+ startingConfig.remove(DriftConfigurationDefinition.PROP_NAME);
+ startingConfig.remove(DriftConfigurationDefinition.PROP_DESCRIPTION);
+ wizard.setNewStartingConfiguration(startingConfig);
String description = selectedTemplate.getDescription();
description = (null == description) ? MSG.common_val_none() : description;
form.getItem("Description").setValue(description);
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 6af05d9..c06f7a4 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
@@ -589,12 +589,13 @@ public class DriftManagerBean implements DriftManagerLocal, DriftManagerRemote {
}
try {
- AgentClient agent = agentManager.getAgentClient(subjectManager.getOverlord(), driftDef.getResource().getId());
+ AgentClient agent = agentManager.getAgentClient(subjectManager.getOverlord(), driftDef.getResource()
+ .getId());
DriftAgentService driftService = agent.getDriftAgentService();
driftService.pinSnapshot(driftDef.getResource().getId(), driftDef.getName(), snapshot);
} catch (Exception e) {
- log.warn("Unable to notify agent that DriftDefinition[driftDefinitionId: " + driftDefId +
- ", driftDefinitionName: " + driftDef.getName() + "] has been pinned. The agent may be down.", e);
+ log.warn("Unable to notify agent that DriftDefinition[driftDefinitionId: " + driftDefId
+ + ", driftDefinitionName: " + driftDef.getName() + "] has been pinned. The agent may be down.", e);
}
}
@@ -745,7 +746,7 @@ public class DriftManagerBean implements DriftManagerLocal, DriftManagerRemote {
break;
} else {
throw new IllegalArgumentException(
- "You cannot change an existing drift definition's base directory or includes/excludes filters.");
+ "A new definition must have a unique name. An existing definition cannot update it's base directory or includes/excludes filters.");
}
}
}
commit 5938a790c0aeb32865f4b115e721de22220ca100
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Dec 9 14:05:05 2011 -0500
[Bug 765592 - drift diff is different depending on order in which files are selected]
Fixed, the new changes will now always get the "+" prefixes and the older
will get the "-" prefixes.
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 f941c6f..b3de83f 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
@@ -267,11 +267,18 @@ public class DriftCarouselView extends BookmarkableCarousel implements DetailsVi
public void executeAction(Object actionValue) {
- String id1 = selectedRecords.get(0).getAttribute(DriftDataSource.ATTR_ID);
- String id2 = selectedRecords.get(1).getAttribute(DriftDataSource.ATTR_ID);
- final String path = selectedRecords.get(0).getAttribute(DriftDataSource.ATTR_PATH);
-
- GWTServiceLookup.getDriftService().generateUnifiedDiffByIds(id1, id2,
+ Record record1 = selectedRecords.get(0);
+ Record record2 = selectedRecords.get(1);
+ final String path = record1.getAttribute(DriftDataSource.ATTR_PATH);
+ String id1 = record1.getAttribute(DriftDataSource.ATTR_ID);
+ String id2 = record2.getAttribute(DriftDataSource.ATTR_ID);
+ // regardless of selection order, compare the same way, showing newest changes with '+' signs
+ int version1 = record1.getAttributeAsInt(DriftDataSource.ATTR_CHANGESET_VERSION);
+ int version2 = record2.getAttributeAsInt(DriftDataSource.ATTR_CHANGESET_VERSION);
+ String diffOldId = (version1 < version2) ? id1 : id2;
+ String diffNewId = (version1 < version2) ? id2 : id1;
+
+ GWTServiceLookup.getDriftService().generateUnifiedDiffByIds(diffOldId, diffNewId,
new AsyncCallback<FileDiffReport>() {
@Override
public void onFailure(Throwable caught) {
commit 84c83f8760f6f37f7b7eed1b5e27346691901c11
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Dec 9 14:03:21 2011 -0500
[Bug 755673 - Adding new drift: includes/excludes - Path property required?]
It's now optional. It will be set to "." by default in the GUI. But even
if omitted will again get set to "." at the lower levels.
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftConfigurationDefinition.java b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftConfigurationDefinition.java
index 9829480..2b21f03 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftConfigurationDefinition.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftConfigurationDefinition.java
@@ -519,8 +519,8 @@ public class DriftConfigurationDefinition implements Serializable {
private static PropertyDefinitionSimple createIncludePath(boolean readOnly) {
String name = PROP_PATH;
- String description = "A file system path that can be a directory or a file. The path is assumed to be relative to the base directory of the drift definition.";
- boolean required = true;
+ String description = "A file system directory path that is relative to (a sub-directory of) the base directory of the drift definition. The default is '.', the base directory itself.";
+ boolean required = false;
PropertySimpleType type = PropertySimpleType.STRING;
PropertyDefinitionSimple pd = new PropertyDefinitionSimple(name, description, required, type);
@@ -529,6 +529,7 @@ public class DriftConfigurationDefinition implements Serializable {
pd.setSummary(true);
pd.setOrder(0);
pd.setAllowCustomEnumeratedValue(false);
+ pd.setDefaultValue(".");
RegexConstraint constraint = new RegexConstraint();
constraint.setDetails(PROP_FILTER_PATH_REGEX_PATTERN);
@@ -539,7 +540,7 @@ public class DriftConfigurationDefinition implements Serializable {
private static PropertyDefinitionSimple createIncludePattern(boolean readOnly) {
String name = PROP_PATTERN;
- String description = "Pathname pattern that must match for the items in the directory path to be included. '*' matches zero or more characters, '?' matches one character. For example, '*.txt' (no quotes) will match text files in the filter's path directory. '**/*.txt' will match text files in any subdirectory below the filter's path directory";
+ String description = "Pathname pattern that must match for the items in the directory path to be included. '*' matches zero or more characters, '?' matches one character. For example, '*.txt' (no quotes) will match text files in the filter's path directory. '**/*.txt' will match text files in any subdirectory below the filter's path directory.";
boolean required = false;
PropertySimpleType type = PropertySimpleType.STRING;
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftDefinition.java b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftDefinition.java
index a104843..92015ab 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftDefinition.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/DriftDefinition.java
@@ -149,7 +149,7 @@ public class DriftDefinition implements Serializable {
name = (null != name) ? name.trim() : name;
if (null == name || "".equals(name)) {
- throw new IllegalArgumentException("Drift congig name can not be null or empty");
+ throw new IllegalArgumentException("Drift definition name can not be null or empty");
}
this.name = name;
@@ -534,7 +534,7 @@ public class DriftDefinition implements Serializable {
List<Filter> filters = new ArrayList<Filter>();
for (Property property : filtersListProperty.getList()) {
PropertyMap filter = (PropertyMap) property;
- filters.add(new Filter(filter.getSimpleValue(DriftConfigurationDefinition.PROP_PATH, ""), filter
+ filters.add(new Filter(filter.getSimpleValue(DriftConfigurationDefinition.PROP_PATH, "."), filter
.getSimpleValue(DriftConfigurationDefinition.PROP_PATTERN, "")));
}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/Filter.java b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/Filter.java
index 96797ca..b57eb32 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/drift/Filter.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/drift/Filter.java
@@ -42,7 +42,7 @@ public class Filter implements Serializable {
public void setPath(String path) {
if (path == null) {
- this.path = "";
+ this.path = ".";
} else {
this.path = path;
}
12 years, 5 months