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/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/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
| 210
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/DriftFilesSender.java
| 37
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/test/java/org/rhq/core/pc/drift/DriftClientTestStub.java
| 11
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/DriftFilesSenderTest.java
| 40
modules/core/plugin-container/src/test/java/org/rhq/core/pc/drift/DriftManagerTest.java
| 2
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/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/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/drift/DriftCarouselMemberView.java
| 1
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/DriftCarouselView.java
| 21
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/drift/wizard/DriftAddDefinitionWizardInfoStep.java
| 7
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/resources/org/rhq/enterprise/gui/coregui/client/Messages_ja.properties
| 508
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/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/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/client-api/pom.xml
| 7
modules/enterprise/server/client-api/src/test/java/org/rhq/enterprise/client/security/test/EjbAccessTest.java
| 111
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/container-lib/pom.xml
| 5
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingContextDecorator.java
| 237
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryBuilder.java
| 180
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryDecorator.java
| 60
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AllowRhqServerInternalsAccessPermission.java
| 36
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringContext.java
| 104
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringInitialContextFactoryDecorator.java
| 54
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstaller.java
| 45
modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstallerMBean.java
| 32
modules/enterprise/server/container/pom.xml
| 19
modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-service.xml
| 7
modules/enterprise/server/container/src/main/resources/jbossas/server/default/deploy/rhq-cache-service.xml
| 170
modules/enterprise/server/container/src/main/scripts/rhq-container.build.xml
| 21
modules/enterprise/server/ear/pom.xml
| 69
modules/enterprise/server/jar/pom.xml
| 39
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/auth/SessionManager.java
| 4
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/authz/RoleManagerBean.java
| 28
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/drift/DriftManagerBean.java
| 9
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/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
| 6
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/jar/src/test/resources/embedded-jboss-beans.xml
| 10
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/src/main/java/org/rhq/plugins/jbossas/JBossASDiscoveryComponent.java
| 96
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/test-utils/src/main/java/org/rhq/test/DuplicatePackagesDetector.java
| 194
pom.xml
| 3
129 files changed, 15240 insertions(+), 2503 deletions(-)
New commits:
commit f2803b9046bfe26c9cbc80ec15bd458ac4f46db5
Merge: 8f9acfd 35fcafb
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Dec 19 19:56:15 2011 +0100
Merge branch 'master' into
lkrejci/dissalow-alert-scripts-from-accessing-local-slsbs
commit 8f9acfd64f725fb1ce4d1fafb3e44fde57de4b99
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Mon Dec 19 19:55:08 2011 +0100
Revamped the script security to apply the checks at the JNDI lookup level
to support restricting access to EntityManager or a datasource.
diff --git a/modules/enterprise/server/client-api/pom.xml
b/modules/enterprise/server/client-api/pom.xml
index 6932b60..4dfd109 100644
--- a/modules/enterprise/server/client-api/pom.xml
+++ b/modules/enterprise/server/client-api/pom.xml
@@ -93,6 +93,13 @@
</dependency>
<dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-container-lib</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. -->
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
index 9c9d1bb..dc1fc83 100644
---
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
@@ -22,9 +22,6 @@ 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;
@@ -41,9 +38,9 @@ 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;
+import org.rhq.jndi.AllowRhqServerInternalsAccessPermission;
/**
*
@@ -78,7 +75,7 @@ public class EjbAccessTest extends AbstractEJB3Test {
Assert.fail("The script shouldn't have been able to call local SLSB
method.");
} catch (ScriptException e) {
- assert e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ checkIsDesiredSecurityException(e);
}
}
@@ -95,66 +92,34 @@ public class EjbAccessTest extends AbstractEJB3Test {
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());
+ checkIsDesiredSecurityException(e);
}
}
- 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.";
+ private String sessionManager = ""
+ +
"org.rhq.enterprise.server.auth.SessionManager.getInstance().";
public void testInvoke(String methodCall) throws ScriptException {
- String code = obtainSessionManagerUsingReflection + methodCall;
+ String code = sessionManager + methodCall;
try {
engine.eval(code);
- Assert.fail("The script shouldn't have been able to a method
on a SessionManager.");
+ Assert.fail("The script shouldn't have been able to call a
method on a SessionManager: " + methodCall);
} catch (ScriptException e) {
- assert
e.getMessage().contains(AllowEjbAccessPermission.class.getName());
+ checkIsDesiredSecurityException(e);
}
}
};
G manager = new G();
- manager.testInvoke("getLastAccess(0);");
+ manager.testInvoke("getlastAccess(0);");
manager.testInvoke("getOverlord()");
manager.testInvoke("getSubject(2);");
manager.testInvoke("invalidate(0);");
@@ -163,6 +128,57 @@ public class EjbAccessTest extends AbstractEJB3Test {
manager.testInvoke("put(new org.rhq.core.domain.auth.Subject(), 0);");
}
+ public void testScriptCantObtainRawJDBCConnectionsWithoutCredentials() throws
Exception {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ try {
+ engine.eval(""
+ + "context = new javax.naming.InitialContext();\n"
+ + "datasource = context.lookup('java:/RHQDS');\n"
+ + "con = datasource.getConnection();");
+
+ Assert.fail("The script shouldn't have been able to obtain the
datasource from the JNDI.");
+ } catch (ScriptException e) {
+ checkIsDesiredSecurityException(e);
+ }
+ }
+
+ public void testScriptCantUseEntityManager() throws Exception {
+ Subject overlord = LookupUtil.getSubjectManager().getOverlord();
+
+ ScriptEngine engine = getEngine(overlord);
+
+ try {
+ engine.eval(""
+ + "context = new javax.naming.InitialContext();\n"
+ + "entityManagerFactory =
context.lookup('java:/RHQEntityManagerFactory');\n"
+ + "entityManager =
entityManagerFactory.createEntityManager();\n"
+ +
"entityManager.find(java.lang.Class.forName('org.rhq.core.domain.resource.Resource'),
java.lang.Integer.valueOf('10001'));");
+
+ Assert.fail("The script shouldn't have been able to use the
EntityManager.");
+ } catch (ScriptException e) {
+ checkIsDesiredSecurityException(e);
+ }
+
+ //try harder with manually specifying the initial context factory
+ try {
+ engine.eval(""
+ + "env = new java.util.Hashtable();"
+ + "env.put('java.naming.factory.initial',
'org.jnp.interfaces.LocalOnlyContextFactory');"
+ + "env.put('java.naming.factory.url.pkgs',
'org.jboss.naming:org.jnp.interfaces');"
+ + "context = new javax.naming.InitialContext(env);\n"
+ + "entityManagerFactory =
context.lookup('java:/RHQEntityManagerFactory');\n"
+ + "entityManager =
entityManagerFactory.createEntityManager();\n"
+ +
"entityManager.find(java.lang.Class.forName('org.rhq.core.domain.resource.Resource'),
java.lang.Integer.valueOf('10001'));");
+
+ Assert.fail("The script shouldn't have been able to use the
EntityManager even using custom initial context factory.");
+ } catch (ScriptException e) {
+ checkIsDesiredSecurityException(e);
+ }
+ }
+
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);
@@ -172,4 +188,11 @@ public class EjbAccessTest extends AbstractEJB3Test {
return new SandboxedScriptEngine(engine, perms);
}
+
+ private static void checkIsDesiredSecurityException(ScriptException e) {
+ String message = e.getMessage();
+ String permissionTrace =
AllowRhqServerInternalsAccessPermission.class.getName();
+
+ Assert.assertTrue(message.contains(permissionTrace), "The script exception
doesn't seem to be caused by the AllowRhqServerInternalsAccessPermission security
exception. " + message);
+ }
}
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
deleted file mode 100644
index caa7513..0000000
--- a/modules/enterprise/server/client-api/src/test/resources/META-INF/ejb-jar.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?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
deleted file mode 100644
index b7c5c66..0000000
--- a/modules/enterprise/server/client-api/src/test/resources/META-INF/jboss.xml
+++ /dev/null
@@ -1,168 +0,0 @@
-<?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/container-lib/pom.xml
b/modules/enterprise/server/container-lib/pom.xml
index b832b06..9d7d603 100644
--- a/modules/enterprise/server/container-lib/pom.xml
+++ b/modules/enterprise/server/container-lib/pom.xml
@@ -39,6 +39,11 @@
<scope>provided</scope> <!-- by JBossAS -->
</dependency>
+ <dependency>
+ <groupId>jboss</groupId>
+ <artifactId>jnpserver</artifactId>
+ <version>4.2.2.GA</version>
+ </dependency>
</dependencies>
<profiles>
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingContextDecorator.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingContextDecorator.java
new file mode 100644
index 0000000..df42afe
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingContextDecorator.java
@@ -0,0 +1,237 @@
+/*
+ * 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.jndi;
+
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.naming.Binding;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameClassPair;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+
+/**
+ * This is the "meat" of the RHQ's secured JNDI access. This {@link
Context} decorator
+ * applied security checks in each method (lookups, (un)bindings, etc).
+ * <p>
+ * The security check consists of checking if the current callstack has the {@link
AllowRhqServerInternalsAccessPermission}.
+ * <p>
+ * This decorator applies the security check on any JNDI name without a scheme and
+ * on any name that has a scheme listed in the {@link #checkedSchemes} list supplied
+ * in the constructor.
+ *
+ * @author Lukas Krejci
+ */
+public class AccessCheckingContextDecorator implements Context {
+
+ private static final AllowRhqServerInternalsAccessPermission PERM = new
AllowRhqServerInternalsAccessPermission();
+ private Context original;
+ private List<String> checkedSchemes;
+
+ public AccessCheckingContextDecorator(Context original, String... checkedSchemes) {
+ this.original = original;
+ this.checkedSchemes = Arrays.asList(checkedSchemes);
+ }
+
+ private static void check() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) sm.checkPermission(PERM);
+ }
+
+ private void checkScheme(String scheme) {
+ if (scheme == null || checkedSchemes.contains(scheme)) {
+ check();
+ }
+ }
+
+ private void check(String name) {
+ checkScheme(getURLScheme(name));
+ }
+
+ private void check(Name name) {
+ if (name.size() == 0) {
+ check();
+ } else {
+ String first = name.get(0);
+ checkScheme(getURLScheme(first));
+ }
+ }
+
+ public Object lookup(Name name) throws NamingException {
+ check(name);
+ return original.lookup(name);
+ }
+
+ public Object lookup(String name) throws NamingException {
+ check(name);
+ return original.lookup(name);
+ }
+
+ public void bind(Name name, Object obj) throws NamingException {
+ check(name);
+ original.bind(name, obj);
+ }
+
+ public void bind(String name, Object obj) throws NamingException {
+ check(name);
+ original.bind(name, obj);
+ }
+
+ public void rebind(Name name, Object obj) throws NamingException {
+ check(name);
+ original.rebind(name, obj);
+ }
+
+ public void rebind(String name, Object obj) throws NamingException {
+ check(name);
+ original.rebind(name, obj);
+ }
+
+ public void unbind(Name name) throws NamingException {
+ check(name);
+ original.unbind(name);
+ }
+
+ public void unbind(String name) throws NamingException {
+ check(name);
+ original.unbind(name);
+ }
+
+ public void rename(Name oldName, Name newName) throws NamingException {
+ check(oldName);
+ check(newName);
+ original.rename(oldName, newName);
+ }
+
+ public void rename(String oldName, String newName) throws NamingException {
+ check(oldName);
+ check(newName);
+ original.rename(oldName, newName);
+ }
+
+ public NamingEnumeration<NameClassPair> list(Name name) throws NamingException
{
+ check(name);
+ return original.list(name);
+ }
+
+ public NamingEnumeration<NameClassPair> list(String name) throws
NamingException {
+ check(name);
+ return original.list(name);
+ }
+
+ public NamingEnumeration<Binding> listBindings(Name name) throws
NamingException {
+ check(name);
+ return original.listBindings(name);
+ }
+
+ public NamingEnumeration<Binding> listBindings(String name) throws
NamingException {
+ check(name);
+ return original.listBindings(name);
+ }
+
+ public void destroySubcontext(Name name) throws NamingException {
+ check(name);
+ original.destroySubcontext(name);
+ }
+
+ public void destroySubcontext(String name) throws NamingException {
+ check(name);
+ original.destroySubcontext(name);
+ }
+
+ public Context createSubcontext(Name name) throws NamingException {
+ check(name);
+ return original.createSubcontext(name);
+ }
+
+ public Context createSubcontext(String name) throws NamingException {
+ check(name);
+ return original.createSubcontext(name);
+ }
+
+ public Object lookupLink(Name name) throws NamingException {
+ check(name);
+ return original.lookupLink(name);
+ }
+
+ public Object lookupLink(String name) throws NamingException {
+ check(name);
+ return original.lookupLink(name);
+ }
+
+ public NameParser getNameParser(Name name) throws NamingException {
+ check(name);
+ return original.getNameParser(name);
+ }
+
+ public NameParser getNameParser(String name) throws NamingException {
+ check(name);
+ return original.getNameParser(name);
+ }
+
+ public Name composeName(Name name, Name prefix) throws NamingException {
+ check(name);
+ return original.composeName(name, prefix);
+ }
+
+ public String composeName(String name, String prefix) throws NamingException {
+ check(name);
+ return original.composeName(name, prefix);
+ }
+
+ public Object addToEnvironment(String propName, Object propVal) throws
NamingException {
+ check();
+ return original.addToEnvironment(propName, propVal);
+ }
+
+ public Object removeFromEnvironment(String propName) throws NamingException {
+ check();
+ return original.removeFromEnvironment(propName);
+ }
+
+ public Hashtable<?, ?> getEnvironment() throws NamingException {
+ check();
+ return original.getEnvironment();
+ }
+
+ public void close() throws NamingException {
+ check();
+ original.close();
+ }
+
+ public String getNameInNamespace() throws NamingException {
+ check();
+ return original.getNameInNamespace();
+ }
+
+ //copied from InitialContext
+ private static String getURLScheme(String str) {
+ int colon_posn = str.indexOf(':');
+ int slash_posn = str.indexOf('/');
+
+ if (colon_posn > 0 && (slash_posn == -1 || colon_posn <
slash_posn))
+ return str.substring(0, colon_posn);
+ return null;
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryBuilder.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryBuilder.java
new file mode 100644
index 0000000..1a9562a
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryBuilder.java
@@ -0,0 +1,180 @@
+/*
+ * 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.jndi;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.InitialContextFactoryBuilder;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jnp.interfaces.NamingContextFactory;
+
+/**
+ * This initial context factory builder is installed early on during the RHQ server
startup
+ * and is later on used for obtaining the {@link Context}s for all JNDI lookups in the
+ * RHQ server.
+ * <p>
+ * We use a custom initial context factory builder to prevent the potential malicious 3rd
party
+ * code (like CLI alert scripts) from supplying custom environment variables to {@link
InitialContext}
+ * that would modify the JNDI lookup to skip our security access checks.
+ * <p>
+ * By using a builder we effectively take control of the initial context creation
process
+ * and are free to ignore whatever the script is trying to supply.
+ * <p>
+ * This builder makes sure to install the RHQ server's security access checks to
whatever
+ * initial context that is configured by the standard environment variables
+ * ({@link Context#INITIAL_CONTEXT_FACTORY}, etc.)
+ * <p>
+ * This class is heavily inspired by the implementation of a similar builder in JBoss AS
7.
+ *
+ * @see AllowRhqServerInternalsAccessPermission
+ *
+ * @author Lukas Krejci
+ */
+public class AccessCheckingInitialContextFactoryBuilder implements
InitialContextFactoryBuilder {
+ private static final Log LOG =
LogFactory.getLog(AccessCheckingInitialContextFactoryBuilder.class);
+
+ /**
+ * The list of JNDI name schemes that should be checked for security permissions
+ * (in addition to the names with no scheme).
+ *
+ * @see AccessCheckingContextDecorator
+ */
+ private static final String[] CHECKED_SCHEMES = { "java" };
+
+ private static final Set<InetAddress> SERVER_BIND_IPS;
+
+ static {
+ SERVER_BIND_IPS = new HashSet<InetAddress>();
+
+ try {
+ String bindingAddressString =
System.getProperty("jboss.bind.address");
+ InetAddress bindingAddress = InetAddress.getByName(bindingAddressString);
+
+ if (bindingAddress.isAnyLocalAddress()) {
+ Enumeration<NetworkInterface> ifaces =
NetworkInterface.getNetworkInterfaces();
+ while(ifaces.hasMoreElements()) {
+ NetworkInterface iface = ifaces.nextElement();
+ SERVER_BIND_IPS.addAll(Collections.list(iface.getInetAddresses()));
+ }
+ } else {
+ SERVER_BIND_IPS.add(bindingAddress);
+ }
+ } catch (SocketException e) {
+ LOG.error("Could not obtain the list of local IPs", e);
+ } catch (UnknownHostException e) {
+ LOG.error("Failed to get the binding address of the RHQ server.",
e);
+ }
+
+ }
+
+ /**
+ * This is the default initial context factory that is returned when no other is
+ * configured using the environment variables.
+ * <p>
+ * It uses {@link NamingContextFactory} as the underlying mechanism - the same
+ * as the default configuration in JBoss 4.
+ */
+ private static final InitialContextFactory DEFAULT_FACTORY = new
InitialContextFactory() {
+ public Context getInitialContext(Hashtable<?, ?> environment) throws
NamingException {
+ return createSecureWrapper(new NamingContextFactory(),
environment).getInitialContext(environment);
+ }
+ };
+
+ /**
+ * Create a InitialContext factory. If the environment does not override the factory
class it will use the
+ * default context factory.
+ *
+ * @param environment The environment
+ * @return An initial context factory
+ * @throws NamingException If an error occurs loading the factory class.
+ */
+ public InitialContextFactory createInitialContextFactory(Hashtable<?, ?>
environment) throws NamingException {
+ final String factoryClassName = (String)
environment.get(Context.INITIAL_CONTEXT_FACTORY);
+ if (factoryClassName == null) {
+ return DEFAULT_FACTORY;
+ }
+ final ClassLoader classLoader = getContextClassLoader();
+ try {
+ final Class<?> factoryClass = Class.forName(factoryClassName, true,
classLoader);
+ InitialContextFactory configuredFactory = (InitialContextFactory)
factoryClass.newInstance();
+ return createSecureWrapper(configuredFactory, environment);
+ } catch (Exception e) {
+ throw new NamingException("Failed instantiate InitialContextFactory
" + factoryClassName
+ + " from classloader " + classLoader);
+ }
+ }
+
+ private ClassLoader getContextClassLoader() {
+ return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+ });
+ }
+
+ private static InitialContextFactory createSecureWrapper(InitialContextFactory
factory, Hashtable<?, ?> environment) {
+ String providerUrl = (String) environment.get(Context.PROVIDER_URL);
+
+ if (providerUrl == null) {
+ return new AccessCheckingInitialContextFactoryDecorator(factory,
CHECKED_SCHEMES);
+ } else {
+ try {
+ URI uri = new URI(providerUrl);
+ InetAddress providerHost = InetAddress.getByName(uri.getHost());
+
+ //check if we are accessing the RHQ server through some remoting
+ //interface.
+ //I just can't find where to read the magic number 1099 from.
+ //it is defined in the jboss config files, but in the code
+ //it seems hardcoded - see JDBCLoginModule.
+ if (uri.getPort() == 1099 &&
SERVER_BIND_IPS.contains(providerHost)) {
+ return new AccessCheckingInitialContextFactoryDecorator(factory,
CHECKED_SCHEMES);
+ } else {
+ return new URLPreferringInitialContextFactoryDecorator(factory);
+ }
+ } catch (URISyntaxException e) {
+ return new AccessCheckingInitialContextFactoryDecorator(factory,
CHECKED_SCHEMES);
+ } catch (UnknownHostException e) {
+ //let the factory deal with the unknown host...
+ //this most probably shouldn't be secured because localhost
addresses
+ //should be resolvable.
+ return new URLPreferringInitialContextFactoryDecorator(factory);
+ }
+ }
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryDecorator.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryDecorator.java
new file mode 100644
index 0000000..b23935f
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AccessCheckingInitialContextFactoryDecorator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.jndi;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+
+/**
+ * A decorator of an {@link InitialContextFactory} that adds the security checking
+ * machinery to the returned initial contexts.
+ *
+ * @author Lukas Krejci
+ */
+public class AccessCheckingInitialContextFactoryDecorator extends
URLPreferringInitialContextFactoryDecorator {
+
+ private final String[] checkedSchemes;
+
+ /**
+ * @param factory the factory to wrap
+ * @param checkedSchemes the list of JNDI name schemes to check for permissions
+ * @see AccessCheckingContextDecorator
+ */
+ public AccessCheckingInitialContextFactoryDecorator(InitialContextFactory factory,
String... checkedSchemes) {
+ super(factory);
+ this.checkedSchemes = checkedSchemes;
+ }
+
+ /**
+ * @param environment the environment variables for the return {@link Context} to
use
+ *
+ * @return the initial context returned by the decorated factory wrapped in
+ * {@link URLPreferringContext} and {@link AccessCheckingContextDecorator} in that
+ * order.
+ */
+ @Override
+ public Context getInitialContext(Hashtable<?, ?> environment) throws
NamingException {
+ return new AccessCheckingContextDecorator(super.getInitialContext(environment),
checkedSchemes);
+ }
+
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AllowRhqServerInternalsAccessPermission.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AllowRhqServerInternalsAccessPermission.java
new file mode 100644
index 0000000..aa807d6
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/AllowRhqServerInternalsAccessPermission.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.jndi;
+
+import java.security.BasicPermission;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class AllowRhqServerInternalsAccessPermission extends BasicPermission {
+
+ private static final long serialVersionUID = 1L;
+
+ public AllowRhqServerInternalsAccessPermission() {
+ super("org.rhq.allow.server.internals.access");
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringContext.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringContext.java
new file mode 100644
index 0000000..b291c3e
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringContext.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 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.jndi;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+import javax.naming.spi.NamingManager;
+
+/**
+ * This is a modification of the default {@link InitialContext} class that
+ * ignores the initial context factory builder when constructing the default
+ * context to use in lookups etc.
+ * <p>
+ * This is important because RHQ server has its own initial context factory
+ * builder that creates factories that in turn create contexts. If the default
+ * {@link InitialContext} implementation was used, we'd end up with a stack overflow
+ * error caused by the callchain loop when the {@link InitialContext} would ask
+ * the builder for a factory that would create another context that would ask the
+ * builder for the factory, etc. ad infinitum.
+ * <p>
+ * This class prevents that by acting exactly like the InitialContext would if
+ * the initial context factory builder wasn't installed. This enables the callers
+ * to still use the standard InitialContext when doing lookups etc. This standard
+ * initial context will ask our initial context factory builder which will then
+ * in chain create this {@link URLPreferringContext} that behaves like the original
+ * InitialContext would if the builder wasn't there.
+ *
+ * @author Lukas Krejci
+ */
+public class URLPreferringContext extends InitialContext {
+
+ public URLPreferringContext(InitialContextFactory defaultContextFactory) throws
NamingException {
+ super(true);
+ this.defaultInitCtx = defaultContextFactory.getInitialContext(null);
+ this.gotDefault = true;
+ init(null);
+ }
+
+ public URLPreferringContext(Hashtable<?, ?> environment, InitialContextFactory
defaultContextFactory) throws NamingException {
+ super(true);
+ this.defaultInitCtx = defaultContextFactory.getInitialContext(environment);
+ this.gotDefault = true;
+ init(environment);
+ }
+
+ @Override
+ protected Context getURLOrDefaultInitCtx(Name name) throws NamingException {
+ if (name.size() > 0) {
+ String first = name.get(0);
+ String scheme = getURLScheme(first);
+ if (scheme != null) {
+ Context ctx = NamingManager.getURLContext(scheme, myProps);
+ if (ctx != null) {
+ return ctx;
+ }
+ }
+ }
+ return getDefaultInitCtx();
+ }
+
+ @Override
+ protected Context getURLOrDefaultInitCtx(String name) throws NamingException {
+ String scheme = getURLScheme(name);
+ if (scheme != null) {
+ Context ctx = NamingManager.getURLContext(scheme, myProps);
+ if (ctx != null) {
+ return ctx;
+ }
+ }
+ return getDefaultInitCtx();
+ }
+
+ //copied from InitialContext
+ private static String getURLScheme(String str) {
+ int colon_posn = str.indexOf(':');
+ int slash_posn = str.indexOf('/');
+
+ if (colon_posn > 0 && (slash_posn == -1 || colon_posn <
slash_posn))
+ return str.substring(0, colon_posn);
+ return null;
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringInitialContextFactoryDecorator.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringInitialContextFactoryDecorator.java
new file mode 100644
index 0000000..6c311c7
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/URLPreferringInitialContextFactoryDecorator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.jndi;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.naming.spi.InitialContextFactory;
+
+/**
+ * A decorator of an {@link InitialContextFactory} that returns an {@link
URLPreferringContext}
+ * backed by the wrapped initial context factory.
+ * <p>
+ * This is to support contexts that don't need to be secured, yet we need to make
sure to
+ * break the call-chain loop caused by the {@link InitialContext} asking the RHQ's
{@link AccessCheckingInitialContextFactoryBuilder} for
+ * default contexts.
+ *
+ * @author Lukas Krejci
+ */
+public class URLPreferringInitialContextFactoryDecorator implements InitialContextFactory
{
+
+ private final InitialContextFactory factory;
+
+ public URLPreferringInitialContextFactoryDecorator(InitialContextFactory factory) {
+ this.factory = factory;
+ }
+
+ public Context getInitialContext(Hashtable<?, ?> environment) throws
NamingException {
+ return new URLPreferringContext(environment, getFactory());
+ }
+
+ protected InitialContextFactory getFactory() {
+ return factory;
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstaller.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstaller.java
new file mode 100644
index 0000000..d938c50
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstaller.java
@@ -0,0 +1,45 @@
+/*
+ * 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.jndi.mbean;
+
+import javax.naming.spi.NamingManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.jndi.AccessCheckingInitialContextFactoryBuilder;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public class AccessCheckingInitialContextFactoryBuilderInstaller implements
AccessCheckingInitialContextFactoryBuilderInstallerMBean {
+ private static final Log LOG =
LogFactory.getLog(AccessCheckingInitialContextFactoryBuilder.class);
+
+ public void start() throws Exception {
+ LOG.info("Installing RHQ's access permission checking initial context
factory builder");
+
+ NamingManager.setInitialContextFactoryBuilder(new
AccessCheckingInitialContextFactoryBuilder());
+ }
+
+ public void stop() throws Exception {
+ }
+}
diff --git
a/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstallerMBean.java
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstallerMBean.java
new file mode 100644
index 0000000..87d19ab
--- /dev/null
+++
b/modules/enterprise/server/container-lib/src/main/java/org/rhq/jndi/mbean/AccessCheckingInitialContextFactoryBuilderInstallerMBean.java
@@ -0,0 +1,32 @@
+/*
+ * 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.jndi.mbean;
+
+/**
+ *
+ *
+ * @author Lukas Krejci
+ */
+public interface AccessCheckingInitialContextFactoryBuilderInstallerMBean {
+
+ void start() throws Exception;
+
+ void stop() throws Exception;
+}
diff --git
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-service.xml
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-service.xml
index 859b552..f231f15 100644
---
a/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-service.xml
+++
b/modules/enterprise/server/container/src/main/resources/jbossas/server/default/conf/jboss-service.xml
@@ -225,6 +225,13 @@
<!-- JNDI -->
<!-- ==================================================================== -->
+ <!-- This installs a custom initial context factory builder into the JVM
+ that will ensure that all the default InitialContexts are going to check
+ for the permission to access the RHQ internals. -->
+ <mbean
code="org.rhq.jndi.mbean.AccessCheckingInitialContextFactoryBuilderInstaller"
+
name="org.rhq:service=AccessCheckingInitialContextFactoryBuilderInstaller">
+ </mbean>
+
<!-- A simple mbean wrapper around the jndi Naming object. This
only handles an in memory instance. The NamingService uses this
as the JNDI store and exposes it remotely.
diff --git a/modules/enterprise/server/jar/pom.xml
b/modules/enterprise/server/jar/pom.xml
index db286a1..a876809 100644
--- a/modules/enterprise/server/jar/pom.xml
+++ b/modules/enterprise/server/jar/pom.xml
@@ -78,6 +78,13 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-container-lib</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+
<dependency>
<groupId>com.googlecode.java-diff-utils</groupId>
<artifactId>diffutils</artifactId>
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 b4a19b7..ecf2f6f 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,8 +25,8 @@ 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;
+import org.rhq.jndi.AllowRhqServerInternalsAccessPermission;
/**
* This is the JON Server's own session ID generator. It is outside any
container-provided session mechanism. Its sole
@@ -45,7 +45,7 @@ import org.rhq.enterprise.server.util.LookupUtil;
*/
public final class SessionManager {
- private static final AllowEjbAccessPermission ACCESS_PERMISSION = new
AllowEjbAccessPermission();
+ private static final AllowRhqServerInternalsAccessPermission ACCESS_PERMISSION = new
AllowRhqServerInternalsAccessPermission();
/**
* Our source for random session IDs.
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
deleted file mode 100644
index 350453fb..0000000
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/security/AllowEjbAccessInterceptor.java
+++ /dev/null
@@ -1,46 +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.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
deleted file mode 100644
index 67f418d..0000000
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/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.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 caa7513..8222948 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
@@ -9,12 +9,6 @@
<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>
diff --git a/modules/enterprise/server/jar/src/test/resources/embedded-jboss-beans.xml
b/modules/enterprise/server/jar/src/test/resources/embedded-jboss-beans.xml
index 05edefa..85e2fb3 100644
--- a/modules/enterprise/server/jar/src/test/resources/embedded-jboss-beans.xml
+++ b/modules/enterprise/server/jar/src/test/resources/embedded-jboss-beans.xml
@@ -4,6 +4,14 @@
xsi:schemaLocation="urn:jboss:bean-deployer:2.0
bean-deployer_2_0.xsd"
xmlns="urn:jboss:bean-deployer:2.0">
+ <!-- This installs a custom initial context factory builder into the JVM
+ that will ensure that all the default InitialContexts are going to check
+ for the permission to access the RHQ internals. -->
+ <bean
class="org.rhq.jndi.mbean.AccessCheckingInitialContextFactoryBuilderInstaller"
+ name="AccessCheckingInitialContextFactoryBuilderInstaller">
+ </bean>
+
+
<bean name="Naming"
class="org.jnp.server.SingletonNamingServer"/>
<bean name="InitialContextProperties"
class="java.util.Hashtable">
@@ -20,7 +28,7 @@
</entry>
</map>
</parameter>
- </constructor>
+ </constructor>
</bean>
<bean name="java:comp/Initializer"
class="org.jboss.ejb3.embedded.JavaCompInitializer">
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.
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 = アップロードは処理中です...
大きなファイルを配布するには数分かかることもあります。
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;
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;
}
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 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 096cb99394259a950b92c81781405ad87fb206f6
Merge: 123ac05 90081e7
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 17:56:43 2011 +0100
Merge branch 'master' into feature/rest
Conflicts:
modules/enterprise/server/container/pom.xml
modules/enterprise/server/ear/pom.xml
diff --cc modules/enterprise/server/ear/pom.xml
index b792ab8,258a946e..941c88a
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@@ -95,7 -89,11 +89,11 @@@
<groupId>org.rhq</groupId>
<artifactId>rhq-core-domain</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </exclusion>
- </exclusions>
+ </exclusions>
</dependency>
<dependency>
diff --cc modules/jopr/modules-plugins-pom.xml
index dc1fa28,3b602ea..1bb52cf
--- a/modules/jopr/modules-plugins-pom.xml
+++ b/modules/jopr/modules-plugins-pom.xml
@@@ -1,83 -1,74 +1,74 @@@
--<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
-- <modelVersion>4.0.0</modelVersion>
--
-- <parent>
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-modules-parent</artifactId>
-- <version>2.4.0-SNAPSHOT</version>
-- </parent>
--
-- <!-- Plugin poms should not set their parent pom to this artifact. Instead, set to
"jopr-modules-parent". This is due to
-- the fact that this pom runs integration tests *on* the plugins. This pom must process
in the reactor after the
-- plugins themselves, as such, it can't be their parent. -->
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-plugins-parent</artifactId>
-- <version>2.3.0.EmbJopr.1.3.0-1</version>
-- <packaging>pom</packaging>
-- <name>Jopr Plugins</name>
-- <description>parent POM for all Jopr plugins</description>
--
- <scm>
-
<
connection>scm:svn:http://svn.jboss.org/repos/jopr/tags/Jopr_2_3_0_Emb...
-
<
developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/tags/Jopr...
- </scm>
-
- <properties>
- <scm.module.path>modules/plugins/</scm.module.path>
- </properties>
-
-- <dependencies>
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-jmx-plugin</artifactId>
-- <version>${rhq.version}</version>
-- <scope>test</scope>
-- </dependency>
-- </dependencies>
--
-- <!-- plugins - order is important -->
-- <modules>
-- <module>tomcat</module>
-- <module>jboss-as</module>
-- <module>hibernate</module>
-- <module>rhq-server</module>
-- <module>jboss-cache</module>
-- <module>jboss-as-5</module>
-- <module>jboss-cache-v3</module>
-- </modules>
--
-- <build>
-- <plugins>
-- <plugin>
-- <artifactId>maven-antrun-plugin</artifactId>
-- <executions>
-- <execution>
-- <phase>integration-test</phase>
-- <configuration>
-- <tasks>
-- <property name="test.classpath"
refid="maven.test.classpath" />
-- <echo>Validating plugins...</echo>
-- <java classname="org.rhq.core.pc.plugin.PluginValidator"
failonerror="true" fork="true">
-- <classpath>
-- <pathelement path="${test.classpath}" />
-- <pathelement
location="hibernate/target/jopr-hibernate-plugin-${project.version}.jar" />
-- <pathelement
location="tomcat/target/jopr-tomcat-plugin-${project.version}.jar" />
-- <pathelement
location="jboss-as/target/jopr-jboss-as-plugin-${project.version}.jar" />
-- <!-- TODO: Figure out why this is causing NoClassDefFoundErrors and fix it.
-->
-- <!--<pathelement
location="jboss-as-5/target/jopr-jboss-as-5-plugin-${project.version}.jar"
/>-->
-- <pathelement
location="rhq-server/target/jopr-rhq-server-plugin-${project.version}.jar"
/>
-- </classpath>
-- <sysproperty key="org.apache.commons.logging.Log"
value="org.apache.commons.logging.impl.SimpleLog" />
-- </java>
-- </tasks>
-- </configuration>
-- <goals>
-- <goal>run</goal>
-- </goals>
-- </execution>
-- </executions>
-- </plugin>
-- </plugins>
-- </build>
--
--</project>
++<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
++ <modelVersion>4.0.0</modelVersion>
++
++ <parent>
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-modules-parent</artifactId>
++ <version>2.4.0-SNAPSHOT</version>
++ </parent>
++
++ <!-- Plugin poms should not set their parent pom to this artifact. Instead, set to
"jopr-modules-parent". This is due to
++ the fact that this pom runs integration tests *on* the plugins. This pom must process
in the reactor after the
++ plugins themselves, as such, it can't be their parent. -->
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-plugins-parent</artifactId>
++ <version>2.3.0.EmbJopr.1.3.0-1</version>
++ <packaging>pom</packaging>
++ <name>Jopr Plugins</name>
++ <description>parent POM for all Jopr plugins</description>
++
++ <dependencies>
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-jmx-plugin</artifactId>
++ <version>${rhq.version}</version>
++ <scope>test</scope>
++ </dependency>
++ </dependencies>
++
++ <!-- plugins - order is important -->
++ <modules>
++ <module>tomcat</module>
++ <module>jboss-as</module>
++ <module>hibernate</module>
++ <module>rhq-server</module>
++ <module>jboss-cache</module>
++ <module>jboss-as-5</module>
++ <module>jboss-cache-v3</module>
++ </modules>
++
++ <build>
++ <plugins>
++ <plugin>
++ <artifactId>maven-antrun-plugin</artifactId>
++ <executions>
++ <execution>
++ <phase>integration-test</phase>
++ <configuration>
++ <tasks>
++ <property name="test.classpath"
refid="maven.test.classpath" />
++ <echo>Validating plugins...</echo>
++ <java classname="org.rhq.core.pc.plugin.PluginValidator"
failonerror="true" fork="true">
++ <classpath>
++ <pathelement path="${test.classpath}" />
++ <pathelement
location="hibernate/target/jopr-hibernate-plugin-${project.version}.jar" />
++ <pathelement
location="tomcat/target/jopr-tomcat-plugin-${project.version}.jar" />
++ <pathelement
location="jboss-as/target/jopr-jboss-as-plugin-${project.version}.jar" />
++ <!-- TODO: Figure out why this is causing NoClassDefFoundErrors and fix it.
-->
++ <!--<pathelement
location="jboss-as-5/target/jopr-jboss-as-5-plugin-${project.version}.jar"
/>-->
++ <pathelement
location="rhq-server/target/jopr-rhq-server-plugin-${project.version}.jar"
/>
++ </classpath>
++ <sysproperty key="org.apache.commons.logging.Log"
value="org.apache.commons.logging.impl.SimpleLog" />
++ </java>
++ </tasks>
++ </configuration>
++ <goals>
++ <goal>run</goal>
++ </goals>
++ </execution>
++ </executions>
++ </plugin>
++ </plugins>
++ </build>
++
++</project>
diff --cc modules/jopr/modules-pom.xml
index 7571c50,67e763b..de9ca10
--- a/modules/jopr/modules-pom.xml
+++ b/modules/jopr/modules-pom.xml
@@@ -1,131 -1,124 +1,124 @@@
--<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
--
-- <modelVersion>4.0.0</modelVersion>
--
-- <parent>
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-parent</artifactId>
-- <version>2.4.0-SNAPSHOT</version>
-- </parent>
--
-- <groupId>org.jboss.on</groupId>
-- <artifactId>jopr-modules-parent</artifactId>
-- <packaging>pom</packaging>
--
-- <name>Jopr Modules</name>
-- <description>parent POM for all Jopr modules</description>
--
- <scm>
-
<
connection>scm:svn:http://svn.jboss.org/repos/jopr/trunk/modules/</...
-
<
developerConnection>scm:svn:https://svn.jboss.org/repos/jopr/trunk/mod...
- </scm>
-
-- <properties>
- <scm.module.path>modules/</scm.module.path>
-
-- <!-- dependency versions -->
-- <hibernate-all.version>1.0.0.Alpha9</hibernate-all.version>
-- <persistence-api.version>1.0</persistence-api.version>
-- </properties>
--
-- <!-- These dependencies are (mostly) common to the plugin artifacts built under
the
-- Jopr umbrella. They are not specific to the build a one particular artifact but
-- placed here to simplify the child poms. -->
-- <dependencies>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-util</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-domain</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-plugin-api</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-native-system</artifactId>
-- <version>${rhq.version}</version>
-- <scope>provided</scope> <!-- by PC -->
-- </dependency>
--
-- <dependency>
-- <groupId>${rhq.groupId}</groupId>
-- <artifactId>rhq-core-plugin-container</artifactId>
-- <version>${rhq.version}</version>
-- <scope>test</scope>
-- </dependency>
--
-- <!-- Third-party libs -->
-- <dependency>
-- <groupId>javax.xml.bind</groupId>
-- <artifactId>jaxb-api</artifactId>
-- <scope>provided</scope>
-- </dependency>
--
-- <dependency>
-- <groupId>com.sun.xml.bind</groupId>
-- <artifactId>jaxb-impl</artifactId>
-- <!-- NOTE: The version is defined in the root POM's dependencyManagement
section. -->
-- <scope>provided</scope>
-- </dependency>
--
-- <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes. It is fixed in JDK 6. -->
-- <dependency>
-- <groupId>javax.persistence</groupId>
-- <artifactId>persistence-api</artifactId>
-- <version>${persistence-api.version}</version>
-- <scope>provided</scope> <!-- by ON container -->
-- </dependency>
--
-- <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes; it is fixed in JDK 6. -->
-- <dependency>
-- <groupId>jboss.jboss-embeddable-ejb3</groupId>
-- <artifactId>hibernate-all</artifactId>
-- <version>${hibernate-all.version}</version>
-- <scope>provided</scope> <!-- by ON container -->
-- </dependency>
--
-- </dependencies>
--
-- <modules>
-- <module>common</module>
-- <module>plugins</module>
-- </modules>
--
-- <profiles>
--
-- <!-- enterprise-only stuff -->
-- <profile>
-- <id>enterprise</id>
-- <activation>
-- <activeByDefault>true</activeByDefault>
-- </activation>
-- <modules>
-- <module>serverplugins</module>
-- </modules>
-- </profile>
--
-- <!-- same as enterprise profile, except includes dist -->
-- <profile>
-- <id>dist</id>
-- <modules>
-- <module>serverplugins</module>
-- <module>dist</module>
-- </modules>
-- </profile>
--
-- </profiles>
--</project>
++<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
++
++ <modelVersion>4.0.0</modelVersion>
++
++ <parent>
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-parent</artifactId>
++ <version>2.4.0-SNAPSHOT</version>
++ </parent>
++
++ <groupId>org.jboss.on</groupId>
++ <artifactId>jopr-modules-parent</artifactId>
++ <packaging>pom</packaging>
++
++ <name>Jopr Modules</name>
++ <description>parent POM for all Jopr modules</description>
++
++ <properties>
++ <!-- dependency versions -->
++ <hibernate-all.version>1.0.0.Alpha9</hibernate-all.version>
++ <persistence-api.version>1.0</persistence-api.version>
++ </properties>
++
++ <!-- These dependencies are (mostly) common to the plugin artifacts built under
the
++ Jopr umbrella. They are not specific to the build a one particular artifact but
++ placed here to simplify the child poms. -->
++ <dependencies>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-util</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-domain</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-plugin-api</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-native-system</artifactId>
++ <version>${rhq.version}</version>
++ <scope>provided</scope> <!-- by PC -->
++ </dependency>
++
++ <dependency>
++ <groupId>${rhq.groupId}</groupId>
++ <artifactId>rhq-core-plugin-container</artifactId>
++ <version>${rhq.version}</version>
++ <scope>test</scope>
++ </dependency>
++
++ <!-- Third-party libs -->
++ <dependency>
++ <groupId>javax.xml.bind</groupId>
++ <artifactId>jaxb-api</artifactId>
++ <scope>provided</scope>
++ </dependency>
++
++ <dependency>
++ <groupId>com.sun.xml.bind</groupId>
++ <artifactId>jaxb-impl</artifactId>
++ <!-- NOTE: The version is defined in the root POM's dependencyManagement
section. -->
++ <scope>provided</scope>
++ </dependency>
++
++ <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes. It is fixed in JDK 6. -->
++ <dependency>
++ <groupId>javax.persistence</groupId>
++ <artifactId>persistence-api</artifactId>
++ <version>${persistence-api.version}</version>
++ <scope>provided</scope> <!-- by ON container -->
++ </dependency>
++
++ <!-- TODO: This is a fix for the Javac bug requiring annotations to be available
when compiling dependent classes; it is fixed in JDK 6. -->
++ <dependency>
++ <groupId>jboss.jboss-embeddable-ejb3</groupId>
++ <artifactId>hibernate-all</artifactId>
++ <version>${hibernate-all.version}</version>
++ <scope>provided</scope> <!-- by ON container -->
++ </dependency>
++
++ </dependencies>
++
++ <modules>
++ <module>common</module>
++ <module>plugins</module>
++ </modules>
++
++ <profiles>
++
++ <!-- enterprise-only stuff -->
++ <profile>
++ <id>enterprise</id>
++ <activation>
++ <activeByDefault>true</activeByDefault>
++ </activation>
++ <modules>
++ <module>serverplugins</module>
++ </modules>
++ </profile>
++
++ <!-- same as enterprise profile, except includes dist -->
++ <profile>
++ <id>dist</id>
++ <modules>
++ <module>serverplugins</module>
++ <module>dist</module>
++ </modules>
++ </profile>
++
++ </profiles>
++</project>
commit 123ac05d5c93d5f66961e435ccfb67d907c86287
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Nov 25 10:23:05 2011 +0100
Turn down logging. Prevent a LazyLoadException for schedules.
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
index e670afb..ce4dd43 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/AbstractRestBean.java
@@ -146,7 +146,7 @@ public class AbstractRestBean {
protected <T>T getFromCache(Fqn fqn,Class<T> clazz) {
Object o=null;
if (treeCache.exists(fqn)) {
- log.info("Hit for " + fqn.toString());
+ log.debug("Hit for " + fqn.toString());
try{
Node n = treeCache.get(fqn);
Set<Integer> readers= (Set<Integer>)
n.get("readers");
@@ -154,11 +154,10 @@ public class AbstractRestBean {
o = n.get("item");
}
else {
- log.info("No object for caller " +caller.toString() +
" found");
+ log.debug("No object for caller " +caller.toString() +
" found");
}
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
- log.info("Miss for " + fqn.toString());
+ log.debug("Miss for " + fqn.toString());
}
}
return (T)o;
@@ -187,9 +186,9 @@ public class AbstractRestBean {
treeCache.put(fqn,"readers",readers);
treeCache.put(fqn,"item",o);
success = true;
- log.info("Put " + fqn);
+ log.debug("Put " + fqn);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
return success;
}
@@ -235,7 +234,7 @@ public class AbstractRestBean {
childParentMap.put(res.getId(), pid);
treeCache.put(resourceMeta,"childParentMap",childParentMap);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -269,7 +268,7 @@ public class AbstractRestBean {
ret.add(getFromCache(resId, Resource.class));
}
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -287,7 +286,7 @@ public class AbstractRestBean {
if (visibleResources.contains(resourceid))
res = getFromCache(resourceid,Resource.class);
} catch (CacheException e) {
- e.printStackTrace(); // TODO: Customise this generated block
+ log.warn(e.getMessage());
}
}
@@ -316,7 +315,7 @@ public class AbstractRestBean {
if (treeCache.exists(fqn)) {
try {
treeCache.remove(fqn);
- log.info("Cancel " + fqn);
+ log.debug("Cancel " + fqn);
return true;
} catch (CacheException e) {
return false;
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
index e500a33..91ed23a 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/rest/ResourceHandlerBean.java
@@ -200,7 +200,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
if (scheduleType.equals("metric"))
scheduleType=DataType.MEASUREMENT.toString().toLowerCase();
- Resource res = fetchResource(resourceId);
+ Resource res = resMgr.getResource(caller,resourceId); // Don't fetch(), as
this would yield a LazyLoadException
Set<MeasurementSchedule> schedules = res.getSchedules();
List<MetricSchedule> ret = new
ArrayList<MetricSchedule>(schedules.size());
@@ -290,6 +290,7 @@ public class ResourceHandlerBean extends AbstractRestBean implements
ResourceHan
Link link = new Link();
link.setRel("alert");
link.setHref("/alert/" + al.getId());
+ links.add(link);
}
return links;
}
commit 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>