[rhq] modules/enterprise
by mazz
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java | 27 ++++--
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java | 2
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java | 42 ++--------
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java | 7 +
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java | 38 +++++++--
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java | 9 +-
6 files changed, 72 insertions(+), 53 deletions(-)
New commits:
commit 6b5624ee8eba0fbe9876a442b374610c996b0100
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Fri Jan 3 17:22:35 2014 -0500
BZ 994250 - fix some problems that caused the install to fail. fixed some other things as well
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index a13a598..52a45e7 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -162,9 +162,8 @@ public abstract class ControlCommand {
rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
} catch (ConfigurationException e) {
throw new RHQControlException("Failed to update " + getRhqCtlProperties(), e);
- } finally {
- return rValue;
}
+ return rValue;
}
public void printUsage() {
@@ -457,7 +456,7 @@ public abstract class ControlCommand {
protected void killPid(String pid) throws IOException {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
- PumpStreamHandler streamHandler = new PumpStreamHandler(new NullOutputStream(), new NullOutputStream());
+ PumpStreamHandler streamHandler = new PumpStreamHandler(createNullOutputStream(), createNullOutputStream());
executor.setStreamHandler(streamHandler);
org.apache.commons.exec.CommandLine commandLine;
@@ -469,19 +468,18 @@ public abstract class ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
- PumpStreamHandler streamHandler = new PumpStreamHandler(new NullOutputStream(), new NullOutputStream());
+ PumpStreamHandler streamHandler = new PumpStreamHandler(createNullOutputStream(), createNullOutputStream());
executor.setStreamHandler(streamHandler);
- org.apache.commons.exec.CommandLine commandLine = new org.apache.commons.exec.CommandLine("/bin/kill")
- .addArgument("-0")
- .addArgument(pid);
+ org.apache.commons.exec.CommandLine commandLine;
+ commandLine = new org.apache.commons.exec.CommandLine("kill").addArgument("-0").addArgument(pid);
try {
int code = executor.execute(commandLine);
- if (code!=0) {
+ if (code != 0) {
return false;
}
} catch (ExecuteException ee ) {
- if (ee.getExitValue()==1) {
+ if (ee.getExitValue() == 1) {
// return code 1 means process does not exist
return false;
}
@@ -507,6 +505,17 @@ public abstract class ControlCommand {
}
}
+ /**
+ * Call this method to get an output stream that throws away all data. Useful
+ * when executing commands which you don't care about seeing its output on stdout.
+ * Example usage:
+ * executor.setStreamHandler(new PumpStreamHandler(createNullOutputStream(), createNullOutputStream()));
+ * @return a null output stream
+ */
+ protected OutputStream createNullOutputStream() {
+ return new NullOutputStream();
+ }
+
private class NullOutputStream extends OutputStream {
@Override
public void write(byte[] b, int off, int len) {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 88b085f..3cfa665 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -61,8 +61,6 @@ public class RHQControl {
public static final int EXIT_CODE_OPERATION_FAILED = 1;
public static final int EXIT_CODE_INVALID_ARGUMENT = 2;
public static final int EXIT_CODE_NOT_INSTALLED = 5;
-// public static final int EXIT_CODE_OPERATION_NOT_RUNNING = 7;
-
private Commands commands = new Commands();
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index ed2b2f8..121aef5 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -43,7 +43,6 @@ import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
-import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
@@ -160,31 +159,6 @@ public abstract class AbstractInstall extends ControlCommand {
}
- protected boolean isUnixPidRunning(String pid) {
-
- Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
- executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine;
-
- commandLine = new org.apache.commons.exec.CommandLine("/bin/kill").addArgument("-0").addArgument(pid);
-
- try {
- int code = executor.execute(commandLine);
- if (code != 0) {
- return false;
- }
- } catch (ExecuteException ee) {
- if (ee.getExitValue() == 1) {
- // return code 1 means process does not exist
- return false;
- }
- } catch (IOException e) {
- log.error("Checking for running process failed: " + e.getMessage());
- }
- return true;
- }
-
protected void waitForRHQServerToInitialize() throws Exception {
try {
final long messageInterval = 30000L;
@@ -520,8 +494,12 @@ public abstract class AbstractInstall extends ControlCommand {
executor.execute(commandLine, executeHandler);
log.info("The server installer is running");
- return executeHandler.getExitValue();
- } catch (IOException e) {
+ if (executeHandler.hasResult()) {
+ return executeHandler.getExitValue();
+ } else {
+ return RHQControl.EXIT_CODE_OK; // the installer really didn't exit yet, but just indicate we started it OK
+ }
+ } catch (Exception e) {
log.error("An error occurred while starting the server installer: " + e.getMessage());
return RHQControl.EXIT_CODE_NOT_INSTALLED;
}
@@ -575,7 +553,7 @@ public abstract class AbstractInstall extends ControlCommand {
}
protected int installStorageNode(final File storageBasedir, CommandLine rhqctlCommandLine, boolean start)
- throws IOException {
+ throws Exception {
try {
log.info("Preparing to install RHQ storage node.");
@@ -621,7 +599,7 @@ public abstract class AbstractInstall extends ControlCommand {
addUndoTaskToStopComponent("--storage");
return exitCode;
- } catch (IOException e) {
+ } catch (Exception e) {
log.error("An error occurred while running the storage installer: " + e.getMessage());
if (e.getMessage().toLowerCase().contains("exit value: 3")) {
log.error("Try to point your root data directory via --" + STORAGE_DATA_ROOT_DIR
@@ -638,7 +616,7 @@ public abstract class AbstractInstall extends ControlCommand {
return rValue;
}
- private int installAgent(final File agentBasedir) throws IOException {
+ private int installAgent(final File agentBasedir) throws Exception {
try {
log.info("Installing RHQ agent");
@@ -668,7 +646,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
return exitValue;
- } catch (IOException e) {
+ } catch (Exception e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
index 3ab3f00..f8d37d9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
@@ -164,8 +164,11 @@ public class Install extends AbstractInstall {
} else {
startedServer = true;
startRHQServerForInstallation();
- rValue = Math.max(rValue, runRHQServerInstaller());
- waitForRHQServerToInitialize();
+ int installerStatusCode = runRHQServerInstaller();
+ rValue = Math.max(rValue, installerStatusCode);
+ if (installerStatusCode == RHQControl.EXIT_CODE_OK) {
+ waitForRHQServerToInitialize();
+ }
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
index e52f0a8..9c6ed7f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
@@ -131,7 +131,13 @@ public class Status extends ControlCommand {
commandLine = getCommandLine("rhq-storage", "status");
try {
rValue = executor.execute(commandLine);
-
+ } catch (ExecuteException ee) {
+ log.debug("Failed to check storage service status", ee);
+ rValue = ee.getExitValue();
+ if (rValue == RHQControl.EXIT_CODE_OK) {
+ // if somehow we were told it was OK, change it to unknown since it can't be OK
+ rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
+ }
} catch (Exception e) {
log.debug("Failed to check storage service status", e);
rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
@@ -154,7 +160,18 @@ public class Status extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- return executor.execute(commandLine);
+
+ int rValue;
+ try {
+ rValue = executor.execute(commandLine);
+ } catch (ExecuteException ee) {
+ rValue = ee.getExitValue();
+ if (rValue == RHQControl.EXIT_CODE_OK) {
+ // if somehow we were told it was OK, change it to unknown since it can't be OK
+ rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
+ }
+ }
+ return rValue;
}
private int checkAgentStatus() throws Exception {
@@ -166,16 +183,27 @@ public class Status extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
+
+ int rValue;
try {
- return executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (ExecuteException e) {
// For windows the JSW exit code for a status check is expected to be a mask value and the agent wrapper
// .bat will return it explicitly. We can ignore it and assume that the logged output is sufficient.
// See http://wrapper.tanukisoftware.com/doc/english/launch-win.html#standalone-...
if (!isWindows()) {
- throw e;
+ // UNIX script will exit with 1 if its not running, this is expected, so don't throw exception on 1
+ if (e.getExitValue() != 1) {
+ throw e;
+ }
+ }
+ rValue = e.getExitValue();
+ if (rValue == RHQControl.EXIT_CODE_OK) {
+ // if somehow we were told it was OK, change it to unknown since it can't be OK
+ rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
- return RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
+
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index e80edd9..8195284 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -351,8 +351,11 @@ public class Upgrade extends AbstractInstall {
// start the server, the invoke the installer and wait for the server to be completely installed
startRHQServerForInstallation();
- rValue = Math.max(rValue, runRHQServerInstaller());
- waitForRHQServerToInitialize();
+ int installerStatusCode = runRHQServerInstaller();
+ rValue = Math.max(rValue, installerStatusCode);
+ if (installerStatusCode == RHQControl.EXIT_CODE_OK) {
+ waitForRHQServerToInitialize();
+ }
return rValue;
}
@@ -546,7 +549,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = getServerPropertiesFile().getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
10 years, 4 months
[rhq] 23 commits - modules/core modules/enterprise
by Jay Shaughnessy
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java | 46 -
modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/discovery/DiscoveryAgentService.java | 19
modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java | 3
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java | 73 +-
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java | 140 ++--
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java | 98 --
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java | 36 -
modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java | 3
modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/sync/RuntimeSynchronizer.java | 2
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java | 338 ++++------
modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/AbstractResourceUpgradeHandlingTest.java | 4
modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java | 50 -
modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/ResourceUpgradeTest.java | 20
modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java | 37 -
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/ConfigurationManagerBeanTest.java | 28
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/LargeGroupPluginConfigurationTest.java | 17
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java | 65 +
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/test/TestAgentClient.java | 18
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java | 87 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java | 7
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java | 5
21 files changed, 582 insertions(+), 514 deletions(-)
New commits:
commit 5ef600890aaedcc3a040f5496d18c3c808d73795
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Jan 3 16:50:39 2014 -0500
fix merge issue, this class somehow disappeared
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java
new file mode 100644
index 0000000..b2ab566
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java
@@ -0,0 +1,94 @@
+/*
+ * 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.domain.install.remote;
+
+import java.io.Serializable;
+
+/**
+ * @author Greg Hinkle
+ */
+public class RemoteAccessInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ private String host;
+ private String user;
+ private String password;
+ private byte[] key;
+ private int port = 22;
+
+ public RemoteAccessInfo(String host, String user, byte[] key) {
+ this.host = host;
+ this.user = user;
+ this.key = key;
+ }
+
+ public RemoteAccessInfo(String host, String user, String password) {
+ this(host, 22, user, password);
+ }
+
+ public RemoteAccessInfo(String host, int port, String user, String password) {
+ this.host = host;
+ this.port = port;
+ this.user = user;
+ this.password = password;
+ }
+
+ public RemoteAccessInfo() {
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public byte[] getKey() {
+ return key;
+ }
+
+ public void setKey(byte[] key) {
+ this.key = key;
+ }
+}
commit d0ab1fc5e0163804c1db9179f598e4b8d791e56b
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Jan 3 16:05:16 2014 -0500
Some tweaks to test code
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
index 2bcb7aa..2cca059 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
@@ -421,10 +421,6 @@ public class FakeServerInventory {
return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- private Collection<ResourceSyncInfo> getResourceSyncInfo(Resource resource) {
- return resource == null ? null : convert(resource);
- }
-
private static Collection<ResourceSyncInfo> convert(Resource root) {
Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>();
convertInternal(root, result);
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java b/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
index c9528a0..5beb7a5 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
@@ -60,7 +60,7 @@ import org.rhq.test.JMockTest;
* <p>
* This class is used to declaratively setup a plugin container a test wants
* to use using the {@link PluginContainerSetup} annotation on a test method or class.
- *
+ *
* @author Lukas Krejci
*/
public class PluginContainerTest extends JMockTest {
@@ -96,9 +96,9 @@ public class PluginContainerTest extends JMockTest {
* provided by JMock.
* <p>
* This method together with {@link #setServerSideFake(String, Object)} provides a generic
- * "storage" for tests to share these objects and is only provided as a convenience to
+ * "storage" for tests to share these objects and is only provided as a convenience to
* the test writers. There's nothing that would manadate using it.
- *
+ *
* @param name
* @return
*/
@@ -114,7 +114,7 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * Returns the {@link PluginContainerConfiguration} as configured using
+ * Returns the {@link PluginContainerConfiguration} as configured using
* the {@link PluginContainerSetup} annotation on the current test (or null
* if no such thing is configured).
* @return
@@ -202,7 +202,7 @@ public class PluginContainerTest extends JMockTest {
* <p>
* This is not done automatically to support sharing the plugin container among
* multiple tests to simulate upgrades, etc.
- *
+ *
* @throws IOException
*/
public static void clearStorageOfCurrentPluginContainer() throws IOException {
@@ -219,9 +219,9 @@ public class PluginContainerTest extends JMockTest {
/**
* This method clears the storage of all tests made. This is useful in {@link AfterSuite}
- * method to clean up after all the tests (annotated with {@link PluginContainerSetup})
+ * method to clean up after all the tests (annotated with {@link PluginContainerSetup})
* that have been run.
- *
+ *
* @throws IOException
*/
public synchronized static void clearStorage() throws IOException {
@@ -238,9 +238,9 @@ public class PluginContainerTest extends JMockTest {
* this method is provided to automatically clean up after all the plugin container tests that ran
* in the test suite.
* <p>
- * If you use PluginContainerTest as a listener, you have to call {@link #clearStorage()} method
+ * If you use PluginContainerTest as a listener, you have to call {@link #clearStorage()} method
* on your own.
- *
+ *
* @throws IOException
*/
@AfterSuite
@@ -249,12 +249,12 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * This method returns the {@link PluginContainerConfiguration} that will be used in
- * the current test as was configured by the PluginContainerSetup annotation.
+ * This method returns the {@link PluginContainerConfiguration} that will be used in
+ * the current test as was configured by the PluginContainerSetup annotation.
* <p>
- * If your test class inherits from PluginContainerTest, you can override this method
+ * If your test class inherits from PluginContainerTest, you can override this method
* to provide custom configuration.
- *
+ *
* @param testObject the object of the current test
* @param testMethod the test method currently being executed on the test object
* @return
@@ -293,7 +293,7 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * This method is called after the test to tear down resources associated with the
+ * This method is called after the test to tear down resources associated with the
* current plugin container configuration.
*/
protected void tearDownPluginContainerConfiguration() {
@@ -371,9 +371,16 @@ public class PluginContainerTest extends JMockTest {
}
}
+ // Note that on WINDOWS this does not always/usually work and therefore test failures
+ // tend to happen. For unknown reasons windows/jvm keeps a lock on the plugin jar and it
+ // fails to delete, thus allowing for multiple versions of the plugin in the same path. I've
+ // added a system out to make it more clear in the log. There was no obvious workaround.
private void deletePlugins(File deployDirectory) throws IOException {
if (deployDirectory.exists()) {
- FileUtil.purge(deployDirectory, false);
+ FileUtil.purge(deployDirectory, true);
+ }
+ if (deployDirectory.exists()) {
+ throw new IllegalStateException("Failed to clean up plugins in [" + deployDirectory.getPath() + "]");
}
}
commit 5a919182bd6a2fe23ff0e920b7047b40f76c2edc
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 11:12:18 2014 -0500
i'll assume this was a mistake to import a JUnit annotation, and it should have been testng
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
index 4060d07..8227e38 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
@@ -28,8 +28,8 @@ import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
-import org.junit.BeforeClass;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
commit 348b20e0dd42e0cb4ad381f6589f7518d8c40567
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 10:50:02 2014 -0500
BZ 994250 - finish the merging/peer review for patches submitted so that rhqctl returns proper exit codes. Note that this completes the merge of the two submitted patches - see prior two commits to this one. This third commit fixes some problems with the original patches: 1. Some scripts/files are not found in bin/internal of the distro, but rather are in bin/ - so we need to avoid using getBinDir() in those cases 2. Fix the code to conform to code conventions - DEATH TO TABS! 3. Remove a constant that got resurrected (ControlCommand.RHQ_STORAGE_BASEDIR_PROP is no longer needed) 4. A couple other minor things
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index 5db80a1..a13a598 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,7 +60,6 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
- public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 28a37de..88b085f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index 1b3d47b..ed2b2f8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ org.apache.commons.exec.CommandLine commandLine;
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 5d540a1..e2ef3a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index a3920e0..7ef66b4 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if(isStorageRunning()) {
+ if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index cc3d8c2..e80edd9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
+ executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 250b302c53dfb020cdf61ef82162f9836ed32bfc
Author: burmanm <yak(a)iki.fi>
Date: Tue Aug 6 17:52:38 2013 +0200
Fix return codes of the rhqctl command, rebased to the 4.10 master.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index a13a598..5db80a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,6 +60,7 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
+ public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 88b085f..28a37de 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index ed2b2f8..1b3d47b 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine;
+ org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index e2ef3a8..5d540a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index 7ef66b4..a3920e0 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if (isStorageRunning()) {
+ if(isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index e80edd9..cc3d8c2 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
+ executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 0a6f9edeb14c03c5c0f1c9312bd0d939ba097c2c
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 17 21:35:47 2013 +0100
Bug 968361 - Improve database plugin design to support connection pooling
This changeset introduces a new API for database plugins and deprecates the previous one. Compatibility with the previous API will be maintained until next major version of RHQ.
The 'rhq-database-plugin' was based on org.rhq.plugins.database.DatabaseComponent interface which encouraged plugin authors to share a single JDBC connection across database components. This was wrong for various reasons (connection leaks, concurrent JDBC calls... etc).
The new API introduces three important classes:
* org.rhq.plugins.database.PooledConnectionProvider
* org.rhq.plugins.database.BasePooledConnectionProvider
* org.rhq.plugins.database.ConnectionPoolingSupport
BasePooledConnectionProvider is a base implementation of a PooledConnectionProvider. Plugin authors should create a concrete implementation of BasePooledConnectionProvider which overrides the #getDriverClass() method. This is important if a database plugin embeds a JDBC driver: the database-specific driver class must be loaded by the child plugin classloader.
ConnectionPoolingSupport helps to manage the compatibility with the old API. It's a contract that all new database resource components should obey to. It declares the following methods:
* #supportsConnectionPooling()
* #getPooledConnectionProvider()
Results of calls to #supportsConnectionPooling() #getPooledConnectionProvider() must be consistent. In practice, a top level server database component should be able to create a PooledConnectionProvider instance, and child servers and services should indicate they support connection pooling only if their parent component does.
The 'rhq-database-plugin' embeds the BoneCP library (JDBC connection pooling) and its dependencies (Google's Guava). Child plugins will have all the classes accessible as soon as they have this node in their plugin descriptor:
===
<depends plugin="Database" useClasses="true"/>
===
This changeset includes the necessary changes to support connection pooling in the Oracle, Postgres and MySQL plugins.
Thanks to Elias Ross for contributing the original patch from which this changeset is derived.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
deleted file mode 100644
index 6c81332..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ /dev/null
@@ -1,125 +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.plugins.mysql;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.HashMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A class to manage the connections to MySQL
- * This class keeps a cache of connections to MySQL and reuses them on demand
- * We assume single threaded access to the Connection in the agent
- * this will need to be reworked if that assumption is not correct
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionManager {
-
- private HashMap<MySqlConnectionInfo, Connection> connections;
- private static MySqlConnectionManager singleton;
- private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
-
- private MySqlConnectionManager() {
- connections = new HashMap<MySqlConnectionInfo,Connection>();
- }
-
- static MySqlConnectionManager getConnectionManager() {
- if (singleton == null) {
- singleton = new MySqlConnectionManager();
- }
- return singleton;
- }
-
- public void shutdown() {
- Driver driver = null;
- for (Connection conn : connections.values()) {
- try {
- if (driver == null) {
- String driverName = conn.getMetaData().getDriverName();
- driver = DriverManager.getDriver(driverName);
- }
- conn.close();
- }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
- }
- // deregister driver as well
- if (driver != null) {
- try {
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- logger.warn("Unable to deregister MySQL Driver on shutdown");
- }
- }
- }
-
- void closeConnection(MySqlConnectionInfo info) {
- Connection conn = connections.get(info);
- if (conn != null) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Closing Connection to " + info.buildURL());
- }
- conn.close();
- } catch (SQLException e) {
- logger.warn("Problem closing connection to " + info.buildURL() + " on close");
- }
- }
- connections.remove(info);
- }
-
- Connection getConnection (MySqlConnectionInfo info) throws SQLException {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
-
- Connection conn = connections.get(info);
- String url = info.buildURL();
- if (conn == null) {
- if (logger.isInfoEnabled()) {
- logger.info("Attemping connection to " + url);
- }
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- if (logger.isInfoEnabled()) {
- logger.info("Successfully connected to " + url);
- }
- connections.put(info, conn);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Reusing existing connection to " + url);
- }
- }
-
- // check the validity of the connection
- if (!conn.isValid(0)) {
- // attempt a single reconnect here and now
- conn.close();
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- connections.put(info, conn);
- logger.info("Refreshed a connection to " + url);
- }
- return conn;
- }
-
-}
commit 08c7491931efe151b6cd798a429821d57b33cec9
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Dec 23 14:50:55 2013 +0100
Plugin validation for mysql plugin was failing because the Class.forName() statement was invoked from the constructor of the component. I moved this code to the method that actually opens the connection. This is the same strategy we use with our postgres plugin.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
new file mode 100644
index 0000000..6c81332
--- /dev/null
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
@@ -0,0 +1,125 @@
+/*
+ * 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.plugins.mysql;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.HashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A class to manage the connections to MySQL
+ * This class keeps a cache of connections to MySQL and reuses them on demand
+ * We assume single threaded access to the Connection in the agent
+ * this will need to be reworked if that assumption is not correct
+ * @author Steve Millidge (C2B2 Consulting Limited)
+ */
+class MySqlConnectionManager {
+
+ private HashMap<MySqlConnectionInfo, Connection> connections;
+ private static MySqlConnectionManager singleton;
+ private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
+
+ private MySqlConnectionManager() {
+ connections = new HashMap<MySqlConnectionInfo,Connection>();
+ }
+
+ static MySqlConnectionManager getConnectionManager() {
+ if (singleton == null) {
+ singleton = new MySqlConnectionManager();
+ }
+ return singleton;
+ }
+
+ public void shutdown() {
+ Driver driver = null;
+ for (Connection conn : connections.values()) {
+ try {
+ if (driver == null) {
+ String driverName = conn.getMetaData().getDriverName();
+ driver = DriverManager.getDriver(driverName);
+ }
+ conn.close();
+ }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
+ }
+ // deregister driver as well
+ if (driver != null) {
+ try {
+ DriverManager.deregisterDriver(driver);
+ } catch (SQLException ex) {
+ logger.warn("Unable to deregister MySQL Driver on shutdown");
+ }
+ }
+ }
+
+ void closeConnection(MySqlConnectionInfo info) {
+ Connection conn = connections.get(info);
+ if (conn != null) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Closing Connection to " + info.buildURL());
+ }
+ conn.close();
+ } catch (SQLException e) {
+ logger.warn("Problem closing connection to " + info.buildURL() + " on close");
+ }
+ }
+ connections.remove(info);
+ }
+
+ Connection getConnection (MySqlConnectionInfo info) throws SQLException {
+ try {
+ Class.forName("com.mysql.jdbc.Driver");
+ } catch (Exception ex) {
+ logger.error("Unable to find com.mysql.jdbc.Driver");
+ }
+
+ Connection conn = connections.get(info);
+ String url = info.buildURL();
+ if (conn == null) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Attemping connection to " + url);
+ }
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ if (logger.isInfoEnabled()) {
+ logger.info("Successfully connected to " + url);
+ }
+ connections.put(info, conn);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Reusing existing connection to " + url);
+ }
+ }
+
+ // check the validity of the connection
+ if (!conn.isValid(0)) {
+ // attempt a single reconnect here and now
+ conn.close();
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ connections.put(info, conn);
+ logger.info("Refreshed a connection to " + url);
+ }
+ return conn;
+ }
+
+}
commit e7efdb0be2b07af94576bf50388b5dee4084f164
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Dec 13 17:39:57 2013 +0100
[BZ 1042892] Don't use ${} notation for shell variables in CLI scripts.
As of BZ 959603, the files are subject to maven resource filtering and
so it can happen that the build server passes a variable to the build
process that clashes with the variable name defined in the script.
(Yes, this actually happened on our Jenkins server that passed
"-DCLASSPATH=" to the maven process for some reason, which resulted in
wonderful corruption of the rhq-cli.sh script).
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
index 112ec66..80f24c5 100755
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
@@ -3,6 +3,14 @@
#===========================================================================
#
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
+# notation. This file is subject to maven resource variable expansion
+# during the build and so it can happen that the build environment
+# could corrupt this file by expanding variables that clash with
+# the names defined herein.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# RHQ_CLI_DEBUG - If this is defined, the script will emit debug
# messages. It will also enable debug
# messages to be emitted from the cli itself.
@@ -41,7 +49,7 @@
# to the CLI's defaults, then you will want to
# use RHQ_CLI_ADDITIONAL_JAVA_OPTS instead.
#
-#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
+#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
# RHQ_CLI_JAVA_ENDORSED_DIRS - Java VM command line option to set the
# endorsed dirs for the CLI's VM. If this
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
index 8399b50..b0980e6 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
@@ -11,6 +11,14 @@
# set via rhq-client-env.sh, which is sourced by this script.
# =============================================================================
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
+# notation. This file is subject to maven resource variable expansion
+# during the build and so it can happen that the build environment
+# could corrupt this file by expanding variables that clash with
+# the names defined herein.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# ----------------------------------------------------------------------
# Subroutine that simply dumps a message iff debug mode is enabled
# ----------------------------------------------------------------------
@@ -42,16 +50,17 @@ esac
_DOLLARZERO=`readlink "$0" 2>/dev/null || echo "$0"`
RHQ_CLI_BIN_DIR_PATH=`dirname "$_DOLLARZERO"`
-if [ -f "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" ]; then
- debug_msg "Loading environment script: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
- . "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" $*
+if [ -f "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" ]; then
+ debug_msg "Loading environment script: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
+ . "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" $*
else
- debug_msg "No environment script found at: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
+ debug_msg "No environment script found at: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
fi
# this variable is set during the build and defines the desired default behavior for directory changing
# we do want to change the dir in RHQ, but possibly don't want to do that in JBoss ON for backwards compatibility
# reasons.
+# (yes, this USES ${} in the source (not in the distribution) because we seed the default value from the build)
RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT=${rhq.cli.change-dir-on-start-default}
if [ -z "$RHQ_CLI_CHANGE_DIR_ON_START" ]; then
RHQ_CLI_CHANGE_DIR_ON_START="$RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT"
@@ -61,24 +70,24 @@ fi
# Previous versions always changed directory.
if [ -n "$RHQ_CLI_CHANGE_DIR_ON_START" -a "$RHQ_CLI_CHANGE_DIR_ON_START" != "false" ]; then
if [ -z "$RHQ_CLI_HOME" ]; then
- cd "${RHQ_CLI_BIN_DIR_PATH}/.."
+ cd "$RHQ_CLI_BIN_DIR_PATH/.."
else
- cd "${RHQ_CLI_HOME}" || {
- echo "Cannot go to the RHQ_CLI_HOME directory: ${RHQ_CLI_HOME}"
+ cd "$RHQ_CLI_HOME" || {
+ echo "Cannot go to the RHQ_CLI_HOME directory: $RHQ_CLI_HOME"
exit 1
}
fi
RHQ_CLI_HOME=`pwd`
else
if [ -z "$RHQ_CLI_HOME" ]; then
- RHQ_CLI_HOME="${RHQ_CLI_BIN_DIR_PATH}/.."
+ RHQ_CLI_HOME="$RHQ_CLI_BIN_DIR_PATH/.."
fi
#get an absolute path
RHQ_CLI_HOME=`readlink -f "$RHQ_CLI_HOME"`
if [ ! -d "$RHQ_CLI_HOME" ]; then
- echo "RHQ_CLI_HOME detected or defined as [${RHQ_CLI_HOME}] doesn't seem to exist or is not a directory"
+ echo "RHQ_CLI_HOME detected or defined as [$RHQ_CLI_HOME] doesn't seem to exist or is not a directory"
exit 1
fi
fi
@@ -90,7 +99,7 @@ debug_msg "RHQ_CLI_HOME: $RHQ_CLI_HOME"
# sample modules.
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_MODULES_DIR" ]; then
- RHQ_CLI_MODULES_DIR="${RHQ_CLI_HOME}/samples/modules"
+ RHQ_CLI_MODULES_DIR="$RHQ_CLI_HOME/samples/modules"
fi
# ----------------------------------------------------------------------
@@ -110,7 +119,7 @@ fi
if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
if [ -z "$RHQ_CLI_JAVA_HOME" ]; then
- RHQ_CLI_JAVA_HOME="${RHQ_CLI_HOME}/jre"
+ RHQ_CLI_JAVA_HOME="$RHQ_CLI_HOME/jre"
if [ -d "$RHQ_CLI_JAVA_HOME" ]; then
debug_msg "Using the embedded JRE"
else
@@ -119,7 +128,7 @@ if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
fi
fi
debug_msg "RHQ_CLI_JAVA_HOME: $RHQ_CLI_JAVA_HOME"
- RHQ_CLI_JAVA_EXE_FILE_PATH=${RHQ_CLI_JAVA_HOME}/bin/java
+ RHQ_CLI_JAVA_EXE_FILE_PATH="$RHQ_CLI_JAVA_HOME/bin/java"
fi
debug_msg "RHQ_CLI_JAVA_EXE_FILE_PATH: $RHQ_CLI_JAVA_EXE_FILE_PATH"
@@ -133,14 +142,14 @@ fi
# Prepare the classpath (take into account possible spaces in dir names)
# ----------------------------------------------------------------------
-CLASSPATH="${RHQ_CLI_HOME}/conf"
-_JAR_FILES=`cd "${RHQ_CLI_HOME}/lib";ls -1 *.jar`
+CLASSPATH="$RHQ_CLI_HOME/conf"
+_JAR_FILES=`cd "$RHQ_CLI_HOME/lib";ls -1 *.jar`
for _JAR in $_JAR_FILES ; do
- _JAR="${RHQ_CLI_HOME}/lib/${_JAR}"
+ _JAR="$RHQ_CLI_HOME/lib/$_JAR"
if [ -z "$CLASSPATH" ]; then
- CLASSPATH="${_JAR}"
+ CLASSPATH="$_JAR"
else
- CLASSPATH="${CLASSPATH}:${_JAR}"
+ CLASSPATH="$CLASSPATH:$_JAR"
fi
debug_msg "CLASSPATH entry: $_JAR"
done
@@ -151,7 +160,7 @@ debug_msg "CLASSPATH entry: $_JAR"
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_JAVA_OPTS" ]; then
- RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
+ RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
fi
debug_msg "RHQ_CLI_JAVA_OPTS: $RHQ_CLI_JAVA_OPTS"
@@ -159,7 +168,7 @@ if [ "$RHQ_CLI_JAVA_ENDORSED_DIRS" = "none" ]; then
debug_msg "Not explicitly setting java.endorsed.dirs"
else
if [ -z "$RHQ_CLI_JAVA_ENDORSED_DIRS" ]; then
- RHQ_CLI_JAVA_ENDORSED_DIRS="${RHQ_CLI_HOME}/lib/endorsed"
+ RHQ_CLI_JAVA_ENDORSED_DIRS="$RHQ_CLI_HOME/lib/endorsed"
fi
# convert the path if on Windows
@@ -167,14 +176,14 @@ else
RHQ_CLI_JAVA_ENDORSED_DIRS=`cygpath --windows --path "$RHQ_CLI_JAVA_ENDORSED_DIRS"`
fi
debug_msg "RHQ_CLI_JAVA_ENDORSED_DIRS: $RHQ_CLI_JAVA_ENDORSED_DIRS"
- _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"${RHQ_CLI_JAVA_ENDORSED_DIRS}\""
+ _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"$RHQ_CLI_JAVA_ENDORSED_DIRS\""
fi
if [ "$RHQ_CLI_JAVA_LIBRARY_PATH" = "none" ]; then
debug_msg "Not explicitly setting java.library.path"
else
if [ -z "$RHQ_CLI_JAVA_LIBRARY_PATH" ]; then
- RHQ_CLI_JAVA_LIBRARY_PATH="${RHQ_CLI_HOME}/lib"
+ RHQ_CLI_JAVA_LIBRARY_PATH="$RHQ_CLI_HOME/lib"
fi
# convert the path if on Windows
@@ -182,7 +191,7 @@ else
RHQ_CLI_JAVA_LIBRARY_PATH=`cygpath --windows --path "$RHQ_CLI_JAVA_LIBRARY_PATH"`
fi
debug_msg "RHQ_CLI_JAVA_LIBRARY_PATH: $RHQ_CLI_JAVA_LIBRARY_PATH"
- _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"${RHQ_CLI_JAVA_LIBRARY_PATH}\""
+ _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"$RHQ_CLI_JAVA_LIBRARY_PATH\""
fi
debug_msg "RHQ_CLI_ADDITIONAL_JAVA_OPTS: $RHQ_CLI_ADDITIONAL_JAVA_OPTS"
@@ -201,8 +210,8 @@ if [ -n "$RHQ_CLI_DEBUG" ]; then
fi
# create the logs directory
-if [ ! -d "${RHQ_CLI_HOME}/logs" ]; then
- mkdir "${RHQ_CLI_HOME}/logs"
+if [ ! -d "$RHQ_CLI_HOME/logs" ]; then
+ mkdir "$RHQ_CLI_HOME/logs"
fi
# convert some of the paths if we are on Windows
@@ -220,15 +229,15 @@ fi
debug_msg "Executing the CLI with this command line:"
exit_code=0
if [ -z "$RHQ_CLI_CMDLINE_OPTS" ]; then
- debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain $@"
- "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain "$@"
+ debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $@"
+ "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain "$@"
exit_code=$?
else
- debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}"
- "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}
+ debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS"
+ "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS
exit_code=$?
fi
debug_msg "$0 done."
-exit ${exit_code}
+exit $exit_code
commit c215cd1fcf8f3676928c3a0d12de0174691c25f0
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 12 22:43:55 2013 +0100
Better testability in the ModulesDirectoryScriptSourceProvider.
Also changed the default module path from "./samples/modules" to
"./modules". This should be safe with the fix for BZ 959603 which
causes the CLI to always pass the system property to load the modules
from. The new default path is less coupled with the FS layout of the CLI
distribution.
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
index 8227e38..4060d07 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
@@ -28,8 +28,8 @@ import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
+import org.junit.BeforeClass;
import org.testng.annotations.AfterClass;
-import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
commit cade5bd5f33aa7b8e8aafcaa883b0eaa053b8878
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 12 23:10:14 2013 +0100
[BZ 959603] - Don't change CWD on CLI startup.
Since the very beginning of CLI, RHQ always changed the CWD to
$RHQ_CLI_HOME when starting up, which is rather strange and
non-standard.
To keep the backwards compatibility, the behavior can be toggled on
or off.
The default behavior can be changed in the build by setting the
rhq.cli.change-dir-on-start-default
property. In RHQ, this defaults to "false", causing RHQ to NOT change
directories when starting up the CLI anymore.
Further, the behavior can be toggled by the user by setting the
RHQ_CLI_CHANGE_DIR_ON_START environment variable to "true" (or any
other value but "false" actually) or "false" explicitly.
Additionally, a new environment variable called "RHQ_CLI_MODULES_DIR"
was added. This was necessary to not break the loading of the
provided CommonJS modules available in the
$RHQ_CLI_HOME/samples/modules directory if the CLI was started from
another directory.
By adding the new RHQ_CLI_MODULES_DIR, which is initialized to the
correct value if not provided externally, we correctly load the
sample modules with the additional benefit of easier change of the
modules location for the user (previously this was only possible
through
RHQ_CLI_ADDITIONAL_JAVA_OPTS="-Drhq.scripting.modules.root-dir=..."
, while now one only needs RHQ_CLI_MODULES_DIR=...).
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
index 80f24c5..112ec66 100755
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
@@ -3,14 +3,6 @@
#===========================================================================
#
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
-# notation. This file is subject to maven resource variable expansion
-# during the build and so it can happen that the build environment
-# could corrupt this file by expanding variables that clash with
-# the names defined herein.
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
# RHQ_CLI_DEBUG - If this is defined, the script will emit debug
# messages. It will also enable debug
# messages to be emitted from the cli itself.
@@ -49,7 +41,7 @@
# to the CLI's defaults, then you will want to
# use RHQ_CLI_ADDITIONAL_JAVA_OPTS instead.
#
-#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
+#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
# RHQ_CLI_JAVA_ENDORSED_DIRS - Java VM command line option to set the
# endorsed dirs for the CLI's VM. If this
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
index b0980e6..8399b50 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
@@ -11,14 +11,6 @@
# set via rhq-client-env.sh, which is sourced by this script.
# =============================================================================
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
-# notation. This file is subject to maven resource variable expansion
-# during the build and so it can happen that the build environment
-# could corrupt this file by expanding variables that clash with
-# the names defined herein.
-# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
# ----------------------------------------------------------------------
# Subroutine that simply dumps a message iff debug mode is enabled
# ----------------------------------------------------------------------
@@ -50,17 +42,16 @@ esac
_DOLLARZERO=`readlink "$0" 2>/dev/null || echo "$0"`
RHQ_CLI_BIN_DIR_PATH=`dirname "$_DOLLARZERO"`
-if [ -f "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" ]; then
- debug_msg "Loading environment script: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
- . "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" $*
+if [ -f "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" ]; then
+ debug_msg "Loading environment script: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
+ . "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" $*
else
- debug_msg "No environment script found at: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
+ debug_msg "No environment script found at: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
fi
# this variable is set during the build and defines the desired default behavior for directory changing
# we do want to change the dir in RHQ, but possibly don't want to do that in JBoss ON for backwards compatibility
# reasons.
-# (yes, this USES ${} in the source (not in the distribution) because we seed the default value from the build)
RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT=${rhq.cli.change-dir-on-start-default}
if [ -z "$RHQ_CLI_CHANGE_DIR_ON_START" ]; then
RHQ_CLI_CHANGE_DIR_ON_START="$RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT"
@@ -70,24 +61,24 @@ fi
# Previous versions always changed directory.
if [ -n "$RHQ_CLI_CHANGE_DIR_ON_START" -a "$RHQ_CLI_CHANGE_DIR_ON_START" != "false" ]; then
if [ -z "$RHQ_CLI_HOME" ]; then
- cd "$RHQ_CLI_BIN_DIR_PATH/.."
+ cd "${RHQ_CLI_BIN_DIR_PATH}/.."
else
- cd "$RHQ_CLI_HOME" || {
- echo "Cannot go to the RHQ_CLI_HOME directory: $RHQ_CLI_HOME"
+ cd "${RHQ_CLI_HOME}" || {
+ echo "Cannot go to the RHQ_CLI_HOME directory: ${RHQ_CLI_HOME}"
exit 1
}
fi
RHQ_CLI_HOME=`pwd`
else
if [ -z "$RHQ_CLI_HOME" ]; then
- RHQ_CLI_HOME="$RHQ_CLI_BIN_DIR_PATH/.."
+ RHQ_CLI_HOME="${RHQ_CLI_BIN_DIR_PATH}/.."
fi
#get an absolute path
RHQ_CLI_HOME=`readlink -f "$RHQ_CLI_HOME"`
if [ ! -d "$RHQ_CLI_HOME" ]; then
- echo "RHQ_CLI_HOME detected or defined as [$RHQ_CLI_HOME] doesn't seem to exist or is not a directory"
+ echo "RHQ_CLI_HOME detected or defined as [${RHQ_CLI_HOME}] doesn't seem to exist or is not a directory"
exit 1
fi
fi
@@ -99,7 +90,7 @@ debug_msg "RHQ_CLI_HOME: $RHQ_CLI_HOME"
# sample modules.
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_MODULES_DIR" ]; then
- RHQ_CLI_MODULES_DIR="$RHQ_CLI_HOME/samples/modules"
+ RHQ_CLI_MODULES_DIR="${RHQ_CLI_HOME}/samples/modules"
fi
# ----------------------------------------------------------------------
@@ -119,7 +110,7 @@ fi
if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
if [ -z "$RHQ_CLI_JAVA_HOME" ]; then
- RHQ_CLI_JAVA_HOME="$RHQ_CLI_HOME/jre"
+ RHQ_CLI_JAVA_HOME="${RHQ_CLI_HOME}/jre"
if [ -d "$RHQ_CLI_JAVA_HOME" ]; then
debug_msg "Using the embedded JRE"
else
@@ -128,7 +119,7 @@ if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
fi
fi
debug_msg "RHQ_CLI_JAVA_HOME: $RHQ_CLI_JAVA_HOME"
- RHQ_CLI_JAVA_EXE_FILE_PATH="$RHQ_CLI_JAVA_HOME/bin/java"
+ RHQ_CLI_JAVA_EXE_FILE_PATH=${RHQ_CLI_JAVA_HOME}/bin/java
fi
debug_msg "RHQ_CLI_JAVA_EXE_FILE_PATH: $RHQ_CLI_JAVA_EXE_FILE_PATH"
@@ -142,14 +133,14 @@ fi
# Prepare the classpath (take into account possible spaces in dir names)
# ----------------------------------------------------------------------
-CLASSPATH="$RHQ_CLI_HOME/conf"
-_JAR_FILES=`cd "$RHQ_CLI_HOME/lib";ls -1 *.jar`
+CLASSPATH="${RHQ_CLI_HOME}/conf"
+_JAR_FILES=`cd "${RHQ_CLI_HOME}/lib";ls -1 *.jar`
for _JAR in $_JAR_FILES ; do
- _JAR="$RHQ_CLI_HOME/lib/$_JAR"
+ _JAR="${RHQ_CLI_HOME}/lib/${_JAR}"
if [ -z "$CLASSPATH" ]; then
- CLASSPATH="$_JAR"
+ CLASSPATH="${_JAR}"
else
- CLASSPATH="$CLASSPATH:$_JAR"
+ CLASSPATH="${CLASSPATH}:${_JAR}"
fi
debug_msg "CLASSPATH entry: $_JAR"
done
@@ -160,7 +151,7 @@ debug_msg "CLASSPATH entry: $_JAR"
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_JAVA_OPTS" ]; then
- RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
+ RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
fi
debug_msg "RHQ_CLI_JAVA_OPTS: $RHQ_CLI_JAVA_OPTS"
@@ -168,7 +159,7 @@ if [ "$RHQ_CLI_JAVA_ENDORSED_DIRS" = "none" ]; then
debug_msg "Not explicitly setting java.endorsed.dirs"
else
if [ -z "$RHQ_CLI_JAVA_ENDORSED_DIRS" ]; then
- RHQ_CLI_JAVA_ENDORSED_DIRS="$RHQ_CLI_HOME/lib/endorsed"
+ RHQ_CLI_JAVA_ENDORSED_DIRS="${RHQ_CLI_HOME}/lib/endorsed"
fi
# convert the path if on Windows
@@ -176,14 +167,14 @@ else
RHQ_CLI_JAVA_ENDORSED_DIRS=`cygpath --windows --path "$RHQ_CLI_JAVA_ENDORSED_DIRS"`
fi
debug_msg "RHQ_CLI_JAVA_ENDORSED_DIRS: $RHQ_CLI_JAVA_ENDORSED_DIRS"
- _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"$RHQ_CLI_JAVA_ENDORSED_DIRS\""
+ _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"${RHQ_CLI_JAVA_ENDORSED_DIRS}\""
fi
if [ "$RHQ_CLI_JAVA_LIBRARY_PATH" = "none" ]; then
debug_msg "Not explicitly setting java.library.path"
else
if [ -z "$RHQ_CLI_JAVA_LIBRARY_PATH" ]; then
- RHQ_CLI_JAVA_LIBRARY_PATH="$RHQ_CLI_HOME/lib"
+ RHQ_CLI_JAVA_LIBRARY_PATH="${RHQ_CLI_HOME}/lib"
fi
# convert the path if on Windows
@@ -191,7 +182,7 @@ else
RHQ_CLI_JAVA_LIBRARY_PATH=`cygpath --windows --path "$RHQ_CLI_JAVA_LIBRARY_PATH"`
fi
debug_msg "RHQ_CLI_JAVA_LIBRARY_PATH: $RHQ_CLI_JAVA_LIBRARY_PATH"
- _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"$RHQ_CLI_JAVA_LIBRARY_PATH\""
+ _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"${RHQ_CLI_JAVA_LIBRARY_PATH}\""
fi
debug_msg "RHQ_CLI_ADDITIONAL_JAVA_OPTS: $RHQ_CLI_ADDITIONAL_JAVA_OPTS"
@@ -210,8 +201,8 @@ if [ -n "$RHQ_CLI_DEBUG" ]; then
fi
# create the logs directory
-if [ ! -d "$RHQ_CLI_HOME/logs" ]; then
- mkdir "$RHQ_CLI_HOME/logs"
+if [ ! -d "${RHQ_CLI_HOME}/logs" ]; then
+ mkdir "${RHQ_CLI_HOME}/logs"
fi
# convert some of the paths if we are on Windows
@@ -229,15 +220,15 @@ fi
debug_msg "Executing the CLI with this command line:"
exit_code=0
if [ -z "$RHQ_CLI_CMDLINE_OPTS" ]; then
- debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $@"
- "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain "$@"
+ debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain $@"
+ "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain "$@"
exit_code=$?
else
- debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS"
- "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS
+ debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}"
+ "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}
exit_code=$?
fi
debug_msg "$0 done."
-exit $exit_code
+exit ${exit_code}
commit d7f38193b70683744ee13d7238d2d5b7f75b82db
Author: John Sanda <jsanda(a)redhat.com>
Date: Thu Dec 12 10:14:12 2013 -0500
calculate bloom filters
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
index 77c22fd..d185f56 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -24,8 +24,113 @@
var time = new Time();
+ function BloomFilter(keys) {
+ function BloomSpecification(k, bucketsPerElement) {
+ this.K = k;
+ this.bucketsPerElement = bucketsPerElement;
+ }
+
+ // This is taken directly from BloomCalculations.java
+ this.probs = [
+ [1.0], // dummy row representing 0 buckets per element
+ [1.0, 1.0], // dummy row representing 1 buckets per element
+ [1.0, 0.393, 0.400],
+ [1.0, 0.283, 0.237, 0.253],
+ [1.0, 0.221, 0.155, 0.147, 0.160],
+ [1.0, 0.181, 0.109, 0.092, 0.092, 0.101], // 5
+ [1.0, 0.154, 0.0804, 0.0609, 0.0561, 0.0578, 0.0638],
+ [1.0, 0.133, 0.0618, 0.0423, 0.0359, 0.0347, 0.0364],
+ [1.0, 0.118, 0.0489, 0.0306, 0.024, 0.0217, 0.0216, 0.0229],
+ [1.0, 0.105, 0.0397, 0.0228, 0.0166, 0.0141, 0.0133, 0.0135, 0.0145],
+ [1.0, 0.0952, 0.0329, 0.0174, 0.0118, 0.00943, 0.00844, 0.00819, 0.00846], // 10
+ [1.0, 0.0869, 0.0276, 0.0136, 0.00864, 0.0065, 0.00552, 0.00513, 0.00509],
+ [1.0, 0.08, 0.0236, 0.0108, 0.00646, 0.00459, 0.00371, 0.00329, 0.00314],
+ [1.0, 0.074, 0.0203, 0.00875, 0.00492, 0.00332, 0.00255, 0.00217, 0.00199, 0.00194],
+ [1.0, 0.0689, 0.0177, 0.00718, 0.00381, 0.00244, 0.00179, 0.00146, 0.00129, 0.00121, 0.0012],
+ [1.0, 0.0645, 0.0156, 0.00596, 0.003, 0.00183, 0.00128, 0.001, 0.000852, 0.000775, 0.000744], // 15
+ [1.0, 0.0606, 0.0138, 0.005, 0.00239, 0.00139, 0.000935, 0.000702, 0.000574, 0.000505, 0.00047, 0.000459],
+ [1.0, 0.0571, 0.0123, 0.00423, 0.00193, 0.00107, 0.000692, 0.000499, 0.000394, 0.000335, 0.000302, 0.000287, 0.000284],
+ [1.0, 0.054, 0.0111, 0.00362, 0.00158, 0.000839, 0.000519, 0.00036, 0.000275, 0.000226, 0.000198, 0.000183, 0.000176],
+ [1.0, 0.0513, 0.00998, 0.00312, 0.0013, 0.000663, 0.000394, 0.000264, 0.000194, 0.000155, 0.000132, 0.000118, 0.000111, 0.000109],
+ [1.0, 0.0488, 0.00906, 0.0027, 0.00108, 0.00053, 0.000303, 0.000196, 0.00014, 0.000108, 8.89e-05, 7.77e-05, 7.12e-05, 6.79e-05, 6.71e-05] // 20
+ ]; // the first column is a dummy column representing K=0.
+
+ var self = this;
+ var excess = 20;
+ var bitset_excess = 20;
+ var minBuckets = 2;
+ var minK = 1;
+ var optKPerBuckets = [];
+ var maxFalsePosProb = 0.01;
+
+
+ for (i = 0; i < this.probs.length; i++) {
+ var min = java.lang.Double.MAX_VALUE;
+ var prob = this.probs[i];
+ for (j = 0; j < prob.length; j++) {
+ if (prob[j] < min) {
+ min = prob[j];
+ optKPerBuckets[i] = Math.max(minK, j);
+ }
+ }
+ }
+
+ var bucketsPerElement = maxBucketsPerElement(keys);
+ var spec = computeBloomSpec(bucketsPerElement, maxFalsePosProb);
+ var numBits = (keys * spec.bucketsPerElement) + bitset_excess;
+ var wordCount = bits2words(numBits);
+
+ if (wordCount > java.lang.Integer.MAX_VALUE) {
+ throw "Bloom filter size is > 16GB, reduce the bloom_filter_fp_chance";
+ }
+
+ var bytes = wordCount * 8;
+ this.size = bytes + 8;
+
+ function maxBucketsPerElement(numElements) {
+ numElements = Math.max(1, numElements);
+ var v = (java.lang.Long.MAX_VALUE - excess) / keys;
+ if (v < 1) {
+ throw "Cannot compute probabilities for " + numElements + " elements.";
+ }
+ return Math.min(self.probs.length - 1, v);
+ }
+
+ // This is taken from BloomCalculations.java
+ function computeBloomSpec(maxBucketsPerElement, maxFalsePosProb) {
+ var maxK = self.probs[maxBucketsPerElement].length - 1;
+
+ // Handle the trivial cases
+ if(maxFalsePosProb >= self.probs[minBuckets][minK]) {
+ return new BloomSpecification(2, optKPerBuckets[2]);
+ }
+ if (maxFalsePosProb < self.probs[maxBucketsPerElement][maxK]) {
+ throw "Unable to satisfy " + maxFalsePosProb + " with " + maxBucketsPerElement + " buckets per element";
+ }
+
+ // First find the minimal required number of buckets:
+ var bucketsPerElement = 2;
+ var K = optKPerBuckets[2];
+ while(self.probs[bucketsPerElement][K] > maxFalsePosProb){
+ bucketsPerElement++;
+ K = optKPerBuckets[bucketsPerElement];
+ }
+ // Now that the number of buckets is sufficient, see if we can relax K
+ // without losing too much precision.
+ while(self.probs[bucketsPerElement][K - 1] <= maxFalsePosProb) {
+ K--;
+ }
+ println('K = ' + K + ', bucketsPerElement = ' + bucketsPerElement);
+ return new BloomSpecification(K, bucketsPerElement);
+ }
+
+ function bits2words(numBits) {
+ return (((numBits-1)>>>6)+1);
+ }
+ }
+
/**
- * Encapsulates sizing data for a set of rows, having the samae number of columns and
+ * Encapsulates sizing data for a set of rows, having the same number of columns and
* each column being the same size.
*/
function RowInfo() {
@@ -124,6 +229,7 @@
schedules) {
var table = new Table(name);
table.data = calculateData(schedules, columnSize, rowKeySize, duration);
+ table.bloomFilter = new BloomFilter(5).size;
util.foreach(table.data.rows, function(interval) {
var rowInfo = table.data.rows[interval];
@@ -216,8 +322,8 @@
var rowKeyValueSize = 4;
var columnNameSize = 8;
- return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize, time.week,
- schedules);
+ return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize,
+ time.week, schedules);
};
/**
@@ -261,4 +367,9 @@
exports.sizeOf24HourMetrics = function(schedules) {
return calculateAggregatesTableSize('twenty_four_hour_metrics', time.day * 365, schedules);
};
+
+ // exposed for testing
+ exports.bloomFilter = function(keys) {
+ return new BloomFilter(keys).size;
+ }
})();
\ No newline at end of file
commit 2d3db381399b7dae1328e63be912ad8cc976b624
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Dec 11 17:03:35 2013 -0500
adding some usage docs
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
index 77e78b7..77c22fd 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -5,6 +5,11 @@
* installs, some input, in terms of the numbers and types of resources, will have to be provided in order to generate
* the estimates. Right now the module has functionality for generating the sizes of the data and partition index files
* for the metrics tables (which does not include the metrics_index table).
+ *
+ * current usage (from CLI shell):
+ *
+ * $ storage = require('modules:/rhq.storage.sizing.js');
+ * $ results = storage.sizeOfRawMetrics({30000: 5, 60000: 5});
*/
(function() {
var util = require('modules:/util');
commit 059d79cb7dc66b343785a620f3e4dc11d0f654b5
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Dec 11 16:59:10 2013 -0500
initial commit for storage sizing CLI script
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
index d185f56..77e78b7 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -5,11 +5,6 @@
* installs, some input, in terms of the numbers and types of resources, will have to be provided in order to generate
* the estimates. Right now the module has functionality for generating the sizes of the data and partition index files
* for the metrics tables (which does not include the metrics_index table).
- *
- * current usage (from CLI shell):
- *
- * $ storage = require('modules:/rhq.storage.sizing.js');
- * $ results = storage.sizeOfRawMetrics({30000: 5, 60000: 5});
*/
(function() {
var util = require('modules:/util');
@@ -24,113 +19,8 @@
var time = new Time();
- function BloomFilter(keys) {
- function BloomSpecification(k, bucketsPerElement) {
- this.K = k;
- this.bucketsPerElement = bucketsPerElement;
- }
-
- // This is taken directly from BloomCalculations.java
- this.probs = [
- [1.0], // dummy row representing 0 buckets per element
- [1.0, 1.0], // dummy row representing 1 buckets per element
- [1.0, 0.393, 0.400],
- [1.0, 0.283, 0.237, 0.253],
- [1.0, 0.221, 0.155, 0.147, 0.160],
- [1.0, 0.181, 0.109, 0.092, 0.092, 0.101], // 5
- [1.0, 0.154, 0.0804, 0.0609, 0.0561, 0.0578, 0.0638],
- [1.0, 0.133, 0.0618, 0.0423, 0.0359, 0.0347, 0.0364],
- [1.0, 0.118, 0.0489, 0.0306, 0.024, 0.0217, 0.0216, 0.0229],
- [1.0, 0.105, 0.0397, 0.0228, 0.0166, 0.0141, 0.0133, 0.0135, 0.0145],
- [1.0, 0.0952, 0.0329, 0.0174, 0.0118, 0.00943, 0.00844, 0.00819, 0.00846], // 10
- [1.0, 0.0869, 0.0276, 0.0136, 0.00864, 0.0065, 0.00552, 0.00513, 0.00509],
- [1.0, 0.08, 0.0236, 0.0108, 0.00646, 0.00459, 0.00371, 0.00329, 0.00314],
- [1.0, 0.074, 0.0203, 0.00875, 0.00492, 0.00332, 0.00255, 0.00217, 0.00199, 0.00194],
- [1.0, 0.0689, 0.0177, 0.00718, 0.00381, 0.00244, 0.00179, 0.00146, 0.00129, 0.00121, 0.0012],
- [1.0, 0.0645, 0.0156, 0.00596, 0.003, 0.00183, 0.00128, 0.001, 0.000852, 0.000775, 0.000744], // 15
- [1.0, 0.0606, 0.0138, 0.005, 0.00239, 0.00139, 0.000935, 0.000702, 0.000574, 0.000505, 0.00047, 0.000459],
- [1.0, 0.0571, 0.0123, 0.00423, 0.00193, 0.00107, 0.000692, 0.000499, 0.000394, 0.000335, 0.000302, 0.000287, 0.000284],
- [1.0, 0.054, 0.0111, 0.00362, 0.00158, 0.000839, 0.000519, 0.00036, 0.000275, 0.000226, 0.000198, 0.000183, 0.000176],
- [1.0, 0.0513, 0.00998, 0.00312, 0.0013, 0.000663, 0.000394, 0.000264, 0.000194, 0.000155, 0.000132, 0.000118, 0.000111, 0.000109],
- [1.0, 0.0488, 0.00906, 0.0027, 0.00108, 0.00053, 0.000303, 0.000196, 0.00014, 0.000108, 8.89e-05, 7.77e-05, 7.12e-05, 6.79e-05, 6.71e-05] // 20
- ]; // the first column is a dummy column representing K=0.
-
- var self = this;
- var excess = 20;
- var bitset_excess = 20;
- var minBuckets = 2;
- var minK = 1;
- var optKPerBuckets = [];
- var maxFalsePosProb = 0.01;
-
-
- for (i = 0; i < this.probs.length; i++) {
- var min = java.lang.Double.MAX_VALUE;
- var prob = this.probs[i];
- for (j = 0; j < prob.length; j++) {
- if (prob[j] < min) {
- min = prob[j];
- optKPerBuckets[i] = Math.max(minK, j);
- }
- }
- }
-
- var bucketsPerElement = maxBucketsPerElement(keys);
- var spec = computeBloomSpec(bucketsPerElement, maxFalsePosProb);
- var numBits = (keys * spec.bucketsPerElement) + bitset_excess;
- var wordCount = bits2words(numBits);
-
- if (wordCount > java.lang.Integer.MAX_VALUE) {
- throw "Bloom filter size is > 16GB, reduce the bloom_filter_fp_chance";
- }
-
- var bytes = wordCount * 8;
- this.size = bytes + 8;
-
- function maxBucketsPerElement(numElements) {
- numElements = Math.max(1, numElements);
- var v = (java.lang.Long.MAX_VALUE - excess) / keys;
- if (v < 1) {
- throw "Cannot compute probabilities for " + numElements + " elements.";
- }
- return Math.min(self.probs.length - 1, v);
- }
-
- // This is taken from BloomCalculations.java
- function computeBloomSpec(maxBucketsPerElement, maxFalsePosProb) {
- var maxK = self.probs[maxBucketsPerElement].length - 1;
-
- // Handle the trivial cases
- if(maxFalsePosProb >= self.probs[minBuckets][minK]) {
- return new BloomSpecification(2, optKPerBuckets[2]);
- }
- if (maxFalsePosProb < self.probs[maxBucketsPerElement][maxK]) {
- throw "Unable to satisfy " + maxFalsePosProb + " with " + maxBucketsPerElement + " buckets per element";
- }
-
- // First find the minimal required number of buckets:
- var bucketsPerElement = 2;
- var K = optKPerBuckets[2];
- while(self.probs[bucketsPerElement][K] > maxFalsePosProb){
- bucketsPerElement++;
- K = optKPerBuckets[bucketsPerElement];
- }
- // Now that the number of buckets is sufficient, see if we can relax K
- // without losing too much precision.
- while(self.probs[bucketsPerElement][K - 1] <= maxFalsePosProb) {
- K--;
- }
- println('K = ' + K + ', bucketsPerElement = ' + bucketsPerElement);
- return new BloomSpecification(K, bucketsPerElement);
- }
-
- function bits2words(numBits) {
- return (((numBits-1)>>>6)+1);
- }
- }
-
/**
- * Encapsulates sizing data for a set of rows, having the same number of columns and
+ * Encapsulates sizing data for a set of rows, having the samae number of columns and
* each column being the same size.
*/
function RowInfo() {
@@ -229,7 +119,6 @@
schedules) {
var table = new Table(name);
table.data = calculateData(schedules, columnSize, rowKeySize, duration);
- table.bloomFilter = new BloomFilter(5).size;
util.foreach(table.data.rows, function(interval) {
var rowInfo = table.data.rows[interval];
@@ -322,8 +211,8 @@
var rowKeyValueSize = 4;
var columnNameSize = 8;
- return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize,
- time.week, schedules);
+ return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize, time.week,
+ schedules);
};
/**
@@ -367,9 +256,4 @@
exports.sizeOf24HourMetrics = function(schedules) {
return calculateAggregatesTableSize('twenty_four_hour_metrics', time.day * 365, schedules);
};
-
- // exposed for testing
- exports.bloomFilter = function(keys) {
- return new BloomFilter(keys).size;
- }
})();
\ No newline at end of file
commit d7e1db17c8ab375a5369bf704891124bab449555
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 10:50:02 2014 -0500
BZ 994250 - finish the merging/peer review for patches submitted so that rhqctl returns proper exit codes. Note that this completes the merge of the two submitted patches - see prior two commits to this one. This third commit fixes some problems with the original patches: 1. Some scripts/files are not found in bin/internal of the distro, but rather are in bin/ - so we need to avoid using getBinDir() in those cases 2. Fix the code to conform to code conventions - DEATH TO TABS! 3. Remove a constant that got resurrected (ControlCommand.RHQ_STORAGE_BASEDIR_PROP is no longer needed) 4. A couple other minor things
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index 5db80a1..a13a598 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,7 +60,6 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
- public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 28a37de..88b085f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index 1b3d47b..ed2b2f8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ org.apache.commons.exec.CommandLine commandLine;
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 5d540a1..e2ef3a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index a3920e0..7ef66b4 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if(isStorageRunning()) {
+ if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index cc3d8c2..e80edd9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
+ executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 005a2ac4e16f1ea57db5172cd7c2f2fda2bde30e
Author: burmanm <yak(a)iki.fi>
Date: Tue Aug 6 17:52:38 2013 +0200
Fix return codes of the rhqctl command, rebased to the 4.10 master.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index a13a598..5db80a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,6 +60,7 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
+ public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 88b085f..28a37de 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index ed2b2f8..1b3d47b 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine;
+ org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index e2ef3a8..5d540a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index 7ef66b4..a3920e0 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if (isStorageRunning()) {
+ if(isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index e80edd9..cc3d8c2 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
+ executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 964dfe9331a111394acea159433635fb2378ddce
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 17 21:35:47 2013 +0100
Bug 968361 - Improve database plugin design to support connection pooling
This changeset introduces a new API for database plugins and deprecates the previous one. Compatibility with the previous API will be maintained until next major version of RHQ.
The 'rhq-database-plugin' was based on org.rhq.plugins.database.DatabaseComponent interface which encouraged plugin authors to share a single JDBC connection across database components. This was wrong for various reasons (connection leaks, concurrent JDBC calls... etc).
The new API introduces three important classes:
* org.rhq.plugins.database.PooledConnectionProvider
* org.rhq.plugins.database.BasePooledConnectionProvider
* org.rhq.plugins.database.ConnectionPoolingSupport
BasePooledConnectionProvider is a base implementation of a PooledConnectionProvider. Plugin authors should create a concrete implementation of BasePooledConnectionProvider which overrides the #getDriverClass() method. This is important if a database plugin embeds a JDBC driver: the database-specific driver class must be loaded by the child plugin classloader.
ConnectionPoolingSupport helps to manage the compatibility with the old API. It's a contract that all new database resource components should obey to. It declares the following methods:
* #supportsConnectionPooling()
* #getPooledConnectionProvider()
Results of calls to #supportsConnectionPooling() #getPooledConnectionProvider() must be consistent. In practice, a top level server database component should be able to create a PooledConnectionProvider instance, and child servers and services should indicate they support connection pooling only if their parent component does.
The 'rhq-database-plugin' embeds the BoneCP library (JDBC connection pooling) and its dependencies (Google's Guava). Child plugins will have all the classes accessible as soon as they have this node in their plugin descriptor:
===
<depends plugin="Database" useClasses="true"/>
===
This changeset includes the necessary changes to support connection pooling in the Oracle, Postgres and MySQL plugins.
Thanks to Elias Ross for contributing the original patch from which this changeset is derived.
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java
deleted file mode 100644
index b2ab566..0000000
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/install/remote/RemoteAccessInfo.java
+++ /dev/null
@@ -1,94 +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.core.domain.install.remote;
-
-import java.io.Serializable;
-
-/**
- * @author Greg Hinkle
- */
-public class RemoteAccessInfo implements Serializable {
- private static final long serialVersionUID = 1L;
-
- private String host;
- private String user;
- private String password;
- private byte[] key;
- private int port = 22;
-
- public RemoteAccessInfo(String host, String user, byte[] key) {
- this.host = host;
- this.user = user;
- this.key = key;
- }
-
- public RemoteAccessInfo(String host, String user, String password) {
- this(host, 22, user, password);
- }
-
- public RemoteAccessInfo(String host, int port, String user, String password) {
- this.host = host;
- this.port = port;
- this.user = user;
- this.password = password;
- }
-
- public RemoteAccessInfo() {
- }
-
- public String getHost() {
- return host;
- }
-
- public void setHost(String host) {
- this.host = host;
- }
-
- public int getPort() {
- return port;
- }
-
- public void setPort(int port) {
- this.port = port;
- }
-
- public String getUser() {
- return user;
- }
-
- public void setUser(String user) {
- this.user = user;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public byte[] getKey() {
- return key;
- }
-
- public void setKey(byte[] key) {
- this.key = key;
- }
-}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
deleted file mode 100644
index 6c81332..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ /dev/null
@@ -1,125 +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.plugins.mysql;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.HashMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A class to manage the connections to MySQL
- * This class keeps a cache of connections to MySQL and reuses them on demand
- * We assume single threaded access to the Connection in the agent
- * this will need to be reworked if that assumption is not correct
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionManager {
-
- private HashMap<MySqlConnectionInfo, Connection> connections;
- private static MySqlConnectionManager singleton;
- private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
-
- private MySqlConnectionManager() {
- connections = new HashMap<MySqlConnectionInfo,Connection>();
- }
-
- static MySqlConnectionManager getConnectionManager() {
- if (singleton == null) {
- singleton = new MySqlConnectionManager();
- }
- return singleton;
- }
-
- public void shutdown() {
- Driver driver = null;
- for (Connection conn : connections.values()) {
- try {
- if (driver == null) {
- String driverName = conn.getMetaData().getDriverName();
- driver = DriverManager.getDriver(driverName);
- }
- conn.close();
- }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
- }
- // deregister driver as well
- if (driver != null) {
- try {
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- logger.warn("Unable to deregister MySQL Driver on shutdown");
- }
- }
- }
-
- void closeConnection(MySqlConnectionInfo info) {
- Connection conn = connections.get(info);
- if (conn != null) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Closing Connection to " + info.buildURL());
- }
- conn.close();
- } catch (SQLException e) {
- logger.warn("Problem closing connection to " + info.buildURL() + " on close");
- }
- }
- connections.remove(info);
- }
-
- Connection getConnection (MySqlConnectionInfo info) throws SQLException {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
-
- Connection conn = connections.get(info);
- String url = info.buildURL();
- if (conn == null) {
- if (logger.isInfoEnabled()) {
- logger.info("Attemping connection to " + url);
- }
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- if (logger.isInfoEnabled()) {
- logger.info("Successfully connected to " + url);
- }
- connections.put(info, conn);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Reusing existing connection to " + url);
- }
- }
-
- // check the validity of the connection
- if (!conn.isValid(0)) {
- // attempt a single reconnect here and now
- conn.close();
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- connections.put(info, conn);
- logger.info("Refreshed a connection to " + url);
- }
- return conn;
- }
-
-}
commit 3e115204a92a25e133016df93afb444d78cd933a
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Dec 23 14:50:55 2013 +0100
Plugin validation for mysql plugin was failing because the Class.forName() statement was invoked from the constructor of the component. I moved this code to the method that actually opens the connection. This is the same strategy we use with our postgres plugin.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
new file mode 100644
index 0000000..6c81332
--- /dev/null
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
@@ -0,0 +1,125 @@
+/*
+ * 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.plugins.mysql;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.HashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A class to manage the connections to MySQL
+ * This class keeps a cache of connections to MySQL and reuses them on demand
+ * We assume single threaded access to the Connection in the agent
+ * this will need to be reworked if that assumption is not correct
+ * @author Steve Millidge (C2B2 Consulting Limited)
+ */
+class MySqlConnectionManager {
+
+ private HashMap<MySqlConnectionInfo, Connection> connections;
+ private static MySqlConnectionManager singleton;
+ private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
+
+ private MySqlConnectionManager() {
+ connections = new HashMap<MySqlConnectionInfo,Connection>();
+ }
+
+ static MySqlConnectionManager getConnectionManager() {
+ if (singleton == null) {
+ singleton = new MySqlConnectionManager();
+ }
+ return singleton;
+ }
+
+ public void shutdown() {
+ Driver driver = null;
+ for (Connection conn : connections.values()) {
+ try {
+ if (driver == null) {
+ String driverName = conn.getMetaData().getDriverName();
+ driver = DriverManager.getDriver(driverName);
+ }
+ conn.close();
+ }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
+ }
+ // deregister driver as well
+ if (driver != null) {
+ try {
+ DriverManager.deregisterDriver(driver);
+ } catch (SQLException ex) {
+ logger.warn("Unable to deregister MySQL Driver on shutdown");
+ }
+ }
+ }
+
+ void closeConnection(MySqlConnectionInfo info) {
+ Connection conn = connections.get(info);
+ if (conn != null) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Closing Connection to " + info.buildURL());
+ }
+ conn.close();
+ } catch (SQLException e) {
+ logger.warn("Problem closing connection to " + info.buildURL() + " on close");
+ }
+ }
+ connections.remove(info);
+ }
+
+ Connection getConnection (MySqlConnectionInfo info) throws SQLException {
+ try {
+ Class.forName("com.mysql.jdbc.Driver");
+ } catch (Exception ex) {
+ logger.error("Unable to find com.mysql.jdbc.Driver");
+ }
+
+ Connection conn = connections.get(info);
+ String url = info.buildURL();
+ if (conn == null) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Attemping connection to " + url);
+ }
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ if (logger.isInfoEnabled()) {
+ logger.info("Successfully connected to " + url);
+ }
+ connections.put(info, conn);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Reusing existing connection to " + url);
+ }
+ }
+
+ // check the validity of the connection
+ if (!conn.isValid(0)) {
+ // attempt a single reconnect here and now
+ conn.close();
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ connections.put(info, conn);
+ logger.info("Refreshed a connection to " + url);
+ }
+ return conn;
+ }
+
+}
commit bd89436f6e6b5db574a32ffd6636e13e1d702d54
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Thu Dec 19 14:03:24 2013 -0500
Add a little more logging to get a better start/end of a sync.
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index 24dcb44..6bdbede 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -1228,10 +1228,12 @@ public class InventoryManager extends AgentService implements ContainerService,
Collection<ResourceSyncInfo> syncInfos = platformSyncInfo.getServices();
syncInfos.add(platformResourceSyncInfo);
- log.info("Sync Starting: Platform [" + platformSyncInfo.getPlatform().getId() + "] and top level services.");
+ log.info("Sync Starting: Platform [" + platformSyncInfo.getPlatform().getId() + "]");
+
+ log.info("Sync Starting: Platform Top level services [" + platformSyncInfo.getPlatform().getId() + "]");
hadSyncedResources = syncResources(platformResourceSyncInfo.getId(), syncInfos) || hadSyncedResources;
- log.info("Sync Complete: Platform [" + platformSyncInfo.getPlatform().getId() + "]. Local inventory changed: ["
- + hadSyncedResources + "]");
+ log.info("Sync Complete: Platform Top level services [" + platformSyncInfo.getPlatform().getId()
+ + "] Local inventory changed: [" + hadSyncedResources + "]");
syncInfos = null; // release to GC
@@ -1257,6 +1259,8 @@ public class InventoryManager extends AgentService implements ContainerService,
purgeObsoleteResources(allServerSideUuids);
+ log.info("Sync Complete: Platform [" + platformSyncInfo.getPlatform().getId() + "].");
+
// If we synced any Resources, one or more Resource components were probably started, request a
// full avail report to make sure their availabilities are determined on the next avail run (typically
// < 30s away). A full avail report will ensure an initial avail check is performed for a resource.
@@ -1271,6 +1275,9 @@ public class InventoryManager extends AgentService implements ContainerService,
// time the upgrade kicks in..
if (hadSyncedResources && !isResourceUpgradeActive()) {
+ log.info("Sync changes detected, requesting full availability report and service discovery: Platform ["
+ + platformSyncInfo.getPlatform().getId() + "]");
+
// TODO: If someday this is undesirable for scalability reasons, we could probably instead call
// requestAvailabilityCheck on each unknown or modified resource.
requestFullAvailabilityReport();
commit ad9e7671b663f622cee1dac8e3e0113fe838743f
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Wed Dec 18 09:28:48 2013 -0500
[1023451] [perf] Large retained heap during inventory report merge (causing OOMs)
Note - this is related work, not necessarily a solution to this problem.
This commit builds on the "chunking" work introduced earlier. This work
replaces the tree structure, previously used to pass sync info between
agent and server, with flat collections. This allows us to replace the
costly Hibernate-based tree building approach with more comprehensive queries
that reduce the number of DB round trips dramatically.
Notes:
- This commit fixed an issue with the previous work regarding the handling
of top level services.
- The ResourceSyncInfo class is now even lighter weight, parent-child info is
removed.
- PlatformSyncInfo is now a POJO.
- The handling of unknown resources (agent side) changed significantly
because it had depended on the previous tree structure.
- mocks again had to be updated
diff --git a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
index 74a0acc..60f61c1 100644
--- a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
+++ b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
@@ -19,7 +19,6 @@
package org.rhq.test.arquillian;
-import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -248,10 +247,10 @@ public class FakeServerInventory {
};
}
- public synchronized Answer<ResourceSyncInfo> getResourceSyncInfo() {
- return new Answer<ResourceSyncInfo>() {
+ public synchronized Answer<Collection<ResourceSyncInfo>> getResourceSyncInfo() {
+ return new Answer<Collection<ResourceSyncInfo>>() {
@Override
- public ResourceSyncInfo answer(InvocationOnMock invocation) throws Throwable {
+ public Collection<ResourceSyncInfo> answer(InvocationOnMock invocation) throws Throwable {
synchronized (FakeServerInventory.this) {
Integer resourceId = (Integer) invocation.getArguments()[0];
@@ -622,37 +621,29 @@ public class FakeServerInventory {
return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- private ResourceSyncInfo getResourceSyncInfo(Resource resource) {
+ private Collection<ResourceSyncInfo> getResourceSyncInfo(Resource resource) {
return resource == null ? null : convert(resource);
}
- private static ResourceSyncInfo convert(Resource root) {
- return convertInternal(root, true, new HashMap<String, ResourceSyncInfo>());
+ private static Collection<ResourceSyncInfo> convert(Resource root) {
+ Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>();
+ convertInternal(root, result);
+ return result;
}
- private static ResourceSyncInfo convertInternal(Resource root, boolean isTopLevelServer,
- Map<String, ResourceSyncInfo> intermediateResults) {
+ private static void convertInternal(Resource root, Collection<ResourceSyncInfo> result) {
- ResourceSyncInfo ret = intermediateResults.get(root.getUuid());
+ ResourceSyncInfo rootSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(root);
- if (ret != null) {
- return ret;
+ if (result.contains(rootSyncInfo)) {
+ return;
}
try {
- ret = ResourceSyncInfo.buildResourceSyncInfo(root);
- intermediateResults.put(root.getUuid(), ret);
+ result.add(rootSyncInfo);
- Integer parentId = root.getParentResource() == null ? null : root.getParentResource().getId();
- getPrivateField(ResourceSyncInfo.class, "parentId").set(ret, parentId);
-
- Set<ResourceSyncInfo> children = new LinkedHashSet<ResourceSyncInfo>();
for (Resource child : root.getChildResources()) {
- ResourceSyncInfo syncChild = convertInternal(child, false, intermediateResults);
-
- children.add(syncChild);
+ convertInternal(child, result);
}
- getPrivateField(ResourceSyncInfo.class, "childSyncInfos").set(ret, children);
- return ret;
} catch (Exception e) {
throw new IllegalStateException("Failed to convert resource " + root
@@ -660,15 +651,6 @@ public class FakeServerInventory {
}
}
- private static Field getPrivateField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
- Field field = clazz.getDeclaredField(fieldName);
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
-
- return field;
- }
-
private void throwIfFailing() {
if (failing) {
throw new RuntimeException("Fake server inventory is in the failing mode.");
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/discovery/DiscoveryAgentService.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/discovery/DiscoveryAgentService.java
index e3313f0..4c2a821 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/discovery/DiscoveryAgentService.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/agent/discovery/DiscoveryAgentService.java
@@ -22,6 +22,8 @@
*/
package org.rhq.core.clientapi.agent.discovery;
+import java.util.Collection;
+
import org.jetbrains.annotations.NotNull;
import org.rhq.core.clientapi.agent.PluginContainerException;
@@ -29,8 +31,8 @@ import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
-import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceError;
@@ -56,11 +58,20 @@ public interface DiscoveryAgentService {
throws InvalidPluginConfigurationClientException, PluginContainerException;
/**
- * Called to inform the agent of a status change for the resource represented by syncInfo. The agent processes the syncInfo for the resource and initiates a status update for its sub-tree.
+ * Called by the server when requesting a full platform sync. The provided info will guide the subsequent
+ * agent-initiated sync.
+ *
+ * @param syncInfo for the platform to be synchronized with the server.
+ */
+ void synchronizePlatform(PlatformSyncInfo syncInfo);
+
+ /**
+ * Called by the server to update the agent with changed for specified top level server. The agent will
+ * synchronize its inventory for the server and its subtree given the provided information.
*
- * @param syncInfo for the root of the tree to be updated.
+ * @param syncInfo for the top level server to be synchronized with the server.
*/
- void synchronizeInventory(ResourceSyncInfo syncInfo);
+ void synchronizeServer(int resourceId, Collection<ResourceSyncInfo> toplevelServerSyncInfo);
/**
* Access to the current inventory managed by the plugin container.
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
index df2a286..4280305 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
@@ -22,6 +22,7 @@
*/
package org.rhq.core.clientapi.server.discovery;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -73,7 +74,7 @@ public interface DiscoveryServerService {
@LimitedConcurrency(CONCURRENCY_LIMIT_INVENTORY_REPORT)
@Timeout(0L)
// should be something like 1000L * 60 * 30 but until we can be assured we never take longer, disable timeout
- ResourceSyncInfo getResourceSyncInfo(int resourceId);
+ Collection<ResourceSyncInfo> getResourceSyncInfo(int resourceId);
/**
* Merges a new availability report from the agent into the server. This updates the availability statuses of known
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java
index 951f542..3d989f3 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java
@@ -23,52 +23,69 @@
package org.rhq.core.domain.discovery;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.Collections;
+import java.util.HashSet;
import java.util.Set;
-import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.OneToMany;
-import javax.persistence.Table;
-
-import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
+import org.rhq.core.domain.resource.ResourceCategory;
/**
- * @author Ian Springer
+ * This immutable POJO returns the information necessary for the agent to perform a complete sync with the server
+ * inventory. It does not provide *all* of the sync info, only the platform and its top level *service* hierarchy. It
+ * expects the agent to call back to the server for each of the platform's top level servers and therefore provides
+ * only the top level server Ids.
+ *
* @author Jay Shaughnessy
*/
-@Entity
-@Table(name = "RHQ_RESOURCE")
-public class PlatformSyncInfo extends SyncInfo implements Serializable {
+public class PlatformSyncInfo implements Serializable {
private static final long serialVersionUID = 1L;
- @OneToMany(mappedBy = "parentResource", fetch = FetchType.EAGER)
- private Set<Resource> topLevelServers;
+ private ResourceSyncInfo platform;
+ private Set<ResourceSyncInfo> services;
+ private Set<Integer> topLevelServerIds;
+
+ public PlatformSyncInfo(ResourceSyncInfo platform, Set<ResourceSyncInfo> services, Set<Integer> topLevelServerIds) {
+ super();
+ this.platform = platform;
+ this.services = services;
+ this.topLevelServerIds = topLevelServerIds;
+ }
+
+ /**
+ * @return just the platform sync info
+ */
+ public ResourceSyncInfo getPlatform() {
+ return platform;
+ }
- // JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
- public PlatformSyncInfo() {
+ /**
+ * @return the sync info for the platform hierarchy excluding the platform itself and the top level servers
+ */
+ public Set<ResourceSyncInfo> getServices() {
+ return services;
}
- public Collection<Resource> getTopLevelServers() {
- return topLevelServers;
+ /**
+ * @return just the type level server ids, so that the agent can call back for sync info on each top level server
+ */
+ public Set<Integer> getTopLevelServerIds() {
+ return topLevelServerIds;
}
// for testing
public static PlatformSyncInfo buildPlatformSyncInfo(Resource platform) {
- Set<Resource> toplevelServers = platform.getChildResources();
+ Set<Integer> toplevelServerIds = new HashSet<Integer>();
+ for (Resource r : platform.getChildResources()) {
+ if (r.getResourceType().getCategory().equals(ResourceCategory.SERVER)) {
+ toplevelServerIds.add(r.getId());
+ }
+ }
- PlatformSyncInfo syncInfo = new PlatformSyncInfo(platform.getId(), platform.getUuid(), platform.getMtime(),
- platform.getInventoryStatus(), (null == toplevelServers ? Collections.EMPTY_SET : toplevelServers));
+ ResourceSyncInfo resSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platform);
- return syncInfo;
- }
+ PlatformSyncInfo syncInfo = new PlatformSyncInfo(resSyncInfo, new HashSet<ResourceSyncInfo>(1),
+ toplevelServerIds);
- // for testing
- private PlatformSyncInfo(int id, String uuid, long mtime, InventoryStatus istatus, Set<Resource> children) {
- super(id, uuid, mtime, istatus);
- this.topLevelServers = children;
+ return syncInfo;
}
-
}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
index e9cb222..b1fb366 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
@@ -23,85 +23,131 @@
package org.rhq.core.domain.discovery;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.HashSet;
import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.FetchType;
-import javax.persistence.OneToMany;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
import javax.persistence.Table;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
/**
- * Sync info for any non-platform resource.
+ * Sync info for a resource. This is a lightweight "Resource" entity that contains only the information required
+ * to perform Inventory Sync between the Agent and Server.
*
- * @author Ian Springer
* @author Jay Shaughnessy
*/
@Entity
+@NamedQueries({
+ @NamedQuery(name = ResourceSyncInfo.QUERY_SERVICE_CHILDREN, query = "" //
+ + "SELECT r " //
+ + " FROM ResourceSyncInfo r " //
+ + " WHERE r.id IN ( SELECT rr.id FROM Resource rr WHERE rr.parentResource.id IN ( :parentIds )) " //
+ + ""),
+ @NamedQuery(name = ResourceSyncInfo.QUERY_TOP_LEVEL_SERVER, query = "" //
+ + "SELECT rsi " //
+ + " FROM ResourceSyncInfo rsi " //
+ + " WHERE rsi.id = :resourceId " //
+ + " OR rsi.id IN (SELECT rr.id FROM Resource rr WHERE rr.parentResource.id = :resourceId) "
+ + " OR rsi.id IN (SELECT rr.id FROM Resource rr WHERE rr.parentResource.parentResource.id = :resourceId) "
+ + " OR rsi.id IN (SELECT rr.id FROM Resource rr WHERE rr.parentResource.parentResource.parentResource.id = :resourceId) "
+ + " OR rsi.id IN (SELECT rr.id FROM Resource rr WHERE rr.parentResource.parentResource.parentResource.parentResource.id = :resourceId) "
+ + " OR rsi.id IN (SELECT rr.id FROM Resource rr WHERE rr.parentResource.parentResource.parentResource.parentResource.parentResource.id = :resourceId) "
+ + " ") })
@Table(name = "RHQ_RESOURCE")
-public class ResourceSyncInfo extends SyncInfo implements Serializable {
+public class ResourceSyncInfo implements Serializable {
private static final long serialVersionUID = 1L;
- @Column(name = "PARENT_RESOURCE_ID")
- private Integer parentId;
-
- @OneToMany(mappedBy = "parentId", fetch = FetchType.EAGER)
- private Collection<ResourceSyncInfo> childSyncInfos;
+ /** Sync info for platform service children (for building up hierarchy that excludes the top level servers */
+ public static final String QUERY_SERVICE_CHILDREN = "ResourceSyncInfo.platformServiceChildren";
+ /** Sync info rooted at the specified top level server and including all of it's hierarchy (up to 5 levels below
+ * the top level server. note that we support up to 6 levels below platform but we are starting one level down) */
+ public static final String QUERY_TOP_LEVEL_SERVER = "ResourceSyncInfo.topLevelServer";
+
+ /**
+ * Server-assigned id
+ */
+ @Column(name = "ID", nullable = false)
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+
+ /**
+ * Agent-assigned uuid
+ */
+ @Column(name = "UUID")
+ private String uuid;
+
+ /**
+ * Last modified time
+ */
+ @Column(name = "MTIME")
+ private long mtime;
+
+ @Column(name = "INVENTORY_STATUS")
+ @Enumerated(EnumType.STRING)
+ private InventoryStatus inventoryStatus;
// JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
public ResourceSyncInfo() {
}
- public Collection<ResourceSyncInfo> getChildSyncInfos() {
- return childSyncInfos;
+ public int getId() {
+ return id;
}
- // for testing
- public static ResourceSyncInfo buildResourceSyncInfo(Resource resource) {
- Collection<ResourceSyncInfo> children;
-
- if (resource.getChildResources() != null) {
- children = new HashSet<ResourceSyncInfo>(resource.getChildResources().size());
- for (Resource child : resource.getChildResources()) {
- children.add(buildResourceSyncInfo(child));
- }
- } else {
- children = new HashSet<ResourceSyncInfo>(0);
- }
-
- return buildResourceSyncInfo(resource, children);
+ public String getUuid() {
+ return uuid;
}
- // for testing
- public static ResourceSyncInfo buildResourceSyncInfo(Resource resource, Collection<ResourceSyncInfo> children) {
-
- ResourceSyncInfo syncInfo = new ResourceSyncInfo(resource.getId(), resource.getUuid(), resource.getMtime(),
- resource.getInventoryStatus(), children);
-
- return syncInfo;
+ public long getMtime() {
+ return mtime;
}
-
- public static ResourceSyncInfo buildResourceSyncInfo(SyncInfo syncInfo) {
-
- return buildResourceSyncInfo(syncInfo, ((Collection<ResourceSyncInfo>) null));
+ public InventoryStatus getInventoryStatus() {
+ return inventoryStatus;
}
- public static ResourceSyncInfo buildResourceSyncInfo(SyncInfo syncInfo, Collection<ResourceSyncInfo> children) {
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((uuid == null) ? 0 : uuid.hashCode());
+ return result;
+ }
- ResourceSyncInfo resourceSyncInfo = new ResourceSyncInfo(syncInfo.getId(), syncInfo.getUuid(),
- syncInfo.getMtime(), syncInfo.getInventoryStatus(), children);
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ ResourceSyncInfo other = (ResourceSyncInfo) obj;
+ if (uuid == null) {
+ if (other.uuid != null)
+ return false;
+ } else if (!uuid.equals(other.uuid))
+ return false;
+ return true;
+ }
- return resourceSyncInfo;
+ protected ResourceSyncInfo(int id, String uuid, long mtime, InventoryStatus istatus) {
+ this.id = id;
+ this.uuid = uuid;
+ this.mtime = mtime;
+ this.inventoryStatus = istatus;
}
- private ResourceSyncInfo(int id, String uuid, long mtime, InventoryStatus istatus,
- Collection<ResourceSyncInfo> children) {
- super(id, uuid, mtime, istatus);
- this.childSyncInfos = children;
+ static public ResourceSyncInfo buildResourceSyncInfo(Resource res) {
+ return new ResourceSyncInfo(res.getId(), res.getUuid(), res.getMtime(), res.getInventoryStatus());
}
}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java
deleted file mode 100644
index 370d84f..0000000
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright (C) 2005-2013 Red Hat, Inc.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation, and/or the GNU Lesser
- * General Public License, version 2.1, also as published by the Free
- * Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License and the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU General Public License
- * and the GNU Lesser General Public License along with this program;
- * if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.rhq.core.domain.discovery;
-
-import java.io.Serializable;
-
-import javax.persistence.Column;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.MappedSuperclass;
-
-import org.rhq.core.domain.resource.InventoryStatus;
-
-/**
- * This is the abstract base class for SyncInfo, which may be for a platform or a [top level server] resource.
- *
- * @author Ian Springer
- * @author Jay Shaughnessy
- */
-@MappedSuperclass
-public abstract class SyncInfo implements Serializable {
- private static final long serialVersionUID = 1L;
-
- /**
- * Server-assigned id
- */
- @Column(name = "ID", nullable = false)
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private int id;
-
- /**
- * Agent-assigned uuid
- */
- @Column(name = "UUID")
- private String uuid;
-
- /**
- * Last modified time
- */
- @Column(name = "MTIME")
- private long mtime;
-
- @Column(name = "INVENTORY_STATUS")
- @Enumerated(EnumType.STRING)
- private InventoryStatus inventoryStatus;
-
- // JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
- public SyncInfo() {
- }
-
- public int getId() {
- return id;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public long getMtime() {
- return mtime;
- }
-
- public InventoryStatus getInventoryStatus() {
- return inventoryStatus;
- }
-
- protected SyncInfo(int id, String uuid, long mtime, InventoryStatus istatus) {
- this.id = id;
- this.uuid = uuid;
- this.mtime = mtime;
- this.inventoryStatus = istatus;
- }
-
-}
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
index 4591dfc..cb799de 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
@@ -22,8 +22,10 @@ import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import java.io.File;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -217,17 +219,43 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
return;
}
- protected Answer<ResourceSyncInfo> getResourceSyncInfo() {
- return new Answer<ResourceSyncInfo>() {
+ protected Answer<Collection<ResourceSyncInfo>> getResourceSyncInfo() {
+ return new Answer<Collection<ResourceSyncInfo>>() {
@Override
- public ResourceSyncInfo answer(InvocationOnMock invocation) throws Throwable {
+ public Collection<ResourceSyncInfo> answer(InvocationOnMock invocation) throws Throwable {
Integer resourceId = (Integer) invocation.getArguments()[0];
- ResourceSyncInfo result = ResourceSyncInfo.buildResourceSyncInfo(simulatedInventory.get(resourceId));
+ Collection<ResourceSyncInfo> result = convert(simulatedInventory.get(resourceId));
return result;
}
};
}
+ private static Collection<ResourceSyncInfo> convert(Resource root) {
+ Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>();
+ convertInternal(root, result);
+ return result;
+ }
+
+ private static void convertInternal(Resource root, Collection<ResourceSyncInfo> result) {
+
+ ResourceSyncInfo rootSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(root);
+
+ if (result.contains(rootSyncInfo)) {
+ return;
+ }
+ try {
+ result.add(rootSyncInfo);
+
+ for (Resource child : root.getChildResources()) {
+ convertInternal(child, result);
+ }
+
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to convert resource " + root
+ + " to a ResourceSyncInfo. This should not happen.", e);
+ }
+ }
+
protected void validateFullInventory() {
System.out.println("Validating full inventory...");
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
index 33cf21a..849541d 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
@@ -25,6 +25,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
@@ -903,7 +904,7 @@ public class StandaloneContainer {
}
@Override
- public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
+ public Collection<ResourceSyncInfo> getResourceSyncInfo(int resourceId) {
return null;
}
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/sync/RuntimeSynchronizer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/sync/RuntimeSynchronizer.java
index 679db62..1708072 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/sync/RuntimeSynchronizer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/drift/sync/RuntimeSynchronizer.java
@@ -45,7 +45,7 @@ import org.rhq.core.pc.drift.ScheduleQueue;
* <br/><br/>
* Note that inventory sync happens regularly after the plugin container is initialized.
* Discovery scans are performed at fixed intervals. The results of a discovery scan are
- * reported to the server, and the server sends back {@link org.rhq.core.domain.discovery.ResourceSyncInfo resource sync info}
+ * reported to the server, and the server sends back {@link org.rhq.core.domain.discovery.OldResourceSyncInfo resource sync info}
* which is then used to sync with the local inventory.
*/
class RuntimeSynchronizer implements DriftSynchronizer {
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index b809f6f..24dcb44 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -228,6 +228,7 @@ public class InventoryManager extends AgentService implements ContainerService,
/**
* @see ContainerService#initialize()
*/
+ @Override
public void initialize() {
inventoryLock.writeLock().lock();
@@ -290,6 +291,7 @@ public class InventoryManager extends AgentService implements ContainerService,
/**
* @see ContainerService#shutdown()
*/
+ @Override
public void shutdown() {
PluginContainer pluginContainer = PluginContainer.getInstance();
pluginContainer.shutdownExecutorService(this.inventoryThreadPoolExecutor, true);
@@ -626,10 +628,12 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
+ @Override
public void setConfiguration(PluginContainerConfiguration configuration) {
this.configuration = configuration;
}
+ @Override
public void updatePluginConfiguration(int resourceId, Configuration newPluginConfiguration)
throws InvalidPluginConfigurationClientException, PluginContainerException {
ResourceContainer container = getResourceContainer(resourceId);
@@ -673,11 +677,13 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
+ @Override
@NotNull
public InventoryReport executeServerScanImmediately() {
return submit(serverScanExecutor);
}
+ @Override
@NotNull
public InventoryReport executeServiceScanImmediately() {
return submit(serviceScanExecutor);
@@ -689,6 +695,7 @@ public class InventoryManager extends AgentService implements ContainerService,
return submit(discoveryExecutor);
}
+ @Override
public void executeServiceScanDeferred() {
inventoryThreadPoolExecutor.submit((Callable<InventoryReport>) this.serviceScanExecutor);
}
@@ -705,6 +712,7 @@ public class InventoryManager extends AgentService implements ContainerService,
* @param changedOnlyReport
* @return The report, for inspection
*/
+ @Override
public AvailabilityReport executeAvailabilityScanImmediately(boolean changedOnlyReport) {
return executeAvailabilityScanImmediately(changedOnlyReport, false);
}
@@ -745,6 +753,7 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
+ @Override
@NotNull
public AvailabilityReport getCurrentAvailability(Resource resource, boolean changesOnly) {
try {
@@ -790,6 +799,7 @@ public class InventoryManager extends AgentService implements ContainerService,
configuration.getServerServices().getDiscoveryServerService().setResourceEnablement(resourceId, setEnabled);
}
+ @Override
public MergeResourceResponse manuallyAddResource(ResourceType resourceType, int parentResourceId,
Configuration pluginConfiguration, int ownerSubjectId) throws InvalidPluginConfigurationClientException,
PluginContainerException {
@@ -1202,36 +1212,45 @@ public class InventoryManager extends AgentService implements ContainerService,
/**
* Performs a full platform sync so that resources passed in are reflected in the agent's inventory.
*
- * @param platformSyncInfo sync info on the platform and references to the top level servers
+ * @param platformSyncInfo sync info on the platform and references to the top level servers. not null.
*/
private void syncPlatform(PlatformSyncInfo platformSyncInfo) {
final Set<String> allServerSideUuids = new HashSet<String>();
boolean hadSyncedResources = false;
+ ResourceSyncInfo platformResourceSyncInfo = platformSyncInfo.getPlatform();
- // always sync the platform because it does not get included in the top level server sync
- allServerSideUuids.add(platformSyncInfo.getUuid());
- ResourceSyncInfo platformResourceSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platformSyncInfo);
- log.info("Sync Starting: Platform [" + platformResourceSyncInfo.getId() + "]");
- hadSyncedResources = syncResource(platformResourceSyncInfo) || hadSyncedResources;
- log.info("Sync Complete: Platform [" + platformResourceSyncInfo.getId() + "]. Local inventory changed: ["
+ // sync the platform because it does not get included in the top level server sync
+ allServerSideUuids.add(platformResourceSyncInfo.getUuid());
+ // sync the top level service hierarchy
+ addAllUuids(platformSyncInfo.getServices(), allServerSideUuids);
+
+ // Add the platform sync info to the service hierarchy in order to process in one batch
+ Collection<ResourceSyncInfo> syncInfos = platformSyncInfo.getServices();
+ syncInfos.add(platformResourceSyncInfo);
+
+ log.info("Sync Starting: Platform [" + platformSyncInfo.getPlatform().getId() + "] and top level services.");
+ hadSyncedResources = syncResources(platformResourceSyncInfo.getId(), syncInfos) || hadSyncedResources;
+ log.info("Sync Complete: Platform [" + platformSyncInfo.getPlatform().getId() + "]. Local inventory changed: ["
+ hadSyncedResources + "]");
+ syncInfos = null; // release to GC
+
// then sync the top level servers by calling back to the server for the sync info for each. We
// do this one at a time to avoid forcing the whole inventory into active memory at one time during the sync.
- Collection<Resource> topLevelServers = platformSyncInfo.getTopLevelServers();
- if (null != topLevelServers) {
+ Collection<Integer> topLevelServerIds = platformSyncInfo.getTopLevelServerIds();
+ if (null != topLevelServerIds) {
DiscoveryServerService service = configuration.getServerServices().getDiscoveryServerService();
- for (Resource topLevelServer : topLevelServers) {
- ResourceSyncInfo topLevelServerSyncInfo = service.getResourceSyncInfo(topLevelServer.getId());
- if (null != topLevelServerSyncInfo) {
- //topLevelServerSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platformSyncInfo,
- // topLevelServerSyncInfo);
- getAllUuids(topLevelServerSyncInfo, allServerSideUuids);
- log.info("Sync Starting: Top Level Server [" + topLevelServerSyncInfo.getId() + "]");
- hadSyncedResources = syncResource(topLevelServerSyncInfo) || hadSyncedResources;
- log.info("Sync Complete: Top Level Server [" + topLevelServerSyncInfo.getId()
- + "] Local inventory changed: [" + hadSyncedResources + "]");
+ for (Integer topLevelServerId : topLevelServerIds) {
+ syncInfos = service.getResourceSyncInfo(topLevelServerId);
+ if (null != syncInfos) {
+ addAllUuids(syncInfos, allServerSideUuids);
+ log.info("Sync Starting: Top Level Server [" + topLevelServerId + "]");
+ hadSyncedResources = syncResources(topLevelServerId, syncInfos) || hadSyncedResources;
+ log.info("Sync Complete: Top Level Server [" + topLevelServerId + "] Local inventory changed: ["
+ + hadSyncedResources + "]");
+
+ syncInfos = null; // release to GC
}
}
}
@@ -1261,13 +1280,9 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
- private void getAllUuids(ResourceSyncInfo syncInfo, Set<String> allServerSideUuids) {
- allServerSideUuids.add(syncInfo.getUuid());
-
- if (null != syncInfo.getChildSyncInfos()) {
- for (ResourceSyncInfo child : syncInfo.getChildSyncInfos()) {
- getAllUuids(child, allServerSideUuids);
- }
+ private void addAllUuids(Collection<ResourceSyncInfo> syncInfos, Set<String> allServerSideUuids) {
+ for (ResourceSyncInfo syncInfo : syncInfos) {
+ allServerSideUuids.add(syncInfo.getUuid());
}
}
@@ -1278,7 +1293,7 @@ public class InventoryManager extends AgentService implements ContainerService,
* @param syncInfo the resources' sync data
* @return true if any resources needed synchronization, false otherwise
*/
- private boolean syncResource(ResourceSyncInfo syncInfo) {
+ private boolean syncResources(int rootResourceId, Collection<ResourceSyncInfo> syncInfos) {
boolean result = false;
final long startTime = System.currentTimeMillis();
final Set<Resource> syncedResources = new LinkedHashSet<Resource>();
@@ -1292,7 +1307,7 @@ public class InventoryManager extends AgentService implements ContainerService,
try {
log.debug("Processing Server sync info...");
- processSyncInfo(syncInfo, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
+ processSyncInfo(syncInfos, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
deletedResourceIds, newlyCommittedResources, ignoredResources);
if (log.isDebugEnabled()) {
@@ -1318,7 +1333,7 @@ public class InventoryManager extends AgentService implements ContainerService,
result = !(syncedResources.isEmpty() && unknownResourceSyncInfos.isEmpty() && modifiedResourceIds.isEmpty());
} catch (Throwable t) {
- log.warn("Failed to synchronize local inventory with Server inventory for Resource [" + syncInfo.getId()
+ log.warn("Failed to synchronize local inventory with Server inventory for Resource [" + rootResourceId
+ "] and its descendants: " + t.getMessage());
// convert to runtime exception so as not to change the api
throw new RuntimeException(t);
@@ -1327,13 +1342,19 @@ public class InventoryManager extends AgentService implements ContainerService,
return result;
}
- public void synchronizeInventory(ResourceSyncInfo resourceSyncInfo) {
- log.info("Synchronizing local inventory with Server inventory for Resource [" + resourceSyncInfo.getId()
- + "] and its descendants...");
+ @Override
+ public void synchronizePlatform(PlatformSyncInfo syncInfo) {
+ syncPlatform(syncInfo);
+ performServiceScan(syncInfo.getPlatform().getId()); // NOTE: This will block (the initial scan blocks).
+ // TODO: (jshaughn) should we also request a full avail scan?
+ }
- // Get the latest resource data rooted at the given id.
- syncResource(resourceSyncInfo); // this method assumes we only get a single resource and its children (BZ 887411)
- performServiceScan(resourceSyncInfo.getId()); // NOTE: This will block (the initial scan blocks).
+ @Override
+ public void synchronizeServer(int resourceId, Collection<ResourceSyncInfo> topLevelServerSyncInfo) {
+ log.info("Synchronizing local inventory with Server inventory for Resource [" + resourceId
+ + "] and its descendants...");
+ syncResources(resourceId, topLevelServerSyncInfo);
+ performServiceScan(resourceId); // NOTE: This will block (the initial scan blocks).
// TODO: (jshaughn) should we also request a full avail scan?
}
@@ -1397,6 +1418,7 @@ public class InventoryManager extends AgentService implements ContainerService,
return resourceContainer.getResourceComponent();
}
+ @Override
public void uninventoryResource(int resourceId) {
ResourceContainer resourceContainer = getResourceContainer(resourceId);
if (resourceContainer == null) {
@@ -1542,6 +1564,7 @@ public class InventoryManager extends AgentService implements ContainerService,
return parentContainerResource.getChildResources();
}
+ @Override
public Resource getPlatform() {
return platform;
}
@@ -2541,6 +2564,7 @@ public class InventoryManager extends AgentService implements ContainerService,
return mgr;
}
+ @Override
public void requestFullAvailabilityReport() {
if (null != availabilityExecutor) {
availabilityExecutor.sendFullReportNextTime();
@@ -2684,10 +2708,12 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
+ @Override
public void enableServiceScans(int serverResourceId, Configuration config) {
throw new UnsupportedOperationException("not implemented yet"); // TODO: Implement this method.
}
+ @Override
public void disableServiceScans(int serverResourceId) {
throw new UnsupportedOperationException("not implemented yet"); // TODO: Implement this method.
}
@@ -2905,84 +2931,79 @@ public class InventoryManager extends AgentService implements ContainerService,
return versionUpdated;
}
- private void processSyncInfo(ResourceSyncInfo syncInfo, Set<Resource> syncedResources,
+ private void processSyncInfo(Collection<ResourceSyncInfo> syncInfos, Set<Resource> syncedResources,
Set<ResourceSyncInfo> unknownResourceSyncInfos, Set<Integer> modifiedResourceIds,
Set<Integer> deletedResourceIds, Set<Resource> newlyCommittedResources, Set<Resource> ignoredResources) {
- if (InventoryStatus.DELETED == syncInfo.getInventoryStatus()) {
- // A previously deleted resource still being reported by the server. Support for this option can
- // be removed if the server is ever modified to not report deleted resources. It is happening currently
- // because deleted resources are kept to support resource history. The deleted resources are rightfully not
- // in the PC inventory, and so must be handled separately, and not as unknown resources.
- deletedResourceIds.add(syncInfo.getId());
- } else {
- ResourceContainer container = getResourceContainer(syncInfo.getUuid());
- if (container == null) {
- // Either a manually added Resource or just something we haven't discovered.
- // If this unknown resource is to be ignored, then don't bother to do anything.
- if (InventoryStatus.IGNORED != syncInfo.getInventoryStatus()) {
- unknownResourceSyncInfos.add(syncInfo);
- log.info("Got unknown resource: " + syncInfo.getId());
- } else {
- log.info("Got an unknown but ignored resource - ignoring it: " + syncInfo.getId());
- }
+ for (ResourceSyncInfo syncInfo : syncInfos) {
+ if (InventoryStatus.DELETED == syncInfo.getInventoryStatus()) {
+ // A previously deleted resource still being reported by the server. Support for this option can
+ // be removed if the server is ever modified to not report deleted resources. It is happening currently
+ // because deleted resources are kept to support resource history. The deleted resources are rightfully not
+ // in the PC inventory, and so must be handled separately, and not as unknown resources.
+ deletedResourceIds.add(syncInfo.getId());
} else {
- Resource resource = container.getResource();
- // Ensure the Resource classloader is initialized on the Resource container.
- initResourceContainer(resource);
-
- if (log.isDebugEnabled()) {
- log.debug("Local Resource: id=" + resource.getId() + ", status=" + resource.getInventoryStatus()
- + ", mtime=" + resource.getMtime());
- log.debug("Sync Resource: " + syncInfo.getId() + ", status=" + syncInfo.getInventoryStatus()
- + ", mtime=" + syncInfo.getMtime());
- }
-
- final boolean ignoreResource = (InventoryStatus.IGNORED == syncInfo.getInventoryStatus());
- final boolean ignoreResourceType = this.pluginManager.getMetadataManager()
- .isDisabledOrIgnoredResourceType(resource.getResourceType());
- if (ignoreResource || ignoreResourceType) {
- // a resource or its type has been tagged to be ignored - we need to remove it from our inventory
- ignoredResources.add(resource);
+ ResourceContainer container = getResourceContainer(syncInfo.getUuid());
+ if (container == null) {
+ // Either a manually added Resource or just something we haven't discovered.
+ // If this unknown resource is to be ignored, then don't bother to do anything.
+ if (InventoryStatus.IGNORED != syncInfo.getInventoryStatus()) {
+ unknownResourceSyncInfos.add(syncInfo);
+ log.info("Got unknown resource: " + syncInfo.getId());
+ } else {
+ log.info("Got an unknown but ignored resource - ignoring it: " + syncInfo.getId());
+ }
} else {
- if (resource.getInventoryStatus() != InventoryStatus.COMMITTED
- && syncInfo.getInventoryStatus() == InventoryStatus.COMMITTED) {
- newlyCommittedResources.add(resource);
+ Resource resource = container.getResource();
+ // Ensure the Resource classloader is initialized on the Resource container.
+ initResourceContainer(resource);
+
+ if (log.isDebugEnabled()) {
+ log.debug("Local Resource: id=" + resource.getId() + ", status="
+ + resource.getInventoryStatus() + ", mtime=" + resource.getMtime());
+ log.debug("Sync Resource: " + syncInfo.getId() + ", status=" + syncInfo.getInventoryStatus()
+ + ", mtime=" + syncInfo.getMtime());
}
- if (resource.getId() == 0) {
- // This must be a Resource we just reported to the server. Just update its id, mtime, and status.
- resource.setId(syncInfo.getId());
- resource.setMtime(syncInfo.getMtime());
- resource.setInventoryStatus(syncInfo.getInventoryStatus());
- refreshResourceComponentState(container, true);
- syncedResources.add(resource);
+ final boolean ignoreResource = (InventoryStatus.IGNORED == syncInfo.getInventoryStatus());
+ final boolean ignoreResourceType = this.pluginManager.getMetadataManager()
+ .isDisabledOrIgnoredResourceType(resource.getResourceType());
+ if (ignoreResource || ignoreResourceType) {
+ // a resource or its type has been tagged to be ignored - we need to remove it from our inventory
+ ignoredResources.add(resource);
} else {
- // It's a resource that was already synced at least once.
- if (resource.getId() != syncInfo.getId()) {
- // This really should never happen, but check for it just to be bulletproof.
- log.error("PC Resource id (" + resource.getId() + ") does not match Server Resource id ("
- + syncInfo.getId() + ") for Resource with uuid " + resource.getUuid() + ": " + resource);
- modifiedResourceIds.add(syncInfo.getId());
+ if (resource.getInventoryStatus() != InventoryStatus.COMMITTED
+ && syncInfo.getInventoryStatus() == InventoryStatus.COMMITTED) {
+ newlyCommittedResources.add(resource);
}
- // See if it's been modified on the Server since the last time we synced.
- else if (resource.getMtime() < syncInfo.getMtime()) {
- modifiedResourceIds.add(resource.getId());
+
+ if (resource.getId() == 0) {
+ // This must be a Resource we just reported to the server. Just update its id, mtime, and status.
+ resource.setId(syncInfo.getId());
+ resource.setMtime(syncInfo.getMtime());
+ resource.setInventoryStatus(syncInfo.getInventoryStatus());
+ refreshResourceComponentState(container, true);
+ syncedResources.add(resource);
} else {
- // Only try to start up the component if the Resource has *not* been modified on the Server.
- // Otherwise, hold off until we've synced the Resource with the Server.
- refreshResourceComponentState(container, false);
+ // It's a resource that was already synced at least once.
+ if (resource.getId() != syncInfo.getId()) {
+ // This really should never happen, but check for it just to be bulletproof.
+ log.error("PC Resource id (" + resource.getId()
+ + ") does not match Server Resource id (" + syncInfo.getId()
+ + ") for Resource with uuid " + resource.getUuid() + ": " + resource);
+ modifiedResourceIds.add(syncInfo.getId());
+ }
+ // See if it's been modified on the Server since the last time we synced.
+ else if (resource.getMtime() < syncInfo.getMtime()) {
+ modifiedResourceIds.add(resource.getId());
+ } else {
+ // Only try to start up the component if the Resource has *not* been modified on the Server.
+ // Otherwise, hold off until we've synced the Resource with the Server.
+ refreshResourceComponentState(container, false);
+ }
}
}
}
-
- // Recurse...
- if (null != syncInfo.getChildSyncInfos()) {
- for (ResourceSyncInfo childSyncInfo : syncInfo.getChildSyncInfos()) {
- processSyncInfo(childSyncInfo, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
- deletedResourceIds, newlyCommittedResources, ignoredResources);
- }
- }
}
}
}
@@ -3037,46 +3058,20 @@ public class InventoryManager extends AgentService implements ContainerService,
}
private Set<Resource> getResourcesFromSyncInfos(Set<ResourceSyncInfo> syncInfos) {
-
- final StopWatch stopWatch = new StopWatch();
- final int syncInfosSize = syncInfos.size();
- final Set<Resource> result = new HashSet<Resource>(syncInfosSize);
-
- for (ResourceSyncInfo syncInfo : syncInfos) {
- Resource resource = getResourceFromSyncInfo(syncInfo);
- result.add(resource);
- }
-
- if (log.isDebugEnabled()) {
- log.debug("Time to build resource tree from [" + syncInfosSize + "] sync infos=" + stopWatch.getElapsed());
- }
-
- return result;
- }
-
- private Resource getResourceFromSyncInfo(ResourceSyncInfo syncInfo) {
- final boolean isDebugEnabled = log.isDebugEnabled();
- final StopWatch stopWatch = new StopWatch();
- String marker = null;
-
/////
- // First we need to do a breadth first traversal of the sync info tree and build a list of all resource IDs.
-
- if (isDebugEnabled) {
- marker = "a. Breadth-first retrieval of sync info tree";
- stopWatch.markTimeBegin(marker);
- }
-
- List<Integer> resourceIdList = treeToBreadthFirstList(syncInfo);
- int fullResourceTreeSize = resourceIdList.size();
- if (isDebugEnabled) {
- stopWatch.markTimeEnd(marker);
+ // First we need to get a list of the unknown resource ids
+ List<Integer> resourceIdList = new ArrayList<Integer>(syncInfos.size());
+ for (ResourceSyncInfo syncInfo : syncInfos) {
+ resourceIdList.add(syncInfo.getId());
}
/////
// Now we need to loop over batches of the resource ID list - asking the server for their resource representations.
// When we get the resources from the server, we put them in our resourceMap, keyed on ID.
+ final boolean isDebugEnabled = log.isDebugEnabled();
+ final StopWatch stopWatch = new StopWatch();
+ String marker = null;
Map<Integer, Resource> resourceMap = new HashMap<Integer, Resource>(resourceIdList.size());
int batchNumber = 0;
while (!resourceIdList.isEmpty()) {
@@ -3091,7 +3086,7 @@ public class InventoryManager extends AgentService implements ContainerService,
// This usage of .clear() will remove the processed resources from the backing list.
String markerPrefix = null;
if (isDebugEnabled) {
- markerPrefix = String.format("b. Batch [%03d] (%d): ", batchNumber, fullResourceTreeSize);
+ markerPrefix = String.format("a. Batch [%03d] (%d): ", batchNumber, syncInfos.size());
marker = String.format("%sGet resource ID sublist - %d of %d remaining", markerPrefix, end, size);
stopWatch.markTimeBegin(marker);
}
@@ -3136,61 +3131,39 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
- if (fullResourceTreeSize != resourceMap.size()) {
- log.warn("Expected [" + fullResourceTreeSize + "] but found [" + resourceMap.size()
+ if (syncInfos.size() != resourceMap.size()) {
+ log.warn("Expected [" + syncInfos.size() + "] but found [" + resourceMap.size()
+ "] resources when fetching from server");
}
/////
// We now have all the resources associated with all sync infos in a map.
- // We need to build the full resource tree using the sync info as the blueprint for how to order the resources in a tree.
+ // We need to build the full resource tree using the resource parent info as the blueprint for how to
+ // link the resources in the tree.
if (isDebugEnabled) {
- marker = "c. Build the full resource tree";
+ marker = "b. Build the resource hierarchies";
stopWatch.markTimeBegin(marker);
}
- Resource result = syncInfoTreeToResourceTree(syncInfo, resourceMap);
- resourceMap.clear();
-
- if (isDebugEnabled) {
- stopWatch.markTimeEnd(marker);
-
- log.debug("Full resource tree built from sync info - performance: " + stopWatch);
- }
-
- return result;
- }
-
- private Resource syncInfoTreeToResourceTree(ResourceSyncInfo syncInfo, Map<Integer, Resource> resourceMap) {
- Resource result = resourceMap.get(syncInfo.getId());
-
- if (null == result || null == syncInfo.getChildSyncInfos()) {
- return result;
- }
-
- for (ResourceSyncInfo child : syncInfo.getChildSyncInfos()) {
- Resource childResource = syncInfoTreeToResourceTree(child, resourceMap);
- if (null != childResource) {
- result.addChildResource(childResource);
+ // The root resources to be merged (i.e. the resources whose parents are not in the map)
+ Set<Resource> result = new HashSet<Resource>();
+ for (Resource resource : resourceMap.values()) {
+ if (null == resource.getParentResource()) {
+ result.add(resource); // the platform, make sure we have this
+ continue;
+ }
+ Resource parent = resourceMap.get(resource.getParentResource().getId());
+ if (null != parent) {
+ parent.addChildResource(resource);
+ } else {
+ result.add(resource);
}
}
- return result;
- }
-
- private List<Integer> treeToBreadthFirstList(ResourceSyncInfo syncInfo) {
- List<Integer> result = new ArrayList<Integer>();
+ if (isDebugEnabled) {
+ stopWatch.markTimeEnd(marker);
- LinkedList<ResourceSyncInfo> queue = new LinkedList<ResourceSyncInfo>();
- queue.add(syncInfo);
- while (!queue.isEmpty()) {
- ResourceSyncInfo node = queue.remove();
- result.add(node.getId());
- if (null != node.getChildSyncInfos()) {
- for (ResourceSyncInfo child : node.getChildSyncInfos()) {
- queue.add(child);
- }
- }
+ log.debug("Resource trees built from map - performance: " + stopWatch);
}
return result;
@@ -3500,6 +3473,7 @@ public class InventoryManager extends AgentService implements ContainerService,
*/
class ResourceGotActivatedListener implements InventoryEventListener {
+ @Override
public void resourceActivated(Resource resource) {
if (resource != null && resource.getId() > 0) {
if (log.isDebugEnabled()) {
@@ -3514,14 +3488,17 @@ public class InventoryManager extends AgentService implements ContainerService,
removeInventoryEventListener(this);
}
+ @Override
public void resourceDeactivated(Resource resource) {
// nothing to do
}
+ @Override
public void resourcesAdded(Set<Resource> resources) {
// nothing to do
}
+ @Override
public void resourcesRemoved(Set<Resource> resources) {
// nothing to do
}
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/AbstractResourceUpgradeHandlingTest.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/AbstractResourceUpgradeHandlingTest.java
index cfa6fa7..e9a3c05 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/AbstractResourceUpgradeHandlingTest.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/AbstractResourceUpgradeHandlingTest.java
@@ -56,6 +56,10 @@ public abstract class AbstractResourceUpgradeHandlingTest extends ResourceUpgrad
expectations.with(Expectations.any(InventoryReport.class)));
expectations.will(inventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ expectations.allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(
+ expectations.with(Expectations.any(Integer.class)));
+ expectations.will(inventory.getResourceSyncInfo());
+
expectations.allowing(ss.getDiscoveryServerService()).upgradeResources(
expectations.with(Expectations.any(Set.class)));
expectations.will(inventory.upgradeResources());
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
index 5a18d65..2bcb7aa 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
@@ -19,7 +19,6 @@
package org.rhq.core.pc.upgrade;
-import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -132,12 +131,12 @@ public class FakeServerInventory {
throwIfFailing();
Integer resourceId = (Integer) invocation.getParameter(0);
-
- for (Resource c : platform.getChildResources()) {
- if (c.getId() == resourceId) {
- return getResourceSyncInfo(c);
+ for (Resource r : resourceStore.values()) {
+ if (resourceId.equals(r.getId())) {
+ return convert(r);
}
}
+
return null;
}
}
@@ -422,37 +421,29 @@ public class FakeServerInventory {
return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- private ResourceSyncInfo getResourceSyncInfo(Resource resource) {
+ private Collection<ResourceSyncInfo> getResourceSyncInfo(Resource resource) {
return resource == null ? null : convert(resource);
}
- private static ResourceSyncInfo convert(Resource root) {
- return convertInternal(root, true, new HashMap<String, ResourceSyncInfo>());
+ private static Collection<ResourceSyncInfo> convert(Resource root) {
+ Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>();
+ convertInternal(root, result);
+ return result;
}
- private static ResourceSyncInfo convertInternal(Resource root, boolean isTopLevelServer,
- Map<String, ResourceSyncInfo> intermediateResults) {
+ private static void convertInternal(Resource root, Collection<ResourceSyncInfo> result) {
- ResourceSyncInfo ret = intermediateResults.get(root.getUuid());
+ ResourceSyncInfo rootSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(root);
- if (ret != null) {
- return ret;
+ if (result.contains(rootSyncInfo)) {
+ return;
}
try {
- ret = ResourceSyncInfo.buildResourceSyncInfo(root);
- intermediateResults.put(root.getUuid(), ret);
-
- Integer parentId = root.getParentResource() == null ? null : root.getParentResource().getId();
- getPrivateField(ResourceSyncInfo.class, "parentId").set(ret, parentId);
+ result.add(rootSyncInfo);
- Set<ResourceSyncInfo> children = new LinkedHashSet<ResourceSyncInfo>();
for (Resource child : root.getChildResources()) {
- ResourceSyncInfo syncChild = convertInternal(child, false, intermediateResults);
-
- children.add(syncChild);
+ convertInternal(child, result);
}
- getPrivateField(ResourceSyncInfo.class, "childSyncInfos").set(ret, children);
- return ret;
} catch (Exception e) {
throw new IllegalStateException("Failed to convert resource " + root
@@ -460,15 +451,6 @@ public class FakeServerInventory {
}
}
- private static Field getPrivateField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
- Field field = clazz.getDeclaredField(fieldName);
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
-
- return field;
- }
-
private static Resource findResource(Resource root, Resource template, Comparator<Resource> comparator) {
if (root == null)
return null;
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/ResourceUpgradeTest.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/ResourceUpgradeTest.java
index 176f0a4..5572adf 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/ResourceUpgradeTest.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/ResourceUpgradeTest.java
@@ -42,7 +42,7 @@ import org.rhq.test.pc.PluginContainerSetup;
import org.rhq.test.pc.PluginContainerTest;
/**
- *
+ *
*
* @author Lukas Krejci
*/
@@ -91,6 +91,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(inv.mergeInventoryReport(InventoryStatus.COMMITTED));
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(inv.getResourceSyncInfo());
+
oneOf(ss.getDiscoveryServerService()).upgradeResources(with(any(Set.class)));
will(inv.upgradeResources());
}
@@ -159,6 +162,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(inv.mergeInventoryReport(InventoryStatus.COMMITTED));
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(inv.getResourceSyncInfo());
+
never(ss.getDiscoveryServerService()).upgradeResources(with(any(Set.class)));
}
});
@@ -208,6 +214,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(inv.mergeInventoryReport(InventoryStatus.COMMITTED));
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(inv.getResourceSyncInfo());
+
never(ss.getDiscoveryServerService()).upgradeResources(with(any(Set.class)));
}
});
@@ -258,6 +267,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(inv.mergeInventoryReport(InventoryStatus.COMMITTED));
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(inv.getResourceSyncInfo());
+
oneOf(ss.getDiscoveryServerService()).upgradeResources(with(any(Set.class)));
will(inv.upgradeResources());
}
@@ -297,6 +309,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(inv.mergeInventoryReport(requiredInventoryStatus));
+
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(inv.getResourceSyncInfo());
}
});
@@ -334,6 +349,9 @@ public class ResourceUpgradeTest extends ResourceUpgradeTestBase {
allowing(ss.getDiscoveryServerService()).mergeInventoryReport(with(any(InventoryReport.class)));
will(serverInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(with(any(Integer.class)));
+ will(serverInventory.getResourceSyncInfo());
+
oneOf(ss.getDiscoveryServerService()).upgradeResources(with(any(Set.class)));
will(serverInventory.upgradeResources());
}
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/ConfigurationManagerBeanTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/ConfigurationManagerBeanTest.java
index 35fa7f1..4de1380 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/ConfigurationManagerBeanTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/ConfigurationManagerBeanTest.java
@@ -20,6 +20,7 @@ package org.rhq.enterprise.server.configuration;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -35,7 +36,6 @@ import org.rhq.core.clientapi.agent.discovery.DiscoveryAgentService;
import org.rhq.core.clientapi.agent.discovery.InvalidPluginConfigurationClientException;
import org.rhq.core.clientapi.server.configuration.ConfigurationUpdateResponse;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
-import org.rhq.core.communications.command.annotation.Asynchronous;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
@@ -50,8 +50,8 @@ import org.rhq.core.domain.configuration.group.GroupPluginConfigurationUpdate;
import org.rhq.core.domain.criteria.ResourceConfigurationUpdateCriteria;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
-import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
@@ -227,7 +227,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
inProgress = configurationManager.isResourceConfigurationUpdateInProgress(overlord, resourceId);
if (inProgress) {
- // history2 should be history1 since the update is not complete
+ // history2 should be history1 since the update is not complete
assert history2 != null;
assert history2.getId() == history1.getId();
myprop = history2.getConfiguration().getSimple("myboolean");
@@ -235,7 +235,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
assert "true".equals(myprop.getStringValue());
myprop = history2.getConfiguration().getSimple("mysleep"); // this wasn't in the first config
assert myprop == null;
- // record that this test case ran, we expect it will if the agent delay is there
+ // record that this test case ran, we expect it will if the agent delay is there
inProgressTested = true;
} else {
// update is complete, history 2 should be different
@@ -284,7 +284,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
assert "true".equals(myprop.getStringValue());
// now update to config2 - the "agent" will sleep for a bit before it completes
- // so we will have an INPROGRESS configuration for a few seconds before it goes to SUCCESS
+ // so we will have an INPROGRESS configuration for a few seconds before it goes to SUCCESS
configurationManager.updateResourceConfiguration(overlord, resourceId, configuration2);
// now update to config3 - this should fail as you can't update while there is one in progress
@@ -306,7 +306,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
inProgress = configurationManager.isResourceConfigurationUpdateInProgress(overlord, resourceId);
if (inProgress) {
- // history2 should be history1 since the update is not complete
+ // history2 should be history1 since the update is not complete
assert history2 != null;
assert history2.getId() == history1.getId();
myprop = history2.getConfiguration().getSimple("myboolean");
@@ -314,7 +314,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
assert "true".equals(myprop.getStringValue());
myprop = history2.getConfiguration().getSimple("mysleep"); // this wasn't in the first config
assert myprop == null;
- // record that this test case ran, we expect it will if the agent delay is there
+ // record that this test case ran, we expect it will if the agent delay is there
inProgressTested = true;
} else {
// update is complete, history 2 should be different
@@ -741,7 +741,7 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
}
/** Exercise the ConfigurationManagerBean getOptionsForConfigurationDefinition.
- *
+ *
* @throws Exception
*/
@Test(enabled = ENABLE_TESTS)
@@ -1219,10 +1219,6 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
public void uninventoryResource(int resourceId) {
}
- @Asynchronous(guaranteedDelivery = true)
- public void synchronizeInventory(ResourceSyncInfo syncInfo) {
- }
-
public Configuration validate(Configuration configuration, int resourceId, boolean isStructured)
throws PluginContainerException {
return null;
@@ -1232,5 +1228,13 @@ public class ConfigurationManagerBeanTest extends AbstractEJB3Test {
public void requestFullAvailabilityReport() {
return;
}
+
+ @Override
+ public void synchronizePlatform(PlatformSyncInfo syncInfo) {
+ }
+
+ @Override
+ public void synchronizeServer(int resourceId, Collection<ResourceSyncInfo> toplevelServerSyncInfo) {
+ }
}
}
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/LargeGroupPluginConfigurationTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/LargeGroupPluginConfigurationTest.java
index 13343d8..b5e0dba 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/LargeGroupPluginConfigurationTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/configuration/LargeGroupPluginConfigurationTest.java
@@ -18,6 +18,7 @@
*/
package org.rhq.enterprise.server.configuration;
+import java.util.Collection;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -34,8 +35,8 @@ import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
-import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.enterprise.server.authz.PermissionException;
@@ -112,7 +113,7 @@ public class LargeGroupPluginConfigurationTest extends LargeGroupTestBase {
int groupUpdateId = configurationManager.scheduleGroupPluginConfigurationUpdate(env.normalSubject,
env.compatibleGroup.getId(), existingMap);
- // group plugin configuration update has been kicked off, wait for the mock agents to each complete their update
+ // group plugin configuration update has been kicked off, wait for the mock agents to each complete their update
System.out.print("Waiting for mock agents");
assert latch.await(5, TimeUnit.MINUTES) : "agents should not have taken this long";
System.out.println(" Mock agents are done.");
@@ -222,10 +223,6 @@ public class LargeGroupPluginConfigurationTest extends LargeGroupTestBase {
}
@Override
- public void synchronizeInventory(ResourceSyncInfo syncInfo) {
- }
-
- @Override
public void uninventoryResource(int resourceId) {
}
@@ -241,5 +238,13 @@ public class LargeGroupPluginConfigurationTest extends LargeGroupTestBase {
public void requestFullAvailabilityReport() {
return;
}
+
+ @Override
+ public void synchronizePlatform(PlatformSyncInfo syncInfo) {
+ }
+
+ @Override
+ public void synchronizeServer(int resourceId, Collection<ResourceSyncInfo> toplevelServerSyncInfo) {
+ }
}
}
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
index 9cf9f98..eb3cf20 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
@@ -205,10 +205,12 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
assertNotNull(results.getPlatformSyncInfo());
- ResourceSyncInfo syncInfo = discoveryBoss.getResourceSyncInfo(results.getPlatformSyncInfo().getId());
- assert syncInfo != null;
+ Collection<ResourceSyncInfo> syncInfos = discoveryBoss.getResourceSyncInfo(results.getPlatformSyncInfo()
+ .getPlatform().getId());
+ assert syncInfos != null;
+ assert !syncInfos.isEmpty();
- platform.setId(syncInfo.getId());
+ platform.setId(results.getPlatformSyncInfo().getPlatform().getId());
// Now submit the server and its children as an update report
inventoryReport = new InventoryReport(agent);
@@ -251,16 +253,19 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
assertNotNull(results.getPlatformSyncInfo());
- ResourceSyncInfo syncInfo = discoveryBoss.getResourceSyncInfo(results.getPlatformSyncInfo().getId());
- assert syncInfo != null;
+ assertNotNull(results.getPlatformSyncInfo().getTopLevelServerIds());
+ assertTrue(!results.getPlatformSyncInfo().getTopLevelServerIds().isEmpty());
+ Integer resourceId = results.getPlatformSyncInfo().getTopLevelServerIds().iterator().next();
+ Collection<ResourceSyncInfo> syncInfos = discoveryBoss.getResourceSyncInfo(resourceId);
+ assert syncInfos != null;
+ assert !syncInfos.isEmpty();
- ResourceSyncInfo serverSyncInfo = syncInfo.getChildSyncInfos().iterator().next();
Resource resource1 = discoveryBoss.manuallyAddResource(subjectManager.getOverlord(), serviceType2.getId(),
- serverSyncInfo.getId(), new Configuration());
+ resourceId, new Configuration());
try {
Resource resource2 = discoveryBoss.manuallyAddResource(subjectManager.getOverlord(), serviceType2.getId(),
- serverSyncInfo.getId(), new Configuration());
+ resourceId, new Configuration());
fail("Manually adding a singleton that already existed succeeded: " + resource2);
} catch (EJBException e) {
assertEquals(String.valueOf(e.getCause()), RuntimeException.class, e.getCause().getClass());
@@ -295,15 +300,15 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert platformSyncInfo != null;
// Check merge result
- assertEquals(InventoryStatus.NEW, platformSyncInfo.getInventoryStatus());
- assertEquals(platform.getChildResources().size(), platformSyncInfo.getTopLevelServers().size());
+ assertEquals(InventoryStatus.NEW, platformSyncInfo.getPlatform().getInventoryStatus());
+ assertEquals(platform.getChildResources().size(), platformSyncInfo.getTopLevelServerIds().size());
// Collect the resource ids generated for the platform and the servers
- int platformId = platformSyncInfo.getId();
+ int platformId = platformSyncInfo.getPlatform().getId();
List<Integer> serverIds = new LinkedList<Integer>();
- for (Resource serverSyncInfo : platformSyncInfo.getTopLevelServers()) {
- serverIds.add(serverSyncInfo.getId());
+ for (Integer serverId : platformSyncInfo.getTopLevelServerIds()) {
+ serverIds.add(serverId);
}
int[] arrayOfServerIds = ArrayUtils.unwrapCollection(serverIds);
@@ -368,8 +373,8 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert mergeResults.getIgnoredResourceTypes().contains(new ResourceTypeFlyweight(serverType));
// Check merge result - make sure we should not see any children under the platform (it should have been ignored)
- assertEquals(InventoryStatus.NEW, platformSyncInfo.getInventoryStatus());
- assertEquals(platformSyncInfo.getTopLevelServers().size(), 0);
+ assertEquals(InventoryStatus.NEW, platformSyncInfo.getPlatform().getInventoryStatus());
+ assertEquals(platformSyncInfo.getTopLevelServerIds().size(), 0);
}
@Test(groups = "integration.ejb3")
@@ -428,10 +433,10 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert platformSyncInfo != null;
// Collect the resource ids generated for the platform and the servers
- int platformId = platformSyncInfo.getId();
+ int platformId = platformSyncInfo.getPlatform().getId();
List<Integer> serverIds = new LinkedList<Integer>();
- for (Resource serverSyncInfo : platformSyncInfo.getTopLevelServers()) {
- serverIds.add(serverSyncInfo.getId());
+ for (Integer serverId : platformSyncInfo.getTopLevelServerIds()) {
+ serverIds.add(serverId);
}
int[] arrayOfServerIds = ArrayUtils.unwrapCollection(serverIds);
@@ -469,8 +474,8 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assertEquals(mergeResults.getIgnoredResourceTypes().size(), 1);
assert mergeResults.getIgnoredResourceTypes().contains(new ResourceTypeFlyweight(serverType));
- assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getInventoryStatus()); // notice platform is committed now
- assertEquals(platformSyncInfo.getTopLevelServers().size(), 0); // notice there are no server children now
+ assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getPlatform().getInventoryStatus()); // notice platform is committed now
+ assertEquals(platformSyncInfo.getTopLevelServerIds().size(), 0); // notice there are no server children now
}
@Test(groups = "integration.ejb3")
@@ -501,11 +506,11 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert platformSyncInfo != null;
// Check merge result
- assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getInventoryStatus());
- assertEquals(storagePlatform.getChildResources().size(), platformSyncInfo.getTopLevelServers().size());
+ assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getPlatform().getInventoryStatus());
+ assertEquals(storagePlatform.getChildResources().size(), platformSyncInfo.getTopLevelServerIds().size());
storageNode = resourceManager.getResourceById(subjectManager.getOverlord(), platformSyncInfo
- .getTopLevelServers().iterator().next().getId());
+ .getTopLevelServerIds().iterator().next());
assertNotNull(storageNode);
assertEquals(InventoryStatus.COMMITTED, storageNode.getInventoryStatus());
}
@@ -533,8 +538,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Then simulate a create resource request
final String userSuppliedResourceName = prefix("User Supplied Resource Name");
final String newResourceKey = prefix("Created Resource Key");
- Resource serverSyncInfo = firstDiscoverySyncInfo.getTopLevelServers().iterator().next();
- final int serverResourceId = serverSyncInfo.getId();
+ final int serverResourceId = firstDiscoverySyncInfo.getTopLevelServerIds().iterator().next();
executeInTransaction(false, new TransactionCallback() {
@Override
@@ -568,10 +572,13 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Check that the resource ends with the user supplied name in inventory
- ResourceSyncInfo topLevelServerSyncInfo = discoveryBoss.getResourceSyncInfo(secondDiscoverySyncInfo
- .getTopLevelServers().iterator().next().getId());
- ResourceSyncInfo service1SyncInfo = topLevelServerSyncInfo.getChildSyncInfos().iterator().next();
- Resource service1Resource = getEntityManager().find(Resource.class, service1SyncInfo.getId());
+ Integer toplevelServerId = secondDiscoverySyncInfo.getTopLevelServerIds().iterator().next();
+ Collection<ResourceSyncInfo> topLevelServerSyncInfo = discoveryBoss.getResourceSyncInfo(toplevelServerId);
+ assert topLevelServerSyncInfo.size() == 2;
+ Iterator<ResourceSyncInfo> iter = topLevelServerSyncInfo.iterator();
+ Integer childId = iter.next().getId();
+ childId = childId.equals(toplevelServerId) ? iter.next().getId() : childId;
+ Resource service1Resource = getEntityManager().find(Resource.class, childId);
assertEquals(userSuppliedResourceName, service1Resource.getName());
}
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/test/TestAgentClient.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/test/TestAgentClient.java
index 0c3a86e..93e2220 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/test/TestAgentClient.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/test/TestAgentClient.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.server.test;
import java.io.InputStream;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -53,17 +54,16 @@ import org.rhq.core.clientapi.server.content.DeletePackagesRequest;
import org.rhq.core.clientapi.server.content.DeployPackagesRequest;
import org.rhq.core.clientapi.server.content.RetrievePackageBitsRequest;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
-import org.rhq.core.communications.command.annotation.Asynchronous;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.content.transfer.DeployPackageStep;
import org.rhq.core.domain.content.transfer.ResourcePackageDetails;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.drift.DriftDefinition;
import org.rhq.core.domain.drift.DriftFile;
import org.rhq.core.domain.drift.DriftSnapshot;
-import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.domain.measurement.ResourceMeasurementScheduleRequest;
@@ -333,12 +333,6 @@ public class TestAgentClient implements AgentClient, BundleAgentService, DriftAg
throws InvalidPluginConfigurationClientException, PluginContainerException {
}
- @Asynchronous(guaranteedDelivery = true)
- @Override
- public void synchronizeInventory(ResourceSyncInfo syncInfo) {
- return;
- }
-
@Override
public void createResource(CreateResourceRequest request) throws PluginContainerException {
}
@@ -421,4 +415,12 @@ public class TestAgentClient implements AgentClient, BundleAgentService, DriftAg
public void requestFullAvailabilityReport() {
return;
}
+
+ @Override
+ public void synchronizePlatform(PlatformSyncInfo syncInfo) {
+ }
+
+ @Override
+ public void synchronizeServer(int resourceId, Collection<ResourceSyncInfo> toplevelServerSyncInfo) {
+ }
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
index 71e2e3f..656abd2 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
@@ -24,6 +24,7 @@ import static org.rhq.core.util.StringUtil.isBlank;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EnumSet;
@@ -271,14 +272,78 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
return null;
}
- PlatformSyncInfo result = entityManager.find(PlatformSyncInfo.class, platform.getId());
+ Set<Resource> toplevelServices = new HashSet<Resource>();
+ Set<Integer> topLevelServerIds = new HashSet<Integer>();
+
+ for (Resource platformChild : platform.getChildResources()) {
+ switch (platformChild.getResourceType().getCategory()) {
+ case SERVER:
+ topLevelServerIds.add(platformChild.getId());
+ break;
+ case SERVICE:
+ toplevelServices.add(platformChild);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ResourceSyncInfo platformSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platform);
+ Set<ResourceSyncInfo> topLevelServiceSyncInfo = getToplevelServiceSyncInfo(toplevelServices);
+ PlatformSyncInfo result = new PlatformSyncInfo(platformSyncInfo, topLevelServiceSyncInfo, topLevelServerIds);
+
return result;
}
+ /**
+ * At the time of writing (4.10) platform top level services don't have children so this will be quick, but
+ * write it to handle any future children. In general this will still be a relatively small number of resources.
+ *
+ * @param topLevelServices
+ * @return The top level service hierarchy sync info
+ */
+ private Set<ResourceSyncInfo> getToplevelServiceSyncInfo(Set<Resource> topLevelServices) {
+ Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>(topLevelServices.size());
+ Set<Integer> topLevelServiceIds = new HashSet<Integer>();
+
+ for (Resource topLevelService : topLevelServices) {
+ result.add(ResourceSyncInfo.buildResourceSyncInfo(topLevelService));
+ topLevelServiceIds.add(topLevelService.getId());
+ }
+
+ getToplevelServiceSyncInfoHierarchy(topLevelServiceIds, result);
+
+ return result;
+ }
+
+ private void getToplevelServiceSyncInfoHierarchy(Set<Integer> parentIds, Set<ResourceSyncInfo> result) {
+ if (parentIds.isEmpty()) {
+ return;
+ }
+
+ Query q = entityManager.createNamedQuery(ResourceSyncInfo.QUERY_SERVICE_CHILDREN);
+ q.setParameter("parentIds", parentIds);
+ List<ResourceSyncInfo> childSyncInfos = q.getResultList();
+
+ if (!childSyncInfos.isEmpty()) {
+ result.addAll(childSyncInfos);
+
+ Set<Integer> childIds = new HashSet<Integer>(childSyncInfos.size());
+ for (ResourceSyncInfo childSyncInfo : childSyncInfos) {
+ childIds.add(childSyncInfo.getId());
+ }
+ getToplevelServiceSyncInfoHierarchy(childIds, result);
+ }
+ }
+
@Override
- public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
- // [PERF] this is expensive, it let's hibernate grab the whole hierarchy via eager fetch of children.
- ResourceSyncInfo result = entityManager.find(ResourceSyncInfo.class, resourceId);
+ public Collection<ResourceSyncInfo> getResourceSyncInfo(int resourceId) {
+ // [PERF] this is an expensive query that can return a large collection. But it's faster than the old way of
+ // letting hibernate grab the whole hierarchy via eager fetch of children...
+ Query q = entityManager.createNamedQuery(ResourceSyncInfo.QUERY_TOP_LEVEL_SERVER);
+ q.setParameter("resourceId", resourceId);
+
+ Collection<ResourceSyncInfo> result = q.getResultList();
return result;
}
@@ -360,7 +425,7 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
servers = attachedServers;
// Update and persist the actual inventory statuses
- // This is done is a separate transaction to stop failures in the agent from rolling back the transaction
+ // This is done in a separate transaction to stop failures in the agent from rolling back the transaction
discoveryBoss.updateInventoryStatusInNewTransaction(user, platforms, servers, status);
scheduleAgentInventoryOperationJob(platforms, servers);
@@ -422,7 +487,7 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
}
/**
- * Synchronize the agents inventory status for platforms, and then the servers,
+ * Synchronize the agent's inventory status for platforms, and then the servers,
* omitting servers under synced platforms since they will have been handled
* already. On status change request an agent sync on the affected resources.
* The agent will sync status and determine what other sync work needs to be
@@ -438,8 +503,9 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
AgentClient agentClient = agentManager.getAgentClient(platform.getAgent());
if (agentClient != null) {
try {
- syncInfo = entityManager.find(ResourceSyncInfo.class, platform.getId());
- agentClient.getDiscoveryAgentService().synchronizeInventory(syncInfo);
+ //syncInfo = entityManager.find(ResourceSyncInfo.class, platform.getId());
+ PlatformSyncInfo platformSyncInfo = getPlatformSyncInfo(platform.getAgent());
+ agentClient.getDiscoveryAgentService().synchronizePlatform(platformSyncInfo);
} catch (Exception e) {
LOG.warn("Could not perform commit synchronization with agent for platform [" + platform.getName()
+ "]", e);
@@ -455,8 +521,9 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
AgentClient agentClient = agentManager.getAgentClient(server.getAgent());
if (agentClient != null) {
try {
- syncInfo = entityManager.find(ResourceSyncInfo.class, server.getId());
- agentClient.getDiscoveryAgentService().synchronizeInventory(syncInfo);
+ //syncInfo = entityManager.find(ResourceSyncInfo.class, server.getId());
+ Collection<ResourceSyncInfo> syncInfos = getResourceSyncInfo(server.getId());
+ agentClient.getDiscoveryAgentService().synchronizeServer(server.getId(), syncInfos);
} catch (Exception e) {
LOG.warn("Could not perform commit synchronization with agent for server [" + server.getName()
+ "]", e);
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
index 7ed9445..85b55f0 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
@@ -18,6 +18,7 @@
*/
package org.rhq.enterprise.server.discovery;
+import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
@@ -87,9 +88,11 @@ public interface DiscoveryBossLocal extends DiscoveryBossRemote {
/**
* @param resourceid the root resourceId on which we want to sync
- * @return null if resource not found, otherwise the entire tree rooted at the specified resource
+ * @return null if resource not found, otherwise the entire tree rooted at the specified resource, as an
+ * unordered collection. Although not strictly a Set (to save on computation) this collection should not
+ * contain duplicates.
*/
- ResourceSyncInfo getResourceSyncInfo(int resourceId);
+ Collection<ResourceSyncInfo> getResourceSyncInfo(int resourceId);
/**
* Returns a map of platforms (the keys) and their servers (the values) that are in the auto-discovery queue but not
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
index 533948b..8fd2cf2 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.server.discovery;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -121,10 +122,10 @@ public class DiscoveryServerServiceImpl implements DiscoveryServerService {
}
@Override
- public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
+ public Collection<ResourceSyncInfo> getResourceSyncInfo(int resourceId) {
long start = System.currentTimeMillis();
DiscoveryBossLocal discoveryBoss = LookupUtil.getDiscoveryBoss();
- ResourceSyncInfo results;
+ Collection<ResourceSyncInfo> results;
results = discoveryBoss.getResourceSyncInfo(resourceId);
commit e8b4257d03e566107be564cfa5f71db594589afc
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 10:50:02 2014 -0500
BZ 994250 - finish the merging/peer review for patches submitted so that rhqctl returns proper exit codes. Note that this completes the merge of the two submitted patches - see prior two commits to this one. This third commit fixes some problems with the original patches: 1. Some scripts/files are not found in bin/internal of the distro, but rather are in bin/ - so we need to avoid using getBinDir() in those cases 2. Fix the code to conform to code conventions - DEATH TO TABS! 3. Remove a constant that got resurrected (ControlCommand.RHQ_STORAGE_BASEDIR_PROP is no longer needed) 4. A couple other minor things
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index 5db80a1..a13a598 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,7 +60,6 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
- public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 28a37de..88b085f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index 1b3d47b..ed2b2f8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ org.apache.commons.exec.CommandLine commandLine;
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 5d540a1..e2ef3a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index a3920e0..7ef66b4 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if(isStorageRunning()) {
+ if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index cc3d8c2..e80edd9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
+ executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 7f9d9c21135cd1d210438f48b7df0737709b5dfe
Author: burmanm <yak(a)iki.fi>
Date: Tue Aug 6 17:52:38 2013 +0200
Fix return codes of the rhqctl command, rebased to the 4.10 master.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index a13a598..5db80a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,6 +60,7 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
+ public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 88b085f..28a37de 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index ed2b2f8..1b3d47b 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine;
+ org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index e2ef3a8..5d540a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index 7ef66b4..a3920e0 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if (isStorageRunning()) {
+ if(isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index e80edd9..cc3d8c2 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
+ executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 1eb8c728c0c1939115fe90bd1c91fbf5ecf0a036
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 17 21:35:47 2013 +0100
Bug 968361 - Improve database plugin design to support connection pooling
This changeset introduces a new API for database plugins and deprecates the previous one. Compatibility with the previous API will be maintained until next major version of RHQ.
The 'rhq-database-plugin' was based on org.rhq.plugins.database.DatabaseComponent interface which encouraged plugin authors to share a single JDBC connection across database components. This was wrong for various reasons (connection leaks, concurrent JDBC calls... etc).
The new API introduces three important classes:
* org.rhq.plugins.database.PooledConnectionProvider
* org.rhq.plugins.database.BasePooledConnectionProvider
* org.rhq.plugins.database.ConnectionPoolingSupport
BasePooledConnectionProvider is a base implementation of a PooledConnectionProvider. Plugin authors should create a concrete implementation of BasePooledConnectionProvider which overrides the #getDriverClass() method. This is important if a database plugin embeds a JDBC driver: the database-specific driver class must be loaded by the child plugin classloader.
ConnectionPoolingSupport helps to manage the compatibility with the old API. It's a contract that all new database resource components should obey to. It declares the following methods:
* #supportsConnectionPooling()
* #getPooledConnectionProvider()
Results of calls to #supportsConnectionPooling() #getPooledConnectionProvider() must be consistent. In practice, a top level server database component should be able to create a PooledConnectionProvider instance, and child servers and services should indicate they support connection pooling only if their parent component does.
The 'rhq-database-plugin' embeds the BoneCP library (JDBC connection pooling) and its dependencies (Google's Guava). Child plugins will have all the classes accessible as soon as they have this node in their plugin descriptor:
===
<depends plugin="Database" useClasses="true"/>
===
This changeset includes the necessary changes to support connection pooling in the Oracle, Postgres and MySQL plugins.
Thanks to Elias Ross for contributing the original patch from which this changeset is derived.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
deleted file mode 100644
index 6c81332..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ /dev/null
@@ -1,125 +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.plugins.mysql;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.HashMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A class to manage the connections to MySQL
- * This class keeps a cache of connections to MySQL and reuses them on demand
- * We assume single threaded access to the Connection in the agent
- * this will need to be reworked if that assumption is not correct
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionManager {
-
- private HashMap<MySqlConnectionInfo, Connection> connections;
- private static MySqlConnectionManager singleton;
- private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
-
- private MySqlConnectionManager() {
- connections = new HashMap<MySqlConnectionInfo,Connection>();
- }
-
- static MySqlConnectionManager getConnectionManager() {
- if (singleton == null) {
- singleton = new MySqlConnectionManager();
- }
- return singleton;
- }
-
- public void shutdown() {
- Driver driver = null;
- for (Connection conn : connections.values()) {
- try {
- if (driver == null) {
- String driverName = conn.getMetaData().getDriverName();
- driver = DriverManager.getDriver(driverName);
- }
- conn.close();
- }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
- }
- // deregister driver as well
- if (driver != null) {
- try {
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- logger.warn("Unable to deregister MySQL Driver on shutdown");
- }
- }
- }
-
- void closeConnection(MySqlConnectionInfo info) {
- Connection conn = connections.get(info);
- if (conn != null) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Closing Connection to " + info.buildURL());
- }
- conn.close();
- } catch (SQLException e) {
- logger.warn("Problem closing connection to " + info.buildURL() + " on close");
- }
- }
- connections.remove(info);
- }
-
- Connection getConnection (MySqlConnectionInfo info) throws SQLException {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
-
- Connection conn = connections.get(info);
- String url = info.buildURL();
- if (conn == null) {
- if (logger.isInfoEnabled()) {
- logger.info("Attemping connection to " + url);
- }
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- if (logger.isInfoEnabled()) {
- logger.info("Successfully connected to " + url);
- }
- connections.put(info, conn);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Reusing existing connection to " + url);
- }
- }
-
- // check the validity of the connection
- if (!conn.isValid(0)) {
- // attempt a single reconnect here and now
- conn.close();
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- connections.put(info, conn);
- logger.info("Refreshed a connection to " + url);
- }
- return conn;
- }
-
-}
commit ec1dee4e898b0383c09df083c0d8cb999aa70e85
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Dec 23 14:50:55 2013 +0100
Plugin validation for mysql plugin was failing because the Class.forName() statement was invoked from the constructor of the component. I moved this code to the method that actually opens the connection. This is the same strategy we use with our postgres plugin.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
new file mode 100644
index 0000000..6c81332
--- /dev/null
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
@@ -0,0 +1,125 @@
+/*
+ * 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.plugins.mysql;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.HashMap;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * A class to manage the connections to MySQL
+ * This class keeps a cache of connections to MySQL and reuses them on demand
+ * We assume single threaded access to the Connection in the agent
+ * this will need to be reworked if that assumption is not correct
+ * @author Steve Millidge (C2B2 Consulting Limited)
+ */
+class MySqlConnectionManager {
+
+ private HashMap<MySqlConnectionInfo, Connection> connections;
+ private static MySqlConnectionManager singleton;
+ private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
+
+ private MySqlConnectionManager() {
+ connections = new HashMap<MySqlConnectionInfo,Connection>();
+ }
+
+ static MySqlConnectionManager getConnectionManager() {
+ if (singleton == null) {
+ singleton = new MySqlConnectionManager();
+ }
+ return singleton;
+ }
+
+ public void shutdown() {
+ Driver driver = null;
+ for (Connection conn : connections.values()) {
+ try {
+ if (driver == null) {
+ String driverName = conn.getMetaData().getDriverName();
+ driver = DriverManager.getDriver(driverName);
+ }
+ conn.close();
+ }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
+ }
+ // deregister driver as well
+ if (driver != null) {
+ try {
+ DriverManager.deregisterDriver(driver);
+ } catch (SQLException ex) {
+ logger.warn("Unable to deregister MySQL Driver on shutdown");
+ }
+ }
+ }
+
+ void closeConnection(MySqlConnectionInfo info) {
+ Connection conn = connections.get(info);
+ if (conn != null) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Closing Connection to " + info.buildURL());
+ }
+ conn.close();
+ } catch (SQLException e) {
+ logger.warn("Problem closing connection to " + info.buildURL() + " on close");
+ }
+ }
+ connections.remove(info);
+ }
+
+ Connection getConnection (MySqlConnectionInfo info) throws SQLException {
+ try {
+ Class.forName("com.mysql.jdbc.Driver");
+ } catch (Exception ex) {
+ logger.error("Unable to find com.mysql.jdbc.Driver");
+ }
+
+ Connection conn = connections.get(info);
+ String url = info.buildURL();
+ if (conn == null) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Attemping connection to " + url);
+ }
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ if (logger.isInfoEnabled()) {
+ logger.info("Successfully connected to " + url);
+ }
+ connections.put(info, conn);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Reusing existing connection to " + url);
+ }
+ }
+
+ // check the validity of the connection
+ if (!conn.isValid(0)) {
+ // attempt a single reconnect here and now
+ conn.close();
+ conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
+ connections.put(info, conn);
+ logger.info("Refreshed a connection to " + url);
+ }
+ return conn;
+ }
+
+}
10 years, 4 months
[rhq] modules/core modules/enterprise modules/integration-tests
by Jay Shaughnessy
modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java | 101 ++--
modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java | 8
modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/InsideAgentSimulationTest.java | 14
modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/avail/AvailTest.java | 14
modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java | 14
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/MergeInventoryReportResults.java | 10
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java | 74 ++
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java | 87 +--
modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java | 98 +++
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/avail/AvailTest.java | 4
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java | 47 +
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/CommitThenIgnoreTypesInventoryManagerTest.java | 3
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackAbortTest.java | 24
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackTest.java | 20
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackVetoTest.java | 20
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryTest.java | 4
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/IgnoreTypesInventoryManagerTest.java | 1
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/InventoryManagerTest.java | 2
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/LateMeasurementRescheduleTest.java | 4
modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java | 2
modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java | 6
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java | 251 +++++-----
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java | 2
modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java | 72 +-
modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java | 50 +
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java | 22
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java | 17
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java | 23
modules/integration-tests/apache-plugin-test/src/test/java/org/rhq/plugins/apache/setup/ApacheTestSetup.java | 10
29 files changed, 668 insertions(+), 336 deletions(-)
New commits:
commit ccdacc890994172b0ef751edbeb0dd4220d18f7e
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Dec 13 17:21:40 2013 -0500
[1023451] [perf] Large retained heap during inventory report merge (causing OOMs)
Note - this is related work, not necessarily a solution to this problem.
One factor in the memory consumption of mergeInventoryReport is that we
return the entire resource hierarchy for the platform. These hierarchy
is comprised of lightweight (ResourceSyncInfo) objects, but still, for
a large inventory this can be large. Moreover, the agent must receive the
same structure into memory.
This commit aims to reduce memory consumption at the expense of "chunking"
the hierarchy by top-level-server. The general flow is that mergeInventoryReport
now returns the new PlatformSyncInfo, which is just the platform sync info and
the list of top level servers. The agent then performs the sync in pieces, calling back to the server for each
top level server's ResourceSyncInfo.
notes:
- New abstract SyncInfo pulled down from ResourceSyncInfo and new superclass
PlatformSyncInfo. These are both now used in the sync process.
- ResourceSyncInfo made more lightweight by replacing parentResource with
parentId.
- Had to change test mocks to handle the new strategy
- add mock support for getResourceSyncInfo service
- enhance AbstractIgnoreTypesInventoryManagerBaseTest to actually assign
resource ids to the simulated server inventory
- Fix a few PC itest issues
- increase waitForInventory max time, it wasn't long enough
- add missing waitForInventory in a couple of places
diff --git a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
index 94654ee..74a0acc 100644
--- a/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
+++ b/modules/core/arquillian-integration/container/src/main/java/org/rhq/test/arquillian/FakeServerInventory.java
@@ -41,6 +41,7 @@ import org.rhq.core.clientapi.agent.upgrade.ResourceUpgradeResponse;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.DisplayType;
@@ -63,7 +64,7 @@ import org.rhq.core.domain.resource.ResourceType;
* own. It is only meant as a helper.
* <p>
* This impl uses mockito for defining the answers to various calls.
- *
+ *
* @author Lukas Krejci
*/
public class FakeServerInventory {
@@ -75,7 +76,7 @@ public class FakeServerInventory {
* for the complete discovery to finish in case your
* fake server commits some resources (which starts off
* asynchronous discovery of children).
- *
+ *
*
* @author Lukas Krejci
*/
@@ -235,7 +236,8 @@ public class FakeServerInventory {
platform = persisted;
}
}
- return new MergeInventoryReportResults(getSyncInfo(), null);
+
+ return new MergeInventoryReportResults(getPlatformSyncInfo(), null);
} finally {
if (discoveryChecker != null && !inventoryReport.getAddedRoots().isEmpty()) {
discoveryChecker.setDepth(getResourceTreeDepth());
@@ -246,14 +248,54 @@ public class FakeServerInventory {
};
}
+ public synchronized Answer<ResourceSyncInfo> getResourceSyncInfo() {
+ return new Answer<ResourceSyncInfo>() {
+ @Override
+ public ResourceSyncInfo answer(InvocationOnMock invocation) throws Throwable {
+ synchronized (FakeServerInventory.this) {
+ Integer resourceId = (Integer) invocation.getArguments()[0];
+
+ try {
+ throwIfFailing();
+
+ for (Resource c : platform.getChildResources()) {
+ if (c.getId() == resourceId) {
+ return getResourceSyncInfo(c);
+ }
+ }
+ return null;
+ } finally {
+ // TODO: We may need to actually do a check here to make sure we've been invoked once
+ // for every top level server, before setting depth...
+ if (discoveryChecker != null) {
+ discoveryChecker.setDepth(getResourceTreeDepth());
+ }
+ }
+ }
+ }
+ };
+ }
+
public synchronized int getResourceTreeDepth() {
if (platform == null) {
return 0;
}
+ // dumpTree(platform, "");
+
return getTreeDepth(platform);
}
+ /*
+ private static void dumpTree(Resource r, String indent) {
+ System.out.println(indent + r.getName());
+ indent += " ";
+ for (Resource c : r.getChildResources()) {
+ dumpTree(c, indent);
+ }
+ }
+ */
+
private static int getTreeDepth(Resource root) {
int maxDepth = 0;
for (Resource c : root.getChildResources()) {
@@ -266,16 +308,16 @@ public class FakeServerInventory {
return maxDepth + 1;
}
- public synchronized Answer<ResourceSyncInfo> clearPlatform() {
- return new Answer<ResourceSyncInfo>() {
+ public synchronized Answer<PlatformSyncInfo> clearPlatform() {
+ return new Answer<PlatformSyncInfo>() {
@Override
- public ResourceSyncInfo answer(InvocationOnMock invocation) throws Throwable {
+ public PlatformSyncInfo answer(InvocationOnMock invocation) throws Throwable {
synchronized (FakeServerInventory.this) {
throwIfFailing();
platform = null;
- return getSyncInfo();
+ return getPlatformSyncInfo();
}
}
};
@@ -576,53 +618,42 @@ public class FakeServerInventory {
return persisted;
}
- private ResourceSyncInfo getSyncInfo() {
- return platform != null ? convert(platform) : null;
+ private PlatformSyncInfo getPlatformSyncInfo() {
+ return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- private void throwIfFailing() {
- if (failing) {
- throw new RuntimeException("Fake server inventory is in the failing mode.");
- }
+ private ResourceSyncInfo getResourceSyncInfo(Resource resource) {
+ return resource == null ? null : convert(resource);
}
private static ResourceSyncInfo convert(Resource root) {
- return convertInternal(root, new HashMap<String, ResourceSyncInfo>());
+ return convertInternal(root, true, new HashMap<String, ResourceSyncInfo>());
}
- private static ResourceSyncInfo convertInternal(Resource root, Map<String, ResourceSyncInfo> intermediateResults) {
+ private static ResourceSyncInfo convertInternal(Resource root, boolean isTopLevelServer,
+ Map<String, ResourceSyncInfo> intermediateResults) {
+
ResourceSyncInfo ret = intermediateResults.get(root.getUuid());
if (ret != null) {
return ret;
}
-
try {
- ret = new ResourceSyncInfo();
-
+ ret = ResourceSyncInfo.buildResourceSyncInfo(root);
intermediateResults.put(root.getUuid(), ret);
- Class<ResourceSyncInfo> clazz = ResourceSyncInfo.class;
-
- getPrivateField(clazz, "id").set(ret, root.getId());
- getPrivateField(clazz, "uuid").set(ret, root.getUuid());
- getPrivateField(clazz, "mtime").set(ret, root.getMtime());
- getPrivateField(clazz, "inventoryStatus").set(ret, root.getInventoryStatus());
-
- ResourceSyncInfo parent = root.getParentResource() == null ? null : convertInternal(
- root.getParentResource(), intermediateResults);
-
- getPrivateField(clazz, "parent").set(ret, parent);
+ Integer parentId = root.getParentResource() == null ? null : root.getParentResource().getId();
+ getPrivateField(ResourceSyncInfo.class, "parentId").set(ret, parentId);
Set<ResourceSyncInfo> children = new LinkedHashSet<ResourceSyncInfo>();
for (Resource child : root.getChildResources()) {
- ResourceSyncInfo syncChild = convertInternal(child, intermediateResults);
+ ResourceSyncInfo syncChild = convertInternal(child, false, intermediateResults);
children.add(syncChild);
}
- getPrivateField(clazz, "childSyncInfos").set(ret, children);
-
+ getPrivateField(ResourceSyncInfo.class, "childSyncInfos").set(ret, children);
return ret;
+
} catch (Exception e) {
throw new IllegalStateException("Failed to convert resource " + root
+ " to a ResourceSyncInfo. This should not happen.", e);
@@ -638,6 +669,12 @@ public class FakeServerInventory {
return field;
}
+ private void throwIfFailing() {
+ if (failing) {
+ throw new RuntimeException("Fake server inventory is in the failing mode.");
+ }
+ }
+
private static Resource findResource(Resource root, Resource template, Comparator<Resource> comparator) {
if (root == null)
return null;
diff --git a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
index 4483cba7..60cd53d 100644
--- a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
+++ b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/CleanUpTest.java
@@ -23,13 +23,13 @@
package org.rhq.test.arquillian;
-import java.util.Arrays;
-import java.util.HashSet;
-
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
+import java.util.Arrays;
+import java.util.HashSet;
+
import org.testng.annotations.Test;
import org.jboss.arquillian.container.test.api.Deployment;
@@ -80,6 +80,8 @@ public class CleanUpTest extends Arquillian {
//autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(int.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@BeforeDiscovery(testMethods = {"testCleanAll", "testClearingAfterTest", "checkDiscoveryCanRunFullBecauseInventoryClear"})
diff --git a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/InsideAgentSimulationTest.java b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/InsideAgentSimulationTest.java
index babfe62..2407a9f 100644
--- a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/InsideAgentSimulationTest.java
+++ b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/InsideAgentSimulationTest.java
@@ -66,28 +66,30 @@ public class InsideAgentSimulationTest extends Arquillian {
private FakeServerInventory fakeServerInventory;
private FakeServerInventory.CompleteDiscoveryChecker discoveryCompleteChecker;
-
+
@BeforeDiscovery(order = 1)
public void resetServerServices() {
serverServices.resetMocks();
fakeServerInventory = new FakeServerInventory();
}
-
+
@BeforeDiscovery(testMethods = "testDeepDiscovery", order = 2)
public void setupDiscoveryMocks() throws Exception {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(3);
//autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(int.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
-
+
@AfterDiscovery
public void waitForAsyncDiscoveries() throws Exception {
if (discoveryCompleteChecker != null) {
discoveryCompleteChecker.waitForDiscoveryComplete();
}
}
-
+
//we need to make sure that the no discovery test is run first, because the plugin container
//would keep the inventory from the previous test.
//the other two tests get each a new serverside, which, when synced with the PC, will cause
@@ -98,7 +100,7 @@ public class InsideAgentSimulationTest extends Arquillian {
Assert.assertEquals(discoveredServers.size(), 0, "There should be no server discovered");
Assert.assertEquals(discoveredServices.size(), 0, "There should be no service discovered");
}
-
+
//the difference between this test and the deep discovery one is that for this test
//the mocks should not be set up and hence only a top server discovery should occur
@Test(dependsOnMethods = "testNoDiscovery")
@@ -107,7 +109,7 @@ public class InsideAgentSimulationTest extends Arquillian {
Assert.assertEquals(discoveredServers.size(), 1, "There should be 1 server discovered");
Assert.assertEquals(discoveredServices.size(), 0, "There should be no service discovered");
}
-
+
@Test(dependsOnMethods = "testNoDiscovery")
@RunDiscovery
public void testDeepDiscovery() throws Exception {
diff --git a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/avail/AvailTest.java b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/avail/AvailTest.java
index d2d5efd..f38d021 100644
--- a/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/avail/AvailTest.java
+++ b/modules/core/arquillian-integration/container/src/test/java/org/rhq/test/arquillian/avail/AvailTest.java
@@ -5,8 +5,6 @@ import static org.mockito.Mockito.when;
import java.util.Set;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -18,7 +16,6 @@ import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
-import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.pc.PluginContainer;
import org.rhq.core.pc.inventory.ResourceContainer;
@@ -55,7 +52,7 @@ public class AvailTest extends Arquillian {
private FakeServerInventory fakeServerInventory;
private FakeServerInventory.CompleteDiscoveryChecker completeDiscoveryChecker;
-
+
@ResourceContainers(plugin = "availPlugin", resourceType = "AvailParentServer1")
private Set<ResourceContainer> parentContainers1;
@@ -98,16 +95,19 @@ public class AvailTest extends Arquillian {
fakeServerInventory = new FakeServerInventory();
completeDiscoveryChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(4);
-
+
//autoimport everything
- when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
+ fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(int.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
public void waitForDiscovery() throws Exception {
completeDiscoveryChecker.waitForDiscoveryComplete();
}
-
+
@Test
@RunDiscovery
public void testConfirmInitialInventory() throws Exception {
diff --git a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
index 18d4dac..df2a286 100644
--- a/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
+++ b/modules/core/client-api/src/main/java/org/rhq/core/clientapi/server/discovery/DiscoveryServerService.java
@@ -34,6 +34,7 @@ import org.rhq.core.communications.command.annotation.Timeout;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.ResourceMeasurementScheduleRequest;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -69,6 +70,11 @@ public interface DiscoveryServerService {
MergeInventoryReportResults mergeInventoryReport(InventoryReport inventoryReport)
throws InvalidInventoryReportException, StaleTypeException;
+ @LimitedConcurrency(CONCURRENCY_LIMIT_INVENTORY_REPORT)
+ @Timeout(0L)
+ // should be something like 1000L * 60 * 30 but until we can be assured we never take longer, disable timeout
+ ResourceSyncInfo getResourceSyncInfo(int resourceId);
+
/**
* Merges a new availability report from the agent into the server. This updates the availability statuses of known
* resources.
@@ -96,7 +102,7 @@ public interface DiscoveryServerService {
Set<Resource> getResources(Set<Integer> resourceIds, boolean includeDescendants);
/**
- * Returns the Resources with the given id's. The children are not set.
+ * Returns the Resources with the given id's. The children are not set.
*
* @param resourceIds
* @return a list of resources in the same order as the passed in ids, with the latest data
@@ -107,7 +113,7 @@ public interface DiscoveryServerService {
/**
* Set the specified resource enabled or disabled. The call has no effect if the resource is already
* in the desired state.
- *
+ *
* @param resourceId The resource to enable or disable.
* @param setEnabled Enable if true, disable if false.
*/
@@ -165,7 +171,7 @@ public interface DiscoveryServerService {
* Upgrades the data of the resources according to the provided reports.
* The server is free to ignore or modify the requests and will provide the
* true changes made to the resources on the server-side in the result of this method.
- *
+ *
* @param upgradeRequests contains the information about the upgrade of individual resources.
* @return details on what resources have been upgraded with what data.
*/
@@ -174,7 +180,7 @@ public interface DiscoveryServerService {
/**
* Gives the server a chance to apply any necessary post-processing that's needed for newly committed resources
* that have been successfully synchronized on the agent.
- *
+ *
* @param resourceIds a collection of{@link Resource} ids that have been newly committed and successfully
* synchronized on the agent
*
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/MergeInventoryReportResults.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/MergeInventoryReportResults.java
index 95a59cf..615f37e 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/MergeInventoryReportResults.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/MergeInventoryReportResults.java
@@ -38,11 +38,11 @@ import org.rhq.core.domain.resource.ResourceType;
public class MergeInventoryReportResults implements Serializable {
private static final long serialVersionUID = 1L;
- private final ResourceSyncInfo resourceSyncInfo;
+ private final PlatformSyncInfo platformSyncInfo;
private final Collection<ResourceTypeFlyweight> ignoredResourceTypes;
- public MergeInventoryReportResults(ResourceSyncInfo rsi, Collection<ResourceType> ignoredResourceTypes) {
- resourceSyncInfo = rsi;
+ public MergeInventoryReportResults(PlatformSyncInfo psi, Collection<ResourceType> ignoredResourceTypes) {
+ platformSyncInfo = psi;
if (ignoredResourceTypes == null || ignoredResourceTypes.isEmpty()) {
this.ignoredResourceTypes = null;
@@ -54,8 +54,8 @@ public class MergeInventoryReportResults implements Serializable {
}
}
- public ResourceSyncInfo getResourceSyncInfo() {
- return resourceSyncInfo;
+ public PlatformSyncInfo getPlatformSyncInfo() {
+ return platformSyncInfo;
}
public Collection<ResourceTypeFlyweight> getIgnoredResourceTypes() {
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java
new file mode 100644
index 0000000..951f542
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/PlatformSyncInfo.java
@@ -0,0 +1,74 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.rhq.core.domain.discovery;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+import org.rhq.core.domain.resource.InventoryStatus;
+import org.rhq.core.domain.resource.Resource;
+
+/**
+ * @author Ian Springer
+ * @author Jay Shaughnessy
+ */
+@Entity
+@Table(name = "RHQ_RESOURCE")
+public class PlatformSyncInfo extends SyncInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @OneToMany(mappedBy = "parentResource", fetch = FetchType.EAGER)
+ private Set<Resource> topLevelServers;
+
+ // JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
+ public PlatformSyncInfo() {
+ }
+
+ public Collection<Resource> getTopLevelServers() {
+ return topLevelServers;
+ }
+
+ // for testing
+ public static PlatformSyncInfo buildPlatformSyncInfo(Resource platform) {
+ Set<Resource> toplevelServers = platform.getChildResources();
+
+ PlatformSyncInfo syncInfo = new PlatformSyncInfo(platform.getId(), platform.getUuid(), platform.getMtime(),
+ platform.getInventoryStatus(), (null == toplevelServers ? Collections.EMPTY_SET : toplevelServers));
+
+ return syncInfo;
+ }
+
+ // for testing
+ private PlatformSyncInfo(int id, String uuid, long mtime, InventoryStatus istatus, Set<Resource> children) {
+ super(id, uuid, mtime, istatus);
+ this.topLevelServers = children;
+ }
+
+}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
index b0a5315..e9cb222 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/ResourceSyncInfo.java
@@ -28,14 +28,7 @@ import java.util.HashSet;
import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.EnumType;
-import javax.persistence.Enumerated;
import javax.persistence.FetchType;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.JoinColumn;
-import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@@ -43,68 +36,31 @@ import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
/**
+ * Sync info for any non-platform resource.
+ *
* @author Ian Springer
+ * @author Jay Shaughnessy
*/
@Entity
@Table(name = "RHQ_RESOURCE")
-public class ResourceSyncInfo implements Serializable {
+public class ResourceSyncInfo extends SyncInfo implements Serializable {
private static final long serialVersionUID = 1L;
- /**
- * Server-assigned id
- */
- @Column(name = "ID", nullable = false)
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private int id;
-
- /**
- * Agent-assigned uuid
- */
- @Column(name = "UUID")
- private String uuid;
-
- /**
- * Last modified time
- */
- @Column(name = "MTIME")
- private long mtime;
-
- @Column(name = "INVENTORY_STATUS")
- @Enumerated(EnumType.STRING)
- private InventoryStatus inventoryStatus;
-
- @JoinColumn(name = "PARENT_RESOURCE_ID", nullable = true)
- @ManyToOne(fetch = FetchType.LAZY, optional = true)
- private ResourceSyncInfo parent;
-
- @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
+ @Column(name = "PARENT_RESOURCE_ID")
+ private Integer parentId;
+
+ @OneToMany(mappedBy = "parentId", fetch = FetchType.EAGER)
private Collection<ResourceSyncInfo> childSyncInfos;
// JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
public ResourceSyncInfo() {
}
- public int getId() {
- return id;
- }
-
- public String getUuid() {
- return uuid;
- }
-
- public long getMtime() {
- return mtime;
- }
-
- public InventoryStatus getInventoryStatus() {
- return inventoryStatus;
- }
-
public Collection<ResourceSyncInfo> getChildSyncInfos() {
return childSyncInfos;
}
+ // for testing
public static ResourceSyncInfo buildResourceSyncInfo(Resource resource) {
Collection<ResourceSyncInfo> children;
@@ -117,18 +73,35 @@ public class ResourceSyncInfo implements Serializable {
children = new HashSet<ResourceSyncInfo>(0);
}
+ return buildResourceSyncInfo(resource, children);
+ }
+
+ // for testing
+ public static ResourceSyncInfo buildResourceSyncInfo(Resource resource, Collection<ResourceSyncInfo> children) {
+
ResourceSyncInfo syncInfo = new ResourceSyncInfo(resource.getId(), resource.getUuid(), resource.getMtime(),
resource.getInventoryStatus(), children);
return syncInfo;
}
+
+ public static ResourceSyncInfo buildResourceSyncInfo(SyncInfo syncInfo) {
+
+ return buildResourceSyncInfo(syncInfo, ((Collection<ResourceSyncInfo>) null));
+ }
+
+ public static ResourceSyncInfo buildResourceSyncInfo(SyncInfo syncInfo, Collection<ResourceSyncInfo> children) {
+
+ ResourceSyncInfo resourceSyncInfo = new ResourceSyncInfo(syncInfo.getId(), syncInfo.getUuid(),
+ syncInfo.getMtime(), syncInfo.getInventoryStatus(), children);
+
+ return resourceSyncInfo;
+ }
+
private ResourceSyncInfo(int id, String uuid, long mtime, InventoryStatus istatus,
Collection<ResourceSyncInfo> children) {
- this.id = id;
- this.uuid = uuid;
- this.mtime = mtime;
- this.inventoryStatus = istatus;
+ super(id, uuid, mtime, istatus);
this.childSyncInfos = children;
}
}
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java
new file mode 100644
index 0000000..370d84f
--- /dev/null
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/discovery/SyncInfo.java
@@ -0,0 +1,98 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation, and/or the GNU Lesser
+ * General Public License, version 2.1, also as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License and the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * and the GNU Lesser General Public License along with this program;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.rhq.core.domain.discovery;
+
+import java.io.Serializable;
+
+import javax.persistence.Column;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+import org.rhq.core.domain.resource.InventoryStatus;
+
+/**
+ * This is the abstract base class for SyncInfo, which may be for a platform or a [top level server] resource.
+ *
+ * @author Ian Springer
+ * @author Jay Shaughnessy
+ */
+@MappedSuperclass
+public abstract class SyncInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Server-assigned id
+ */
+ @Column(name = "ID", nullable = false)
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+
+ /**
+ * Agent-assigned uuid
+ */
+ @Column(name = "UUID")
+ private String uuid;
+
+ /**
+ * Last modified time
+ */
+ @Column(name = "MTIME")
+ private long mtime;
+
+ @Column(name = "INVENTORY_STATUS")
+ @Enumerated(EnumType.STRING)
+ private InventoryStatus inventoryStatus;
+
+ // JPA requires public or protected no-param constructor; Externalizable requires public no-param constructor.
+ public SyncInfo() {
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getUuid() {
+ return uuid;
+ }
+
+ public long getMtime() {
+ return mtime;
+ }
+
+ public InventoryStatus getInventoryStatus() {
+ return inventoryStatus;
+ }
+
+ protected SyncInfo(int id, String uuid, long mtime, InventoryStatus istatus) {
+ this.id = id;
+ this.uuid = uuid;
+ this.mtime = mtime;
+ this.inventoryStatus = istatus;
+ }
+
+}
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/avail/AvailTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/avail/AvailTest.java
index 86cf746..80130ac 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/avail/AvailTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/avail/AvailTest.java
@@ -108,6 +108,8 @@ public class AvailTest extends Arquillian {
// autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
@@ -118,7 +120,7 @@ public class AvailTest extends Arquillian {
}
@BeforeMethod
- public void beforeMethod() throws Exception {
+ protected void beforeMethod() throws Exception {
System.out.println("\n!!!!!!!!!!!!!!!!!!!!!!!!!! BEFORE METHOD (" + Thread.currentThread().getName() + ")");
scrub();
}
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
index 6533b21..4591dfc 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/AbstractIgnoreTypesInventoryManagerBaseTest.java
@@ -40,6 +40,7 @@ import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -82,7 +83,7 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
protected Resource platform;
- protected HashMap<String, Resource> simulatedInventory; // key == UUID
+ protected HashMap<Integer, Resource> simulatedInventory; // key == resourceId == UUID.hashcode()
protected HashSet<ResourceType> ignoredTypes;
@@ -101,7 +102,7 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
@BeforeDiscovery
public void resetServerServices() throws Exception {
platform = null;
- simulatedInventory = new HashMap<String, Resource>();
+ simulatedInventory = new HashMap<Integer, Resource>();
gotIgnoredTypeFromAgent = new CountDownLatch(1); // will be open once we know agent discovered types we want ignored
initializeIgnoredTypes();
@@ -110,6 +111,8 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
mergeInventoryReport());
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ getResourceSyncInfo());
}
@AfterDiscovery
@@ -131,23 +134,26 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
if (platform != null) {
platform.getChildResources().clear();
if (simulatedInventory != null) {
- simulatedInventory.put(platform.getUuid(), platform);
+ simulatedInventory.put(platform.getId(), platform);
}
}
}
protected void waitForInventory(int depth) throws Exception {
long start = System.currentTimeMillis();
- while (getInventoryDepth(platform) < depth) {
- Thread.sleep(1000);
- if (System.currentTimeMillis() - start > 30000L) {
- break; // this should never take longer than 30s
+ int inventoryDepth = getInventoryDepth(platform);
+ while (inventoryDepth < depth) {
+ if (System.currentTimeMillis() - start > 60000L) {
+ assert false : "Failed to get proper depth, depth is currently at=" + inventoryDepth;
}
+ Thread.sleep(1000);
+ inventoryDepth = getInventoryDepth(platform);
}
return;
}
protected int getInventoryDepth(Resource root) {
+ System.out.println("Inventory depth chart: " + root);
if (root == null) {
return 0;
}
@@ -172,27 +178,31 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
}
protected MergeInventoryReportResults simulateInventoryReportServerProcessing(InventoryReport inventoryReport) {
- ResourceSyncInfo syncInfo = null;
+ PlatformSyncInfo syncInfo = null;
if (inventoryReport.getAddedRoots() != null && !inventoryReport.getAddedRoots().isEmpty()) {
for (Resource res : inventoryReport.getAddedRoots()) {
persistInSimulatedInventory(res);
}
- syncInfo = ResourceSyncInfo.buildResourceSyncInfo(platform);
+ syncInfo = PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- return new MergeInventoryReportResults(syncInfo, ignoredTypes);
+
+ MergeInventoryReportResults result = new MergeInventoryReportResults(syncInfo, ignoredTypes);
+ return result;
}
protected void persistInSimulatedInventory(Resource res) {
if (!ignoredTypes.contains(res.getResourceType())) {
- if (!simulatedInventory.containsKey(res.getUuid())) {
+ if (!simulatedInventory.containsKey(res.getUuid().hashCode())) {
Resource persisted = new Resource(res.getResourceKey(), res.getName(), res.getResourceType());
+ persisted.setId(res.getUuid().hashCode());
persisted.setUuid(res.getUuid());
persisted.setInventoryStatus(InventoryStatus.COMMITTED);
- simulatedInventory.put(persisted.getUuid(), persisted);
+ // System.out.println("***** PERSISTED:\n" + persisted.getUuid() + ", " + persisted.getName() + "\n****");
+ simulatedInventory.put(persisted.getUuid().hashCode(), persisted);
if (res.getParentResource() == Resource.ROOT) {
platform = persisted;
} else {
- Resource parent = simulatedInventory.get(res.getParentResource().getUuid());
+ Resource parent = simulatedInventory.get(res.getParentResource().getUuid().hashCode());
if (parent != null) {
parent.addChildResource(persisted);
}
@@ -207,6 +217,17 @@ public abstract class AbstractIgnoreTypesInventoryManagerBaseTest extends Arquil
return;
}
+ protected Answer<ResourceSyncInfo> getResourceSyncInfo() {
+ return new Answer<ResourceSyncInfo>() {
+ @Override
+ public ResourceSyncInfo answer(InvocationOnMock invocation) throws Throwable {
+ Integer resourceId = (Integer) invocation.getArguments()[0];
+ ResourceSyncInfo result = ResourceSyncInfo.buildResourceSyncInfo(simulatedInventory.get(resourceId));
+ return result;
+ }
+ };
+ }
+
protected void validateFullInventory() {
System.out.println("Validating full inventory...");
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/CommitThenIgnoreTypesInventoryManagerTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/CommitThenIgnoreTypesInventoryManagerTest.java
index 65c9ab7..8965ea1 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/CommitThenIgnoreTypesInventoryManagerTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/CommitThenIgnoreTypesInventoryManagerTest.java
@@ -44,6 +44,7 @@ public class CommitThenIgnoreTypesInventoryManagerTest extends AbstractIgnoreTyp
@RunDiscovery
public void testIgnoreTypesAfterFullCommit() throws Exception {
// make sure the agent inventory has a full inventory
+ waitForInventory(5);
validateFullInventory();
// simulate the ignoring of types
@@ -59,8 +60,6 @@ public class CommitThenIgnoreTypesInventoryManagerTest extends AbstractIgnoreTyp
System.out.println("Executing full discovery...");
InventoryReport report = inventoryManager.executeServerScanImmediately();
inventoryManager.handleReport(report);
- report = inventoryManager.executeServiceScanImmediately();
- inventoryManager.handleReport(report);
waitForInventory(3);
validatePartiallyIgnoredInventory();
}
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackAbortTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackAbortTest.java
index 648971e..867e1fa 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackAbortTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackAbortTest.java
@@ -18,11 +18,20 @@
*/
package org.rhq.core.pc.inventory;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
+
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -31,24 +40,13 @@ import org.rhq.core.pc.PluginContainerConfiguration;
import org.rhq.core.pc.inventory.discoverycallback.DiscoveryCallbackAbortCallback1;
import org.rhq.core.pc.inventory.discoverycallback.DiscoveryCallbackAbortCallback2;
import org.rhq.core.pc.inventory.discoverycallback.DiscoveryCallbackAbortDiscoveryComponent;
-import org.rhq.core.pc.inventory.discoverycallback.PluginOneCallback;
-import org.rhq.core.pc.inventory.discoverycallback.PluginTwoCallback1;
-import org.rhq.core.pc.inventory.discoverycallback.PluginTwoCallback2;
import org.rhq.core.pc.inventory.testplugin.TestResourceComponent;
-import org.rhq.core.pc.inventory.testplugin.TestResourceDiscoveryComponent;
import org.rhq.test.arquillian.AfterDiscovery;
import org.rhq.test.arquillian.BeforeDiscovery;
import org.rhq.test.arquillian.FakeServerInventory;
import org.rhq.test.arquillian.MockingServerServices;
import org.rhq.test.arquillian.RunDiscovery;
import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import java.util.Set;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
/**
* A unit test for testing discovery callbacks.
@@ -86,6 +84,8 @@ public class DiscoveryCallbackAbortTest extends Arquillian {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(2);
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
@@ -101,7 +101,7 @@ public class DiscoveryCallbackAbortTest extends Arquillian {
// make sure our inventory is as we expect it to be
validatePluginContainerInventory();
}
-
+
private void validatePluginContainerInventory() throws Exception {
System.out.println("Validating PC inventory...");
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackTest.java
index 4b4b985..0c2cb9c 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackTest.java
@@ -18,11 +18,20 @@
*/
package org.rhq.core.pc.inventory;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
+
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -39,13 +48,6 @@ import org.rhq.test.arquillian.FakeServerInventory;
import org.rhq.test.arquillian.MockingServerServices;
import org.rhq.test.arquillian.RunDiscovery;
import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import java.util.Set;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
/**
* A unit test for testing discovery callbacks.
@@ -86,6 +88,8 @@ public class DiscoveryCallbackTest extends Arquillian {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(2);
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
@@ -101,7 +105,7 @@ public class DiscoveryCallbackTest extends Arquillian {
// make sure our inventory is as we expect it to be
validatePluginContainerInventory();
}
-
+
private void validatePluginContainerInventory() throws Exception {
System.out.println("Validating PC inventory...");
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackVetoTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackVetoTest.java
index 67ad6f7..845e655 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackVetoTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryCallbackVetoTest.java
@@ -18,11 +18,20 @@
*/
package org.rhq.core.pc.inventory;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.TargetsContainer;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
+
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -38,13 +47,6 @@ import org.rhq.test.arquillian.FakeServerInventory;
import org.rhq.test.arquillian.MockingServerServices;
import org.rhq.test.arquillian.RunDiscovery;
import org.rhq.test.shrinkwrap.RhqAgentPluginArchive;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import java.util.Set;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.when;
/**
* A unit test for testing discovery callbacks and their veto feature.
@@ -82,6 +84,8 @@ public class DiscoveryCallbackVetoTest extends Arquillian {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(2);
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
@@ -97,7 +101,7 @@ public class DiscoveryCallbackVetoTest extends Arquillian {
// make sure our inventory is as we expect it to be
validatePluginContainerInventory();
}
-
+
private void validatePluginContainerInventory() throws Exception {
System.out.println("Validating PC inventory...");
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryTest.java
index 8880b92..592c235 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/DiscoveryTest.java
@@ -97,6 +97,8 @@ public class DiscoveryTest extends Arquillian {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(4);
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
@@ -187,7 +189,7 @@ public class DiscoveryTest extends Arquillian {
response.getResourceId(),
"Operation subsystem isn't aware of the correct resource id for manual add resource");
}
-
+
private void validatePluginContainerInventory() throws Exception {
System.out.println("Validating PC inventory...");
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/IgnoreTypesInventoryManagerTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/IgnoreTypesInventoryManagerTest.java
index fc9b610..7495075 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/IgnoreTypesInventoryManagerTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/IgnoreTypesInventoryManagerTest.java
@@ -44,6 +44,7 @@ public class IgnoreTypesInventoryManagerTest extends AbstractIgnoreTypesInventor
@RunDiscovery
public void testIgnoreTypes() throws Exception {
// make sure the agent inventory does not have any resources of the ignored types
+ waitForInventory(3);
validatePartiallyIgnoredInventory();
// simulate the unignoring of all types (i.e. don't ignore any types anymore)
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/InventoryManagerTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/InventoryManagerTest.java
index dcacd01..236d1ce 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/InventoryManagerTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/inventory/InventoryManagerTest.java
@@ -85,6 +85,8 @@ public class InventoryManagerTest extends Arquillian {
discoveryCompleteChecker = fakeServerInventory.createAsyncDiscoveryCompletionChecker(2);
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
}
@AfterDiscovery
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/LateMeasurementRescheduleTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/LateMeasurementRescheduleTest.java
index 301d613..159112a 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/LateMeasurementRescheduleTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/LateMeasurementRescheduleTest.java
@@ -76,6 +76,8 @@ public class LateMeasurementRescheduleTest extends Arquillian {
// autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
// set up the metric schedules using the metric metadata to determine default intervals and enablement
when(serverServices.getDiscoveryServerService().postProcessNewlyCommittedResources(any(Set.class))).then(
@@ -119,7 +121,7 @@ public class LateMeasurementRescheduleTest extends Arquillian {
// ** metric2 - starting at time 90 (completes at time 91)
// ** metric2 - starting at time 105 (completes at time 105)
// ** metric1 - starting at time [121..150] (completes at time 90 + 30 + [1..30])
- //
+ //
// Metric 1 is late because it was supposed to start at t60 but instead came up for eval at t=90. It is then
// rescheduled by our fix for (currentTime=t90 + delay=30s + randomInterval=[1..30] based on the 30s Interval).
// And you can see above, that is when the next request to collect metric1 is done.
diff --git a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
index 4747851..081d601 100644
--- a/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
+++ b/modules/core/plugin-container-itest/src/test/java/org/rhq/core/pc/measurement/ReadOnlyScheduleSetTest.java
@@ -75,6 +75,8 @@ public class ReadOnlyScheduleSetTest extends Arquillian {
// autoimport everything
when(serverServices.getDiscoveryServerService().mergeInventoryReport(any(InventoryReport.class))).then(
fakeServerInventory.mergeInventoryReport(InventoryStatus.COMMITTED));
+ when(serverServices.getDiscoveryServerService().getResourceSyncInfo(any(Integer.class))).then(
+ fakeServerInventory.getResourceSyncInfo());
// set up the metric schedules using the metric metadata to determine default intervals and enablement
when(serverServices.getDiscoveryServerService().postProcessNewlyCommittedResources(any(Set.class))).then(
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
index 380e91b..33cf21a 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/StandaloneContainer.java
@@ -50,6 +50,7 @@ import org.rhq.core.domain.configuration.definition.PropertyDefinition;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementDefinition;
@@ -902,6 +903,11 @@ public class StandaloneContainer {
}
@Override
+ public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
+ return null;
+ }
+
+ @Override
public Set<Resource> getResources(Set<Integer> resourceIds, boolean includeDescendants) {
return null;
}
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index 35ba2af..b809f6f 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -19,6 +19,9 @@
package org.rhq.core.pc.inventory;
+import gnu.trove.map.TIntObjectMap;
+import gnu.trove.map.hash.TIntObjectHashMap;
+
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
@@ -39,20 +42,17 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-import gnu.trove.map.TIntObjectMap;
-import gnu.trove.map.hash.TIntObjectHashMap;
-
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.clientapi.agent.PluginContainerException;
import org.rhq.core.clientapi.agent.configuration.ConfigurationUtility;
import org.rhq.core.clientapi.agent.discovery.DiscoveryAgentService;
@@ -70,6 +70,7 @@ import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeInventoryReportResults.ResourceTypeFlyweight;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.AvailabilityType;
@@ -197,12 +198,14 @@ public class InventoryManager extends AgentService implements ContainerService,
/**
* UUID to ResourceContainer map
*/
- private final Map<String, ResourceContainer> resourceContainersByUUID = new ConcurrentHashMap<String, ResourceContainer>(500);
+ private final Map<String, ResourceContainer> resourceContainersByUUID = new ConcurrentHashMap<String, ResourceContainer>(
+ 500);
/**
* ResourceID to ResourceContainer map
*/
- private final TIntObjectMap<ResourceContainer> resourceContainerByResourceId = new TIntObjectHashMap<ResourceContainer>(500);
+ private final TIntObjectMap<ResourceContainer> resourceContainerByResourceId = new TIntObjectHashMap<ResourceContainer>(
+ 500);
/**
* Collection of event listeners to inform of changes to the inventory.
@@ -353,7 +356,8 @@ public class InventoryManager extends AgentService implements ContainerService,
}
// find the discovery callbacks defined, if there are none, just return the results as-is
- Map<String, List<String>> callbacks = this.pluginManager.getMetadataManager().getDiscoveryCallbacks(context.getResourceType());
+ Map<String, List<String>> callbacks = this.pluginManager.getMetadataManager().getDiscoveryCallbacks(
+ context.getResourceType());
if (callbacks == null || callbacks.isEmpty()) {
return results;
}
@@ -364,7 +368,7 @@ public class InventoryManager extends AgentService implements ContainerService,
PluginComponentFactory pluginComponentFactory = PluginContainer.getInstance().getPluginComponentFactory();
ClassLoader originalContextClassLoader = Thread.currentThread().getContextClassLoader();
- for (Iterator<DiscoveredResourceDetails> detailsIterator = results.iterator(); detailsIterator.hasNext(); ) {
+ for (Iterator<DiscoveredResourceDetails> detailsIterator = results.iterator(); detailsIterator.hasNext();) {
DiscoveredResourceDetails details = detailsIterator.next();
int callbackCount = 0;
boolean stopProcessing = false; // if true, a callback told us he found a details that he modified and we should stop
@@ -374,38 +378,39 @@ public class InventoryManager extends AgentService implements ContainerService,
String pluginName = entry.getKey();
List<String> callbackClassNames = entry.getValue();
for (String className : callbackClassNames) {
- ResourceDiscoveryCallback callback = pluginComponentFactory.getDiscoveryCallback(pluginName, className);
+ ResourceDiscoveryCallback callback = pluginComponentFactory.getDiscoveryCallback(pluginName,
+ className);
try {
Thread.currentThread().setContextClassLoader(callback.getClass().getClassLoader());
- callbackResults= callback.discoveredResources(details);// inline in our calling thread - no time outs or anything; hopefully the plugin plays nice
+ callbackResults = callback.discoveredResources(details);// inline in our calling thread - no time outs or anything; hopefully the plugin plays nice
callbackCount++;
if (log.isDebugEnabled()) {
log.debug("Discovery callback [{" + pluginName + "}" + className + "] returned ["
- + callbackResults + "] #invocations=" + callbackCount);
+ + callbackResults + "] #invocations=" + callbackCount);
}
switch (callbackResults) {
- case PROCESSED: {
- if (stopProcessing) {
- abortDiscovery = true;
- log.warn("Another discovery callback [{" + pluginName + "}" + className
- + "] processed details [" + details
- + "]. This is not allowed. Discovery will be aborted for that resource");
- } else {
- stopProcessing = true;
- }
- break;
- }
- case VETO: {
- vetoDiscovery = true;
- log.warn("Discovery callback [{" + pluginName + "}" + className
- + "] vetoed resource [" + details
- + "]. Discovery will be skipped for that resource and it will not be inventoried.");
- break;
- }
- default: {
- // callback left the details unprocessed, nothing to do.
- break;
+ case PROCESSED: {
+ if (stopProcessing) {
+ abortDiscovery = true;
+ log.warn("Another discovery callback [{" + pluginName + "}" + className
+ + "] processed details [" + details
+ + "]. This is not allowed. Discovery will be aborted for that resource");
+ } else {
+ stopProcessing = true;
}
+ break;
+ }
+ case VETO: {
+ vetoDiscovery = true;
+ log.warn("Discovery callback [{" + pluginName + "}" + className + "] vetoed resource ["
+ + details
+ + "]. Discovery will be skipped for that resource and it will not be inventoried.");
+ break;
+ }
+ default: {
+ // callback left the details unprocessed, nothing to do.
+ break;
+ }
}
// note that we keep going, even if we set stopProcessing is true - this is because we
// want to keep calling callbacks and check if they, too, think they can identify the details. If
@@ -680,8 +685,7 @@ public class InventoryManager extends AgentService implements ContainerService,
@NotNull
public InventoryReport executeServiceScanImmediately(Resource resource) {
- RuntimeDiscoveryExecutor discoveryExecutor = new RuntimeDiscoveryExecutor(this, this.configuration,
- resource);
+ RuntimeDiscoveryExecutor discoveryExecutor = new RuntimeDiscoveryExecutor(this, this.configuration, resource);
return submit(discoveryExecutor);
}
@@ -746,7 +750,7 @@ public class InventoryManager extends AgentService implements ContainerService,
try {
//make sure we have the full version of the resource
ResourceContainer container = getResourceContainer(resource.getId());
- if (container == null) {
+ if (container == null) {
//don't bother doing anything
return new AvailabilityReport(changesOnly, getAgent().getName());
}
@@ -1125,7 +1129,7 @@ public class InventoryManager extends AgentService implements ContainerService,
return true;
}
- ResourceSyncInfo syncInfo;
+ PlatformSyncInfo platformSyncInfo;
Collection<ResourceTypeFlyweight> ignoredTypes;
try {
String reportType = (report.isRuntimeReport()) ? "runtime" : "server";
@@ -1135,10 +1139,10 @@ public class InventoryManager extends AgentService implements ContainerService,
.getDiscoveryServerService();
MergeInventoryReportResults results = discoveryServerService.mergeInventoryReport(report);
if (results != null) {
- syncInfo = results.getResourceSyncInfo();
+ platformSyncInfo = results.getPlatformSyncInfo();
ignoredTypes = results.getIgnoredResourceTypes();
} else {
- syncInfo = null;
+ platformSyncInfo = null;
ignoredTypes = null;
}
if (log.isDebugEnabled()) {
@@ -1183,8 +1187,8 @@ public class InventoryManager extends AgentService implements ContainerService,
//Another (rare) scenario where this would happen would be when the platform resource type
//would change.
//In either case, let's sync up with the server - if it's got nothing, neither should the agent.
- if (syncInfo != null) {
- synchInventory(syncInfo);
+ if (platformSyncInfo != null) {
+ syncPlatform(platformSyncInfo);
} else {
purgeObsoleteResources(Collections.<String> emptySet());
@@ -1196,24 +1200,86 @@ public class InventoryManager extends AgentService implements ContainerService,
}
/**
- * Performs a sync so that resources passed in are reflected in the agent's inventory.
- * This assumes the resource sync infos passed in represent the full inventory tree.
+ * Performs a full platform sync so that resources passed in are reflected in the agent's inventory.
*
- * @param syncInfo information on all resources in the entire inventory tree
+ * @param platformSyncInfo sync info on the platform and references to the top level servers
*/
- private void synchInventory(ResourceSyncInfo syncInfo) {
- synchInventory(syncInfo, false);
+ private void syncPlatform(PlatformSyncInfo platformSyncInfo) {
+ final Set<String> allServerSideUuids = new HashSet<String>();
+ boolean hadSyncedResources = false;
+
+ // always sync the platform because it does not get included in the top level server sync
+ allServerSideUuids.add(platformSyncInfo.getUuid());
+ ResourceSyncInfo platformResourceSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platformSyncInfo);
+ log.info("Sync Starting: Platform [" + platformResourceSyncInfo.getId() + "]");
+ hadSyncedResources = syncResource(platformResourceSyncInfo) || hadSyncedResources;
+ log.info("Sync Complete: Platform [" + platformResourceSyncInfo.getId() + "]. Local inventory changed: ["
+ + hadSyncedResources + "]");
+
+ // then sync the top level servers by calling back to the server for the sync info for each. We
+ // do this one at a time to avoid forcing the whole inventory into active memory at one time during the sync.
+ Collection<Resource> topLevelServers = platformSyncInfo.getTopLevelServers();
+ if (null != topLevelServers) {
+ DiscoveryServerService service = configuration.getServerServices().getDiscoveryServerService();
+
+ for (Resource topLevelServer : topLevelServers) {
+ ResourceSyncInfo topLevelServerSyncInfo = service.getResourceSyncInfo(topLevelServer.getId());
+ if (null != topLevelServerSyncInfo) {
+ //topLevelServerSyncInfo = ResourceSyncInfo.buildResourceSyncInfo(platformSyncInfo,
+ // topLevelServerSyncInfo);
+ getAllUuids(topLevelServerSyncInfo, allServerSideUuids);
+ log.info("Sync Starting: Top Level Server [" + topLevelServerSyncInfo.getId() + "]");
+ hadSyncedResources = syncResource(topLevelServerSyncInfo) || hadSyncedResources;
+ log.info("Sync Complete: Top Level Server [" + topLevelServerSyncInfo.getId()
+ + "] Local inventory changed: [" + hadSyncedResources + "]");
+ }
+ }
+ }
+
+ purgeObsoleteResources(allServerSideUuids);
+
+ // If we synced any Resources, one or more Resource components were probably started, request a
+ // full avail report to make sure their availabilities are determined on the next avail run (typically
+ // < 30s away). A full avail report will ensure an initial avail check is performed for a resource.
+ //
+ // Also kick off a service scan to scan those Resources for new child Resources. Kick both tasks off
+ // asynchronously.
+ //
+ // Do this only if we are finished with resource upgrade because no availability checks
+ // or discoveries can happen during upgrade. This is to ensure maximum consistency of the
+ // inventory with the server side as well as to disallow any other server-agent traffic during
+ // the upgrade phase. Not to mention the fact that no thread pools are initialized yet by the
+ // time the upgrade kicks in..
+ if (hadSyncedResources && !isResourceUpgradeActive()) {
+
+ // TODO: If someday this is undesirable for scalability reasons, we could probably instead call
+ // requestAvailabilityCheck on each unknown or modified resource.
+ requestFullAvailabilityReport();
+
+ this.inventoryThreadPoolExecutor.schedule((Callable<? extends Object>) this.serviceScanExecutor,
+ configuration.getChildResourceDiscoveryDelay(), TimeUnit.SECONDS);
+ }
+ }
+
+ private void getAllUuids(ResourceSyncInfo syncInfo, Set<String> allServerSideUuids) {
+ allServerSideUuids.add(syncInfo.getUuid());
+
+ if (null != syncInfo.getChildSyncInfos()) {
+ for (ResourceSyncInfo child : syncInfo.getChildSyncInfos()) {
+ getAllUuids(child, allServerSideUuids);
+ }
+ }
}
/**
- * Performs a sync so that resources passed in are reflected in the agent's inventory.
+ * Performs a synch on only the single resource and its descendants. This is assumed to be a partial
+ * inventory. To synch on the full inventory call {@link #syncPlatform(PlatformSyncInfo)}
*
* @param syncInfo the resources' sync data
- * @param partialInventory if true, syncInfo represents only a partial inventory.
- * if false, syncInfo represents the full inventory tree of all resources
+ * @return true if any resources needed synchronization, false otherwise
*/
- private void synchInventory(ResourceSyncInfo syncInfo, boolean partialInventory) {
- log.info("Syncing local inventory with Server inventory...");
+ private boolean syncResource(ResourceSyncInfo syncInfo) {
+ boolean result = false;
final long startTime = System.currentTimeMillis();
final Set<Resource> syncedResources = new LinkedHashSet<Resource>();
final Set<ResourceSyncInfo> unknownResourceSyncInfos = new LinkedHashSet<ResourceSyncInfo>();
@@ -1221,20 +1287,14 @@ public class InventoryManager extends AgentService implements ContainerService,
final Set<Integer> deletedResourceIds = new LinkedHashSet<Integer>();
final Set<Resource> newlyCommittedResources = new LinkedHashSet<Resource>();
final Set<Resource> ignoredResources = new LinkedHashSet<Resource>();
- final Set<String> allServerSideUuids = new HashSet<String>();
// rhq-980 Adding agent-side logging to report any unexpected synch failure.
try {
- // don't bother doing this if we are processing a partial inventory.
- // allServerSideUuids is only ever used to purge obsolete resources, but we don't
- // do that for partial inventories, so we don't need to prepare that collection for partials.
- if (!partialInventory) {
- getAllUuids(syncInfo, allServerSideUuids);
- }
-
log.debug("Processing Server sync info...");
+
processSyncInfo(syncInfo, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
deletedResourceIds, newlyCommittedResources, ignoredResources);
+
if (log.isDebugEnabled()) {
log.debug(String.format("DONE Processing sync info: [%d] ms: synced [%d] resources: "
+ "[%d] unknown, [%d] modified, [%d] deleted, [%d] newly committed",
@@ -1245,9 +1305,7 @@ public class InventoryManager extends AgentService implements ContainerService,
mergeUnknownResources(unknownResourceSyncInfos);
mergeModifiedResources(modifiedResourceIds);
purgeIgnoredResources(ignoredResources);
- if (!partialInventory) {
- purgeObsoleteResources(allServerSideUuids);
- }
+
postProcessNewlyCommittedResources(newlyCommittedResources);
if (log.isDebugEnabled()) {
if (!deletedResourceIds.isEmpty()) {
@@ -1257,41 +1315,26 @@ public class InventoryManager extends AgentService implements ContainerService,
(System.currentTimeMillis() - startTime)));
}
- // If we synced any Resources, one or more Resource components were probably started, request a
- // full avail report to make sure their availabilities are determined on the next avail run (typically
- // < 30s away). A full avail report will ensure an initial avail check is performed for a resource.
- //
- // Also kick off a service scan to scan those Resources for new child Resources. Kick both tasks off
- // asynchronously.
- //
- // Do this only if we are finished with resource upgrade because no availability checks
- // or discoveries can happen during upgrade. This is to ensure maximum consistency of the
- // inventory with the server side as well as to disallow any other server-agent traffic during
- // the upgrade phase. Not to mention the fact that no thread pools are initialized yet by the
- // time the upgrade kicks in..
- if (!isResourceUpgradeActive()
- && (!syncedResources.isEmpty() || !unknownResourceSyncInfos.isEmpty() || !modifiedResourceIds.isEmpty())) {
-
- // TODO: If someday this is undesirable for scalability reasons, we could probably instead call
- // requestAvailabilityCheck on each unknown or modified resource.
- requestFullAvailabilityReport();
+ result = !(syncedResources.isEmpty() && unknownResourceSyncInfos.isEmpty() && modifiedResourceIds.isEmpty());
- this.inventoryThreadPoolExecutor.schedule((Callable<? extends Object>) this.serviceScanExecutor,
- configuration.getChildResourceDiscoveryDelay(), TimeUnit.SECONDS);
- }
} catch (Throwable t) {
log.warn("Failed to synchronize local inventory with Server inventory for Resource [" + syncInfo.getId()
+ "] and its descendants: " + t.getMessage());
// convert to runtime exception so as not to change the api
throw new RuntimeException(t);
}
+
+ return result;
}
- private void getAllUuids(ResourceSyncInfo syncInfo, Set<String> allServerSideUuids) {
- allServerSideUuids.add(syncInfo.getUuid());
- for (ResourceSyncInfo child : syncInfo.getChildSyncInfos()) {
- getAllUuids(child, allServerSideUuids);
- }
+ public void synchronizeInventory(ResourceSyncInfo resourceSyncInfo) {
+ log.info("Synchronizing local inventory with Server inventory for Resource [" + resourceSyncInfo.getId()
+ + "] and its descendants...");
+
+ // Get the latest resource data rooted at the given id.
+ syncResource(resourceSyncInfo); // this method assumes we only get a single resource and its children (BZ 887411)
+ performServiceScan(resourceSyncInfo.getId()); // NOTE: This will block (the initial scan blocks).
+ // TODO: (jshaughn) should we also request a full avail scan?
}
/**
@@ -1420,8 +1463,7 @@ public class InventoryManager extends AgentService implements ContainerService,
log.debug("Asked to remove an unknown Resource [" + resource + "] with UUID [" + resource.getUuid()
+ "]");
}
- }
- else {
+ } else {
this.resourceContainerByResourceId.remove(resource.getId());
}
@@ -1965,8 +2007,8 @@ public class InventoryManager extends AgentService implements ContainerService,
getEventContext(resource), // for event access
getOperationContext(resource), // for operation manager access
getContentContext(resource), // for content manager access
- getAvailabilityContext(resource),
- getInventoryContext(resource), this.configuration.getPluginContainerDeployment(), // helps components make determinations of what to do
+ getAvailabilityContext(resource), getInventoryContext(resource),
+ this.configuration.getPluginContainerDeployment(), // helps components make determinations of what to do
new ComponentInvocationContextImpl());
}
@@ -2162,8 +2204,8 @@ public class InventoryManager extends AgentService implements ContainerService,
this.resourceContainerByResourceId.put(resource.getId(), resourceContainer);
}
- log.info("Inventory with size [" + this.resourceContainersByUUID.size() + "] loaded from data file in ["
- + (System.currentTimeMillis() - start) + "ms]");
+ log.info("Inventory with size [" + this.resourceContainersByUUID.size()
+ + "] loaded from data file in [" + (System.currentTimeMillis() - start) + "ms]");
}
} catch (Exception e) {
this.platform = null;
@@ -2346,15 +2388,6 @@ public class InventoryManager extends AgentService implements ContainerService,
return platform;
}
- public void synchronizeInventory(ResourceSyncInfo syncInfo) {
- log.info("Synchronizing local inventory with Server inventory for Resource [" + syncInfo.getId()
- + "] and its descendants...");
-
- // Get the latest resource data rooted at the given id.
- synchInventory(syncInfo, true); // this method assumes we only get a single resource and its children (BZ 887411)
- performServiceScan(syncInfo.getId()); // NOTE: This will block (the initial scan blocks).
- }
-
/**
* This method is called for a resource tree that exists in the server inventory but
* not in the agent's inventory.
@@ -2944,9 +2977,11 @@ public class InventoryManager extends AgentService implements ContainerService,
}
// Recurse...
- for (ResourceSyncInfo childSyncInfo : syncInfo.getChildSyncInfos()) {
- processSyncInfo(childSyncInfo, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
- deletedResourceIds, newlyCommittedResources, ignoredResources);
+ if (null != syncInfo.getChildSyncInfos()) {
+ for (ResourceSyncInfo childSyncInfo : syncInfo.getChildSyncInfos()) {
+ processSyncInfo(childSyncInfo, syncedResources, unknownResourceSyncInfos, modifiedResourceIds,
+ deletedResourceIds, newlyCommittedResources, ignoredResources);
+ }
}
}
}
@@ -3151,8 +3186,10 @@ public class InventoryManager extends AgentService implements ContainerService,
while (!queue.isEmpty()) {
ResourceSyncInfo node = queue.remove();
result.add(node.getId());
- for (ResourceSyncInfo child : node.getChildSyncInfos()) {
- queue.add(child);
+ if (null != node.getChildSyncInfos()) {
+ for (ResourceSyncInfo child : node.getChildSyncInfos()) {
+ queue.add(child);
+ }
}
}
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
index 5265977..d9431d0 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/RuntimeDiscoveryExecutor.java
@@ -216,7 +216,7 @@ public class RuntimeDiscoveryExecutor implements Runnable, Callable<InventoryRep
AvailabilityType currentAvailabilityType = (null == currentAvailability) ? AvailabilityType.DOWN
: currentAvailability.getAvailabilityType();
- // If there is no current avail, or this is a SERVER, we must perfom the live check.
+ // If there is no current avail, or this is a SERVER, we must perform the live check.
if (AvailabilityType.UP != currentAvailabilityType
|| ResourceCategory.SERVER == parentContainer.getResource().getResourceType().getCategory()) {
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
index 3f2bbb9..5a18d65 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
@@ -19,8 +19,6 @@
package org.rhq.core.pc.upgrade;
-import static org.testng.Assert.fail;
-
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,6 +39,7 @@ import org.rhq.core.clientapi.agent.upgrade.ResourceUpgradeRequest;
import org.rhq.core.clientapi.agent.upgrade.ResourceUpgradeResponse;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
@@ -120,7 +119,26 @@ public class FakeServerInventory {
platform = persisted;
}
}
- return new MergeInventoryReportResults(getSyncInfo(), null);
+ return new MergeInventoryReportResults(getPlatformSyncInfo(), null);
+ }
+ }
+ };
+ }
+
+ public synchronized CustomAction getResourceSyncInfo() {
+ return new CustomAction("getResourceSyncInfo") {
+ public Object invoke(Invocation invocation) throws Throwable {
+ synchronized (FakeServerInventory.this) {
+ throwIfFailing();
+
+ Integer resourceId = (Integer) invocation.getParameter(0);
+
+ for (Resource c : platform.getChildResources()) {
+ if (c.getId() == resourceId) {
+ return getResourceSyncInfo(c);
+ }
+ }
+ return null;
}
}
};
@@ -134,7 +152,7 @@ public class FakeServerInventory {
platform = null;
- return new MergeInventoryReportResults(getSyncInfo(), null);
+ return new MergeInventoryReportResults(getPlatformSyncInfo(), null);
}
}
};
@@ -394,57 +412,51 @@ public class FakeServerInventory {
return persisted;
}
- private ResourceSyncInfo getSyncInfo() {
- return platform != null ? convert(platform) : null;
- }
-
private void throwIfFailing() {
if (failing) {
throw new RuntimeException("Fake server inventory is in the failing mode.");
}
}
+ private PlatformSyncInfo getPlatformSyncInfo() {
+ return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
+ }
+
+ private ResourceSyncInfo getResourceSyncInfo(Resource resource) {
+ return resource == null ? null : convert(resource);
+ }
+
private static ResourceSyncInfo convert(Resource root) {
- return convertInternal(root, new HashMap<String, ResourceSyncInfo>());
+ return convertInternal(root, true, new HashMap<String, ResourceSyncInfo>());
}
- private static ResourceSyncInfo convertInternal(Resource root, Map<String, ResourceSyncInfo> intermediateResults) {
+ private static ResourceSyncInfo convertInternal(Resource root, boolean isTopLevelServer,
+ Map<String, ResourceSyncInfo> intermediateResults) {
+
ResourceSyncInfo ret = intermediateResults.get(root.getUuid());
if (ret != null) {
return ret;
}
-
try {
- ret = new ResourceSyncInfo();
-
+ ret = ResourceSyncInfo.buildResourceSyncInfo(root);
intermediateResults.put(root.getUuid(), ret);
- Class<ResourceSyncInfo> clazz = ResourceSyncInfo.class;
-
- getPrivateField(clazz, "id").set(ret, root.getId());
- getPrivateField(clazz, "uuid").set(ret, root.getUuid());
- getPrivateField(clazz, "mtime").set(ret, root.getMtime());
- getPrivateField(clazz, "inventoryStatus").set(ret, root.getInventoryStatus());
-
- ResourceSyncInfo parent = root.getParentResource() == null ? null : convertInternal(
- root.getParentResource(), intermediateResults);
-
- getPrivateField(clazz, "parent").set(ret, parent);
+ Integer parentId = root.getParentResource() == null ? null : root.getParentResource().getId();
+ getPrivateField(ResourceSyncInfo.class, "parentId").set(ret, parentId);
Set<ResourceSyncInfo> children = new LinkedHashSet<ResourceSyncInfo>();
for (Resource child : root.getChildResources()) {
- ResourceSyncInfo syncChild = convertInternal(child, intermediateResults);
+ ResourceSyncInfo syncChild = convertInternal(child, false, intermediateResults);
children.add(syncChild);
}
-
- getPrivateField(clazz, "childSyncInfos").set(ret, children);
-
+ getPrivateField(ResourceSyncInfo.class, "childSyncInfos").set(ret, children);
return ret;
+
} catch (Exception e) {
- fail("Failed to convert resource " + root + " to a ResourceSyncInfo. This should not happen.", e);
- return null;
+ throw new IllegalStateException("Failed to convert resource " + root
+ + " to a ResourceSyncInfo. This should not happen.", e);
}
}
diff --git a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
index e09db85..9cf9f98 100644
--- a/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
+++ b/modules/enterprise/server/itests-2/src/test/java/org/rhq/enterprise/server/discovery/DiscoveryBossBeanTest.java
@@ -67,6 +67,7 @@ import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeInventoryReportResults.ResourceTypeFlyweight;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.CreateResourceHistory;
@@ -190,8 +191,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
MergeInventoryReportResults results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
- ResourceSyncInfo syncInfo = results.getResourceSyncInfo();
- assert syncInfo != null;
+ assertNotNull(results.getPlatformSyncInfo());
}
@Test(groups = "integration.ejb3")
@@ -204,7 +204,8 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
MergeInventoryReportResults results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
- ResourceSyncInfo syncInfo = results.getResourceSyncInfo();
+ assertNotNull(results.getPlatformSyncInfo());
+ ResourceSyncInfo syncInfo = discoveryBoss.getResourceSyncInfo(results.getPlatformSyncInfo().getId());
assert syncInfo != null;
platform.setId(syncInfo.getId());
@@ -227,8 +228,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
- syncInfo = results.getResourceSyncInfo();
- assert syncInfo != null;
+ assertNotNull(results.getPlatformSyncInfo());
}
@Test(groups = "integration.ejb3")
@@ -250,7 +250,8 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
MergeInventoryReportResults results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert results != null;
assert results.getIgnoredResourceTypes() == null : "nothing should have been ignored in this test";
- ResourceSyncInfo syncInfo = results.getResourceSyncInfo();
+ assertNotNull(results.getPlatformSyncInfo());
+ ResourceSyncInfo syncInfo = discoveryBoss.getResourceSyncInfo(results.getPlatformSyncInfo().getId());
assert syncInfo != null;
ResourceSyncInfo serverSyncInfo = syncInfo.getChildSyncInfos().iterator().next();
@@ -290,18 +291,18 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert mergeResults != null;
assert mergeResults.getIgnoredResourceTypes() == null : "nothing should have been ignored: "
+ mergeResults.getIgnoredResourceTypes();
- ResourceSyncInfo platformSyncInfo = mergeResults.getResourceSyncInfo();
+ PlatformSyncInfo platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
// Check merge result
assertEquals(InventoryStatus.NEW, platformSyncInfo.getInventoryStatus());
- assertEquals(platform.getChildResources().size(), platformSyncInfo.getChildSyncInfos().size());
+ assertEquals(platform.getChildResources().size(), platformSyncInfo.getTopLevelServers().size());
// Collect the resource ids generated for the platform and the servers
int platformId = platformSyncInfo.getId();
List<Integer> serverIds = new LinkedList<Integer>();
- for (ResourceSyncInfo serverSyncInfo : platformSyncInfo.getChildSyncInfos()) {
+ for (Resource serverSyncInfo : platformSyncInfo.getTopLevelServers()) {
serverIds.add(serverSyncInfo.getId());
}
int[] arrayOfServerIds = ArrayUtils.unwrapCollection(serverIds);
@@ -360,7 +361,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Merge this inventory report
MergeInventoryReportResults mergeResults = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert mergeResults != null;
- ResourceSyncInfo platformSyncInfo = mergeResults.getResourceSyncInfo();
+ PlatformSyncInfo platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
assertNotNull(mergeResults.getIgnoredResourceTypes());
assertEquals(mergeResults.getIgnoredResourceTypes().size(), 1);
@@ -368,7 +369,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Check merge result - make sure we should not see any children under the platform (it should have been ignored)
assertEquals(InventoryStatus.NEW, platformSyncInfo.getInventoryStatus());
- assertEquals(platformSyncInfo.getChildSyncInfos().size(), 0);
+ assertEquals(platformSyncInfo.getTopLevelServers().size(), 0);
}
@Test(groups = "integration.ejb3")
@@ -392,7 +393,7 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Merge this inventory report
MergeInventoryReportResults mergeResults = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert mergeResults != null;
- ResourceSyncInfo platformSyncInfo = mergeResults.getResourceSyncInfo();
+ PlatformSyncInfo platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
// now see that we were told about the service types being ignored (even though we had no resources of that type in the report)
@@ -423,13 +424,13 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert mergeResults != null;
assert mergeResults.getIgnoredResourceTypes() == null : "nothing should have been ignored: "
+ mergeResults.getIgnoredResourceTypes();
- ResourceSyncInfo platformSyncInfo = mergeResults.getResourceSyncInfo();
+ PlatformSyncInfo platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
// Collect the resource ids generated for the platform and the servers
int platformId = platformSyncInfo.getId();
List<Integer> serverIds = new LinkedList<Integer>();
- for (ResourceSyncInfo serverSyncInfo : platformSyncInfo.getChildSyncInfos()) {
+ for (Resource serverSyncInfo : platformSyncInfo.getTopLevelServers()) {
serverIds.add(serverSyncInfo.getId());
}
int[] arrayOfServerIds = ArrayUtils.unwrapCollection(serverIds);
@@ -462,14 +463,14 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
// Merge the inventory report again to simulate another discovery - the servers should be ignored now
mergeResults = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assert mergeResults != null;
- platformSyncInfo = mergeResults.getResourceSyncInfo();
+ platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
assertNotNull(mergeResults.getIgnoredResourceTypes());
assertEquals(mergeResults.getIgnoredResourceTypes().size(), 1);
assert mergeResults.getIgnoredResourceTypes().contains(new ResourceTypeFlyweight(serverType));
assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getInventoryStatus()); // notice platform is committed now
- assertEquals(platformSyncInfo.getChildSyncInfos().size(), 0); // notice there are no server children now
+ assertEquals(platformSyncInfo.getTopLevelServers().size(), 0); // notice there are no server children now
}
@Test(groups = "integration.ejb3")
@@ -496,15 +497,15 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
assert mergeResults != null;
assert mergeResults.getIgnoredResourceTypes() == null : "nothing should have been ignored: "
+ mergeResults.getIgnoredResourceTypes();
- ResourceSyncInfo platformSyncInfo = mergeResults.getResourceSyncInfo();
+ PlatformSyncInfo platformSyncInfo = mergeResults.getPlatformSyncInfo();
assert platformSyncInfo != null;
// Check merge result
assertEquals(InventoryStatus.COMMITTED, platformSyncInfo.getInventoryStatus());
- assertEquals(storagePlatform.getChildResources().size(), platformSyncInfo.getChildSyncInfos().size());
+ assertEquals(storagePlatform.getChildResources().size(), platformSyncInfo.getTopLevelServers().size());
storageNode = resourceManager.getResourceById(subjectManager.getOverlord(), platformSyncInfo
- .getChildSyncInfos().iterator().next().getId());
+ .getTopLevelServers().iterator().next().getId());
assertNotNull(storageNode);
assertEquals(InventoryStatus.COMMITTED, storageNode.getInventoryStatus());
}
@@ -526,13 +527,13 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
MergeInventoryReportResults results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assertNotNull(results);
assertNull("nothing should have been ignored in this test", results.getIgnoredResourceTypes());
- final ResourceSyncInfo firstDiscoverySyncInfo = results.getResourceSyncInfo();
+ final PlatformSyncInfo firstDiscoverySyncInfo = results.getPlatformSyncInfo();
assertNotNull(firstDiscoverySyncInfo);
// Then simulate a create resource request
final String userSuppliedResourceName = prefix("User Supplied Resource Name");
final String newResourceKey = prefix("Created Resource Key");
- ResourceSyncInfo serverSyncInfo = firstDiscoverySyncInfo.getChildSyncInfos().iterator().next();
+ Resource serverSyncInfo = firstDiscoverySyncInfo.getTopLevelServers().iterator().next();
final int serverResourceId = serverSyncInfo.getId();
executeInTransaction(false, new TransactionCallback() {
@@ -562,13 +563,14 @@ public class DiscoveryBossBeanTest extends AbstractEJB3Test {
results = discoveryBoss.mergeInventoryReport(serialize(inventoryReport));
assertNotNull(results);
assertNull("nothing should have been ignored in this test", results.getIgnoredResourceTypes());
- ResourceSyncInfo secondDiscoverySyncInfo = results.getResourceSyncInfo();
+ PlatformSyncInfo secondDiscoverySyncInfo = results.getPlatformSyncInfo();
assertNotNull(secondDiscoverySyncInfo);
// Check that the resource ends with the user supplied name in inventory
- serverSyncInfo = secondDiscoverySyncInfo.getChildSyncInfos().iterator().next();
- ResourceSyncInfo service1SyncInfo = serverSyncInfo.getChildSyncInfos().iterator().next();
+ ResourceSyncInfo topLevelServerSyncInfo = discoveryBoss.getResourceSyncInfo(secondDiscoverySyncInfo
+ .getTopLevelServers().iterator().next().getId());
+ ResourceSyncInfo service1SyncInfo = topLevelServerSyncInfo.getChildSyncInfos().iterator().next();
Resource service1Resource = getEntityManager().find(Resource.class, service1SyncInfo.getId());
assertEquals(userSuppliedResourceName, service1Resource.getName());
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
index d7e57bd..71e2e3f 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossBean.java
@@ -72,6 +72,7 @@ import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.criteria.ResourceTypeCriteria;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Agent;
@@ -208,7 +209,7 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
Set<Resource> roots = report.getAddedRoots();
LOG.debug(report);
- final Map<String, ResourceType> allTypes = new HashMap<String, ResourceType>();
+ Map<String, ResourceType> allTypes = new HashMap<String, ResourceType>();
for (Resource root : roots) {
// Make sure all platform, server, and service types are valid. Also, make sure they're fetched - otherwise
@@ -231,7 +232,7 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
}
}
- allTypes.clear(); // help GC, we don't need this anymore
+ allTypes = null; // maybe help GC? we don't need this anymore
// Prepare the ResourceSyncInfo tree which contains all the info the PC needs to sync itself up with us.
// The platform can be null in only one scenario.. a brand new agent has connected to the server
@@ -239,7 +240,7 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
// the current inventory on the server side. But at this point there isn't any since that very
// agent just registered and is starting up for the very first time and therefore hasn't had
// a chance yet to send us its full inventory report.
- ResourceSyncInfo syncInfo = discoveryBoss.getResourceSyncInfo(knownAgent);
+ PlatformSyncInfo syncInfo = discoveryBoss.getPlatformSyncInfo(knownAgent);
// we need to also tell the agent if there were any ignored types - we must provide the agent with
// ALL types that are ignored, not just for those resources that were in the report
@@ -264,13 +265,21 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
}
@Override
- public ResourceSyncInfo getResourceSyncInfo(Agent knownAgent) {
+ public PlatformSyncInfo getPlatformSyncInfo(Agent knownAgent) {
Resource platform = resourceManager.getPlatform(knownAgent);
if (null == platform) {
return null;
}
- ResourceSyncInfo result = entityManager.find(ResourceSyncInfo.class, platform.getId());
+ PlatformSyncInfo result = entityManager.find(PlatformSyncInfo.class, platform.getId());
+ return result;
+ }
+
+ @Override
+ public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
+ // [PERF] this is expensive, it let's hibernate grab the whole hierarchy via eager fetch of children.
+ ResourceSyncInfo result = entityManager.find(ResourceSyncInfo.class, resourceId);
+
return result;
}
@@ -1235,7 +1244,8 @@ public class DiscoveryBossBean implements DiscoveryBossLocal, DiscoveryBossRemot
// Add a product version entry for the new resource.
if ((resource.getVersion() != null) && (resource.getVersion().length() > 0)) {
- ProductVersion productVersion = productVersionManager.addProductVersion(resourceType, resource.getVersion());
+ ProductVersion productVersion = productVersionManager
+ .addProductVersion(resourceType, resource.getVersion());
resource.setProductVersion(productVersion);
}
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
index a05d2b4..7ed9445 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryBossLocal.java
@@ -37,6 +37,7 @@ import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.PlatformSyncInfo;
import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.InventoryStatus;
@@ -57,9 +58,9 @@ public interface DiscoveryBossLocal extends DiscoveryBossRemote {
*
* @param report the inventory report to be merged
*
- * @return the server's response, which will include the true IDs for new resources that were found.
- * This can return null in one specific case - if this is a brand new agent and it is currently initializing
- * for the very first time.
+ * @return the server's response, which will include the information necessary for the agent to
+ * start synchronizing its inventory with the server's inventory. This can return null in one specific
+ * case - if this is a brand new agent and it is currently initializing for the very first time.
* @throws InvalidInventoryReportException if the inventory report is invalid
*/
MergeInventoryReportResults mergeInventoryReport(InventoryReport report) throws InvalidInventoryReportException;
@@ -77,10 +78,18 @@ public interface DiscoveryBossLocal extends DiscoveryBossRemote {
throws InvalidInventoryReportException;
/**
+ * Just get the top level server info for the agent's platform. Then, each top level server
+ * can be individually synced
* @param knownAgent the agent for the platform we want to sync with
* @return null if platform not found
*/
- ResourceSyncInfo getResourceSyncInfo(Agent knownAgent);
+ PlatformSyncInfo getPlatformSyncInfo(Agent knownAgent);
+
+ /**
+ * @param resourceid the root resourceId on which we want to sync
+ * @return null if resource not found, otherwise the entire tree rooted at the specified resource
+ */
+ ResourceSyncInfo getResourceSyncInfo(int resourceId);
/**
* Returns a map of platforms (the keys) and their servers (the values) that are in the auto-discovery queue but not
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
index d7ff2b3..533948b 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/discovery/DiscoveryServerServiceImpl.java
@@ -39,6 +39,7 @@ import org.rhq.core.domain.criteria.ResourceCriteria;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeResourceResponse;
+import org.rhq.core.domain.discovery.ResourceSyncInfo;
import org.rhq.core.domain.measurement.ResourceMeasurementScheduleRequest;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.InventoryStatus;
@@ -91,7 +92,7 @@ public class DiscoveryServerServiceImpl implements DiscoveryServerService {
if (log.isDebugEnabled()) {
log.error("Received invalid inventory report from agent [" + agent + "]", e);
} else {
- /*
+ /*
* this is expected when the platform is uninventoried, because the agent often has in-flight reports
* going to the server at the time the platform's agent is being deleted from the database
*/
@@ -120,6 +121,26 @@ public class DiscoveryServerServiceImpl implements DiscoveryServerService {
}
@Override
+ public ResourceSyncInfo getResourceSyncInfo(int resourceId) {
+ long start = System.currentTimeMillis();
+ DiscoveryBossLocal discoveryBoss = LookupUtil.getDiscoveryBoss();
+ ResourceSyncInfo results;
+
+ results = discoveryBoss.getResourceSyncInfo(resourceId);
+
+ long elapsed = (System.currentTimeMillis() - start);
+ if (elapsed > 30000L) {
+ log.warn("Performance: get resource sync info (" + elapsed + ")ms");
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Performance: get resource sync info (" + elapsed + ")ms");
+ }
+ }
+
+ return results;
+ }
+
+ @Override
public boolean mergeAvailabilityReport(AvailabilityReport availabilityReport) {
AvailabilityReportSerializer.getSingleton().lock(availabilityReport.getAgentName());
try {
diff --git a/modules/integration-tests/apache-plugin-test/src/test/java/org/rhq/plugins/apache/setup/ApacheTestSetup.java b/modules/integration-tests/apache-plugin-test/src/test/java/org/rhq/plugins/apache/setup/ApacheTestSetup.java
index 908f7d7..f317b3f 100644
--- a/modules/integration-tests/apache-plugin-test/src/test/java/org/rhq/plugins/apache/setup/ApacheTestSetup.java
+++ b/modules/integration-tests/apache-plugin-test/src/test/java/org/rhq/plugins/apache/setup/ApacheTestSetup.java
@@ -45,6 +45,7 @@ import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pc.ServerServices;
import org.rhq.core.pc.upgrade.FakeServerInventory;
import org.rhq.core.system.SystemInfoFactory;
+import org.rhq.core.util.TokenReplacingReader;
import org.rhq.core.util.file.FileUtil;
import org.rhq.plugins.apache.ApacheServerComponent;
import org.rhq.plugins.apache.ApacheServerDiscoveryComponent;
@@ -60,7 +61,6 @@ import org.rhq.plugins.apache.util.HttpdAddressUtility;
import org.rhq.plugins.apache.util.ResourceTypes;
import org.rhq.plugins.apache.util.VHostSpec;
import org.rhq.test.ObjectCollectionSerializer;
-import org.rhq.core.util.TokenReplacingReader;
import org.rhq.test.pc.PluginContainerTest;
public class ApacheTestSetup {
@@ -247,7 +247,7 @@ public class ApacheTestSetup {
public ApacheTestSetup withDefaultExpectations() throws Exception {
context.checking(new Expectations() {
{
- addDefaultExceptations(this);
+ addDefaultExpectations(this);
}
});
@@ -255,7 +255,7 @@ public class ApacheTestSetup {
}
@SuppressWarnings("unchecked")
- public void addDefaultExceptations(Expectations expectations) throws Exception {
+ public void addDefaultExpectations(Expectations expectations) throws Exception {
ServerServices ss = PluginContainerTest.getCurrentPluginContainerConfiguration().getServerServices();
//only import the apache servers we actually care about - we can't assume another apache won't be present
@@ -276,6 +276,10 @@ public class ApacheTestSetup {
}
}));
+ expectations.allowing(ss.getDiscoveryServerService()).getResourceSyncInfo(
+ expectations.with(Expectations.any(Integer.class)));
+ expectations.will(fakeInventory.getResourceSyncInfo());
+
expectations.allowing(ss.getDiscoveryServerService()).upgradeResources(
expectations.with(Expectations.any(Set.class)));
expectations.will(fakeInventory.upgradeResources());
10 years, 4 months
[rhq] Branch 'jay-sync' - 29 commits - .classpath modules/core modules/enterprise modules/helpers modules/plugins pom.xml
by Jay Shaughnessy
.classpath | 1
modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java | 4
modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java | 37
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java | 9
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java | 2
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/D3GroupGraphListView.java | 1
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/ResourceGroupDetailView.java | 3
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupTableView.java | 408
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupView.java | 193
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupViewDataSource.java | 314
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricAvailabilityView.java | 223
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsGridFieldName.java | 63
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java | 3
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java | 30
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java | 56
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java | 224
modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/util/rpc/TrackingRequestCallback.java | 6
modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/CoreGUI.gwt.xml | 2
modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_de.properties | 601
modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_ja.properties | 210
modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.js | 9293 ++++++++++
modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.min.js | 5
modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.js | 8768 ---------
modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.min.js | 5
modules/enterprise/remoting/cli/pom.xml | 19
modules/enterprise/remoting/cli/src/etc/Remoting_Setup.txt | 43
modules/enterprise/remoting/cli/src/etc/build.xml | 80
modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.bat | 16
modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.sh | 18
modules/enterprise/remoting/cli/src/etc/rhq-cli-env.bat | 32
modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh | 40
modules/enterprise/remoting/cli/src/etc/rhq-cli.bat | 51
modules/enterprise/remoting/cli/src/etc/rhq-cli.sh | 104
modules/enterprise/remoting/cli/src/etc/toRun.txt | 3
modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProvider.java | 33
modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js | 375
modules/enterprise/remoting/cli/src/main/samples/modules/util.js | 30
modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml | 2
modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java | 67
modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh | 5
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/ResourceManagerBean.java | 2
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java | 12
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java | 39
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java | 36
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java | 112
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java | 33
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java | 27
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java | 55
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java | 6
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java | 55
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java | 35
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java | 55
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java | 67
modules/enterprise/server/server-metrics/pom.xml | 4
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/AbortedException.java | 23
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java | 37
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java | 92
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/SignalingCountDownLatch.java | 34
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate1HourData.java | 127
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate6HourData.java | 84
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateIndexEntriesHandler.java | 73
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateRawData.java | 133
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregationState.java | 257
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregator.java | 378
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute1HourData.java | 113
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute24HourData.java | 99
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute6HourData.java | 106
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/AggregationTests.java | 498
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java | 41
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java | 26
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsPerfTests.java | 191
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java | 38
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsTest.java | 138
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForRawInserts.java | 51
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForWrite.java | 48
modules/enterprise/server/server-metrics/src/test/resources/log4j.xml | 2
modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/MeasurementAggregator.java | 7
modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java | 41
modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlan.java | 40
modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlanner.java | 3
modules/plugins/database/pom.xml | 64
modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java | 36
modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java | 226
modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java | 52
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java | 65
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java | 84
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java | 84
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java | 14
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java | 7
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java | 379
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java | 16
modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java | 103
modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java | 39
modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java | 19
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java | 82
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java | 20
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java | 59
modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java | 17
modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java | 88
modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml | 94
modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/ApplicationServerPluginConfigurationProperties.java | 2
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/BaseComponent.java | 49
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DeploymentComponent.java | 46
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DomainDeploymentComponent.java | 21
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java | 17
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ManagedASComponent.java | 19
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/MemoryPoolComponent.java | 88
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterConfigurationDiscovery.java | 11
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterContextComponent.java | 21
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/SubsystemDiscovery.java | 4
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/VHostComponent.java | 8
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/WebRuntimeComponent.java | 29
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/helper/ServerPluginConfiguration.java | 11
modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml | 6
modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java | 2
modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheDiscoveryComponent.java | 10
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java | 131
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java | 92
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java | 124
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java | 182
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java | 79
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java | 117
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java | 36
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java | 61
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java | 126
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java | 72
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java | 103
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java | 62
modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java | 6
modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java | 9
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java | 147
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java | 65
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java | 35
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java | 40
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java | 19
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java | 71
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java | 96
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java | 40
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java | 40
modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml | 8
modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java | 19
modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java | 28
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java | 312
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java | 18
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java | 95
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java | 7
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java | 62
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java | 258
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java | 98
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java | 61
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java | 100
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java | 28
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java | 15
modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java | 10
modules/plugins/virt/src/main/java/org/rhq/plugins/virt/VirtualizationHostDiscoveryComponent.java | 9
pom.xml | 4
156 files changed, 18144 insertions(+), 11230 deletions(-)
New commits:
commit 3498c4eb990be7416a2fa79942ade306773e63bb
Author: Jay Shaughnessy <jshaughn(a)redhat.com>
Date: Fri Jan 3 16:05:16 2014 -0500
Some tweaks to test code
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
index 2bcb7aa..2cca059 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/core/pc/upgrade/FakeServerInventory.java
@@ -421,10 +421,6 @@ public class FakeServerInventory {
return platform == null ? null : PlatformSyncInfo.buildPlatformSyncInfo(platform);
}
- private Collection<ResourceSyncInfo> getResourceSyncInfo(Resource resource) {
- return resource == null ? null : convert(resource);
- }
-
private static Collection<ResourceSyncInfo> convert(Resource root) {
Set<ResourceSyncInfo> result = new HashSet<ResourceSyncInfo>();
convertInternal(root, result);
diff --git a/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java b/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
index c9528a0..5beb7a5 100644
--- a/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
+++ b/modules/core/plugin-container/src/test/java/org/rhq/test/pc/PluginContainerTest.java
@@ -60,7 +60,7 @@ import org.rhq.test.JMockTest;
* <p>
* This class is used to declaratively setup a plugin container a test wants
* to use using the {@link PluginContainerSetup} annotation on a test method or class.
- *
+ *
* @author Lukas Krejci
*/
public class PluginContainerTest extends JMockTest {
@@ -96,9 +96,9 @@ public class PluginContainerTest extends JMockTest {
* provided by JMock.
* <p>
* This method together with {@link #setServerSideFake(String, Object)} provides a generic
- * "storage" for tests to share these objects and is only provided as a convenience to
+ * "storage" for tests to share these objects and is only provided as a convenience to
* the test writers. There's nothing that would manadate using it.
- *
+ *
* @param name
* @return
*/
@@ -114,7 +114,7 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * Returns the {@link PluginContainerConfiguration} as configured using
+ * Returns the {@link PluginContainerConfiguration} as configured using
* the {@link PluginContainerSetup} annotation on the current test (or null
* if no such thing is configured).
* @return
@@ -202,7 +202,7 @@ public class PluginContainerTest extends JMockTest {
* <p>
* This is not done automatically to support sharing the plugin container among
* multiple tests to simulate upgrades, etc.
- *
+ *
* @throws IOException
*/
public static void clearStorageOfCurrentPluginContainer() throws IOException {
@@ -219,9 +219,9 @@ public class PluginContainerTest extends JMockTest {
/**
* This method clears the storage of all tests made. This is useful in {@link AfterSuite}
- * method to clean up after all the tests (annotated with {@link PluginContainerSetup})
+ * method to clean up after all the tests (annotated with {@link PluginContainerSetup})
* that have been run.
- *
+ *
* @throws IOException
*/
public synchronized static void clearStorage() throws IOException {
@@ -238,9 +238,9 @@ public class PluginContainerTest extends JMockTest {
* this method is provided to automatically clean up after all the plugin container tests that ran
* in the test suite.
* <p>
- * If you use PluginContainerTest as a listener, you have to call {@link #clearStorage()} method
+ * If you use PluginContainerTest as a listener, you have to call {@link #clearStorage()} method
* on your own.
- *
+ *
* @throws IOException
*/
@AfterSuite
@@ -249,12 +249,12 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * This method returns the {@link PluginContainerConfiguration} that will be used in
- * the current test as was configured by the PluginContainerSetup annotation.
+ * This method returns the {@link PluginContainerConfiguration} that will be used in
+ * the current test as was configured by the PluginContainerSetup annotation.
* <p>
- * If your test class inherits from PluginContainerTest, you can override this method
+ * If your test class inherits from PluginContainerTest, you can override this method
* to provide custom configuration.
- *
+ *
* @param testObject the object of the current test
* @param testMethod the test method currently being executed on the test object
* @return
@@ -293,7 +293,7 @@ public class PluginContainerTest extends JMockTest {
}
/**
- * This method is called after the test to tear down resources associated with the
+ * This method is called after the test to tear down resources associated with the
* current plugin container configuration.
*/
protected void tearDownPluginContainerConfiguration() {
@@ -371,9 +371,16 @@ public class PluginContainerTest extends JMockTest {
}
}
+ // Note that on WINDOWS this does not always/usually work and therefore test failures
+ // tend to happen. For unknown reasons windows/jvm keeps a lock on the plugin jar and it
+ // fails to delete, thus allowing for multiple versions of the plugin in the same path. I've
+ // added a system out to make it more clear in the log. There was no obvious workaround.
private void deletePlugins(File deployDirectory) throws IOException {
if (deployDirectory.exists()) {
- FileUtil.purge(deployDirectory, false);
+ FileUtil.purge(deployDirectory, true);
+ }
+ if (deployDirectory.exists()) {
+ throw new IllegalStateException("Failed to clean up plugins in [" + deployDirectory.getPath() + "]");
}
}
commit cfc253df7a2a3853adebde405d15dce8246d71d2
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 11:12:18 2014 -0500
i'll assume this was a mistake to import a JUnit annotation, and it should have been testng
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
index 4060d07..8227e38 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
@@ -28,8 +28,8 @@ import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
-import org.junit.BeforeClass;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
commit fe8df7628c6ec39ed99be17b6c951a1b64e11299
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 10:50:02 2014 -0500
BZ 994250 - finish the merging/peer review for patches submitted so that rhqctl returns proper exit codes. Note that this completes the merge of the two submitted patches - see prior two commits to this one. This third commit fixes some problems with the original patches: 1. Some scripts/files are not found in bin/internal of the distro, but rather are in bin/ - so we need to avoid using getBinDir() in those cases 2. Fix the code to conform to code conventions - DEATH TO TABS! 3. Remove a constant that got resurrected (ControlCommand.RHQ_STORAGE_BASEDIR_PROP is no longer needed) 4. A couple other minor things
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index 27f8e8f..a13a598 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,7 +60,6 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
- public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
@@ -300,7 +299,7 @@ public abstract class ControlCommand {
}
protected String getStoragePid() throws IOException {
- File pidFile = getStoragePidFile();
+ File pidFile = getStoragePidFile();
if (pidFile.exists()) {
return StreamUtil.slurp(new FileReader(pidFile));
@@ -494,18 +493,18 @@ public abstract class ControlCommand {
protected boolean isStorageRunning() throws IOException {
String pid = getStoragePid();
- if(pid == null) {
- return false;
- } else if(pid != null && !isUnixPidRunning(pid)) {
- // There is a phantom pidfile
- File pidFile = getStoragePidFile();
- if(!pidFile.delete()) {
- throw new RHQControlException("Could not delete storage pidfile " + pidFile.getAbsolutePath());
- }
- return false;
- } else {
- return true;
- }
+ if (pid == null) {
+ return false;
+ } else if (pid != null && !isUnixPidRunning(pid)) {
+ // There is a phantom pidfile
+ File pidFile = getStoragePidFile();
+ if (!pidFile.delete()) {
+ throw new RHQControlException("Could not delete storage pidfile " + pidFile.getAbsolutePath());
+ }
+ return false;
+ } else {
+ return true;
+ }
}
private class NullOutputStream extends OutputStream {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 28a37de..88b085f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index 1b3d47b..ed2b2f8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ org.apache.commons.exec.CommandLine commandLine;
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 5d540a1..e2ef3a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index a3920e0..7ef66b4 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if(isStorageRunning()) {
+ if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index cc3d8c2..e80edd9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
+ executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit c34ceb3b055bbd0ec67ef71bb7c14212fffae76e
Author: burmanm <yak(a)iki.fi>
Date: Sat Dec 28 23:56:16 2013 +0200
Fix the return codes for rhq-server.sh status and clean commands.
diff --git a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
index 9856190..1b974ae 100755
--- a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
+++ b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
@@ -610,7 +610,7 @@ case "$1" in
if [ "$_SERVER_RUNNING" = "1" ]; then
echo "$_SERVER_STATUS"
echo "Please shutdown the server before cleaning."
- exit 0
+ exit 1
fi
echo "Cleaning data, tmp and log directories..."
@@ -622,6 +622,9 @@ case "$1" in
'status')
echo "$_SERVER_STATUS"
echo "$_JVM_STATUS"
+ if [ "$_SERVER_RUNNING" = "0" ] || [ "$_JVM_RUNNING" = 0 ]; then
+ exit 3
+ fi
exit 0
;;
commit 59ff38658a1fc8a54b7e6a2b937ee0fa645666fe
Author: burmanm <yak(a)iki.fi>
Date: Tue Aug 6 17:52:38 2013 +0200
Fix return codes of the rhqctl command, rebased to the 4.10 master.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index db6a0ca..27f8e8f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,6 +60,7 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
+ public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
@@ -133,6 +134,7 @@ public abstract class ControlCommand {
throw new RHQControlException("Failed to load configuration", e);
}
}
+
defaultStorageBasedir = new File(getBaseDir(), STORAGE_BASEDIR_NAME);
defaultAgentBasedir = new File(getBaseDir().getParent(), AGENT_BASEDIR_NAME);
}
@@ -143,22 +145,26 @@ public abstract class ControlCommand {
public abstract Options getOptions();
- protected abstract void exec(CommandLine commandLine);
+ protected abstract int exec(CommandLine commandLine);
- public void exec(String[] args) {
+ public int exec(String[] args) {
Options options = getOptions();
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
CommandLineParser parser = new PosixParser();
CommandLine cmdLine = parser.parse(options, args);
- exec(cmdLine);
+ rValue = exec(cmdLine);
if (rhqctlConfig != null) {
rhqctlConfig.save();
}
} catch (ParseException e) {
printUsage();
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
} catch (ConfigurationException e) {
throw new RHQControlException("Failed to update " + getRhqCtlProperties(), e);
+ } finally {
+ return rValue;
}
}
@@ -386,7 +392,6 @@ public abstract class ControlCommand {
result = new org.apache.commons.exec.CommandLine("cmd.exe");
result.addArgument("/C");
result.addArgument(scriptName.replace('/', '\\') + ".bat");
-
} else {
result = new org.apache.commons.exec.CommandLine("./" + (addShExt ? scriptName + ".sh" : scriptName));
}
@@ -456,6 +461,7 @@ public abstract class ControlCommand {
PumpStreamHandler streamHandler = new PumpStreamHandler(new NullOutputStream(), new NullOutputStream());
executor.setStreamHandler(streamHandler);
org.apache.commons.exec.CommandLine commandLine;
+
commandLine = new org.apache.commons.exec.CommandLine("kill").addArgument(pid);
executor.execute(commandLine);
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 24e4995..28a37de 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -22,6 +22,7 @@
* * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
+
package org.rhq.server.control;
import java.io.Console;
@@ -50,6 +51,19 @@ public class RHQControl {
private final Log log = LogFactory.getLog(RHQControl.class);
+ public static final int EXIT_CODE_OK = 0;
+
+ // These try to follow the LSB specification - status command
+ public static final int EXIT_CODE_STATUS_NOT_RUNNING = 3;
+ public static final int EXIT_CODE_STATUS_UNKNOWN = 4;
+
+ // These try to follow the LSB specification - other commands
+ public static final int EXIT_CODE_OPERATION_FAILED = 1;
+ public static final int EXIT_CODE_INVALID_ARGUMENT = 2;
+ public static final int EXIT_CODE_NOT_INSTALLED = 5;
+// public static final int EXIT_CODE_OPERATION_NOT_RUNNING = 7;
+
+
private Commands commands = new Commands();
public void printUsage() {
@@ -63,7 +77,8 @@ public class RHQControl {
helpFormatter.printHelp(syntax, header, commands.getOptions(), footer);
}
- public void exec(String[] args) {
+ public int exec(String[] args) {
+ int rValue = EXIT_CODE_OK;
ControlCommand command = null;
boolean undo = false;
AbortHook abortHook = new AbortHook();
@@ -71,6 +86,7 @@ public class RHQControl {
try {
if (args.length == 0) {
printUsage();
+ rValue = EXIT_CODE_INVALID_ARGUMENT;
} else {
String commandName = findCommand(commands, args);
command = commands.get(commandName);
@@ -84,10 +100,11 @@ public class RHQControl {
Runtime.getRuntime().addShutdownHook(abortHook);
// run the command
- command.exec(getCommandLine(commandName, args));
+ rValue = command.exec(getCommandLine(commandName, args));
}
} catch (UsageException e) {
printUsage();
+ rValue = EXIT_CODE_INVALID_ARGUMENT;
} catch (RHQControlException e) {
undo = true;
@@ -98,9 +115,11 @@ public class RHQControl {
} else {
log.error(rootCause.getMessage());
}
+ rValue = EXIT_CODE_OPERATION_FAILED;
} catch (Throwable t) {
undo = true;
log.error(t);
+ rValue = EXIT_CODE_OPERATION_FAILED;
} finally {
abortHook.setCommand(null);
Runtime.getRuntime().removeShutdownHook(abortHook);
@@ -116,10 +135,11 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
- return;
+ return rValue;
}
private void logWarningIfAgentRPMIsInstalled(ControlCommand command) {
@@ -303,9 +323,9 @@ public class RHQControl {
public static void main(String[] args) throws Exception {
RHQControl control = new RHQControl();
+ int rValue;
try {
- control.exec(args);
- System.exit(0);
+ rValue = control.exec(args);
} catch (RHQControlException e) {
Throwable rootCause = ThrowableUtil.getRootCause(e);
// Only show the messy stack trace if we're in debug mode. Otherwise keep it cleaner for the user...
@@ -314,8 +334,9 @@ public class RHQControl {
} else {
control.log.error("There was an unexpected error: " + rootCause.getMessage());
}
- System.exit(1);
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
+ System.exit(rValue);
}
private class AbortHook extends Thread {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index eb6cd15..1b3d47b 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -43,6 +43,7 @@ import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
@@ -55,6 +56,7 @@ import org.rhq.core.util.file.FileReverter;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -79,28 +81,31 @@ public abstract class AbstractInstall extends ControlCommand {
private static final String PREF_RHQ_AGENT_SERVER_BINDPORT = "rhq.agent.server.bind-port";
private static final String PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS = "rhq.agent.server.transport-params";
- protected void installWindowsService(File workingDir, String batFile, boolean replaceExistingService, boolean start)
+ protected int installWindowsService(File workingDir, String batFile, boolean replaceExistingService, boolean start)
throws Exception {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(workingDir);
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- executor.execute(commandLine);
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- executor.execute(commandLine);
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
if (start) {
commandLine = getCommandLine(batFile, "start");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
+ return rValue;
}
protected void validateCustomStorageDataDirectories(CommandLine commandLine, List<String> errors) {
@@ -155,6 +160,31 @@ public abstract class AbstractInstall extends ControlCommand {
}
+ protected boolean isUnixPidRunning(String pid) {
+
+ Executor executor = new DefaultExecutor();
+ executor.setWorkingDirectory(getBinDir());
+ executor.setStreamHandler(new PumpStreamHandler());
+ org.apache.commons.exec.CommandLine commandLine;
+
+ commandLine = new org.apache.commons.exec.CommandLine("/bin/kill").addArgument("-0").addArgument(pid);
+
+ try {
+ int code = executor.execute(commandLine);
+ if (code != 0) {
+ return false;
+ }
+ } catch (ExecuteException ee) {
+ if (ee.getExitValue() == 1) {
+ // return code 1 means process does not exist
+ return false;
+ }
+ } catch (IOException e) {
+ log.error("Checking for running process failed: " + e.getMessage());
+ }
+ return true;
+ }
+
protected void waitForRHQServerToInitialize() throws Exception {
try {
final long messageInterval = 30000L;
@@ -169,13 +199,13 @@ public abstract class AbstractInstall extends ControlCommand {
long totalWait = (now - timerStart);
if (totalWait < problemMessageInterval) {
- log.info("Still waiting for server to initialize...");
+ log.info("Still waiting for server to start...");
} else {
long minutes = totalWait / 60000;
log.info("It has been over ["
+ minutes
- + "] minutes - you may want to ensure your server initialization is proceeding as expected. You can check the log at ["
+ + "] minutes - you may want to ensure your server startup is proceeding as expected. You can check the log at ["
+ new File(getBaseDir(), "logs/server.log").getPath() + "].");
timerStart = now;
@@ -196,6 +226,7 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
+ @SuppressWarnings("resource")
protected boolean isRHQServerInitialized() throws IOException {
BufferedReader reader = null;
@@ -241,11 +272,13 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void updateWindowsAgentService(final File agentBasedir) throws Exception {
+ protected int updateWindowsAgentService(final File agentBasedir) throws Exception {
if (!isWindows()) {
- return;
+ return RHQControl.EXIT_CODE_OK;
}
+ int rValue = 0;
+
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -262,7 +295,7 @@ public abstract class AbstractInstall extends ControlCommand {
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
@@ -270,22 +303,26 @@ public abstract class AbstractInstall extends ControlCommand {
commandLine = getCommandLine("rhq-agent-wrapper", "remove");
try {
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
} catch (Exception e) {
// Ignore, service may not exist, script returns 1
log.debug("Failed to uninstall agent service", e);
}
commandLine = getCommandLine("rhq-agent-wrapper", "install");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
} catch (IOException e) {
log.error("An error occurred while updating the agent service: " + e.getMessage());
throw e;
}
+
+ return rValue;
}
- protected void startAgent(final File agentBasedir) throws Exception {
+ protected int startAgent(final File agentBasedir) throws Exception {
+ int rValue;
+
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -300,7 +337,7 @@ public abstract class AbstractInstall extends ControlCommand {
// For *nix, just start the server in the background, for Win, now that the service is installed, start it
commandLine = getCommandLine("rhq-agent-wrapper", "start");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
// if any errors occur after now, we need to stop the agent
addUndoTask(new ControlCommand.UndoTask("Stopping agent") {
@@ -314,9 +351,10 @@ public abstract class AbstractInstall extends ControlCommand {
log.error("An error occurred while starting the agent: " + e.getMessage());
throw e;
}
+ return rValue;
}
- protected void killAgent(File agentBasedir) throws Exception {
+ protected int killAgent(File agentBasedir) throws Exception {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -328,25 +366,32 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
+ org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+
+ int rValue = 0;
if (isWindows()) {
try {
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
- executor.execute(commandLine);
+ commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "kill");
- executor.execute(commandLine);
+ commandLine = getCommandLine("rhq-agent-wrapper", "kill");
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- protected void stopServer() throws Exception {
+ protected int stopServer() throws Exception {
File serverBinDir = getBinDir();
if (!serverBinDir.exists()) {
@@ -360,16 +405,20 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+ return rValue;
}
protected void startRHQServerForInstallation() throws IOException {
@@ -446,7 +495,7 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void runRHQServerInstaller() throws IOException {
+ protected int runRHQServerInstaller() throws IOException {
try {
log.info("Installing RHQ server");
@@ -467,10 +516,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine, new DefaultExecuteResultHandler());
+ DefaultExecuteResultHandler executeHandler = new DefaultExecuteResultHandler();
+
+ executor.execute(commandLine, executeHandler);
log.info("The server installer is running");
- } catch (Exception e) {
+ return executeHandler.getExitValue();
+ } catch (IOException e) {
log.error("An error occurred while starting the server installer: " + e.getMessage());
+ return RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
@@ -560,6 +613,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
+
int exitCode = executor.execute(commandLine);
log.info("The storage node installer has finished with an exit value of " + exitCode);
@@ -577,13 +631,14 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void installAgent(final File agentBasedir, final CommandLine commandLine) throws Exception {
+ protected int installAgent(final File agentBasedir, final CommandLine commandLine) throws Exception {
clearAgentPreferences();
- installAgent(agentBasedir);
+ int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
+ return rValue;
}
- private void installAgent(final File agentBasedir) throws IOException {
+ private int installAgent(final File agentBasedir) throws IOException {
try {
log.info("Installing RHQ agent");
@@ -612,6 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
index 4139969..6039e9d 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
@@ -32,6 +32,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -70,32 +71,38 @@ public class Console extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (commandLine.getOptions().length != 1) {
printUsage();
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
} else {
String option = commandLine.getOptions()[0].getLongOpt();
try {
if (option.equals(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- startStorageInForeground();
+ rValue = Math.max(rValue, startStorageInForeground());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else if (option.equals(SERVER_OPTION)) {
if (isServerInstalled()) {
- startServerInForeground();
+ rValue = Math.max(rValue, startServerInForeground());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else if (option.equals(AGENT_OPTION)) {
if (isAgentInstalled()) {
- startAgentInForeground();
+ rValue = Math.max(rValue, startAgentInForeground());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else {
throw new IllegalArgumentException(option + " is not a supported option");
@@ -104,32 +111,33 @@ public class Console extends ControlCommand {
throw new RHQControlException("Failed to execute console command", e);
}
}
+ return rValue;
}
- private void startStorageInForeground() throws Exception {
+ private int startStorageInForeground() throws Exception {
log.debug("Starting RHQ storage node in foreground");
File storageBinDir = new File(getStorageBasedir(), "bin");
org.apache.commons.exec.CommandLine commandLine = new org.apache.commons.exec.CommandLine(getCommandLine(false,
"cassandra", "-f"));
- Executor exeuctor = new DefaultExecutor();
- exeuctor.setWorkingDirectory(storageBinDir);
- exeuctor.setStreamHandler(new PumpStreamHandler());
- exeuctor.execute(commandLine);
+ Executor executor = new DefaultExecutor();
+ executor.setWorkingDirectory(storageBinDir);
+ executor.setStreamHandler(new PumpStreamHandler());
+ return executor.execute(commandLine);
}
- private void startServerInForeground() throws Exception {
+ private int startServerInForeground() throws Exception {
log.debug("Starting RHQ server in foreground");
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "console");
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine);
+ return executor.execute(commandLine);
}
- private void startAgentInForeground() throws Exception {
+ private int startAgentInForeground() throws Exception {
log.info("Starting RHQ agent in foreground");
File agentHomeDir = getAgentBasedir();
@@ -165,6 +173,7 @@ public class Console extends ControlCommand {
// agentThread.start();
// doneSignal.await();
// agentThread.join();
+ return RHQControl.EXIT_CODE_OK;
}
private class AgentInputStreamPipe extends Thread {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
index be76e59..3ab3f00 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
@@ -33,6 +33,7 @@ import org.apache.commons.cli.Options;
import org.rhq.core.util.file.FileReverter;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -95,10 +96,11 @@ public class Install extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
boolean start = commandLine.hasOption(START_OPTION);
boolean startedStorage = false;
boolean startedServer = false;
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
List<String> errors = validateOptions(commandLine);
@@ -107,7 +109,7 @@ public class Install extends AbstractInstall {
log.error(error);
}
log.error("Exiting due to the previous errors");
- return;
+ return RHQControl.EXIT_CODE_NOT_INSTALLED;
}
// If any failures occur, we know we need to reset rhq-server.properties.
@@ -137,10 +139,10 @@ public class Install extends AbstractInstall {
if (isWindows()) {
log.info("Ensuring the RHQ Storage Windows service exists. Ignore any CreateService failure.");
- installWindowsService(getBinDir(), "rhq-storage", false, false);
+ rValue = Math.max(rValue, installWindowsService(getBinDir(), "rhq-storage", false, false));
}
} else {
- installStorageNode(getStorageBasedir(), commandLine, false);
+ rValue = Math.max(rValue, installStorageNode(getStorageBasedir(), commandLine, false));
startStorage = start;
}
}
@@ -148,7 +150,7 @@ public class Install extends AbstractInstall {
if (startStorage || installServer) {
startedStorage = true;
Start startCommand = new Start();
- startCommand.exec(new String[] { "start", "--storage" });
+ rValue = Math.max(rValue, startCommand.exec(new String[] { "start", "--storage" }));
}
if (installServer) {
@@ -157,12 +159,12 @@ public class Install extends AbstractInstall {
if (isWindows()) {
log.info("Ensuring the RHQ Server Windows service exists. Ignore any CreateService failure.");
- installWindowsService(getBinDir(), "rhq-server", false, false);
+ rValue = Math.max(rValue, installWindowsService(getBinDir(), "rhq-server", false, false));
}
} else {
startedServer = true;
startRHQServerForInstallation();
- runRHQServerInstaller();
+ rValue = Math.max(rValue, runRHQServerInstaller());
waitForRHQServerToInitialize();
}
}
@@ -175,7 +177,7 @@ public class Install extends AbstractInstall {
if (isWindows()) {
try {
log.info("Ensuring the RHQ Agent Windows service exists. Ignore any CreateService failure.");
- installWindowsService(new File(getAgentBasedir(), "bin"), "rhq-agent-wrapper", false, false);
+ rValue = Math.max(rValue, installWindowsService(new File(getAgentBasedir(), "bin"), "rhq-agent-wrapper", false, false));
} catch (Exception e) {
// Ignore, service may already exist or be running, wrapper script returns 1
log.debug("Failed to stop agent service", e);
@@ -185,10 +187,10 @@ public class Install extends AbstractInstall {
File agentBasedir = getAgentBasedir();
installAgent(agentBasedir, commandLine);
- updateWindowsAgentService(agentBasedir);
+ rValue = Math.max(rValue, updateWindowsAgentService(agentBasedir));
if (start) {
- startAgent(agentBasedir);
+ rValue = Math.max(rValue, startAgent(agentBasedir));
}
}
}
@@ -200,13 +202,14 @@ public class Install extends AbstractInstall {
if (!start && (startedStorage || startedServer)) {
Stop stopCommand = new Stop();
if (startedServer) {
- stopCommand.exec(new String[] { "stop", "--server" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--server" }));
}
if (startedStorage) {
- stopCommand.exec(new String[] { "stop", "--storage" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--storage" }));
}
}
}
+ return rValue;
}
private List<String> validateOptions(CommandLine commandLine) {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
index cb8f43b..1de4250 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
@@ -34,6 +34,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -74,52 +75,56 @@ public class Remove extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then stop whatever is installed
if (commandLine.getOptions().length == 0) {
if (isAgentInstalled()) {
- removeAgentService();
+ rValue = Math.max(rValue, removeAgentService());
}
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to remove the service. This
// may help clean up a failed install.
- removeServerService();
+ rValue = Math.max(rValue, removeServerService());
if (isStorageInstalled()) {
- removeStorageService();
+ rValue = Math.max(rValue, removeStorageService());
}
} else {
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- removeAgentService();
+ rValue = Math.max(rValue, removeAgentService());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
if (commandLine.hasOption(SERVER_OPTION)) {
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to remove the service. This
// may help clean up a failed install.
- removeServerService();
+ rValue = Math.max(rValue, removeServerService());
}
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- removeStorageService();
+ rValue = Math.max(rValue, removeStorageService());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to stop services", e);
}
+ return rValue;
}
- private void removeStorageService() throws Exception {
+ private int removeStorageService() throws Exception {
log.debug("Stopping RHQ storage node");
Executor executor = new DefaultExecutor();
@@ -127,12 +132,15 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
commandLine = getCommandLine("rhq-storage", "remove");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
log.debug("Failed to remove storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getStoragePid();
@@ -141,15 +149,18 @@ public class Remove extends ControlCommand {
System.out.println("RHQ storage node (pid=" + pid + ") is stopping...");
commandLine = new org.apache.commons.exec.CommandLine("kill").addArgument(pid);
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
System.out.println("RHQ storage node has stopped");
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void removeServerService() throws Exception {
+ private int removeServerService() throws Exception {
log.debug("Stopping RHQ server");
Executor executor = new DefaultExecutor();
@@ -157,25 +168,31 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
try {
commandLine = getCommandLine("rhq-server", "remove");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to remove server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getServerPid();
if (pid != null) {
commandLine = getCommandLine("rhq-server", "stop");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void removeAgentService() throws Exception {
+ private int removeAgentService() throws Exception {
log.debug("Stopping RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -184,21 +201,27 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
try {
commandLine = getCommandLine("rhq-agent-wrapper", "remove");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist, script returns 1
log.debug("Failed to remove agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
index fffb769..40d61c6 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
@@ -64,11 +64,13 @@ public class Restart extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
Stop stop = new Stop();
stop.exec(commandLine);
+ // If the server isn't stopped.. restart had some LSB rules.. check 'em.
+
Start start = new Start();
- start.exec(commandLine);
+ return start.exec(commandLine);
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 7d621cb..5d540a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -36,6 +36,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -72,7 +73,8 @@ public class Start extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then start whatever is installed
if (commandLine.getOptions().length == 0) {
@@ -82,55 +84,62 @@ public class Start extends ControlCommand {
if (!(storageInstalled || serverInstalled || agentInstalled)) {
log.warn("Nothing to start. No RHQ services are installed.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
} else {
if (storageInstalled) {
- startStorage();
+ rValue = Math.max(rValue, startStorage());
}
if (serverInstalled) {
- startRHQServer();
+ rValue = Math.max(rValue, startRHQServer());
}
if (agentInstalled) {
- startAgent();
+ rValue = Math.max(rValue, startAgent());
}
}
} else {
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- startStorage();
+ rValue = Math.max(rValue, startStorage());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
if (commandLine.hasOption(SERVER_OPTION)) {
if (isServerInstalled()) {
- startRHQServer();
+ rValue = Math.max(rValue, startRHQServer());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- startAgent();
+ rValue = Math.max(rValue, startAgent());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to start services", e);
}
+ return rValue;
}
- private void startStorage() throws Exception {
+ private int startStorage() throws Exception {
log.debug("Starting RHQ storage node");
Executor executor = new DefaultExecutor();
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
// environment.
@@ -144,11 +153,12 @@ public class Start extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
commandLine = getCommandLine("rhq-storage", "start");
try {
- executor.execute(commandLine, env);
+ rValue = executor.execute(commandLine, env);
} catch (Exception e) {
// Ignore, service may not exist or may already be running, script returns 1
log.debug("Failed to start storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
File storageBinDir = new File(getStorageBasedir(), "bin");
@@ -159,16 +169,18 @@ public class Start extends ControlCommand {
if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
+ rValue = RHQControl.EXIT_CODE_OK;
} else {
commandLine = getCommandLine(false, "cassandra", "-p", pidFile.getAbsolutePath());
executor.setWorkingDirectory(storageBinDir);
- executor.execute(commandLine, env);
+ rValue = executor.execute(commandLine, env);
}
}
+ return rValue;
}
- private void startRHQServer() throws Exception {
+ private int startRHQServer() throws Exception {
log.debug("Starting RHQ server");
Executor executor = new DefaultExecutor();
@@ -176,19 +188,23 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "start");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to start server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+ return rValue;
}
- private void startAgent() throws Exception {
+ private int startAgent() throws Exception {
log.debug("Starting RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -197,16 +213,21 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "start");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to start agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
index 9ba9f60..e52f0a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
@@ -35,6 +35,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -71,23 +72,24 @@ public class Status extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then check the status of whatever is installed
if (commandLine.getOptions().length == 0) {
if (isStorageInstalled()) {
- checkStorageStatus();
+ rValue = Math.max(rValue, checkStorageStatus());
}
if (isServerInstalled()) {
- checkServerStatus();
+ rValue = Math.max(rValue, checkServerStatus());
}
if (isAgentInstalled()) {
- checkAgentStatus();
+ rValue = Math.max(rValue, checkAgentStatus());
}
} else {
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- checkStorageStatus();
+ rValue = Math.max(rValue, checkStorageStatus());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
@@ -95,7 +97,7 @@ public class Status extends ControlCommand {
}
if (commandLine.hasOption(SERVER_OPTION)) {
if (isServerInstalled()) {
- checkServerStatus();
+ rValue = Math.max(rValue, checkServerStatus());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
@@ -103,7 +105,7 @@ public class Status extends ControlCommand {
}
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- checkAgentStatus();
+ rValue = Math.max(rValue, checkAgentStatus());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
@@ -113,11 +115,14 @@ public class Status extends ControlCommand {
} catch (Exception e) {
throw new RHQControlException("Failed to check statuses", e);
}
+ return rValue;
}
- private void checkStorageStatus() throws Exception {
+ private int checkStorageStatus() throws Exception {
log.debug("Checking RHQ storage node status");
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (isWindows()) {
Executor executor = new DefaultExecutor();
executor.setStreamHandler(new PumpStreamHandler());
@@ -125,31 +130,34 @@ public class Status extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
commandLine = getCommandLine("rhq-storage", "status");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
log.debug("Failed to check storage service status", e);
+ rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
} else {
if(isStorageRunning()) {
System.out.println(String.format("%-30s", "RHQ Storage Node") + " (pid " + String.format("%-7s", getStoragePid()) + ") IS running");
} else {
System.out.println(String.format("%-30s", "RHQ Storage Node") + " (no pid file) IS NOT running");
+ rValue = RHQControl.EXIT_CODE_STATUS_NOT_RUNNING;
}
}
+ return rValue;
}
- private void checkServerStatus() throws Exception {
+ private int checkServerStatus() throws Exception {
log.debug("Checking RHQ server status");
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "status");
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine);
+ return executor.execute(commandLine);
}
- private void checkAgentStatus() throws Exception {
+ private int checkAgentStatus() throws Exception {
log.debug("Checking RHQ agent status");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -159,7 +167,7 @@ public class Status extends ControlCommand {
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
try {
- executor.execute(commandLine);
+ return executor.execute(commandLine);
} catch (ExecuteException e) {
// For windows the JSW exit code for a status check is expected to be a mask value and the agent wrapper
// .bat will return it explicitly. We can ignore it and assume that the logged output is sufficient.
@@ -167,6 +175,7 @@ public class Status extends ControlCommand {
if (!isWindows()) {
throw e;
}
+ return RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index 9b51e28..a3920e0 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -33,6 +33,7 @@ import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -68,29 +69,33 @@ public class Stop extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+
+ int rValue = RHQControl.EXIT_CODE_OK;
+
try {
// if no options specified, then stop whatever is installed
if (commandLine.getOptions().length == 0) {
if (isAgentInstalled()) {
- stopAgent();
+ rValue = Math.max(rValue, stopAgent());
}
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to stop the service. This
// may help clean up a failed install.
- stopRHQServer();
+ rValue = Math.max(rValue, stopRHQServer());
if (isStorageInstalled()) {
- stopStorage();
+ rValue = Math.max(rValue, stopStorage());
}
} else {
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- stopAgent();
+ rValue = Math.max(rValue, stopAgent());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
@@ -98,24 +103,26 @@ public class Stop extends AbstractInstall {
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to stop the service. This
// may help clean up a failed install.
- stopRHQServer();
+ rValue = Math.max(rValue, stopRHQServer());
}
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- stopStorage();
+ rValue = Math.max(rValue, stopStorage());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to stop services", e);
}
+ return rValue;
}
- private void stopStorage() throws Exception {
+ private int stopStorage() throws Exception {
log.debug("Stopping RHQ storage node");
Executor executor = new DefaultExecutor();
@@ -123,17 +130,21 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
commandLine = getCommandLine("rhq-storage", "stop");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ System.out.println("RHQ storage node has stopped");
} catch (Exception e) {
// Ignore, service may not exist or be running, script returns 1
log.debug("Failed to stop storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if (isStorageRunning()) {
+ if(isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
@@ -145,11 +156,13 @@ public class Stop extends AbstractInstall {
System.out.println("RHQ storage node has stopped");
}
+ rValue = RHQControl.EXIT_CODE_OK; // If process isn't running, stopping it is considered OK.
}
+ return rValue;
}
- private void stopRHQServer() throws Exception {
+ private int stopRHQServer() throws Exception {
log.debug("Stopping RHQ server");
Executor executor = new DefaultExecutor();
@@ -157,23 +170,29 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getServerPid();
if (pid != null) {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void stopAgent() throws Exception {
+ private int stopAgent() throws Exception {
log.debug("Stopping RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -182,19 +201,25 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index 5a762cd..cc3d8c2 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -48,6 +48,7 @@ import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.file.FileVisitor;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -125,7 +126,8 @@ public class Upgrade extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
boolean start = commandLine.hasOption(START_OPTION);
try {
@@ -135,9 +137,13 @@ public class Upgrade extends AbstractInstall {
log.error(error);
}
log.error("Exiting due to the previous errors");
- return;
+ return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
+ // Attempt to shutdown any running components. A failure to shutdown a component is not a failure as it
+ // really shouldn't be running anyway. This is just an attempt to avoid upgrade problems.
+ log.info("Stopping any running RHQ components...");
+
// If using non-default agent location then save it so it will be applied to all subsequent rhqctl commands.
boolean hasFromAgentOption = commandLine.hasOption(FROM_AGENT_DIR_OPTION);
if (hasFromAgentOption) {
@@ -149,7 +155,7 @@ public class Upgrade extends AbstractInstall {
// if the agent already exists in the default location, it may be there from a prior install.
if (isStorageInstalled() || isServerInstalled()) {
log.warn("RHQ is already installed so upgrade can not be performed.");
- return;
+ return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// Attempt to shutdown any running components. A failure to shutdown a component is not a failure as it
@@ -158,7 +164,7 @@ public class Upgrade extends AbstractInstall {
// Stop the agent, if running.
if (hasFromAgentOption) {
- killAgent(getFromAgentDir(commandLine)); // this validates the path as well
+ rValue = Math.max(rValue, killAgent(getFromAgentDir(commandLine))); // this validates the path as well
}
// If rhqctl exists in the old version, use it to stop old components, otherwise, just try and stop the
@@ -177,10 +183,10 @@ public class Upgrade extends AbstractInstall {
} else {
log.error("The old installation components failed to be stopped. Please stop them manually before continuing. exit code="
+ exitValue);
- return;
+ return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -193,9 +199,9 @@ public class Upgrade extends AbstractInstall {
});
// now upgrade everything
- upgradeStorage(commandLine);
- upgradeServer(commandLine);
- upgradeAgent(commandLine);
+ rValue = Math.max(rValue, upgradeStorage(commandLine));
+ rValue = Math.max(rValue, upgradeServer(commandLine));
+ rValue = Math.max(rValue, upgradeAgent(commandLine));
File agentDir;
@@ -208,7 +214,7 @@ public class Upgrade extends AbstractInstall {
updateWindowsAgentService(agentDir);
if (start) {
- startAgent(agentDir);
+ rValue = Math.max(rValue, startAgent(agentDir));
}
} catch (Exception e) {
throw new RHQControlException("An error occurred while executing the upgrade command", e);
@@ -218,30 +224,33 @@ public class Upgrade extends AbstractInstall {
Stop stopCommand = new Stop();
stopCommand.exec(new String[] { "stop", "--server" });
if (!commandLine.hasOption(RUN_DATA_MIGRATION)) {
- stopCommand.exec(new String[] { "stop", "--storage" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--storage" }));
}
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
if (!isRhq48OrLater(commandLine) && commandLine.hasOption(RUN_DATA_MIGRATION)) {
- runDataMigration(commandLine);
+ rValue = Math.max(rValue, runDataMigration(commandLine));
}
-
+ return rValue;
}
- private void runDataMigration(CommandLine rhqctlCommandLine) {
+ private int runDataMigration(CommandLine rhqctlCommandLine) {
String migrationOption = rhqctlCommandLine.getOptionValue(RUN_DATA_MIGRATION);
+ int rValue;
+
if (migrationOption.equals("none")) {
log.info("No data migration will run");
if (!isRhq48OrLater(rhqctlCommandLine)) {
printDataMigrationNotice();
}
- return;
+ return RHQControl.EXIT_CODE_OK;
}
// We deduct the database parameters from the server properties
@@ -253,23 +262,28 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
+ executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
log.info("The data migrator finished with exit value " + exitValue);
+ rValue = exitValue;
} catch (Exception e) {
log.error("Running the data migrator failed - please try to run it from the command line: "
+ e.getMessage());
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
+ return rValue;
}
- private void upgradeStorage(CommandLine rhqctlCommandLine) throws Exception {
+ private int upgradeStorage(CommandLine rhqctlCommandLine) throws Exception {
if (rhqctlCommandLine.hasOption(USE_REMOTE_STORAGE_NODE)) {
log.info("Ignoring storage node upgrade, a remote storage node is configured.");
- return;
+ return RHQControl.EXIT_CODE_OK;
}
+ int rValue;
+
// If upgrading from a pre-cassandra then just install an initial storage node. Otherwise, upgrade
if (isRhq48OrLater(rhqctlCommandLine)) {
try {
@@ -293,28 +307,30 @@ public class Upgrade extends AbstractInstall {
int exitCode = executor.execute(commandLine);
log.info("The storage node upgrade has finished with an exit value of " + exitCode);
-
+ rValue = exitCode;
} catch (IOException e) {
log.error("An error occurred while running the storage node upgrade: " + e.getMessage());
throw e;
}
} else {
- installStorageNode(getStorageBasedir(), rhqctlCommandLine, true);
+ rValue = installStorageNode(getStorageBasedir(), rhqctlCommandLine, true);
}
+ return rValue;
}
- private void upgradeServer(CommandLine commandLine) throws Exception {
+ private int upgradeServer(CommandLine commandLine) throws Exception {
// don't upgrade the server if this is a storage node only install
File oldServerDir = getFromServerDir(commandLine);
if (!(!isRhq48OrLater(commandLine) || isServerInstalled(oldServerDir))) {
log.info("Ignoring server upgrade, this is a storage node only installation.");
- return;
+ return RHQControl.EXIT_CODE_OK;
}
// copy all the old settings into the new rhq-server.properties file
upgradeServerPropertiesFile(commandLine);
+ int rValue = RHQControl.EXIT_CODE_OK;
// make sure we retain the oracle driver if one exists
try {
copyOracleDriver(oldServerDir);
@@ -323,6 +339,7 @@ public class Upgrade extends AbstractInstall {
+ "The upgrade will continue but your server may not work if connecting to an Oracle database, "
+ "in which case you will need to manually install an Oracle driver to your server. " + "Cause: "
+ ThrowableUtil.getAllMessages(e));
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// copy over any wrapper.inc that may have been added
@@ -334,10 +351,10 @@ public class Upgrade extends AbstractInstall {
// start the server, the invoke the installer and wait for the server to be completely installed
startRHQServerForInstallation();
- runRHQServerInstaller();
+ rValue = Math.max(rValue, runRHQServerInstaller());
waitForRHQServerToInitialize();
- return;
+ return rValue;
}
public void copyOracleDriver(File oldServerDir) throws IOException {
@@ -529,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
@@ -606,7 +623,7 @@ public class Upgrade extends AbstractInstall {
return (null != path) ? path.replace('\\', '/') : null;
}
- private void upgradeAgent(CommandLine rhqctlCommandLine) throws Exception {
+ private int upgradeAgent(CommandLine rhqctlCommandLine) throws Exception {
try {
File oldAgentDir;
if (rhqctlCommandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
@@ -628,8 +645,7 @@ public class Upgrade extends AbstractInstall {
+ " option specified and no agent found in the default location ["
+ oldAgentDir.getAbsolutePath()
+ "]. Installing agent in the default location as part of the upgrade.");
- installAgent(getAgentBasedir(), rhqctlCommandLine);
- return;
+ return installAgent(getAgentBasedir(), rhqctlCommandLine);
}
}
@@ -687,6 +703,7 @@ public class Upgrade extends AbstractInstall {
});
log.info("The agent has been upgraded and placed in: " + agentBasedir);
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while upgrading the agent: " + e.getMessage());
commit a90246c660b740055b9671b83a55681fcfb9a9bb
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jan 2 16:59:20 2014 +0100
Add BoneCP to Eclipse classpath file
diff --git a/.classpath b/.classpath
index f8048fc..f371634 100644
--- a/.classpath
+++ b/.classpath
@@ -399,5 +399,6 @@
<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.1/jackson-core-2.1.1.jar"/>
<classpathentry kind="var" path="M2_REPO/com/codahale/metrics/metrics-core/3.0.1/metrics-core-3.0.1.jar"/>
<classpathentry kind="var" path="M2_REPO/com/datastax/cassandra/cassandra-driver-core/1.0.2/cassandra-driver-core-1.0.2.jar"/>
+ <classpathentry kind="var" path="M2_REPO/com/jolbox/bonecp/0.8.0.RELEASE/bonecp-0.8.0.RELEASE.jar"/>
<classpathentry kind="output" path="eclipse-classes"/>
</classpath>
commit 4bd71559e3c124828f66725fb002ca6a2c1b0b9a
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 17 21:35:47 2013 +0100
Bug 968361 - Improve database plugin design to support connection pooling
This changeset introduces a new API for database plugins and deprecates the previous one. Compatibility with the previous API will be maintained until next major version of RHQ.
The 'rhq-database-plugin' was based on org.rhq.plugins.database.DatabaseComponent interface which encouraged plugin authors to share a single JDBC connection across database components. This was wrong for various reasons (connection leaks, concurrent JDBC calls... etc).
The new API introduces three important classes:
* org.rhq.plugins.database.PooledConnectionProvider
* org.rhq.plugins.database.BasePooledConnectionProvider
* org.rhq.plugins.database.ConnectionPoolingSupport
BasePooledConnectionProvider is a base implementation of a PooledConnectionProvider. Plugin authors should create a concrete implementation of BasePooledConnectionProvider which overrides the #getDriverClass() method. This is important if a database plugin embeds a JDBC driver: the database-specific driver class must be loaded by the child plugin classloader.
ConnectionPoolingSupport helps to manage the compatibility with the old API. It's a contract that all new database resource components should obey to. It declares the following methods:
* #supportsConnectionPooling()
* #getPooledConnectionProvider()
Results of calls to #supportsConnectionPooling() #getPooledConnectionProvider() must be consistent. In practice, a top level server database component should be able to create a PooledConnectionProvider instance, and child servers and services should indicate they support connection pooling only if their parent component does.
The 'rhq-database-plugin' embeds the BoneCP library (JDBC connection pooling) and its dependencies (Google's Guava). Child plugins will have all the classes accessible as soon as they have this node in their plugin descriptor:
===
<depends plugin="Database" useClasses="true"/>
===
This changeset includes the necessary changes to support connection pooling in the Oracle, Postgres and MySQL plugins.
Thanks to Elias Ross for contributing the original patch from which this changeset is derived.
diff --git a/modules/plugins/database/pom.xml b/modules/plugins/database/pom.xml
index 7ede2af..952e919 100644
--- a/modules/plugins/database/pom.xml
+++ b/modules/plugins/database/pom.xml
@@ -17,9 +17,33 @@
<properties>
<rhq.internal>false</rhq.internal>
+ <slf4j.version>1.7.2</slf4j.version>
</properties>
<dependencies>
+
+ <!-- Bone CP and its dependencies -->
+ <dependency>
+ <groupId>com.jolbox</groupId>
+ <artifactId>bonecp</artifactId>
+ <version>0.8.0.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>15.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <!-- Test dependencies -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
@@ -28,7 +52,45 @@
</dependency>
</dependencies>
- <profiles>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-dependency-jars</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.jolbox</groupId>
+ <artifactId>bonecp</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.build.outputDirectory}/lib</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
<profile>
<id>dev</id>
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
index 5976695..a0fdf07 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,31 +13,49 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
/**
- * Base class for database components.
+ * Base class for nested database components.
* @author Greg Hinkle
*/
-public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>> implements DatabaseComponent {
+public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>> implements DatabaseComponent,
+ ConnectionPoolingSupport {
+
+ private PooledConnectionProvider pooledConnectionProvider;
protected ResourceContext<T> resourceContext;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
+ if (hasConnectionPoolingSupport(resourceContext.getParentResourceComponent())) {
+ pooledConnectionProvider = ((ConnectionPoolingSupport) resourceContext.getParentResourceComponent())
+ .getPooledConnectionProvider();
+ }
}
- /*public void start(ResourceContext<T> resourceContext) throws InvalidPluginConfigurationException, Exception
- * { this.resourceContext = resourceContext;}*/
-
public void stop() {
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return pooledConnectionProvider != null;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public Connection getConnection() {
@@ -52,4 +70,4 @@ public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>>
public String toString() {
return getClass().getName() + " key=" + resourceContext.getResourceKey();
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java
new file mode 100644
index 0000000..c9b3a59
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java
@@ -0,0 +1,226 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.sql.DataSource;
+
+import com.jolbox.bonecp.BoneCP;
+import com.jolbox.bonecp.BoneCPConfig;
+import com.jolbox.bonecp.hooks.AbstractConnectionHook;
+import com.jolbox.bonecp.hooks.AcquireFailConfig;
+import com.jolbox.bonecp.hooks.ConnectionHook;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.configuration.Configuration;
+
+/**
+ * Base implementation of a {@link PooledConnectionProvider}. Plugin authors <em>should</em>:
+ * <ol>
+ * <li>create a concrete implementation which overrides the {@link #getDriverClass()} method</li>
+ * <li>adopt the configuration properties described here or override the corresponding #get method</li>
+ * </ol>
+ *
+ * The first point is important if a concrete database plugin embeds the JDBC driver: the database-specific driver class
+ * <strong>must</strong> be loaded by the child plugin classloader.
+ *
+ * @author Elias Ross
+ */
+public abstract class BasePooledConnectionProvider implements PooledConnectionProvider {
+ private static final Log LOG = LogFactory.getLog(BasePooledConnectionProvider.class);
+
+ /**
+ * Driver class key.
+ */
+ public static final String DRIVER_CLASS = "driverClass";
+
+ /**
+ * JDBC URL config key.
+ */
+ public static final String URL = "url";
+
+ /**
+ * JDBC username config key.
+ */
+ public static final String USERNAME = "username";
+
+ /**
+ * JDBC password config key.
+ */
+ public static final String PASSWORD = "password";
+
+ /**
+ * If true, track connections and statements.
+ */
+ public static final String TRACK = "track";
+
+ /**
+ * Connection timeout setting.
+ */
+ public static final String TIMEOUT = "connectionTimeout";
+
+ protected final Configuration pluginConfig;
+
+ /**
+ * Connection pool.
+ * The connection pool implementation details should not be exposed as
+ * the implementation may change.
+ */
+ private final BoneCP connectionPool;
+
+ protected BasePooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ this.pluginConfig = pluginConfig;
+ BoneCPConfig bconfig = new BoneCPConfig(getConnectionProperties());
+ Class<Driver> driverClass;
+ try {
+ driverClass = getDriverClass();
+ } catch (ClassNotFoundException e) {
+ LOG.warn("Could not load driver class from: " + Thread.currentThread().getContextClassLoader());
+ throw e;
+ }
+ if (driverClass != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using driver class " + driverClass);
+ }
+ Driver driver = driverClass.newInstance();
+ DataSource datasourceBean = new DriverDataSource(driver, getJdbcUrl(), getConnectionProperties());
+ bconfig.setDatasourceBean(datasourceBean);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using driver manager for " + getJdbcUrl());
+ }
+ bconfig.setJdbcUrl(getJdbcUrl());
+ }
+ bconfig.setAcquireIncrement(1);
+ bconfig.setAcquireRetryAttempts(0);
+ bconfig.setAcquireRetryDelayInMs(0);
+ bconfig.setPartitionCount(2);
+ bconfig.setMaxConnectionsPerPartition(5);
+ bconfig.setPassword(getPassword());
+ bconfig.setUsername(getUsername());
+ bconfig.setConnectionTimeoutInMs(getConnectionTimeout());
+ if (isTrack()) {
+ bconfig.setCloseConnectionWatch(true);
+ bconfig.setCloseConnectionWatchTimeout(10, TimeUnit.MINUTES);
+ }
+ bconfig.setLazyInit(false);
+ bconfig.setDisableJMX(true);
+ // Do not manage retry
+ ConnectionHook hook = new AbstractConnectionHook() {
+ public boolean onAcquireFail(Throwable t, AcquireFailConfig acquireConfig) {
+ LOG.error("Failed to obtain connection", t);
+ return false;
+ }
+ };
+ bconfig.setConnectionHook(hook);
+ connectionPool = new BoneCP(bconfig);
+ }
+
+ private boolean isTrack() {
+ return Boolean.valueOf(pluginConfig.getSimpleValue(TRACK, null));
+ }
+
+ @Override
+ public Connection getPooledConnection() throws SQLException {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("get connection for " + getJdbcUrl() + " free " + connectionPool.getTotalFree() + "/"
+ + connectionPool.getTotalCreatedConnections());
+ }
+ return connectionPool.getConnection();
+ }
+
+ /**
+ * Return additional database connection pool properties.
+ * By default, returns an empty properties object.
+ */
+ protected Properties getConnectionProperties() {
+ return new Properties();
+ }
+
+ /**
+ * Returns the driver class, by default the name from {@link #getDriverClassName()}.
+ * If not configured, this assumes the plugin will load the appropriate driver.
+ * @throws ClassNotFoundException if the classloader could not load this class
+ */
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ String cname = getDriverClassName();
+ if (cname != null) {
+ return (Class<Driver>) Thread.currentThread().getContextClassLoader().loadClass(cname);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the driver class, by default configuration item {@link #DRIVER_CLASS}.
+ */
+ protected String getDriverClassName() throws ClassNotFoundException {
+ return pluginConfig.getSimpleValue(DRIVER_CLASS, null);
+ }
+
+ /**
+ * Implemented by subclasses to return the JDBC connection URL.
+ * By default, returns the configuration item {@link #URL}.
+ */
+ protected String getJdbcUrl() {
+ return pluginConfig.getSimpleValue(URL);
+ }
+
+ /**
+ * Return the JDBC username.
+ * By default, returns the configuration item {@link #USERNAME}.
+ */
+ protected String getUsername() {
+ return pluginConfig.getSimpleValue(USERNAME);
+ }
+
+ /**
+ * Return the JDBC password.
+ * By default, returns the configuration item {@link #PASSWORD}.
+ */
+ protected String getPassword() {
+ return pluginConfig.getSimpleValue(PASSWORD);
+ }
+
+ /**
+ * Return the connection timeout setting, in milliseconds.
+ * By default, returns the configuration item {@link #TIMEOUT}.
+ */
+ protected long getConnectionTimeout() {
+ String s = pluginConfig.getSimpleValue(TIMEOUT);
+ if (s == null) {
+ return 1000 * 10;
+ }
+ return Long.parseLong(s);
+ }
+
+ /**
+ * Shutdown the connection pool.
+ */
+ public void close() {
+ connectionPool.shutdown();
+ }
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java
new file mode 100644
index 0000000..a899e44
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java
@@ -0,0 +1,52 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+/**
+ * <p>
+ * A contract that all database related resource components should obey to.
+ * </p>
+ * <p>
+ * Results of calls to {@link #supportsConnectionPooling()} and {@link #getPooledConnectionProvider()}
+ * <strong>MUST</strong> be consistent.
+ * </p>
+ * <p>
+ * In practice, a top level server database component should be able to create a {@link PooledConnectionProvider}
+ * instance, and child servers and services should indicate they support connection pooling only if their parent
+ * component does.
+ * </p>
+ *
+ * @author Thomas Segismont
+ */
+public interface ConnectionPoolingSupport {
+
+ /**
+ * @return true if this component can give a reference to a {@link PooledConnectionProvider}, false otherwise.
+ */
+ boolean supportsConnectionPooling();
+
+ /**
+ * @return a reference to a {@link PooledConnectionProvider} or null if this component does not support connection
+ * pooling.
+ * @see #supportsConnectionPooling()
+ */
+ PooledConnectionProvider getPooledConnectionProvider();
+
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
index f2e8fa7..90f3f2f 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,12 +13,19 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValueMap;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
@@ -36,24 +43,40 @@ import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* The component for the {@link CustomTableDiscoveryComponent}.
*
* @author Greg Hinkle
*/
-public class CustomTableComponent implements DatabaseComponent<DatabaseComponent<?>>, MeasurementFacet {
- private ResourceContext<DatabaseComponent<?>> context;
+public class CustomTableComponent implements DatabaseComponent<DatabaseComponent<?>>, ConnectionPoolingSupport,
+ MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(CustomTableComponent.class);
- private static final Log log = LogFactory.getLog(CustomTableComponent.class);
+ private ResourceContext<DatabaseComponent<?>> context;
+ private PooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext<DatabaseComponent<?>> resourceContext)
throws InvalidPluginConfigurationException, Exception {
this.context = resourceContext;
+ if (hasConnectionPoolingSupport(context.getParentResourceComponent())) {
+ pooledConnectionProvider = ((ConnectionPoolingSupport) context.getParentResourceComponent())
+ .getPooledConnectionProvider();
+ }
}
public void stop() {
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return pooledConnectionProvider != null;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public String getTable() {
@@ -66,16 +89,22 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
return AvailabilityType.UP;
}
Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
- statement = getConnection().createStatement();
+ connection = getConnectionFromComponent(this);
+ statement = connection.createStatement();
statement.setMaxRows(1);
statement.setFetchSize(1);
- statement.executeQuery("SELECT * FROM " + getTable()).close();
+ resultSet = statement.executeQuery("SELECT * FROM " + getTable());
return AvailabilityType.UP;
} catch (SQLException e) {
return AvailabilityType.DOWN;
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
}
@@ -85,10 +114,10 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
String query = config.getSimpleValue("metricQuery", null);
if (query == null) {
- if (log.isTraceEnabled()) {
+ if (LOG.isTraceEnabled()) {
ResourceType type = this.context.getResourceType();
String resourceKey = this.context.getResourceKey();
- log.trace("Resource "
+ LOG.trace("Resource "
+ resourceKey
+ " ("
+ type.getName()
@@ -104,13 +133,13 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
Map<String, Double> values;
if (Boolean.parseBoolean(column)) {
// data in column format
- values = DatabaseQueryUtility.getNumericQueryValues(this, query);
+ values = getNumericQueryValues(this, query);
} else {
// data in row format
- values = DatabaseQueryUtility.getNumericQueryValueMap(this, query);
+ values = getNumericQueryValueMap(this, query);
}
- if (log.isDebugEnabled())
- log.debug("returned values " + values);
+ if (LOG.isDebugEnabled())
+ LOG.debug("returned values " + values);
// this is a for loop because the name of each column can be the name of the metric
for (MeasurementScheduleRequest request : metrics) {
@@ -129,7 +158,9 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
if (value != null) {
report.addData(new MeasurementDataNumeric(request, value));
} else {
- log.debug("Missing column in query results - metric not collected: " + columnName);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Missing column in query results - metric not collected: " + columnName);
+ }
}
}
}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
index aeab429..b3aca8d 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,12 +13,18 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.canProvideConnection;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
@@ -26,14 +32,15 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ManualAddFacet;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Discovery for a generic component that can read data out of a table for
@@ -46,62 +53,85 @@ import org.rhq.core.util.jdbc.JDBCUtil;
*
* @author Greg Hinkle
*/
-public class CustomTableDiscoveryComponent implements ManualAddFacet<DatabaseComponent<?>>,
- ResourceDiscoveryComponent<DatabaseComponent<?>> {
+public class CustomTableDiscoveryComponent implements ManualAddFacet<ResourceComponent<?>>,
+ ResourceDiscoveryComponent<ResourceComponent<?>> {
protected Log log = LogFactory.getLog(getClass());
+ @Override
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
- throws InvalidPluginConfigurationException, Exception {
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException,
+ Exception {
- Configuration config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ ResourceComponent<?> parentComponent = discoveryContext.getParentResourceComponent();
+
+ Configuration config = discoveryContext.getDefaultPluginConfiguration();
String table = config.getSimpleValue("table", "");
- ResourceType rt = resourceDiscoveryContext.getResourceType();
- String resourceName = config.getSimpleValue("name", rt.getName());
- String resourceDescription = config.getSimpleValue("description", rt.getDescription());
+ ResourceType resourceType = discoveryContext.getResourceType();
+ String resourceName = config.getSimpleValue("name", resourceType.getName());
+ String resourceDescription = config.getSimpleValue("description", resourceType.getDescription());
+
+ if (!canProvideConnection(parentComponent)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parent component does not provide JDBC connections, cannot discover" + resourceName);
+ }
+ return Collections.emptySet();
+ }
if (table.isEmpty()) {
- log.debug("'table' value not set, cannot discover " + resourceName);
+ if (log.isDebugEnabled()) {
+ log.debug("'table' value not set, cannot discover " + resourceName);
+ }
return Collections.emptySet();
}
Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
- if (conn == null)
+ connection = getConnectionFromComponent(parentComponent);
+ if (connection == null) {
throw new InvalidPluginConfigurationException("cannot obtain connection from parent");
-
- statement = conn.createStatement();
+ }
+ statement = connection.createStatement();
statement.setMaxRows(1);
statement.setFetchSize(1);
// This is more efficient than 'count(*)'
// unless the JDBC driver fails to support setMaxRows or doesn't stream results
- statement.executeQuery("SELECT * FROM " + table).close();
- DiscoveredResourceDetails details = new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(), table + resourceName, resourceName, null,
- resourceDescription, config, null);
+ resultSet = statement.executeQuery("SELECT * FROM " + table);
- log.debug("discovered " + details);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(), table
+ + resourceName, resourceName, null, resourceDescription, config, null);
+
+ if (log.isDebugEnabled()) {
+ log.debug("discovered " + details);
+ }
return Collections.singleton(details);
+
} catch (SQLException e) {
- log.debug("discovery failed " + e + " for " + table);
+ if (log.isDebugEnabled()) {
+ log.debug("discovery failed " + e + " for " + table);
+ }
// table not found, don't inventory
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
return Collections.emptySet();
}
+ @Override
public DiscoveredResourceDetails discoverResource(Configuration pluginConfiguration,
- ResourceDiscoveryContext<DatabaseComponent<?>> discoveryContext) throws InvalidPluginConfigurationException {
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException {
Configuration config = pluginConfiguration;
String table = config.getSimpleValue("table", null);
String resourceName = config.getSimpleValue("name", table);
- String resourceDescription = config.getSimpleValue("description",
- discoveryContext.getResourceType().getDescription());
+ String resourceDescription = config.getSimpleValue("description", discoveryContext.getResourceType()
+ .getDescription());
String resourceVersion = config.getSimpleValue("version", null);
DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(), table
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
index 50da33b..bde426a 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.canProvideConnection;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -29,13 +34,15 @@ import java.util.regex.Matcher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Discovery for a generic component that can read data out of a table for
@@ -47,57 +54,70 @@ import org.rhq.core.util.jdbc.JDBCUtil;
*
* @author Greg Hinkle
*/
-public class CustomTableRowDiscoveryComponent implements ResourceDiscoveryComponent<DatabaseComponent<?>> {
-
- private final Log log = LogFactory.getLog(getClass());
+public class CustomTableRowDiscoveryComponent implements ResourceDiscoveryComponent<ResourceComponent<?>> {
+ private static final Log LOG = LogFactory.getLog(CustomTableRowDiscoveryComponent.class);
+ @Override
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
- throws InvalidPluginConfigurationException, Exception {
- Statement statement = null;
- ResultSet resultSet = null;
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException,
+ Exception {
+
+ ResourceComponent<?> parentComponent = discoveryContext.getParentResourceComponent();
- Configuration config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ Configuration config = discoveryContext.getDefaultPluginConfiguration();
String table = config.getSimpleValue("table", null);
String keyColumn = config.getSimpleValue("keyColumn", null);
- try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
+ ResourceType resourceType = discoveryContext.getResourceType();
+ String resourceName = config.getSimpleValue("name", resourceType.getName());
+ String resourceDescription = config.getSimpleValue("description", "");
- String resourceName = config.getSimpleValue("name", null);
- String resourceDescription = config.getSimpleValue("description", "");
+ if (!canProvideConnection(parentComponent)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Parent component does not provide JDBC connections, cannot discover" + resourceName);
+ }
+ return Collections.emptySet();
+ }
- if (resourceName == null) {
- throw new InvalidPluginConfigurationException("The 'name' connection property has to be specified.");
+ if (resourceName == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("'name' property not set, cannot discover" + resourceName);
}
+ return Collections.emptySet();
+ }
- statement = conn.createStatement();
+ Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnectionFromComponent(parentComponent);
+ if (connection == null) {
+ throw new InvalidPluginConfigurationException("cannot obtain connection from parent");
+ }
+ statement = connection.createStatement();
resultSet = statement.executeQuery("SELECT * FROM " + table);
Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>();
while (resultSet.next()) {
- config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ config = discoveryContext.getDefaultPluginConfiguration();
String key = resultSet.getString(keyColumn);
config.put(new PropertySimple("key", key));
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(),
- key,
- formatMessage(resourceName, key),
- null,
- formatMessage(resourceDescription,
- key), config, null);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(),
+ key, formatMessage(resourceName, key), null, formatMessage(resourceDescription, key), config, null);
found.add(details);
}
return found;
+
} catch (SQLException e) {
- log.debug("table " + table + " column " + keyColumn, e);
+ LOG.debug("table " + table + " column " + keyColumn, e);
} finally {
- JDBCUtil.safeClose(resultSet);
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
- return Collections.emptySet();
+ return Collections.emptySet();
}
/**
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
index 5b8ad5f..3878c9d 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Connection;
@@ -24,9 +25,14 @@ import org.rhq.core.pluginapi.inventory.ResourceComponent;
/**
* @author Greg Hinkle
+ * @deprecated as of RHQ 4.10. Use {@link ConnectionPoolingSupport} instead.
*/
+@Deprecated
public interface DatabaseComponent<T extends ResourceComponent<?>> extends ResourceComponent<T> {
+
+ @Deprecated
Connection getConnection();
+ @Deprecated
void removeConnection();
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
index 38cd949..0a1fdb8 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Driver;
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java
new file mode 100644
index 0000000..f17c9b4
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java
@@ -0,0 +1,379 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+import org.rhq.core.util.exception.ThrowableUtil;
+
+/**
+ * @author Thomas Segismont
+ */
+public class DatabasePluginUtil {
+ private static final Log LOG = LogFactory.getLog(DatabasePluginUtil.class);
+
+ public static final class ComponentCannotProvideConnectionException extends IllegalArgumentException {
+ private final ResourceComponent component;
+
+ public ComponentCannotProvideConnectionException(ResourceComponent component) {
+ super(component + " cannot provide a JDBC Connection");
+ this.component = component;
+ }
+
+ public ResourceComponent getComponent() {
+ return component;
+ }
+ }
+
+ /**
+ * @return false unless <code>component</code> is not null and supports connection pooling or is an instance of
+ * {@link DatabaseComponent}.
+ */
+ public static boolean canProvideConnection(ResourceComponent component) {
+ return hasConnectionPoolingSupport(component) || component instanceof DatabaseComponent;
+ }
+
+ /**
+ * Determines if a resource component supports connection pooling.
+ *
+ * @param component the resource component
+ * @return false unless <code>component</code> is not null, implements
+ * {@link org.rhq.plugins.database.ConnectionPoolingSupport} and a call to
+ * {@link ConnectionPoolingSupport#supportsConnectionPooling()} returns true.
+ */
+ public static boolean hasConnectionPoolingSupport(ResourceComponent component) {
+ if (component instanceof ConnectionPoolingSupport) {
+ return ((ConnectionPoolingSupport) component).supportsConnectionPooling();
+ }
+ return false;
+ }
+
+ /**
+ * Gets a {@link Connection} from the <code>component</code>.
+ *
+ * @param component a resource component that must be able to provide a {@link Connection}.
+ * @throws SQLException if <code>component</code> supports connection pooling and a pooled connection could not be
+ * retrieved.
+ * @throws IllegalArgumentException if resource component is null or cannot provide a {@link Connection} (does not
+ * support connection pooling and does not implement {@link DatabaseComponent}).
+ */
+ public static Connection getConnectionFromComponent(ResourceComponent component) throws SQLException {
+ if (hasConnectionPoolingSupport(component)) {
+ return getConnectionFromPool((ConnectionPoolingSupport) component);
+ }
+ if (component instanceof DatabaseComponent) {
+ return getConnectionFromDatabaseComponent((DatabaseComponent) component);
+ }
+ throw new ComponentCannotProvideConnectionException(component);
+ }
+
+ /**
+ * Executes a query, returning the results as a map where the keys are the column names and values are the value of
+ * that column. Note that depending on the database, the column names may be uppercase (Oracle) or lowercase.
+ *
+ * @param component
+ * @param query SQL query string
+ * @param parameters optional bind parameters
+ *
+ * @return a map of query results
+ */
+ public static Map<String, Double> getNumericQueryValues(ResourceComponent component, String query,
+ Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ Map<String, Double> row = new HashMap<String, Double>();
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ String[] names = getColumns(md);
+
+ if (resultSet.next()) {
+ for (String name : names) {
+ try {
+ row.put(name, resultSet.getDouble(name));
+ } catch (SQLException e) {
+ // Ignore columns that can't be read as doubles
+ }
+ }
+ }
+
+ return row;
+ } catch (SQLException e) {
+ LOG.debug("Unable to read value", e);
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns a mapping of rows as key-value pairs where the key is the first column (a string) and the second column
+ * is a value numeric.
+ *
+ * @param component the component to execute on
+ * @param query the sql query to run
+ * @param parameters any parameters to bind first
+ *
+ * @return a Map<String,Double> of the keys against the value
+ */
+ public static Map<String, Double> getNumericQueryValueMap(ResourceComponent component, String query,
+ Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ Map<String, Double> map = new HashMap<String, Double>();
+
+ while (resultSet.next()) {
+ try {
+ map.put(resultSet.getString(1), resultSet.getDouble(2));
+ } catch (SQLException e) {
+ // Ignore columns that can't be read as doubles
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("A query column value is not a double, ignoring:" + ThrowableUtil.getAllMessages(e));
+ }
+ }
+ }
+
+ return map;
+ } catch (SQLException e) {
+ LOG.info("Unable to read value", e);
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns the result of a query as a single Double value.
+ * Returns {@link Double#NaN} if the query fails.
+ */
+ public static Double getSingleNumericQueryValue(ResourceComponent component, String query, Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+ resultSet = statement.executeQuery();
+
+ if (resultSet.next()) {
+ return resultSet.getDouble(1);
+ }
+ } catch (SQLException e) {
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * Returns a list of values, one per row, containing a map of column names to values of that row.
+ * Note depending on the database, the column names may be uppercase (Oracle) or lowercase.
+ * @param component database to query
+ * @param query SQL query
+ * @param parameters parameters to bind to the query
+ *
+ * @throws SQLException if query fails
+ */
+ public static List<Map<String, Object>> getGridValues(ResourceComponent component, String query,
+ Object... parameters) throws SQLException {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ List<Map<String, Object>> l = new ArrayList<Map<String, Object>>();
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ while (resultSet.next()) {
+ Map<String, Object> row = new HashMap<String, Object>();
+ l.add(row);
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ String[] names = getColumns(md);
+
+ for (String name : names) {
+ Object o = resultSet.getObject(name);
+ row.put(name, o);
+ }
+ }
+
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+ return l;
+
+ }
+
+ public static void safeClose(Connection connection) {
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ public static void safeClose(Connection connection, Statement statement) {
+ safeClose(statement);
+ safeClose(connection);
+ }
+
+ public static void safeClose(Connection connection, Statement statement, ResultSet resultSet) {
+ safeClose(resultSet);
+ safeClose(statement);
+ safeClose(connection);
+ }
+
+ public static void safeClose(Statement statement) {
+ if (statement != null) {
+ try {
+ statement.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ public static void safeClose(ResultSet resultSet) {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ private static void checkComponent(ResourceComponent component, boolean componentHasConnectionPoolingSupport) {
+ if (!componentHasConnectionPoolingSupport && !(component instanceof DatabaseComponent)) {
+ throw new ComponentCannotProvideConnectionException(component);
+ }
+ }
+
+ private static Connection getConnection0(ResourceComponent component, boolean componentHasConnectionPoolingSupport)
+ throws SQLException {
+ return componentHasConnectionPoolingSupport ? getConnectionFromPool((ConnectionPoolingSupport) component)
+ : getConnectionFromDatabaseComponent((DatabaseComponent) component);
+ }
+
+ private static Connection getConnectionFromPool(ConnectionPoolingSupport component) throws SQLException {
+ return component.getPooledConnectionProvider().getPooledConnection();
+ }
+
+ private static Connection getConnectionFromDatabaseComponent(DatabaseComponent component) {
+ return component.getConnection();
+ }
+
+ private static void bindParameters(PreparedStatement statement, Object... parameters) throws SQLException {
+ int i = 1;
+ for (Object p : parameters) {
+ if (p instanceof String) {
+ statement.setString(i++, (String) p);
+ } else if (p instanceof Number) {
+ statement.setDouble(i++, ((Number) p).doubleValue());
+ } else {
+ statement.setObject(i++, p);
+ }
+ }
+ }
+
+ private static String[] getColumns(ResultSetMetaData rsmd) throws SQLException {
+ String[] names = new String[rsmd.getColumnCount()];
+ for (int i = 0; i < rsmd.getColumnCount(); i++) {
+ names[i] = rsmd.getColumnName(i + 1);
+ }
+ return names;
+ }
+
+ private DatabasePluginUtil() {
+ // Utility class
+ }
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
index d6d83f2..70024c0 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Connection;
@@ -39,7 +40,9 @@ import org.rhq.core.util.exception.ThrowableUtil;
* Various database (JDBC) query functions.
*
* @author Greg Hinkle
+ * @deprecated as of RHQ 4.10, use {@link DatabasePluginUtil} instead.
*/
+@Deprecated
public class DatabaseQueryUtility {
private static final Log LOG = LogFactory.getLog(DatabaseQueryUtility.class);
@@ -59,6 +62,7 @@ public class DatabaseQueryUtility {
* @return
* @throws SQLException
*/
+ @Deprecated
public static int executeUpdate(DatabaseComponent databaseComponent, String query, Object... parameters)
throws SQLException {
PreparedStatement statement = null;
@@ -79,6 +83,7 @@ public class DatabaseQueryUtility {
* Returns the result of a query as a single Double value.
* Returns {@link Double#NaN} if the query fails.
*/
+ @Deprecated
public static Double getSingleNumericQueryValue(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -111,6 +116,7 @@ public class DatabaseQueryUtility {
*
* @return a map of query results
*/
+ @Deprecated
public static Map<String, Double> getNumericQueryValues(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -156,6 +162,7 @@ public class DatabaseQueryUtility {
*
* @throws SQLException if query fails
*/
+ @Deprecated
public static List<Map<String, Object>> getGridValues(DatabaseComponent databaseComponent, String query,
Object... parameters) throws SQLException {
PreparedStatement statement = null;
@@ -197,6 +204,7 @@ public class DatabaseQueryUtility {
*
* @return a Map<String,Double> of the keys against the value
*/
+ @Deprecated
public static Map<String, Double> getNumericQueryValueMap(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -251,6 +259,7 @@ public class DatabaseQueryUtility {
/**
* Returns an array of strings as upper-case column names.
*/
+ @Deprecated
public static String[] getColumns(ResultSetMetaData rsmd) throws SQLException {
String[] names = new String[rsmd.getColumnCount()];
for (int i = 0; i < rsmd.getColumnCount(); i++) {
@@ -263,6 +272,7 @@ public class DatabaseQueryUtility {
/**
* Closes statements and result sets.
*/
+ @Deprecated
public static void close(Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java
new file mode 100644
index 0000000..4fce379
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java
@@ -0,0 +1,103 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+/**
+ * Works with BoneCP to provide connections, because DriverManager won't load the class correctly through BoneCP
+ * (the actual driver class will be loaded by concrete database plugins).
+ */
+class DriverDataSource implements DataSource {
+
+ private final Properties properties;
+ private final Driver driver;
+ private final String url;
+ private PrintWriter out = new PrintWriter(System.out);
+
+ DriverDataSource(Driver driver, String url, Properties properties) {
+ this.driver = driver;
+ this.url = url;
+ if (properties == null) {
+ properties = new Properties();
+ }
+ this.properties = properties;
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ return out;
+ }
+
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException {
+ this.out = out;
+ }
+
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return driver.connect(url, properties);
+ }
+
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException {
+ Properties p = new Properties(properties);
+ if (username != null) {
+ p.put("user", username);
+ }
+ if (password != null) {
+ p.put("password", password);
+ }
+ return driver.connect(url, p);
+ }
+
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java
new file mode 100644
index 0000000..8089152
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java
@@ -0,0 +1,39 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Contract for resource components providing pooled {@link Connection} objects.
+ *
+ * @author Thomas Segismont
+ */
+public interface PooledConnectionProvider {
+ /**
+ * Get a pooled connection. It's the responsibility of the caller to return the connection to the pool by calling
+ * {@link java.sql.Connection#close()} on the returned {@link Connection} object.
+ *
+ * @return a pooled {@link Connection}
+ * @throws SQLException if a pooled connection could not be retrieved
+ */
+ Connection getPooledConnection() throws SQLException;
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
index 6caba36..8e869e4 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.database;
import static org.testng.AssertJUnit.assertEquals;
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
index a81d998..2d388b6 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
@@ -1,66 +1,97 @@
package org.rhq.plugins.database;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Tests using the H2Database.
*/
-public class H2Database implements DatabaseComponent<ResourceComponent<?>> {
+public class H2Database implements DatabaseComponent<ResourceComponent<?>>, ConnectionPoolingSupport {
+ private static final Log LOG = LogFactory.getLog(H2Database.class);
+
+ static final String DRIVER_CLASS_PROPERTY = "driverClass";
+ static final String URL_PROPERTY = "url";
+ static final String USERNAME_PROPERTY = "username";
+ static final String PASSWORD_PROPERTY = "password";
- private Log log = LogFactory.getLog(this.getClass());
protected ResourceContext resourceContext;
+ @Deprecated
private Connection connection;
- private Configuration configuration;
+ private H2PooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- this.configuration = resourceContext.getPluginConfiguration();
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new H2PooledConnectionProvider(resourceContext.getPluginConfiguration());
}
public void stop() {
- removeConnection();
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(connection);
+ connection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public AvailabilityType getAvailability() {
- Connection conn = getConnection();
- AvailabilityType result = AvailabilityType.DOWN;
- if (conn != null) {
- result = AvailabilityType.UP;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
- return result;
-
}
public Connection getConnection() {
+ buildSharedConnectionIfNeeded();
+ return connection;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
try {
- if (this.connection == null || connection.isClosed()) {
- this.connection = buildConnection();
+ if ((connection == null) || connection.isClosed()) {
+ connection = buildConnection(resourceContext.getPluginConfiguration());
}
} catch (SQLException e) {
- log.info("Unable to create connection", e);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
- return this.connection;
}
- @Override
public void removeConnection() {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
- private Connection buildConnection() throws SQLException {
- String driverClass = configuration.getSimpleValue("driverClass", "org.h2.Driver");
+ static Connection buildConnection(Configuration pluginConfig) throws SQLException {
+ String driverClass = pluginConfig.getSimpleValue(DRIVER_CLASS_PROPERTY, "org.h2.Driver");
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
@@ -68,12 +99,13 @@ public class H2Database implements DatabaseComponent<ResourceComponent<?>> {
+ ") not found.");
}
- String url = configuration.getSimpleValue("url", "jdbc:h2:test");
- String username = configuration.getSimpleValue("username", "sa");
- String password = configuration.getSimpleValue("password", "");
- log.debug("Attempting JDBC connection to [" + url + "]");
+ String url = pluginConfig.getSimpleValue(URL_PROPERTY, "jdbc:h2:test");
+ String username = pluginConfig.getSimpleValue(USERNAME_PROPERTY, "sa");
+ String password = pluginConfig.getSimpleValue(PASSWORD_PROPERTY, "");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempting JDBC connection to [" + url + "]");
+ }
return DriverManager.getConnection(url, username, password);
}
-
}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
index dbce116..97e9d41 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
@@ -1,9 +1,29 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.database;
import java.util.Collections;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java
new file mode 100644
index 0000000..9d414c3
--- /dev/null
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java
@@ -0,0 +1,59 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import static org.rhq.plugins.database.H2Database.DRIVER_CLASS_PROPERTY;
+import static org.rhq.plugins.database.H2Database.PASSWORD_PROPERTY;
+import static org.rhq.plugins.database.H2Database.URL_PROPERTY;
+import static org.rhq.plugins.database.H2Database.USERNAME_PROPERTY;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+
+/**
+ * @author Thomas Segismont
+ */
+class H2PooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public H2PooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimpleValue(DRIVER_CLASS_PROPERTY, "org.h2.Driver"));
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return pluginConfig.getSimpleValue(URL_PROPERTY, "jdbc:h2:test");
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(USERNAME_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(PASSWORD_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java
new file mode 100644
index 0000000..496e654
--- /dev/null
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java
@@ -0,0 +1,17 @@
+package org.rhq.plugins.database;
+
+/**
+ * @author Thomas Segismont
+ */
+public class NonPoolingCustomTableComponent extends CustomTableComponent {
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return false;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return null;
+ }
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
index 4ce8107..45bac41 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,40 +13,94 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.getGridValues;
+import static org.testng.Assert.assertEquals;
+
import java.sql.Connection;
+import java.sql.SQLException;
import java.util.List;
import java.util.Map;
+import org.testng.annotations.Test;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.resource.ResourceType;
-import org.testng.annotations.Test;
-import org.testng.AssertJUnit;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
@Test
public class PluginTest extends ComponentTest {
public void test() throws Exception {
- H2Database db = (H2Database)manuallyAdd("H2 Database");
+
+ H2Database db = (H2Database) manuallyAdd("H2 Database");
assertUp(db);
- Connection connection = db.getConnection();
- connection.prepareStatement("create table sometable(a int, b int)").execute();
- connection.prepareStatement("insert into sometable values(42, 54)").execute();
- ResourceType rt = resourceTypes.get("Generic Query");
+
+ setupData(db);
+
+ // Pooling < Non Pooling < Pooling hierarchy
+
+ ResourceComponent level1 = add("Generic Query", db);
+ assertUp(level1);
+ checkAllMetrics("Generic Query", level1);
+
+ ResourceComponent level2 = add("Generic Query Non Pooling", level1);
+ assertUp(level2);
+ checkAllMetrics("Generic Query Non Pooling", level2);
+
+ ResourceComponent level3 = add("Nested Generic Query", level2);
+ assertUp(level3);
+ checkAllMetrics("Generic Query Non Pooling", level3);
+
+ checkData(db);
+ }
+
+ private ResourceComponent add(String resourceType, ResourceComponent component) throws Exception {
+ ResourceType rt = resourceTypes.get(resourceType);
Configuration configuration = getConfiguration(rt);
- CustomTableComponent ctc = (CustomTableComponent) manuallyAdd(rt, configuration, db);
- MeasurementReport report = getMeasurementReport(ctc);
- assertAll(report, getResourceDescriptor("Generic Query"));
- List<Map<String, Object>> grid = DatabaseQueryUtility.getGridValues(db, "select a, b from sometable");
- assert grid.size() == 1;
- Map<String, Object> map = grid.get(0);
- AssertJUnit.assertEquals(42, map.get("A"));
+ return manuallyAdd(rt, configuration, component);
+ }
+
+ private void checkAllMetrics(String resourceType, ResourceComponent component) throws Exception {
+ MeasurementReport report = getMeasurementReport(component);
+ assertAll(report, getResourceDescriptor(resourceType));
+ }
+
+ private void setupData(H2Database db) throws SQLException {
+ Connection connection = null;
+ try {
+ connection = db.getPooledConnectionProvider().getPooledConnection();
+ connection.prepareStatement("create table table_a(a int, b int)").execute();
+ connection.prepareStatement("insert into table_a values(1, 2)").execute();
+ connection.prepareStatement("create table table_b(a int, b int)").execute();
+ connection.prepareStatement("insert into table_b values(3, 4)").execute();
+ connection.prepareStatement("create table table_c(a int, b int)").execute();
+ connection.prepareStatement("insert into table_c values(5, 6)").execute();
+ } finally {
+ DatabasePluginUtil.safeClose(connection);
+ }
+ }
+
+ private void checkData(H2Database db) throws SQLException {
+ List<Map<String, Object>> grid = getGridValues(db, "select a, b from table_a");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 1);
+ assertEquals(grid.get(0).get("B"), 2);
+ grid = getGridValues(db, "select a, b from table_b");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 3);
+ assertEquals(grid.get(0).get("B"), 4);
+ grid = getGridValues(db, "select a, b from table_c");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 5);
+ assertEquals(grid.get(0).get("B"), 6);
+ System.out.println("grid = " + grid);
}
}
diff --git a/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml b/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
index 1e8f7d1..79f6413 100644
--- a/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
@@ -5,43 +5,89 @@
description="Plugin supporting H2 database"
package="org.rhq.plugins.database"
pluginLifecycleListener="DatabasePluginLifecycleListener"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="urn:xmlns:rhq-configuration"
xmlns="urn:xmlns:rhq-plugin">
- <depends plugin="Database" useClasses="true"/>
+ <depends plugin="Database" useClasses="true"/>
- <server name="H2 Database" class="org.rhq.plugins.database.H2Database" discovery="org.rhq.plugins.database.H2DatabaseDiscovery">
- <plugin-configuration>
- <c:simple-property name="url" type="string" default="jdbc:h2:mem:"/>
- <c:simple-property name="username" type="string" default="sa"/>
- <c:simple-property name="password" type="string" default=""/>
- </plugin-configuration>
+ <server name="H2 Database" class="org.rhq.plugins.database.H2Database"
+ discovery="org.rhq.plugins.database.H2DatabaseDiscovery">
+ <plugin-configuration>
+ <c:simple-property name="url" type="string" default="jdbc:h2:mem:testdb"/>
+ <c:simple-property name="username" type="string" default="sa"/>
+ <c:simple-property name="password" type="string" default=""/>
+ </plugin-configuration>
- </server>
+ </server>
- <service name="Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
- discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
- description="Query the database for various results"
- supportsManualAdd="true" singleton="false"
- createDeletePolicy="both">
+ <service name="Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
- <runs-inside>
- <parent-resource-type name="H2 Database" plugin="H2"/>
- </runs-inside>
+ <runs-inside>
+ <parent-resource-type name="H2 Database" plugin="H2"/>
+ </runs-inside>
- <plugin-configuration>
- <c:simple-property name="metricQuery" type="string" default="select a, b from sometable"/>
- <c:simple-property name="column" type="boolean" default="true"/>
- </plugin-configuration>
- <metric property="A" displayName="A results" displayType="summary"
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_a"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_a"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
description="Number appearing in A column"
units="none" dataType="measurement"/>
- <metric property="B" displayName="B results" displayType="summary"
+ <metric property="B" displayName="B results" displayType="summary"
description="Number appearing in B column"
units="none" dataType="measurement"/>
- </service>
+ </service>
+ <service name="Generic Query Non Pooling" class="org.rhq.plugins.database.NonPoolingCustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
+
+ <runs-inside>
+ <parent-resource-type name="Generic Query" plugin="H2"/>
+ </runs-inside>
+
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_b"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_b"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
+ description="Number appearing in A column"
+ units="none" dataType="measurement"/>
+ <metric property="B" displayName="B results" displayType="summary"
+ description="Number appearing in B column"
+ units="none" dataType="measurement"/>
+ </service>
+
+ <service name="Nested Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
+
+ <runs-inside>
+ <parent-resource-type name="Generic Query Non Pooling" plugin="H2"/>
+ </runs-inside>
+
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_c"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_c"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
+ description="Number appearing in A column"
+ units="none" dataType="measurement"/>
+ <metric property="B" displayName="B results" displayType="summary"
+ description="Number appearing in B column"
+ units="none" dataType="measurement"/>
+ </service>
</plugin>
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
index 9d7169e..3cd2401 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
@@ -43,78 +47,104 @@ import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.system.AggregateProcessInfo;
import org.rhq.core.system.ProcessInfo;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
* @author Steve Millidge
*/
-public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
+public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>, ConnectionPoolingSupport,
ResourceComponent<ResourceComponent<?>>, MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(MySqlComponent.class);
+
private ResourceContext resourceContext;
private AggregateProcessInfo aggregateProcessInfo;
- private MySqlConnectionInfo info;
- private Log log = LogFactory.getLog(this.getClass());
private Map<String, String> globalStatusValues = new HashMap<String, String>();
private Map<String, String> globalVariables = new HashMap<String, String>();
+ private MySqlPooledConnectionProvider pooledConnectionProvider;
+ @Deprecated
+ private Connection sharedConnection;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- info = MySqlDiscoveryComponent.buildConnectionInfo(resourceContext.getPluginConfiguration());
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new MySqlPooledConnectionProvider(resourceContext.getPluginConfiguration());
ProcessInfo processInfo = resourceContext.getNativeProcess();
if (processInfo != null) {
aggregateProcessInfo = processInfo.getAggregateProcessTree();
} else {
- //findProcessInfo();
- //log.debug("Unable to locate native process information. Process level statistics will be unavailable.");
+ findProcessInfo();
+ }
+ }
+
+ private void buildSharedConnectionIfNeeded() {
+ try {
+ if ((sharedConnection == null) || sharedConnection.isClosed()) {
+ sharedConnection = MySqlDiscoveryComponent.buildConnection(this.resourceContext
+ .getPluginConfiguration());
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
}
public void stop() {
- MySqlConnectionManager.getConnectionManager().closeConnection(info);
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(sharedConnection);
+ sharedConnection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ aggregateProcessInfo = null;
}
- public AvailabilityType getAvailability() {
- if (log.isDebugEnabled()) {
- log.debug("Doing an availability check on " + info.buildURL());
- }
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
- Connection conn = getConnection();
- AvailabilityType result = AvailabilityType.DOWN;
- if (conn != null) {
- // the connection must be OK as the validity check will have worked
- result = AvailabilityType.UP;
- }
- if (log.isDebugEnabled()) {
- log.debug("Availability check on " + info.buildURL() + " gives " + result);
- }
- return result;
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
+ }
+ public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Connection conn = getConnection();
- if (conn != null) {
- ResultSet rs = null;
- Statement stmt = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("SHOW GLOBAL STATUS");
- while (rs.next()) {
- globalStatusValues.put(rs.getString(1), rs.getString(2));
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW GLOBAL STATUS");
+ while (resultSet.next()) {
+ globalStatusValues.put(resultSet.getString(1), resultSet.getString(2));
+ }
- rs.close();
- rs = stmt.executeQuery("select * from information_schema.global_variables");
- while (rs.next()) {
- globalVariables.put(rs.getString(1), rs.getString(2));
- }
- } catch (SQLException sqle) {
- } finally {
- DatabaseQueryUtility.close(stmt, rs);
+ resultSet.close();
+ resultSet = statement.executeQuery("select * from information_schema.global_variables");
+ while (resultSet.next()) {
+ globalVariables.put(resultSet.getString(1), resultSet.getString(2));
}
+ } catch (SQLException ignore) {
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
// get process information
@@ -154,7 +184,7 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
String strVal = globalStatusValues.get(request.getName());
double val = Double.parseDouble(strVal);
report.addData(new MeasurementDataNumeric(request, val));
- } catch (Exception e) {
+ } catch (Exception ignore) {
}
}
}
@@ -162,17 +192,14 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
}
public Connection getConnection() {
- try {
- return MySqlConnectionManager.getConnectionManager().getConnection(info);
- } catch (SQLException ex) {
- log.warn("Unable to obtain database connection ", ex);
- return null;
- }
+ buildSharedConnectionIfNeeded();
+ return sharedConnection;
}
@Override
public void removeConnection() {
- MySqlConnectionManager.getConnectionManager().closeConnection(info);
+ DatabasePluginUtil.safeClose(this.sharedConnection);
+ this.sharedConnection = null;
}
private AggregateProcessInfo findProcessInfo() {
@@ -212,7 +239,7 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
pidFileReader.close();
}
} catch (Exception ex) {
- log.warn("Unable to read MySQL pid file " + pidFile);
+ LOG.warn("Unable to read MySQL pid file " + pidFile);
}
}
return result;
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java
deleted file mode 100644
index 4fa0a4f..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java
+++ /dev/null
@@ -1,92 +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.plugins.mysql;
-
-/**
- * A class to act as a key to a specific MySQL connection
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionInfo {
-
- private String host;
- private String port;
- private String db;
- private String user;
- private String password;
- private int hashCode;
-
- MySqlConnectionInfo(String host, String port, String db, String user, String password ) {
- this.host = host;
- this.port = port;
- this.db = db;
- this.user = user;
- this.password = password;
- this.hashCode = new StringBuilder().append(host).
- append(port).
- append(db).
- append(user).
- append(password).toString().hashCode();
-
- }
-
- public String getDb() {
- return db;
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- public String getHost() {
- return host;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getPort() {
- return port;
- }
-
- public String getUser() {
- return user;
- }
-
- public String buildURL() {
- return new StringBuilder().append("jdbc:mysql://")
- .append(host)
- .append(":")
- .append(port)
- .append("/")
- .append(db).toString();
- }
-
- @Override
- public boolean equals(Object other) {
- boolean result = false;
- if ((other instanceof MySqlConnectionInfo) && (other.hashCode() == this.hashCode())) {
- result = true;
- }
- return result;
- }
-
-}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
deleted file mode 100644
index 6c81332..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ /dev/null
@@ -1,125 +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.plugins.mysql;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.HashMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A class to manage the connections to MySQL
- * This class keeps a cache of connections to MySQL and reuses them on demand
- * We assume single threaded access to the Connection in the agent
- * this will need to be reworked if that assumption is not correct
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionManager {
-
- private HashMap<MySqlConnectionInfo, Connection> connections;
- private static MySqlConnectionManager singleton;
- private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
-
- private MySqlConnectionManager() {
- connections = new HashMap<MySqlConnectionInfo,Connection>();
- }
-
- static MySqlConnectionManager getConnectionManager() {
- if (singleton == null) {
- singleton = new MySqlConnectionManager();
- }
- return singleton;
- }
-
- public void shutdown() {
- Driver driver = null;
- for (Connection conn : connections.values()) {
- try {
- if (driver == null) {
- String driverName = conn.getMetaData().getDriverName();
- driver = DriverManager.getDriver(driverName);
- }
- conn.close();
- }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
- }
- // deregister driver as well
- if (driver != null) {
- try {
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- logger.warn("Unable to deregister MySQL Driver on shutdown");
- }
- }
- }
-
- void closeConnection(MySqlConnectionInfo info) {
- Connection conn = connections.get(info);
- if (conn != null) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Closing Connection to " + info.buildURL());
- }
- conn.close();
- } catch (SQLException e) {
- logger.warn("Problem closing connection to " + info.buildURL() + " on close");
- }
- }
- connections.remove(info);
- }
-
- Connection getConnection (MySqlConnectionInfo info) throws SQLException {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
-
- Connection conn = connections.get(info);
- String url = info.buildURL();
- if (conn == null) {
- if (logger.isInfoEnabled()) {
- logger.info("Attemping connection to " + url);
- }
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- if (logger.isInfoEnabled()) {
- logger.info("Successfully connected to " + url);
- }
- connections.put(info, conn);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Reusing existing connection to " + url);
- }
- }
-
- // check the validity of the connection
- if (!conn.isValid(0)) {
- // attempt a single reconnect here and now
- conn.close();
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- connections.put(info, conn);
- logger.info("Refreshed a connection to " + url);
- }
- return conn;
- }
-
-}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
index 43747bc..be436af 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,40 +13,45 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-package org.rhq.plugins.mysql;
+package org.rhq.plugins.mysql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.availability.AvailabilityFacet;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
-import org.rhq.core.domain.configuration.PropertySimple;
-import org.rhq.core.domain.measurement.AvailabilityType;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlDatabaseComponent implements DatabaseComponent, AvailabilityFacet, OperationFacet {
+public class MySqlDatabaseComponent implements DatabaseComponent, ConnectionPoolingSupport, AvailabilityFacet,
+ OperationFacet {
+
+ private static final Log LOG = LogFactory.getLog(MySqlDatabaseComponent.class);
private ResourceContext resourceContext;
private MySqlComponent parent;
private String databaseName;
- private static Log log = LogFactory.getLog(MySqlDatabaseComponent.class);
@Override
public Connection getConnection() {
@@ -59,109 +64,120 @@ public class MySqlDatabaseComponent implements DatabaseComponent, AvailabilityFa
}
@Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
+ }
+
+ @Override
public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
resourceContext = rc;
databaseName = rc.getResourceKey();
- parent = (MySqlComponent)resourceContext.getParentResourceComponent();
+ parent = (MySqlComponent) resourceContext.getParentResourceComponent();
}
- public String getName() { return databaseName; }
+ public String getName() {
+ return databaseName;
+ }
@Override
public void stop() {
+ resourceContext = null;
+ databaseName = null;
+ parent = null;
}
@Override
public AvailabilityType getAvailability() {
AvailabilityType result = AvailabilityType.DOWN;
- if (log.isDebugEnabled()) {
- log.debug("Availability check for " + databaseName);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Availability check for " + databaseName);
}
- Connection conn = getConnection();
- if (conn != null) {
- Statement statement = null;
- ResultSet resultSet = null;
- try {
- statement = conn.createStatement();
- resultSet = statement.executeQuery("SHOW DATABASES LIKE '" + databaseName + "'");
- if (resultSet.next()) {
- if (resultSet.getString(1).equalsIgnoreCase(databaseName)) {
- result = AvailabilityType.UP;
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW DATABASES LIKE '" + databaseName + "'");
+ if (resultSet.next()) {
+ if (resultSet.getString(1).equalsIgnoreCase(databaseName)) {
+ result = AvailabilityType.UP;
}
- }catch(SQLException e) {
- if (log.isDebugEnabled()) {
- log.debug("Got Exception when determining database availability",e);
- }
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
}
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Got Exception when determining database availability", e);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return result;
}
@Override
- public OperationResult invokeOperation(String name, Configuration parameters)
- throws InterruptedException, Exception {
-
+ public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
+ Exception {
if ("invokeSql".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- String sql = parameters.getSimple("sql").getStringValue();
- OperationResult result = new OperationResult();
-
- if (parameters.getSimple("type").getStringValue().equals("update")) {
- int updateCount = stmt.executeUpdate(sql);
- result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
-
- } else {
- rs = stmt.executeQuery(parameters.getSimple("sql").getStringValue());
-
- ResultSetMetaData md = rs.getMetaData();
- StringBuilder buf = new StringBuilder();
- int rowCount = 0;
-
- buf.append("<table>");
- buf.append("<th>");
+ return invokeSql(parameters);
+ } else {
+ throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ }
+ }
+
+ private OperationResult invokeSql(Configuration parameters) throws SQLException {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = getConnection().createStatement();
+ String sql = parameters.getSimple("sql").getStringValue();
+ OperationResult result = new OperationResult();
+
+ if (parameters.getSimple("type").getStringValue().equals("update")) {
+ int updateCount = statement.executeUpdate(sql);
+ result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+
+ } else {
+ resultSet = statement.executeQuery(parameters.getSimple("sql").getStringValue());
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ StringBuilder buf = new StringBuilder();
+ int rowCount = 0;
+
+ buf.append("<table>");
+ buf.append("<th>");
+ for (int i = 1; i <= md.getColumnCount(); i++) {
+ buf.append("<td>");
+ buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append("</td>");
+ }
+ buf.append("</th>");
+
+ while (resultSet.next()) {
+ rowCount++;
+ buf.append("<tr>");
for (int i = 1; i <= md.getColumnCount(); i++) {
buf.append("<td>");
- buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append(resultSet.getString(i));
buf.append("</td>");
}
- buf.append("</th>");
-
-
- while (rs.next()) {
- rowCount++;
- buf.append("<tr>");
- for (int i = 1; i <= md.getColumnCount(); i++) {
- buf.append("<td>");
- buf.append(rs.getString(i));
- buf.append("</td>");
- }
- buf.append("</tr>");
- }
-
- buf.append("</table>");
- result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
- result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
- }
- return result;
- } finally {
- if (rs != null) {
- rs.close();
+ buf.append("</tr>");
}
- if (stmt != null) {
- stmt.close();
- }
+ buf.append("</table>");
+ result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
+ result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
}
- } else {
- throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
}
-
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
index 100e536..8356d56 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,78 +13,63 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-package org.rhq.plugins.mysql;
-import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
-import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
-import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+package org.rhq.plugins.mysql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
* @author Steve Millidge
*/
public class MySqlDatabaseDiscoveryComponent implements ResourceDiscoveryComponent<MySqlComponent> {
-
- private Log logger = LogFactory.getLog(this.getClass());
+ private static final Log LOG = LogFactory.getLog(MySqlDatabaseDiscoveryComponent.class);
@Override
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<MySqlComponent> context) {
-
- if (logger.isDebugEnabled()) {
- logger.debug("Database discovery started");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Database discovery started");
}
- Set<DiscoveredResourceDetails> databases = new LinkedHashSet<DiscoveredResourceDetails>();
- Connection connection = context.getParentResourceComponent().getConnection();
-
-
+ Connection jdbcConnection = null;
Statement statement = null;
ResultSet resultSet = null;
- if (connection != null) {
- try {
- statement = connection.createStatement();
- resultSet = statement.executeQuery("SHOW DATABASES");
-
- while (resultSet.next()) {
- String databaseName = resultSet.getString(1);
- Configuration config = context.getDefaultPluginConfiguration();
- config.put(new PropertySimple("databaseName",databaseName));
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- context.getResourceType(),
- databaseName,
- databaseName + " Database",
- null,
- "A MySql Database",
- config,
- null);
- databases.add(details);
- }
-
- } catch (SQLException e) {
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
- }
- } else {
- if (logger.isInfoEnabled()) {
- logger.info("No connection to MySQL obtained from connection manager");
+ try {
+ jdbcConnection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW DATABASES");
+ Set<DiscoveredResourceDetails> databases = new LinkedHashSet<DiscoveredResourceDetails>();
+ while (resultSet.next()) {
+ String databaseName = resultSet.getString(1);
+ Configuration config = context.getDefaultPluginConfiguration();
+ config.put(new PropertySimple("databaseName", databaseName));
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(context.getResourceType(),
+ databaseName, databaseName + " Database", null, "A MySql Database", config, null);
+ databases.add(details);
}
+ return databases;
+ } catch (SQLException ignore) {
+ return Collections.emptySet();
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
-
- return databases;
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
index c4ada90..30ccef4 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,28 +13,31 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
@@ -42,7 +45,7 @@ import java.util.Set;
* @author Steve Millidge
*/
public class MySqlDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
- private static final Log log = LogFactory.getLog(MySqlDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(MySqlDiscoveryComponent.class);
public static final String HOST_CONFIGURATION_PROPERTY = "host";
public static final String PORT_CONFIGURATION_PROPERTY = "port";
@@ -52,81 +55,83 @@ public class MySqlDiscoveryComponent implements ResourceDiscoveryComponent, Manu
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext context) {
- if (log.isDebugEnabled()) {
- log.debug("Resource Discovery Started");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Resource Discovery Started");
}
Set<DiscoveredResourceDetails> servers = new LinkedHashSet<DiscoveredResourceDetails>();
// Process any auto-discovered resources.
List<ProcessScanResult> autoDiscoveryResults = context.getAutoDiscoveredProcesses();
for (ProcessScanResult result : autoDiscoveryResults) {
- log.info("Discovered a mysql process: " + result);
+ LOG.info("Discovered a mysql process: " + result);
ProcessInfo procInfo = result.getProcessInfo();
- servers.add(createResourceDetails(context,context.getDefaultPluginConfiguration(),procInfo));
+ servers.add(createResourceDetails(context, context.getDefaultPluginConfiguration(), procInfo));
}
return servers;
}
public DiscoveredResourceDetails discoverResource(Configuration pluginConfiguration,
- ResourceDiscoveryContext resourceDiscoveryContext)
- throws InvalidPluginConfigurationException {
+ ResourceDiscoveryContext resourceDiscoveryContext) throws InvalidPluginConfigurationException {
ProcessInfo processInfo = null;
- DiscoveredResourceDetails resourceDetails = createResourceDetails(resourceDiscoveryContext, pluginConfiguration,
- processInfo);
+ DiscoveredResourceDetails resourceDetails = createResourceDetails(resourceDiscoveryContext,
+ pluginConfiguration, processInfo);
return resourceDetails;
}
protected static DiscoveredResourceDetails createResourceDetails(ResourceDiscoveryContext discoveryContext,
- Configuration pluginConfiguration,
- ProcessInfo processInfo) throws InvalidPluginConfigurationException {
+ Configuration pluginConfig, ProcessInfo processInfo) throws InvalidPluginConfigurationException {
- MySqlConnectionInfo ci = buildConnectionInfo(pluginConfiguration);
- Connection conn;
+ Connection conn = null;
String version = "";
try {
- conn = MySqlConnectionManager.getConnectionManager().getConnection(ci);
+ conn = buildConnection(pluginConfig);
version = conn.getMetaData().getDatabaseProductVersion();
} catch (SQLException ex) {
// ignore so we can still add to the inventory even though we can't currently connect
+ } finally {
+ DatabasePluginUtil.safeClose(conn);
}
- String key = new StringBuilder().append("MySql:")
- .append(ci.getDb())
- .append(":")
- .append(ci.getHost())
- .append(":")
- .append(ci.getPort())
- .append("-")
- .append(ci.getUser()).toString();
+ String key = new StringBuilder().append("MySql:")
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue()).append("-")
+ .append(pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue()).toString();
String name = new StringBuilder().append("MySql [")
- .append(ci.getDb())
- .append("]").toString();
-
- DiscoveredResourceDetails result = new DiscoveredResourceDetails(
- discoveryContext.getResourceType(),
- key,
- name,
- version,
- "MySql Server",
- pluginConfiguration,
- processInfo);
-
- if (log.isDebugEnabled()) {
- log.debug("Discovered Database Server for MySQL Database " + ci.buildURL());
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).append("]").toString();
+
+ DiscoveredResourceDetails result = new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name,
+ version, "MySql Server", pluginConfig, processInfo);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovered Database Server for MySQL Database " + buildConnectionURL(pluginConfig));
}
return result;
}
- static MySqlConnectionInfo buildConnectionInfo(Configuration configuration) {
- // build the Discovered Resource from the configuration
- String host = configuration.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue();
- String port = configuration.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue();
- String user = configuration.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
- String pass = configuration.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
- String db = configuration.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue();
- return new MySqlConnectionInfo(host, port, db, user, pass);
- }
+ static String buildConnectionURL(Configuration pluginConfig) {
+ return new StringBuilder().append("jdbc:mysql://")
+ .append(pluginConfig.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue()).append("/")
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).toString();
+ }
+
+ static Connection buildConnection(Configuration pluginConfig) throws SQLException {
+ String driverClass = "com.mysql.jdbc.Driver";
+ try {
+ Class.forName(driverClass);
+ } catch (ClassNotFoundException e) {
+ throw new InvalidPluginConfigurationException("Specified JDBC driver class (" + driverClass
+ + ") not found.");
+ }
+
+ String url = buildConnectionURL(pluginConfig);
+ String principal = pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ String credentials = pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+
+ return DriverManager.getConnection(url, principal, credentials);
+ }
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
index a731aed..db4580e 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,34 +13,50 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.util.Enumeration;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.pluginapi.plugin.PluginContext;
import org.rhq.core.pluginapi.plugin.PluginLifecycleListener;
+import org.rhq.core.util.exception.ThrowableUtil;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlPluginLifecycleListener implements PluginLifecycleListener {
- private final Log log = LogFactory.getLog(MySqlPluginLifecycleListener.class);
- private String pluginName;
+ private static final Log LOG = LogFactory.getLog(MySqlPluginLifecycleListener.class);
public void initialize(PluginContext context) throws Exception {
- pluginName = context.getPluginName();
}
public void shutdown() {
- if (log.isDebugEnabled()) {
- log.debug(new StringBuilder().append(pluginName).append(" Plugin Shutdown").toString());
+ // so we do not cause our classloader to leak perm gen, we need to de-register
+ // any and all JDBC drivers this plugin registered
+ Enumeration<Driver> drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ try {
+ Driver driver = drivers.nextElement();
+ DriverManager.deregisterDriver(driver);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Deregistered JDBC driver: " + driver.getClass());
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this.getClass().getSimpleName() + " completed shutdown.");
}
- MySqlConnectionManager.getConnectionManager().shutdown();
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java
new file mode 100644
index 0000000..56f5782
--- /dev/null
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java
@@ -0,0 +1,61 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.mysql;
+
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.CREDENTIALS_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.PRINCIPAL_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.buildConnectionURL;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * A MySql plugin adapted {@link org.rhq.plugins.database.PooledConnectionProvider}.
+ *
+ * @author Thomas Segismont
+ */
+public class MySqlPooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public MySqlPooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName("com.mysql.jdbc.Driver");
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return buildConnectionURL(pluginConfig);
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
index 73354f9..ecbde73 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,18 +13,24 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementDataTrait;
@@ -33,19 +39,20 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlTableComponent implements DatabaseComponent, MeasurementFacet {
+public class MySqlTableComponent implements DatabaseComponent, ConnectionPoolingSupport, MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(MySqlTableComponent.class);
private String tableName;
private MySqlDatabaseComponent parent;
private String databaseName;
- private Log log = LogFactory.getLog(this.getClass());
@Override
public Connection getConnection() {
@@ -58,72 +65,85 @@ public class MySqlTableComponent implements DatabaseComponent, MeasurementFacet
}
@Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
+ }
+
+ @Override
public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
tableName = rc.getResourceKey();
- parent = (MySqlDatabaseComponent)rc.getParentResourceComponent();
+ parent = (MySqlDatabaseComponent) rc.getParentResourceComponent();
databaseName = parent.getName();
}
@Override
public void stop() {
+ tableName = null;
+ parent = null;
+ databaseName = null;
}
@Override
public AvailabilityType getAvailability() {
- AvailabilityType result = AvailabilityType.DOWN;
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show tables from " + databaseName + " like '" + tableName + "'");
- if (rs.first()) {
- result = AvailabilityType.UP;
- }
- }catch (SQLException se) {
- // ignore as unablailable if we can't execute the query
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show tables from " + databaseName + " like '" + tableName + "'");
+ if (resultSet.first()) {
+ return UP;
}
+ } catch (SQLException se) {
+ // Will return down
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- return result;
+ return DOWN;
}
@Override
public void getValues(MeasurementReport mr, Set<MeasurementScheduleRequest> set) throws Exception {
- Connection conn = parent.getConnection();
- if (conn != null ) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show table status from " + databaseName+ " like '" + tableName + "'");
- if (rs.next()) {
- for (MeasurementScheduleRequest request : set) {
- String value = rs.getString(request.getName());
- if (value == null) {value = "0";}
- switch (request.getDataType()) {
- case MEASUREMENT: {
- mr.addData(new MeasurementDataNumeric(request, Double.valueOf(value)));
- break;
- } case TRAIT: {
- mr.addData(new MeasurementDataTrait(request, value));
- break;
- } default: {
- break;
- }
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show table status from " + databaseName + " like '" + tableName + "'");
+ if (resultSet.next()) {
+ for (MeasurementScheduleRequest request : set) {
+ String value = resultSet.getString(request.getName());
+ if (value == null) {
+ value = "0";
+ }
+ switch (request.getDataType()) {
+ case MEASUREMENT: {
+ mr.addData(new MeasurementDataNumeric(request, Double.valueOf(value)));
+ break;
+ }
+ case TRAIT: {
+ mr.addData(new MeasurementDataTrait(request, value));
+ break;
+ }
+ default: {
+ break;
+ }
}
}
- } catch(Exception se) {
- if (log.isInfoEnabled()) {
- log.info("Unable to measure table statistics", se);
- }
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
}
+ } catch (Exception se) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to measure table statistics", se);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
index 4d7b354..83535e2 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
import java.sql.Connection;
@@ -24,71 +25,66 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers MySQL tables.
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlTableDiscoveryComponent implements ResourceDiscoveryComponent {
+ private static final Log LOG = LogFactory.getLog(MySqlTableDiscoveryComponent.class);
private static final String TABLE_DISCOVERY = "tableDiscovery";
- private Log log = LogFactory.getLog(this.getClass());
@Override
public Set discoverResources(ResourceDiscoveryContext rdc) throws InvalidPluginConfigurationException, Exception {
-
- HashSet<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
- MySqlDatabaseComponent parent = (MySqlDatabaseComponent)rdc.getParentResourceComponent();
+ Set<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
+ MySqlDatabaseComponent parent = (MySqlDatabaseComponent) rdc.getParentResourceComponent();
Configuration pconfig = rdc.getParentResourceContext().getPluginConfiguration();
// If the user has disabled table discovery on the parent, we don't autodiscover
// them, as we may hit temporary ones that go away any time soon again
// See BZ-797356
if (!Boolean.parseBoolean(pconfig.getSimpleValue(TABLE_DISCOVERY, "true"))) {
- log.debug("table discovery disabled");
+ LOG.debug("table discovery disabled");
return set;
}
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show tables from " + parent.getName());
- while (rs.next()) {
- String tableName = rs.getString(1);
- if (log.isDebugEnabled()) {
- log.debug("Discovered Table "+ tableName);
- }
- Configuration config = new Configuration();
- config.put(new PropertySimple("tableName",tableName));
- DiscoveredResourceDetails details = new DiscoveredResourceDetails(
- rdc.getResourceType(),
- tableName,
- tableName + " Table",
- null,
- tableName + " MySql Table", config, null);
- set.add(details);
- }
- } catch(SQLException se) {
- if (log.isDebugEnabled()) {
- log.debug("Unable to Discover Tables",se);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = parent.getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show tables from " + parent.getName());
+ while (resultSet.next()) {
+ String tableName = resultSet.getString(1);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovered Table " + tableName);
}
-
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
+ Configuration config = new Configuration();
+ config.put(new PropertySimple("tableName", tableName));
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(rdc.getResourceType(), tableName,
+ tableName + " Table", null, tableName + " MySql Table", config, null);
+ set.add(details);
+ }
+ } catch (SQLException se) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to Discover Tables", se);
}
+
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return set;
}
-
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
index 32525c2..d0ffb26 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,20 +13,24 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
@@ -34,20 +38,21 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent {
+public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent, ConnectionPoolingSupport {
+ private static final Log LOG = LogFactory.getLog(MySqlUserComponent.class);
private String userName;
private String host;
private MySqlComponent parent;
- private Log log = LogFactory.getLog(this.getClass());
- private ResourceContext context;
+ private ResourceContext resourceContext;
@Override
public Connection getConnection() {
@@ -60,69 +65,85 @@ public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent {
}
@Override
- public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
- parent = (MySqlComponent)rc.getParentResourceComponent();
- context = rc;
- userName = context.getPluginConfiguration().getSimple("userName").getStringValue();
- host = context.getPluginConfiguration().getSimple("host").getStringValue();
+ public boolean supportsConnectionPooling() {
+ return true;
}
@Override
- public void stop() {
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
}
+ @Override
+ public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
+ parent = (MySqlComponent) resourceContext.getParentResourceComponent();
+ this.resourceContext = resourceContext;
+ userName = this.resourceContext.getPluginConfiguration().getSimple("userName").getStringValue();
+ host = this.resourceContext.getPluginConfiguration().getSimple("host").getStringValue();
+ }
+
+ @Override
+ public void stop() {
+ parent = null;
+ resourceContext = null;
+ userName = null;
+ host = null;
+ }
public void getValues(MeasurementReport mr, Set<MeasurementScheduleRequest> requests) throws Exception {
- Connection conn = getConnection();
- ResultSet rs = null;
- Statement stmt = null;
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
int activeConnections = 0;
int totalConnections = 0;
try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select User,Host,State from information_schema.processlist where User='"+userName+"'");
- while(rs.next()) {
- String hostVal = rs.getString(2);
- String state = rs.getString(3);
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User,Host,State from information_schema.processlist where User='" + userName
+ + "'");
+ while (resultSet.next()) {
+ String hostVal = resultSet.getString(2);
+ String state = resultSet.getString(3);
if (hostVal.startsWith(host)) {
if (state.length() > 1) {
- activeConnections ++;
+ activeConnections++;
}
totalConnections++;
}
}
- }catch(SQLException sqle) {
-
+ } catch (SQLException ignore) {
} finally {
- DatabaseQueryUtility.close(stmt, rs);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
for (MeasurementScheduleRequest request : requests) {
if (request.getName().equals("TotalConnections")) {
- mr.addData(new MeasurementDataNumeric(request, new Double((double)totalConnections)));
+ mr.addData(new MeasurementDataNumeric(request, new Double((double) totalConnections)));
} else if (request.getName().equals("ActiveConnections")) {
- mr.addData(new MeasurementDataNumeric(request, new Double((double)activeConnections)));
+ mr.addData(new MeasurementDataNumeric(request, new Double((double) activeConnections)));
}
}
}
public AvailabilityType getAvailability() {
- AvailabilityType result = AvailabilityType.DOWN;
- Connection conn = getConnection();
- ResultSet rs = null;
- Statement stmt = null;
+ Connection jdbcConnection = null;
+ ResultSet resultSet = null;
+ Statement statement = null;
try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select User from mysql.user where User='"+userName+"' and Host='" + host +"'");
- if (rs.first()) {
- result = AvailabilityType.UP;
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User from mysql.user where User='" + userName + "' and Host='"
+ + host + "'");
+ if (resultSet.first()) {
+ return UP;
}
- }catch(SQLException sqle) {
-
+ } catch (SQLException sqle) {
+ // Will return DOWN
+ System.out.println("sqle = " + sqle);
} finally {
- DatabaseQueryUtility.close(stmt, rs);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- return result;
+ return DOWN;
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
index d05682a..9075b1c 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
import java.sql.Connection;
@@ -23,53 +24,44 @@ import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
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.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlUserDiscoveryComponent implements ResourceDiscoveryComponent {
public Set discoverResources(ResourceDiscoveryContext rdc) throws InvalidPluginConfigurationException, Exception {
- HashSet<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
+ Set<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
MySqlComponent parent = (MySqlComponent) rdc.getParentResourceComponent();
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement statement = null;
- ResultSet resultSet = null;
- try {
- statement = conn.createStatement();
- resultSet = statement.executeQuery("select User,Host from mysql.user");
- while (resultSet.next()) {
- String user = resultSet.getString(1);
- String host = resultSet.getString(2);
- String userName = user + "@" + host;
- Configuration config = new Configuration();
- config.put(new PropertySimple("userName",user));
- config.put(new PropertySimple("host",host));
- DiscoveredResourceDetails discoveredUser =
- new DiscoveredResourceDetails(
- rdc.getResourceType(),
- userName,
- userName,
- null,
- "A MySql User",
- config,
- null);
- set.add(discoveredUser);
- }
- } catch (Exception e) {
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = parent.getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User,Host from mysql.user");
+ while (resultSet.next()) {
+ String user = resultSet.getString(1);
+ String host = resultSet.getString(2);
+ String userName = user + "@" + host;
+ Configuration config = new Configuration();
+ config.put(new PropertySimple("userName", user));
+ config.put(new PropertySimple("host", host));
+ DiscoveredResourceDetails discoveredUser = new DiscoveredResourceDetails(rdc.getResourceType(),
+ userName, userName, null, "A MySql User", config, null);
+ set.add(discoveredUser);
}
+ } catch (Exception e) {
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return set;
}
diff --git a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
index 3b431d6..fdcce4a 100644
--- a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
+++ b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
diff --git a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
index 498fe22..90703ca 100644
--- a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
+++ b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,16 +13,17 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
+import org.testng.annotations.Test;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
-import org.testng.annotations.Test;
/**
* Tests MySql Server.
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
index 09e1a80..129b971 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -26,6 +30,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
@@ -33,9 +38,8 @@ import org.rhq.core.domain.measurement.MeasurementDataTrait;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle ASM Disk Group Component.
@@ -43,75 +47,72 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
@SuppressWarnings("rawtypes")
-public class OracleAsmDiskGroupComponent extends AbstractDatabaseComponent
- implements MeasurementFacet {
-
- private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$asm_diskgroup WHERE group_number = ? and STATE <> 'BROKEN'";
-
- private static final String SQL_VALUES = "SELECT GROUP_NUMBER, " + "NAME, "
- + "SECTOR_SIZE sectorSize, " + "BLOCK_SIZE blockSize, "
- + "ALLOCATION_UNIT_SIZE allocationUnitSize, " + "STATE state, "
- + "TYPE type, " + "TOTAL_MB totalMb, " + "FREE_MB freeMb, "
- + "((TOTAL_MB-FREE_MB)/TOTAL_MB) usedPercent, "
- + "REQUIRED_MIRROR_FREE_MB requiredMirrorFreeMb, "
- + "USABLE_FILE_MB usableFileMb, " + "OFFLINE_DISKS offlineDisks, "
- + "COMPATIBILITY compatibility, "
- + "DATABASE_COMPATIBILITY databaseCompatibility "
- + "FROM v$asm_diskgroup WHERE group_number = ?";
+public class OracleAsmDiskGroupComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleAsmDiskGroupComponent.class);
- private static Log log = LogFactory
- .getLog(OracleAsmDiskGroupComponent.class);
+ private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$asm_diskgroup WHERE group_number = ? and STATE <> 'BROKEN'";
+ private static final String SQL_VALUES = "SELECT GROUP_NUMBER, " + "NAME, " + "SECTOR_SIZE sectorSize, "
+ + "BLOCK_SIZE blockSize, " + "ALLOCATION_UNIT_SIZE allocationUnitSize, " + "STATE state, " + "TYPE type, "
+ + "TOTAL_MB totalMb, " + "FREE_MB freeMb, " + "((TOTAL_MB-FREE_MB)/TOTAL_MB) usedPercent, "
+ + "REQUIRED_MIRROR_FREE_MB requiredMirrorFreeMb, " + "USABLE_FILE_MB usableFileMb, "
+ + "OFFLINE_DISKS offlineDisks, " + "COMPATIBILITY compatibility, "
+ + "DATABASE_COMPATIBILITY databaseCompatibility " + "FROM v$asm_diskgroup WHERE group_number = ?";
- public AvailabilityType getAvailability() {
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
- statement.setString(1, this.resourceContext.getResourceKey());
- resultSet = statement.executeQuery();
- if (resultSet.next() && (resultSet.getInt(1) == 1)) {
- return AvailabilityType.UP;
- }
- } catch (SQLException e) {
- log.debug("unable to query", e);
- } finally {
- JDBCUtil.safeClose(statement, resultSet);
- }
+ public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
+ statement.setString(1, this.resourceContext.getResourceKey());
+ resultSet = statement.executeQuery();
+ if (resultSet.next() && (resultSet.getInt(1) == 1)) {
+ return AvailabilityType.UP;
+ }
+ } catch (SQLException e) {
+ LOG.debug("unable to query", e);
+ } finally {
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
- return AvailabilityType.DOWN;
- }
+ return AvailabilityType.DOWN;
+ }
- public void getValues(MeasurementReport report,
- Set<MeasurementScheduleRequest> metrics) throws Exception {
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- try {
- statement = this.getConnection().prepareStatement(SQL_VALUES);
- statement.setString(1, this.resourceContext.getResourceKey());
- resultSet = statement.executeQuery();
- if (resultSet.next()) {
- for (MeasurementScheduleRequest request : metrics) {
- String name = request.getName().toUpperCase(Locale.US);
- if (request.getDataType().equals(DataType.TRAIT)) {
- report.addData(new MeasurementDataTrait(request,
- resultSet.getString(name)));
- } else {
- try {
- report.addData(new MeasurementDataNumeric(request,
- resultSet.getDouble(name)));
- } catch (SQLException e) {
- // Ignoring metrics that cannot be read as a double
- log.warn("Ignoring metric " + name
- + " as it cannot be read as a double");
- }
- }
- }
- }
- } catch (SQLException e) {
- log.debug("Unable to read value", e);
- removeConnection();
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
- }
- }
-}
\ No newline at end of file
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
+ Connection jdbcConnection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_VALUES);
+ statement.setString(1, this.resourceContext.getResourceKey());
+ resultSet = statement.executeQuery();
+ if (resultSet.next()) {
+ for (MeasurementScheduleRequest request : metrics) {
+ String name = request.getName().toUpperCase(Locale.US);
+ if (request.getDataType().equals(DataType.TRAIT)) {
+ report.addData(new MeasurementDataTrait(request, resultSet.getString(name)));
+ } else {
+ try {
+ report.addData(new MeasurementDataNumeric(request, resultSet.getDouble(name)));
+ } catch (SQLException e) {
+ // Ignoring metrics that cannot be read as a double
+ LOG.warn("Ignoring metric " + name + " as it cannot be read as a double");
+ }
+ }
+ }
+ }
+ } catch (SQLException e) {
+ LOG.debug("Unable to read value", e);
+ } finally {
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
+ }
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
index c023b1b..f4b49ef 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -28,65 +32,66 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
-import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovery Oracle ASM Disk Groups.
*
* @author Richard Hensman
*/
-public class OracleAsmDiskGroupDiscoveryComponent implements ResourceDiscoveryComponent<DatabaseComponent<?>> {
-
- private final Log log = LogFactory.getLog(getClass());
+public class OracleAsmDiskGroupDiscoveryComponent implements ResourceDiscoveryComponent<ResourceComponent<?>> {
+ private static final Log LOG = LogFactory.getLog(OracleAsmDiskGroupDiscoveryComponent.class);
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
+ ResourceDiscoveryContext<ResourceComponent<?>> resourceDiscoveryContext)
throws InvalidPluginConfigurationException, Exception {
- Statement statement = null;
- ResultSet resultSet = null;
-
+
String table = "V$ASM_DISKGROUP";
String keyColumn = "GROUP_NUMBER";
String nameColumn = "NAME";
String description = "Oracle ASM Disk Groups";
-
- try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
- statement = conn.createStatement();
+ ResourceComponent<?> parentComponent = resourceDiscoveryContext.getParentResourceComponent();
+
+ Connection connection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnectionFromComponent(parentComponent);
+ statement = connection.createStatement();
resultSet = statement.executeQuery("SELECT * FROM " + table);
Configuration config = null;
Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>();
while (resultSet.next()) {
- config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ config = resourceDiscoveryContext.getDefaultPluginConfiguration();
String key = resultSet.getString(keyColumn);
String name = resultSet.getString(nameColumn);
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(),
- key,
- name,
- null,
- description, config, null);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(
+ resourceDiscoveryContext.getResourceType(), key, name, null, description, config, null);
found.add(details);
}
return found;
} catch (SQLException e) {
- log.debug("table " + table + " column " + keyColumn, e);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("table " + table + " column " + keyColumn, e);
+ }
} finally {
- JDBCUtil.safeClose(resultSet);
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection);
+ }
}
- return Collections.emptySet();
- }
+ return Collections.emptySet();
+ }
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
index 79d67fd..f3ab7b6 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
import java.sql.Connection;
@@ -23,26 +24,27 @@ import java.sql.DatabaseMetaData;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
-
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
*/
public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
- private static Log log = LogFactory.getLog(OracleDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(OracleDiscoveryComponent.class);
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext resourceDiscoveryContext)
throws InvalidPluginConfigurationException, Exception {
@@ -51,7 +53,7 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
for (ProcessScanResult process : autoDiscoveryResults) {
String sid = process.getProcessInfo().getEnvironmentVariable("ORACLE_SID");
if ((sid == null) || (sid.length() == 0)) {
- log.info("Unable to discover Oracle instance SID. Use manual inventory to complete setup.");
+ LOG.info("Unable to discover Oracle instance SID. Use manual inventory to complete setup.");
continue;
}
@@ -74,22 +76,21 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
}
public DiscoveredResourceDetails discoverResource(Configuration pluginConfig,
- ResourceDiscoveryContext resourceDiscoveryContext)
- throws InvalidPluginConfigurationException {
-
+ ResourceDiscoveryContext resourceDiscoveryContext) throws InvalidPluginConfigurationException {
+
Connection connection = null;
try {
connection = OracleServerComponent.buildConnection(pluginConfig);
DatabaseMetaData dbmd = connection.getMetaData();
String version = dbmd.getDatabaseMajorVersion() + "." + dbmd.getDatabaseMinorVersion();
- DiscoveredResourceDetails details = createResourceDetails(resourceDiscoveryContext, pluginConfig,
- version, null);
+ DiscoveredResourceDetails details = createResourceDetails(resourceDiscoveryContext, pluginConfig, version,
+ null);
return details;
} catch (Exception e) {
- log.warn("Could not connect to oracle with supplied configuration", e);
- throw new InvalidPluginConfigurationException("Unable to connect to Oracle",e);
+ LOG.warn("Could not connect to oracle with supplied configuration", e);
+ throw new InvalidPluginConfigurationException("Unable to connect to Oracle", e);
} finally {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(connection);
}
}
@@ -102,4 +103,4 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version, description,
pluginConfig, processInfo);
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
index 0446d7f..c1ef458 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,14 +32,14 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle Flash Recovery Area Component.
@@ -42,37 +47,38 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
public class OracleFlashRecoveryAreaComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleFlashRecoveryAreaComponent.class);
private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$recovery_file_dest WHERE name = ?";
- private static final String SQL_VALUES =
- "SELECT space_limit spaceLimit, space_used spaceUsed, space_reclaimable spaceReclaimable, number_of_files numberOfFiles, (space_used/space_limit) usedPercent " +
- "FROM v$recovery_file_dest WHERE name = ?";
-
-
- private static Log log = LogFactory.getLog(OracleFlashRecoveryAreaComponent.class);
+ private static final String SQL_VALUES = "SELECT space_limit spaceLimit, space_used spaceUsed, space_reclaimable spaceReclaimable, number_of_files numberOfFiles, (space_used/space_limit) usedPercent "
+ + "FROM v$recovery_file_dest WHERE name = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SQL_VALUES,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SQL_VALUES, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -80,4 +86,4 @@ public class OracleFlashRecoveryAreaComponent extends AbstractDatabaseComponent
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
index 488404f..a6e390b 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
import java.sql.Driver;
@@ -30,7 +31,7 @@ import org.rhq.core.pluginapi.plugin.PluginLifecycleListener;
import org.rhq.core.util.exception.ThrowableUtil;
public class OraclePluginLifecycleListener implements PluginLifecycleListener {
- private final Log log = LogFactory.getLog(OraclePluginLifecycleListener.class);
+ private static final Log LOG = LogFactory.getLog(OraclePluginLifecycleListener.class);
public void initialize(PluginContext context) throws Exception {
// no-op
@@ -44,13 +45,17 @@ public class OraclePluginLifecycleListener implements PluginLifecycleListener {
try {
Driver driver = drivers.nextElement();
DriverManager.deregisterDriver(driver);
- log.debug("Deregistered JDBC driver: " + driver.getClass());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Deregistered JDBC driver: " + driver.getClass());
+ }
} catch (Exception e) {
- log.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
+ LOG.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
}
}
- log.debug(this.getClass().getSimpleName() + " completed shutdown.");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this.getClass().getSimpleName() + " completed shutdown.");
+ }
return;
}
}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java
new file mode 100644
index 0000000..b057a40
--- /dev/null
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java
@@ -0,0 +1,71 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.oracle;
+
+import java.sql.Driver;
+import java.util.Properties;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * An Oracle plugin adapted {@link org.rhq.plugins.database.PooledConnectionProvider}.
+ *
+ * @author Thomas Segismont
+ */
+public class OraclePooledConnectionProvider extends BasePooledConnectionProvider {
+
+ static final String DRIVER_CLASS_PROPERTY = "driverClass";
+ static final String PRINCIPAL_PROPERTY = "principal";
+ static final String CREDENTIALS_PROPERTY = "credentials";
+
+ public OraclePooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimple(DRIVER_CLASS_PROPERTY).getStringValue());
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return OracleServerComponent.buildUrl(pluginConfig);
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected Properties getConnectionProperties() {
+ Properties connectionProperties = super.getConnectionProperties();
+ if (getUsername().equalsIgnoreCase("SYS")) {
+ connectionProperties.put("internal_logon", "sysdba");
+ }
+ return connectionProperties;
+ }
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
index 3849657..6e6b6b9 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,20 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValueMap;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.CREDENTIALS_PROPERTY;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.DRIVER_CLASS_PROPERTY;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.PRINCIPAL_PROPERTY;
+
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -36,48 +45,69 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
*/
-public class OracleServerComponent implements DatabaseComponent, MeasurementFacet {
+public class OracleServerComponent implements DatabaseComponent, ConnectionPoolingSupport, MeasurementFacet {
private static final Log LOG = LogFactory.getLog(OracleServerComponent.class);
- private Connection connection;
-
private ResourceContext resourceContext;
-
- private boolean started;
+ @Deprecated
+ private Connection connection;
+ private OraclePooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- this.connection = buildConnection(resourceContext.getPluginConfiguration());
- this.started = true;
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new OraclePooledConnectionProvider(resourceContext.getPluginConfiguration());
}
public void stop() {
removeConnection();
- this.started = false;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
+ try {
+ if (this.connection == null || connection.isClosed()) {
+ this.connection = buildConnection(this.resourceContext.getPluginConfiguration());
+ }
+ } catch (SQLException e) {
+ LOG.debug("Unable to create oracle connection", e);
+ }
}
public AvailabilityType getAvailability() {
- if (started && getConnection() != null) {
- return AvailabilityType.UP;
- } else {
- return AvailabilityType.DOWN;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValueMap(this,
- "SELECT name, value FROM V$SYSSTAT");
+ Map<String, Double> values = getNumericQueryValueMap(this, "SELECT name, value FROM V$SYSSTAT");
for (MeasurementScheduleRequest request : metrics) {
if (request.getName().equals("totalSize")) {
- Double val = DatabaseQueryUtility.getSingleNumericQueryValue(this,
- "SELECT SUM(bytes) FROM SYS.DBA_DATA_FILES");
+ Double val = getSingleNumericQueryValue(this, "SELECT SUM(bytes) FROM SYS.DBA_DATA_FILES");
report.addData(new MeasurementDataNumeric(request, val));
} else {
Double value = values.get(request.getName());
@@ -89,23 +119,17 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
}
public Connection getConnection() {
- try {
- if (this.connection == null || connection.isClosed()) {
- this.connection = buildConnection(this.resourceContext.getPluginConfiguration());
- }
- } catch (SQLException e) {
- LOG.info("Unable to create oracle connection", e);
- }
+ buildSharedConnectionIfNeeded();
return this.connection;
}
public void removeConnection() {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
public static Connection buildConnection(Configuration configuration) throws SQLException {
- String driverClass = configuration.getSimple("driverClass").getStringValue();
+ String driverClass = configuration.getSimple(DRIVER_CLASS_PROPERTY).getStringValue();
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
@@ -114,10 +138,12 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
}
String url = buildUrl(configuration);
- LOG.debug("Attempting JDBC connection to [" + url + "]");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempting JDBC connection to [" + url + "]");
+ }
- String principal = configuration.getSimple("principal").getStringValue();
- String credentials = configuration.getSimple("credentials").getStringValue();
+ String principal = configuration.getSimple(PRINCIPAL_PROPERTY).getStringValue();
+ String credentials = configuration.getSimple(CREDENTIALS_PROPERTY).getStringValue();
Properties props = new Properties();
props.put("user", principal);
@@ -129,7 +155,7 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
return DriverManager.getConnection(url, props);
}
- private static String buildUrl(Configuration configuration) {
+ static String buildUrl(Configuration configuration) {
String connMethod = configuration.getSimpleValue("connectionMethod", "SID");
if (connMethod.equalsIgnoreCase("SID")) {
return "jdbc:oracle:thin:@" + configuration.getSimpleValue("host", "localhost") + ":"
@@ -139,4 +165,4 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
+ configuration.getSimpleValue("port", "1521") + "/" + configuration.getSimpleValue("sid", "XE");
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
index e99787b..e7e457e 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,14 +32,14 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle Tablespace Component.
@@ -42,37 +47,38 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
public class OracleTablespaceComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleTablespaceComponent.class);
private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM dba_tablespaces WHERE tablespace_name = ?";
- private static final String SQL_VALUES =
- "SELECT USED_SPACE usedSpace, TABLESPACE_SIZE totalSize, (USED_PERCENT/100) usedPercent " +
- "FROM dba_tablespace_usage_metrics where tablespace_name = ?";
-
-
- private static Log log = LogFactory.getLog(OracleTablespaceComponent.class);
+ private static final String SQL_VALUES = "SELECT USED_SPACE usedSpace, TABLESPACE_SIZE totalSize, (USED_PERCENT/100) usedPercent "
+ + "FROM dba_tablespace_usage_metrics where tablespace_name = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SQL_VALUES,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SQL_VALUES, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -80,4 +86,4 @@ public class OracleTablespaceComponent extends AbstractDatabaseComponent impleme
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
index 49999fd..63b9985 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,50 +32,51 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
*/
public class OracleUserComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleUserComponent.class);
private static final String SQL_USER = "SELECT COUNT(*) FROM DBA_USERS WHERE username = ?";
- private static final String SESSIONS =
- "SELECT SUM(DECODE(Status, 'ACTIVE', 1, 0)) active, COUNT(1) connections " +
- "FROM V$SESSION where username = ?";
-
-
- private static Log log = LogFactory.getLog(OracleUserComponent.class);
+ private static final String SESSIONS = "SELECT SUM(DECODE(Status, 'ACTIVE', 1, 0)) active, COUNT(1) connections "
+ + "FROM V$SESSION where username = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_USER);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_USER);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SESSIONS,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SESSIONS, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -78,4 +84,4 @@ public class OracleUserComponent extends AbstractDatabaseComponent implements Me
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
index b8ac5cb..4820785 100644
--- a/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
@@ -641,14 +641,6 @@
discovery="org.rhq.plugins.oracle.OracleAsmDiskGroupDiscoveryComponent"
class="org.rhq.plugins.oracle.OracleAsmDiskGroupComponent">
- <!--plugin-configuration>
- <c:simple-property name="table" default="V$ASM_DISKGROUP"/>
- <c:simple-property name="metricQuery" default="SELECT {key} FROM V$ASM_DISKGROUP"/>
- <c:simple-property name="keyColumn" default="GROUP_NUMBER"/>
- <c:simple-property name="name" default="NAME"/>
- <c:simple-property name="description" default="Oracle ASM Disk Groups"/>
- </plugin-configuration -->
-
<metric property="sectorSize" displayName="Sector Size" description="Physical block size (in bytes)" units="bytes" dataType="trait"/>
<metric property="blockSize" displayName="Block Size" description="Automatic Storage Management metadata block size (in bytes)" units="bytes" dataType="trait"/>
<metric property="allocationUnitSize" displayName="Allocation Unit Size" description="Size of the allocation unit (in bytes)" units="bytes" dataType="trait"/>
diff --git a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
index e98c2e9..346565b 100644
--- a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
+++ b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.oracle;
import static org.testng.AssertJUnit.assertEquals;
diff --git a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
index d9d2fb0..dfb16ac 100644
--- a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
+++ b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.oracle;
import static org.testng.AssertJUnit.assertNotNull;
@@ -6,15 +25,16 @@ import static org.testng.AssertJUnit.assertTrue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.rhq.core.domain.configuration.Configuration;
-import org.rhq.core.domain.measurement.MeasurementReport;
-import org.rhq.core.domain.resource.ResourceType;
-import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.measurement.MeasurementReport;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+
public class OracleServerComponentTest extends ComponentTest {
private static final String ORACLE_SERVER = "Oracle Server";
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
index f235f93..ec58833 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,20 +13,29 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+import static org.rhq.core.domain.resource.CreateResourceStatus.FAILURE;
+import static org.rhq.core.domain.resource.CreateResourceStatus.SUCCESS;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.buildConnection;
+
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
-import java.sql.ResultSetMetaData;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
@@ -36,43 +45,71 @@ import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
-import org.rhq.core.domain.resource.CreateResourceStatus;
import org.rhq.core.pluginapi.inventory.CreateChildResourceFacet;
import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
-public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServerComponent<?>>, MeasurementFacet,
- CreateChildResourceFacet, OperationFacet {
- private Log log = LogFactory.getLog(PostgresDatabaseComponent.class);
+public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServerComponent<?>>,
+ ConnectionPoolingSupport, MeasurementFacet, CreateChildResourceFacet, OperationFacet {
- private ResourceContext<PostgresServerComponent<?>> resourceContext;
+ private static final Log LOG = LogFactory.getLog(PostgresDatabaseComponent.class);
- private Connection databaseConnection;
+ private static final String QUERY_DATABASE_SIZE = "SELECT *, pg_database_size(datname) AS size FROM pg_stat_database where datname = ?";
+ private ResourceContext<PostgresServerComponent<?>> resourceContext;
private String databaseName;
+ private PostgresServerComponent<?> postgresServerComponent;
+ private boolean useOwnJdbcConnections;
+ @Deprecated
+ private Connection databaseConnection;
+ private PostgresPooledConnectionProvider pooledConnectionProvider;
+
+ public void start(ResourceContext<PostgresServerComponent<?>> context) throws Exception {
+ this.resourceContext = context;
+ databaseName = resourceContext.getPluginConfiguration().getSimple("databaseName").getStringValue();
+ postgresServerComponent = resourceContext.getParentResourceComponent();
+ useOwnJdbcConnections = !databaseName.equals(postgresServerComponent.getResourceContext()
+ .getPluginConfiguration().getSimple("db").getStringValue());
+ if (useOwnJdbcConnections) {
+ buildDatabaseConnectionIfNeeded();
+ pooledConnectionProvider = new PostgresPooledConnectionProvider(createDatabaseSpecificConfig());
+ }
+ }
+
+ public void stop() {
+ this.resourceContext = null;
+ databaseName = null;
+ postgresServerComponent = null;
+ if (useOwnJdbcConnections) {
+ DatabasePluginUtil.safeClose(databaseConnection);
+ databaseConnection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ }
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return useOwnJdbcConnections ? pooledConnectionProvider : postgresServerComponent.getPooledConnectionProvider();
+ }
public Connection getConnection() {
- this.databaseName = resourceContext.getPluginConfiguration().getSimple("databaseName").getStringValue();
- if (this.databaseName.equals(resourceContext.getParentResourceComponent().getResourceContext()
- .getPluginConfiguration().getSimple("db").getStringValue())) {
- return resourceContext.getParentResourceComponent().getConnection();
+ if (useOwnJdbcConnections) {
+ return postgresServerComponent.getConnection();
} else {
- // ??? Need to use a different connection to talk to a different db?
- if (this.databaseConnection == null) {
- Configuration config = resourceContext.getParentResourceComponent().getResourceContext()
- .getPluginConfiguration();
- config = config.deepCopy();
- config.put(new PropertySimple("db", databaseName));
- log.debug("Getting db specific connection to postgres for [" + databaseName + "] database");
- this.databaseConnection = PostgresDiscoveryComponent.buildConnection(config, true);
- }
-
- // TODO GH: Attempt to load other db connections? or only monitor this dbs stuff? Weird situation.
+ buildDatabaseConnectionIfNeeded();
return this.databaseConnection;
}
}
@@ -82,23 +119,47 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
if ((this.databaseConnection != null) && !this.databaseConnection.isClosed()) {
this.databaseConnection.close();
}
- } catch (SQLException se) {
- log.debug("Closing and removing postgres connection");
+ } catch (SQLException e) {
+ LOG.debug("Could not remove connection", e);
}
-
this.databaseConnection = null;
}
- public void start(ResourceContext<PostgresServerComponent<?>> context) {
- this.resourceContext = context;
+ private void buildDatabaseConnectionIfNeeded() {
+ try {
+ if (this.databaseConnection == null || this.databaseConnection.isClosed()) {
+ this.databaseConnection = buildConnection(createDatabaseSpecificConfig(), true);
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
+ }
}
- public void stop() {
- this.resourceContext = null;
+ private Configuration createDatabaseSpecificConfig() {
+ Configuration config = postgresServerComponent.getResourceContext().getPluginConfiguration();
+ config = config.deepCopy();
+ config.put(new PropertySimple("db", databaseName));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Getting db specific connection to postgres for [" + databaseName + "] database");
+ }
+ return config;
}
public AvailabilityType getAvailability() {
- return resourceContext.getParentResourceComponent().getAvailability();
+ if (useOwnJdbcConnections) {
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
+ return postgresServerComponent.getAvailability();
}
public String getDatabaseName() {
@@ -106,35 +167,27 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
+ ResultSet resultSet = null;
try {
- statement = this.resourceContext.getParentResourceComponent().getConnection().prepareStatement(
- "SELECT *, pg_database_size(datname) AS size FROM pg_stat_database where datname = ?");
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.prepareStatement(QUERY_DATABASE_SIZE);
statement.setString(1, this.resourceContext.getPluginConfiguration().getSimple("databaseName")
.getStringValue());
- ResultSet results = statement.executeQuery();
- try {
- if (!results.next()) {
- throw new RuntimeException("Couldn't get the data"); // TODO Error handling system
- }
-
- for (MeasurementScheduleRequest request : metrics) {
- // Only size expected
- double val = results.getDouble(request.getName());
- report.addData(new MeasurementDataNumeric(request, val));
- }
- } finally {
- results.close();
+ resultSet = statement.executeQuery();
+ if (!resultSet.next()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Result set is empty: " + QUERY_DATABASE_SIZE);
+ }
+ }
+ for (MeasurementScheduleRequest request : metrics) {
+ report.addData(new MeasurementDataNumeric(request, resultSet.getDouble(request.getName())));
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
- try {
- if (statement != null) {
- statement.close();
- }
- } catch (SQLException e) {
- }
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
@@ -188,7 +241,8 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
buf.append("\n)");
- log.info("Creating table with: " + buf.toString());
+ String createTableSql = buf.toString();
+ LOG.info("Creating table with: " + createTableSql);
PropertyList constraintList = configuration.getList("constraints");
if (constraintList != null) {
for (Property c : constraintList.getList()) {
@@ -197,99 +251,105 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
}
}
+ Connection jdbcConnection = null;
Statement statement = null;
try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ statement.executeUpdate(createTableSql);
+ report.setStatus(SUCCESS);
report.setResourceKey(tableName);
- statement = getConnection().createStatement();
- statement.executeUpdate(buf.toString());
- report.setStatus(CreateResourceStatus.SUCCESS);
report.setResourceName(tableName);
} catch (SQLException e) {
report.setException(e);
- report.setStatus(CreateResourceStatus.FAILURE);
+ report.setStatus(FAILURE);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, null);
}
return report;
}
- public OperationResult invokeOperation(String name, Configuration parameters)
- throws InterruptedException, Exception {
-
+ public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
+ Exception {
if ("resetStatistics".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- rs = stmt.executeQuery("select * from pg_stat_reset()");
-
- } finally {
- if (rs != null) {
- rs.close();
- }
-
- if (stmt != null) {
- stmt.close();
- }
- }
- return null;
+ return resetStatistics();
} else if ("invokeSql".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- String sql = parameters.getSimple("sql").getStringValue();
- OperationResult result = new OperationResult();
-
- if (parameters.getSimple("type").getStringValue().equals("update")) {
- int updateCount = stmt.executeUpdate(sql);
- result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+ return invokeSql(parameters);
+ } else {
+ throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ }
+ }
- } else {
- rs = stmt.executeQuery(parameters.getSimple("sql").getStringValue());
+ private OperationResult resetStatistics() {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select * from pg_stat_reset()");
+ return null; // does not return results
+ } catch (SQLException e) {
+ OperationResult result = new OperationResult("Failed to reset statistics");
+ result.setErrorMessage(e.getMessage());
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
+ }
+ }
- ResultSetMetaData md = rs.getMetaData();
- StringBuilder buf = new StringBuilder();
- int rowCount = 0;
+ private OperationResult invokeSql(Configuration parameters) throws SQLException {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ String sql = parameters.getSimple("sql").getStringValue();
+
+ OperationResult result = new OperationResult();
+ if (parameters.getSimple("type").getStringValue().equals("update")) {
+ int updateCount = statement.executeUpdate(sql);
+ result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+ } else {
+ resultSet = statement.executeQuery(sql);
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ StringBuilder buf = new StringBuilder();
+ int rowCount = 0;
+
+ buf.append("<table>");
+ buf.append("<th>");
+ for (int i = 1; i <= md.getColumnCount(); i++) {
+ buf.append("<td>");
+ buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append("</td>");
+ }
+ buf.append("</th>");
- buf.append("<table>");
- buf.append("<th>");
+ while (resultSet.next()) {
+ rowCount++;
+ buf.append("<tr>");
for (int i = 1; i <= md.getColumnCount(); i++) {
buf.append("<td>");
- buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append(resultSet.getString(i));
buf.append("</td>");
}
- buf.append("</th>");
-
-
- while (rs.next()) {
- rowCount++;
- buf.append("<tr>");
- for (int i = 1; i <= md.getColumnCount(); i++) {
- buf.append("<td>");
- buf.append(rs.getString(i));
- buf.append("</td>");
- }
- buf.append("</tr>");
- }
-
- buf.append("</table>");
- result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
- result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
- }
- return result;
- } finally {
- if (rs != null) {
- rs.close();
+ buf.append("</tr>");
}
- if (stmt != null) {
- stmt.close();
- }
+ buf.append("</table>");
+ result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
+ result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
}
- } else {
- throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ return result;
+ } catch (SQLException e) {
+ OperationResult result = new OperationResult("Failed to invoke SQL");
+ result.setErrorMessage(e.getMessage());
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
index cfc60b2..d4c89dc 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,13 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
@@ -27,7 +29,7 @@ import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
@@ -37,10 +39,12 @@ public class PostgresDatabaseDiscoveryComponent implements ResourceDiscoveryComp
throws Exception {
Set<DiscoveredResourceDetails> databases = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
- statement = context.getParentResourceComponent().getConnection().createStatement();
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = connection.createStatement();
resultSet = statement
.executeQuery("SELECT *, pg_database_size(datname) FROM pg_database where datistemplate = false");
while (resultSet.next()) {
@@ -51,9 +55,9 @@ public class PostgresDatabaseDiscoveryComponent implements ResourceDiscoveryComp
databases.add(database);
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
return databases;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
index b62c029..59150c4 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.io.File;
@@ -39,6 +40,7 @@ import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.ProcExe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
@@ -51,7 +53,7 @@ import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.ProcessInfo;
import org.rhq.core.system.SystemInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
import org.rhq.plugins.postgres.util.PostgresqlConfFile;
/**
@@ -59,8 +61,7 @@ import org.rhq.plugins.postgres.util.PostgresqlConfFile;
* @author Ian Springer
*/
public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
-
- private static final Log log = LogFactory.getLog(PostgresDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresDiscoveryComponent.class);
public static final String PGDATA_DIR_CONFIGURATION_PROPERTY = "pgdataDir";
public static final String CONFIG_FILE_CONFIGURATION_PROPERTY = "configFile";
@@ -82,15 +83,15 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
// Process any auto-discovered resources.
List<ProcessScanResult> autoDiscoveryResults = context.getAutoDiscoveredProcesses();
for (ProcessScanResult result : autoDiscoveryResults) {
- log.info("Discovered a postgres process: " + result);
+ LOG.info("Discovered a postgres process: " + result);
ProcessInfo procInfo = result.getProcessInfo();
String pgDataPath = getDataDirPath(procInfo);
if (pgDataPath == null) {
- log.error("Unable to obtain data directory for postgres process with pid " + procInfo.getPid()
- + " (tried checking both -D command line argument, as well as " + PGDATA_ENV_VAR
- + " environment variable).");
+ LOG.error("Unable to obtain data directory for postgres process with pid " + procInfo.getPid()
+ + " (tried checking both -D command line argument, as well as " + PGDATA_ENV_VAR
+ + " environment variable).");
continue;
}
@@ -99,26 +100,28 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
PostgresqlConfFile confFile = null;
if (!pgData.exists()) {
- log.warn("PostgreSQL data directory (" + pgData + ") does not exist or is not readable. "
+ LOG.warn("PostgreSQL data directory ("
+ + pgData
+ + ") does not exist or is not readable. "
+ "Make sure the user the RHQ Agent is running as has read permissions on the directory and its parent directory.");
} else {
- log.debug("PostgreSQL data directory: " + pgData);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("PostgreSQL data directory: " + pgData);
+ }
File postgresConfFile = (configFilePath != null) ? new File(configFilePath) : new File(pgData,
PostgresServerComponent.DEFAULT_CONFIG_FILE_NAME);
- log.debug("PostgreSQL configuration file: " + postgresConfFile);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("PostgreSQL configuration file: " + postgresConfFile);
+ }
if (!postgresConfFile.exists()) {
- log.warn("PostgreSQL configuration file (" + postgresConfFile + ") does not exist.");
+ LOG.warn("PostgreSQL configuration file (" + postgresConfFile + ") does not exist.");
} else {
try {
confFile = new PostgresqlConfFile(postgresConfFile);
} catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.debug("Could not load PostgreSQL configuration file [" + postgresConfFile + "].", e);
- } else {
- log.warn("Could not load PostgreSQL configuration file [" + postgresConfFile + "]: " + e);
- }
+ LOG.warn("Could not load PostgreSQL configuration file [" + postgresConfFile + "]: " + e);
}
}
}
@@ -167,14 +170,19 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
protected static DiscoveredResourceDetails createResourceDetails(ResourceDiscoveryContext discoveryContext,
- Configuration pluginConfiguration, @Nullable ProcessInfo processInfo, boolean logConnectionFailure) {
+ Configuration pluginConfiguration, @Nullable
+ ProcessInfo processInfo, boolean logConnectionFailure) {
String key = buildUrl(pluginConfiguration);
- Connection conn = buildConnection(pluginConfiguration, logConnectionFailure);
- String name = getServerResourceName(pluginConfiguration, conn);
- String version = getVersion(processInfo, discoveryContext.getSystemInformation(), conn);
- JDBCUtil.safeClose(conn);
- return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version,
- DEFAULT_RESOURCE_DESCRIPTION, pluginConfiguration, processInfo);
+ Connection conn = null;
+ try {
+ conn = buildConnection(pluginConfiguration, logConnectionFailure);
+ String name = getServerResourceName(pluginConfiguration, conn);
+ String version = getVersion(processInfo, discoveryContext.getSystemInformation(), conn);
+ return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version,
+ DEFAULT_RESOURCE_DESCRIPTION, pluginConfiguration, processInfo);
+ } finally {
+ DatabasePluginUtil.safeClose(conn);
+ }
}
protected static String buildUrl(Configuration config) {
@@ -193,7 +201,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
} catch (SQLException e) {
// TODO GH: How to put this back to the server while inventorying this resource in an unconfigured state
- log.info("Exception detecting postgres instance version.", e);
+ LOG.info("Exception detecting postgres instance version.", e);
}
//now try to extract the version information by asking the server executable itself
@@ -211,11 +219,11 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
if (m.find()) {
version = versionInfo.substring(m.start(), m.end());
} else {
- log.debug("Can't get the process executable - does the agent have the right permissions?");
+ LOG.debug("Can't get the process executable - does the agent have the right permissions?");
}
}
} catch (Exception e) {
- log.info("Failed to obtain Postgres version information from the executable file.", e);
+ LOG.info("Failed to obtain Postgres version information from the executable file.", e);
}
}
@@ -240,16 +248,19 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
return DriverManager.getConnection(url, principal, credentials);
} catch (SQLException e) {
if (logFailure) {
- log.info("Failed to connect to the database: " + e.getMessage());
+ LOG.info("Failed to connect to the database: " + e.getMessage());
} else {
- log.debug("Failed to connect to the database: " + e.getMessage());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Failed to connect to the database: " + e.getMessage());
+ }
}
return null;
}
}
@Nullable
- protected static String getDataDirPath(@NotNull ProcessInfo procInfo) {
+ protected static String getDataDirPath(@NotNull
+ ProcessInfo procInfo) {
String dataDirPath = null;
String[] cmdLine = procInfo.getCommandLine();
for (int i = 0; i < cmdLine.length; i++) {
@@ -258,7 +269,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
dataDirPath = cmdLine[i + 1];
break;
} else {
- log.error("-D option was last option on postgres command line: " + Arrays.asList(cmdLine));
+ LOG.error("-D option was last option on postgres command line: " + Arrays.asList(cmdLine));
}
}
}
@@ -274,7 +285,8 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
@Nullable
- private static String getConfigFilePath(@NotNull ProcessInfo procInfo) {
+ private static String getConfigFilePath(@NotNull
+ ProcessInfo procInfo) {
String configFilePath = null;
String[] cmdLine = procInfo.getCommandLine();
for (int i = 0; i < cmdLine.length; i++) {
@@ -283,8 +295,8 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
String paramString = cmdLine[i + 1];
int equalsIndex = paramString.indexOf('=');
if (equalsIndex == -1) {
- log.error("Invalid value '" + paramString + "' for -c option on postgres command line: "
- + Arrays.asList(cmdLine));
+ LOG.error("Invalid value '" + paramString + "' for -c option on postgres command line: "
+ + Arrays.asList(cmdLine));
continue;
}
@@ -294,7 +306,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
break;
}
} else {
- log.error("-c option was last option on postgres command line: " + Arrays.asList(cmdLine));
+ LOG.error("-c option was last option on postgres command line: " + Arrays.asList(cmdLine));
}
}
}
@@ -303,13 +315,12 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
private static List<String> getDatabaseNames(Connection conn) {
- Statement statement = null;
- ResultSet resultSet = null;
-
if (conn == null) {
return Collections.emptyList();
}
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
List<String> ret = new ArrayList<String>();
@@ -323,10 +334,10 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
return ret;
} catch (SQLException e) {
- log.info("Failed to obtain the list of databases in a postgres instance", e);
+ LOG.error("Failed to obtain the list of databases in a postgres instance", e);
return Collections.emptyList();
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
}
}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
index 526caeb..d651506 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Driver;
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java
new file mode 100644
index 0000000..c505bae
--- /dev/null
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java
@@ -0,0 +1,62 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.postgres;
+
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.CREDENTIALS_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.DRIVER_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.PRINCIPAL_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.buildUrl;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * A Postgres plugin adapated {@link org.rhq.plugins.database.PooledConnectionProvider}
+ *
+ * @author Thomas Segismont
+ */
+public class PostgresPooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public PostgresPooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimple(DRIVER_CONFIGURATION_PROPERTY).getStringValue());
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return buildUrl(pluginConfig);
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
index d132598..3fb7354 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
@@ -61,8 +65,10 @@ import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.core.system.AggregateProcessInfo;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
import org.rhq.plugins.postgres.util.PostgresqlConfFile;
/**
@@ -70,43 +76,48 @@ import org.rhq.plugins.postgres.util.PostgresqlConfFile;
*
* @author Greg Hinkle
*/
-public class PostgresServerComponent<T extends ResourceComponent<?>> implements DatabaseComponent<T>, ConfigurationFacet, MeasurementFacet,
- OperationFacet, CreateChildResourceFacet {
+public class PostgresServerComponent<T extends ResourceComponent<?>> implements DatabaseComponent<T>,
+ ConnectionPoolingSupport, ConfigurationFacet, MeasurementFacet, OperationFacet, CreateChildResourceFacet {
- private static Log log = LogFactory.getLog(PostgresServerComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresServerComponent.class);
- private Connection connection;
+ static final String DEFAULT_CONFIG_FILE_NAME = "postgresql.conf";
private AggregateProcessInfo aggregateProcessInfo;
-
+ @Deprecated
+ private Connection connection;
private ResourceContext resourceContext;
+ private PostgresPooledConnectionProvider pooledConnectionProvider;
- static final String DEFAULT_CONFIG_FILE_NAME = "postgresql.conf";
-
- /*
- * TODO: Other things to support active sessions: select * from pg_stat_activity
- */
-
- public void start(ResourceContext context) throws SQLException {
+ public void start(ResourceContext context) throws Exception {
this.resourceContext = context;
- Configuration config = context.getPluginConfiguration();
-
- JDBCUtil.safeClose(this.connection); // just to be sure we don't leak a connection
- this.connection = PostgresDiscoveryComponent.buildConnection(config, true);
-
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new PostgresPooledConnectionProvider(resourceContext.getPluginConfiguration());
ProcessInfo processInfo = resourceContext.getNativeProcess();
if (processInfo != null) {
aggregateProcessInfo = processInfo.getAggregateProcessTree();
} else {
findProcessInfo();
- //log.debug("Unable to locate native process information. Process level statistics will be unavailable.");
}
}
public void stop() {
- this.resourceContext = null;
- JDBCUtil.safeClose(this.connection);
- this.connection = null;
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(connection);
+ connection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ aggregateProcessInfo = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
protected String getJDBCUrl() {
@@ -114,15 +125,15 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
public AvailabilityType getAvailability() {
- AvailabilityType type;
- getConnection(); // This retries the connection if its null
- if (connection == null) {
- type = AvailabilityType.DOWN;
- } else {
- type = AvailabilityType.UP;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
-
- return type;
}
ResourceContext getResourceContext() {
@@ -130,20 +141,25 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
public Connection getConnection() {
- // TODO: This method should probably be synchronized to prevent connection leaks. (ips, 10/4/07)
+ buildSharedConnectionIfNeeded();
+ return connection;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
try {
if ((connection == null) || connection.isClosed()) {
- connection = PostgresDiscoveryComponent.buildConnection(this.resourceContext.getPluginConfiguration(), true);
+ connection = PostgresDiscoveryComponent.buildConnection(this.resourceContext.getPluginConfiguration(),
+ true);
}
} catch (SQLException e) {
- // TODO Should we throw this?
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
-
- return connection;
}
public void removeConnection() {
- JDBCUtil.safeClose(this.connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
@@ -177,12 +193,12 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
// Runtime settings (session params) - obtained via SQL.
- // TODO (ips, 05/16/12): We should move these to a separate Resource, since they are loaded via a completely
- // separate mechanism than the static config above.
+ Connection jdbcConnection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
- statement = connection.createStatement();
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
resultSet = statement.executeQuery("show all");
PropertyMap runtimeSettings = new PropertyMap("runtimeSettings");
@@ -201,7 +217,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return config;
@@ -230,69 +246,80 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
PostgresqlConfFile confFile = getConfigurationFile();
confFile.setProperties(parameters);
} catch (IOException e) {
- log.error("Unable to update postgres configuration file", e);
+ LOG.error("Unable to update postgres configuration file", e);
}
report.setStatus(ConfigurationUpdateStatus.SUCCESS);
}
- /**
- * Get data about the database server. Currently we have two categories:
- * <ul>
- * <li>Database.* are metrics that are obtained from the database server itself</li>
- * <li>Process.* are metrics obtained from the native system.</li>
- * </ul>
- *
- * @param report the report where all collected measurement data will be added
- * @param metrics the schedule of what needs to be collected when
- */
+ /**
+ * Get data about the database server. Currently we have two categories:
+ * <ul>
+ * <li>Database.* are metrics that are obtained from the database server itself</li>
+ * <li>Process.* are metrics obtained from the native system.</li>
+ * </ul>
+ *
+ * @param report the report where all collected measurement data will be added
+ * @param metrics the schedule of what needs to be collected when
+ */
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) {
- for (MeasurementScheduleRequest request : metrics) {
- String property = request.getName();
- if (property.startsWith("Process.")) {
- if (aggregateProcessInfo != null) {
- aggregateProcessInfo.refresh();
+ for (MeasurementScheduleRequest request : metrics) {
+ String property = request.getName();
+ if (property.startsWith("Process.")) {
+ if (aggregateProcessInfo != null) {
+ aggregateProcessInfo.refresh();
- //report.addData(new MeasurementDataNumeric(request, getProcessProperty(request.getName())));
+ //report.addData(new MeasurementDataNumeric(request, getProcessProperty(request.getName())));
- Object val = lookupAttributeProperty(aggregateProcessInfo, property.substring("Process.".length()));
- if (val != null && val instanceof Number) {
-// aggregateProcessInfo.getAggregateMemory().Cpu().getTotal()
- report.addData(new MeasurementDataNumeric(request, ((Number) val).doubleValue()));
+ Object val = lookupAttributeProperty(aggregateProcessInfo, property.substring("Process.".length()));
+ if (val != null && val instanceof Number) {
+ // aggregateProcessInfo.getAggregateMemory().Cpu().getTotal()
+ report.addData(new MeasurementDataNumeric(request, ((Number) val).doubleValue()));
+ }
}
- }
- } else if (property.startsWith("Database")) {
- try {
+ } else if (property.startsWith("Database")) {
if (property.endsWith("startTime")) {
- // db start time
- ResultSet rs = getConnection().createStatement().executeQuery("SELECT pg_postmaster_start_time()");
+ // db start time
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- if (rs.next()) {
- report.addData(new MeasurementDataTrait(request, rs.getTimestamp(1).toString()));
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SELECT pg_postmaster_start_time()");
+ if (resultSet.next()) {
+ report.addData(new MeasurementDataTrait(request, resultSet.getTimestamp(1).toString()));
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Can not collect property: " + property + ": " + e.getLocalizedMessage());
}
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- }
- else if (property.endsWith("backends")) {
+ } else if (property.endsWith("backends")) {
// number of connected backends
- ResultSet rs = getConnection().createStatement().executeQuery("select count(*) from pg_stat_activity");
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- if (rs.next()) {
- report.addData(new MeasurementDataNumeric(request, (double) rs.getLong(1)));
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select count(*) from pg_stat_activity");
+ if (resultSet.next()) {
+ report.addData(new MeasurementDataNumeric(request, (double) resultSet.getLong(1)));
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Can not collect property: " + property + ": " + e.getLocalizedMessage());
}
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-
- }
- catch (SQLException e) {
- log.warn("Can not collect property: " + property + ": " + e.getLocalizedMessage());
- }
- }
- }
+ }
+ }
}
private Double getProcessProperty(String property) {
@@ -320,8 +347,10 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} catch (Exception e) {
- log.debug("Unable to read property from measurement attribute [" + searchProperty + "] not found on ["
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to read property from measurement attribute [" + searchProperty + "] not found on ["
+ this.resourceContext.getResourceKey() + "]");
+ }
}
if (ps.length > 1) {
@@ -340,34 +369,31 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} catch (Exception e) {
- log.error("Error occurred while retrieving property '" + name + "' from object [" + object + "]", e);
+ LOG.error("Error occurred while retrieving property '" + name + "' from object [" + object + "]", e);
}
return Double.NaN;
}
- /*private ProcessInfo getProcess(String pgdata)
- * { List<ProcessScanResult> matches = this.resourceContext.getNativeProcessesForType(); for (ProcessScanResult
- * process : matches) { if (pgdata.equals(process.getProcessInfo().getEnvironmentProperty("PGDATA"))) return
- * process.getProcessInfo(); } return null;}*/
-
public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
Exception {
if (name.equals("listProcessStatistics")) {
- Statement stmt = null;
- ResultSet rs = null;
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- stmt = getConnection().createStatement();
- rs = stmt.executeQuery("SELECT * FROM pg_stat_activity ORDER BY current_query desc");
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SELECT * FROM pg_stat_activity ORDER BY current_query desc");
PropertyList procList = new PropertyList("processList");
- while (rs.next()) {
+ while (resultSet.next()) {
PropertyMap pm = new PropertyMap("process");
- pm.put(new PropertySimple("pid", rs.getInt("procpid")));
- pm.put(new PropertySimple("userName", rs.getString("usename")));
- pm.put(new PropertySimple("query", rs.getString("current_query")));
- pm.put(new PropertySimple("address", rs.getString("client_addr")));
- pm.put(new PropertySimple("port", rs.getInt("client_port")));
+ pm.put(new PropertySimple("pid", resultSet.getInt("procpid")));
+ pm.put(new PropertySimple("userName", resultSet.getString("usename")));
+ pm.put(new PropertySimple("query", resultSet.getString("current_query")));
+ pm.put(new PropertySimple("address", resultSet.getString("client_addr")));
+ pm.put(new PropertySimple("port", resultSet.getInt("client_port")));
procList.add(pm);
}
@@ -376,13 +402,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
result.getComplexResults().put(procList);
return result;
} finally {
- if (rs != null) {
- rs.close();
- }
-
- if (stmt != null) {
- stmt.close();
- }
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
@@ -393,11 +413,12 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
Configuration userConfig = report.getResourceConfiguration();
String user = userConfig.getSimpleValue("user", null);
+ Connection jdbcConnection = null;
Statement statement = null;
String sql = PostgresUserComponent.getUserSQL(userConfig, PostgresUserComponent.UpdateType.CREATE);
try {
- statement = getConnection().createStatement();
-
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
// NOTE: Postgres doesn't seem to indicate the expect count of 1 row updated but this work
// Postgres returns 0 for DDL that does not return rows
statement.executeUpdate(sql);
@@ -406,7 +427,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
} catch (SQLException e) {
report.setException(e);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement);
}
return report;
@@ -436,7 +457,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
jonValue = Boolean.FALSE.toString();
} else {
jonValue = (propDef.isRequired()) ? Boolean.FALSE.toString() : null;
- log.warn("Boolean PostgreSQL configuration parameter '" + propDef.getName()
+ LOG.warn("Boolean PostgreSQL configuration parameter '" + propDef.getName()
+ "' has an invalid value: '" + value + "' - defaulting value to '" + jonValue + "'");
}
} else {
@@ -446,16 +467,15 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
return new PropertySimple(propDef.getName(), jonValue);
}
-
public void findProcessInfo() {
- List<ProcessInfo> processes =
- this.resourceContext.getSystemInformation().getProcesses(
+ List<ProcessInfo> processes = this.resourceContext
+ .getSystemInformation()
+ .getProcesses(
"process|basename|match=^(?i)(postgres|postmaster)\\.exe$,process|basename|nomatch|parent=^(?i)(postgres|postmaster)\\.exe$");
- processes.addAll(
- this.resourceContext.getSystemInformation().getProcesses(
- "process|basename|match=^(postgres|postmaster)$,process|basename|nomatch|parent=^(postgres|postmaster)$"));
+ processes.addAll(this.resourceContext.getSystemInformation().getProcesses(
+ "process|basename|match=^(postgres|postmaster)$,process|basename|nomatch|parent=^(postgres|postmaster)$"));
for (ProcessInfo processInfo : processes) {
String pgDataPath = PostgresDiscoveryComponent.getDataDirPath(processInfo);
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
index 9dc07bd..c65a97d 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,13 +13,18 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+
import java.sql.Connection;
import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
@@ -27,6 +32,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
@@ -44,16 +50,20 @@ import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* Represents a postgres table
*
* @author Greg Hinkle
*/
-public class PostgresTableComponent implements DatabaseComponent<PostgresDatabaseComponent>, MeasurementFacet,
- ConfigurationFacet, DeleteResourceFacet, OperationFacet {
+public class PostgresTableComponent implements DatabaseComponent<PostgresDatabaseComponent>, ConnectionPoolingSupport,
+ MeasurementFacet, ConfigurationFacet, DeleteResourceFacet, OperationFacet {
+
private static final List<String> PG_STAT_USER_TABLE_STATS = Arrays.asList("seq_scan", "seq_tup_read", "idx_scan",
"idx_tup_fetch", "n_tup_ins", "n_tup_upd", "n_tup_del", "table_size", "total_size");
@@ -77,6 +87,16 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
this.resourceContext = null;
}
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return resourceContext.getParentResourceComponent().getPooledConnectionProvider();
+ }
+
public String getTableName() {
return this.resourceContext.getPluginConfiguration().getSimple("tableName").getStringValue();
}
@@ -88,28 +108,35 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) {
this.resourceContext.getParentResourceComponent().getConnection();
- Map<String, Double> results = DatabaseQueryUtility.getNumericQueryValues(this, PG_STAT_USER_TABLES_QUERY,
- getTableName());
+ Map<String, Double> results = getNumericQueryValues(this, PG_STAT_USER_TABLES_QUERY, getTableName());
for (MeasurementScheduleRequest request : requests) {
String metricName = request.getName();
Double value;
if (metricName.equals("rows")) {
- value = DatabaseQueryUtility.getSingleNumericQueryValue(this, PG_COUNT_ROWS + getTableName());
+ value = getSingleNumericQueryValue(this, PG_COUNT_ROWS + getTableName());
} else if (metricName.equals("rows_approx")) {
- value = DatabaseQueryUtility.getSingleNumericQueryValue(this, PG_COUNT_ROWS_APPROX, getTableName());
+ value = getSingleNumericQueryValue(this, PG_COUNT_ROWS_APPROX, getTableName());
} else {
value = results.get(metricName);
}
- if (value!=null) {
+ if (value != null) {
MeasurementDataNumeric mdn = new MeasurementDataNumeric(request, value);
report.addData(mdn);
}
}
}
- public void deleteResource() throws SQLException {
- DatabaseQueryUtility.executeUpdate(this, "DROP TABLE " + getTableName(), new Object[] {});
+ public void deleteResource() throws Exception {
+ Connection connection = null;
+ PreparedStatement statement = null;
+ try {
+ connection = getPooledConnectionProvider().getPooledConnection();
+ statement = connection.prepareStatement("DROP TABLE " + getTableName());
+ statement.executeUpdate();
+ } finally {
+ DatabasePluginUtil.safeClose(connection, statement);
+ }
}
public Configuration loadResourceConfiguration() throws Exception {
@@ -117,29 +144,31 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
config.put(new PropertySimple("tableName", resourceContext.getPluginConfiguration().getSimple("tableName")
.getStringValue()));
- Connection connection = this.resourceContext.getParentResourceComponent().getConnection();
-
- DatabaseMetaData dmd = connection.getMetaData();
- ResultSet rs = dmd.getColumns("", "", getTableName(), "");
+ Connection connection = null;
+ ResultSet columns = null;
try {
+ connection = this.resourceContext.getParentResourceComponent().getConnection();
+ DatabaseMetaData databaseMetaData = connection.getMetaData();
+ columns = databaseMetaData.getColumns("", "", getTableName(), "");
+
PropertyList columnList = new PropertyList("columns");
- while (rs.next()) {
+ while (columns.next()) {
PropertyMap col = new PropertyMap("columnDefinition");
- col.put(new PropertySimple("columnName", rs.getString("COLUMN_NAME")));
- col.put(new PropertySimple("columnType", rs.getString("TYPE_NAME")));
- col.put(new PropertySimple("columnLength", rs.getInt("COLUMN_SIZE")));
- col.put(new PropertySimple("columnPrecision", rs.getInt("DECIMAL_DIGITS")));
- col.put(new PropertySimple("columnDefault", rs.getString("COLUMN_DEF")));
- col.put(new PropertySimple("columnNullable", rs.getBoolean("IS_NULLABLE")));
+ col.put(new PropertySimple("columnName", columns.getString("COLUMN_NAME")));
+ col.put(new PropertySimple("columnType", columns.getString("TYPE_NAME")));
+ col.put(new PropertySimple("columnLength", columns.getInt("COLUMN_SIZE")));
+ col.put(new PropertySimple("columnPrecision", columns.getInt("DECIMAL_DIGITS")));
+ col.put(new PropertySimple("columnDefault", columns.getString("COLUMN_DEF")));
+ col.put(new PropertySimple("columnNullable", columns.getBoolean("IS_NULLABLE")));
columnList.add(col);
}
config.put(columnList);
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(connection);
}
return config;
@@ -177,13 +206,12 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
}
} else {
existingDefs.remove(existingDef.columnName);
- boolean columnLengthChanged = ((existingDef.columnLength != null && !existingDef.columnLength.equals(newDef.columnLength)) ||
- (existingDef.columnLength == null && existingDef.columnLength != null));
- boolean columnPrecisionChanged = ((existingDef.columnPrecision != null && !existingDef.columnPrecision.equals(newDef.columnPrecision)) ||
- (existingDef.columnPrecision == null && existingDef.columnPrecision != null));
- if (!existingDef.columnType.equals(newDef.columnType) ||
- columnLengthChanged ||
- columnPrecisionChanged) {
+ boolean columnLengthChanged = ((existingDef.columnLength != null && !existingDef.columnLength
+ .equals(newDef.columnLength)) || (existingDef.columnLength == null && existingDef.columnLength != null));
+ boolean columnPrecisionChanged = ((existingDef.columnPrecision != null && !existingDef.columnPrecision
+ .equals(newDef.columnPrecision)) || (existingDef.columnPrecision == null && existingDef.columnPrecision != null));
+ if (!existingDef.columnType.equals(newDef.columnType) || columnLengthChanged
+ || columnPrecisionChanged) {
String sql = "ALTER TABLE " + getTableName() + " ALTER COLUMN " + newDef.columnName + " TYPE "
+ newDef.columnType;
if (newDef.columnLength != null) {
@@ -200,8 +228,8 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
}
// Set default separately.
- boolean columnDefaultChanged = ((existingDef.columnDefault != null && !existingDef.columnDefault.equals(newDef.columnDefault)) ||
- (existingDef.columnDefault == null && newDef.columnDefault != null));
+ boolean columnDefaultChanged = ((existingDef.columnDefault != null && !existingDef.columnDefault
+ .equals(newDef.columnDefault)) || (existingDef.columnDefault == null && newDef.columnDefault != null));
if (columnDefaultChanged) {
String sql = "ALTER TABLE " + getTableName() + " ALTER COLUMN " + newDef.columnName;
if (newDef.columnDefault == null) {
@@ -242,7 +270,7 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
Exception {
if ("vacuum".equals(name)) {
- DatabaseQueryUtility.executeUpdate(this,"vacuum " + getTableName());
+ DatabaseQueryUtility.executeUpdate(this, "vacuum " + getTableName());
}
return null;
}
@@ -301,4 +329,4 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
return buf.toString();
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
index 3114a60..9d5864a 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,23 +13,27 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Connection;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers postgres tables
@@ -37,34 +41,45 @@ import org.rhq.core.util.jdbc.JDBCUtil;
* @author Greg Hinkle
*/
public class PostgresTableDiscoveryComponent implements ResourceDiscoveryComponent<PostgresDatabaseComponent> {
- private static final Log log = LogFactory.getLog(PostgresTableDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresTableDiscoveryComponent.class);
public static final String TABLE_NAMES_QUERY = "select relname from pg_stat_user_tables";
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<PostgresDatabaseComponent> context)
throws Exception {
- log.debug("Discovering postgres tables for " + context.getParentResourceComponent().getDatabaseName() + "...");
- Set<DiscoveredResourceDetails> discoveredTables = new HashSet<DiscoveredResourceDetails>();
-
- Connection connection = context.getParentResourceComponent().getConnection();
- if (connection == null) // For databases we don't have access to don't find the tables
- {
- return discoveredTables;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovering postgres tables for " + context.getParentResourceComponent().getDatabaseName()
+ + "...");
}
+ Set<DiscoveredResourceDetails> discoveredTables = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
+ Statement statement = null;
ResultSet resultSet = null;
- Statement statement = connection.createStatement();
- resultSet = statement.executeQuery(TABLE_NAMES_QUERY);
- while (resultSet.next()) {
- String tableName = resultSet.getString(1);
- DiscoveredResourceDetails service = new DiscoveredResourceDetails(context.getResourceType(), tableName,
- tableName, null, null, null, null);
- service.getPluginConfiguration().put(new PropertySimple("tableName", tableName));
- discoveredTables.add(service);
+ try {
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery(TABLE_NAMES_QUERY);
+ while (resultSet.next()) {
+ String tableName = resultSet.getString(1);
+ DiscoveredResourceDetails service = new DiscoveredResourceDetails(context.getResourceType(), tableName,
+ tableName, null, null, null, null);
+ service.getPluginConfiguration().put(new PropertySimple("tableName", tableName));
+ discoveredTables.add(service);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found " + discoveredTables.size() + " tables in database "
+ + context.getParentResourceComponent().getDatabaseName());
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Could not find tables in database " + context.getParentResourceComponent().getDatabaseName(), e);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
- JDBCUtil.safeClose(statement, resultSet);
-
return discoveredTables;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
index 92a5203..064c3ec 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -25,6 +29,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Set;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.PropertySimple;
@@ -37,15 +42,17 @@ import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.DeleteResourceFacet;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
*/
-public class PostgresUserComponent implements DatabaseComponent<PostgresServerComponent<?>>, MeasurementFacet,
- ConfigurationFacet, DeleteResourceFacet {
+public class PostgresUserComponent implements DatabaseComponent<PostgresServerComponent<?>>, ConnectionPoolingSupport,
+ MeasurementFacet, ConfigurationFacet, DeleteResourceFacet {
+
private ResourceContext<PostgresServerComponent<?>> resourceContext;
public void start(ResourceContext<PostgresServerComponent<?>> resourceContext) {
@@ -56,13 +63,22 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
this.resourceContext = null;
}
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return resourceContext.getParentResourceComponent().getPooledConnectionProvider();
+ }
+
public String getUserName() {
return this.resourceContext.getPluginConfiguration().getSimpleValue("userName", null);
}
public AvailabilityType getAvailability() {
- if (DatabaseQueryUtility.getSingleNumericQueryValue(this, "SELECT COUNT(*) FROM PG_ROLES WHERE rolname = ?",
- getUserName()) == 1) {
+ if (getSingleNumericQueryValue(this, "SELECT COUNT(*) FROM PG_ROLES WHERE rolname = ?", getUserName()) == 1) {
return AvailabilityType.UP;
} else {
return AvailabilityType.DOWN;
@@ -78,7 +94,7 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this,
+ Map<String, Double> values = getNumericQueryValues(this,
"SELECT (SELECT COUNT(*) FROM pg_stat_activity where usename = ? AND current_query != '<IDLE>') AS active,\n"
+ " (SELECT COUNT(*) FROM pg_stat_activity WHERE usename = ?) AS total", getUserName(), getUserName());
@@ -88,49 +104,49 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
}
public Configuration loadResourceConfiguration() throws Exception {
+ Connection connection = null;
PreparedStatement statement = null;
- ResultSet rs = null;
+ ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement("SELECT * FROM PG_ROLES WHERE rolname = ?");
+ connection = getPooledConnectionProvider().getPooledConnection();
+ statement = connection.prepareStatement("SELECT * FROM PG_ROLES WHERE rolname = ?");
statement.setString(1, getUserName());
- rs = statement.executeQuery();
- rs.next();
+ resultSet = statement.executeQuery();
+ resultSet.next();
Configuration config = new Configuration();
- config.put(new PropertySimple("user", rs.getString("rolname")));
- config.put(new PropertySimple("canLogin", rs.getBoolean("rolcanlogin")));
- config.put(new PropertySimple("inheritRights", rs.getBoolean("rolinherit")));
- config.put(new PropertySimple("superuser", rs.getBoolean("rolsuper")));
- config.put(new PropertySimple("canCreateDatabaseObjects", rs.getBoolean("rolcreatedb")));
- config.put(new PropertySimple("canCreateRoles", rs.getBoolean("rolcreaterole")));
- config.put(new PropertySimple("canModifyCatalogDirectly", rs.getBoolean("rolcatupdate")));
- config.put(new PropertySimple("connectionLimit", rs.getInt("rolconnlimit")));
+ config.put(new PropertySimple("user", resultSet.getString("rolname")));
+ config.put(new PropertySimple("canLogin", resultSet.getBoolean("rolcanlogin")));
+ config.put(new PropertySimple("inheritRights", resultSet.getBoolean("rolinherit")));
+ config.put(new PropertySimple("superuser", resultSet.getBoolean("rolsuper")));
+ config.put(new PropertySimple("canCreateDatabaseObjects", resultSet.getBoolean("rolcreatedb")));
+ config.put(new PropertySimple("canCreateRoles", resultSet.getBoolean("rolcreaterole")));
+ config.put(new PropertySimple("canModifyCatalogDirectly", resultSet.getBoolean("rolcatupdate")));
+ config.put(new PropertySimple("connectionLimit", resultSet.getInt("rolconnlimit")));
return config;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
- JDBCUtil.safeClose(statement, rs);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
}
public void updateResourceConfiguration(ConfigurationUpdateReport report) {
Configuration config = report.getConfiguration();
+ String sql = getUserSQL(config, UpdateType.ALTER);
+ Connection connection = null;
Statement statement = null;
- String sql = getUserSQL(config, UpdateType.ALTER);
try {
+ connection = getPooledConnectionProvider().getPooledConnection();
statement = getConnection().createStatement();
- int updates = statement.executeUpdate(sql);
- if (updates != 1) {
- report.setErrorMessage("Failed to update user " + config.getSimpleValue("user", null));
- } else {
- report.setStatus(ConfigurationUpdateStatus.SUCCESS);
- }
+ statement.executeUpdate(sql);
+ report.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (SQLException e) {
report.setErrorMessageFromThrowable(e);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(connection, statement);
}
}
@@ -145,36 +161,32 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
connectionLimit = connLimit.getIntegerValue();
}
- String sql = type.name()
- + " USER "
- + config.getSimpleValue("user", null)
- + " ";
+ String sql = type.name() + " USER " + config.getSimpleValue("user", null) + " ";
if (type != UpdateType.DROP) {
- String password = config.getSimpleValue("password",null);
+ String password = config.getSimpleValue("password", null);
if (password != null && password.length() != 0) {
- sql += " WITH PASSWORD '" + config.getSimpleValue("password",null) + "' ";
+ sql += " WITH PASSWORD '" + config.getSimpleValue("password", null) + "' ";
}
sql += (config.getSimple("canCreateDatabaseObjects").getBooleanValue() ? "CREATEDB " : "NOCREATEDB ");
sql += (config.getSimple("canCreateRoles").getBooleanValue() ? "CREATEUSER " : "NOCREATEUSER ");
- sql += (connectionLimit > -1) ? ("CONNECTION LIMIT " + connectionLimit): "";
+ sql += (connectionLimit > -1) ? ("CONNECTION LIMIT " + connectionLimit) : "";
}
return sql;
}
public void deleteResource() throws Exception {
- Statement statement = null;
String sql = "DROP USER " + this.resourceContext.getResourceKey();
+ Connection connection = null;
+ Statement statement = null;
try {
+ connection = getPooledConnectionProvider().getPooledConnection();
statement = getConnection().createStatement();
-
- // Note: Postgres doesn't seem to return the expected update count of 1 here... but this does work
- // Note: executeUpdate() returns 0 for statements that don't return rows like e.g. drop xxx
statement.executeUpdate(sql);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(connection, statement);
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
index 9c7ae80..6301cdb 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Connection;
@@ -31,7 +32,7 @@ import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers Postgres Users though shouldn't need super user access
@@ -39,25 +40,22 @@ import org.rhq.core.util.jdbc.JDBCUtil;
* @author Greg Hinkle
*/
public class PostgresUserDiscoveryComponent implements ResourceDiscoveryComponent<PostgresServerComponent<?>> {
- private static final Log log = LogFactory.getLog(PostgresUserDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresUserDiscoveryComponent.class);
public static final String USERS_QUERY = "select * from pg_roles";
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<PostgresServerComponent<?>> context)
throws Exception {
- log.debug("Discovering postgres users for " + context.getParentResourceComponent().getJDBCUrl() + "...");
- Set<DiscoveredResourceDetails> discoveredUsers = new HashSet<DiscoveredResourceDetails>();
-
- Connection connection = context.getParentResourceComponent().getConnection();
- if (connection == null) // For databases we don't have access to don't find the tables
- {
- return discoveredUsers;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovering postgres users for " + context.getParentResourceComponent().getJDBCUrl() + "...");
}
+ Set<DiscoveredResourceDetails> discoveredUsers = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
-
try {
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(USERS_QUERY);
while (resultSet.next()) {
@@ -68,9 +66,9 @@ public class PostgresUserDiscoveryComponent implements ResourceDiscoveryComponen
discoveredUsers.add(service);
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
return discoveredUsers;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
index a80652e..3c5765a 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres.util;
import java.io.BufferedOutputStream;
@@ -33,11 +34,13 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.rhq.core.util.jdbc.JDBCUtil;
+
+import org.rhq.core.util.stream.StreamUtil;
/**
* Represents a PostgreSQL configuration file (i.e. postgresql.conf) - provides methods for reading and updating
@@ -99,7 +102,7 @@ public class PostgresqlConfFile {
}
}
} finally {
- JDBCUtil.safeClose(r);
+ StreamUtil.safeClose(r);
}
}
@@ -261,4 +264,4 @@ public class PostgresqlConfFile {
return this.configurationFile.toString();
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java b/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
index bc0d247..c1e9e60 100644
--- a/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
+++ b/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres.test;
import java.io.File;
@@ -25,6 +26,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.annotations.AfterSuite;
@@ -171,4 +173,4 @@ public class PostgresPluginTest {
}
}
}
-}
\ No newline at end of file
+}
commit 89f82d19be7b225111a10704db75c97850ba9aa5
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Dec 23 14:50:55 2013 +0100
Plugin validation for mysql plugin was failing because the Class.forName() statement was invoked from the constructor of the component. I moved this code to the method that actually opens the connection. This is the same strategy we use with our postgres plugin.
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
index 9dd60ee..6c81332 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
@@ -42,11 +42,6 @@ class MySqlConnectionManager {
private MySqlConnectionManager() {
connections = new HashMap<MySqlConnectionInfo,Connection>();
- try {
- Class.forName("com.mysql.jdbc.Driver").newInstance();
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
}
static MySqlConnectionManager getConnectionManager() {
@@ -93,6 +88,12 @@ class MySqlConnectionManager {
}
Connection getConnection (MySqlConnectionInfo info) throws SQLException {
+ try {
+ Class.forName("com.mysql.jdbc.Driver");
+ } catch (Exception ex) {
+ logger.error("Unable to find com.mysql.jdbc.Driver");
+ }
+
Connection conn = connections.get(info);
String url = info.buildURL();
if (conn == null) {
commit e649c531e6ff25e706685c99fc3f560d49aaf9dc
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Fri Dec 20 15:26:40 2013 -0800
Upgrade to d3 version version 3.3.13.
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
index 6fcd585..b546776 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/AvailabilityOverUnderGraphType.java
@@ -140,6 +140,7 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
* The magic JSNI to draw the charts with d3.
*/
public native void drawJsniChart() /*-{
+ "use strict";
//console.log("Draw Enhanced Availability chart");
var global = this,
@@ -163,14 +164,11 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
var availabilityGraph = function () {
- "use strict";
- // privates
var margin = {top: 5, right: 5, bottom: 5, left: 40},
barOffset = 10,
width = 750 - margin.left - margin.right + barOffset,
- height = 40 - margin.top - margin.bottom,
- svg;
+ height = 40 - margin.top - margin.bottom;
function drawBars(availChartContext) {
@@ -362,7 +360,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
return {
// Public API
draw: function (availChartContext) {
- "use strict";
drawBars(availChartContext);
}
}; // end public closure
@@ -426,4 +423,6 @@ public class AvailabilityOverUnderGraphType implements AvailabilityGraphType {
public String getChartXaxisTimeFormatHoursMinutes() {
return MSG.chart_xaxis_time_format_hours_minutes();
}
+
+
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
index 33e3d97..17eeec3 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/common/graph/graphtype/StackedBarMetricGraphImpl.java
@@ -40,6 +40,7 @@ public class StackedBarMetricGraphImpl extends AbstractMetricGraph {
*/
@Override
public native void drawJsniChart() /*-{
+ "use strict";
//console.log("Draw Stacked Bar jsni chart");
var global = this,
@@ -759,7 +760,6 @@ public class StackedBarMetricGraphImpl extends AbstractMetricGraph {
return {
// Public API
draw: function (chartContext) {
- "use strict";
// Guard condition that can occur when a portlet has not been configured yet
if (chartContext.data.length > 0) {
//console.log("Creating Chart: "+ chartContext.chartSelection + " --> "+ chartContext.chartTitle);
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/CoreGUI.gwt.xml b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/CoreGUI.gwt.xml
index 21eb46b..ba23b4d 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/CoreGUI.gwt.xml
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/CoreGUI.gwt.xml
@@ -57,7 +57,7 @@
-->
<script src="/coregui/js/jquery-1.7.2.min.js"/>
<script src="/coregui/js/jquery.sparkline-2.1.min.js"/>
- <script src="/coregui/js/d3.v3.min.js"/>
+ <script src="/coregui/js/d3.v3.3.13.min.js"/>
<!--
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.js b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.js
new file mode 100755
index 0000000..16087fd
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.js
@@ -0,0 +1,9293 @@
+d3 = function() {
+ var d3 = {
+ version: "3.3.13"
+ };
+ if (!Date.now) Date.now = function() {
+ return +new Date();
+ };
+ var d3_arraySlice = [].slice, d3_array = function(list) {
+ return d3_arraySlice.call(list);
+ };
+ var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
+ try {
+ d3_array(d3_documentElement.childNodes)[0].nodeType;
+ } catch (e) {
+ d3_array = function(list) {
+ var i = list.length, array = new Array(i);
+ while (i--) array[i] = list[i];
+ return array;
+ };
+ }
+ try {
+ d3_document.createElement("div").style.setProperty("opacity", 0, "");
+ } catch (error) {
+ var d3_element_prototype = d3_window.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
+ d3_element_prototype.setAttribute = function(name, value) {
+ d3_element_setAttribute.call(this, name, value + "");
+ };
+ d3_element_prototype.setAttributeNS = function(space, local, value) {
+ d3_element_setAttributeNS.call(this, space, local, value + "");
+ };
+ d3_style_prototype.setProperty = function(name, value, priority) {
+ d3_style_setProperty.call(this, name, value + "", priority);
+ };
+ }
+ d3.ascending = function(a, b) {
+ return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
+ };
+ d3.descending = function(a, b) {
+ return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
+ };
+ d3.min = function(array, f) {
+ var i = -1, n = array.length, a, b;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = array[i]) != null && a > b) a = b;
+ } else {
+ while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
+ }
+ return a;
+ };
+ d3.max = function(array, f) {
+ var i = -1, n = array.length, a, b;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = array[i]) != null && b > a) a = b;
+ } else {
+ while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
+ }
+ return a;
+ };
+ d3.extent = function(array, f) {
+ var i = -1, n = array.length, a, b, c;
+ if (arguments.length === 1) {
+ while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined;
+ while (++i < n) if ((b = array[i]) != null) {
+ if (a > b) a = b;
+ if (c < b) c = b;
+ }
+ } else {
+ while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
+ while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
+ if (a > b) a = b;
+ if (c < b) c = b;
+ }
+ }
+ return [ a, c ];
+ };
+ d3.sum = function(array, f) {
+ var s = 0, n = array.length, a, i = -1;
+ if (arguments.length === 1) {
+ while (++i < n) if (!isNaN(a = +array[i])) s += a;
+ } else {
+ while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a;
+ }
+ return s;
+ };
+ function d3_number(x) {
+ return x != null && !isNaN(x);
+ }
+ d3.mean = function(array, f) {
+ var n = array.length, a, m = 0, i = -1, j = 0;
+ if (arguments.length === 1) {
+ while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
+ } else {
+ while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
+ }
+ return j ? m : undefined;
+ };
+ d3.quantile = function(values, p) {
+ var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
+ return e ? v + e * (values[h] - v) : v;
+ };
+ d3.median = function(array, f) {
+ if (arguments.length > 1) array = array.map(f);
+ array = array.filter(d3_number);
+ return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined;
+ };
+ d3.bisector = function(f) {
+ return {
+ left: function(a, x, lo, hi) {
+ if (arguments.length < 3) lo = 0;
+ if (arguments.length < 4) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid;
+ }
+ return lo;
+ },
+ right: function(a, x, lo, hi) {
+ if (arguments.length < 3) lo = 0;
+ if (arguments.length < 4) hi = a.length;
+ while (lo < hi) {
+ var mid = lo + hi >>> 1;
+ if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1;
+ }
+ return lo;
+ }
+ };
+ };
+ var d3_bisector = d3.bisector(function(d) {
+ return d;
+ });
+ d3.bisectLeft = d3_bisector.left;
+ d3.bisect = d3.bisectRight = d3_bisector.right;
+ d3.shuffle = function(array) {
+ var m = array.length, t, i;
+ while (m) {
+ i = Math.random() * m-- | 0;
+ t = array[m], array[m] = array[i], array[i] = t;
+ }
+ return array;
+ };
+ d3.permute = function(array, indexes) {
+ var i = indexes.length, permutes = new Array(i);
+ while (i--) permutes[i] = array[indexes[i]];
+ return permutes;
+ };
+ d3.pairs = function(array) {
+ var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n);
+ while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ];
+ return pairs;
+ };
+ d3.zip = function() {
+ if (!(n = arguments.length)) return [];
+ for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) {
+ for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) {
+ zip[j] = arguments[j][i];
+ }
+ }
+ return zips;
+ };
+ function d3_zipLength(d) {
+ return d.length;
+ }
+ d3.transpose = function(matrix) {
+ return d3.zip.apply(d3, matrix);
+ };
+ d3.keys = function(map) {
+ var keys = [];
+ for (var key in map) keys.push(key);
+ return keys;
+ };
+ d3.values = function(map) {
+ var values = [];
+ for (var key in map) values.push(map[key]);
+ return values;
+ };
+ d3.entries = function(map) {
+ var entries = [];
+ for (var key in map) entries.push({
+ key: key,
+ value: map[key]
+ });
+ return entries;
+ };
+ d3.merge = function(arrays) {
+ var n = arrays.length, m, i = -1, j = 0, merged, array;
+ while (++i < n) j += arrays[i].length;
+ merged = new Array(j);
+ while (--n >= 0) {
+ array = arrays[n];
+ m = array.length;
+ while (--m >= 0) {
+ merged[--j] = array[m];
+ }
+ }
+ return merged;
+ };
+ var abs = Math.abs;
+ d3.range = function(start, stop, step) {
+ if (arguments.length < 3) {
+ step = 1;
+ if (arguments.length < 2) {
+ stop = start;
+ start = 0;
+ }
+ }
+ if ((stop - start) / step === Infinity) throw new Error("infinite range");
+ var range = [], k = d3_range_integerScale(abs(step)), i = -1, j;
+ start *= k, stop *= k, step *= k;
+ if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
+ return range;
+ };
+ function d3_range_integerScale(x) {
+ var k = 1;
+ while (x * k % 1) k *= 10;
+ return k;
+ }
+ function d3_class(ctor, properties) {
+ try {
+ for (var key in properties) {
+ Object.defineProperty(ctor.prototype, key, {
+ value: properties[key],
+ enumerable: false
+ });
+ }
+ } catch (e) {
+ ctor.prototype = properties;
+ }
+ }
+ d3.map = function(object) {
+ var map = new d3_Map();
+ if (object instanceof d3_Map) object.forEach(function(key, value) {
+ map.set(key, value);
+ }); else for (var key in object) map.set(key, object[key]);
+ return map;
+ };
+ function d3_Map() {}
+ d3_class(d3_Map, {
+ has: function(key) {
+ return d3_map_prefix + key in this;
+ },
+ get: function(key) {
+ return this[d3_map_prefix + key];
+ },
+ set: function(key, value) {
+ return this[d3_map_prefix + key] = value;
+ },
+ remove: function(key) {
+ key = d3_map_prefix + key;
+ return key in this && delete this[key];
+ },
+ keys: function() {
+ var keys = [];
+ this.forEach(function(key) {
+ keys.push(key);
+ });
+ return keys;
+ },
+ values: function() {
+ var values = [];
+ this.forEach(function(key, value) {
+ values.push(value);
+ });
+ return values;
+ },
+ entries: function() {
+ var entries = [];
+ this.forEach(function(key, value) {
+ entries.push({
+ key: key,
+ value: value
+ });
+ });
+ return entries;
+ },
+ forEach: function(f) {
+ for (var key in this) {
+ if (key.charCodeAt(0) === d3_map_prefixCode) {
+ f.call(this, key.substring(1), this[key]);
+ }
+ }
+ }
+ });
+ var d3_map_prefix = "\x00", d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
+ d3.nest = function() {
+ var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
+ function map(mapType, array, depth) {
+ if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
+ var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
+ while (++i < n) {
+ if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
+ values.push(object);
+ } else {
+ valuesByKey.set(keyValue, [ object ]);
+ }
+ }
+ if (mapType) {
+ object = mapType();
+ setter = function(keyValue, values) {
+ object.set(keyValue, map(mapType, values, depth));
+ };
+ } else {
+ object = {};
+ setter = function(keyValue, values) {
+ object[keyValue] = map(mapType, values, depth);
+ };
+ }
+ valuesByKey.forEach(setter);
+ return object;
+ }
+ function entries(map, depth) {
+ if (depth >= keys.length) return map;
+ var array = [], sortKey = sortKeys[depth++];
+ map.forEach(function(key, keyMap) {
+ array.push({
+ key: key,
+ values: entries(keyMap, depth)
+ });
+ });
+ return sortKey ? array.sort(function(a, b) {
+ return sortKey(a.key, b.key);
+ }) : array;
+ }
+ nest.map = function(array, mapType) {
+ return map(mapType, array, 0);
+ };
+ nest.entries = function(array) {
+ return entries(map(d3.map, array, 0), 0);
+ };
+ nest.key = function(d) {
+ keys.push(d);
+ return nest;
+ };
+ nest.sortKeys = function(order) {
+ sortKeys[keys.length - 1] = order;
+ return nest;
+ };
+ nest.sortValues = function(order) {
+ sortValues = order;
+ return nest;
+ };
+ nest.rollup = function(f) {
+ rollup = f;
+ return nest;
+ };
+ return nest;
+ };
+ d3.set = function(array) {
+ var set = new d3_Set();
+ if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]);
+ return set;
+ };
+ function d3_Set() {}
+ d3_class(d3_Set, {
+ has: function(value) {
+ return d3_map_prefix + value in this;
+ },
+ add: function(value) {
+ this[d3_map_prefix + value] = true;
+ return value;
+ },
+ remove: function(value) {
+ value = d3_map_prefix + value;
+ return value in this && delete this[value];
+ },
+ values: function() {
+ var values = [];
+ this.forEach(function(value) {
+ values.push(value);
+ });
+ return values;
+ },
+ forEach: function(f) {
+ for (var value in this) {
+ if (value.charCodeAt(0) === d3_map_prefixCode) {
+ f.call(this, value.substring(1));
+ }
+ }
+ }
+ });
+ d3.behavior = {};
+ d3.rebind = function(target, source) {
+ var i = 1, n = arguments.length, method;
+ while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
+ return target;
+ };
+ function d3_rebind(target, source, method) {
+ return function() {
+ var value = method.apply(source, arguments);
+ return value === source ? target : value;
+ };
+ }
+ function d3_vendorSymbol(object, name) {
+ if (name in object) return name;
+ name = name.charAt(0).toUpperCase() + name.substring(1);
+ for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
+ var prefixName = d3_vendorPrefixes[i] + name;
+ if (prefixName in object) return prefixName;
+ }
+ }
+ var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
+ function d3_noop() {}
+ d3.dispatch = function() {
+ var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
+ while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+ return dispatch;
+ };
+ function d3_dispatch() {}
+ d3_dispatch.prototype.on = function(type, listener) {
+ var i = type.indexOf("."), name = "";
+ if (i >= 0) {
+ name = type.substring(i + 1);
+ type = type.substring(0, i);
+ }
+ if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
+ if (arguments.length === 2) {
+ if (listener == null) for (type in this) {
+ if (this.hasOwnProperty(type)) this[type].on(name, null);
+ }
+ return this;
+ }
+ };
+ function d3_dispatch_event(dispatch) {
+ var listeners = [], listenerByName = new d3_Map();
+ function event() {
+ var z = listeners, i = -1, n = z.length, l;
+ while (++i < n) if (l = z[i].on) l.apply(this, arguments);
+ return dispatch;
+ }
+ event.on = function(name, listener) {
+ var l = listenerByName.get(name), i;
+ if (arguments.length < 2) return l && l.on;
+ if (l) {
+ l.on = null;
+ listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
+ listenerByName.remove(name);
+ }
+ if (listener) listeners.push(listenerByName.set(name, {
+ on: listener
+ }));
+ return dispatch;
+ };
+ return event;
+ }
+ d3.event = null;
+ function d3_eventPreventDefault() {
+ d3.event.preventDefault();
+ }
+ function d3_eventSource() {
+ var e = d3.event, s;
+ while (s = e.sourceEvent) e = s;
+ return e;
+ }
+ function d3_eventDispatch(target) {
+ var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
+ while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
+ dispatch.of = function(thiz, argumentz) {
+ return function(e1) {
+ try {
+ var e0 = e1.sourceEvent = d3.event;
+ e1.target = target;
+ d3.event = e1;
+ dispatch[e1.type].apply(thiz, argumentz);
+ } finally {
+ d3.event = e0;
+ }
+ };
+ };
+ return dispatch;
+ }
+ d3.requote = function(s) {
+ return s.replace(d3_requote_re, "\\$&");
+ };
+ var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
+ var d3_subclass = {}.__proto__ ? function(object, prototype) {
+ object.__proto__ = prototype;
+ } : function(object, prototype) {
+ for (var property in prototype) object[property] = prototype[property];
+ };
+ function d3_selection(groups) {
+ d3_subclass(groups, d3_selectionPrototype);
+ return groups;
+ }
+ var d3_select = function(s, n) {
+ return n.querySelector(s);
+ }, d3_selectAll = function(s, n) {
+ return n.querySelectorAll(s);
+ }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) {
+ return d3_selectMatcher.call(n, s);
+ };
+ if (typeof Sizzle === "function") {
+ d3_select = function(s, n) {
+ return Sizzle(s, n)[0] || null;
+ };
+ d3_selectAll = function(s, n) {
+ return Sizzle.uniqueSort(Sizzle(s, n));
+ };
+ d3_selectMatches = Sizzle.matchesSelector;
+ }
+ d3.selection = function() {
+ return d3_selectionRoot;
+ };
+ var d3_selectionPrototype = d3.selection.prototype = [];
+ d3_selectionPrototype.select = function(selector) {
+ var subgroups = [], subgroup, subnode, group, node;
+ selector = d3_selection_selector(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = (group = this[j]).parentNode;
+ for (var i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroup.push(subnode = selector.call(node, node.__data__, i, j));
+ if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_selector(selector) {
+ return typeof selector === "function" ? selector : function() {
+ return d3_select(selector, this);
+ };
+ }
+ d3_selectionPrototype.selectAll = function(selector) {
+ var subgroups = [], subgroup, node;
+ selector = d3_selection_selectorAll(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j)));
+ subgroup.parentNode = node;
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_selectorAll(selector) {
+ return typeof selector === "function" ? selector : function() {
+ return d3_selectAll(selector, this);
+ };
+ }
+ var d3_nsPrefix = {
+ svg: "http://www.w3.org/2000/svg",
+ xhtml: "http://www.w3.org/1999/xhtml",
+ xlink: "http://www.w3.org/1999/xlink",
+ xml: "http://www.w3.org/XML/1998/namespace",
+ xmlns: "http://www.w3.org/2000/xmlns/"
+ };
+ d3.ns = {
+ prefix: d3_nsPrefix,
+ qualify: function(name) {
+ var i = name.indexOf(":"), prefix = name;
+ if (i >= 0) {
+ prefix = name.substring(0, i);
+ name = name.substring(i + 1);
+ }
+ return d3_nsPrefix.hasOwnProperty(prefix) ? {
+ space: d3_nsPrefix[prefix],
+ local: name
+ } : name;
+ }
+ };
+ d3_selectionPrototype.attr = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") {
+ var node = this.node();
+ name = d3.ns.qualify(name);
+ return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
+ }
+ for (value in name) this.each(d3_selection_attr(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_attr(name, value));
+ };
+ function d3_selection_attr(name, value) {
+ name = d3.ns.qualify(name);
+ function attrNull() {
+ this.removeAttribute(name);
+ }
+ function attrNullNS() {
+ this.removeAttributeNS(name.space, name.local);
+ }
+ function attrConstant() {
+ this.setAttribute(name, value);
+ }
+ function attrConstantNS() {
+ this.setAttributeNS(name.space, name.local, value);
+ }
+ function attrFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
+ }
+ function attrFunctionNS() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
+ }
+ return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
+ }
+ function d3_collapse(s) {
+ return s.trim().replace(/\s+/g, " ");
+ }
+ d3_selectionPrototype.classed = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") {
+ var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1;
+ if (value = node.classList) {
+ while (++i < n) if (!value.contains(name[i])) return false;
+ } else {
+ value = node.getAttribute("class");
+ while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
+ }
+ return true;
+ }
+ for (value in name) this.each(d3_selection_classed(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_classed(name, value));
+ };
+ function d3_selection_classedRe(name) {
+ return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
+ }
+ function d3_selection_classes(name) {
+ return name.trim().split(/^|\s+/);
+ }
+ function d3_selection_classed(name, value) {
+ name = d3_selection_classes(name).map(d3_selection_classedName);
+ var n = name.length;
+ function classedConstant() {
+ var i = -1;
+ while (++i < n) name[i](this, value);
+ }
+ function classedFunction() {
+ var i = -1, x = value.apply(this, arguments);
+ while (++i < n) name[i](this, x);
+ }
+ return typeof value === "function" ? classedFunction : classedConstant;
+ }
+ function d3_selection_classedName(name) {
+ var re = d3_selection_classedRe(name);
+ return function(node, value) {
+ if (c = node.classList) return value ? c.add(name) : c.remove(name);
+ var c = node.getAttribute("class") || "";
+ if (value) {
+ re.lastIndex = 0;
+ if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
+ } else {
+ node.setAttribute("class", d3_collapse(c.replace(re, " ")));
+ }
+ };
+ }
+ d3_selectionPrototype.style = function(name, value, priority) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof name !== "string") {
+ if (n < 2) value = "";
+ for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
+ return this;
+ }
+ if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
+ priority = "";
+ }
+ return this.each(d3_selection_style(name, value, priority));
+ };
+ function d3_selection_style(name, value, priority) {
+ function styleNull() {
+ this.style.removeProperty(name);
+ }
+ function styleConstant() {
+ this.style.setProperty(name, value, priority);
+ }
+ function styleFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
+ }
+ return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
+ }
+ d3_selectionPrototype.property = function(name, value) {
+ if (arguments.length < 2) {
+ if (typeof name === "string") return this.node()[name];
+ for (value in name) this.each(d3_selection_property(value, name[value]));
+ return this;
+ }
+ return this.each(d3_selection_property(name, value));
+ };
+ function d3_selection_property(name, value) {
+ function propertyNull() {
+ delete this[name];
+ }
+ function propertyConstant() {
+ this[name] = value;
+ }
+ function propertyFunction() {
+ var x = value.apply(this, arguments);
+ if (x == null) delete this[name]; else this[name] = x;
+ }
+ return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
+ }
+ d3_selectionPrototype.text = function(value) {
+ return arguments.length ? this.each(typeof value === "function" ? function() {
+ var v = value.apply(this, arguments);
+ this.textContent = v == null ? "" : v;
+ } : value == null ? function() {
+ this.textContent = "";
+ } : function() {
+ this.textContent = value;
+ }) : this.node().textContent;
+ };
+ d3_selectionPrototype.html = function(value) {
+ return arguments.length ? this.each(typeof value === "function" ? function() {
+ var v = value.apply(this, arguments);
+ this.innerHTML = v == null ? "" : v;
+ } : value == null ? function() {
+ this.innerHTML = "";
+ } : function() {
+ this.innerHTML = value;
+ }) : this.node().innerHTML;
+ };
+ d3_selectionPrototype.append = function(name) {
+ name = d3_selection_creator(name);
+ return this.select(function() {
+ return this.appendChild(name.apply(this, arguments));
+ });
+ };
+ function d3_selection_creator(name) {
+ return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? function() {
+ return this.ownerDocument.createElementNS(name.space, name.local);
+ } : function() {
+ return this.ownerDocument.createElementNS(this.namespaceURI, name);
+ };
+ }
+ d3_selectionPrototype.insert = function(name, before) {
+ name = d3_selection_creator(name);
+ before = d3_selection_selector(before);
+ return this.select(function() {
+ return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null);
+ });
+ };
+ d3_selectionPrototype.remove = function() {
+ return this.each(function() {
+ var parent = this.parentNode;
+ if (parent) parent.removeChild(this);
+ });
+ };
+ d3_selectionPrototype.data = function(value, key) {
+ var i = -1, n = this.length, group, node;
+ if (!arguments.length) {
+ value = new Array(n = (group = this[0]).length);
+ while (++i < n) {
+ if (node = group[i]) {
+ value[i] = node.__data__;
+ }
+ }
+ return value;
+ }
+ function bind(group, groupData) {
+ var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
+ if (key) {
+ var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue;
+ for (i = -1; ++i < n; ) {
+ keyValue = key.call(node = group[i], node.__data__, i);
+ if (nodeByKeyValue.has(keyValue)) {
+ exitNodes[i] = node;
+ } else {
+ nodeByKeyValue.set(keyValue, node);
+ }
+ keyValues.push(keyValue);
+ }
+ for (i = -1; ++i < m; ) {
+ keyValue = key.call(groupData, nodeData = groupData[i], i);
+ if (node = nodeByKeyValue.get(keyValue)) {
+ updateNodes[i] = node;
+ node.__data__ = nodeData;
+ } else if (!dataByKeyValue.has(keyValue)) {
+ enterNodes[i] = d3_selection_dataNode(nodeData);
+ }
+ dataByKeyValue.set(keyValue, nodeData);
+ nodeByKeyValue.remove(keyValue);
+ }
+ for (i = -1; ++i < n; ) {
+ if (nodeByKeyValue.has(keyValues[i])) {
+ exitNodes[i] = group[i];
+ }
+ }
+ } else {
+ for (i = -1; ++i < n0; ) {
+ node = group[i];
+ nodeData = groupData[i];
+ if (node) {
+ node.__data__ = nodeData;
+ updateNodes[i] = node;
+ } else {
+ enterNodes[i] = d3_selection_dataNode(nodeData);
+ }
+ }
+ for (;i < m; ++i) {
+ enterNodes[i] = d3_selection_dataNode(groupData[i]);
+ }
+ for (;i < n; ++i) {
+ exitNodes[i] = group[i];
+ }
+ }
+ enterNodes.update = updateNodes;
+ enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
+ enter.push(enterNodes);
+ update.push(updateNodes);
+ exit.push(exitNodes);
+ }
+ var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
+ if (typeof value === "function") {
+ while (++i < n) {
+ bind(group = this[i], value.call(group, group.parentNode.__data__, i));
+ }
+ } else {
+ while (++i < n) {
+ bind(group = this[i], value);
+ }
+ }
+ update.enter = function() {
+ return enter;
+ };
+ update.exit = function() {
+ return exit;
+ };
+ return update;
+ };
+ function d3_selection_dataNode(data) {
+ return {
+ __data__: data
+ };
+ }
+ d3_selectionPrototype.datum = function(value) {
+ return arguments.length ? this.property("__data__", value) : this.property("__data__");
+ };
+ d3_selectionPrototype.filter = function(filter) {
+ var subgroups = [], subgroup, group, node;
+ if (typeof filter !== "function") filter = d3_selection_filter(filter);
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = (group = this[j]).parentNode;
+ for (var i = 0, n = group.length; i < n; i++) {
+ if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+ subgroup.push(node);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ function d3_selection_filter(selector) {
+ return function() {
+ return d3_selectMatches(this, selector);
+ };
+ }
+ d3_selectionPrototype.order = function() {
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
+ if (node = group[i]) {
+ if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
+ next = node;
+ }
+ }
+ }
+ return this;
+ };
+ d3_selectionPrototype.sort = function(comparator) {
+ comparator = d3_selection_sortComparator.apply(this, arguments);
+ for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
+ return this.order();
+ };
+ function d3_selection_sortComparator(comparator) {
+ if (!arguments.length) comparator = d3.ascending;
+ return function(a, b) {
+ return a && b ? comparator(a.__data__, b.__data__) : !a - !b;
+ };
+ }
+ d3_selectionPrototype.each = function(callback) {
+ return d3_selection_each(this, function(node, i, j) {
+ callback.call(node, node.__data__, i, j);
+ });
+ };
+ function d3_selection_each(groups, callback) {
+ for (var j = 0, m = groups.length; j < m; j++) {
+ for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
+ if (node = group[i]) callback(node, i, j);
+ }
+ }
+ return groups;
+ }
+ d3_selectionPrototype.call = function(callback) {
+ var args = d3_array(arguments);
+ callback.apply(args[0] = this, args);
+ return this;
+ };
+ d3_selectionPrototype.empty = function() {
+ return !this.node();
+ };
+ d3_selectionPrototype.node = function() {
+ for (var j = 0, m = this.length; j < m; j++) {
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ var node = group[i];
+ if (node) return node;
+ }
+ }
+ return null;
+ };
+ d3_selectionPrototype.size = function() {
+ var n = 0;
+ this.each(function() {
+ ++n;
+ });
+ return n;
+ };
+ function d3_selection_enter(selection) {
+ d3_subclass(selection, d3_selection_enterPrototype);
+ return selection;
+ }
+ var d3_selection_enterPrototype = [];
+ d3.selection.enter = d3_selection_enter;
+ d3.selection.enter.prototype = d3_selection_enterPrototype;
+ d3_selection_enterPrototype.append = d3_selectionPrototype.append;
+ d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
+ d3_selection_enterPrototype.node = d3_selectionPrototype.node;
+ d3_selection_enterPrototype.call = d3_selectionPrototype.call;
+ d3_selection_enterPrototype.size = d3_selectionPrototype.size;
+ d3_selection_enterPrototype.select = function(selector) {
+ var subgroups = [], subgroup, subnode, upgroup, group, node;
+ for (var j = -1, m = this.length; ++j < m; ) {
+ upgroup = (group = this[j]).update;
+ subgroups.push(subgroup = []);
+ subgroup.parentNode = group.parentNode;
+ for (var i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j));
+ subnode.__data__ = node.__data__;
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_selection(subgroups);
+ };
+ d3_selection_enterPrototype.insert = function(name, before) {
+ if (arguments.length < 2) before = d3_selection_enterInsertBefore(this);
+ return d3_selectionPrototype.insert.call(this, name, before);
+ };
+ function d3_selection_enterInsertBefore(enter) {
+ var i0, j0;
+ return function(d, i, j) {
+ var group = enter[j].update, n = group.length, node;
+ if (j != j0) j0 = j, i0 = 0;
+ if (i >= i0) i0 = i + 1;
+ while (!(node = group[i0]) && ++i0 < n) ;
+ return node;
+ };
+ }
+ d3_selectionPrototype.transition = function() {
+ var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = d3_transitionInherit || {
+ time: Date.now(),
+ ease: d3_ease_cubicInOut,
+ delay: 0,
+ duration: 250
+ };
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) d3_transitionNode(node, i, id, transition);
+ subgroup.push(node);
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_selectionPrototype.interrupt = function() {
+ return this.each(d3_selection_interrupt);
+ };
+ function d3_selection_interrupt() {
+ var lock = this.__transition__;
+ if (lock) ++lock.active;
+ }
+ d3.select = function(node) {
+ var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
+ group.parentNode = d3_documentElement;
+ return d3_selection([ group ]);
+ };
+ d3.selectAll = function(nodes) {
+ var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
+ group.parentNode = d3_documentElement;
+ return d3_selection([ group ]);
+ };
+ var d3_selectionRoot = d3.select(d3_documentElement);
+ d3_selectionPrototype.on = function(type, listener, capture) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof type !== "string") {
+ if (n < 2) listener = false;
+ for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
+ return this;
+ }
+ if (n < 2) return (n = this.node()["__on" + type]) && n._;
+ capture = false;
+ }
+ return this.each(d3_selection_on(type, listener, capture));
+ };
+ function d3_selection_on(type, listener, capture) {
+ var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
+ if (i > 0) type = type.substring(0, i);
+ var filter = d3_selection_onFilters.get(type);
+ if (filter) type = filter, wrap = d3_selection_onFilter;
+ function onRemove() {
+ var l = this[name];
+ if (l) {
+ this.removeEventListener(type, l, l.$);
+ delete this[name];
+ }
+ }
+ function onAdd() {
+ var l = wrap(listener, d3_array(arguments));
+ onRemove.call(this);
+ this.addEventListener(type, this[name] = l, l.$ = capture);
+ l._ = listener;
+ }
+ function removeAll() {
+ var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
+ for (var name in this) {
+ if (match = name.match(re)) {
+ var l = this[name];
+ this.removeEventListener(match[1], l, l.$);
+ delete this[name];
+ }
+ }
+ }
+ return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
+ }
+ var d3_selection_onFilters = d3.map({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+ });
+ d3_selection_onFilters.forEach(function(k) {
+ if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
+ });
+ function d3_selection_onListener(listener, argumentz) {
+ return function(e) {
+ var o = d3.event;
+ d3.event = e;
+ argumentz[0] = this.__data__;
+ try {
+ listener.apply(this, argumentz);
+ } finally {
+ d3.event = o;
+ }
+ };
+ }
+ function d3_selection_onFilter(listener, argumentz) {
+ var l = d3_selection_onListener(listener, argumentz);
+ return function(e) {
+ var target = this, related = e.relatedTarget;
+ if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
+ l.call(target, e);
+ }
+ };
+ }
+ var d3_event_dragSelect = "onselectstart" in d3_document ? null : d3_vendorSymbol(d3_documentElement.style, "userSelect"), d3_event_dragId = 0;
+ function d3_event_dragSuppress() {
+ var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault);
+ if (d3_event_dragSelect) {
+ var style = d3_documentElement.style, select = style[d3_event_dragSelect];
+ style[d3_event_dragSelect] = "none";
+ }
+ return function(suppressClick) {
+ w.on(name, null);
+ if (d3_event_dragSelect) style[d3_event_dragSelect] = select;
+ if (suppressClick) {
+ function off() {
+ w.on(click, null);
+ }
+ w.on(click, function() {
+ d3_eventPreventDefault();
+ off();
+ }, true);
+ setTimeout(off, 0);
+ }
+ };
+ }
+ d3.mouse = function(container) {
+ return d3_mousePoint(container, d3_eventSource());
+ };
+ var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
+ function d3_mousePoint(container, e) {
+ if (e.changedTouches) e = e.changedTouches[0];
+ var svg = container.ownerSVGElement || container;
+ if (svg.createSVGPoint) {
+ var point = svg.createSVGPoint();
+ if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
+ svg = d3.select("body").append("svg").style({
+ position: "absolute",
+ top: 0,
+ left: 0,
+ margin: 0,
+ padding: 0,
+ border: "none"
+ }, "important");
+ var ctm = svg[0][0].getScreenCTM();
+ d3_mouse_bug44083 = !(ctm.f || ctm.e);
+ svg.remove();
+ }
+ if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX,
+ point.y = e.clientY;
+ point = point.matrixTransform(container.getScreenCTM().inverse());
+ return [ point.x, point.y ];
+ }
+ var rect = container.getBoundingClientRect();
+ return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
+ }
+ d3.touches = function(container, touches) {
+ if (arguments.length < 2) touches = d3_eventSource().touches;
+ return touches ? d3_array(touches).map(function(touch) {
+ var point = d3_mousePoint(container, touch);
+ point.identifier = touch.identifier;
+ return point;
+ }) : [];
+ };
+ d3.behavior.drag = function() {
+ var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, "mousemove", "mouseup"), touchstart = dragstart(touchid, touchposition, "touchmove", "touchend");
+ function drag() {
+ this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart);
+ }
+ function touchid() {
+ return d3.event.changedTouches[0].identifier;
+ }
+ function touchposition(parent, id) {
+ return d3.touches(parent).filter(function(p) {
+ return p.identifier === id;
+ })[0];
+ }
+ function dragstart(id, position, move, end) {
+ return function() {
+ var target = this, parent = target.parentNode, event_ = event.of(target, arguments), eventTarget = d3.event.target, eventId = id(), drag = eventId == null ? "drag" : "drag-" + eventId, origin_ = position(parent, eventId), dragged = 0, offset, w = d3.select(d3_window).on(move + "." + drag, moved).on(end + "." + drag, ended), dragRestore = d3_event_dragSuppress();
+ if (origin) {
+ offset = origin.apply(target, arguments);
+ offset = [ offset.x - origin_[0], offset.y - origin_[1] ];
+ } else {
+ offset = [ 0, 0 ];
+ }
+ event_({
+ type: "dragstart"
+ });
+ function moved() {
+ var p = position(parent, eventId), dx = p[0] - origin_[0], dy = p[1] - origin_[1];
+ dragged |= dx | dy;
+ origin_ = p;
+ event_({
+ type: "drag",
+ x: p[0] + offset[0],
+ y: p[1] + offset[1],
+ dx: dx,
+ dy: dy
+ });
+ }
+ function ended() {
+ w.on(move + "." + drag, null).on(end + "." + drag, null);
+ dragRestore(dragged && d3.event.target === eventTarget);
+ event_({
+ type: "dragend"
+ });
+ }
+ };
+ }
+ drag.origin = function(x) {
+ if (!arguments.length) return origin;
+ origin = x;
+ return drag;
+ };
+ return d3.rebind(drag, event, "on");
+ };
+ var Ï = Math.PI, Ï = 2 * Ï, halfÏ = Ï / 2, ε = 1e-6, ε2 = ε * ε, d3_radians = Ï / 180, d3_degrees = 180 / Ï;
+ function d3_sgn(x) {
+ return x > 0 ? 1 : x < 0 ? -1 : 0;
+ }
+ function d3_acos(x) {
+ return x > 1 ? 0 : x < -1 ? Ï : Math.acos(x);
+ }
+ function d3_asin(x) {
+ return x > 1 ? halfÏ : x < -1 ? -halfÏ : Math.asin(x);
+ }
+ function d3_sinh(x) {
+ return ((x = Math.exp(x)) - 1 / x) / 2;
+ }
+ function d3_cosh(x) {
+ return ((x = Math.exp(x)) + 1 / x) / 2;
+ }
+ function d3_tanh(x) {
+ return ((x = Math.exp(2 * x)) - 1) / (x + 1);
+ }
+ function d3_haversin(x) {
+ return (x = Math.sin(x / 2)) * x;
+ }
+ var Ï = Math.SQRT2, Ï2 = 2, Ï4 = 4;
+ d3.interpolateZoom = function(p0, p1) {
+ var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2];
+ var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + Ï4 * d2) / (2 * w0 * Ï2 * d1), b1 = (w1 * w1 - w0 * w0 - Ï4 * d2) / (2 * w1 * Ï2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / Ï;
+ function interpolate(t) {
+ var s = t * S;
+ if (dr) {
+ var coshr0 = d3_cosh(r0), u = w0 / (Ï2 * d1) * (coshr0 * d3_tanh(Ï * s + r0) - d3_sinh(r0));
+ return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(Ï * s + r0) ];
+ }
+ return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(Ï * s) ];
+ }
+ interpolate.duration = S * 1e3;
+ return interpolate;
+ };
+ d3.behavior.zoom = function() {
+ var view = {
+ x: 0,
+ y: 0,
+ k: 1
+ }, translate0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1;
+ function zoom(g) {
+ g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on(mousemove, mousewheelreset).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted);
+ }
+ zoom.event = function(g) {
+ g.each(function() {
+ var event_ = event.of(this, arguments), view1 = view;
+ if (d3_transitionInheritId) {
+ d3.select(this).transition().each("start.zoom", function() {
+ view = this.__chart__ || {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ zoomstarted(event_);
+ }).tween("zoom:zoom", function() {
+ var dx = size[0], dy = size[1], cx = dx / 2, cy = dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]);
+ return function(t) {
+ var l = i(t), k = dx / l[2];
+ this.__chart__ = view = {
+ x: cx - l[0] * k,
+ y: cy - l[1] * k,
+ k: k
+ };
+ zoomed(event_);
+ };
+ }).each("end.zoom", function() {
+ zoomended(event_);
+ });
+ } else {
+ this.__chart__ = view;
+ zoomstarted(event_);
+ zoomed(event_);
+ zoomended(event_);
+ }
+ });
+ };
+ zoom.translate = function(_) {
+ if (!arguments.length) return [ view.x, view.y ];
+ view = {
+ x: +_[0],
+ y: +_[1],
+ k: view.k
+ };
+ rescale();
+ return zoom;
+ };
+ zoom.scale = function(_) {
+ if (!arguments.length) return view.k;
+ view = {
+ x: view.x,
+ y: view.y,
+ k: +_
+ };
+ rescale();
+ return zoom;
+ };
+ zoom.scaleExtent = function(_) {
+ if (!arguments.length) return scaleExtent;
+ scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.center = function(_) {
+ if (!arguments.length) return center;
+ center = _ && [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.size = function(_) {
+ if (!arguments.length) return size;
+ size = _ && [ +_[0], +_[1] ];
+ return zoom;
+ };
+ zoom.x = function(z) {
+ if (!arguments.length) return x1;
+ x1 = z;
+ x0 = z.copy();
+ view = {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ return zoom;
+ };
+ zoom.y = function(z) {
+ if (!arguments.length) return y1;
+ y1 = z;
+ y0 = z.copy();
+ view = {
+ x: 0,
+ y: 0,
+ k: 1
+ };
+ return zoom;
+ };
+ function location(p) {
+ return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ];
+ }
+ function point(l) {
+ return [ l[0] * view.k + view.x, l[1] * view.k + view.y ];
+ }
+ function scaleTo(s) {
+ view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
+ }
+ function translateTo(p, l) {
+ l = point(l);
+ view.x += p[0] - l[0];
+ view.y += p[1] - l[1];
+ }
+ function rescale() {
+ if (x1) x1.domain(x0.range().map(function(x) {
+ return (x - view.x) / view.k;
+ }).map(x0.invert));
+ if (y1) y1.domain(y0.range().map(function(y) {
+ return (y - view.y) / view.k;
+ }).map(y0.invert));
+ }
+ function zoomstarted(event) {
+ event({
+ type: "zoomstart"
+ });
+ }
+ function zoomed(event) {
+ rescale();
+ event({
+ type: "zoom",
+ scale: view.k,
+ translate: [ view.x, view.y ]
+ });
+ }
+ function zoomended(event) {
+ event({
+ type: "zoomend"
+ });
+ }
+ function mousedowned() {
+ var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, dragged = 0, w = d3.select(d3_window).on(mousemove, moved).on(mouseup, ended), l = location(d3.mouse(target)), dragRestore = d3_event_dragSuppress();
+ d3_selection_interrupt.call(target);
+ zoomstarted(event_);
+ function moved() {
+ dragged = 1;
+ translateTo(d3.mouse(target), l);
+ zoomed(event_);
+ }
+ function ended() {
+ w.on(mousemove, d3_window === target ? mousewheelreset : null).on(mouseup, null);
+ dragRestore(dragged && d3.event.target === eventTarget);
+ zoomended(event_);
+ }
+ }
+ function touchstarted() {
+ var target = this, event_ = event.of(target, arguments), locations0 = {}, distance0 = 0, scale0, eventId = d3.event.changedTouches[0].identifier, touchmove = "touchmove.zoom-" + eventId, touchend = "touchend.zoom-" + eventId, w = d3.select(d3_window).on(touchmove, moved).on(touchend, ended), t = d3.select(target).on(mousedown, null).on(touchstart, started), dragRestore = d3_event_dragSuppress();
+ d3_selection_interrupt.call(target);
+ started();
+ zoomstarted(event_);
+ function relocate() {
+ var touches = d3.touches(target);
+ scale0 = view.k;
+ touches.forEach(function(t) {
+ if (t.identifier in locations0) locations0[t.identifier] = location(t);
+ });
+ return touches;
+ }
+ function started() {
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ locations0[changed[i].identifier] = null;
+ }
+ var touches = relocate(), now = Date.now();
+ if (touches.length === 1) {
+ if (now - touchtime < 500) {
+ var p = touches[0], l = locations0[p.identifier];
+ scaleTo(view.k * 2);
+ translateTo(p, l);
+ d3_eventPreventDefault();
+ zoomed(event_);
+ }
+ touchtime = now;
+ } else if (touches.length > 1) {
+ var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
+ distance0 = dx * dx + dy * dy;
+ }
+ }
+ function moved() {
+ var touches = d3.touches(target), p0, l0, p1, l1;
+ for (var i = 0, n = touches.length; i < n; ++i, l1 = null) {
+ p1 = touches[i];
+ if (l1 = locations0[p1.identifier]) {
+ if (l0) break;
+ p0 = p1, l0 = l1;
+ }
+ }
+ if (l1) {
+ var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0);
+ p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
+ l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
+ scaleTo(scale1 * scale0);
+ }
+ touchtime = null;
+ translateTo(p0, l0);
+ zoomed(event_);
+ }
+ function ended() {
+ if (d3.event.touches.length) {
+ var changed = d3.event.changedTouches;
+ for (var i = 0, n = changed.length; i < n; ++i) {
+ delete locations0[changed[i].identifier];
+ }
+ for (var identifier in locations0) {
+ return void relocate();
+ }
+ }
+ w.on(touchmove, null).on(touchend, null);
+ t.on(mousedown, mousedowned).on(touchstart, touchstarted);
+ dragRestore();
+ zoomended(event_);
+ }
+ }
+ function mousewheeled() {
+ var event_ = event.of(this, arguments);
+ if (mousewheelTimer) clearTimeout(mousewheelTimer); else d3_selection_interrupt.call(this),
+ zoomstarted(event_);
+ mousewheelTimer = setTimeout(function() {
+ mousewheelTimer = null;
+ zoomended(event_);
+ }, 50);
+ d3_eventPreventDefault();
+ var point = center || d3.mouse(this);
+ if (!translate0) translate0 = location(point);
+ scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k);
+ translateTo(point, translate0);
+ zoomed(event_);
+ }
+ function mousewheelreset() {
+ translate0 = null;
+ }
+ function dblclicked() {
+ var event_ = event.of(this, arguments), p = d3.mouse(this), l = location(p), k = Math.log(view.k) / Math.LN2;
+ zoomstarted(event_);
+ scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1));
+ translateTo(p, l);
+ zoomed(event_);
+ zoomended(event_);
+ }
+ return d3.rebind(zoom, event, "on");
+ };
+ var d3_behavior_zoomInfinity = [ 0, Infinity ];
+ var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+ return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
+ }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
+ return d3.event.wheelDelta;
+ }, "mousewheel") : (d3_behavior_zoomDelta = function() {
+ return -d3.event.detail;
+ }, "MozMousePixelScroll");
+ function d3_Color() {}
+ d3_Color.prototype.toString = function() {
+ return this.rgb() + "";
+ };
+ d3.hsl = function(h, s, l) {
+ return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l);
+ };
+ function d3_hsl(h, s, l) {
+ return new d3_Hsl(h, s, l);
+ }
+ function d3_Hsl(h, s, l) {
+ this.h = h;
+ this.s = s;
+ this.l = l;
+ }
+ var d3_hslPrototype = d3_Hsl.prototype = new d3_Color();
+ d3_hslPrototype.brighter = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_hsl(this.h, this.s, this.l / k);
+ };
+ d3_hslPrototype.darker = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_hsl(this.h, this.s, k * this.l);
+ };
+ d3_hslPrototype.rgb = function() {
+ return d3_hsl_rgb(this.h, this.s, this.l);
+ };
+ function d3_hsl_rgb(h, s, l) {
+ var m1, m2;
+ h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
+ s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
+ l = l < 0 ? 0 : l > 1 ? 1 : l;
+ m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
+ m1 = 2 * l - m2;
+ function v(h) {
+ if (h > 360) h -= 360; else if (h < 0) h += 360;
+ if (h < 60) return m1 + (m2 - m1) * h / 60;
+ if (h < 180) return m2;
+ if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
+ return m1;
+ }
+ function vv(h) {
+ return Math.round(v(h) * 255);
+ }
+ return d3_rgb(vv(h + 120), vv(h), vv(h - 120));
+ }
+ d3.hcl = function(h, c, l) {
+ return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l);
+ };
+ function d3_hcl(h, c, l) {
+ return new d3_Hcl(h, c, l);
+ }
+ function d3_Hcl(h, c, l) {
+ this.h = h;
+ this.c = c;
+ this.l = l;
+ }
+ var d3_hclPrototype = d3_Hcl.prototype = new d3_Color();
+ d3_hclPrototype.brighter = function(k) {
+ return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
+ };
+ d3_hclPrototype.darker = function(k) {
+ return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
+ };
+ d3_hclPrototype.rgb = function() {
+ return d3_hcl_lab(this.h, this.c, this.l).rgb();
+ };
+ function d3_hcl_lab(h, c, l) {
+ if (isNaN(h)) h = 0;
+ if (isNaN(c)) c = 0;
+ return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
+ }
+ d3.lab = function(l, a, b) {
+ return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b);
+ };
+ function d3_lab(l, a, b) {
+ return new d3_Lab(l, a, b);
+ }
+ function d3_Lab(l, a, b) {
+ this.l = l;
+ this.a = a;
+ this.b = b;
+ }
+ var d3_lab_K = 18;
+ var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
+ var d3_labPrototype = d3_Lab.prototype = new d3_Color();
+ d3_labPrototype.brighter = function(k) {
+ return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+ };
+ d3_labPrototype.darker = function(k) {
+ return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
+ };
+ d3_labPrototype.rgb = function() {
+ return d3_lab_rgb(this.l, this.a, this.b);
+ };
+ function d3_lab_rgb(l, a, b) {
+ var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
+ x = d3_lab_xyz(x) * d3_lab_X;
+ y = d3_lab_xyz(y) * d3_lab_Y;
+ z = d3_lab_xyz(z) * d3_lab_Z;
+ return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
+ }
+ function d3_lab_hcl(l, a, b) {
+ return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l);
+ }
+ function d3_lab_xyz(x) {
+ return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
+ }
+ function d3_xyz_lab(x) {
+ return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
+ }
+ function d3_xyz_rgb(r) {
+ return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
+ }
+ d3.rgb = function(r, g, b) {
+ return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b);
+ };
+ function d3_rgbNumber(value) {
+ return d3_rgb(value >> 16, value >> 8 & 255, value & 255);
+ }
+ function d3_rgbString(value) {
+ return d3_rgbNumber(value) + "";
+ }
+ function d3_rgb(r, g, b) {
+ return new d3_Rgb(r, g, b);
+ }
+ function d3_Rgb(r, g, b) {
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ }
+ var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color();
+ d3_rgbPrototype.brighter = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ var r = this.r, g = this.g, b = this.b, i = 30;
+ if (!r && !g && !b) return d3_rgb(i, i, i);
+ if (r && r < i) r = i;
+ if (g && g < i) g = i;
+ if (b && b < i) b = i;
+ return d3_rgb(Math.min(255, ~~(r / k)), Math.min(255, ~~(g / k)), Math.min(255, ~~(b / k)));
+ };
+ d3_rgbPrototype.darker = function(k) {
+ k = Math.pow(.7, arguments.length ? k : 1);
+ return d3_rgb(~~(k * this.r), ~~(k * this.g), ~~(k * this.b));
+ };
+ d3_rgbPrototype.hsl = function() {
+ return d3_rgb_hsl(this.r, this.g, this.b);
+ };
+ d3_rgbPrototype.toString = function() {
+ return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
+ };
+ function d3_rgb_hex(v) {
+ return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
+ }
+ function d3_rgb_parse(format, rgb, hsl) {
+ var r = 0, g = 0, b = 0, m1, m2, name;
+ m1 = /([a-z]+)\((.*)\)/i.exec(format);
+ if (m1) {
+ m2 = m1[2].split(",");
+ switch (m1[1]) {
+ case "hsl":
+ {
+ return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
+ }
+
+ case "rgb":
+ {
+ return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
+ }
+ }
+ }
+ if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b);
+ if (format != null && format.charAt(0) === "#") {
+ if (format.length === 4) {
+ r = format.charAt(1);
+ r += r;
+ g = format.charAt(2);
+ g += g;
+ b = format.charAt(3);
+ b += b;
+ } else if (format.length === 7) {
+ r = format.substring(1, 3);
+ g = format.substring(3, 5);
+ b = format.substring(5, 7);
+ }
+ r = parseInt(r, 16);
+ g = parseInt(g, 16);
+ b = parseInt(b, 16);
+ }
+ return rgb(r, g, b);
+ }
+ function d3_rgb_hsl(r, g, b) {
+ var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
+ if (d) {
+ s = l < .5 ? d / (max + min) : d / (2 - max - min);
+ if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
+ h *= 60;
+ } else {
+ h = NaN;
+ s = l > 0 && l < 1 ? 0 : h;
+ }
+ return d3_hsl(h, s, l);
+ }
+ function d3_rgb_lab(r, g, b) {
+ r = d3_rgb_xyz(r);
+ g = d3_rgb_xyz(g);
+ b = d3_rgb_xyz(b);
+ var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
+ return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
+ }
+ function d3_rgb_xyz(r) {
+ return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
+ }
+ function d3_rgb_parseNumber(c) {
+ var f = parseFloat(c);
+ return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
+ }
+ var d3_rgb_names = d3.map({
+ aliceblue: 15792383,
+ antiquewhite: 16444375,
+ aqua: 65535,
+ aquamarine: 8388564,
+ azure: 15794175,
+ beige: 16119260,
+ bisque: 16770244,
+ black: 0,
+ blanchedalmond: 16772045,
+ blue: 255,
+ blueviolet: 9055202,
+ brown: 10824234,
+ burlywood: 14596231,
+ cadetblue: 6266528,
+ chartreuse: 8388352,
+ chocolate: 13789470,
+ coral: 16744272,
+ cornflowerblue: 6591981,
+ cornsilk: 16775388,
+ crimson: 14423100,
+ cyan: 65535,
+ darkblue: 139,
+ darkcyan: 35723,
+ darkgoldenrod: 12092939,
+ darkgray: 11119017,
+ darkgreen: 25600,
+ darkgrey: 11119017,
+ darkkhaki: 12433259,
+ darkmagenta: 9109643,
+ darkolivegreen: 5597999,
+ darkorange: 16747520,
+ darkorchid: 10040012,
+ darkred: 9109504,
+ darksalmon: 15308410,
+ darkseagreen: 9419919,
+ darkslateblue: 4734347,
+ darkslategray: 3100495,
+ darkslategrey: 3100495,
+ darkturquoise: 52945,
+ darkviolet: 9699539,
+ deeppink: 16716947,
+ deepskyblue: 49151,
+ dimgray: 6908265,
+ dimgrey: 6908265,
+ dodgerblue: 2003199,
+ firebrick: 11674146,
+ floralwhite: 16775920,
+ forestgreen: 2263842,
+ fuchsia: 16711935,
+ gainsboro: 14474460,
+ ghostwhite: 16316671,
+ gold: 16766720,
+ goldenrod: 14329120,
+ gray: 8421504,
+ green: 32768,
+ greenyellow: 11403055,
+ grey: 8421504,
+ honeydew: 15794160,
+ hotpink: 16738740,
+ indianred: 13458524,
+ indigo: 4915330,
+ ivory: 16777200,
+ khaki: 15787660,
+ lavender: 15132410,
+ lavenderblush: 16773365,
+ lawngreen: 8190976,
+ lemonchiffon: 16775885,
+ lightblue: 11393254,
+ lightcoral: 15761536,
+ lightcyan: 14745599,
+ lightgoldenrodyellow: 16448210,
+ lightgray: 13882323,
+ lightgreen: 9498256,
+ lightgrey: 13882323,
+ lightpink: 16758465,
+ lightsalmon: 16752762,
+ lightseagreen: 2142890,
+ lightskyblue: 8900346,
+ lightslategray: 7833753,
+ lightslategrey: 7833753,
+ lightsteelblue: 11584734,
+ lightyellow: 16777184,
+ lime: 65280,
+ limegreen: 3329330,
+ linen: 16445670,
+ magenta: 16711935,
+ maroon: 8388608,
+ mediumaquamarine: 6737322,
+ mediumblue: 205,
+ mediumorchid: 12211667,
+ mediumpurple: 9662683,
+ mediumseagreen: 3978097,
+ mediumslateblue: 8087790,
+ mediumspringgreen: 64154,
+ mediumturquoise: 4772300,
+ mediumvioletred: 13047173,
+ midnightblue: 1644912,
+ mintcream: 16121850,
+ mistyrose: 16770273,
+ moccasin: 16770229,
+ navajowhite: 16768685,
+ navy: 128,
+ oldlace: 16643558,
+ olive: 8421376,
+ olivedrab: 7048739,
+ orange: 16753920,
+ orangered: 16729344,
+ orchid: 14315734,
+ palegoldenrod: 15657130,
+ palegreen: 10025880,
+ paleturquoise: 11529966,
+ palevioletred: 14381203,
+ papayawhip: 16773077,
+ peachpuff: 16767673,
+ peru: 13468991,
+ pink: 16761035,
+ plum: 14524637,
+ powderblue: 11591910,
+ purple: 8388736,
+ red: 16711680,
+ rosybrown: 12357519,
+ royalblue: 4286945,
+ saddlebrown: 9127187,
+ salmon: 16416882,
+ sandybrown: 16032864,
+ seagreen: 3050327,
+ seashell: 16774638,
+ sienna: 10506797,
+ silver: 12632256,
+ skyblue: 8900331,
+ slateblue: 6970061,
+ slategray: 7372944,
+ slategrey: 7372944,
+ snow: 16775930,
+ springgreen: 65407,
+ steelblue: 4620980,
+ tan: 13808780,
+ teal: 32896,
+ thistle: 14204888,
+ tomato: 16737095,
+ turquoise: 4251856,
+ violet: 15631086,
+ wheat: 16113331,
+ white: 16777215,
+ whitesmoke: 16119285,
+ yellow: 16776960,
+ yellowgreen: 10145074
+ });
+ d3_rgb_names.forEach(function(key, value) {
+ d3_rgb_names.set(key, d3_rgbNumber(value));
+ });
+ function d3_functor(v) {
+ return typeof v === "function" ? v : function() {
+ return v;
+ };
+ }
+ d3.functor = d3_functor;
+ function d3_identity(d) {
+ return d;
+ }
+ d3.xhr = d3_xhrType(d3_identity);
+ function d3_xhrType(response) {
+ return function(url, mimeType, callback) {
+ if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
+ mimeType = null;
+ return d3_xhr(url, mimeType, response, callback);
+ };
+ }
+ function d3_xhr(url, mimeType, response, callback) {
+ var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
+ if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
+ "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
+ request.readyState > 3 && respond();
+ };
+ function respond() {
+ var status = request.status, result;
+ if (!status && request.responseText || status >= 200 && status < 300 || status === 304) {
+ try {
+ result = response.call(xhr, request);
+ } catch (e) {
+ dispatch.error.call(xhr, e);
+ return;
+ }
+ dispatch.load.call(xhr, result);
+ } else {
+ dispatch.error.call(xhr, request);
+ }
+ }
+ request.onprogress = function(event) {
+ var o = d3.event;
+ d3.event = event;
+ try {
+ dispatch.progress.call(xhr, request);
+ } finally {
+ d3.event = o;
+ }
+ };
+ xhr.header = function(name, value) {
+ name = (name + "").toLowerCase();
+ if (arguments.length < 2) return headers[name];
+ if (value == null) delete headers[name]; else headers[name] = value + "";
+ return xhr;
+ };
+ xhr.mimeType = function(value) {
+ if (!arguments.length) return mimeType;
+ mimeType = value == null ? null : value + "";
+ return xhr;
+ };
+ xhr.responseType = function(value) {
+ if (!arguments.length) return responseType;
+ responseType = value;
+ return xhr;
+ };
+ xhr.response = function(value) {
+ response = value;
+ return xhr;
+ };
+ [ "get", "post" ].forEach(function(method) {
+ xhr[method] = function() {
+ return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
+ };
+ });
+ xhr.send = function(method, data, callback) {
+ if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
+ request.open(method, url, true);
+ if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
+ if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
+ if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
+ if (responseType != null) request.responseType = responseType;
+ if (callback != null) xhr.on("error", callback).on("load", function(request) {
+ callback(null, request);
+ });
+ dispatch.beforesend.call(xhr, request);
+ request.send(data == null ? null : data);
+ return xhr;
+ };
+ xhr.abort = function() {
+ request.abort();
+ return xhr;
+ };
+ d3.rebind(xhr, dispatch, "on");
+ return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
+ }
+ function d3_xhr_fixCallback(callback) {
+ return callback.length === 1 ? function(error, request) {
+ callback(error == null ? request : null);
+ } : callback;
+ }
+ d3.dsv = function(delimiter, mimeType) {
+ var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
+ function dsv(url, row, callback) {
+ if (arguments.length < 3) callback = row, row = null;
+ var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback);
+ xhr.row = function(_) {
+ return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
+ };
+ return xhr;
+ }
+ function response(request) {
+ return dsv.parse(request.responseText);
+ }
+ function typedResponse(f) {
+ return function(request) {
+ return dsv.parse(request.responseText, f);
+ };
+ }
+ dsv.parse = function(text, f) {
+ var o;
+ return dsv.parseRows(text, function(row, i) {
+ if (o) return o(row, i - 1);
+ var a = new Function("d", "return {" + row.map(function(name, i) {
+ return JSON.stringify(name) + ": d[" + i + "]";
+ }).join(",") + "}");
+ o = f ? function(row, i) {
+ return f(a(row), i);
+ } : a;
+ });
+ };
+ dsv.parseRows = function(text, f) {
+ var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
+ function token() {
+ if (I >= N) return EOF;
+ if (eol) return eol = false, EOL;
+ var j = I;
+ if (text.charCodeAt(j) === 34) {
+ var i = j;
+ while (i++ < N) {
+ if (text.charCodeAt(i) === 34) {
+ if (text.charCodeAt(i + 1) !== 34) break;
+ ++i;
+ }
+ }
+ I = i + 2;
+ var c = text.charCodeAt(i + 1);
+ if (c === 13) {
+ eol = true;
+ if (text.charCodeAt(i + 2) === 10) ++I;
+ } else if (c === 10) {
+ eol = true;
+ }
+ return text.substring(j + 1, i).replace(/""/g, '"');
+ }
+ while (I < N) {
+ var c = text.charCodeAt(I++), k = 1;
+ if (c === 10) eol = true; else if (c === 13) {
+ eol = true;
+ if (text.charCodeAt(I) === 10) ++I, ++k;
+ } else if (c !== delimiterCode) continue;
+ return text.substring(j, I - k);
+ }
+ return text.substring(j);
+ }
+ while ((t = token()) !== EOF) {
+ var a = [];
+ while (t !== EOL && t !== EOF) {
+ a.push(t);
+ t = token();
+ }
+ if (f && !(a = f(a, n++))) continue;
+ rows.push(a);
+ }
+ return rows;
+ };
+ dsv.format = function(rows) {
+ if (Array.isArray(rows[0])) return dsv.formatRows(rows);
+ var fieldSet = new d3_Set(), fields = [];
+ rows.forEach(function(row) {
+ for (var field in row) {
+ if (!fieldSet.has(field)) {
+ fields.push(fieldSet.add(field));
+ }
+ }
+ });
+ return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
+ return fields.map(function(field) {
+ return formatValue(row[field]);
+ }).join(delimiter);
+ })).join("\n");
+ };
+ dsv.formatRows = function(rows) {
+ return rows.map(formatRow).join("\n");
+ };
+ function formatRow(row) {
+ return row.map(formatValue).join(delimiter);
+ }
+ function formatValue(text) {
+ return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
+ }
+ return dsv;
+ };
+ d3.csv = d3.dsv(",", "text/csv");
+ d3.tsv = d3.dsv(" ", "text/tab-separated-values");
+ var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
+ setTimeout(callback, 17);
+ };
+ d3.timer = function(callback, delay, then) {
+ var n = arguments.length;
+ if (n < 2) delay = 0;
+ if (n < 3) then = Date.now();
+ var time = then + delay, timer = {
+ c: callback,
+ t: time,
+ f: false,
+ n: null
+ };
+ if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer;
+ d3_timer_queueTail = timer;
+ if (!d3_timer_interval) {
+ d3_timer_timeout = clearTimeout(d3_timer_timeout);
+ d3_timer_interval = 1;
+ d3_timer_frame(d3_timer_step);
+ }
+ };
+ function d3_timer_step() {
+ var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
+ if (delay > 24) {
+ if (isFinite(delay)) {
+ clearTimeout(d3_timer_timeout);
+ d3_timer_timeout = setTimeout(d3_timer_step, delay);
+ }
+ d3_timer_interval = 0;
+ } else {
+ d3_timer_interval = 1;
+ d3_timer_frame(d3_timer_step);
+ }
+ }
+ d3.timer.flush = function() {
+ d3_timer_mark();
+ d3_timer_sweep();
+ };
+ function d3_timer_mark() {
+ var now = Date.now();
+ d3_timer_active = d3_timer_queueHead;
+ while (d3_timer_active) {
+ if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t);
+ d3_timer_active = d3_timer_active.n;
+ }
+ return now;
+ }
+ function d3_timer_sweep() {
+ var t0, t1 = d3_timer_queueHead, time = Infinity;
+ while (t1) {
+ if (t1.f) {
+ t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n;
+ } else {
+ if (t1.t < time) time = t1.t;
+ t1 = (t0 = t1).n;
+ }
+ }
+ d3_timer_queueTail = t0;
+ return time;
+ }
+ var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ], d3_format_currencySymbol = "$";
+ var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
+ d3.formatPrefix = function(value, precision) {
+ var i = 0;
+ if (value) {
+ if (value < 0) value *= -1;
+ if (precision) value = d3.round(value, d3_format_precision(value, precision));
+ i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
+ i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
+ }
+ return d3_formatPrefixes[8 + i / 3];
+ };
+ function d3_formatPrefix(d, i) {
+ var k = Math.pow(10, abs(8 - i) * 3);
+ return {
+ scale: i > 8 ? function(d) {
+ return d / k;
+ } : function(d) {
+ return d * k;
+ },
+ symbol: d
+ };
+ }
+ d3.round = function(x, n) {
+ return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
+ };
+ d3.format = function(specifier) {
+ var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false;
+ if (precision) precision = +precision.substring(1);
+ if (zfill || fill === "0" && align === "=") {
+ zfill = fill = "0";
+ align = "=";
+ if (comma) width -= Math.floor((width - 1) / 4);
+ }
+ switch (type) {
+ case "n":
+ comma = true;
+ type = "g";
+ break;
+
+ case "%":
+ scale = 100;
+ suffix = "%";
+ type = "f";
+ break;
+
+ case "p":
+ scale = 100;
+ suffix = "%";
+ type = "r";
+ break;
+
+ case "b":
+ case "o":
+ case "x":
+ case "X":
+ if (symbol === "#") symbol = "0" + type.toLowerCase();
+
+ case "c":
+ case "d":
+ integer = true;
+ precision = 0;
+ break;
+
+ case "s":
+ scale = -1;
+ type = "r";
+ break;
+ }
+ if (symbol === "#") symbol = ""; else if (symbol === "$") symbol = d3_format_currencySymbol;
+ if (type == "r" && !precision) type = "g";
+ if (precision != null) {
+ if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
+ }
+ type = d3_format_types.get(type) || d3_format_typeDefault;
+ var zcomma = zfill && comma;
+ return function(value) {
+ if (integer && value % 1) return "";
+ var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign;
+ if (scale < 0) {
+ var prefix = d3.formatPrefix(value, precision);
+ value = prefix.scale(value);
+ suffix = prefix.symbol;
+ } else {
+ value *= scale;
+ }
+ value = type(value, precision);
+ var i = value.lastIndexOf("."), before = i < 0 ? value : value.substring(0, i), after = i < 0 ? "" : d3_format_decimalPoint + value.substring(i + 1);
+ if (!zfill && comma) before = d3_format_group(before);
+ var length = symbol.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
+ if (zcomma) before = d3_format_group(padding + before);
+ negative += symbol;
+ value = before + after;
+ return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + suffix;
+ };
+ };
+ var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
+ var d3_format_types = d3.map({
+ b: function(x) {
+ return x.toString(2);
+ },
+ c: function(x) {
+ return String.fromCharCode(x);
+ },
+ o: function(x) {
+ return x.toString(8);
+ },
+ x: function(x) {
+ return x.toString(16);
+ },
+ X: function(x) {
+ return x.toString(16).toUpperCase();
+ },
+ g: function(x, p) {
+ return x.toPrecision(p);
+ },
+ e: function(x, p) {
+ return x.toExponential(p);
+ },
+ f: function(x, p) {
+ return x.toFixed(p);
+ },
+ r: function(x, p) {
+ return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
+ }
+ });
+ function d3_format_precision(x, p) {
+ return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
+ }
+ function d3_format_typeDefault(x) {
+ return x + "";
+ }
+ var d3_format_group = d3_identity;
+ if (d3_format_grouping) {
+ var d3_format_groupingLength = d3_format_grouping.length;
+ d3_format_group = function(value) {
+ var i = value.length, t = [], j = 0, g = d3_format_grouping[0];
+ while (i > 0 && g > 0) {
+ t.push(value.substring(i -= g, i + g));
+ g = d3_format_grouping[j = (j + 1) % d3_format_groupingLength];
+ }
+ return t.reverse().join(d3_format_thousandsSeparator);
+ };
+ }
+ d3.geo = {};
+ function d3_adder() {}
+ d3_adder.prototype = {
+ s: 0,
+ t: 0,
+ add: function(y) {
+ d3_adderSum(y, this.t, d3_adderTemp);
+ d3_adderSum(d3_adderTemp.s, this.s, this);
+ if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
+ },
+ reset: function() {
+ this.s = this.t = 0;
+ },
+ valueOf: function() {
+ return this.s;
+ }
+ };
+ var d3_adderTemp = new d3_adder();
+ function d3_adderSum(a, b, o) {
+ var x = o.s = a + b, bv = x - a, av = x - bv;
+ o.t = a - av + (b - bv);
+ }
+ d3.geo.stream = function(object, listener) {
+ if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
+ d3_geo_streamObjectType[object.type](object, listener);
+ } else {
+ d3_geo_streamGeometry(object, listener);
+ }
+ };
+ function d3_geo_streamGeometry(geometry, listener) {
+ if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
+ d3_geo_streamGeometryType[geometry.type](geometry, listener);
+ }
+ }
+ var d3_geo_streamObjectType = {
+ Feature: function(feature, listener) {
+ d3_geo_streamGeometry(feature.geometry, listener);
+ },
+ FeatureCollection: function(object, listener) {
+ var features = object.features, i = -1, n = features.length;
+ while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
+ }
+ };
+ var d3_geo_streamGeometryType = {
+ Sphere: function(object, listener) {
+ listener.sphere();
+ },
+ Point: function(object, listener) {
+ object = object.coordinates;
+ listener.point(object[0], object[1], object[2]);
+ },
+ MultiPoint: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]);
+ },
+ LineString: function(object, listener) {
+ d3_geo_streamLine(object.coordinates, listener, 0);
+ },
+ MultiLineString: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
+ },
+ Polygon: function(object, listener) {
+ d3_geo_streamPolygon(object.coordinates, listener);
+ },
+ MultiPolygon: function(object, listener) {
+ var coordinates = object.coordinates, i = -1, n = coordinates.length;
+ while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
+ },
+ GeometryCollection: function(object, listener) {
+ var geometries = object.geometries, i = -1, n = geometries.length;
+ while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
+ }
+ };
+ function d3_geo_streamLine(coordinates, listener, closed) {
+ var i = -1, n = coordinates.length - closed, coordinate;
+ listener.lineStart();
+ while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]);
+ listener.lineEnd();
+ }
+ function d3_geo_streamPolygon(coordinates, listener) {
+ var i = -1, n = coordinates.length;
+ listener.polygonStart();
+ while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
+ listener.polygonEnd();
+ }
+ d3.geo.area = function(object) {
+ d3_geo_areaSum = 0;
+ d3.geo.stream(object, d3_geo_area);
+ return d3_geo_areaSum;
+ };
+ var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
+ var d3_geo_area = {
+ sphere: function() {
+ d3_geo_areaSum += 4 * Ï;
+ },
+ point: d3_noop,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: function() {
+ d3_geo_areaRingSum.reset();
+ d3_geo_area.lineStart = d3_geo_areaRingStart;
+ },
+ polygonEnd: function() {
+ var area = 2 * d3_geo_areaRingSum;
+ d3_geo_areaSum += area < 0 ? 4 * Ï + area : area;
+ d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
+ }
+ };
+ function d3_geo_areaRingStart() {
+ var λ00, Ï00, λ0, cosÏ0, sinÏ0;
+ d3_geo_area.point = function(λ, Ï) {
+ d3_geo_area.point = nextPoint;
+ λ0 = (λ00 = λ) * d3_radians, cosÏ0 = Math.cos(Ï = (Ï00 = Ï) * d3_radians / 2 + Ï / 4),
+ sinÏ0 = Math.sin(Ï);
+ };
+ function nextPoint(λ, Ï) {
+ λ *= d3_radians;
+ Ï = Ï * d3_radians / 2 + Ï / 4;
+ var dλ = λ - λ0, cosÏ = Math.cos(Ï), sinÏ = Math.sin(Ï), k = sinÏ0 * sinÏ, u = cosÏ0 * cosÏ + k * Math.cos(dλ), v = k * Math.sin(dλ);
+ d3_geo_areaRingSum.add(Math.atan2(v, u));
+ λ0 = λ, cosÏ0 = cosÏ, sinÏ0 = sinÏ;
+ }
+ d3_geo_area.lineEnd = function() {
+ nextPoint(λ00, Ï00);
+ };
+ }
+ function d3_geo_cartesian(spherical) {
+ var λ = spherical[0], Ï = spherical[1], cosÏ = Math.cos(Ï);
+ return [ cosÏ * Math.cos(λ), cosÏ * Math.sin(λ), Math.sin(Ï) ];
+ }
+ function d3_geo_cartesianDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
+ }
+ function d3_geo_cartesianCross(a, b) {
+ return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
+ }
+ function d3_geo_cartesianAdd(a, b) {
+ a[0] += b[0];
+ a[1] += b[1];
+ a[2] += b[2];
+ }
+ function d3_geo_cartesianScale(vector, k) {
+ return [ vector[0] * k, vector[1] * k, vector[2] * k ];
+ }
+ function d3_geo_cartesianNormalize(d) {
+ var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
+ d[0] /= l;
+ d[1] /= l;
+ d[2] /= l;
+ }
+ function d3_geo_spherical(cartesian) {
+ return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
+ }
+ function d3_geo_sphericalEqual(a, b) {
+ return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε;
+ }
+ d3.geo.bounds = function() {
+ var λ0, Ï0, λ1, Ï1, λ_, λ__, Ï__, p0, dλSum, ranges, range;
+ var bound = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ bound.point = ringPoint;
+ bound.lineStart = ringStart;
+ bound.lineEnd = ringEnd;
+ dλSum = 0;
+ d3_geo_area.polygonStart();
+ },
+ polygonEnd: function() {
+ d3_geo_area.polygonEnd();
+ bound.point = point;
+ bound.lineStart = lineStart;
+ bound.lineEnd = lineEnd;
+ if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), Ï0 = -(Ï1 = 90); else if (dλSum > ε) Ï1 = 90; else if (dλSum < -ε) Ï0 = -90;
+ range[0] = λ0, range[1] = λ1;
+ }
+ };
+ function point(λ, Ï) {
+ ranges.push(range = [ λ0 = λ, λ1 = λ ]);
+ if (Ï < Ï0) Ï0 = Ï;
+ if (Ï > Ï1) Ï1 = Ï;
+ }
+ function linePoint(λ, Ï) {
+ var p = d3_geo_cartesian([ λ * d3_radians, Ï * d3_radians ]);
+ if (p0) {
+ var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
+ d3_geo_cartesianNormalize(inflection);
+ inflection = d3_geo_spherical(inflection);
+ var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180;
+ if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+ var Ïi = inflection[1] * d3_degrees;
+ if (Ïi > Ï1) Ï1 = Ïi;
+ } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
+ var Ïi = -inflection[1] * d3_degrees;
+ if (Ïi < Ï0) Ï0 = Ïi;
+ } else {
+ if (Ï < Ï0) Ï0 = Ï;
+ if (Ï > Ï1) Ï1 = Ï;
+ }
+ if (antimeridian) {
+ if (λ < λ_) {
+ if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+ } else {
+ if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+ }
+ } else {
+ if (λ1 >= λ0) {
+ if (λ < λ0) λ0 = λ;
+ if (λ > λ1) λ1 = λ;
+ } else {
+ if (λ > λ_) {
+ if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
+ } else {
+ if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
+ }
+ }
+ }
+ } else {
+ point(λ, Ï);
+ }
+ p0 = p, λ_ = λ;
+ }
+ function lineStart() {
+ bound.point = linePoint;
+ }
+ function lineEnd() {
+ range[0] = λ0, range[1] = λ1;
+ bound.point = point;
+ p0 = null;
+ }
+ function ringPoint(λ, Ï) {
+ if (p0) {
+ var dλ = λ - λ_;
+ dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
+ } else λ__ = λ, Ï__ = Ï;
+ d3_geo_area.point(λ, Ï);
+ linePoint(λ, Ï);
+ }
+ function ringStart() {
+ d3_geo_area.lineStart();
+ }
+ function ringEnd() {
+ ringPoint(λ__, Ï__);
+ d3_geo_area.lineEnd();
+ if (abs(dλSum) > ε) λ0 = -(λ1 = 180);
+ range[0] = λ0, range[1] = λ1;
+ p0 = null;
+ }
+ function angle(λ0, λ1) {
+ return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
+ }
+ function compareRanges(a, b) {
+ return a[0] - b[0];
+ }
+ function withinRange(x, range) {
+ return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
+ }
+ return function(feature) {
+ Ï1 = λ1 = -(λ0 = Ï0 = Infinity);
+ ranges = [];
+ d3.geo.stream(feature, bound);
+ var n = ranges.length;
+ if (n) {
+ ranges.sort(compareRanges);
+ for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
+ b = ranges[i];
+ if (withinRange(b[0], a) || withinRange(b[1], a)) {
+ if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
+ if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
+ } else {
+ merged.push(a = b);
+ }
+ }
+ var best = -Infinity, dλ;
+ for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
+ b = merged[i];
+ if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
+ }
+ }
+ ranges = range = null;
+ return λ0 === Infinity || Ï0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, Ï0 ], [ λ1, Ï1 ] ];
+ };
+ }();
+ d3.geo.centroid = function(object) {
+ d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+ d3.geo.stream(object, d3_geo_centroid);
+ var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
+ if (m < ε2) {
+ x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
+ if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
+ m = x * x + y * y + z * z;
+ if (m < ε2) return [ NaN, NaN ];
+ }
+ return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
+ };
+ var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
+ var d3_geo_centroid = {
+ sphere: d3_noop,
+ point: d3_geo_centroidPoint,
+ lineStart: d3_geo_centroidLineStart,
+ lineEnd: d3_geo_centroidLineEnd,
+ polygonStart: function() {
+ d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
+ }
+ };
+ function d3_geo_centroidPoint(λ, Ï) {
+ λ *= d3_radians;
+ var cosÏ = Math.cos(Ï *= d3_radians);
+ d3_geo_centroidPointXYZ(cosÏ * Math.cos(λ), cosÏ * Math.sin(λ), Math.sin(Ï));
+ }
+ function d3_geo_centroidPointXYZ(x, y, z) {
+ ++d3_geo_centroidW0;
+ d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
+ d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
+ d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
+ }
+ function d3_geo_centroidLineStart() {
+ var x0, y0, z0;
+ d3_geo_centroid.point = function(λ, Ï) {
+ λ *= d3_radians;
+ var cosÏ = Math.cos(Ï *= d3_radians);
+ x0 = cosÏ * Math.cos(λ);
+ y0 = cosÏ * Math.sin(λ);
+ z0 = Math.sin(Ï);
+ d3_geo_centroid.point = nextPoint;
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ };
+ function nextPoint(λ, Ï) {
+ λ *= d3_radians;
+ var cosÏ = Math.cos(Ï *= d3_radians), x = cosÏ * Math.cos(λ), y = cosÏ * Math.sin(λ), z = Math.sin(Ï), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
+ d3_geo_centroidW1 += w;
+ d3_geo_centroidX1 += w * (x0 + (x0 = x));
+ d3_geo_centroidY1 += w * (y0 + (y0 = y));
+ d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ }
+ }
+ function d3_geo_centroidLineEnd() {
+ d3_geo_centroid.point = d3_geo_centroidPoint;
+ }
+ function d3_geo_centroidRingStart() {
+ var λ00, Ï00, x0, y0, z0;
+ d3_geo_centroid.point = function(λ, Ï) {
+ λ00 = λ, Ï00 = Ï;
+ d3_geo_centroid.point = nextPoint;
+ λ *= d3_radians;
+ var cosÏ = Math.cos(Ï *= d3_radians);
+ x0 = cosÏ * Math.cos(λ);
+ y0 = cosÏ * Math.sin(λ);
+ z0 = Math.sin(Ï);
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ };
+ d3_geo_centroid.lineEnd = function() {
+ nextPoint(λ00, Ï00);
+ d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
+ d3_geo_centroid.point = d3_geo_centroidPoint;
+ };
+ function nextPoint(λ, Ï) {
+ λ *= d3_radians;
+ var cosÏ = Math.cos(Ï *= d3_radians), x = cosÏ * Math.cos(λ), y = cosÏ * Math.sin(λ), z = Math.sin(Ï), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
+ d3_geo_centroidX2 += v * cx;
+ d3_geo_centroidY2 += v * cy;
+ d3_geo_centroidZ2 += v * cz;
+ d3_geo_centroidW1 += w;
+ d3_geo_centroidX1 += w * (x0 + (x0 = x));
+ d3_geo_centroidY1 += w * (y0 + (y0 = y));
+ d3_geo_centroidZ1 += w * (z0 + (z0 = z));
+ d3_geo_centroidPointXYZ(x0, y0, z0);
+ }
+ }
+ function d3_true() {
+ return true;
+ }
+ function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) {
+ var subject = [], clip = [];
+ segments.forEach(function(segment) {
+ if ((n = segment.length - 1) <= 0) return;
+ var n, p0 = segment[0], p1 = segment[n];
+ if (d3_geo_sphericalEqual(p0, p1)) {
+ listener.lineStart();
+ for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
+ listener.lineEnd();
+ return;
+ }
+ var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false);
+ a.o = b;
+ subject.push(a);
+ clip.push(b);
+ a = new d3_geo_clipPolygonIntersection(p1, segment, null, false);
+ b = new d3_geo_clipPolygonIntersection(p1, null, a, true);
+ a.o = b;
+ subject.push(a);
+ clip.push(b);
+ });
+ clip.sort(compare);
+ d3_geo_clipPolygonLinkCircular(subject);
+ d3_geo_clipPolygonLinkCircular(clip);
+ if (!subject.length) return;
+ for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) {
+ clip[i].e = entry = !entry;
+ }
+ var start = subject[0], points, point;
+ while (1) {
+ var current = start, isSubject = true;
+ while (current.v) if ((current = current.n) === start) return;
+ points = current.z;
+ listener.lineStart();
+ do {
+ current.v = current.o.v = true;
+ if (current.e) {
+ if (isSubject) {
+ for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.n.x, 1, listener);
+ }
+ current = current.n;
+ } else {
+ if (isSubject) {
+ points = current.p.z;
+ for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]);
+ } else {
+ interpolate(current.x, current.p.x, -1, listener);
+ }
+ current = current.p;
+ }
+ current = current.o;
+ points = current.z;
+ isSubject = !isSubject;
+ } while (!current.v);
+ listener.lineEnd();
+ }
+ }
+ function d3_geo_clipPolygonLinkCircular(array) {
+ if (!(n = array.length)) return;
+ var n, i = 0, a = array[0], b;
+ while (++i < n) {
+ a.n = b = array[i];
+ b.p = a;
+ a = b;
+ }
+ a.n = b = array[0];
+ b.p = a;
+ }
+ function d3_geo_clipPolygonIntersection(point, points, other, entry) {
+ this.x = point;
+ this.z = points;
+ this.o = other;
+ this.e = entry;
+ this.v = false;
+ this.n = this.p = null;
+ }
+ function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) {
+ return function(rotate, listener) {
+ var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]);
+ var clip = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ clip.point = pointRing;
+ clip.lineStart = ringStart;
+ clip.lineEnd = ringEnd;
+ segments = [];
+ polygon = [];
+ listener.polygonStart();
+ },
+ polygonEnd: function() {
+ clip.point = point;
+ clip.lineStart = lineStart;
+ clip.lineEnd = lineEnd;
+ segments = d3.merge(segments);
+ var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon);
+ if (segments.length) {
+ d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener);
+ } else if (clipStartInside) {
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ }
+ listener.polygonEnd();
+ segments = polygon = null;
+ },
+ sphere: function() {
+ listener.polygonStart();
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ listener.polygonEnd();
+ }
+ };
+ function point(λ, Ï) {
+ var point = rotate(λ, Ï);
+ if (pointVisible(λ = point[0], Ï = point[1])) listener.point(λ, Ï);
+ }
+ function pointLine(λ, Ï) {
+ var point = rotate(λ, Ï);
+ line.point(point[0], point[1]);
+ }
+ function lineStart() {
+ clip.point = pointLine;
+ line.lineStart();
+ }
+ function lineEnd() {
+ clip.point = point;
+ line.lineEnd();
+ }
+ var segments;
+ var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring;
+ function pointRing(λ, Ï) {
+ ring.push([ λ, Ï ]);
+ var point = rotate(λ, Ï);
+ ringListener.point(point[0], point[1]);
+ }
+ function ringStart() {
+ ringListener.lineStart();
+ ring = [];
+ }
+ function ringEnd() {
+ pointRing(ring[0][0], ring[0][1]);
+ ringListener.lineEnd();
+ var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
+ ring.pop();
+ polygon.push(ring);
+ ring = null;
+ if (!n) return;
+ if (clean & 1) {
+ segment = ringSegments[0];
+ var n = segment.length - 1, i = -1, point;
+ listener.lineStart();
+ while (++i < n) listener.point((point = segment[i])[0], point[1]);
+ listener.lineEnd();
+ return;
+ }
+ if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
+ segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
+ }
+ return clip;
+ };
+ }
+ function d3_geo_clipSegmentLength1(segment) {
+ return segment.length > 1;
+ }
+ function d3_geo_clipBufferListener() {
+ var lines = [], line;
+ return {
+ lineStart: function() {
+ lines.push(line = []);
+ },
+ point: function(λ, Ï) {
+ line.push([ λ, Ï ]);
+ },
+ lineEnd: d3_noop,
+ buffer: function() {
+ var buffer = lines;
+ lines = [];
+ line = null;
+ return buffer;
+ },
+ rejoin: function() {
+ if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
+ }
+ };
+ }
+ function d3_geo_clipSort(a, b) {
+ return ((a = a.x)[0] < 0 ? a[1] - halfÏ - ε : halfÏ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfÏ - ε : halfÏ - b[1]);
+ }
+ function d3_geo_pointInPolygon(point, polygon) {
+ var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0;
+ d3_geo_areaRingSum.reset();
+ for (var i = 0, n = polygon.length; i < n; ++i) {
+ var ring = polygon[i], m = ring.length;
+ if (!m) continue;
+ var point0 = ring[0], λ0 = point0[0], Ï0 = point0[1] / 2 + Ï / 4, sinÏ0 = Math.sin(Ï0), cosÏ0 = Math.cos(Ï0), j = 1;
+ while (true) {
+ if (j === m) j = 0;
+ point = ring[j];
+ var λ = point[0], Ï = point[1] / 2 + Ï / 4, sinÏ = Math.sin(Ï), cosÏ = Math.cos(Ï), dλ = λ - λ0, antimeridian = abs(dλ) > Ï, k = sinÏ0 * sinÏ;
+ d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosÏ0 * cosÏ + k * Math.cos(dλ)));
+ polarAngle += antimeridian ? dλ + (dλ >= 0 ? Ï : -Ï) : dλ;
+ if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
+ var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
+ d3_geo_cartesianNormalize(arc);
+ var intersection = d3_geo_cartesianCross(meridianNormal, arc);
+ d3_geo_cartesianNormalize(intersection);
+ var Ïarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
+ if (parallel > Ïarc || parallel === Ïarc && (arc[0] || arc[1])) {
+ winding += antimeridian ^ dλ >= 0 ? 1 : -1;
+ }
+ }
+ if (!j++) break;
+ λ0 = λ, sinÏ0 = sinÏ, cosÏ0 = cosÏ, point0 = point;
+ }
+ }
+ return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1;
+ }
+ var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -Ï, -Ï / 2 ]);
+ function d3_geo_clipAntimeridianLine(listener) {
+ var λ0 = NaN, Ï0 = NaN, sλ0 = NaN, clean;
+ return {
+ lineStart: function() {
+ listener.lineStart();
+ clean = 1;
+ },
+ point: function(λ1, Ï1) {
+ var sλ1 = λ1 > 0 ? Ï : -Ï, dλ = abs(λ1 - λ0);
+ if (abs(dλ - Ï) < ε) {
+ listener.point(λ0, Ï0 = (Ï0 + Ï1) / 2 > 0 ? halfÏ : -halfÏ);
+ listener.point(sλ0, Ï0);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(sλ1, Ï0);
+ listener.point(λ1, Ï0);
+ clean = 0;
+ } else if (sλ0 !== sλ1 && dλ >= Ï) {
+ if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
+ if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
+ Ï0 = d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1);
+ listener.point(sλ0, Ï0);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(sλ1, Ï0);
+ clean = 0;
+ }
+ listener.point(λ0 = λ1, Ï0 = Ï1);
+ sλ0 = sλ1;
+ },
+ lineEnd: function() {
+ listener.lineEnd();
+ λ0 = Ï0 = NaN;
+ },
+ clean: function() {
+ return 2 - clean;
+ }
+ };
+ }
+ function d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1) {
+ var cosÏ0, cosÏ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
+ return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(Ï0) * (cosÏ1 = Math.cos(Ï1)) * Math.sin(λ1) - Math.sin(Ï1) * (cosÏ0 = Math.cos(Ï0)) * Math.sin(λ0)) / (cosÏ0 * cosÏ1 * sinλ0_λ1)) : (Ï0 + Ï1) / 2;
+ }
+ function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
+ var Ï;
+ if (from == null) {
+ Ï = direction * halfÏ;
+ listener.point(-Ï, Ï);
+ listener.point(0, Ï);
+ listener.point(Ï, Ï);
+ listener.point(Ï, 0);
+ listener.point(Ï, -Ï);
+ listener.point(0, -Ï);
+ listener.point(-Ï, -Ï);
+ listener.point(-Ï, 0);
+ listener.point(-Ï, Ï);
+ } else if (abs(from[0] - to[0]) > ε) {
+ var s = from[0] < to[0] ? Ï : -Ï;
+ Ï = direction * s / 2;
+ listener.point(-s, Ï);
+ listener.point(0, Ï);
+ listener.point(s, Ï);
+ } else {
+ listener.point(to[0], to[1]);
+ }
+ }
+ function d3_geo_clipCircle(radius) {
+ var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
+ return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -Ï, radius - Ï ]);
+ function visible(λ, Ï) {
+ return Math.cos(λ) * Math.cos(Ï) > cr;
+ }
+ function clipLine(listener) {
+ var point0, c0, v0, v00, clean;
+ return {
+ lineStart: function() {
+ v00 = v0 = false;
+ clean = 1;
+ },
+ point: function(λ, Ï) {
+ var point1 = [ λ, Ï ], point2, v = visible(λ, Ï), c = smallRadius ? v ? 0 : code(λ, Ï) : v ? code(λ + (λ < 0 ? Ï : -Ï), Ï) : 0;
+ if (!point0 && (v00 = v0 = v)) listener.lineStart();
+ if (v !== v0) {
+ point2 = intersect(point0, point1);
+ if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
+ point1[0] += ε;
+ point1[1] += ε;
+ v = visible(point1[0], point1[1]);
+ }
+ }
+ if (v !== v0) {
+ clean = 0;
+ if (v) {
+ listener.lineStart();
+ point2 = intersect(point1, point0);
+ listener.point(point2[0], point2[1]);
+ } else {
+ point2 = intersect(point0, point1);
+ listener.point(point2[0], point2[1]);
+ listener.lineEnd();
+ }
+ point0 = point2;
+ } else if (notHemisphere && point0 && smallRadius ^ v) {
+ var t;
+ if (!(c & c0) && (t = intersect(point1, point0, true))) {
+ clean = 0;
+ if (smallRadius) {
+ listener.lineStart();
+ listener.point(t[0][0], t[0][1]);
+ listener.point(t[1][0], t[1][1]);
+ listener.lineEnd();
+ } else {
+ listener.point(t[1][0], t[1][1]);
+ listener.lineEnd();
+ listener.lineStart();
+ listener.point(t[0][0], t[0][1]);
+ }
+ }
+ }
+ if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
+ listener.point(point1[0], point1[1]);
+ }
+ point0 = point1, v0 = v, c0 = c;
+ },
+ lineEnd: function() {
+ if (v0) listener.lineEnd();
+ point0 = null;
+ },
+ clean: function() {
+ return clean | (v00 && v0) << 1;
+ }
+ };
+ }
+ function intersect(a, b, two) {
+ var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
+ var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
+ if (!determinant) return !two && a;
+ var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
+ d3_geo_cartesianAdd(A, B);
+ var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
+ if (t2 < 0) return;
+ var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
+ d3_geo_cartesianAdd(q, A);
+ q = d3_geo_spherical(q);
+ if (!two) return q;
+ var λ0 = a[0], λ1 = b[0], Ï0 = a[1], Ï1 = b[1], z;
+ if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
+ var Ύλ = λ1 - λ0, polar = abs(Ύλ - Ï) < ε, meridian = polar || Ύλ < ε;
+ if (!polar && Ï1 < Ï0) z = Ï0, Ï0 = Ï1, Ï1 = z;
+ if (meridian ? polar ? Ï0 + Ï1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? Ï0 : Ï1) : Ï0 <= q[1] && q[1] <= Ï1 : Ύλ > Ï ^ (λ0 <= q[0] && q[0] <= λ1)) {
+ var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
+ d3_geo_cartesianAdd(q1, A);
+ return [ q, d3_geo_spherical(q1) ];
+ }
+ }
+ function code(λ, Ï) {
+ var r = smallRadius ? radius : Ï - radius, code = 0;
+ if (λ < -r) code |= 1; else if (λ > r) code |= 2;
+ if (Ï < -r) code |= 4; else if (Ï > r) code |= 8;
+ return code;
+ }
+ }
+ function d3_geom_clipLine(x0, y0, x1, y1) {
+ return function(line) {
+ var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r;
+ r = x0 - ax;
+ if (!dx && r > 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dx > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+ r = x1 - ax;
+ if (!dx && r < 0) return;
+ r /= dx;
+ if (dx < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dx > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+ r = y0 - ay;
+ if (!dy && r > 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ } else if (dy > 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ }
+ r = y1 - ay;
+ if (!dy && r < 0) return;
+ r /= dy;
+ if (dy < 0) {
+ if (r > t1) return;
+ if (r > t0) t0 = r;
+ } else if (dy > 0) {
+ if (r < t0) return;
+ if (r < t1) t1 = r;
+ }
+ if (t0 > 0) line.a = {
+ x: ax + t0 * dx,
+ y: ay + t0 * dy
+ };
+ if (t1 < 1) line.b = {
+ x: ax + t1 * dx,
+ y: ay + t1 * dy
+ };
+ return line;
+ };
+ }
+ var d3_geo_clipExtentMAX = 1e9;
+ d3.geo.clipExtent = function() {
+ var x0, y0, x1, y1, stream, clip, clipExtent = {
+ stream: function(output) {
+ if (stream) stream.valid = false;
+ stream = clip(output);
+ stream.valid = true;
+ return stream;
+ },
+ extent: function(_) {
+ if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+ clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]);
+ if (stream) stream.valid = false, stream = null;
+ return clipExtent;
+ }
+ };
+ return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]);
+ };
+ function d3_geo_clipExtent(x0, y0, x1, y1) {
+ return function(listener) {
+ var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring;
+ var clip = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ listener = bufferListener;
+ segments = [];
+ polygon = [];
+ clean = true;
+ },
+ polygonEnd: function() {
+ listener = listener_;
+ segments = d3.merge(segments);
+ var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length;
+ if (inside || visible) {
+ listener.polygonStart();
+ if (inside) {
+ listener.lineStart();
+ interpolate(null, null, 1, listener);
+ listener.lineEnd();
+ }
+ if (visible) {
+ d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener);
+ }
+ listener.polygonEnd();
+ }
+ segments = polygon = ring = null;
+ }
+ };
+ function insidePolygon(p) {
+ var wn = 0, n = polygon.length, y = p[1];
+ for (var i = 0; i < n; ++i) {
+ for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
+ b = v[j];
+ if (a[1] <= y) {
+ if (b[1] > y && isLeft(a, b, p) > 0) ++wn;
+ } else {
+ if (b[1] <= y && isLeft(a, b, p) < 0) --wn;
+ }
+ a = b;
+ }
+ }
+ return wn !== 0;
+ }
+ function isLeft(a, b, c) {
+ return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
+ }
+ function interpolate(from, to, direction, listener) {
+ var a = 0, a1 = 0;
+ if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
+ do {
+ listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
+ } while ((a = (a + direction + 4) % 4) !== a1);
+ } else {
+ listener.point(to[0], to[1]);
+ }
+ }
+ function pointVisible(x, y) {
+ return x0 <= x && x <= x1 && y0 <= y && y <= y1;
+ }
+ function point(x, y) {
+ if (pointVisible(x, y)) listener.point(x, y);
+ }
+ var x__, y__, v__, x_, y_, v_, first, clean;
+ function lineStart() {
+ clip.point = linePoint;
+ if (polygon) polygon.push(ring = []);
+ first = true;
+ v_ = false;
+ x_ = y_ = NaN;
+ }
+ function lineEnd() {
+ if (segments) {
+ linePoint(x__, y__);
+ if (v__ && v_) bufferListener.rejoin();
+ segments.push(bufferListener.buffer());
+ }
+ clip.point = point;
+ if (v_) listener.lineEnd();
+ }
+ function linePoint(x, y) {
+ x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x));
+ y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y));
+ var v = pointVisible(x, y);
+ if (polygon) ring.push([ x, y ]);
+ if (first) {
+ x__ = x, y__ = y, v__ = v;
+ first = false;
+ if (v) {
+ listener.lineStart();
+ listener.point(x, y);
+ }
+ } else {
+ if (v && v_) listener.point(x, y); else {
+ var l = {
+ a: {
+ x: x_,
+ y: y_
+ },
+ b: {
+ x: x,
+ y: y
+ }
+ };
+ if (clipLine(l)) {
+ if (!v_) {
+ listener.lineStart();
+ listener.point(l.a.x, l.a.y);
+ }
+ listener.point(l.b.x, l.b.y);
+ if (!v) listener.lineEnd();
+ clean = false;
+ } else if (v) {
+ listener.lineStart();
+ listener.point(x, y);
+ clean = false;
+ }
+ }
+ }
+ x_ = x, y_ = y, v_ = v;
+ }
+ return clip;
+ };
+ function corner(p, direction) {
+ return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
+ }
+ function compare(a, b) {
+ return comparePoints(a.x, b.x);
+ }
+ function comparePoints(a, b) {
+ var ca = corner(a, 1), cb = corner(b, 1);
+ return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
+ }
+ }
+ function d3_geo_compose(a, b) {
+ function compose(x, y) {
+ return x = a(x, y), b(x[0], x[1]);
+ }
+ if (a.invert && b.invert) compose.invert = function(x, y) {
+ return x = b.invert(x, y), x && a.invert(x[0], x[1]);
+ };
+ return compose;
+ }
+ function d3_geo_conic(projectAt) {
+ var Ï0 = 0, Ï1 = Ï / 3, m = d3_geo_projectionMutator(projectAt), p = m(Ï0, Ï1);
+ p.parallels = function(_) {
+ if (!arguments.length) return [ Ï0 / Ï * 180, Ï1 / Ï * 180 ];
+ return m(Ï0 = _[0] * Ï / 180, Ï1 = _[1] * Ï / 180);
+ };
+ return p;
+ }
+ function d3_geo_conicEqualArea(Ï0, Ï1) {
+ var sinÏ0 = Math.sin(Ï0), n = (sinÏ0 + Math.sin(Ï1)) / 2, C = 1 + sinÏ0 * (2 * n - sinÏ0), Ï0 = Math.sqrt(C) / n;
+ function forward(λ, Ï) {
+ var Ï = Math.sqrt(C - 2 * n * Math.sin(Ï)) / n;
+ return [ Ï * Math.sin(λ *= n), Ï0 - Ï * Math.cos(λ) ];
+ }
+ forward.invert = function(x, y) {
+ var Ï0_y = Ï0 - y;
+ return [ Math.atan2(x, Ï0_y) / n, d3_asin((C - (x * x + Ï0_y * Ï0_y) * n * n) / (2 * n)) ];
+ };
+ return forward;
+ }
+ (d3.geo.conicEqualArea = function() {
+ return d3_geo_conic(d3_geo_conicEqualArea);
+ }).raw = d3_geo_conicEqualArea;
+ d3.geo.albers = function() {
+ return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
+ };
+ d3.geo.albersUsa = function() {
+ var lower48 = d3.geo.albers();
+ var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
+ var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
+ var point, pointStream = {
+ point: function(x, y) {
+ point = [ x, y ];
+ }
+ }, lower48Point, alaskaPoint, hawaiiPoint;
+ function albersUsa(coordinates) {
+ var x = coordinates[0], y = coordinates[1];
+ point = null;
+ (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
+ return point;
+ }
+ albersUsa.invert = function(coordinates) {
+ var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
+ return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
+ };
+ albersUsa.stream = function(stream) {
+ var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
+ return {
+ point: function(x, y) {
+ lower48Stream.point(x, y);
+ alaskaStream.point(x, y);
+ hawaiiStream.point(x, y);
+ },
+ sphere: function() {
+ lower48Stream.sphere();
+ alaskaStream.sphere();
+ hawaiiStream.sphere();
+ },
+ lineStart: function() {
+ lower48Stream.lineStart();
+ alaskaStream.lineStart();
+ hawaiiStream.lineStart();
+ },
+ lineEnd: function() {
+ lower48Stream.lineEnd();
+ alaskaStream.lineEnd();
+ hawaiiStream.lineEnd();
+ },
+ polygonStart: function() {
+ lower48Stream.polygonStart();
+ alaskaStream.polygonStart();
+ hawaiiStream.polygonStart();
+ },
+ polygonEnd: function() {
+ lower48Stream.polygonEnd();
+ alaskaStream.polygonEnd();
+ hawaiiStream.polygonEnd();
+ }
+ };
+ };
+ albersUsa.precision = function(_) {
+ if (!arguments.length) return lower48.precision();
+ lower48.precision(_);
+ alaska.precision(_);
+ hawaii.precision(_);
+ return albersUsa;
+ };
+ albersUsa.scale = function(_) {
+ if (!arguments.length) return lower48.scale();
+ lower48.scale(_);
+ alaska.scale(_ * .35);
+ hawaii.scale(_);
+ return albersUsa.translate(lower48.translate());
+ };
+ albersUsa.translate = function(_) {
+ if (!arguments.length) return lower48.translate();
+ var k = lower48.scale(), x = +_[0], y = +_[1];
+ lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
+ alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+ hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
+ return albersUsa;
+ };
+ return albersUsa.scale(1070);
+ };
+ var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
+ point: d3_noop,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: function() {
+ d3_geo_pathAreaPolygon = 0;
+ d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
+ d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2);
+ }
+ };
+ function d3_geo_pathAreaRingStart() {
+ var x00, y00, x0, y0;
+ d3_geo_pathArea.point = function(x, y) {
+ d3_geo_pathArea.point = nextPoint;
+ x00 = x0 = x, y00 = y0 = y;
+ };
+ function nextPoint(x, y) {
+ d3_geo_pathAreaPolygon += y0 * x - x0 * y;
+ x0 = x, y0 = y;
+ }
+ d3_geo_pathArea.lineEnd = function() {
+ nextPoint(x00, y00);
+ };
+ }
+ var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
+ var d3_geo_pathBounds = {
+ point: d3_geo_pathBoundsPoint,
+ lineStart: d3_noop,
+ lineEnd: d3_noop,
+ polygonStart: d3_noop,
+ polygonEnd: d3_noop
+ };
+ function d3_geo_pathBoundsPoint(x, y) {
+ if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
+ if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
+ if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
+ if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
+ }
+ function d3_geo_pathBuffer() {
+ var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
+ var stream = {
+ point: point,
+ lineStart: function() {
+ stream.point = pointLineStart;
+ },
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.lineEnd = lineEndPolygon;
+ },
+ polygonEnd: function() {
+ stream.lineEnd = lineEnd;
+ stream.point = point;
+ },
+ pointRadius: function(_) {
+ pointCircle = d3_geo_pathBufferCircle(_);
+ return stream;
+ },
+ result: function() {
+ if (buffer.length) {
+ var result = buffer.join("");
+ buffer = [];
+ return result;
+ }
+ }
+ };
+ function point(x, y) {
+ buffer.push("M", x, ",", y, pointCircle);
+ }
+ function pointLineStart(x, y) {
+ buffer.push("M", x, ",", y);
+ stream.point = pointLine;
+ }
+ function pointLine(x, y) {
+ buffer.push("L", x, ",", y);
+ }
+ function lineEnd() {
+ stream.point = point;
+ }
+ function lineEndPolygon() {
+ buffer.push("Z");
+ }
+ return stream;
+ }
+ function d3_geo_pathBufferCircle(radius) {
+ return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
+ }
+ var d3_geo_pathCentroid = {
+ point: d3_geo_pathCentroidPoint,
+ lineStart: d3_geo_pathCentroidLineStart,
+ lineEnd: d3_geo_pathCentroidLineEnd,
+ polygonStart: function() {
+ d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
+ },
+ polygonEnd: function() {
+ d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+ d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
+ d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
+ }
+ };
+ function d3_geo_pathCentroidPoint(x, y) {
+ d3_geo_centroidX0 += x;
+ d3_geo_centroidY0 += y;
+ ++d3_geo_centroidZ0;
+ }
+ function d3_geo_pathCentroidLineStart() {
+ var x0, y0;
+ d3_geo_pathCentroid.point = function(x, y) {
+ d3_geo_pathCentroid.point = nextPoint;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ };
+ function nextPoint(x, y) {
+ var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+ d3_geo_centroidX1 += z * (x0 + x) / 2;
+ d3_geo_centroidY1 += z * (y0 + y) / 2;
+ d3_geo_centroidZ1 += z;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ }
+ }
+ function d3_geo_pathCentroidLineEnd() {
+ d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
+ }
+ function d3_geo_pathCentroidRingStart() {
+ var x00, y00, x0, y0;
+ d3_geo_pathCentroid.point = function(x, y) {
+ d3_geo_pathCentroid.point = nextPoint;
+ d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
+ };
+ function nextPoint(x, y) {
+ var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
+ d3_geo_centroidX1 += z * (x0 + x) / 2;
+ d3_geo_centroidY1 += z * (y0 + y) / 2;
+ d3_geo_centroidZ1 += z;
+ z = y0 * x - x0 * y;
+ d3_geo_centroidX2 += z * (x0 + x);
+ d3_geo_centroidY2 += z * (y0 + y);
+ d3_geo_centroidZ2 += z * 3;
+ d3_geo_pathCentroidPoint(x0 = x, y0 = y);
+ }
+ d3_geo_pathCentroid.lineEnd = function() {
+ nextPoint(x00, y00);
+ };
+ }
+ function d3_geo_pathContext(context) {
+ var pointRadius = 4.5;
+ var stream = {
+ point: point,
+ lineStart: function() {
+ stream.point = pointLineStart;
+ },
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.lineEnd = lineEndPolygon;
+ },
+ polygonEnd: function() {
+ stream.lineEnd = lineEnd;
+ stream.point = point;
+ },
+ pointRadius: function(_) {
+ pointRadius = _;
+ return stream;
+ },
+ result: d3_noop
+ };
+ function point(x, y) {
+ context.moveTo(x, y);
+ context.arc(x, y, pointRadius, 0, Ï);
+ }
+ function pointLineStart(x, y) {
+ context.moveTo(x, y);
+ stream.point = pointLine;
+ }
+ function pointLine(x, y) {
+ context.lineTo(x, y);
+ }
+ function lineEnd() {
+ stream.point = point;
+ }
+ function lineEndPolygon() {
+ context.closePath();
+ }
+ return stream;
+ }
+ function d3_geo_resample(project) {
+ var ÎŽ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
+ function resample(stream) {
+ return (maxDepth ? resampleRecursive : resampleNone)(stream);
+ }
+ function resampleNone(stream) {
+ return d3_geo_transformPoint(stream, function(x, y) {
+ x = project(x, y);
+ stream.point(x[0], x[1]);
+ });
+ }
+ function resampleRecursive(stream) {
+ var λ00, Ï00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
+ var resample = {
+ point: point,
+ lineStart: lineStart,
+ lineEnd: lineEnd,
+ polygonStart: function() {
+ stream.polygonStart();
+ resample.lineStart = ringStart;
+ },
+ polygonEnd: function() {
+ stream.polygonEnd();
+ resample.lineStart = lineStart;
+ }
+ };
+ function point(x, y) {
+ x = project(x, y);
+ stream.point(x[0], x[1]);
+ }
+ function lineStart() {
+ x0 = NaN;
+ resample.point = linePoint;
+ stream.lineStart();
+ }
+ function linePoint(λ, Ï) {
+ var c = d3_geo_cartesian([ λ, Ï ]), p = project(λ, Ï);
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
+ stream.point(x0, y0);
+ }
+ function lineEnd() {
+ resample.point = point;
+ stream.lineEnd();
+ }
+ function ringStart() {
+ lineStart();
+ resample.point = ringPoint;
+ resample.lineEnd = ringEnd;
+ }
+ function ringPoint(λ, Ï) {
+ linePoint(λ00 = λ, Ï00 = Ï), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
+ resample.point = linePoint;
+ }
+ function ringEnd() {
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
+ resample.lineEnd = lineEnd;
+ lineEnd();
+ }
+ return resample;
+ }
+ function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
+ var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
+ if (d2 > 4 * ÎŽ2 && depth--) {
+ var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), Ï2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, Ï2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
+ if (dz * dz / d2 > ÎŽ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
+ resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
+ stream.point(x2, y2);
+ resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
+ }
+ }
+ }
+ resample.precision = function(_) {
+ if (!arguments.length) return Math.sqrt(ÎŽ2);
+ maxDepth = (ÎŽ2 = _ * _) > 0 && 16;
+ return resample;
+ };
+ return resample;
+ }
+ d3.geo.path = function() {
+ var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
+ function path(object) {
+ if (object) {
+ if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
+ if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
+ d3.geo.stream(object, cacheStream);
+ }
+ return contextStream.result();
+ }
+ path.area = function(object) {
+ d3_geo_pathAreaSum = 0;
+ d3.geo.stream(object, projectStream(d3_geo_pathArea));
+ return d3_geo_pathAreaSum;
+ };
+ path.centroid = function(object) {
+ d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
+ d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
+ return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
+ };
+ path.bounds = function(object) {
+ d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
+ d3.geo.stream(object, projectStream(d3_geo_pathBounds));
+ return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
+ };
+ path.projection = function(_) {
+ if (!arguments.length) return projection;
+ projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
+ return reset();
+ };
+ path.context = function(_) {
+ if (!arguments.length) return context;
+ contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
+ if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
+ return reset();
+ };
+ path.pointRadius = function(_) {
+ if (!arguments.length) return pointRadius;
+ pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
+ return path;
+ };
+ function reset() {
+ cacheStream = null;
+ return path;
+ }
+ return path.projection(d3.geo.albersUsa()).context(null);
+ };
+ function d3_geo_pathProjectStream(project) {
+ var resample = d3_geo_resample(function(x, y) {
+ return project([ x * d3_degrees, y * d3_degrees ]);
+ });
+ return function(stream) {
+ return d3_geo_projectionRadians(resample(stream));
+ };
+ }
+ d3.geo.transform = function(methods) {
+ return {
+ stream: function(stream) {
+ var transform = new d3_geo_transform(stream);
+ for (var k in methods) transform[k] = methods[k];
+ return transform;
+ }
+ };
+ };
+ function d3_geo_transform(stream) {
+ this.stream = stream;
+ }
+ d3_geo_transform.prototype = {
+ point: function(x, y) {
+ this.stream.point(x, y);
+ },
+ sphere: function() {
+ this.stream.sphere();
+ },
+ lineStart: function() {
+ this.stream.lineStart();
+ },
+ lineEnd: function() {
+ this.stream.lineEnd();
+ },
+ polygonStart: function() {
+ this.stream.polygonStart();
+ },
+ polygonEnd: function() {
+ this.stream.polygonEnd();
+ }
+ };
+ function d3_geo_transformPoint(stream, point) {
+ return {
+ point: point,
+ sphere: function() {
+ stream.sphere();
+ },
+ lineStart: function() {
+ stream.lineStart();
+ },
+ lineEnd: function() {
+ stream.lineEnd();
+ },
+ polygonStart: function() {
+ stream.polygonStart();
+ },
+ polygonEnd: function() {
+ stream.polygonEnd();
+ }
+ };
+ }
+ d3.geo.projection = d3_geo_projection;
+ d3.geo.projectionMutator = d3_geo_projectionMutator;
+ function d3_geo_projection(project) {
+ return d3_geo_projectionMutator(function() {
+ return project;
+ })();
+ }
+ function d3_geo_projectionMutator(projectAt) {
+ var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
+ x = project(x, y);
+ return [ x[0] * k + ÎŽx, ÎŽy - x[1] * k ];
+ }), k = 150, x = 480, y = 250, λ = 0, Ï = 0, Ύλ = 0, ÎŽÏ = 0, Ύγ = 0, ÎŽx, ÎŽy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
+ function projection(point) {
+ point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
+ return [ point[0] * k + ÎŽx, ÎŽy - point[1] * k ];
+ }
+ function invert(point) {
+ point = projectRotate.invert((point[0] - ÎŽx) / k, (ÎŽy - point[1]) / k);
+ return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
+ }
+ projection.stream = function(output) {
+ if (stream) stream.valid = false;
+ stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output))));
+ stream.valid = true;
+ return stream;
+ };
+ projection.clipAngle = function(_) {
+ if (!arguments.length) return clipAngle;
+ preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
+ return invalidate();
+ };
+ projection.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent;
+ clipExtent = _;
+ postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity;
+ return invalidate();
+ };
+ projection.scale = function(_) {
+ if (!arguments.length) return k;
+ k = +_;
+ return reset();
+ };
+ projection.translate = function(_) {
+ if (!arguments.length) return [ x, y ];
+ x = +_[0];
+ y = +_[1];
+ return reset();
+ };
+ projection.center = function(_) {
+ if (!arguments.length) return [ λ * d3_degrees, Ï * d3_degrees ];
+ λ = _[0] % 360 * d3_radians;
+ Ï = _[1] % 360 * d3_radians;
+ return reset();
+ };
+ projection.rotate = function(_) {
+ if (!arguments.length) return [ Ύλ * d3_degrees, ÎŽÏ * d3_degrees, Ύγ * d3_degrees ];
+ Ύλ = _[0] % 360 * d3_radians;
+ ÎŽÏ = _[1] % 360 * d3_radians;
+ Ύγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
+ return reset();
+ };
+ d3.rebind(projection, projectResample, "precision");
+ function reset() {
+ projectRotate = d3_geo_compose(rotate = d3_geo_rotation(Ύλ, ÎŽÏ, Ύγ), project);
+ var center = project(λ, Ï);
+ ÎŽx = x - center[0] * k;
+ ÎŽy = y + center[1] * k;
+ return invalidate();
+ }
+ function invalidate() {
+ if (stream) stream.valid = false, stream = null;
+ return projection;
+ }
+ return function() {
+ project = projectAt.apply(this, arguments);
+ projection.invert = project.invert && invert;
+ return reset();
+ };
+ }
+ function d3_geo_projectionRadians(stream) {
+ return d3_geo_transformPoint(stream, function(x, y) {
+ stream.point(x * d3_radians, y * d3_radians);
+ });
+ }
+ function d3_geo_equirectangular(λ, Ï) {
+ return [ λ, Ï ];
+ }
+ (d3.geo.equirectangular = function() {
+ return d3_geo_projection(d3_geo_equirectangular);
+ }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
+ d3.geo.rotation = function(rotate) {
+ rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
+ function forward(coordinates) {
+ coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+ return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+ }
+ forward.invert = function(coordinates) {
+ coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
+ return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
+ };
+ return forward;
+ };
+ function d3_geo_identityRotation(λ, Ï) {
+ return [ λ > Ï ? λ - Ï : λ < -Ï ? λ + Ï : λ, Ï ];
+ }
+ d3_geo_identityRotation.invert = d3_geo_equirectangular;
+ function d3_geo_rotation(Ύλ, ÎŽÏ, Ύγ) {
+ return Ύλ ? ÎŽÏ || Ύγ ? d3_geo_compose(d3_geo_rotationλ(Ύλ), d3_geo_rotationÏγ(ÎŽÏ, Ύγ)) : d3_geo_rotationλ(Ύλ) : ÎŽÏ || Ύγ ? d3_geo_rotationÏγ(ÎŽÏ, Ύγ) : d3_geo_identityRotation;
+ }
+ function d3_geo_forwardRotationλ(Ύλ) {
+ return function(λ, Ï) {
+ return λ += Ύλ, [ λ > Ï ? λ - Ï : λ < -Ï ? λ + Ï : λ, Ï ];
+ };
+ }
+ function d3_geo_rotationλ(Ύλ) {
+ var rotation = d3_geo_forwardRotationλ(Ύλ);
+ rotation.invert = d3_geo_forwardRotationλ(-Ύλ);
+ return rotation;
+ }
+ function d3_geo_rotationÏγ(ÎŽÏ, Ύγ) {
+ var cosÎŽÏ = Math.cos(ÎŽÏ), sinÎŽÏ = Math.sin(ÎŽÏ), cosΎγ = Math.cos(Ύγ), sinΎγ = Math.sin(Ύγ);
+ function rotation(λ, Ï) {
+ var cosÏ = Math.cos(Ï), x = Math.cos(λ) * cosÏ, y = Math.sin(λ) * cosÏ, z = Math.sin(Ï), k = z * cosÎŽÏ + x * sinÎŽÏ;
+ return [ Math.atan2(y * cosΎγ - k * sinΎγ, x * cosÎŽÏ - z * sinÎŽÏ), d3_asin(k * cosΎγ + y * sinΎγ) ];
+ }
+ rotation.invert = function(λ, Ï) {
+ var cosÏ = Math.cos(Ï), x = Math.cos(λ) * cosÏ, y = Math.sin(λ) * cosÏ, z = Math.sin(Ï), k = z * cosΎγ - y * sinΎγ;
+ return [ Math.atan2(y * cosΎγ + z * sinΎγ, x * cosÎŽÏ + k * sinÎŽÏ), d3_asin(k * cosÎŽÏ - x * sinÎŽÏ) ];
+ };
+ return rotation;
+ }
+ d3.geo.circle = function() {
+ var origin = [ 0, 0 ], angle, precision = 6, interpolate;
+ function circle() {
+ var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
+ interpolate(null, null, 1, {
+ point: function(x, y) {
+ ring.push(x = rotate(x, y));
+ x[0] *= d3_degrees, x[1] *= d3_degrees;
+ }
+ });
+ return {
+ type: "Polygon",
+ coordinates: [ ring ]
+ };
+ }
+ circle.origin = function(x) {
+ if (!arguments.length) return origin;
+ origin = x;
+ return circle;
+ };
+ circle.angle = function(x) {
+ if (!arguments.length) return angle;
+ interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
+ return circle;
+ };
+ circle.precision = function(_) {
+ if (!arguments.length) return precision;
+ interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
+ return circle;
+ };
+ return circle.angle(90);
+ };
+ function d3_geo_circleInterpolate(radius, precision) {
+ var cr = Math.cos(radius), sr = Math.sin(radius);
+ return function(from, to, direction, listener) {
+ var step = direction * precision;
+ if (from != null) {
+ from = d3_geo_circleAngle(cr, from);
+ to = d3_geo_circleAngle(cr, to);
+ if (direction > 0 ? from < to : from > to) from += direction * Ï;
+ } else {
+ from = radius + direction * Ï;
+ to = radius - .5 * step;
+ }
+ for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) {
+ listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
+ }
+ };
+ }
+ function d3_geo_circleAngle(cr, point) {
+ var a = d3_geo_cartesian(point);
+ a[0] -= cr;
+ d3_geo_cartesianNormalize(a);
+ var angle = d3_acos(-a[1]);
+ return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
+ }
+ d3.geo.distance = function(a, b) {
+ var Îλ = (b[0] - a[0]) * d3_radians, Ï0 = a[1] * d3_radians, Ï1 = b[1] * d3_radians, sinÎλ = Math.sin(Îλ), cosÎλ = Math.cos(Îλ), sinÏ0 = Math.sin(Ï0), cosÏ0 = Math.cos(Ï0), sinÏ1 = Math.sin(Ï1), cosÏ1 = Math.cos(Ï1), t;
+ return Math.atan2(Math.sqrt((t = cosÏ1 * sinÎλ) * t + (t = cosÏ0 * sinÏ1 - sinÏ0 * cosÏ1 * cosÎλ) * t), sinÏ0 * sinÏ1 + cosÏ0 * cosÏ1 * cosÎλ);
+ };
+ d3.geo.graticule = function() {
+ var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
+ function graticule() {
+ return {
+ type: "MultiLineString",
+ coordinates: lines()
+ };
+ }
+ function lines() {
+ return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
+ return abs(x % DX) > ε;
+ }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
+ return abs(y % DY) > ε;
+ }).map(y));
+ }
+ graticule.lines = function() {
+ return lines().map(function(coordinates) {
+ return {
+ type: "LineString",
+ coordinates: coordinates
+ };
+ });
+ };
+ graticule.outline = function() {
+ return {
+ type: "Polygon",
+ coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
+ };
+ };
+ graticule.extent = function(_) {
+ if (!arguments.length) return graticule.minorExtent();
+ return graticule.majorExtent(_).minorExtent(_);
+ };
+ graticule.majorExtent = function(_) {
+ if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
+ X0 = +_[0][0], X1 = +_[1][0];
+ Y0 = +_[0][1], Y1 = +_[1][1];
+ if (X0 > X1) _ = X0, X0 = X1, X1 = _;
+ if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
+ return graticule.precision(precision);
+ };
+ graticule.minorExtent = function(_) {
+ if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
+ x0 = +_[0][0], x1 = +_[1][0];
+ y0 = +_[0][1], y1 = +_[1][1];
+ if (x0 > x1) _ = x0, x0 = x1, x1 = _;
+ if (y0 > y1) _ = y0, y0 = y1, y1 = _;
+ return graticule.precision(precision);
+ };
+ graticule.step = function(_) {
+ if (!arguments.length) return graticule.minorStep();
+ return graticule.majorStep(_).minorStep(_);
+ };
+ graticule.majorStep = function(_) {
+ if (!arguments.length) return [ DX, DY ];
+ DX = +_[0], DY = +_[1];
+ return graticule;
+ };
+ graticule.minorStep = function(_) {
+ if (!arguments.length) return [ dx, dy ];
+ dx = +_[0], dy = +_[1];
+ return graticule;
+ };
+ graticule.precision = function(_) {
+ if (!arguments.length) return precision;
+ precision = +_;
+ x = d3_geo_graticuleX(y0, y1, 90);
+ y = d3_geo_graticuleY(x0, x1, precision);
+ X = d3_geo_graticuleX(Y0, Y1, 90);
+ Y = d3_geo_graticuleY(X0, X1, precision);
+ return graticule;
+ };
+ return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
+ };
+ function d3_geo_graticuleX(y0, y1, dy) {
+ var y = d3.range(y0, y1 - ε, dy).concat(y1);
+ return function(x) {
+ return y.map(function(y) {
+ return [ x, y ];
+ });
+ };
+ }
+ function d3_geo_graticuleY(x0, x1, dx) {
+ var x = d3.range(x0, x1 - ε, dx).concat(x1);
+ return function(y) {
+ return x.map(function(x) {
+ return [ x, y ];
+ });
+ };
+ }
+ function d3_source(d) {
+ return d.source;
+ }
+ function d3_target(d) {
+ return d.target;
+ }
+ d3.geo.greatArc = function() {
+ var source = d3_source, source_, target = d3_target, target_;
+ function greatArc() {
+ return {
+ type: "LineString",
+ coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
+ };
+ }
+ greatArc.distance = function() {
+ return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
+ };
+ greatArc.source = function(_) {
+ if (!arguments.length) return source;
+ source = _, source_ = typeof _ === "function" ? null : _;
+ return greatArc;
+ };
+ greatArc.target = function(_) {
+ if (!arguments.length) return target;
+ target = _, target_ = typeof _ === "function" ? null : _;
+ return greatArc;
+ };
+ greatArc.precision = function() {
+ return arguments.length ? greatArc : 0;
+ };
+ return greatArc;
+ };
+ d3.geo.interpolate = function(source, target) {
+ return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
+ };
+ function d3_geo_interpolate(x0, y0, x1, y1) {
+ var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
+ var interpolate = d ? function(t) {
+ var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
+ return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
+ } : function() {
+ return [ x0 * d3_degrees, y0 * d3_degrees ];
+ };
+ interpolate.distance = d;
+ return interpolate;
+ }
+ d3.geo.length = function(object) {
+ d3_geo_lengthSum = 0;
+ d3.geo.stream(object, d3_geo_length);
+ return d3_geo_lengthSum;
+ };
+ var d3_geo_lengthSum;
+ var d3_geo_length = {
+ sphere: d3_noop,
+ point: d3_noop,
+ lineStart: d3_geo_lengthLineStart,
+ lineEnd: d3_noop,
+ polygonStart: d3_noop,
+ polygonEnd: d3_noop
+ };
+ function d3_geo_lengthLineStart() {
+ var λ0, sinÏ0, cosÏ0;
+ d3_geo_length.point = function(λ, Ï) {
+ λ0 = λ * d3_radians, sinÏ0 = Math.sin(Ï *= d3_radians), cosÏ0 = Math.cos(Ï);
+ d3_geo_length.point = nextPoint;
+ };
+ d3_geo_length.lineEnd = function() {
+ d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
+ };
+ function nextPoint(λ, Ï) {
+ var sinÏ = Math.sin(Ï *= d3_radians), cosÏ = Math.cos(Ï), t = abs((λ *= d3_radians) - λ0), cosÎλ = Math.cos(t);
+ d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosÏ * Math.sin(t)) * t + (t = cosÏ0 * sinÏ - sinÏ0 * cosÏ * cosÎλ) * t), sinÏ0 * sinÏ + cosÏ0 * cosÏ * cosÎλ);
+ λ0 = λ, sinÏ0 = sinÏ, cosÏ0 = cosÏ;
+ }
+ }
+ function d3_geo_azimuthal(scale, angle) {
+ function azimuthal(λ, Ï) {
+ var cosλ = Math.cos(λ), cosÏ = Math.cos(Ï), k = scale(cosλ * cosÏ);
+ return [ k * cosÏ * Math.sin(λ), k * Math.sin(Ï) ];
+ }
+ azimuthal.invert = function(x, y) {
+ var Ï = Math.sqrt(x * x + y * y), c = angle(Ï), sinc = Math.sin(c), cosc = Math.cos(c);
+ return [ Math.atan2(x * sinc, Ï * cosc), Math.asin(Ï && y * sinc / Ï) ];
+ };
+ return azimuthal;
+ }
+ var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosÏ) {
+ return Math.sqrt(2 / (1 + cosλcosÏ));
+ }, function(Ï) {
+ return 2 * Math.asin(Ï / 2);
+ });
+ (d3.geo.azimuthalEqualArea = function() {
+ return d3_geo_projection(d3_geo_azimuthalEqualArea);
+ }).raw = d3_geo_azimuthalEqualArea;
+ var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosÏ) {
+ var c = Math.acos(cosλcosÏ);
+ return c && c / Math.sin(c);
+ }, d3_identity);
+ (d3.geo.azimuthalEquidistant = function() {
+ return d3_geo_projection(d3_geo_azimuthalEquidistant);
+ }).raw = d3_geo_azimuthalEquidistant;
+ function d3_geo_conicConformal(Ï0, Ï1) {
+ var cosÏ0 = Math.cos(Ï0), t = function(Ï) {
+ return Math.tan(Ï / 4 + Ï / 2);
+ }, n = Ï0 === Ï1 ? Math.sin(Ï0) : Math.log(cosÏ0 / Math.cos(Ï1)) / Math.log(t(Ï1) / t(Ï0)), F = cosÏ0 * Math.pow(t(Ï0), n) / n;
+ if (!n) return d3_geo_mercator;
+ function forward(λ, Ï) {
+ var Ï = abs(abs(Ï) - halfÏ) < ε ? 0 : F / Math.pow(t(Ï), n);
+ return [ Ï * Math.sin(n * λ), F - Ï * Math.cos(n * λ) ];
+ }
+ forward.invert = function(x, y) {
+ var Ï0_y = F - y, Ï = d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y);
+ return [ Math.atan2(x, Ï0_y) / n, 2 * Math.atan(Math.pow(F / Ï, 1 / n)) - halfÏ ];
+ };
+ return forward;
+ }
+ (d3.geo.conicConformal = function() {
+ return d3_geo_conic(d3_geo_conicConformal);
+ }).raw = d3_geo_conicConformal;
+ function d3_geo_conicEquidistant(Ï0, Ï1) {
+ var cosÏ0 = Math.cos(Ï0), n = Ï0 === Ï1 ? Math.sin(Ï0) : (cosÏ0 - Math.cos(Ï1)) / (Ï1 - Ï0), G = cosÏ0 / n + Ï0;
+ if (abs(n) < ε) return d3_geo_equirectangular;
+ function forward(λ, Ï) {
+ var Ï = G - Ï;
+ return [ Ï * Math.sin(n * λ), G - Ï * Math.cos(n * λ) ];
+ }
+ forward.invert = function(x, y) {
+ var Ï0_y = G - y;
+ return [ Math.atan2(x, Ï0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y) ];
+ };
+ return forward;
+ }
+ (d3.geo.conicEquidistant = function() {
+ return d3_geo_conic(d3_geo_conicEquidistant);
+ }).raw = d3_geo_conicEquidistant;
+ var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosÏ) {
+ return 1 / cosλcosÏ;
+ }, Math.atan);
+ (d3.geo.gnomonic = function() {
+ return d3_geo_projection(d3_geo_gnomonic);
+ }).raw = d3_geo_gnomonic;
+ function d3_geo_mercator(λ, Ï) {
+ return [ λ, Math.log(Math.tan(Ï / 4 + Ï / 2)) ];
+ }
+ d3_geo_mercator.invert = function(x, y) {
+ return [ x, 2 * Math.atan(Math.exp(y)) - halfÏ ];
+ };
+ function d3_geo_mercatorProjection(project) {
+ var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
+ m.scale = function() {
+ var v = scale.apply(m, arguments);
+ return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+ };
+ m.translate = function() {
+ var v = translate.apply(m, arguments);
+ return v === m ? clipAuto ? m.clipExtent(null) : m : v;
+ };
+ m.clipExtent = function(_) {
+ var v = clipExtent.apply(m, arguments);
+ if (v === m) {
+ if (clipAuto = _ == null) {
+ var k = Ï * scale(), t = translate();
+ clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
+ }
+ } else if (clipAuto) {
+ v = null;
+ }
+ return v;
+ };
+ return m.clipExtent(null);
+ }
+ (d3.geo.mercator = function() {
+ return d3_geo_mercatorProjection(d3_geo_mercator);
+ }).raw = d3_geo_mercator;
+ var d3_geo_orthographic = d3_geo_azimuthal(function() {
+ return 1;
+ }, Math.asin);
+ (d3.geo.orthographic = function() {
+ return d3_geo_projection(d3_geo_orthographic);
+ }).raw = d3_geo_orthographic;
+ var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosÏ) {
+ return 1 / (1 + cosλcosÏ);
+ }, function(Ï) {
+ return 2 * Math.atan(Ï);
+ });
+ (d3.geo.stereographic = function() {
+ return d3_geo_projection(d3_geo_stereographic);
+ }).raw = d3_geo_stereographic;
+ function d3_geo_transverseMercator(λ, Ï) {
+ return [ Math.log(Math.tan(Ï / 4 + Ï / 2)), -λ ];
+ }
+ d3_geo_transverseMercator.invert = function(x, y) {
+ return [ -y, 2 * Math.atan(Math.exp(x)) - halfÏ ];
+ };
+ (d3.geo.transverseMercator = function() {
+ var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate;
+ projection.center = function(_) {
+ return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ -_[1], _[0] ]);
+ };
+ projection.rotate = function(_) {
+ return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(),
+ [ _[0], _[1], _[2] - 90 ]);
+ };
+ return projection.rotate([ 0, 0 ]);
+ }).raw = d3_geo_transverseMercator;
+ d3.geom = {};
+ function d3_geom_pointX(d) {
+ return d[0];
+ }
+ function d3_geom_pointY(d) {
+ return d[1];
+ }
+ d3.geom.hull = function(vertices) {
+ var x = d3_geom_pointX, y = d3_geom_pointY;
+ if (arguments.length) return hull(vertices);
+ function hull(data) {
+ if (data.length < 3) return [];
+ var fx = d3_functor(x), fy = d3_functor(y), n = data.length, vertices, plen = n - 1, points = [], stack = [], d, i, j, h = 0, x1, y1, x2, y2, u, v, a, sp;
+ if (fx === d3_geom_pointX && y === d3_geom_pointY) vertices = data; else for (i = 0,
+ vertices = []; i < n; ++i) {
+ vertices.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
+ }
+ for (i = 1; i < n; ++i) {
+ if (vertices[i][1] < vertices[h][1] || vertices[i][1] == vertices[h][1] && vertices[i][0] < vertices[h][0]) h = i;
+ }
+ for (i = 0; i < n; ++i) {
+ if (i === h) continue;
+ y1 = vertices[i][1] - vertices[h][1];
+ x1 = vertices[i][0] - vertices[h][0];
+ points.push({
+ angle: Math.atan2(y1, x1),
+ index: i
+ });
+ }
+ points.sort(function(a, b) {
+ return a.angle - b.angle;
+ });
+ a = points[0].angle;
+ v = points[0].index;
+ u = 0;
+ for (i = 1; i < plen; ++i) {
+ j = points[i].index;
+ if (a == points[i].angle) {
+ x1 = vertices[v][0] - vertices[h][0];
+ y1 = vertices[v][1] - vertices[h][1];
+ x2 = vertices[j][0] - vertices[h][0];
+ y2 = vertices[j][1] - vertices[h][1];
+ if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) {
+ points[i].index = -1;
+ continue;
+ } else {
+ points[u].index = -1;
+ }
+ }
+ a = points[i].angle;
+ u = i;
+ v = j;
+ }
+ stack.push(h);
+ for (i = 0, j = 0; i < 2; ++j) {
+ if (points[j].index > -1) {
+ stack.push(points[j].index);
+ i++;
+ }
+ }
+ sp = stack.length;
+ for (;j < plen; ++j) {
+ if (points[j].index < 0) continue;
+ while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) {
+ --sp;
+ }
+ stack[sp++] = points[j].index;
+ }
+ var poly = [];
+ for (i = sp - 1; i >= 0; --i) poly.push(data[stack[i]]);
+ return poly;
+ }
+ hull.x = function(_) {
+ return arguments.length ? (x = _, hull) : x;
+ };
+ hull.y = function(_) {
+ return arguments.length ? (y = _, hull) : y;
+ };
+ return hull;
+ };
+ function d3_geom_hullCCW(i1, i2, i3, v) {
+ var t, a, b, c, d, e, f;
+ t = v[i1];
+ a = t[0];
+ b = t[1];
+ t = v[i2];
+ c = t[0];
+ d = t[1];
+ t = v[i3];
+ e = t[0];
+ f = t[1];
+ return (f - b) * (c - a) - (d - b) * (e - a) > 0;
+ }
+ d3.geom.polygon = function(coordinates) {
+ d3_subclass(coordinates, d3_geom_polygonPrototype);
+ return coordinates;
+ };
+ var d3_geom_polygonPrototype = d3.geom.polygon.prototype = [];
+ d3_geom_polygonPrototype.area = function() {
+ var i = -1, n = this.length, a, b = this[n - 1], area = 0;
+ while (++i < n) {
+ a = b;
+ b = this[i];
+ area += a[1] * b[0] - a[0] * b[1];
+ }
+ return area * .5;
+ };
+ d3_geom_polygonPrototype.centroid = function(k) {
+ var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c;
+ if (!arguments.length) k = -1 / (6 * this.area());
+ while (++i < n) {
+ a = b;
+ b = this[i];
+ c = a[0] * b[1] - b[0] * a[1];
+ x += (a[0] + b[0]) * c;
+ y += (a[1] + b[1]) * c;
+ }
+ return [ x * k, y * k ];
+ };
+ d3_geom_polygonPrototype.clip = function(subject) {
+ var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d;
+ while (++i < n) {
+ input = subject.slice();
+ subject.length = 0;
+ b = this[i];
+ c = input[(m = input.length - closed) - 1];
+ j = -1;
+ while (++j < m) {
+ d = input[j];
+ if (d3_geom_polygonInside(d, a, b)) {
+ if (!d3_geom_polygonInside(c, a, b)) {
+ subject.push(d3_geom_polygonIntersect(c, d, a, b));
+ }
+ subject.push(d);
+ } else if (d3_geom_polygonInside(c, a, b)) {
+ subject.push(d3_geom_polygonIntersect(c, d, a, b));
+ }
+ c = d;
+ }
+ if (closed) subject.push(subject[0]);
+ a = b;
+ }
+ return subject;
+ };
+ function d3_geom_polygonInside(p, a, b) {
+ return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
+ }
+ function d3_geom_polygonIntersect(c, d, a, b) {
+ var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
+ return [ x1 + ua * x21, y1 + ua * y21 ];
+ }
+ function d3_geom_polygonClosed(coordinates) {
+ var a = coordinates[0], b = coordinates[coordinates.length - 1];
+ return !(a[0] - b[0] || a[1] - b[1]);
+ }
+ var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = [];
+ function d3_geom_voronoiBeach() {
+ d3_geom_voronoiRedBlackNode(this);
+ this.edge = this.site = this.circle = null;
+ }
+ function d3_geom_voronoiCreateBeach(site) {
+ var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach();
+ beach.site = site;
+ return beach;
+ }
+ function d3_geom_voronoiDetachBeach(beach) {
+ d3_geom_voronoiDetachCircle(beach);
+ d3_geom_voronoiBeaches.remove(beach);
+ d3_geom_voronoiBeachPool.push(beach);
+ d3_geom_voronoiRedBlackNode(beach);
+ }
+ function d3_geom_voronoiRemoveBeach(beach) {
+ var circle = beach.circle, x = circle.x, y = circle.cy, vertex = {
+ x: x,
+ y: y
+ }, previous = beach.P, next = beach.N, disappearing = [ beach ];
+ d3_geom_voronoiDetachBeach(beach);
+ var lArc = previous;
+ while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) {
+ previous = lArc.P;
+ disappearing.unshift(lArc);
+ d3_geom_voronoiDetachBeach(lArc);
+ lArc = previous;
+ }
+ disappearing.unshift(lArc);
+ d3_geom_voronoiDetachCircle(lArc);
+ var rArc = next;
+ while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) {
+ next = rArc.N;
+ disappearing.push(rArc);
+ d3_geom_voronoiDetachBeach(rArc);
+ rArc = next;
+ }
+ disappearing.push(rArc);
+ d3_geom_voronoiDetachCircle(rArc);
+ var nArcs = disappearing.length, iArc;
+ for (iArc = 1; iArc < nArcs; ++iArc) {
+ rArc = disappearing[iArc];
+ lArc = disappearing[iArc - 1];
+ d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex);
+ }
+ lArc = disappearing[0];
+ rArc = disappearing[nArcs - 1];
+ rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ }
+ function d3_geom_voronoiAddBeach(site) {
+ var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._;
+ while (node) {
+ dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x;
+ if (dxl > ε) node = node.L; else {
+ dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix);
+ if (dxr > ε) {
+ if (!node.R) {
+ lArc = node;
+ break;
+ }
+ node = node.R;
+ } else {
+ if (dxl > -ε) {
+ lArc = node.P;
+ rArc = node;
+ } else if (dxr > -ε) {
+ lArc = node;
+ rArc = node.N;
+ } else {
+ lArc = rArc = node;
+ }
+ break;
+ }
+ }
+ }
+ var newArc = d3_geom_voronoiCreateBeach(site);
+ d3_geom_voronoiBeaches.insert(lArc, newArc);
+ if (!lArc && !rArc) return;
+ if (lArc === rArc) {
+ d3_geom_voronoiDetachCircle(lArc);
+ rArc = d3_geom_voronoiCreateBeach(lArc.site);
+ d3_geom_voronoiBeaches.insert(newArc, rArc);
+ newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ return;
+ }
+ if (!rArc) {
+ newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site);
+ return;
+ }
+ d3_geom_voronoiDetachCircle(lArc);
+ d3_geom_voronoiDetachCircle(rArc);
+ var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = {
+ x: (cy * hb - by * hc) / d + ax,
+ y: (bx * hc - cx * hb) / d + ay
+ };
+ d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex);
+ newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex);
+ rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex);
+ d3_geom_voronoiAttachCircle(lArc);
+ d3_geom_voronoiAttachCircle(rArc);
+ }
+ function d3_geom_voronoiLeftBreakPoint(arc, directrix) {
+ var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix;
+ if (!pby2) return rfocx;
+ var lArc = arc.P;
+ if (!lArc) return -Infinity;
+ site = lArc.site;
+ var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix;
+ if (!plby2) return lfocx;
+ var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2;
+ if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx;
+ return (rfocx + lfocx) / 2;
+ }
+ function d3_geom_voronoiRightBreakPoint(arc, directrix) {
+ var rArc = arc.N;
+ if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix);
+ var site = arc.site;
+ return site.y === directrix ? site.x : Infinity;
+ }
+ function d3_geom_voronoiCell(site) {
+ this.site = site;
+ this.edges = [];
+ }
+ d3_geom_voronoiCell.prototype.prepare = function() {
+ var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge;
+ while (iHalfEdge--) {
+ edge = halfEdges[iHalfEdge].edge;
+ if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1);
+ }
+ halfEdges.sort(d3_geom_voronoiHalfEdgeOrder);
+ return halfEdges.length;
+ };
+ function d3_geom_voronoiCloseCells(extent) {
+ var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end;
+ while (iCell--) {
+ cell = cells[iCell];
+ if (!cell || !cell.prepare()) continue;
+ halfEdges = cell.edges;
+ nHalfEdges = halfEdges.length;
+ iHalfEdge = 0;
+ while (iHalfEdge < nHalfEdges) {
+ end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y;
+ start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y;
+ if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) {
+ halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? {
+ x: x0,
+ y: abs(x2 - x0) < ε ? y2 : y1
+ } : abs(y3 - y1) < ε && x1 - x3 > ε ? {
+ x: abs(y2 - y1) < ε ? x2 : x1,
+ y: y1
+ } : abs(x3 - x1) < ε && y3 - y0 > ε ? {
+ x: x1,
+ y: abs(x2 - x1) < ε ? y2 : y0
+ } : abs(y3 - y0) < ε && x3 - x0 > ε ? {
+ x: abs(y2 - y0) < ε ? x2 : x0,
+ y: y0
+ } : null), cell.site, null));
+ ++nHalfEdges;
+ }
+ }
+ }
+ }
+ function d3_geom_voronoiHalfEdgeOrder(a, b) {
+ return b.angle - a.angle;
+ }
+ function d3_geom_voronoiCircle() {
+ d3_geom_voronoiRedBlackNode(this);
+ this.x = this.y = this.arc = this.site = this.cy = null;
+ }
+ function d3_geom_voronoiAttachCircle(arc) {
+ var lArc = arc.P, rArc = arc.N;
+ if (!lArc || !rArc) return;
+ var lSite = lArc.site, cSite = arc.site, rSite = rArc.site;
+ if (lSite === rSite) return;
+ var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by;
+ var d = 2 * (ax * cy - ay * cx);
+ if (d >= -ε2) return;
+ var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by;
+ var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle();
+ circle.arc = arc;
+ circle.site = cSite;
+ circle.x = x + bx;
+ circle.y = cy + Math.sqrt(x * x + y * y);
+ circle.cy = cy;
+ arc.circle = circle;
+ var before = null, node = d3_geom_voronoiCircles._;
+ while (node) {
+ if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) {
+ if (node.L) node = node.L; else {
+ before = node.P;
+ break;
+ }
+ } else {
+ if (node.R) node = node.R; else {
+ before = node;
+ break;
+ }
+ }
+ }
+ d3_geom_voronoiCircles.insert(before, circle);
+ if (!before) d3_geom_voronoiFirstCircle = circle;
+ }
+ function d3_geom_voronoiDetachCircle(arc) {
+ var circle = arc.circle;
+ if (circle) {
+ if (!circle.P) d3_geom_voronoiFirstCircle = circle.N;
+ d3_geom_voronoiCircles.remove(circle);
+ d3_geom_voronoiCirclePool.push(circle);
+ d3_geom_voronoiRedBlackNode(circle);
+ arc.circle = null;
+ }
+ }
+ function d3_geom_voronoiClipEdges(extent) {
+ var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e;
+ while (i--) {
+ e = edges[i];
+ if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) {
+ e.a = e.b = null;
+ edges.splice(i, 1);
+ }
+ }
+ }
+ function d3_geom_voronoiConnectEdge(edge, extent) {
+ var vb = edge.b;
+ if (vb) return true;
+ var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb;
+ if (ry === ly) {
+ if (fx < x0 || fx >= x1) return;
+ if (lx > rx) {
+ if (!va) va = {
+ x: fx,
+ y: y0
+ }; else if (va.y >= y1) return;
+ vb = {
+ x: fx,
+ y: y1
+ };
+ } else {
+ if (!va) va = {
+ x: fx,
+ y: y1
+ }; else if (va.y < y0) return;
+ vb = {
+ x: fx,
+ y: y0
+ };
+ }
+ } else {
+ fm = (lx - rx) / (ry - ly);
+ fb = fy - fm * fx;
+ if (fm < -1 || fm > 1) {
+ if (lx > rx) {
+ if (!va) va = {
+ x: (y0 - fb) / fm,
+ y: y0
+ }; else if (va.y >= y1) return;
+ vb = {
+ x: (y1 - fb) / fm,
+ y: y1
+ };
+ } else {
+ if (!va) va = {
+ x: (y1 - fb) / fm,
+ y: y1
+ }; else if (va.y < y0) return;
+ vb = {
+ x: (y0 - fb) / fm,
+ y: y0
+ };
+ }
+ } else {
+ if (ly < ry) {
+ if (!va) va = {
+ x: x0,
+ y: fm * x0 + fb
+ }; else if (va.x >= x1) return;
+ vb = {
+ x: x1,
+ y: fm * x1 + fb
+ };
+ } else {
+ if (!va) va = {
+ x: x1,
+ y: fm * x1 + fb
+ }; else if (va.x < x0) return;
+ vb = {
+ x: x0,
+ y: fm * x0 + fb
+ };
+ }
+ }
+ }
+ edge.a = va;
+ edge.b = vb;
+ return true;
+ }
+ function d3_geom_voronoiEdge(lSite, rSite) {
+ this.l = lSite;
+ this.r = rSite;
+ this.a = this.b = null;
+ }
+ function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) {
+ var edge = new d3_geom_voronoiEdge(lSite, rSite);
+ d3_geom_voronoiEdges.push(edge);
+ if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va);
+ if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb);
+ d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite));
+ d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite));
+ return edge;
+ }
+ function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) {
+ var edge = new d3_geom_voronoiEdge(lSite, null);
+ edge.a = va;
+ edge.b = vb;
+ d3_geom_voronoiEdges.push(edge);
+ return edge;
+ }
+ function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) {
+ if (!edge.a && !edge.b) {
+ edge.a = vertex;
+ edge.l = lSite;
+ edge.r = rSite;
+ } else if (edge.l === rSite) {
+ edge.b = vertex;
+ } else {
+ edge.a = vertex;
+ }
+ }
+ function d3_geom_voronoiHalfEdge(edge, lSite, rSite) {
+ var va = edge.a, vb = edge.b;
+ this.edge = edge;
+ this.site = lSite;
+ this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y);
+ }
+ d3_geom_voronoiHalfEdge.prototype = {
+ start: function() {
+ return this.edge.l === this.site ? this.edge.a : this.edge.b;
+ },
+ end: function() {
+ return this.edge.l === this.site ? this.edge.b : this.edge.a;
+ }
+ };
+ function d3_geom_voronoiRedBlackTree() {
+ this._ = null;
+ }
+ function d3_geom_voronoiRedBlackNode(node) {
+ node.U = node.C = node.L = node.R = node.P = node.N = null;
+ }
+ d3_geom_voronoiRedBlackTree.prototype = {
+ insert: function(after, node) {
+ var parent, grandpa, uncle;
+ if (after) {
+ node.P = after;
+ node.N = after.N;
+ if (after.N) after.N.P = node;
+ after.N = node;
+ if (after.R) {
+ after = after.R;
+ while (after.L) after = after.L;
+ after.L = node;
+ } else {
+ after.R = node;
+ }
+ parent = after;
+ } else if (this._) {
+ after = d3_geom_voronoiRedBlackFirst(this._);
+ node.P = null;
+ node.N = after;
+ after.P = after.L = node;
+ parent = after;
+ } else {
+ node.P = node.N = null;
+ this._ = node;
+ parent = null;
+ }
+ node.L = node.R = null;
+ node.U = parent;
+ node.C = true;
+ after = node;
+ while (parent && parent.C) {
+ grandpa = parent.U;
+ if (parent === grandpa.L) {
+ uncle = grandpa.R;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.R) {
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, grandpa);
+ }
+ } else {
+ uncle = grandpa.L;
+ if (uncle && uncle.C) {
+ parent.C = uncle.C = false;
+ grandpa.C = true;
+ after = grandpa;
+ } else {
+ if (after === parent.L) {
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ after = parent;
+ parent = after.U;
+ }
+ parent.C = false;
+ grandpa.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, grandpa);
+ }
+ }
+ parent = after.U;
+ }
+ this._.C = false;
+ },
+ remove: function(node) {
+ if (node.N) node.N.P = node.P;
+ if (node.P) node.P.N = node.N;
+ node.N = node.P = null;
+ var parent = node.U, sibling, left = node.L, right = node.R, next, red;
+ if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right);
+ if (parent) {
+ if (parent.L === node) parent.L = next; else parent.R = next;
+ } else {
+ this._ = next;
+ }
+ if (left && right) {
+ red = next.C;
+ next.C = node.C;
+ next.L = left;
+ left.U = next;
+ if (next !== right) {
+ parent = next.U;
+ next.U = node.U;
+ node = next.R;
+ parent.L = node;
+ next.R = right;
+ right.U = next;
+ } else {
+ next.U = parent;
+ parent = next;
+ node = next.R;
+ }
+ } else {
+ red = node.C;
+ node = next;
+ }
+ if (node) node.U = parent;
+ if (red) return;
+ if (node && node.C) {
+ node.C = false;
+ return;
+ }
+ do {
+ if (node === this._) break;
+ if (node === parent.L) {
+ sibling = parent.R;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ sibling = parent.R;
+ }
+ if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+ if (!sibling.R || !sibling.R.C) {
+ sibling.L.C = false;
+ sibling.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, sibling);
+ sibling = parent.R;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.R.C = false;
+ d3_geom_voronoiRedBlackRotateLeft(this, parent);
+ node = this._;
+ break;
+ }
+ } else {
+ sibling = parent.L;
+ if (sibling.C) {
+ sibling.C = false;
+ parent.C = true;
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ sibling = parent.L;
+ }
+ if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) {
+ if (!sibling.L || !sibling.L.C) {
+ sibling.R.C = false;
+ sibling.C = true;
+ d3_geom_voronoiRedBlackRotateLeft(this, sibling);
+ sibling = parent.L;
+ }
+ sibling.C = parent.C;
+ parent.C = sibling.L.C = false;
+ d3_geom_voronoiRedBlackRotateRight(this, parent);
+ node = this._;
+ break;
+ }
+ }
+ sibling.C = true;
+ node = parent;
+ parent = parent.U;
+ } while (!node.C);
+ if (node) node.C = false;
+ }
+ };
+ function d3_geom_voronoiRedBlackRotateLeft(tree, node) {
+ var p = node, q = node.R, parent = p.U;
+ if (parent) {
+ if (parent.L === p) parent.L = q; else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+ q.U = parent;
+ p.U = q;
+ p.R = q.L;
+ if (p.R) p.R.U = p;
+ q.L = p;
+ }
+ function d3_geom_voronoiRedBlackRotateRight(tree, node) {
+ var p = node, q = node.L, parent = p.U;
+ if (parent) {
+ if (parent.L === p) parent.L = q; else parent.R = q;
+ } else {
+ tree._ = q;
+ }
+ q.U = parent;
+ p.U = q;
+ p.L = q.R;
+ if (p.L) p.L.U = p;
+ q.R = p;
+ }
+ function d3_geom_voronoiRedBlackFirst(node) {
+ while (node.L) node = node.L;
+ return node;
+ }
+ function d3_geom_voronoi(sites, bbox) {
+ var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle;
+ d3_geom_voronoiEdges = [];
+ d3_geom_voronoiCells = new Array(sites.length);
+ d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree();
+ d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree();
+ while (true) {
+ circle = d3_geom_voronoiFirstCircle;
+ if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) {
+ if (site.x !== x0 || site.y !== y0) {
+ d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site);
+ d3_geom_voronoiAddBeach(site);
+ x0 = site.x, y0 = site.y;
+ }
+ site = sites.pop();
+ } else if (circle) {
+ d3_geom_voronoiRemoveBeach(circle.arc);
+ } else {
+ break;
+ }
+ }
+ if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox);
+ var diagram = {
+ cells: d3_geom_voronoiCells,
+ edges: d3_geom_voronoiEdges
+ };
+ d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null;
+ return diagram;
+ }
+ function d3_geom_voronoiVertexOrder(a, b) {
+ return b.y - a.y || b.x - a.x;
+ }
+ d3.geom.voronoi = function(points) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent;
+ if (points) return voronoi(points);
+ function voronoi(data) {
+ var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1];
+ d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) {
+ var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) {
+ var s = e.start();
+ return [ s.x, s.y ];
+ }) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : [];
+ polygon.point = data[i];
+ });
+ return polygons;
+ }
+ function sites(data) {
+ return data.map(function(d, i) {
+ return {
+ x: Math.round(fx(d, i) / ε) * ε,
+ y: Math.round(fy(d, i) / ε) * ε,
+ i: i
+ };
+ });
+ }
+ voronoi.links = function(data) {
+ return d3_geom_voronoi(sites(data)).edges.filter(function(edge) {
+ return edge.l && edge.r;
+ }).map(function(edge) {
+ return {
+ source: data[edge.l.i],
+ target: data[edge.r.i]
+ };
+ });
+ };
+ voronoi.triangles = function(data) {
+ var triangles = [];
+ d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) {
+ var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l;
+ while (++j < m) {
+ e0 = e1;
+ s0 = s1;
+ e1 = edges[j].edge;
+ s1 = e1.l === site ? e1.r : e1.l;
+ if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) {
+ triangles.push([ data[i], data[s0.i], data[s1.i] ]);
+ }
+ }
+ });
+ return triangles;
+ };
+ voronoi.x = function(_) {
+ return arguments.length ? (fx = d3_functor(x = _), voronoi) : x;
+ };
+ voronoi.y = function(_) {
+ return arguments.length ? (fy = d3_functor(y = _), voronoi) : y;
+ };
+ voronoi.clipExtent = function(_) {
+ if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent;
+ clipExtent = _ == null ? d3_geom_voronoiClipExtent : _;
+ return voronoi;
+ };
+ voronoi.size = function(_) {
+ if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1];
+ return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
+ };
+ return voronoi;
+ };
+ var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ];
+ function d3_geom_voronoiTriangleArea(a, b, c) {
+ return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y);
+ }
+ d3.geom.delaunay = function(vertices) {
+ return d3.geom.voronoi().triangles(vertices);
+ };
+ d3.geom.quadtree = function(points, x1, y1, x2, y2) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, compat;
+ if (compat = arguments.length) {
+ x = d3_geom_quadtreeCompatX;
+ y = d3_geom_quadtreeCompatY;
+ if (compat === 3) {
+ y2 = y1;
+ x2 = x1;
+ y1 = x1 = 0;
+ }
+ return quadtree(points);
+ }
+ function quadtree(data) {
+ var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
+ if (x1 != null) {
+ x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
+ } else {
+ x2_ = y2_ = -(x1_ = y1_ = Infinity);
+ xs = [], ys = [];
+ n = data.length;
+ if (compat) for (i = 0; i < n; ++i) {
+ d = data[i];
+ if (d.x < x1_) x1_ = d.x;
+ if (d.y < y1_) y1_ = d.y;
+ if (d.x > x2_) x2_ = d.x;
+ if (d.y > y2_) y2_ = d.y;
+ xs.push(d.x);
+ ys.push(d.y);
+ } else for (i = 0; i < n; ++i) {
+ var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
+ if (x_ < x1_) x1_ = x_;
+ if (y_ < y1_) y1_ = y_;
+ if (x_ > x2_) x2_ = x_;
+ if (y_ > y2_) y2_ = y_;
+ xs.push(x_);
+ ys.push(y_);
+ }
+ }
+ var dx = x2_ - x1_, dy = y2_ - y1_;
+ if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
+ function insert(n, d, x, y, x1, y1, x2, y2) {
+ if (isNaN(x) || isNaN(y)) return;
+ if (n.leaf) {
+ var nx = n.x, ny = n.y;
+ if (nx != null) {
+ if (abs(nx - x) + abs(ny - y) < .01) {
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ } else {
+ var nPoint = n.point;
+ n.x = n.y = n.point = null;
+ insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ }
+ } else {
+ n.x = x, n.y = y, n.point = d;
+ }
+ } else {
+ insertChild(n, d, x, y, x1, y1, x2, y2);
+ }
+ }
+ function insertChild(n, d, x, y, x1, y1, x2, y2) {
+ var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right;
+ n.leaf = false;
+ n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
+ if (right) x1 = sx; else x2 = sx;
+ if (bottom) y1 = sy; else y2 = sy;
+ insert(n, d, x, y, x1, y1, x2, y2);
+ }
+ var root = d3_geom_quadtreeNode();
+ root.add = function(d) {
+ insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
+ };
+ root.visit = function(f) {
+ d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
+ };
+ i = -1;
+ if (x1 == null) {
+ while (++i < n) {
+ insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
+ }
+ --i;
+ } else data.forEach(root.add);
+ xs = ys = data = d = null;
+ return root;
+ }
+ quadtree.x = function(_) {
+ return arguments.length ? (x = _, quadtree) : x;
+ };
+ quadtree.y = function(_) {
+ return arguments.length ? (y = _, quadtree) : y;
+ };
+ quadtree.extent = function(_) {
+ if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
+ if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
+ y2 = +_[1][1];
+ return quadtree;
+ };
+ quadtree.size = function(_) {
+ if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
+ if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
+ return quadtree;
+ };
+ return quadtree;
+ };
+ function d3_geom_quadtreeCompatX(d) {
+ return d.x;
+ }
+ function d3_geom_quadtreeCompatY(d) {
+ return d.y;
+ }
+ function d3_geom_quadtreeNode() {
+ return {
+ leaf: true,
+ nodes: [],
+ point: null,
+ x: null,
+ y: null
+ };
+ }
+ function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
+ if (!f(node, x1, y1, x2, y2)) {
+ var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
+ if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
+ if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
+ if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
+ if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
+ }
+ }
+ d3.interpolateRgb = d3_interpolateRgb;
+ function d3_interpolateRgb(a, b) {
+ a = d3.rgb(a);
+ b = d3.rgb(b);
+ var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
+ return function(t) {
+ return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
+ };
+ }
+ d3.interpolateObject = d3_interpolateObject;
+ function d3_interpolateObject(a, b) {
+ var i = {}, c = {}, k;
+ for (k in a) {
+ if (k in b) {
+ i[k] = d3_interpolate(a[k], b[k]);
+ } else {
+ c[k] = a[k];
+ }
+ }
+ for (k in b) {
+ if (!(k in a)) {
+ c[k] = b[k];
+ }
+ }
+ return function(t) {
+ for (k in i) c[k] = i[k](t);
+ return c;
+ };
+ }
+ d3.interpolateNumber = d3_interpolateNumber;
+ function d3_interpolateNumber(a, b) {
+ b -= a = +a;
+ return function(t) {
+ return a + b * t;
+ };
+ }
+ d3.interpolateString = d3_interpolateString;
+ function d3_interpolateString(a, b) {
+ var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o;
+ a = a + "", b = b + "";
+ d3_interpolate_number.lastIndex = 0;
+ for (i = 0; m = d3_interpolate_number.exec(b); ++i) {
+ if (m.index) s.push(b.substring(s0, s1 = m.index));
+ q.push({
+ i: s.length,
+ x: m[0]
+ });
+ s.push(null);
+ s0 = d3_interpolate_number.lastIndex;
+ }
+ if (s0 < b.length) s.push(b.substring(s0));
+ for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) {
+ o = q[i];
+ if (o.x == m[0]) {
+ if (o.i) {
+ if (s[o.i + 1] == null) {
+ s[o.i - 1] += o.x;
+ s.splice(o.i, 1);
+ for (j = i + 1; j < n; ++j) q[j].i--;
+ } else {
+ s[o.i - 1] += o.x + s[o.i + 1];
+ s.splice(o.i, 2);
+ for (j = i + 1; j < n; ++j) q[j].i -= 2;
+ }
+ } else {
+ if (s[o.i + 1] == null) {
+ s[o.i] = o.x;
+ } else {
+ s[o.i] = o.x + s[o.i + 1];
+ s.splice(o.i + 1, 1);
+ for (j = i + 1; j < n; ++j) q[j].i--;
+ }
+ }
+ q.splice(i, 1);
+ n--;
+ i--;
+ } else {
+ o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x));
+ }
+ }
+ while (i < n) {
+ o = q.pop();
+ if (s[o.i + 1] == null) {
+ s[o.i] = o.x;
+ } else {
+ s[o.i] = o.x + s[o.i + 1];
+ s.splice(o.i + 1, 1);
+ }
+ n--;
+ }
+ if (s.length === 1) {
+ return s[0] == null ? (o = q[0].x, function(t) {
+ return o(t) + "";
+ }) : function() {
+ return b;
+ };
+ }
+ return function(t) {
+ for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t);
+ return s.join("");
+ };
+ }
+ var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
+ d3.interpolate = d3_interpolate;
+ function d3_interpolate(a, b) {
+ var i = d3.interpolators.length, f;
+ while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
+ return f;
+ }
+ d3.interpolators = [ function(a, b) {
+ var t = typeof b;
+ return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : t === "object" ? Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject : d3_interpolateNumber)(a, b);
+ } ];
+ d3.interpolateArray = d3_interpolateArray;
+ function d3_interpolateArray(a, b) {
+ var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
+ for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
+ for (;i < na; ++i) c[i] = a[i];
+ for (;i < nb; ++i) c[i] = b[i];
+ return function(t) {
+ for (i = 0; i < n0; ++i) c[i] = x[i](t);
+ return c;
+ };
+ }
+ var d3_ease_default = function() {
+ return d3_identity;
+ };
+ var d3_ease = d3.map({
+ linear: d3_ease_default,
+ poly: d3_ease_poly,
+ quad: function() {
+ return d3_ease_quad;
+ },
+ cubic: function() {
+ return d3_ease_cubic;
+ },
+ sin: function() {
+ return d3_ease_sin;
+ },
+ exp: function() {
+ return d3_ease_exp;
+ },
+ circle: function() {
+ return d3_ease_circle;
+ },
+ elastic: d3_ease_elastic,
+ back: d3_ease_back,
+ bounce: function() {
+ return d3_ease_bounce;
+ }
+ });
+ var d3_ease_mode = d3.map({
+ "in": d3_identity,
+ out: d3_ease_reverse,
+ "in-out": d3_ease_reflect,
+ "out-in": function(f) {
+ return d3_ease_reflect(d3_ease_reverse(f));
+ }
+ });
+ d3.ease = function(name) {
+ var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in";
+ t = d3_ease.get(t) || d3_ease_default;
+ m = d3_ease_mode.get(m) || d3_identity;
+ return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1))));
+ };
+ function d3_ease_clamp(f) {
+ return function(t) {
+ return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
+ };
+ }
+ function d3_ease_reverse(f) {
+ return function(t) {
+ return 1 - f(1 - t);
+ };
+ }
+ function d3_ease_reflect(f) {
+ return function(t) {
+ return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
+ };
+ }
+ function d3_ease_quad(t) {
+ return t * t;
+ }
+ function d3_ease_cubic(t) {
+ return t * t * t;
+ }
+ function d3_ease_cubicInOut(t) {
+ if (t <= 0) return 0;
+ if (t >= 1) return 1;
+ var t2 = t * t, t3 = t2 * t;
+ return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
+ }
+ function d3_ease_poly(e) {
+ return function(t) {
+ return Math.pow(t, e);
+ };
+ }
+ function d3_ease_sin(t) {
+ return 1 - Math.cos(t * halfÏ);
+ }
+ function d3_ease_exp(t) {
+ return Math.pow(2, 10 * (t - 1));
+ }
+ function d3_ease_circle(t) {
+ return 1 - Math.sqrt(1 - t * t);
+ }
+ function d3_ease_elastic(a, p) {
+ var s;
+ if (arguments.length < 2) p = .45;
+ if (arguments.length) s = p / Ï * Math.asin(1 / a); else a = 1, s = p / 4;
+ return function(t) {
+ return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * Ï / p);
+ };
+ }
+ function d3_ease_back(s) {
+ if (!s) s = 1.70158;
+ return function(t) {
+ return t * t * ((s + 1) * t - s);
+ };
+ }
+ function d3_ease_bounce(t) {
+ return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
+ }
+ d3.interpolateHcl = d3_interpolateHcl;
+ function d3_interpolateHcl(a, b) {
+ a = d3.hcl(a);
+ b = d3.hcl(b);
+ var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
+ if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
+ if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+ return function(t) {
+ return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
+ };
+ }
+ d3.interpolateHsl = d3_interpolateHsl;
+ function d3_interpolateHsl(a, b) {
+ a = d3.hsl(a);
+ b = d3.hsl(b);
+ var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
+ if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
+ if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
+ return function(t) {
+ return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
+ };
+ }
+ d3.interpolateLab = d3_interpolateLab;
+ function d3_interpolateLab(a, b) {
+ a = d3.lab(a);
+ b = d3.lab(b);
+ var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
+ return function(t) {
+ return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
+ };
+ }
+ d3.interpolateRound = d3_interpolateRound;
+ function d3_interpolateRound(a, b) {
+ b -= a;
+ return function(t) {
+ return Math.round(a + b * t);
+ };
+ }
+ d3.transform = function(string) {
+ var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
+ return (d3.transform = function(string) {
+ if (string != null) {
+ g.setAttribute("transform", string);
+ var t = g.transform.baseVal.consolidate();
+ }
+ return new d3_transform(t ? t.matrix : d3_transformIdentity);
+ })(string);
+ };
+ function d3_transform(m) {
+ var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
+ if (r0[0] * r1[1] < r1[0] * r0[1]) {
+ r0[0] *= -1;
+ r0[1] *= -1;
+ kx *= -1;
+ kz *= -1;
+ }
+ this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
+ this.translate = [ m.e, m.f ];
+ this.scale = [ kx, ky ];
+ this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
+ }
+ d3_transform.prototype.toString = function() {
+ return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
+ };
+ function d3_transformDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+ }
+ function d3_transformNormalize(a) {
+ var k = Math.sqrt(d3_transformDot(a, a));
+ if (k) {
+ a[0] /= k;
+ a[1] /= k;
+ }
+ return k;
+ }
+ function d3_transformCombine(a, b, k) {
+ a[0] += k * b[0];
+ a[1] += k * b[1];
+ return a;
+ }
+ var d3_transformIdentity = {
+ a: 1,
+ b: 0,
+ c: 0,
+ d: 1,
+ e: 0,
+ f: 0
+ };
+ d3.interpolateTransform = d3_interpolateTransform;
+ function d3_interpolateTransform(a, b) {
+ var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
+ if (ta[0] != tb[0] || ta[1] != tb[1]) {
+ s.push("translate(", null, ",", null, ")");
+ q.push({
+ i: 1,
+ x: d3_interpolateNumber(ta[0], tb[0])
+ }, {
+ i: 3,
+ x: d3_interpolateNumber(ta[1], tb[1])
+ });
+ } else if (tb[0] || tb[1]) {
+ s.push("translate(" + tb + ")");
+ } else {
+ s.push("");
+ }
+ if (ra != rb) {
+ if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
+ q.push({
+ i: s.push(s.pop() + "rotate(", null, ")") - 2,
+ x: d3_interpolateNumber(ra, rb)
+ });
+ } else if (rb) {
+ s.push(s.pop() + "rotate(" + rb + ")");
+ }
+ if (wa != wb) {
+ q.push({
+ i: s.push(s.pop() + "skewX(", null, ")") - 2,
+ x: d3_interpolateNumber(wa, wb)
+ });
+ } else if (wb) {
+ s.push(s.pop() + "skewX(" + wb + ")");
+ }
+ if (ka[0] != kb[0] || ka[1] != kb[1]) {
+ n = s.push(s.pop() + "scale(", null, ",", null, ")");
+ q.push({
+ i: n - 4,
+ x: d3_interpolateNumber(ka[0], kb[0])
+ }, {
+ i: n - 2,
+ x: d3_interpolateNumber(ka[1], kb[1])
+ });
+ } else if (kb[0] != 1 || kb[1] != 1) {
+ s.push(s.pop() + "scale(" + kb + ")");
+ }
+ n = q.length;
+ return function(t) {
+ var i = -1, o;
+ while (++i < n) s[(o = q[i]).i] = o.x(t);
+ return s.join("");
+ };
+ }
+ function d3_uninterpolateNumber(a, b) {
+ b = b - (a = +a) ? 1 / (b - a) : 0;
+ return function(x) {
+ return (x - a) * b;
+ };
+ }
+ function d3_uninterpolateClamp(a, b) {
+ b = b - (a = +a) ? 1 / (b - a) : 0;
+ return function(x) {
+ return Math.max(0, Math.min(1, (x - a) * b));
+ };
+ }
+ d3.layout = {};
+ d3.layout.bundle = function() {
+ return function(links) {
+ var paths = [], i = -1, n = links.length;
+ while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
+ return paths;
+ };
+ };
+ function d3_layout_bundlePath(link) {
+ var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
+ while (start !== lca) {
+ start = start.parent;
+ points.push(start);
+ }
+ var k = points.length;
+ while (end !== lca) {
+ points.splice(k, 0, end);
+ end = end.parent;
+ }
+ return points;
+ }
+ function d3_layout_bundleAncestors(node) {
+ var ancestors = [], parent = node.parent;
+ while (parent != null) {
+ ancestors.push(node);
+ node = parent;
+ parent = parent.parent;
+ }
+ ancestors.push(node);
+ return ancestors;
+ }
+ function d3_layout_bundleLeastCommonAncestor(a, b) {
+ if (a === b) return a;
+ var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
+ while (aNode === bNode) {
+ sharedNode = aNode;
+ aNode = aNodes.pop();
+ bNode = bNodes.pop();
+ }
+ return sharedNode;
+ }
+ d3.layout.chord = function() {
+ var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
+ function relayout() {
+ var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
+ chords = [];
+ groups = [];
+ k = 0, i = -1;
+ while (++i < n) {
+ x = 0, j = -1;
+ while (++j < n) {
+ x += matrix[i][j];
+ }
+ groupSums.push(x);
+ subgroupIndex.push(d3.range(n));
+ k += x;
+ }
+ if (sortGroups) {
+ groupIndex.sort(function(a, b) {
+ return sortGroups(groupSums[a], groupSums[b]);
+ });
+ }
+ if (sortSubgroups) {
+ subgroupIndex.forEach(function(d, i) {
+ d.sort(function(a, b) {
+ return sortSubgroups(matrix[i][a], matrix[i][b]);
+ });
+ });
+ }
+ k = (Ï - padding * n) / k;
+ x = 0, i = -1;
+ while (++i < n) {
+ x0 = x, j = -1;
+ while (++j < n) {
+ var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
+ subgroups[di + "-" + dj] = {
+ index: di,
+ subindex: dj,
+ startAngle: a0,
+ endAngle: a1,
+ value: v
+ };
+ }
+ groups[di] = {
+ index: di,
+ startAngle: x0,
+ endAngle: x,
+ value: (x - x0) / k
+ };
+ x += padding;
+ }
+ i = -1;
+ while (++i < n) {
+ j = i - 1;
+ while (++j < n) {
+ var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
+ if (source.value || target.value) {
+ chords.push(source.value < target.value ? {
+ source: target,
+ target: source
+ } : {
+ source: source,
+ target: target
+ });
+ }
+ }
+ }
+ if (sortChords) resort();
+ }
+ function resort() {
+ chords.sort(function(a, b) {
+ return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
+ });
+ }
+ chord.matrix = function(x) {
+ if (!arguments.length) return matrix;
+ n = (matrix = x) && matrix.length;
+ chords = groups = null;
+ return chord;
+ };
+ chord.padding = function(x) {
+ if (!arguments.length) return padding;
+ padding = x;
+ chords = groups = null;
+ return chord;
+ };
+ chord.sortGroups = function(x) {
+ if (!arguments.length) return sortGroups;
+ sortGroups = x;
+ chords = groups = null;
+ return chord;
+ };
+ chord.sortSubgroups = function(x) {
+ if (!arguments.length) return sortSubgroups;
+ sortSubgroups = x;
+ chords = null;
+ return chord;
+ };
+ chord.sortChords = function(x) {
+ if (!arguments.length) return sortChords;
+ sortChords = x;
+ if (chords) resort();
+ return chord;
+ };
+ chord.chords = function() {
+ if (!chords) relayout();
+ return chords;
+ };
+ chord.groups = function() {
+ if (!groups) relayout();
+ return groups;
+ };
+ return chord;
+ };
+ d3.layout.force = function() {
+ var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, gravity = .1, theta = .8, nodes = [], links = [], distances, strengths, charges;
+ function repulse(node) {
+ return function(quad, x1, _, x2) {
+ if (quad.point !== node) {
+ var dx = quad.cx - node.x, dy = quad.cy - node.y, dn = 1 / Math.sqrt(dx * dx + dy * dy);
+ if ((x2 - x1) * dn < theta) {
+ var k = quad.charge * dn * dn;
+ node.px -= dx * k;
+ node.py -= dy * k;
+ return true;
+ }
+ if (quad.point && isFinite(dn)) {
+ var k = quad.pointCharge * dn * dn;
+ node.px -= dx * k;
+ node.py -= dy * k;
+ }
+ }
+ return !quad.charge;
+ };
+ }
+ force.tick = function() {
+ if ((alpha *= .99) < .005) {
+ event.end({
+ type: "end",
+ alpha: alpha = 0
+ });
+ return true;
+ }
+ var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
+ for (i = 0; i < m; ++i) {
+ o = links[i];
+ s = o.source;
+ t = o.target;
+ x = t.x - s.x;
+ y = t.y - s.y;
+ if (l = x * x + y * y) {
+ l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
+ x *= l;
+ y *= l;
+ t.x -= x * (k = s.weight / (t.weight + s.weight));
+ t.y -= y * k;
+ s.x += x * (k = 1 - k);
+ s.y += y * k;
+ }
+ }
+ if (k = alpha * gravity) {
+ x = size[0] / 2;
+ y = size[1] / 2;
+ i = -1;
+ if (k) while (++i < n) {
+ o = nodes[i];
+ o.x += (x - o.x) * k;
+ o.y += (y - o.y) * k;
+ }
+ }
+ if (charge) {
+ d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
+ i = -1;
+ while (++i < n) {
+ if (!(o = nodes[i]).fixed) {
+ q.visit(repulse(o));
+ }
+ }
+ }
+ i = -1;
+ while (++i < n) {
+ o = nodes[i];
+ if (o.fixed) {
+ o.x = o.px;
+ o.y = o.py;
+ } else {
+ o.x -= (o.px - (o.px = o.x)) * friction;
+ o.y -= (o.py - (o.py = o.y)) * friction;
+ }
+ }
+ event.tick({
+ type: "tick",
+ alpha: alpha
+ });
+ };
+ force.nodes = function(x) {
+ if (!arguments.length) return nodes;
+ nodes = x;
+ return force;
+ };
+ force.links = function(x) {
+ if (!arguments.length) return links;
+ links = x;
+ return force;
+ };
+ force.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return force;
+ };
+ force.linkDistance = function(x) {
+ if (!arguments.length) return linkDistance;
+ linkDistance = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.distance = force.linkDistance;
+ force.linkStrength = function(x) {
+ if (!arguments.length) return linkStrength;
+ linkStrength = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.friction = function(x) {
+ if (!arguments.length) return friction;
+ friction = +x;
+ return force;
+ };
+ force.charge = function(x) {
+ if (!arguments.length) return charge;
+ charge = typeof x === "function" ? x : +x;
+ return force;
+ };
+ force.gravity = function(x) {
+ if (!arguments.length) return gravity;
+ gravity = +x;
+ return force;
+ };
+ force.theta = function(x) {
+ if (!arguments.length) return theta;
+ theta = +x;
+ return force;
+ };
+ force.alpha = function(x) {
+ if (!arguments.length) return alpha;
+ x = +x;
+ if (alpha) {
+ if (x > 0) alpha = x; else alpha = 0;
+ } else if (x > 0) {
+ event.start({
+ type: "start",
+ alpha: alpha = x
+ });
+ d3.timer(force.tick);
+ }
+ return force;
+ };
+ force.start = function() {
+ var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
+ for (i = 0; i < n; ++i) {
+ (o = nodes[i]).index = i;
+ o.weight = 0;
+ }
+ for (i = 0; i < m; ++i) {
+ o = links[i];
+ if (typeof o.source == "number") o.source = nodes[o.source];
+ if (typeof o.target == "number") o.target = nodes[o.target];
+ ++o.source.weight;
+ ++o.target.weight;
+ }
+ for (i = 0; i < n; ++i) {
+ o = nodes[i];
+ if (isNaN(o.x)) o.x = position("x", w);
+ if (isNaN(o.y)) o.y = position("y", h);
+ if (isNaN(o.px)) o.px = o.x;
+ if (isNaN(o.py)) o.py = o.y;
+ }
+ distances = [];
+ if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
+ strengths = [];
+ if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
+ charges = [];
+ if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
+ function position(dimension, size) {
+ if (!neighbors) {
+ neighbors = new Array(n);
+ for (j = 0; j < n; ++j) {
+ neighbors[j] = [];
+ }
+ for (j = 0; j < m; ++j) {
+ var o = links[j];
+ neighbors[o.source.index].push(o.target);
+ neighbors[o.target.index].push(o.source);
+ }
+ }
+ var candidates = neighbors[i], j = -1, m = candidates.length, x;
+ while (++j < m) if (!isNaN(x = candidates[j][dimension])) return x;
+ return Math.random() * size;
+ }
+ return force.resume();
+ };
+ force.resume = function() {
+ return force.alpha(.1);
+ };
+ force.stop = function() {
+ return force.alpha(0);
+ };
+ force.drag = function() {
+ if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
+ if (!arguments.length) return drag;
+ this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
+ };
+ function dragmove(d) {
+ d.px = d3.event.x, d.py = d3.event.y;
+ force.resume();
+ }
+ return d3.rebind(force, event, "on");
+ };
+ function d3_layout_forceDragstart(d) {
+ d.fixed |= 2;
+ }
+ function d3_layout_forceDragend(d) {
+ d.fixed &= ~6;
+ }
+ function d3_layout_forceMouseover(d) {
+ d.fixed |= 4;
+ d.px = d.x, d.py = d.y;
+ }
+ function d3_layout_forceMouseout(d) {
+ d.fixed &= ~4;
+ }
+ function d3_layout_forceAccumulate(quad, alpha, charges) {
+ var cx = 0, cy = 0;
+ quad.charge = 0;
+ if (!quad.leaf) {
+ var nodes = quad.nodes, n = nodes.length, i = -1, c;
+ while (++i < n) {
+ c = nodes[i];
+ if (c == null) continue;
+ d3_layout_forceAccumulate(c, alpha, charges);
+ quad.charge += c.charge;
+ cx += c.charge * c.cx;
+ cy += c.charge * c.cy;
+ }
+ }
+ if (quad.point) {
+ if (!quad.leaf) {
+ quad.point.x += Math.random() - .5;
+ quad.point.y += Math.random() - .5;
+ }
+ var k = alpha * charges[quad.point.index];
+ quad.charge += quad.pointCharge = k;
+ cx += k * quad.point.x;
+ cy += k * quad.point.y;
+ }
+ quad.cx = cx / quad.charge;
+ quad.cy = cy / quad.charge;
+ }
+ var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1;
+ d3.layout.hierarchy = function() {
+ var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
+ function recurse(node, depth, nodes) {
+ var childs = children.call(hierarchy, node, depth);
+ node.depth = depth;
+ nodes.push(node);
+ if (childs && (n = childs.length)) {
+ var i = -1, n, c = node.children = new Array(n), v = 0, j = depth + 1, d;
+ while (++i < n) {
+ d = c[i] = recurse(childs[i], j, nodes);
+ d.parent = node;
+ v += d.value;
+ }
+ if (sort) c.sort(sort);
+ if (value) node.value = v;
+ } else {
+ delete node.children;
+ if (value) {
+ node.value = +value.call(hierarchy, node, depth) || 0;
+ }
+ }
+ return node;
+ }
+ function revalue(node, depth) {
+ var children = node.children, v = 0;
+ if (children && (n = children.length)) {
+ var i = -1, n, j = depth + 1;
+ while (++i < n) v += revalue(children[i], j);
+ } else if (value) {
+ v = +value.call(hierarchy, node, depth) || 0;
+ }
+ if (value) node.value = v;
+ return v;
+ }
+ function hierarchy(d) {
+ var nodes = [];
+ recurse(d, 0, nodes);
+ return nodes;
+ }
+ hierarchy.sort = function(x) {
+ if (!arguments.length) return sort;
+ sort = x;
+ return hierarchy;
+ };
+ hierarchy.children = function(x) {
+ if (!arguments.length) return children;
+ children = x;
+ return hierarchy;
+ };
+ hierarchy.value = function(x) {
+ if (!arguments.length) return value;
+ value = x;
+ return hierarchy;
+ };
+ hierarchy.revalue = function(root) {
+ revalue(root, 0);
+ return root;
+ };
+ return hierarchy;
+ };
+ function d3_layout_hierarchyRebind(object, hierarchy) {
+ d3.rebind(object, hierarchy, "sort", "children", "value");
+ object.nodes = object;
+ object.links = d3_layout_hierarchyLinks;
+ return object;
+ }
+ function d3_layout_hierarchyChildren(d) {
+ return d.children;
+ }
+ function d3_layout_hierarchyValue(d) {
+ return d.value;
+ }
+ function d3_layout_hierarchySort(a, b) {
+ return b.value - a.value;
+ }
+ function d3_layout_hierarchyLinks(nodes) {
+ return d3.merge(nodes.map(function(parent) {
+ return (parent.children || []).map(function(child) {
+ return {
+ source: parent,
+ target: child
+ };
+ });
+ }));
+ }
+ d3.layout.partition = function() {
+ var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
+ function position(node, x, dx, dy) {
+ var children = node.children;
+ node.x = x;
+ node.y = node.depth * dy;
+ node.dx = dx;
+ node.dy = dy;
+ if (children && (n = children.length)) {
+ var i = -1, n, c, d;
+ dx = node.value ? dx / node.value : 0;
+ while (++i < n) {
+ position(c = children[i], x, d = c.value * dx, dy);
+ x += d;
+ }
+ }
+ }
+ function depth(node) {
+ var children = node.children, d = 0;
+ if (children && (n = children.length)) {
+ var i = -1, n;
+ while (++i < n) d = Math.max(d, depth(children[i]));
+ }
+ return 1 + d;
+ }
+ function partition(d, i) {
+ var nodes = hierarchy.call(this, d, i);
+ position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
+ return nodes;
+ }
+ partition.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return partition;
+ };
+ return d3_layout_hierarchyRebind(partition, hierarchy);
+ };
+ d3.layout.pie = function() {
+ var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = Ï;
+ function pie(data) {
+ var values = data.map(function(d, i) {
+ return +value.call(pie, d, i);
+ });
+ var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle);
+ var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values);
+ var index = d3.range(data.length);
+ if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
+ return values[j] - values[i];
+ } : function(i, j) {
+ return sort(data[i], data[j]);
+ });
+ var arcs = [];
+ index.forEach(function(i) {
+ var d;
+ arcs[i] = {
+ data: data[i],
+ value: d = values[i],
+ startAngle: a,
+ endAngle: a += d * k
+ };
+ });
+ return arcs;
+ }
+ pie.value = function(x) {
+ if (!arguments.length) return value;
+ value = x;
+ return pie;
+ };
+ pie.sort = function(x) {
+ if (!arguments.length) return sort;
+ sort = x;
+ return pie;
+ };
+ pie.startAngle = function(x) {
+ if (!arguments.length) return startAngle;
+ startAngle = x;
+ return pie;
+ };
+ pie.endAngle = function(x) {
+ if (!arguments.length) return endAngle;
+ endAngle = x;
+ return pie;
+ };
+ return pie;
+ };
+ var d3_layout_pieSortByValue = {};
+ d3.layout.stack = function() {
+ var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
+ function stack(data, index) {
+ var series = data.map(function(d, i) {
+ return values.call(stack, d, i);
+ });
+ var points = series.map(function(d) {
+ return d.map(function(v, i) {
+ return [ x.call(stack, v, i), y.call(stack, v, i) ];
+ });
+ });
+ var orders = order.call(stack, points, index);
+ series = d3.permute(series, orders);
+ points = d3.permute(points, orders);
+ var offsets = offset.call(stack, points, index);
+ var n = series.length, m = series[0].length, i, j, o;
+ for (j = 0; j < m; ++j) {
+ out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
+ for (i = 1; i < n; ++i) {
+ out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
+ }
+ }
+ return data;
+ }
+ stack.values = function(x) {
+ if (!arguments.length) return values;
+ values = x;
+ return stack;
+ };
+ stack.order = function(x) {
+ if (!arguments.length) return order;
+ order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
+ return stack;
+ };
+ stack.offset = function(x) {
+ if (!arguments.length) return offset;
+ offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
+ return stack;
+ };
+ stack.x = function(z) {
+ if (!arguments.length) return x;
+ x = z;
+ return stack;
+ };
+ stack.y = function(z) {
+ if (!arguments.length) return y;
+ y = z;
+ return stack;
+ };
+ stack.out = function(z) {
+ if (!arguments.length) return out;
+ out = z;
+ return stack;
+ };
+ return stack;
+ };
+ function d3_layout_stackX(d) {
+ return d.x;
+ }
+ function d3_layout_stackY(d) {
+ return d.y;
+ }
+ function d3_layout_stackOut(d, y0, y) {
+ d.y0 = y0;
+ d.y = y;
+ }
+ var d3_layout_stackOrders = d3.map({
+ "inside-out": function(data) {
+ var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
+ return max[a] - max[b];
+ }), top = 0, bottom = 0, tops = [], bottoms = [];
+ for (i = 0; i < n; ++i) {
+ j = index[i];
+ if (top < bottom) {
+ top += sums[j];
+ tops.push(j);
+ } else {
+ bottom += sums[j];
+ bottoms.push(j);
+ }
+ }
+ return bottoms.reverse().concat(tops);
+ },
+ reverse: function(data) {
+ return d3.range(data.length).reverse();
+ },
+ "default": d3_layout_stackOrderDefault
+ });
+ var d3_layout_stackOffsets = d3.map({
+ silhouette: function(data) {
+ var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
+ for (j = 0; j < m; ++j) {
+ for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+ if (o > max) max = o;
+ sums.push(o);
+ }
+ for (j = 0; j < m; ++j) {
+ y0[j] = (max - sums[j]) / 2;
+ }
+ return y0;
+ },
+ wiggle: function(data) {
+ var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
+ y0[0] = o = o0 = 0;
+ for (j = 1; j < m; ++j) {
+ for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
+ for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
+ for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
+ s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
+ }
+ s2 += s3 * data[i][j][1];
+ }
+ y0[j] = o -= s1 ? s2 / s1 * dx : 0;
+ if (o < o0) o0 = o;
+ }
+ for (j = 0; j < m; ++j) y0[j] -= o0;
+ return y0;
+ },
+ expand: function(data) {
+ var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
+ for (j = 0; j < m; ++j) {
+ for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
+ if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
+ }
+ for (j = 0; j < m; ++j) y0[j] = 0;
+ return y0;
+ },
+ zero: d3_layout_stackOffsetZero
+ });
+ function d3_layout_stackOrderDefault(data) {
+ return d3.range(data.length);
+ }
+ function d3_layout_stackOffsetZero(data) {
+ var j = -1, m = data[0].length, y0 = [];
+ while (++j < m) y0[j] = 0;
+ return y0;
+ }
+ function d3_layout_stackMaxIndex(array) {
+ var i = 1, j = 0, v = array[0][1], k, n = array.length;
+ for (;i < n; ++i) {
+ if ((k = array[i][1]) > v) {
+ j = i;
+ v = k;
+ }
+ }
+ return j;
+ }
+ function d3_layout_stackReduceSum(d) {
+ return d.reduce(d3_layout_stackSum, 0);
+ }
+ function d3_layout_stackSum(p, d) {
+ return p + d[1];
+ }
+ d3.layout.histogram = function() {
+ var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
+ function histogram(data, i) {
+ var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
+ while (++i < m) {
+ bin = bins[i] = [];
+ bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
+ bin.y = 0;
+ }
+ if (m > 0) {
+ i = -1;
+ while (++i < n) {
+ x = values[i];
+ if (x >= range[0] && x <= range[1]) {
+ bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
+ bin.y += k;
+ bin.push(data[i]);
+ }
+ }
+ }
+ return bins;
+ }
+ histogram.value = function(x) {
+ if (!arguments.length) return valuer;
+ valuer = x;
+ return histogram;
+ };
+ histogram.range = function(x) {
+ if (!arguments.length) return ranger;
+ ranger = d3_functor(x);
+ return histogram;
+ };
+ histogram.bins = function(x) {
+ if (!arguments.length) return binner;
+ binner = typeof x === "number" ? function(range) {
+ return d3_layout_histogramBinFixed(range, x);
+ } : d3_functor(x);
+ return histogram;
+ };
+ histogram.frequency = function(x) {
+ if (!arguments.length) return frequency;
+ frequency = !!x;
+ return histogram;
+ };
+ return histogram;
+ };
+ function d3_layout_histogramBinSturges(range, values) {
+ return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
+ }
+ function d3_layout_histogramBinFixed(range, n) {
+ var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
+ while (++x <= n) f[x] = m * x + b;
+ return f;
+ }
+ function d3_layout_histogramRange(values) {
+ return [ d3.min(values), d3.max(values) ];
+ }
+ d3.layout.tree = function() {
+ var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
+ function tree(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0];
+ function firstWalk(node, previousSibling) {
+ var children = node.children, layout = node._tree;
+ if (children && (n = children.length)) {
+ var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
+ while (++i < n) {
+ child = children[i];
+ firstWalk(child, previousChild);
+ ancestor = apportion(child, previousChild, ancestor);
+ previousChild = child;
+ }
+ d3_layout_treeShift(node);
+ var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
+ if (previousSibling) {
+ layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
+ layout.mod = layout.prelim - midpoint;
+ } else {
+ layout.prelim = midpoint;
+ }
+ } else {
+ if (previousSibling) {
+ layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
+ }
+ }
+ }
+ function secondWalk(node, x) {
+ node.x = node._tree.prelim + x;
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var i = -1, n;
+ x += node._tree.mod;
+ while (++i < n) {
+ secondWalk(children[i], x);
+ }
+ }
+ }
+ function apportion(node, previousSibling, ancestor) {
+ if (previousSibling) {
+ var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift;
+ while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
+ vom = d3_layout_treeLeft(vom);
+ vop = d3_layout_treeRight(vop);
+ vop._tree.ancestor = node;
+ shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
+ if (shift > 0) {
+ d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
+ sip += shift;
+ sop += shift;
+ }
+ sim += vim._tree.mod;
+ sip += vip._tree.mod;
+ som += vom._tree.mod;
+ sop += vop._tree.mod;
+ }
+ if (vim && !d3_layout_treeRight(vop)) {
+ vop._tree.thread = vim;
+ vop._tree.mod += sim - sop;
+ }
+ if (vip && !d3_layout_treeLeft(vom)) {
+ vom._tree.thread = vip;
+ vom._tree.mod += sip - som;
+ ancestor = node;
+ }
+ }
+ return ancestor;
+ }
+ d3_layout_treeVisitAfter(root, function(node, previousSibling) {
+ node._tree = {
+ ancestor: node,
+ prelim: 0,
+ mod: 0,
+ change: 0,
+ shift: 0,
+ number: previousSibling ? previousSibling._tree.number + 1 : 0
+ };
+ });
+ firstWalk(root);
+ secondWalk(root, -root._tree.prelim);
+ var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1;
+ d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
+ node.x *= size[0];
+ node.y = node.depth * size[1];
+ delete node._tree;
+ } : function(node) {
+ node.x = (node.x - x0) / (x1 - x0) * size[0];
+ node.y = node.depth / y1 * size[1];
+ delete node._tree;
+ });
+ return nodes;
+ }
+ tree.separation = function(x) {
+ if (!arguments.length) return separation;
+ separation = x;
+ return tree;
+ };
+ tree.size = function(x) {
+ if (!arguments.length) return nodeSize ? null : size;
+ nodeSize = (size = x) == null;
+ return tree;
+ };
+ tree.nodeSize = function(x) {
+ if (!arguments.length) return nodeSize ? size : null;
+ nodeSize = (size = x) != null;
+ return tree;
+ };
+ return d3_layout_hierarchyRebind(tree, hierarchy);
+ };
+ function d3_layout_treeSeparation(a, b) {
+ return a.parent == b.parent ? 1 : 2;
+ }
+ function d3_layout_treeLeft(node) {
+ var children = node.children;
+ return children && children.length ? children[0] : node._tree.thread;
+ }
+ function d3_layout_treeRight(node) {
+ var children = node.children, n;
+ return children && (n = children.length) ? children[n - 1] : node._tree.thread;
+ }
+ function d3_layout_treeSearch(node, compare) {
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var child, n, i = -1;
+ while (++i < n) {
+ if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
+ node = child;
+ }
+ }
+ }
+ return node;
+ }
+ function d3_layout_treeRightmost(a, b) {
+ return a.x - b.x;
+ }
+ function d3_layout_treeLeftmost(a, b) {
+ return b.x - a.x;
+ }
+ function d3_layout_treeDeepest(a, b) {
+ return a.depth - b.depth;
+ }
+ function d3_layout_treeVisitAfter(node, callback) {
+ function visit(node, previousSibling) {
+ var children = node.children;
+ if (children && (n = children.length)) {
+ var child, previousChild = null, i = -1, n;
+ while (++i < n) {
+ child = children[i];
+ visit(child, previousChild);
+ previousChild = child;
+ }
+ }
+ callback(node, previousSibling);
+ }
+ visit(node, null);
+ }
+ function d3_layout_treeShift(node) {
+ var shift = 0, change = 0, children = node.children, i = children.length, child;
+ while (--i >= 0) {
+ child = children[i]._tree;
+ child.prelim += shift;
+ child.mod += shift;
+ shift += child.shift + (change += child.change);
+ }
+ }
+ function d3_layout_treeMove(ancestor, node, shift) {
+ ancestor = ancestor._tree;
+ node = node._tree;
+ var change = shift / (node.number - ancestor.number);
+ ancestor.change += change;
+ node.change -= change;
+ node.shift += shift;
+ node.prelim += shift;
+ node.mod += shift;
+ }
+ function d3_layout_treeAncestor(vim, node, ancestor) {
+ return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor;
+ }
+ d3.layout.pack = function() {
+ var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
+ function pack(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() {
+ return radius;
+ };
+ root.x = root.y = 0;
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r = +r(d.value);
+ });
+ d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
+ if (padding) {
+ var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r += dr;
+ });
+ d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
+ d3_layout_treeVisitAfter(root, function(d) {
+ d.r -= dr;
+ });
+ }
+ d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
+ return nodes;
+ }
+ pack.size = function(_) {
+ if (!arguments.length) return size;
+ size = _;
+ return pack;
+ };
+ pack.radius = function(_) {
+ if (!arguments.length) return radius;
+ radius = _ == null || typeof _ === "function" ? _ : +_;
+ return pack;
+ };
+ pack.padding = function(_) {
+ if (!arguments.length) return padding;
+ padding = +_;
+ return pack;
+ };
+ return d3_layout_hierarchyRebind(pack, hierarchy);
+ };
+ function d3_layout_packSort(a, b) {
+ return a.value - b.value;
+ }
+ function d3_layout_packInsert(a, b) {
+ var c = a._pack_next;
+ a._pack_next = b;
+ b._pack_prev = a;
+ b._pack_next = c;
+ c._pack_prev = b;
+ }
+ function d3_layout_packSplice(a, b) {
+ a._pack_next = b;
+ b._pack_prev = a;
+ }
+ function d3_layout_packIntersects(a, b) {
+ var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
+ return .999 * dr * dr > dx * dx + dy * dy;
+ }
+ function d3_layout_packSiblings(node) {
+ if (!(nodes = node.children) || !(n = nodes.length)) return;
+ var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
+ function bound(node) {
+ xMin = Math.min(node.x - node.r, xMin);
+ xMax = Math.max(node.x + node.r, xMax);
+ yMin = Math.min(node.y - node.r, yMin);
+ yMax = Math.max(node.y + node.r, yMax);
+ }
+ nodes.forEach(d3_layout_packLink);
+ a = nodes[0];
+ a.x = -a.r;
+ a.y = 0;
+ bound(a);
+ if (n > 1) {
+ b = nodes[1];
+ b.x = b.r;
+ b.y = 0;
+ bound(b);
+ if (n > 2) {
+ c = nodes[2];
+ d3_layout_packPlace(a, b, c);
+ bound(c);
+ d3_layout_packInsert(a, c);
+ a._pack_prev = c;
+ d3_layout_packInsert(c, b);
+ b = a._pack_next;
+ for (i = 3; i < n; i++) {
+ d3_layout_packPlace(a, b, c = nodes[i]);
+ var isect = 0, s1 = 1, s2 = 1;
+ for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
+ if (d3_layout_packIntersects(j, c)) {
+ isect = 1;
+ break;
+ }
+ }
+ if (isect == 1) {
+ for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
+ if (d3_layout_packIntersects(k, c)) {
+ break;
+ }
+ }
+ }
+ if (isect) {
+ if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
+ i--;
+ } else {
+ d3_layout_packInsert(a, c);
+ b = c;
+ bound(c);
+ }
+ }
+ }
+ }
+ var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
+ for (i = 0; i < n; i++) {
+ c = nodes[i];
+ c.x -= cx;
+ c.y -= cy;
+ cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
+ }
+ node.r = cr;
+ nodes.forEach(d3_layout_packUnlink);
+ }
+ function d3_layout_packLink(node) {
+ node._pack_next = node._pack_prev = node;
+ }
+ function d3_layout_packUnlink(node) {
+ delete node._pack_next;
+ delete node._pack_prev;
+ }
+ function d3_layout_packTransform(node, x, y, k) {
+ var children = node.children;
+ node.x = x += k * node.x;
+ node.y = y += k * node.y;
+ node.r *= k;
+ if (children) {
+ var i = -1, n = children.length;
+ while (++i < n) d3_layout_packTransform(children[i], x, y, k);
+ }
+ }
+ function d3_layout_packPlace(a, b, c) {
+ var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
+ if (db && (dx || dy)) {
+ var da = b.r + c.r, dc = dx * dx + dy * dy;
+ da *= da;
+ db *= db;
+ var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
+ c.x = a.x + x * dx + y * dy;
+ c.y = a.y + x * dy - y * dx;
+ } else {
+ c.x = a.x + db;
+ c.y = a.y;
+ }
+ }
+ d3.layout.cluster = function() {
+ var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
+ function cluster(d, i) {
+ var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
+ d3_layout_treeVisitAfter(root, function(node) {
+ var children = node.children;
+ if (children && children.length) {
+ node.x = d3_layout_clusterX(children);
+ node.y = d3_layout_clusterY(children);
+ } else {
+ node.x = previousNode ? x += separation(node, previousNode) : 0;
+ node.y = 0;
+ previousNode = node;
+ }
+ });
+ var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
+ d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
+ node.x = (node.x - root.x) * size[0];
+ node.y = (root.y - node.y) * size[1];
+ } : function(node) {
+ node.x = (node.x - x0) / (x1 - x0) * size[0];
+ node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
+ });
+ return nodes;
+ }
+ cluster.separation = function(x) {
+ if (!arguments.length) return separation;
+ separation = x;
+ return cluster;
+ };
+ cluster.size = function(x) {
+ if (!arguments.length) return nodeSize ? null : size;
+ nodeSize = (size = x) == null;
+ return cluster;
+ };
+ cluster.nodeSize = function(x) {
+ if (!arguments.length) return nodeSize ? size : null;
+ nodeSize = (size = x) != null;
+ return cluster;
+ };
+ return d3_layout_hierarchyRebind(cluster, hierarchy);
+ };
+ function d3_layout_clusterY(children) {
+ return 1 + d3.max(children, function(child) {
+ return child.y;
+ });
+ }
+ function d3_layout_clusterX(children) {
+ return children.reduce(function(x, child) {
+ return x + child.x;
+ }, 0) / children.length;
+ }
+ function d3_layout_clusterLeft(node) {
+ var children = node.children;
+ return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
+ }
+ function d3_layout_clusterRight(node) {
+ var children = node.children, n;
+ return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
+ }
+ d3.layout.treemap = function() {
+ var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
+ function scale(children, k) {
+ var i = -1, n = children.length, child, area;
+ while (++i < n) {
+ area = (child = children[i]).value * (k < 0 ? 0 : k);
+ child.area = isNaN(area) || area <= 0 ? 0 : area;
+ }
+ }
+ function squarify(node) {
+ var children = node.children;
+ if (children && children.length) {
+ var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
+ scale(remaining, rect.dx * rect.dy / node.value);
+ row.area = 0;
+ while ((n = remaining.length) > 0) {
+ row.push(child = remaining[n - 1]);
+ row.area += child.area;
+ if (mode !== "squarify" || (score = worst(row, u)) <= best) {
+ remaining.pop();
+ best = score;
+ } else {
+ row.area -= row.pop().area;
+ position(row, u, rect, false);
+ u = Math.min(rect.dx, rect.dy);
+ row.length = row.area = 0;
+ best = Infinity;
+ }
+ }
+ if (row.length) {
+ position(row, u, rect, true);
+ row.length = row.area = 0;
+ }
+ children.forEach(squarify);
+ }
+ }
+ function stickify(node) {
+ var children = node.children;
+ if (children && children.length) {
+ var rect = pad(node), remaining = children.slice(), child, row = [];
+ scale(remaining, rect.dx * rect.dy / node.value);
+ row.area = 0;
+ while (child = remaining.pop()) {
+ row.push(child);
+ row.area += child.area;
+ if (child.z != null) {
+ position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
+ row.length = row.area = 0;
+ }
+ }
+ children.forEach(stickify);
+ }
+ }
+ function worst(row, u) {
+ var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
+ while (++i < n) {
+ if (!(r = row[i].area)) continue;
+ if (r < rmin) rmin = r;
+ if (r > rmax) rmax = r;
+ }
+ s *= s;
+ u *= u;
+ return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
+ }
+ function position(row, u, rect, flush) {
+ var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
+ if (u == rect.dx) {
+ if (flush || v > rect.dy) v = rect.dy;
+ while (++i < n) {
+ o = row[i];
+ o.x = x;
+ o.y = y;
+ o.dy = v;
+ x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
+ }
+ o.z = true;
+ o.dx += rect.x + rect.dx - x;
+ rect.y += v;
+ rect.dy -= v;
+ } else {
+ if (flush || v > rect.dx) v = rect.dx;
+ while (++i < n) {
+ o = row[i];
+ o.x = x;
+ o.y = y;
+ o.dx = v;
+ y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
+ }
+ o.z = false;
+ o.dy += rect.y + rect.dy - y;
+ rect.x += v;
+ rect.dx -= v;
+ }
+ }
+ function treemap(d) {
+ var nodes = stickies || hierarchy(d), root = nodes[0];
+ root.x = 0;
+ root.y = 0;
+ root.dx = size[0];
+ root.dy = size[1];
+ if (stickies) hierarchy.revalue(root);
+ scale([ root ], root.dx * root.dy / root.value);
+ (stickies ? stickify : squarify)(root);
+ if (sticky) stickies = nodes;
+ return nodes;
+ }
+ treemap.size = function(x) {
+ if (!arguments.length) return size;
+ size = x;
+ return treemap;
+ };
+ treemap.padding = function(x) {
+ if (!arguments.length) return padding;
+ function padFunction(node) {
+ var p = x.call(treemap, node, node.depth);
+ return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
+ }
+ function padConstant(node) {
+ return d3_layout_treemapPad(node, x);
+ }
+ var type;
+ pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
+ padConstant) : padConstant;
+ return treemap;
+ };
+ treemap.round = function(x) {
+ if (!arguments.length) return round != Number;
+ round = x ? Math.round : Number;
+ return treemap;
+ };
+ treemap.sticky = function(x) {
+ if (!arguments.length) return sticky;
+ sticky = x;
+ stickies = null;
+ return treemap;
+ };
+ treemap.ratio = function(x) {
+ if (!arguments.length) return ratio;
+ ratio = x;
+ return treemap;
+ };
+ treemap.mode = function(x) {
+ if (!arguments.length) return mode;
+ mode = x + "";
+ return treemap;
+ };
+ return d3_layout_hierarchyRebind(treemap, hierarchy);
+ };
+ function d3_layout_treemapPadNull(node) {
+ return {
+ x: node.x,
+ y: node.y,
+ dx: node.dx,
+ dy: node.dy
+ };
+ }
+ function d3_layout_treemapPad(node, padding) {
+ var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
+ if (dx < 0) {
+ x += dx / 2;
+ dx = 0;
+ }
+ if (dy < 0) {
+ y += dy / 2;
+ dy = 0;
+ }
+ return {
+ x: x,
+ y: y,
+ dx: dx,
+ dy: dy
+ };
+ }
+ d3.random = {
+ normal: function(µ, Ï) {
+ var n = arguments.length;
+ if (n < 2) Ï = 1;
+ if (n < 1) µ = 0;
+ return function() {
+ var x, y, r;
+ do {
+ x = Math.random() * 2 - 1;
+ y = Math.random() * 2 - 1;
+ r = x * x + y * y;
+ } while (!r || r > 1);
+ return µ + Ï * x * Math.sqrt(-2 * Math.log(r) / r);
+ };
+ },
+ logNormal: function() {
+ var random = d3.random.normal.apply(d3, arguments);
+ return function() {
+ return Math.exp(random());
+ };
+ },
+ bates: function(m) {
+ var random = d3.random.irwinHall(m);
+ return function() {
+ return random() / m;
+ };
+ },
+ irwinHall: function(m) {
+ return function() {
+ for (var s = 0, j = 0; j < m; j++) s += Math.random();
+ return s;
+ };
+ }
+ };
+ d3.scale = {};
+ function d3_scaleExtent(domain) {
+ var start = domain[0], stop = domain[domain.length - 1];
+ return start < stop ? [ start, stop ] : [ stop, start ];
+ }
+ function d3_scaleRange(scale) {
+ return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
+ }
+ function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
+ var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
+ return function(x) {
+ return i(u(x));
+ };
+ }
+ function d3_scale_nice(domain, nice) {
+ var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
+ if (x1 < x0) {
+ dx = i0, i0 = i1, i1 = dx;
+ dx = x0, x0 = x1, x1 = dx;
+ }
+ domain[i0] = nice.floor(x0);
+ domain[i1] = nice.ceil(x1);
+ return domain;
+ }
+ function d3_scale_niceStep(step) {
+ return step ? {
+ floor: function(x) {
+ return Math.floor(x / step) * step;
+ },
+ ceil: function(x) {
+ return Math.ceil(x / step) * step;
+ }
+ } : d3_scale_niceIdentity;
+ }
+ var d3_scale_niceIdentity = {
+ floor: d3_identity,
+ ceil: d3_identity
+ };
+ function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
+ var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
+ if (domain[k] < domain[0]) {
+ domain = domain.slice().reverse();
+ range = range.slice().reverse();
+ }
+ while (++j <= k) {
+ u.push(uninterpolate(domain[j - 1], domain[j]));
+ i.push(interpolate(range[j - 1], range[j]));
+ }
+ return function(x) {
+ var j = d3.bisect(domain, x, 1, k) - 1;
+ return i[j](u[j](x));
+ };
+ }
+ d3.scale.linear = function() {
+ return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
+ };
+ function d3_scale_linear(domain, range, interpolate, clamp) {
+ var output, input;
+ function rescale() {
+ var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
+ output = linear(domain, range, uninterpolate, interpolate);
+ input = linear(range, domain, uninterpolate, d3_interpolate);
+ return scale;
+ }
+ function scale(x) {
+ return output(x);
+ }
+ scale.invert = function(y) {
+ return input(y);
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.map(Number);
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.rangeRound = function(x) {
+ return scale.range(x).interpolate(d3_interpolateRound);
+ };
+ scale.clamp = function(x) {
+ if (!arguments.length) return clamp;
+ clamp = x;
+ return rescale();
+ };
+ scale.interpolate = function(x) {
+ if (!arguments.length) return interpolate;
+ interpolate = x;
+ return rescale();
+ };
+ scale.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ scale.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ scale.nice = function(m) {
+ d3_scale_linearNice(domain, m);
+ return rescale();
+ };
+ scale.copy = function() {
+ return d3_scale_linear(domain, range, interpolate, clamp);
+ };
+ return rescale();
+ }
+ function d3_scale_linearRebind(scale, linear) {
+ return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
+ }
+ function d3_scale_linearNice(domain, m) {
+ return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2]));
+ }
+ function d3_scale_linearTickRange(domain, m) {
+ if (m == null) m = 10;
+ var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
+ if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
+ extent[0] = Math.ceil(extent[0] / step) * step;
+ extent[1] = Math.floor(extent[1] / step) * step + step * .5;
+ extent[2] = step;
+ return extent;
+ }
+ function d3_scale_linearTicks(domain, m) {
+ return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
+ }
+ function d3_scale_linearTickFormat(domain, m, format) {
+ var range = d3_scale_linearTickRange(domain, m);
+ return d3.format(format ? format.replace(d3_format_re, function(a, b, c, d, e, f, g, h, i, j) {
+ return [ b, c, d, e, f, g, h, i || "." + d3_scale_linearFormatPrecision(j, range), j ].join("");
+ }) : ",." + d3_scale_linearPrecision(range[2]) + "f");
+ }
+ var d3_scale_linearFormatSignificant = {
+ s: 1,
+ g: 1,
+ p: 1,
+ r: 1,
+ e: 1
+ };
+ function d3_scale_linearPrecision(value) {
+ return -Math.floor(Math.log(value) / Math.LN10 + .01);
+ }
+ function d3_scale_linearFormatPrecision(type, range) {
+ var p = d3_scale_linearPrecision(range[2]);
+ return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(Math.abs(range[0]), Math.abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2;
+ }
+ d3.scale.log = function() {
+ return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]);
+ };
+ function d3_scale_log(linear, base, positive, domain) {
+ function log(x) {
+ return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base);
+ }
+ function pow(x) {
+ return positive ? Math.pow(base, x) : -Math.pow(base, -x);
+ }
+ function scale(x) {
+ return linear(log(x));
+ }
+ scale.invert = function(x) {
+ return pow(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ positive = x[0] >= 0;
+ linear.domain((domain = x.map(Number)).map(log));
+ return scale;
+ };
+ scale.base = function(_) {
+ if (!arguments.length) return base;
+ base = +_;
+ linear.domain(domain.map(log));
+ return scale;
+ };
+ scale.nice = function() {
+ var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative);
+ linear.domain(niced);
+ domain = niced.map(pow);
+ return scale;
+ };
+ scale.ticks = function() {
+ var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base;
+ if (isFinite(j - i)) {
+ if (positive) {
+ for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k);
+ ticks.push(pow(i));
+ } else {
+ ticks.push(pow(i));
+ for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k);
+ }
+ for (i = 0; ticks[i] < u; i++) {}
+ for (j = ticks.length; ticks[j - 1] > v; j--) {}
+ ticks = ticks.slice(i, j);
+ }
+ return ticks;
+ };
+ scale.tickFormat = function(n, format) {
+ if (!arguments.length) return d3_scale_logFormat;
+ if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
+ var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12,
+ Math.floor), e;
+ return function(d) {
+ return d / pow(f(log(d) + e)) <= k ? format(d) : "";
+ };
+ };
+ scale.copy = function() {
+ return d3_scale_log(linear.copy(), base, positive, domain);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = {
+ floor: function(x) {
+ return -Math.ceil(-x);
+ },
+ ceil: function(x) {
+ return -Math.floor(-x);
+ }
+ };
+ d3.scale.pow = function() {
+ return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
+ };
+ function d3_scale_pow(linear, exponent, domain) {
+ var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
+ function scale(x) {
+ return linear(powp(x));
+ }
+ scale.invert = function(x) {
+ return powb(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ linear.domain((domain = x.map(Number)).map(powp));
+ return scale;
+ };
+ scale.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ scale.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ scale.nice = function(m) {
+ return scale.domain(d3_scale_linearNice(domain, m));
+ };
+ scale.exponent = function(x) {
+ if (!arguments.length) return exponent;
+ powp = d3_scale_powPow(exponent = x);
+ powb = d3_scale_powPow(1 / exponent);
+ linear.domain(domain.map(powp));
+ return scale;
+ };
+ scale.copy = function() {
+ return d3_scale_pow(linear.copy(), exponent, domain);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ function d3_scale_powPow(e) {
+ return function(x) {
+ return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
+ };
+ }
+ d3.scale.sqrt = function() {
+ return d3.scale.pow().exponent(.5);
+ };
+ d3.scale.ordinal = function() {
+ return d3_scale_ordinal([], {
+ t: "range",
+ a: [ [] ]
+ });
+ };
+ function d3_scale_ordinal(domain, ranger) {
+ var index, range, rangeBand;
+ function scale(x) {
+ return range[((index.get(x) || ranger.t === "range" && index.set(x, domain.push(x))) - 1) % range.length];
+ }
+ function steps(start, step) {
+ return d3.range(domain.length).map(function(i) {
+ return start + step * i;
+ });
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = [];
+ index = new d3_Map();
+ var i = -1, n = x.length, xi;
+ while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
+ return scale[ranger.t].apply(scale, ranger.a);
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ rangeBand = 0;
+ ranger = {
+ t: "range",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangePoints = function(x, padding) {
+ if (arguments.length < 2) padding = 0;
+ var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding);
+ range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step);
+ rangeBand = 0;
+ ranger = {
+ t: "rangePoints",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeBands = function(x, padding, outerPadding) {
+ if (arguments.length < 2) padding = 0;
+ if (arguments.length < 3) outerPadding = padding;
+ var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
+ range = steps(start + step * outerPadding, step);
+ if (reverse) range.reverse();
+ rangeBand = step * (1 - padding);
+ ranger = {
+ t: "rangeBands",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeRoundBands = function(x, padding, outerPadding) {
+ if (arguments.length < 2) padding = 0;
+ if (arguments.length < 3) outerPadding = padding;
+ var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step;
+ range = steps(start + Math.round(error / 2), step);
+ if (reverse) range.reverse();
+ rangeBand = Math.round(step * (1 - padding));
+ ranger = {
+ t: "rangeRoundBands",
+ a: arguments
+ };
+ return scale;
+ };
+ scale.rangeBand = function() {
+ return rangeBand;
+ };
+ scale.rangeExtent = function() {
+ return d3_scaleExtent(ranger.a[0]);
+ };
+ scale.copy = function() {
+ return d3_scale_ordinal(domain, ranger);
+ };
+ return scale.domain(domain);
+ }
+ d3.scale.category10 = function() {
+ return d3.scale.ordinal().range(d3_category10);
+ };
+ d3.scale.category20 = function() {
+ return d3.scale.ordinal().range(d3_category20);
+ };
+ d3.scale.category20b = function() {
+ return d3.scale.ordinal().range(d3_category20b);
+ };
+ d3.scale.category20c = function() {
+ return d3.scale.ordinal().range(d3_category20c);
+ };
+ var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString);
+ var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString);
+ var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString);
+ var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString);
+ d3.scale.quantile = function() {
+ return d3_scale_quantile([], []);
+ };
+ function d3_scale_quantile(domain, range) {
+ var thresholds;
+ function rescale() {
+ var k = 0, q = range.length;
+ thresholds = [];
+ while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
+ return scale;
+ }
+ function scale(x) {
+ if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.filter(function(d) {
+ return !isNaN(d);
+ }).sort(d3.ascending);
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.quantiles = function() {
+ return thresholds;
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ];
+ };
+ scale.copy = function() {
+ return d3_scale_quantile(domain, range);
+ };
+ return rescale();
+ }
+ d3.scale.quantize = function() {
+ return d3_scale_quantize(0, 1, [ 0, 1 ]);
+ };
+ function d3_scale_quantize(x0, x1, range) {
+ var kx, i;
+ function scale(x) {
+ return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
+ }
+ function rescale() {
+ kx = range.length / (x1 - x0);
+ i = range.length - 1;
+ return scale;
+ }
+ scale.domain = function(x) {
+ if (!arguments.length) return [ x0, x1 ];
+ x0 = +x[0];
+ x1 = +x[x.length - 1];
+ return rescale();
+ };
+ scale.range = function(x) {
+ if (!arguments.length) return range;
+ range = x;
+ return rescale();
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ y = y < 0 ? NaN : y / kx + x0;
+ return [ y, y + 1 / kx ];
+ };
+ scale.copy = function() {
+ return d3_scale_quantize(x0, x1, range);
+ };
+ return rescale();
+ }
+ d3.scale.threshold = function() {
+ return d3_scale_threshold([ .5 ], [ 0, 1 ]);
+ };
+ function d3_scale_threshold(domain, range) {
+ function scale(x) {
+ if (x <= x) return range[d3.bisect(domain, x)];
+ }
+ scale.domain = function(_) {
+ if (!arguments.length) return domain;
+ domain = _;
+ return scale;
+ };
+ scale.range = function(_) {
+ if (!arguments.length) return range;
+ range = _;
+ return scale;
+ };
+ scale.invertExtent = function(y) {
+ y = range.indexOf(y);
+ return [ domain[y - 1], domain[y] ];
+ };
+ scale.copy = function() {
+ return d3_scale_threshold(domain, range);
+ };
+ return scale;
+ }
+ d3.scale.identity = function() {
+ return d3_scale_identity([ 0, 1 ]);
+ };
+ function d3_scale_identity(domain) {
+ function identity(x) {
+ return +x;
+ }
+ identity.invert = identity;
+ identity.domain = identity.range = function(x) {
+ if (!arguments.length) return domain;
+ domain = x.map(identity);
+ return identity;
+ };
+ identity.ticks = function(m) {
+ return d3_scale_linearTicks(domain, m);
+ };
+ identity.tickFormat = function(m, format) {
+ return d3_scale_linearTickFormat(domain, m, format);
+ };
+ identity.copy = function() {
+ return d3_scale_identity(domain);
+ };
+ return identity;
+ }
+ d3.svg = {};
+ d3.svg.arc = function() {
+ var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
+ function arc() {
+ var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0,
+ a0 = a1, a1 = da), a1 - a0), df = da < Ï ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1);
+ return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z";
+ }
+ arc.innerRadius = function(v) {
+ if (!arguments.length) return innerRadius;
+ innerRadius = d3_functor(v);
+ return arc;
+ };
+ arc.outerRadius = function(v) {
+ if (!arguments.length) return outerRadius;
+ outerRadius = d3_functor(v);
+ return arc;
+ };
+ arc.startAngle = function(v) {
+ if (!arguments.length) return startAngle;
+ startAngle = d3_functor(v);
+ return arc;
+ };
+ arc.endAngle = function(v) {
+ if (!arguments.length) return endAngle;
+ endAngle = d3_functor(v);
+ return arc;
+ };
+ arc.centroid = function() {
+ var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset;
+ return [ Math.cos(a) * r, Math.sin(a) * r ];
+ };
+ return arc;
+ };
+ var d3_svg_arcOffset = -halfÏ, d3_svg_arcMax = Ï - ε;
+ function d3_svg_arcInnerRadius(d) {
+ return d.innerRadius;
+ }
+ function d3_svg_arcOuterRadius(d) {
+ return d.outerRadius;
+ }
+ function d3_svg_arcStartAngle(d) {
+ return d.startAngle;
+ }
+ function d3_svg_arcEndAngle(d) {
+ return d.endAngle;
+ }
+ function d3_svg_line(projection) {
+ var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
+ function line(data) {
+ var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
+ function segment() {
+ segments.push("M", interpolate(projection(points), tension));
+ }
+ while (++i < n) {
+ if (defined.call(this, d = data[i], i)) {
+ points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
+ } else if (points.length) {
+ segment();
+ points = [];
+ }
+ }
+ if (points.length) segment();
+ return segments.length ? segments.join("") : null;
+ }
+ line.x = function(_) {
+ if (!arguments.length) return x;
+ x = _;
+ return line;
+ };
+ line.y = function(_) {
+ if (!arguments.length) return y;
+ y = _;
+ return line;
+ };
+ line.defined = function(_) {
+ if (!arguments.length) return defined;
+ defined = _;
+ return line;
+ };
+ line.interpolate = function(_) {
+ if (!arguments.length) return interpolateKey;
+ if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+ return line;
+ };
+ line.tension = function(_) {
+ if (!arguments.length) return tension;
+ tension = _;
+ return line;
+ };
+ return line;
+ }
+ d3.svg.line = function() {
+ return d3_svg_line(d3_identity);
+ };
+ var d3_svg_lineInterpolators = d3.map({
+ linear: d3_svg_lineLinear,
+ "linear-closed": d3_svg_lineLinearClosed,
+ step: d3_svg_lineStep,
+ "step-before": d3_svg_lineStepBefore,
+ "step-after": d3_svg_lineStepAfter,
+ basis: d3_svg_lineBasis,
+ "basis-open": d3_svg_lineBasisOpen,
+ "basis-closed": d3_svg_lineBasisClosed,
+ bundle: d3_svg_lineBundle,
+ cardinal: d3_svg_lineCardinal,
+ "cardinal-open": d3_svg_lineCardinalOpen,
+ "cardinal-closed": d3_svg_lineCardinalClosed,
+ monotone: d3_svg_lineMonotone
+ });
+ d3_svg_lineInterpolators.forEach(function(key, value) {
+ value.key = key;
+ value.closed = /-closed$/.test(key);
+ });
+ function d3_svg_lineLinear(points) {
+ return points.join("L");
+ }
+ function d3_svg_lineLinearClosed(points) {
+ return d3_svg_lineLinear(points) + "Z";
+ }
+ function d3_svg_lineStep(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
+ if (n > 1) path.push("H", p[0]);
+ return path.join("");
+ }
+ function d3_svg_lineStepBefore(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
+ return path.join("");
+ }
+ function d3_svg_lineStepAfter(points) {
+ var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
+ while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
+ return path.join("");
+ }
+ function d3_svg_lineCardinalOpen(points, tension) {
+ return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
+ }
+ function d3_svg_lineCardinalClosed(points, tension) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
+ points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
+ }
+ function d3_svg_lineCardinal(points, tension) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
+ }
+ function d3_svg_lineHermite(points, tangents) {
+ if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
+ return d3_svg_lineLinear(points);
+ }
+ var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
+ if (quad) {
+ path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
+ p0 = points[1];
+ pi = 2;
+ }
+ if (tangents.length > 1) {
+ t = tangents[1];
+ p = points[pi];
+ pi++;
+ path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+ for (var i = 2; i < tangents.length; i++, pi++) {
+ p = points[pi];
+ t = tangents[i];
+ path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
+ }
+ }
+ if (quad) {
+ var lp = points[pi];
+ path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
+ }
+ return path;
+ }
+ function d3_svg_lineCardinalTangents(points, tension) {
+ var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
+ while (++i < n) {
+ p0 = p1;
+ p1 = p2;
+ p2 = points[i];
+ tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
+ }
+ return tangents;
+ }
+ function d3_svg_lineBasis(points) {
+ if (points.length < 3) return d3_svg_lineLinear(points);
+ var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+ points.push(points[n - 1]);
+ while (++i <= n) {
+ pi = points[i];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ points.pop();
+ path.push("L", pi);
+ return path.join("");
+ }
+ function d3_svg_lineBasisOpen(points) {
+ if (points.length < 4) return d3_svg_lineLinear(points);
+ var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
+ while (++i < 3) {
+ pi = points[i];
+ px.push(pi[0]);
+ py.push(pi[1]);
+ }
+ path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
+ --i;
+ while (++i < n) {
+ pi = points[i];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ return path.join("");
+ }
+ function d3_svg_lineBasisClosed(points) {
+ var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
+ while (++i < 4) {
+ pi = points[i % n];
+ px.push(pi[0]);
+ py.push(pi[1]);
+ }
+ path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
+ --i;
+ while (++i < m) {
+ pi = points[i % n];
+ px.shift();
+ px.push(pi[0]);
+ py.shift();
+ py.push(pi[1]);
+ d3_svg_lineBasisBezier(path, px, py);
+ }
+ return path.join("");
+ }
+ function d3_svg_lineBundle(points, tension) {
+ var n = points.length - 1;
+ if (n) {
+ var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
+ while (++i <= n) {
+ p = points[i];
+ t = i / n;
+ p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
+ p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
+ }
+ }
+ return d3_svg_lineBasis(points);
+ }
+ function d3_svg_lineDot4(a, b) {
+ return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
+ }
+ var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
+ function d3_svg_lineBasisBezier(path, x, y) {
+ path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
+ }
+ function d3_svg_lineSlope(p0, p1) {
+ return (p1[1] - p0[1]) / (p1[0] - p0[0]);
+ }
+ function d3_svg_lineFiniteDifferences(points) {
+ var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
+ while (++i < j) {
+ m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
+ }
+ m[i] = d;
+ return m;
+ }
+ function d3_svg_lineMonotoneTangents(points) {
+ var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
+ while (++i < j) {
+ d = d3_svg_lineSlope(points[i], points[i + 1]);
+ if (abs(d) < ε) {
+ m[i] = m[i + 1] = 0;
+ } else {
+ a = m[i] / d;
+ b = m[i + 1] / d;
+ s = a * a + b * b;
+ if (s > 9) {
+ s = d * 3 / Math.sqrt(s);
+ m[i] = s * a;
+ m[i + 1] = s * b;
+ }
+ }
+ }
+ i = -1;
+ while (++i <= j) {
+ s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
+ tangents.push([ s || 0, m[i] * s || 0 ]);
+ }
+ return tangents;
+ }
+ function d3_svg_lineMonotone(points) {
+ return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
+ }
+ d3.svg.line.radial = function() {
+ var line = d3_svg_line(d3_svg_lineRadial);
+ line.radius = line.x, delete line.x;
+ line.angle = line.y, delete line.y;
+ return line;
+ };
+ function d3_svg_lineRadial(points) {
+ var point, i = -1, n = points.length, r, a;
+ while (++i < n) {
+ point = points[i];
+ r = point[0];
+ a = point[1] + d3_svg_arcOffset;
+ point[0] = r * Math.cos(a);
+ point[1] = r * Math.sin(a);
+ }
+ return points;
+ }
+ function d3_svg_area(projection) {
+ var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
+ function area(data) {
+ var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
+ return x;
+ } : d3_functor(x1), fy1 = y0 === y1 ? function() {
+ return y;
+ } : d3_functor(y1), x, y;
+ function segment() {
+ segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
+ }
+ while (++i < n) {
+ if (defined.call(this, d = data[i], i)) {
+ points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
+ points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
+ } else if (points0.length) {
+ segment();
+ points0 = [];
+ points1 = [];
+ }
+ }
+ if (points0.length) segment();
+ return segments.length ? segments.join("") : null;
+ }
+ area.x = function(_) {
+ if (!arguments.length) return x1;
+ x0 = x1 = _;
+ return area;
+ };
+ area.x0 = function(_) {
+ if (!arguments.length) return x0;
+ x0 = _;
+ return area;
+ };
+ area.x1 = function(_) {
+ if (!arguments.length) return x1;
+ x1 = _;
+ return area;
+ };
+ area.y = function(_) {
+ if (!arguments.length) return y1;
+ y0 = y1 = _;
+ return area;
+ };
+ area.y0 = function(_) {
+ if (!arguments.length) return y0;
+ y0 = _;
+ return area;
+ };
+ area.y1 = function(_) {
+ if (!arguments.length) return y1;
+ y1 = _;
+ return area;
+ };
+ area.defined = function(_) {
+ if (!arguments.length) return defined;
+ defined = _;
+ return area;
+ };
+ area.interpolate = function(_) {
+ if (!arguments.length) return interpolateKey;
+ if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
+ interpolateReverse = interpolate.reverse || interpolate;
+ L = interpolate.closed ? "M" : "L";
+ return area;
+ };
+ area.tension = function(_) {
+ if (!arguments.length) return tension;
+ tension = _;
+ return area;
+ };
+ return area;
+ }
+ d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
+ d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
+ d3.svg.area = function() {
+ return d3_svg_area(d3_identity);
+ };
+ d3.svg.area.radial = function() {
+ var area = d3_svg_area(d3_svg_lineRadial);
+ area.radius = area.x, delete area.x;
+ area.innerRadius = area.x0, delete area.x0;
+ area.outerRadius = area.x1, delete area.x1;
+ area.angle = area.y, delete area.y;
+ area.startAngle = area.y0, delete area.y0;
+ area.endAngle = area.y1, delete area.y1;
+ return area;
+ };
+ d3.svg.chord = function() {
+ var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
+ function chord(d, i) {
+ var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
+ return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
+ }
+ function subgroup(self, f, d, i) {
+ var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset;
+ return {
+ r: r,
+ a0: a0,
+ a1: a1,
+ p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
+ p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
+ };
+ }
+ function equals(a, b) {
+ return a.a0 == b.a0 && a.a1 == b.a1;
+ }
+ function arc(r, p, a) {
+ return "A" + r + "," + r + " 0 " + +(a > Ï) + ",1 " + p;
+ }
+ function curve(r0, p0, r1, p1) {
+ return "Q 0,0 " + p1;
+ }
+ chord.radius = function(v) {
+ if (!arguments.length) return radius;
+ radius = d3_functor(v);
+ return chord;
+ };
+ chord.source = function(v) {
+ if (!arguments.length) return source;
+ source = d3_functor(v);
+ return chord;
+ };
+ chord.target = function(v) {
+ if (!arguments.length) return target;
+ target = d3_functor(v);
+ return chord;
+ };
+ chord.startAngle = function(v) {
+ if (!arguments.length) return startAngle;
+ startAngle = d3_functor(v);
+ return chord;
+ };
+ chord.endAngle = function(v) {
+ if (!arguments.length) return endAngle;
+ endAngle = d3_functor(v);
+ return chord;
+ };
+ return chord;
+ };
+ function d3_svg_chordRadius(d) {
+ return d.radius;
+ }
+ d3.svg.diagonal = function() {
+ var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
+ function diagonal(d, i) {
+ var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
+ x: p0.x,
+ y: m
+ }, {
+ x: p3.x,
+ y: m
+ }, p3 ];
+ p = p.map(projection);
+ return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
+ }
+ diagonal.source = function(x) {
+ if (!arguments.length) return source;
+ source = d3_functor(x);
+ return diagonal;
+ };
+ diagonal.target = function(x) {
+ if (!arguments.length) return target;
+ target = d3_functor(x);
+ return diagonal;
+ };
+ diagonal.projection = function(x) {
+ if (!arguments.length) return projection;
+ projection = x;
+ return diagonal;
+ };
+ return diagonal;
+ };
+ function d3_svg_diagonalProjection(d) {
+ return [ d.x, d.y ];
+ }
+ d3.svg.diagonal.radial = function() {
+ var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
+ diagonal.projection = function(x) {
+ return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
+ };
+ return diagonal;
+ };
+ function d3_svg_diagonalRadialProjection(projection) {
+ return function() {
+ var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset;
+ return [ r * Math.cos(a), r * Math.sin(a) ];
+ };
+ }
+ d3.svg.symbol = function() {
+ var type = d3_svg_symbolType, size = d3_svg_symbolSize;
+ function symbol(d, i) {
+ return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
+ }
+ symbol.type = function(x) {
+ if (!arguments.length) return type;
+ type = d3_functor(x);
+ return symbol;
+ };
+ symbol.size = function(x) {
+ if (!arguments.length) return size;
+ size = d3_functor(x);
+ return symbol;
+ };
+ return symbol;
+ };
+ function d3_svg_symbolSize() {
+ return 64;
+ }
+ function d3_svg_symbolType() {
+ return "circle";
+ }
+ function d3_svg_symbolCircle(size) {
+ var r = Math.sqrt(size / Ï);
+ return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
+ }
+ var d3_svg_symbols = d3.map({
+ circle: d3_svg_symbolCircle,
+ cross: function(size) {
+ var r = Math.sqrt(size / 5) / 2;
+ return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
+ },
+ diamond: function(size) {
+ var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
+ return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
+ },
+ square: function(size) {
+ var r = Math.sqrt(size) / 2;
+ return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
+ },
+ "triangle-down": function(size) {
+ var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+ return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
+ },
+ "triangle-up": function(size) {
+ var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
+ return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
+ }
+ });
+ d3.svg.symbolTypes = d3_svg_symbols.keys();
+ var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
+ function d3_transition(groups, id) {
+ d3_subclass(groups, d3_transitionPrototype);
+ groups.id = id;
+ return groups;
+ }
+ var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit;
+ d3_transitionPrototype.call = d3_selectionPrototype.call;
+ d3_transitionPrototype.empty = d3_selectionPrototype.empty;
+ d3_transitionPrototype.node = d3_selectionPrototype.node;
+ d3_transitionPrototype.size = d3_selectionPrototype.size;
+ d3.transition = function(selection) {
+ return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition();
+ };
+ d3.transition.prototype = d3_transitionPrototype;
+ d3_transitionPrototype.select = function(selector) {
+ var id = this.id, subgroups = [], subgroup, subnode, node;
+ selector = d3_selection_selector(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) {
+ if ("__data__" in node) subnode.__data__ = node.__data__;
+ d3_transitionNode(subnode, i, id, node.__transition__[id]);
+ subgroup.push(subnode);
+ } else {
+ subgroup.push(null);
+ }
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_transitionPrototype.selectAll = function(selector) {
+ var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition;
+ selector = d3_selection_selectorAll(selector);
+ for (var j = -1, m = this.length; ++j < m; ) {
+ for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
+ if (node = group[i]) {
+ transition = node.__transition__[id];
+ subnodes = selector.call(node, node.__data__, i, j);
+ subgroups.push(subgroup = []);
+ for (var k = -1, o = subnodes.length; ++k < o; ) {
+ if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition);
+ subgroup.push(subnode);
+ }
+ }
+ }
+ }
+ return d3_transition(subgroups, id);
+ };
+ d3_transitionPrototype.filter = function(filter) {
+ var subgroups = [], subgroup, group, node;
+ if (typeof filter !== "function") filter = d3_selection_filter(filter);
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ if ((node = group[i]) && filter.call(node, node.__data__, i, j)) {
+ subgroup.push(node);
+ }
+ }
+ }
+ return d3_transition(subgroups, this.id);
+ };
+ d3_transitionPrototype.tween = function(name, tween) {
+ var id = this.id;
+ if (arguments.length < 2) return this.node().__transition__[id].tween.get(name);
+ return d3_selection_each(this, tween == null ? function(node) {
+ node.__transition__[id].tween.remove(name);
+ } : function(node) {
+ node.__transition__[id].tween.set(name, tween);
+ });
+ };
+ function d3_transition_tween(groups, name, value, tween) {
+ var id = groups.id;
+ return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
+ } : (value = tween(value), function(node) {
+ node.__transition__[id].tween.set(name, value);
+ }));
+ }
+ d3_transitionPrototype.attr = function(nameNS, value) {
+ if (arguments.length < 2) {
+ for (value in nameNS) this.attr(value, nameNS[value]);
+ return this;
+ }
+ var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS);
+ function attrNull() {
+ this.removeAttribute(name);
+ }
+ function attrNullNS() {
+ this.removeAttributeNS(name.space, name.local);
+ }
+ function attrTween(b) {
+ return b == null ? attrNull : (b += "", function() {
+ var a = this.getAttribute(name), i;
+ return a !== b && (i = interpolate(a, b), function(t) {
+ this.setAttribute(name, i(t));
+ });
+ });
+ }
+ function attrTweenNS(b) {
+ return b == null ? attrNullNS : (b += "", function() {
+ var a = this.getAttributeNS(name.space, name.local), i;
+ return a !== b && (i = interpolate(a, b), function(t) {
+ this.setAttributeNS(name.space, name.local, i(t));
+ });
+ });
+ }
+ return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
+ };
+ d3_transitionPrototype.attrTween = function(nameNS, tween) {
+ var name = d3.ns.qualify(nameNS);
+ function attrTween(d, i) {
+ var f = tween.call(this, d, i, this.getAttribute(name));
+ return f && function(t) {
+ this.setAttribute(name, f(t));
+ };
+ }
+ function attrTweenNS(d, i) {
+ var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
+ return f && function(t) {
+ this.setAttributeNS(name.space, name.local, f(t));
+ };
+ }
+ return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
+ };
+ d3_transitionPrototype.style = function(name, value, priority) {
+ var n = arguments.length;
+ if (n < 3) {
+ if (typeof name !== "string") {
+ if (n < 2) value = "";
+ for (priority in name) this.style(priority, name[priority], value);
+ return this;
+ }
+ priority = "";
+ }
+ function styleNull() {
+ this.style.removeProperty(name);
+ }
+ function styleString(b) {
+ return b == null ? styleNull : (b += "", function() {
+ var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
+ return a !== b && (i = d3_interpolate(a, b), function(t) {
+ this.style.setProperty(name, i(t), priority);
+ });
+ });
+ }
+ return d3_transition_tween(this, "style." + name, value, styleString);
+ };
+ d3_transitionPrototype.styleTween = function(name, tween, priority) {
+ if (arguments.length < 3) priority = "";
+ function styleTween(d, i) {
+ var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name));
+ return f && function(t) {
+ this.style.setProperty(name, f(t), priority);
+ };
+ }
+ return this.tween("style." + name, styleTween);
+ };
+ d3_transitionPrototype.text = function(value) {
+ return d3_transition_tween(this, "text", value, d3_transition_text);
+ };
+ function d3_transition_text(b) {
+ if (b == null) b = "";
+ return function() {
+ this.textContent = b;
+ };
+ }
+ d3_transitionPrototype.remove = function() {
+ return this.each("end.transition", function() {
+ var p;
+ if (this.__transition__.count < 2 && (p = this.parentNode)) p.removeChild(this);
+ });
+ };
+ d3_transitionPrototype.ease = function(value) {
+ var id = this.id;
+ if (arguments.length < 1) return this.node().__transition__[id].ease;
+ if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
+ return d3_selection_each(this, function(node) {
+ node.__transition__[id].ease = value;
+ });
+ };
+ d3_transitionPrototype.delay = function(value) {
+ var id = this.id;
+ return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].delay = +value.call(node, node.__data__, i, j);
+ } : (value = +value, function(node) {
+ node.__transition__[id].delay = value;
+ }));
+ };
+ d3_transitionPrototype.duration = function(value) {
+ var id = this.id;
+ return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
+ node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j));
+ } : (value = Math.max(1, value), function(node) {
+ node.__transition__[id].duration = value;
+ }));
+ };
+ d3_transitionPrototype.each = function(type, listener) {
+ var id = this.id;
+ if (arguments.length < 2) {
+ var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
+ d3_transitionInheritId = id;
+ d3_selection_each(this, function(node, i, j) {
+ d3_transitionInherit = node.__transition__[id];
+ type.call(node, node.__data__, i, j);
+ });
+ d3_transitionInherit = inherit;
+ d3_transitionInheritId = inheritId;
+ } else {
+ d3_selection_each(this, function(node) {
+ var transition = node.__transition__[id];
+ (transition.event || (transition.event = d3.dispatch("start", "end"))).on(type, listener);
+ });
+ }
+ return this;
+ };
+ d3_transitionPrototype.transition = function() {
+ var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition;
+ for (var j = 0, m = this.length; j < m; j++) {
+ subgroups.push(subgroup = []);
+ for (var group = this[j], i = 0, n = group.length; i < n; i++) {
+ if (node = group[i]) {
+ transition = Object.create(node.__transition__[id0]);
+ transition.delay += transition.duration;
+ d3_transitionNode(node, i, id1, transition);
+ }
+ subgroup.push(node);
+ }
+ }
+ return d3_transition(subgroups, id1);
+ };
+ function d3_transitionNode(node, i, id, inherit) {
+ var lock = node.__transition__ || (node.__transition__ = {
+ active: 0,
+ count: 0
+ }), transition = lock[id];
+ if (!transition) {
+ var time = inherit.time;
+ transition = lock[id] = {
+ tween: new d3_Map(),
+ time: time,
+ ease: inherit.ease,
+ delay: inherit.delay,
+ duration: inherit.duration
+ };
+ ++lock.count;
+ d3.timer(function(elapsed) {
+ var d = node.__data__, ease = transition.ease, delay = transition.delay, duration = transition.duration, timer = d3_timer_active, tweened = [];
+ timer.t = delay + time;
+ if (delay <= elapsed) return start(elapsed - delay);
+ timer.c = start;
+ function start(elapsed) {
+ if (lock.active > id) return stop();
+ lock.active = id;
+ transition.event && transition.event.start.call(node, d, i);
+ transition.tween.forEach(function(key, value) {
+ if (value = value.call(node, d, i)) {
+ tweened.push(value);
+ }
+ });
+ d3.timer(function() {
+ timer.c = tick(elapsed || 1) ? d3_true : tick;
+ return 1;
+ }, 0, time);
+ }
+ function tick(elapsed) {
+ if (lock.active !== id) return stop();
+ var t = elapsed / duration, e = ease(t), n = tweened.length;
+ while (n > 0) {
+ tweened[--n].call(node, e);
+ }
+ if (t >= 1) {
+ transition.event && transition.event.end.call(node, d, i);
+ return stop();
+ }
+ }
+ function stop() {
+ if (--lock.count) delete lock[id]; else delete node.__transition__;
+ return 1;
+ }
+ }, 0, time);
+ }
+ }
+ d3.svg.axis = function() {
+ var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_;
+ function axis(g) {
+ g.each(function() {
+ var g = d3.select(this);
+ var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy();
+ var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform;
+ var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
+ d3.transition(path));
+ tickEnter.append("line");
+ tickEnter.append("text");
+ var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text");
+ switch (orient) {
+ case "bottom":
+ {
+ tickTransform = d3_svg_axisX;
+ lineEnter.attr("y2", innerTickSize);
+ textEnter.attr("y", Math.max(innerTickSize, 0) + tickPadding);
+ lineUpdate.attr("x2", 0).attr("y2", innerTickSize);
+ textUpdate.attr("x", 0).attr("y", Math.max(innerTickSize, 0) + tickPadding);
+ text.attr("dy", ".71em").style("text-anchor", "middle");
+ pathUpdate.attr("d", "M" + range[0] + "," + outerTickSize + "V0H" + range[1] + "V" + outerTickSize);
+ break;
+ }
+
+ case "top":
+ {
+ tickTransform = d3_svg_axisX;
+ lineEnter.attr("y2", -innerTickSize);
+ textEnter.attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
+ lineUpdate.attr("x2", 0).attr("y2", -innerTickSize);
+ textUpdate.attr("x", 0).attr("y", -(Math.max(innerTickSize, 0) + tickPadding));
+ text.attr("dy", "0em").style("text-anchor", "middle");
+ pathUpdate.attr("d", "M" + range[0] + "," + -outerTickSize + "V0H" + range[1] + "V" + -outerTickSize);
+ break;
+ }
+
+ case "left":
+ {
+ tickTransform = d3_svg_axisY;
+ lineEnter.attr("x2", -innerTickSize);
+ textEnter.attr("x", -(Math.max(innerTickSize, 0) + tickPadding));
+ lineUpdate.attr("x2", -innerTickSize).attr("y2", 0);
+ textUpdate.attr("x", -(Math.max(innerTickSize, 0) + tickPadding)).attr("y", 0);
+ text.attr("dy", ".32em").style("text-anchor", "end");
+ pathUpdate.attr("d", "M" + -outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + -outerTickSize);
+ break;
+ }
+
+ case "right":
+ {
+ tickTransform = d3_svg_axisY;
+ lineEnter.attr("x2", innerTickSize);
+ textEnter.attr("x", Math.max(innerTickSize, 0) + tickPadding);
+ lineUpdate.attr("x2", innerTickSize).attr("y2", 0);
+ textUpdate.attr("x", Math.max(innerTickSize, 0) + tickPadding).attr("y", 0);
+ text.attr("dy", ".32em").style("text-anchor", "start");
+ pathUpdate.attr("d", "M" + outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + outerTickSize);
+ break;
+ }
+ }
+ if (scale1.rangeBand) {
+ var x = scale1, dx = x.rangeBand() / 2;
+ scale0 = scale1 = function(d) {
+ return x(d) + dx;
+ };
+ } else if (scale0.rangeBand) {
+ scale0 = scale1;
+ } else {
+ tickExit.call(tickTransform, scale1);
+ }
+ tickEnter.call(tickTransform, scale0);
+ tickUpdate.call(tickTransform, scale1);
+ });
+ }
+ axis.scale = function(x) {
+ if (!arguments.length) return scale;
+ scale = x;
+ return axis;
+ };
+ axis.orient = function(x) {
+ if (!arguments.length) return orient;
+ orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
+ return axis;
+ };
+ axis.ticks = function() {
+ if (!arguments.length) return tickArguments_;
+ tickArguments_ = arguments;
+ return axis;
+ };
+ axis.tickValues = function(x) {
+ if (!arguments.length) return tickValues;
+ tickValues = x;
+ return axis;
+ };
+ axis.tickFormat = function(x) {
+ if (!arguments.length) return tickFormat_;
+ tickFormat_ = x;
+ return axis;
+ };
+ axis.tickSize = function(x) {
+ var n = arguments.length;
+ if (!n) return innerTickSize;
+ innerTickSize = +x;
+ outerTickSize = +arguments[n - 1];
+ return axis;
+ };
+ axis.innerTickSize = function(x) {
+ if (!arguments.length) return innerTickSize;
+ innerTickSize = +x;
+ return axis;
+ };
+ axis.outerTickSize = function(x) {
+ if (!arguments.length) return outerTickSize;
+ outerTickSize = +x;
+ return axis;
+ };
+ axis.tickPadding = function(x) {
+ if (!arguments.length) return tickPadding;
+ tickPadding = +x;
+ return axis;
+ };
+ axis.tickSubdivide = function() {
+ return arguments.length && axis;
+ };
+ return axis;
+ };
+ var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
+ top: 1,
+ right: 1,
+ bottom: 1,
+ left: 1
+ };
+ function d3_svg_axisX(selection, x) {
+ selection.attr("transform", function(d) {
+ return "translate(" + x(d) + ",0)";
+ });
+ }
+ function d3_svg_axisY(selection, y) {
+ selection.attr("transform", function(d) {
+ return "translate(0," + y(d) + ")";
+ });
+ }
+ d3.svg.brush = function() {
+ var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0];
+ function brush(g) {
+ g.each(function() {
+ var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
+ var background = g.selectAll(".background").data([ 0 ]);
+ background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
+ g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move");
+ var resize = g.selectAll(".resize").data(resizes, d3_identity);
+ resize.exit().remove();
+ resize.enter().append("g").attr("class", function(d) {
+ return "resize " + d;
+ }).style("cursor", function(d) {
+ return d3_svg_brushCursor[d];
+ }).append("rect").attr("x", function(d) {
+ return /[ew]$/.test(d) ? -3 : null;
+ }).attr("y", function(d) {
+ return /^[ns]/.test(d) ? -3 : null;
+ }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
+ resize.style("display", brush.empty() ? "none" : null);
+ var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range;
+ if (x) {
+ range = d3_scaleRange(x);
+ backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]);
+ redrawX(gUpdate);
+ }
+ if (y) {
+ range = d3_scaleRange(y);
+ backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]);
+ redrawY(gUpdate);
+ }
+ redraw(gUpdate);
+ });
+ }
+ brush.event = function(g) {
+ g.each(function() {
+ var event_ = event.of(this, arguments), extent1 = {
+ x: xExtent,
+ y: yExtent,
+ i: xExtentDomain,
+ j: yExtentDomain
+ }, extent0 = this.__chart__ || extent1;
+ this.__chart__ = extent1;
+ if (d3_transitionInheritId) {
+ d3.select(this).transition().each("start.brush", function() {
+ xExtentDomain = extent0.i;
+ yExtentDomain = extent0.j;
+ xExtent = extent0.x;
+ yExtent = extent0.y;
+ event_({
+ type: "brushstart"
+ });
+ }).tween("brush:brush", function() {
+ var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y);
+ xExtentDomain = yExtentDomain = null;
+ return function(t) {
+ xExtent = extent1.x = xi(t);
+ yExtent = extent1.y = yi(t);
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ };
+ }).each("end.brush", function() {
+ xExtentDomain = extent1.i;
+ yExtentDomain = extent1.j;
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ event_({
+ type: "brushend"
+ });
+ });
+ } else {
+ event_({
+ type: "brushstart"
+ });
+ event_({
+ type: "brush",
+ mode: "resize"
+ });
+ event_({
+ type: "brushend"
+ });
+ }
+ });
+ };
+ function redraw(g) {
+ g.selectAll(".resize").attr("transform", function(d) {
+ return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")";
+ });
+ }
+ function redrawX(g) {
+ g.select(".extent").attr("x", xExtent[0]);
+ g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]);
+ }
+ function redrawY(g) {
+ g.select(".extent").attr("y", yExtent[0]);
+ g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]);
+ }
+ function brushstart() {
+ var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(), center, origin = d3.mouse(target), offset;
+ var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup);
+ if (d3.event.changedTouches) {
+ w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
+ } else {
+ w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
+ }
+ g.interrupt().selectAll("*").interrupt();
+ if (dragging) {
+ origin[0] = xExtent[0] - origin[0];
+ origin[1] = yExtent[0] - origin[1];
+ } else if (resizing) {
+ var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
+ offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ];
+ origin[0] = xExtent[ex];
+ origin[1] = yExtent[ey];
+ } else if (d3.event.altKey) center = origin.slice();
+ g.style("pointer-events", "none").selectAll(".resize").style("display", null);
+ d3.select("body").style("cursor", eventTarget.style("cursor"));
+ event_({
+ type: "brushstart"
+ });
+ brushmove();
+ function keydown() {
+ if (d3.event.keyCode == 32) {
+ if (!dragging) {
+ center = null;
+ origin[0] -= xExtent[1];
+ origin[1] -= yExtent[1];
+ dragging = 2;
+ }
+ d3_eventPreventDefault();
+ }
+ }
+ function keyup() {
+ if (d3.event.keyCode == 32 && dragging == 2) {
+ origin[0] += xExtent[1];
+ origin[1] += yExtent[1];
+ dragging = 0;
+ d3_eventPreventDefault();
+ }
+ }
+ function brushmove() {
+ var point = d3.mouse(target), moved = false;
+ if (offset) {
+ point[0] += offset[0];
+ point[1] += offset[1];
+ }
+ if (!dragging) {
+ if (d3.event.altKey) {
+ if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ];
+ origin[0] = xExtent[+(point[0] < center[0])];
+ origin[1] = yExtent[+(point[1] < center[1])];
+ } else center = null;
+ }
+ if (resizingX && move1(point, x, 0)) {
+ redrawX(g);
+ moved = true;
+ }
+ if (resizingY && move1(point, y, 1)) {
+ redrawY(g);
+ moved = true;
+ }
+ if (moved) {
+ redraw(g);
+ event_({
+ type: "brush",
+ mode: dragging ? "move" : "resize"
+ });
+ }
+ }
+ function move1(point, scale, i) {
+ var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max;
+ if (dragging) {
+ r0 -= position;
+ r1 -= size + position;
+ }
+ min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i];
+ if (dragging) {
+ max = (min += position) + size;
+ } else {
+ if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
+ if (position < min) {
+ max = min;
+ min = position;
+ } else {
+ max = position;
+ }
+ }
+ if (extent[0] != min || extent[1] != max) {
+ if (i) yExtentDomain = null; else xExtentDomain = null;
+ extent[0] = min;
+ extent[1] = max;
+ return true;
+ }
+ }
+ function brushend() {
+ brushmove();
+ g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
+ d3.select("body").style("cursor", null);
+ w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
+ dragRestore();
+ event_({
+ type: "brushend"
+ });
+ }
+ }
+ brush.x = function(z) {
+ if (!arguments.length) return x;
+ x = z;
+ resizes = d3_svg_brushResizes[!x << 1 | !y];
+ return brush;
+ };
+ brush.y = function(z) {
+ if (!arguments.length) return y;
+ y = z;
+ resizes = d3_svg_brushResizes[!x << 1 | !y];
+ return brush;
+ };
+ brush.clamp = function(z) {
+ if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null;
+ if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z;
+ return brush;
+ };
+ brush.extent = function(z) {
+ var x0, x1, y0, y1, t;
+ if (!arguments.length) {
+ if (x) {
+ if (xExtentDomain) {
+ x0 = xExtentDomain[0], x1 = xExtentDomain[1];
+ } else {
+ x0 = xExtent[0], x1 = xExtent[1];
+ if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
+ if (x1 < x0) t = x0, x0 = x1, x1 = t;
+ }
+ }
+ if (y) {
+ if (yExtentDomain) {
+ y0 = yExtentDomain[0], y1 = yExtentDomain[1];
+ } else {
+ y0 = yExtent[0], y1 = yExtent[1];
+ if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
+ if (y1 < y0) t = y0, y0 = y1, y1 = t;
+ }
+ }
+ return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
+ }
+ if (x) {
+ x0 = z[0], x1 = z[1];
+ if (y) x0 = x0[0], x1 = x1[0];
+ xExtentDomain = [ x0, x1 ];
+ if (x.invert) x0 = x(x0), x1 = x(x1);
+ if (x1 < x0) t = x0, x0 = x1, x1 = t;
+ if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ];
+ }
+ if (y) {
+ y0 = z[0], y1 = z[1];
+ if (x) y0 = y0[1], y1 = y1[1];
+ yExtentDomain = [ y0, y1 ];
+ if (y.invert) y0 = y(y0), y1 = y(y1);
+ if (y1 < y0) t = y0, y0 = y1, y1 = t;
+ if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ];
+ }
+ return brush;
+ };
+ brush.clear = function() {
+ if (!brush.empty()) {
+ xExtent = [ 0, 0 ], yExtent = [ 0, 0 ];
+ xExtentDomain = yExtentDomain = null;
+ }
+ return brush;
+ };
+ brush.empty = function() {
+ return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1];
+ };
+ return d3.rebind(brush, event, "on");
+ };
+ var d3_svg_brushCursor = {
+ n: "ns-resize",
+ e: "ew-resize",
+ s: "ns-resize",
+ w: "ew-resize",
+ nw: "nwse-resize",
+ ne: "nesw-resize",
+ se: "nwse-resize",
+ sw: "nesw-resize"
+ };
+ var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
+ var d3_time = d3.time = {}, d3_date = Date, d3_time_daySymbols = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ];
+ function d3_date_utc() {
+ this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
+ }
+ d3_date_utc.prototype = {
+ getDate: function() {
+ return this._.getUTCDate();
+ },
+ getDay: function() {
+ return this._.getUTCDay();
+ },
+ getFullYear: function() {
+ return this._.getUTCFullYear();
+ },
+ getHours: function() {
+ return this._.getUTCHours();
+ },
+ getMilliseconds: function() {
+ return this._.getUTCMilliseconds();
+ },
+ getMinutes: function() {
+ return this._.getUTCMinutes();
+ },
+ getMonth: function() {
+ return this._.getUTCMonth();
+ },
+ getSeconds: function() {
+ return this._.getUTCSeconds();
+ },
+ getTime: function() {
+ return this._.getTime();
+ },
+ getTimezoneOffset: function() {
+ return 0;
+ },
+ valueOf: function() {
+ return this._.valueOf();
+ },
+ setDate: function() {
+ d3_time_prototype.setUTCDate.apply(this._, arguments);
+ },
+ setDay: function() {
+ d3_time_prototype.setUTCDay.apply(this._, arguments);
+ },
+ setFullYear: function() {
+ d3_time_prototype.setUTCFullYear.apply(this._, arguments);
+ },
+ setHours: function() {
+ d3_time_prototype.setUTCHours.apply(this._, arguments);
+ },
+ setMilliseconds: function() {
+ d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
+ },
+ setMinutes: function() {
+ d3_time_prototype.setUTCMinutes.apply(this._, arguments);
+ },
+ setMonth: function() {
+ d3_time_prototype.setUTCMonth.apply(this._, arguments);
+ },
+ setSeconds: function() {
+ d3_time_prototype.setUTCSeconds.apply(this._, arguments);
+ },
+ setTime: function() {
+ d3_time_prototype.setTime.apply(this._, arguments);
+ }
+ };
+ var d3_time_prototype = Date.prototype;
+ var d3_time_formatDateTime = "%a %b %e %X %Y", d3_time_formatDate = "%m/%d/%Y", d3_time_formatTime = "%H:%M:%S";
+ var d3_time_days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], d3_time_dayAbbreviations = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], d3_time_monthAbbreviations = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
+ function d3_time_interval(local, step, number) {
+ function round(date) {
+ var d0 = local(date), d1 = offset(d0, 1);
+ return date - d0 < d1 - date ? d0 : d1;
+ }
+ function ceil(date) {
+ step(date = local(new d3_date(date - 1)), 1);
+ return date;
+ }
+ function offset(date, k) {
+ step(date = new d3_date(+date), k);
+ return date;
+ }
+ function range(t0, t1, dt) {
+ var time = ceil(t0), times = [];
+ if (dt > 1) {
+ while (time < t1) {
+ if (!(number(time) % dt)) times.push(new Date(+time));
+ step(time, 1);
+ }
+ } else {
+ while (time < t1) times.push(new Date(+time)), step(time, 1);
+ }
+ return times;
+ }
+ function range_utc(t0, t1, dt) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date_utc();
+ utc._ = t0;
+ return range(utc, t1, dt);
+ } finally {
+ d3_date = Date;
+ }
+ }
+ local.floor = local;
+ local.round = round;
+ local.ceil = ceil;
+ local.offset = offset;
+ local.range = range;
+ var utc = local.utc = d3_time_interval_utc(local);
+ utc.floor = utc;
+ utc.round = d3_time_interval_utc(round);
+ utc.ceil = d3_time_interval_utc(ceil);
+ utc.offset = d3_time_interval_utc(offset);
+ utc.range = range_utc;
+ return local;
+ }
+ function d3_time_interval_utc(method) {
+ return function(date, k) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date_utc();
+ utc._ = date;
+ return method(utc, k)._;
+ } finally {
+ d3_date = Date;
+ }
+ };
+ }
+ d3_time.year = d3_time_interval(function(date) {
+ date = d3_time.day(date);
+ date.setMonth(0, 1);
+ return date;
+ }, function(date, offset) {
+ date.setFullYear(date.getFullYear() + offset);
+ }, function(date) {
+ return date.getFullYear();
+ });
+ d3_time.years = d3_time.year.range;
+ d3_time.years.utc = d3_time.year.utc.range;
+ d3_time.day = d3_time_interval(function(date) {
+ var day = new d3_date(2e3, 0);
+ day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
+ return day;
+ }, function(date, offset) {
+ date.setDate(date.getDate() + offset);
+ }, function(date) {
+ return date.getDate() - 1;
+ });
+ d3_time.days = d3_time.day.range;
+ d3_time.days.utc = d3_time.day.utc.range;
+ d3_time.dayOfYear = function(date) {
+ var year = d3_time.year(date);
+ return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
+ };
+ d3_time_daySymbols.forEach(function(day, i) {
+ day = day.toLowerCase();
+ i = 7 - i;
+ var interval = d3_time[day] = d3_time_interval(function(date) {
+ (date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
+ return date;
+ }, function(date, offset) {
+ date.setDate(date.getDate() + Math.floor(offset) * 7);
+ }, function(date) {
+ var day = d3_time.year(date).getDay();
+ return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
+ });
+ d3_time[day + "s"] = interval.range;
+ d3_time[day + "s"].utc = interval.utc.range;
+ d3_time[day + "OfYear"] = function(date) {
+ var day = d3_time.year(date).getDay();
+ return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7);
+ };
+ });
+ d3_time.week = d3_time.sunday;
+ d3_time.weeks = d3_time.sunday.range;
+ d3_time.weeks.utc = d3_time.sunday.utc.range;
+ d3_time.weekOfYear = d3_time.sundayOfYear;
+ d3_time.format = d3_time_format;
+ function d3_time_format(template) {
+ var n = template.length;
+ function format(date) {
+ var string = [], i = -1, j = 0, c, p, f;
+ while (++i < n) {
+ if (template.charCodeAt(i) === 37) {
+ string.push(template.substring(j, i));
+ if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
+ if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
+ string.push(c);
+ j = i + 1;
+ }
+ }
+ string.push(template.substring(j, i));
+ return string.join("");
+ }
+ format.parse = function(string) {
+ var d = {
+ y: 1900,
+ m: 0,
+ d: 1,
+ H: 0,
+ M: 0,
+ S: 0,
+ L: 0,
+ Z: null
+ }, i = d3_time_parse(d, template, string, 0);
+ if (i != string.length) return null;
+ if ("p" in d) d.H = d.H % 12 + d.p * 12;
+ var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)();
+ if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
+ date.setFullYear(d.y, 0, 1);
+ date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
+ } else date.setFullYear(d.y, d.m, d.d);
+ date.setHours(d.H + Math.floor(d.Z / 100), d.M + d.Z % 100, d.S, d.L);
+ return localZ ? date._ : date;
+ };
+ format.toString = function() {
+ return template;
+ };
+ return format;
+ }
+ function d3_time_parse(date, template, string, j) {
+ var c, p, t, i = 0, n = template.length, m = string.length;
+ while (i < n) {
+ if (j >= m) return -1;
+ c = template.charCodeAt(i++);
+ if (c === 37) {
+ t = template.charAt(i++);
+ p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t];
+ if (!p || (j = p(date, string, j)) < 0) return -1;
+ } else if (c != string.charCodeAt(j++)) {
+ return -1;
+ }
+ }
+ return j;
+ }
+ function d3_time_formatRe(names) {
+ return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
+ }
+ function d3_time_formatLookup(names) {
+ var map = new d3_Map(), i = -1, n = names.length;
+ while (++i < n) map.set(names[i].toLowerCase(), i);
+ return map;
+ }
+ function d3_time_formatPad(value, fill, width) {
+ var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
+ return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
+ }
+ var d3_time_dayRe = d3_time_formatRe(d3_time_days), d3_time_dayLookup = d3_time_formatLookup(d3_time_days), d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations), d3_time_dayAbbrevLookup = d3_time_formatLookup(d3_time_dayAbbreviations), d3_time_monthRe = d3_time_formatRe(d3_time_months), d3_time_monthLookup = d3_time_formatLookup(d3_time_months), d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations), d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations), d3_time_percentRe = /^%/;
+ var d3_time_formatPads = {
+ "-": "",
+ _: " ",
+ "0": "0"
+ };
+ var d3_time_formats = {
+ a: function(d) {
+ return d3_time_dayAbbreviations[d.getDay()];
+ },
+ A: function(d) {
+ return d3_time_days[d.getDay()];
+ },
+ b: function(d) {
+ return d3_time_monthAbbreviations[d.getMonth()];
+ },
+ B: function(d) {
+ return d3_time_months[d.getMonth()];
+ },
+ c: d3_time_format(d3_time_formatDateTime),
+ d: function(d, p) {
+ return d3_time_formatPad(d.getDate(), p, 2);
+ },
+ e: function(d, p) {
+ return d3_time_formatPad(d.getDate(), p, 2);
+ },
+ H: function(d, p) {
+ return d3_time_formatPad(d.getHours(), p, 2);
+ },
+ I: function(d, p) {
+ return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
+ },
+ j: function(d, p) {
+ return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3);
+ },
+ L: function(d, p) {
+ return d3_time_formatPad(d.getMilliseconds(), p, 3);
+ },
+ m: function(d, p) {
+ return d3_time_formatPad(d.getMonth() + 1, p, 2);
+ },
+ M: function(d, p) {
+ return d3_time_formatPad(d.getMinutes(), p, 2);
+ },
+ p: function(d) {
+ return d.getHours() >= 12 ? "PM" : "AM";
+ },
+ S: function(d, p) {
+ return d3_time_formatPad(d.getSeconds(), p, 2);
+ },
+ U: function(d, p) {
+ return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2);
+ },
+ w: function(d) {
+ return d.getDay();
+ },
+ W: function(d, p) {
+ return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2);
+ },
+ x: d3_time_format(d3_time_formatDate),
+ X: d3_time_format(d3_time_formatTime),
+ y: function(d, p) {
+ return d3_time_formatPad(d.getFullYear() % 100, p, 2);
+ },
+ Y: function(d, p) {
+ return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
+ },
+ Z: d3_time_zone,
+ "%": function() {
+ return "%";
+ }
+ };
+ var d3_time_parsers = {
+ a: d3_time_parseWeekdayAbbrev,
+ A: d3_time_parseWeekday,
+ b: d3_time_parseMonthAbbrev,
+ B: d3_time_parseMonth,
+ c: d3_time_parseLocaleFull,
+ d: d3_time_parseDay,
+ e: d3_time_parseDay,
+ H: d3_time_parseHour24,
+ I: d3_time_parseHour24,
+ j: d3_time_parseDayOfYear,
+ L: d3_time_parseMilliseconds,
+ m: d3_time_parseMonthNumber,
+ M: d3_time_parseMinutes,
+ p: d3_time_parseAmPm,
+ S: d3_time_parseSeconds,
+ U: d3_time_parseWeekNumberSunday,
+ w: d3_time_parseWeekdayNumber,
+ W: d3_time_parseWeekNumberMonday,
+ x: d3_time_parseLocaleDate,
+ X: d3_time_parseLocaleTime,
+ y: d3_time_parseYear,
+ Y: d3_time_parseFullYear,
+ Z: d3_time_parseZone,
+ "%": d3_time_parseLiteralPercent
+ };
+ function d3_time_parseWeekdayAbbrev(date, string, i) {
+ d3_time_dayAbbrevRe.lastIndex = 0;
+ var n = d3_time_dayAbbrevRe.exec(string.substring(i));
+ return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekday(date, string, i) {
+ d3_time_dayRe.lastIndex = 0;
+ var n = d3_time_dayRe.exec(string.substring(i));
+ return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekdayNumber(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 1));
+ return n ? (date.w = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekNumberSunday(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i));
+ return n ? (date.U = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseWeekNumberMonday(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i));
+ return n ? (date.W = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseMonthAbbrev(date, string, i) {
+ d3_time_monthAbbrevRe.lastIndex = 0;
+ var n = d3_time_monthAbbrevRe.exec(string.substring(i));
+ return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseMonth(date, string, i) {
+ d3_time_monthRe.lastIndex = 0;
+ var n = d3_time_monthRe.exec(string.substring(i));
+ return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
+ }
+ function d3_time_parseLocaleFull(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
+ }
+ function d3_time_parseLocaleDate(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
+ }
+ function d3_time_parseLocaleTime(date, string, i) {
+ return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
+ }
+ function d3_time_parseFullYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 4));
+ return n ? (date.y = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
+ }
+ function d3_time_parseZone(date, string, i) {
+ return /^[+-]\d{4}$/.test(string = string.substring(i, i + 5)) ? (date.Z = +string,
+ i + 5) : -1;
+ }
+ function d3_time_expandYear(d) {
+ return d + (d > 68 ? 1900 : 2e3);
+ }
+ function d3_time_parseMonthNumber(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
+ }
+ function d3_time_parseDay(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.d = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseDayOfYear(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 3));
+ return n ? (date.j = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseHour24(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.H = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseMinutes(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.M = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseSeconds(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 2));
+ return n ? (date.S = +n[0], i + n[0].length) : -1;
+ }
+ function d3_time_parseMilliseconds(date, string, i) {
+ d3_time_numberRe.lastIndex = 0;
+ var n = d3_time_numberRe.exec(string.substring(i, i + 3));
+ return n ? (date.L = +n[0], i + n[0].length) : -1;
+ }
+ var d3_time_numberRe = /^\s*\d+/;
+ function d3_time_parseAmPm(date, string, i) {
+ var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase());
+ return n == null ? -1 : (date.p = n, i);
+ }
+ var d3_time_amPmLookup = d3.map({
+ am: 0,
+ pm: 1
+ });
+ function d3_time_zone(d) {
+ var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(abs(z) / 60), zm = abs(z) % 60;
+ return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
+ }
+ function d3_time_parseLiteralPercent(date, string, i) {
+ d3_time_percentRe.lastIndex = 0;
+ var n = d3_time_percentRe.exec(string.substring(i, i + 1));
+ return n ? i + n[0].length : -1;
+ }
+ d3_time_format.utc = d3_time_formatUtc;
+ function d3_time_formatUtc(template) {
+ var local = d3_time_format(template);
+ function format(date) {
+ try {
+ d3_date = d3_date_utc;
+ var utc = new d3_date();
+ utc._ = date;
+ return local(utc);
+ } finally {
+ d3_date = Date;
+ }
+ }
+ format.parse = function(string) {
+ try {
+ d3_date = d3_date_utc;
+ var date = local.parse(string);
+ return date && date._;
+ } finally {
+ d3_date = Date;
+ }
+ };
+ format.toString = local.toString;
+ return format;
+ }
+ var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ");
+ d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
+ function d3_time_formatIsoNative(date) {
+ return date.toISOString();
+ }
+ d3_time_formatIsoNative.parse = function(string) {
+ var date = new Date(string);
+ return isNaN(date) ? null : date;
+ };
+ d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
+ d3_time.second = d3_time_interval(function(date) {
+ return new d3_date(Math.floor(date / 1e3) * 1e3);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 1e3);
+ }, function(date) {
+ return date.getSeconds();
+ });
+ d3_time.seconds = d3_time.second.range;
+ d3_time.seconds.utc = d3_time.second.utc.range;
+ d3_time.minute = d3_time_interval(function(date) {
+ return new d3_date(Math.floor(date / 6e4) * 6e4);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 6e4);
+ }, function(date) {
+ return date.getMinutes();
+ });
+ d3_time.minutes = d3_time.minute.range;
+ d3_time.minutes.utc = d3_time.minute.utc.range;
+ d3_time.hour = d3_time_interval(function(date) {
+ var timezone = date.getTimezoneOffset() / 60;
+ return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
+ }, function(date, offset) {
+ date.setTime(date.getTime() + Math.floor(offset) * 36e5);
+ }, function(date) {
+ return date.getHours();
+ });
+ d3_time.hours = d3_time.hour.range;
+ d3_time.hours.utc = d3_time.hour.utc.range;
+ d3_time.month = d3_time_interval(function(date) {
+ date = d3_time.day(date);
+ date.setDate(1);
+ return date;
+ }, function(date, offset) {
+ date.setMonth(date.getMonth() + offset);
+ }, function(date) {
+ return date.getMonth();
+ });
+ d3_time.months = d3_time.month.range;
+ d3_time.months.utc = d3_time.month.utc.range;
+ function d3_time_scale(linear, methods, format) {
+ function scale(x) {
+ return linear(x);
+ }
+ scale.invert = function(x) {
+ return d3_time_scaleDate(linear.invert(x));
+ };
+ scale.domain = function(x) {
+ if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
+ linear.domain(x);
+ return scale;
+ };
+ function tickMethod(extent, count) {
+ var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target);
+ return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) {
+ return d / 31536e6;
+ }), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i];
+ }
+ scale.nice = function(interval, skip) {
+ var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval);
+ if (method) interval = method[0], skip = method[1];
+ function skipped(date) {
+ return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length;
+ }
+ return scale.domain(d3_scale_nice(domain, skip > 1 ? {
+ floor: function(date) {
+ while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1);
+ return date;
+ },
+ ceil: function(date) {
+ while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1);
+ return date;
+ }
+ } : interval));
+ };
+ scale.ticks = function(interval, skip) {
+ var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ {
+ range: interval
+ }, skip ];
+ if (method) interval = method[0], skip = method[1];
+ return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip);
+ };
+ scale.tickFormat = function() {
+ return format;
+ };
+ scale.copy = function() {
+ return d3_time_scale(linear.copy(), methods, format);
+ };
+ return d3_scale_linearRebind(scale, linear);
+ }
+ function d3_time_scaleDate(t) {
+ return new Date(t);
+ }
+ function d3_time_scaleFormat(formats) {
+ return function(date) {
+ var i = formats.length - 1, f = formats[i];
+ while (!f[1](date)) f = formats[--i];
+ return f[0](date);
+ };
+ }
+ var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
+ var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ];
+ var d3_time_scaleLocalFormats = [ [ d3_time_format("%Y"), d3_true ], [ d3_time_format("%B"), function(d) {
+ return d.getMonth();
+ } ], [ d3_time_format("%b %d"), function(d) {
+ return d.getDate() != 1;
+ } ], [ d3_time_format("%a %d"), function(d) {
+ return d.getDay() && d.getDate() != 1;
+ } ], [ d3_time_format("%I %p"), function(d) {
+ return d.getHours();
+ } ], [ d3_time_format("%I:%M"), function(d) {
+ return d.getMinutes();
+ } ], [ d3_time_format(":%S"), function(d) {
+ return d.getSeconds();
+ } ], [ d3_time_format(".%L"), function(d) {
+ return d.getMilliseconds();
+ } ] ];
+ var d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats);
+ d3_time_scaleLocalMethods.year = d3_time.year;
+ d3_time.scale = function() {
+ return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
+ };
+ var d3_time_scaleMilliseconds = {
+ range: function(start, stop, step) {
+ return d3.range(+start, +stop, step).map(d3_time_scaleDate);
+ },
+ floor: d3_identity,
+ ceil: d3_identity
+ };
+ var d3_time_scaleUTCMethods = d3_time_scaleLocalMethods.map(function(m) {
+ return [ m[0].utc, m[1] ];
+ });
+ var d3_time_scaleUTCFormats = [ [ d3_time_formatUtc("%Y"), d3_true ], [ d3_time_formatUtc("%B"), function(d) {
+ return d.getUTCMonth();
+ } ], [ d3_time_formatUtc("%b %d"), function(d) {
+ return d.getUTCDate() != 1;
+ } ], [ d3_time_formatUtc("%a %d"), function(d) {
+ return d.getUTCDay() && d.getUTCDate() != 1;
+ } ], [ d3_time_formatUtc("%I %p"), function(d) {
+ return d.getUTCHours();
+ } ], [ d3_time_formatUtc("%I:%M"), function(d) {
+ return d.getUTCMinutes();
+ } ], [ d3_time_formatUtc(":%S"), function(d) {
+ return d.getUTCSeconds();
+ } ], [ d3_time_formatUtc(".%L"), function(d) {
+ return d.getUTCMilliseconds();
+ } ] ];
+ var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats);
+ d3_time_scaleUTCMethods.year = d3_time.year.utc;
+ d3_time.scale.utc = function() {
+ return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat);
+ };
+ d3.text = d3_xhrType(function(request) {
+ return request.responseText;
+ });
+ d3.json = function(url, callback) {
+ return d3_xhr(url, "application/json", d3_json, callback);
+ };
+ function d3_json(request) {
+ return JSON.parse(request.responseText);
+ }
+ d3.html = function(url, callback) {
+ return d3_xhr(url, "text/html", d3_html, callback);
+ };
+ function d3_html(request) {
+ var range = d3_document.createRange();
+ range.selectNode(d3_document.body);
+ return range.createContextualFragment(request.responseText);
+ }
+ d3.xml = d3_xhrType(function(request) {
+ return request.responseXML;
+ });
+ return d3;
+}();
\ No newline at end of file
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.min.js b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.min.js
new file mode 100755
index 0000000..dba5993
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.3.13.min.js
@@ -0,0 +1,5 @@
+d3=function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function u(){}function i(){}function o(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function a(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=fa.length;r>e;++e){var u=fa[e]+t;if(u in n)return u}}function c(){}function s(){}function l(n){function t(){for(var t,r=e,u=-1,i=r.length;++u<i;)(t=r[u].on)&&t.apply(this,arguments);return n}var e=[],r=new u;return t.on=function(t,u){var i,o=r.get(t);return arguments.length<2?o&&o.on:(o&&(o.on=null,e=e.slice(0,i=e.indexOf(o)).concat(e.slice(i+1)),r.remove(t)),u&&e.push(r.set(t,{on:u})),n)},t}function f(){Bo.event.preventDefault()}function h(){for(var n,t=Bo.event;n=t.sourceEvent;)t=n;return t}function g(n){for(var t=new s,e=0,r
=arguments.length;++e<r;)t[arguments[e]]=l(t);return t.of=function(e,r){return function(u){try{var i=u.sourceEvent=Bo.event;u.target=n,Bo.event=u,t[u.type].apply(e,r)}finally{Bo.event=i}}},t}function p(n){return ga(n,ya),n}function v(n){return"function"==typeof n?n:function(){return pa(n,this)}}function d(n){return"function"==typeof n?n:function(){return va(n,this)}}function m(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function u(){this.setAttribute(n,t)}function i(){this.setAttributeNS(n.space,n.local,t)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=Bo.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?a:o:n.local?i:u}function y(n){return n.trim().replace(/\s+/g," ")}function x(n){return new RegExp("(?:^|\\s+)"+Bo.requote(n
)+"(?:\\s+|$)","g")}function M(n){return n.trim().split(/^|\s+/)}function _(n,t){function e(){for(var e=-1;++e<u;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<u;)n[e](this,r)}n=M(n).map(b);var u=n.length;return"function"==typeof t?r:e}function b(n){var t=x(n);return function(e,r){if(u=e.classList)return r?u.add(n):u.remove(n);var u=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(u)||e.setAttribute("class",y(u+" "+n))):e.setAttribute("class",y(u.replace(t," ")))}}function w(n,t,e){function r(){this.style.removeProperty(n)}function u(){this.style.setProperty(n,t,e)}function i(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?i:u}function S(n,t){function e(){delete this[n]}function r(){this[n]=t}function u(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?u:r}function k(n){return"function"==typeof n?n:(n=Bo.ns.qu
alify(n)).local?function(){return this.ownerDocument.createElementNS(n.space,n.local)}:function(){return this.ownerDocument.createElementNS(this.namespaceURI,n)}}function E(n){return{__data__:n}}function A(n){return function(){return ma(this,n)}}function C(n){return arguments.length||(n=Bo.ascending),function(t,e){return t&&e?n(t.__data__,e.__data__):!t-!e}}function N(n,t){for(var e=0,r=n.length;r>e;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function L(n){return ga(n,Ma),n}function T(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t<c;);return o}}function q(){var n=this.__transition__;n&&++n.active}function z(n,t,e){function r(){var t=this[o];t&&(this.removeEventListener(n,t,t.$),delete this[o])}function u(){var u=s(t,Jo(arguments));r.call(this),this.addEventListener(n,this[o]=u,u.$=e),u._=t}function i(){var t,e=new RegExp("^__on([^.]+)"+Bo.requote(n)+"$");for(var r in this)if(t=r.ma
tch(e)){var u=this[r];this.removeEventListener(t[1],u,u.$),delete this[r]}}var o="__on"+n,a=n.indexOf("."),s=R;a>0&&(n=n.substring(0,a));var l=ba.get(n);return l&&(n=l,s=D),a?t?u:r:t?c:i}function R(n,t){return function(e){var r=Bo.event;Bo.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{Bo.event=r}}}function D(n,t){var e=R(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function P(){var n=".dragsuppress-"+ ++Sa,t="click"+n,e=Bo.select(Qo).on("touchmove"+n,f).on("dragstart"+n,f).on("selectstart"+n,f);if(wa){var r=Ko.style,u=r[wa];r[wa]="none"}return function(i){function o(){e.on(t,null)}e.on(n,null),wa&&(r[wa]=u),i&&(e.on(t,function(){f(),o()},!0),setTimeout(o,0))}}function U(n,t){t.changedTouches&&(t=t.changedTouches[0]);var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>ka&&(Qo.scrollX||Qo.scrollY)){e=Bo.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:
0,padding:0,border:"none"},"important");var u=e[0][0].getScreenCTM();ka=!(u.f||u.e),e.remove()}return ka?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var i=n.getBoundingClientRect();return[t.clientX-i.left-n.clientLeft,t.clientY-i.top-n.clientTop]}function j(n){return n>0?1:0>n?-1:0}function H(n){return n>1?0:-1>n?Ea:Math.acos(n)}function F(n){return n>1?Ca:-1>n?-Ca:Math.asin(n)}function O(n){return((n=Math.exp(n))-1/n)/2}function Y(n){return((n=Math.exp(n))+1/n)/2}function I(n){return((n=Math.exp(2*n))-1)/(n+1)}function Z(n){return(n=Math.sin(n/2))*n}function V(){}function X(n,t,e){return new $(n,t,e)}function $(n,t,e){this.h=n,this.s=t,this.l=e}function B(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t
):e+t-e*t,i=2*e-o,at(u(n+120),u(n),u(n-120))}function W(n,t,e){return new J(n,t,e)}function J(n,t,e){this.h=n,this.c=t,this.l=e}function G(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),K(e,Math.cos(n*=Ta)*t,Math.sin(n)*t)}function K(n,t,e){return new Q(n,t,e)}function Q(n,t,e){this.l=n,this.a=t,this.b=e}function nt(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=et(u)*Ya,r=et(r)*Ia,i=et(i)*Za,at(ut(3.2404542*u-1.5371385*r-.4985314*i),ut(-.969266*u+1.8760108*r+.041556*i),ut(.0556434*u-.2040259*r+1.0572252*i))}function tt(n,t,e){return n>0?W(Math.atan2(e,t)*qa,Math.sqrt(t*t+e*e),n):W(0/0,0/0,n)}function et(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function rt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function ut(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function it(n){return at(n>>16,255&n>>8,255&n)}function ot(n){return it(n)+""}function at(n,t,e){return new ct(n,t,e)}function ct(n,t,e){this.r=n,this.g=t,this.b=e}function s
t(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function lt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(pt(u[0]),pt(u[1]),pt(u[2]))}return(i=$a.get(n))?t(i.r,i.g,i.b):(null!=n&&"#"===n.charAt(0)&&(4===n.length?(o=n.charAt(1),o+=o,a=n.charAt(2),a+=a,c=n.charAt(3),c+=c):7===n.length&&(o=n.substring(1,3),a=n.substring(3,5),c=n.substring(5,7)),o=parseInt(o,16),a=parseInt(a,16),c=parseInt(c,16)),t(o,a,c))}function ft(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),X(r,u,c)}function ht(n,t,e){n=gt(n),t=gt(t),e=gt(e);var r=rt((.4124564*n+.3575761*t+.1804375*e)/Ya),u=rt((.2126729*n+.7151522*t+.072175*e)/Ia),i=rt((.0193339*n+.119192*t+.9503041*e)/Za);return K(116*u
-16,500*(r-u),200*(u-i))}function gt(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function pt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function vt(n){return"function"==typeof n?n:function(){return n}}function dt(n){return n}function mt(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),yt(t,e,n,r)}}function yt(n,t,e,r){function u(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return o.error.call(i,r),void 0}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=Bo.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,s=null;return!Qo.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=Bo.event;Bo.event=n;try{o.progress.call(i,c)}finally{Bo.event=t}},i.header=function
(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(s=n,i):s},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(Jo(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var l in a)c.setRequestHeader(l,a[l]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=s&&(c.responseType=s),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},Bo.rebind(i,o,"on"),null==r?i:i.get(xt(r))}function xt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function Mt(){var n=_t(),t=bt()-n;t>24?(isFinite(t)&&(clearTimeout(Ga)
,Ga=setTimeout(Mt,t)),Ja=0):(Ja=1,Qa(Mt))}function _t(){var n=Date.now();for(Ka=Ba;Ka;)n>=Ka.t&&(Ka.f=Ka.c(n-Ka.t)),Ka=Ka.n;return n}function bt(){for(var n,t=Ba,e=1/0;t;)t.f?t=n?n.n=t.n:Ba=t.n:(t.t<e&&(e=t.t),t=(n=t).n);return Wa=n,e}function wt(n,t){var e=Math.pow(10,3*ca(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function St(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function kt(n){return n+""}function Et(){}function At(n,t,e){var r=e.s=n+t,u=r-n,i=r-u;e.t=n-i+(t-u)}function Ct(n,t){n&&fc.hasOwnProperty(n.type)&&fc[n.type](n,t)}function Nt(n,t,e){var r,u=-1,i=n.length-e;for(t.lineStart();++u<i;)r=n[u],t.point(r[0],r[1],r[2]);t.lineEnd()}function Lt(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)Nt(n[e],t,1);t.polygonEnd()}function Tt(){function n(n,t){n*=Ta,t=t*Ta/2+Ea/4;var e=n-r,o=Math.cos(t),a=Math.sin(t),c=i*a,s=u*o+c*Math.cos(e),l=c*Math.sin(e);gc.add(Math.atan2(l,s)),r=n,u=o,i=a}var t,e,r,u,i;pc.point=function(o,
a){pc.point=n,r=(t=o)*Ta,u=Math.cos(a=(e=a)*Ta/2+Ea/4),i=Math.sin(a)},pc.lineEnd=function(){n(t,e)}}function qt(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function zt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function Rt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Dt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function Pt(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Ut(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function jt(n){return[Math.atan2(n[1],n[0]),F(n[2])]}function Ht(n,t){return ca(n[0]-t[0])<Na&&ca(n[1]-t[1])<Na}function Ft(n,t){n*=Ta;var e=Math.cos(t*=Ta);Ot(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function Ot(n,t,e){++vc,mc+=(n-mc)/vc,yc+=(t-yc)/vc,xc+=(e-xc)/vc}function Yt(){function n(n,u){n*=Ta;var i=Math.cos(u*=Ta),o=i*Math.cos(n),a=i*Math.sin(n),c=Math.sin(u),s=Math.atan2(Math.sqrt((s=e*c-r*a)*s+(s=r*o-t*c)*s+(s=t*a-e*o)*s),t*o+e*a+r*c);dc+=s,Mc+=s*(t+(t=o)),_
c+=s*(e+(e=a)),bc+=s*(r+(r=c)),Ot(t,e,r)}var t,e,r;Ec.point=function(u,i){u*=Ta;var o=Math.cos(i*=Ta);t=o*Math.cos(u),e=o*Math.sin(u),r=Math.sin(i),Ec.point=n,Ot(t,e,r)}}function It(){Ec.point=Ft}function Zt(){function n(n,t){n*=Ta;var e=Math.cos(t*=Ta),o=e*Math.cos(n),a=e*Math.sin(n),c=Math.sin(t),s=u*c-i*a,l=i*o-r*c,f=r*a-u*o,h=Math.sqrt(s*s+l*l+f*f),g=r*o+u*a+i*c,p=h&&-H(g)/h,v=Math.atan2(h,g);wc+=p*s,Sc+=p*l,kc+=p*f,dc+=v,Mc+=v*(r+(r=o)),_c+=v*(u+(u=a)),bc+=v*(i+(i=c)),Ot(r,u,i)}var t,e,r,u,i;Ec.point=function(o,a){t=o,e=a,Ec.point=n,o*=Ta;var c=Math.cos(a*=Ta);r=c*Math.cos(o),u=c*Math.sin(o),i=Math.sin(a),Ot(r,u,i)},Ec.lineEnd=function(){n(t,e),Ec.lineEnd=It,Ec.point=Ft}}function Vt(){return!0}function Xt(n,t,e,r,u){var i=[],o=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(Ht(e,r)){u.lineStart();for(var a=0;t>a;++a)u.point((e=n[a])[0],e[1]);return u.lineEnd(),void 0}var c=new Bt(e,n,null,!0),s=new Bt(e,null,c,!1);c.o=s,i.push(c),o.push(s),c=
new Bt(r,n,null,!1),s=new Bt(r,null,c,!0),c.o=s,i.push(c),o.push(s)}}),o.sort(t),$t(i),$t(o),i.length){for(var a=0,c=e,s=o.length;s>a;++a)o[a].e=c=!c;for(var l,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;l=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,s=l.length;s>a;++a)u.point((f=l[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){l=g.p.z;for(var a=l.length-1;a>=0;--a)u.point((f=l[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,l=g.z,p=!p}while(!g.v);u.lineEnd()}}}function $t(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r<t;)u.n=e=n[r],e.p=u,u=e;u.n=e=n[0],e.p=u}}function Bt(n,t,e,r){this.x=n,this.z=t,this.o=e,this.e=r,this.v=!1,this.n=this.p=null}function Wt(n,t,e,r){return function(u,i){function o(t,e){var r=u(t,e);n(t=r[0],e=r[1])&&i.point(t,e)}function a(n,t){var e=u(n,t);d.point(e[0],e[1])}function c(){y.point=a,d.lineStart()}function s(){y.point=o,d.lineEnd()}function l(n,t){v.push([n,t]);var e=u(n,t);M.point(e[0],e[1])}function f(){M.lineStar
t(),v=[]}function h(){l(v[0][0],v[0][1]),M.lineEnd();var n,t=M.clean(),e=x.buffer(),r=e.length;if(v.pop(),p.push(v),v=null,r){if(1&t){n=e[0];var u,r=n.length-1,o=-1;for(i.lineStart();++o<r;)i.point((u=n[o])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Jt))}}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:s,polygonStart:function(){y.point=l,y.lineStart=f,y.lineEnd=h,g=[],p=[],i.polygonStart()},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=s,g=Bo.merge(g);var n=Qt(m,p);g.length?Xt(g,Kt,n,e,i):n&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},x=Gt(),M=t(x);return y}}function Jt(n){return n.length>1}function Gt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:c,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length
>1&&t.push(t.pop().concat(t.shift()))}}}function Kt(n,t){return((n=n.x)[0]<0?n[1]-Ca-Na:Ca-n[1])-((t=t.x)[0]<0?t[1]-Ca-Na:Ca-t[1])}function Qt(n,t){var e=n[0],r=n[1],u=[Math.sin(e),-Math.cos(e),0],i=0,o=0;gc.reset();for(var a=0,c=t.length;c>a;++a){var s=t[a],l=s.length;if(l)for(var f=s[0],h=f[0],g=f[1]/2+Ea/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===l&&(d=0),n=s[d];var m=n[0],y=n[1]/2+Ea/4,x=Math.sin(y),M=Math.cos(y),_=m-h,b=ca(_)>Ea,w=p*x;if(gc.add(Math.atan2(w*Math.sin(_),v*M+w*Math.cos(_))),i+=b?_+(_>=0?Aa:-Aa):_,b^h>=e^m>=e){var S=Rt(qt(f),qt(n));Ut(S);var k=Rt(u,S);Ut(k);var E=(b^_>=0?-1:1)*F(k[2]);(r>E||r===E&&(S[0]||S[1]))&&(o+=b^_>=0?1:-1)}if(!d++)break;h=m,p=x,v=M,f=n}}return(-Na>i||Na>i&&0>gc)^1&o}function ne(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?Ea:-Ea,c=ca(i-e);ca(c-Ea)<Na?(n.point(e,r=(r+o)/2>0?Ca:-Ca),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=Ea&&(ca(e-u)<Na&&(
e-=u*Na),ca(i-a)<Na&&(i-=a*Na),r=te(e,r,i,o),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),t=0),n.point(e=i,r=o),u=a},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function te(n,t,e,r){var u,i,o=Math.sin(n-e);return ca(o)>Na?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function ee(n,t,e,r){var u;if(null==n)u=e*Ca,r.point(-Ea,u),r.point(0,u),r.point(Ea,u),r.point(Ea,0),r.point(Ea,-u),r.point(0,-u),r.point(-Ea,-u),r.point(-Ea,0),r.point(-Ea,u);else if(ca(n[0]-t[0])>Na){var i=n[0]<t[0]?Ea:-Ea;u=e*i/2,r.point(-i,u),r.point(0,u),r.point(i,u)}else r.point(t[0],t[1])}function re(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,s,l;return{lineStart:function(){s=c=!1,l=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?Ea:-Ea),h):0;if(!e&&(s=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(Ht(e,g)||Ht(p,g))&&(p[0]+=Na,p[1]+=Na,v=t(p[0],p[1]))),v!==c)l=0,v
?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(l=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&Ht(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return l|(s&&c)<<1}}}function r(n,t,e){var r=qt(n),u=qt(t),o=[1,0,0],a=Rt(r,u),c=zt(a,a),s=a[0],l=c-s*s;if(!l)return!e&&n;var f=i*c/l,h=-i*s/l,g=Rt(o,a),p=Pt(o,f),v=Pt(a,h);Dt(p,v);var d=g,m=zt(p,d),y=zt(d,d),x=m*m-y*(zt(p,p)-1);if(!(0>x)){var M=Math.sqrt(x),_=Pt(d,(-m-M)/y);if(Dt(_,p),_=jt(_),!e)return _;var b,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(b=w,w=S,S=b);var A=S-w,C=ca(A-Ea)<Na,N=C||Na>A;if(!C&&k>E&&(b=k,k=E,E=b),N?C?k+E>0^_[1]<(ca(_[0]-w)<Na?k:E):k<=_[1]&&_[1]<=E:A>Ea^(w<=_[0]&&_[0]<=S)){var L=Pt(d,(-m+M)/y);return Dt(L,p),[_,jt(L)]}}}function u(t,e){var r=o?n:Ea-n,u=0;r
eturn-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ca(i)>Na,c=Te(n,6*Ta);return Wt(t,e,c,o?[0,-n]:[-Ea,n-Ea])}function ue(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,s=o.y,l=a.x,f=a.y,h=0,g=1,p=l-c,v=f-s;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-s,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-s,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:s+h*v}),1>g&&(u.b={x:c+g*p,y:s+g*v}),u}}}}}}function ie(n,t,e,r){function u(r,u){return ca(r[0]-n)<Na?u>0?0:3:ca(r[0]-e)<Na?u>0?2:1:ca(r[1]-t)<Na?u>0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(
n){for(var t=0,e=m.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=m[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&s(l,i,n)>0&&++t:i[1]<=r&&s(l,i,n)<0&&--t,l=i;return 0!==t}function s(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(e[0]-n[0])*(t[1]-n[1])}function l(i,a,c,s){var l=0,f=0;if(null==i||(l=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do s.point(0===l||3===l?n:e,l>1?r:t);while((l=(l+c+4)%4)!==f)}else s.point(a[0],a[1])}function f(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function h(n,t){f(n,t)&&a.point(n,t)}function g(){L.point=v,m&&m.push(y=[]),k=!0,S=!1,b=w=0/0}function p(){d&&(v(x,M),_&&S&&C.rejoin(),d.push(C.buffer())),L.point=h,S&&a.lineEnd()}function v(n,t){n=Math.max(-Cc,Math.min(Cc,n)),t=Math.max(-Cc,Math.min(Cc,t));var e=f(n,t);if(m&&y.push([n,t]),k)x=n,M=t,_=e,k=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&S)a.point(n,t);else{var r={a:{x:b,y:w},b:{x:n,y:t}};N(r)?(S||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),E=!1):e&&(a.lineStart(),a.point(n,t),E=!1)
}b=n,w=t,S=e}var d,m,y,x,M,_,b,w,S,k,E,A=a,C=Gt(),N=ue(n,t,e,r),L={point:h,lineStart:g,lineEnd:p,polygonStart:function(){a=C,d=[],m=[],E=!0},polygonEnd:function(){a=A,d=Bo.merge(d);var t=c([n,r]),e=E&&t,u=d.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Xt(d,i,t,l,a),a.polygonEnd()),d=m=y=null}};return L}}function oe(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function ae(n){var t=0,e=Ea/3,r=we(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*Ea/180,e=n[1]*Ea/180):[180*(t/Ea),180*(e/Ea)]},u}function ce(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,F((i-(n*n+e*e)*u*u)/(2*u))]},e}function se(){function n(n,t){Lc+=u*n-r*t,r=n,
u=t}var t,e,r,u;Dc.point=function(i,o){Dc.point=n,t=r=i,e=u=o},Dc.lineEnd=function(){n(t,e)}}function le(n,t){Tc>n&&(Tc=n),n>zc&&(zc=n),qc>t&&(qc=t),t>Rc&&(Rc=t)}function fe(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=he(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=he(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function he(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function ge(n,t){mc+=n,yc+=t,++xc}function pe(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);Mc+=o*(t+n)/2,_c+=o*(e+r)/2,bc+=o,ge(t=n,e=r)}var t,e;Uc.point=function(r,u){Uc.point=n,ge(t=r,e=u)}}function ve(){Uc.point=ge}function de(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt
(e*e+i*i);Mc+=o*(r+n)/2,_c+=o*(u+t)/2,bc+=o,o=u*n-r*t,wc+=o*(r+n),Sc+=o*(u+t),kc+=3*o,ge(r=n,u=t)}var t,e,r,u;Uc.point=function(i,o){Uc.point=n,ge(t=r=i,e=u=o)},Uc.lineEnd=function(){n(t,e)}}function me(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,o,0,Aa)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:c};return a}function ye(n){function t(n){return(a?r:e)(n)}function e(t){return _e(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){x=0/0,S.point=i,t.lineStart()}function i(e,r){var i=qt([e,r]),o=n(e,r);u(x,M,y,_,b,w,x=o[0],M=o[1],y=e,_=i[0],b=i[1],w=i[2],a,t),t.point(x,M)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=s,S.lineEnd=l}function s
(n,t){i(f=n,h=t),g=x,p=M,v=_,d=b,m=w,S.point=i}function l(){u(x,M,y,_,b,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,x,M,_,b,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,s,l,f,h,g,p,v,d,m){var y=l-t,x=f-e,M=y*y+x*x;if(M>4*i&&d--){var _=a+g,b=c+p,w=s+v,S=Math.sqrt(_*_+b*b+w*w),k=Math.asin(w/=S),E=ca(ca(w)-1)<Na||ca(r-h)<Na?(r+h)/2:Math.atan2(b,_),A=n(E,k),C=A[0],N=A[1],L=C-t,T=N-e,q=x*L-y*T;(q*q/M>i||ca((y*L+x*T)/M-.5)>.3||o>a*g+c*p+s*v)&&(u(t,e,r,a,c,s,C,N,E,_/=S,b/=S,w,d,m),m.point(C,N),u(C,N,E,_,b,w,l,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Ta),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function xe(n){var t=ye(function(t,e){return n([t*qa,e*qa])});return function(n){return Se(t(n))}}function Me(n){this.stream=n}function _e(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:fu
nction(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function be(n){return we(function(){return n})()}function we(n){function t(n){return n=a(n[0]*Ta,n[1]*Ta),[n[0]*h+c,s-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(s-n[1])/h),n&&[n[0]*qa,n[1]*qa]}function r(){a=oe(o=Ae(m,y,x),i);var n=i(v,d);return c=g-n[0]*h,s=p+n[1]*h,u()}function u(){return l&&(l.valid=!1,l=null),t}var i,o,a,c,s,l,f=ye(function(n,t){return n=i(n,t),[n[0]*h+c,s-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,y=0,x=0,M=Ac,_=dt,b=null,w=null;return t.stream=function(n){return l&&(l.valid=!1),l=Se(M(o,f(_(n)))),l.valid=!0,l},t.clipAngle=function(n){return arguments.length?(M=null==n?(b=n,Ac):re((b=+n)*Ta),u()):b},t.clipExtent=function(n){return arguments.length?(w=n,_=n?ie(n[0][0],n[0][1],n[1][0],n[1][1]):dt,u()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[
0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Ta,d=n[1]%360*Ta,r()):[v*qa,d*qa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Ta,y=n[1]%360*Ta,x=n.length>2?n[2]%360*Ta:0,r()):[m*qa,y*qa,x*qa]},Bo.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function Se(n){return _e(n,function(t,e){n.point(t*Ta,e*Ta)})}function ke(n,t){return[n,t]}function Ee(n,t){return[n>Ea?n-Aa:-Ea>n?n+Aa:n,t]}function Ae(n,t,e){return n?t||e?oe(Ne(n),Le(t,e)):Ne(n):t||e?Le(t,e):Ee}function Ce(n){return function(t,e){return t+=n,[t>Ea?t-Aa:-Ea>t?t+Aa:t,e]}}function Ne(n){var t=Ce(n);return t.invert=Ce(-n),t}function Le(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s*r+a*u;return[Math.atan2(c*i-l*o,a*r-s*u),F(l*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,s=Math.sin(t),l=s
*i-c*o;return[Math.atan2(c*i+s*o,a*r+l*u),F(l*r-a*u)]},e}function Te(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=qe(e,u),i=qe(e,i),(o>0?i>u:u>i)&&(u+=o*Aa)):(u=n+o*Aa,i=n-.5*c);for(var s,l=u;o>0?l>i:i>l;l-=c)a.point((s=jt([e,-r*Math.cos(l),-r*Math.sin(l)]))[0],s[1])}}function qe(n,t){var e=qt(t);e[0]-=n,Ut(e);var r=H(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Na)%(2*Math.PI)}function ze(n,t,e){var r=Bo.range(n,t-Na,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function Re(n,t,e){var r=Bo.range(n,t-Na,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function De(n){return n.source}function Pe(n){return n.target}function Ue(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),s=u*Math.sin(n),l=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(Z(r-t)+u*o*Z(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*l,u=e*s+t*f,o=e*i+
t*a;return[Math.atan2(u,r)*qa,Math.atan2(o,Math.sqrt(r*r+u*u))*qa]}:function(){return[n*qa,t*qa]};return p.distance=h,p}function je(){function n(n,u){var i=Math.sin(u*=Ta),o=Math.cos(u),a=ca((n*=Ta)-t),c=Math.cos(a);jc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Hc.point=function(u,i){t=u*Ta,e=Math.sin(i*=Ta),r=Math.cos(i),Hc.point=n},Hc.lineEnd=function(){Hc.point=Hc.lineEnd=c}}function He(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function Fe(n,t){function e(n,t){var e=ca(ca(t)-Ca)<Na?0:o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(Ea/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t
,r=j(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ca]},e):Ye}function Oe(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ca(u)<Na?ke:(e.invert=function(n,t){var e=i-t;return[Math.atan2(n,e)/u,i-j(u)*Math.sqrt(n*n+e*e)]},e)}function Ye(n,t){return[n,Math.log(Math.tan(Ea/4+t/2))]}function Ie(n){var t,e=be(n),r=e.scale,u=e.translate,i=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=u.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var o=i.apply(e,arguments);if(o===e){if(t=null==n){var a=Ea*r(),c=u();i([[c[0]-a,c[1]-a],[c[0]+a,c[1]+a]])}}else t&&(o=null);return o},e.clipExtent(null)}function Ze(n,t){return[Math.log(Math.tan(Ea/4+t/2)),-n]}function Ve(n){return n[0]}function Xe(n){return n[1]}function $e(n,t,e,r){var u,i,o,a,c,s,l
;return u=r[n],i=u[0],o=u[1],u=r[t],a=u[0],c=u[1],u=r[e],s=u[0],l=u[1],(l-o)*(a-i)-(c-o)*(s-i)>0}function Be(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function We(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],s=e[1],l=t[1]-c,f=r[1]-s,h=(a*(c-s)-f*(u-i))/(f*o-a*l);return[u+h*o,c+h*l]}function Je(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Ge(){yr(this),this.edge=this.site=this.circle=null}function Ke(n){var t=Gc.pop()||new Ge;return t.site=n,t}function Qe(n){sr(n),Bc.remove(n),Gc.push(n),yr(n)}function nr(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Qe(n);for(var c=i;c.circle&&ca(e-c.circle.x)<Na&&ca(r-c.circle.cy)<Na;)i=c.P,a.unshift(c),Qe(c),c=i;a.unshift(c),sr(c);for(var s=o;s.circle&&ca(e-s.circle.x)<Na&&ca(r-s.circle.cy)<Na;)o=s.N,a.push(s),Qe(s),s=o;a.push(s),sr(s);var l,f=a.length;for(l=1;f>l;++l)s=a[l],c=a[l-1],vr(s.edge,c.site,s.site,u);c=a[0],s=a[f-1],s.edge=gr(c.site,s.site,null,u),cr(c),cr(s)}functi
on tr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Bc._;a;)if(r=er(a,o)-i,r>Na)a=a.L;else{if(u=i-rr(a,o),!(u>Na)){r>-Na?(t=a.P,e=a):u>-Na?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Ke(n);if(Bc.insert(t,c),t||e){if(t===e)return sr(t),e=Ke(t.site),Bc.insert(c,e),c.edge=e.edge=gr(t.site,c.site),cr(t),cr(e),void 0;if(!e)return c.edge=gr(t.site,c.site),void 0;sr(t),sr(e);var s=t.site,l=s.x,f=s.y,h=n.x-l,g=n.y-f,p=e.site,v=p.x-l,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,x=v*v+d*d,M={x:(d*y-g*x)/m+l,y:(h*x-v*y)/m+f};vr(e.edge,s,p,M),c.edge=gr(s,n,null,M),e.edge=gr(n,p,null,M),cr(t),cr(e)}}function er(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,s=c-t;if(!s)return a;var l=a-r,f=1/i-1/s,h=l/s;return f?(-h+Math.sqrt(h*h-2*f*(l*l/(-2*s)-c+s/2+u-i/2)))/f+r:(r+a)/2}function rr(n,t){var e=n.N;if(e)return er(e,t);var r=n.site;return r.y===t?r.x:1/0}function ur(n){this.site=n,this.edges=[]}function ir(n){for(var t,e,r,u,i,o,a,c,s,l,f=n[0][0],h
=n[1][0],g=n[0][1],p=n[1][1],v=$c,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)l=a[o].end(),r=l.x,u=l.y,s=a[++o%c].start(),t=s.x,e=s.y,(ca(r-t)>Na||ca(u-e)>Na)&&(a.splice(o,0,new dr(pr(i.site,l,ca(r-f)<Na&&p-u>Na?{x:f,y:ca(t-f)<Na?e:p}:ca(u-p)<Na&&h-r>Na?{x:ca(e-p)<Na?t:h,y:p}:ca(r-h)<Na&&u-g>Na?{x:h,y:ca(t-h)<Na?e:g}:ca(u-g)<Na&&r-f>Na?{x:ca(e-g)<Na?t:f,y:g}:null),i.site,null)),++c)}function or(n,t){return t.angle-n.angle}function ar(){yr(this),this.x=this.y=this.arc=this.site=this.cy=null}function cr(n){var t=n.P,e=n.N;if(t&&e){var r=t.site,u=n.site,i=e.site;if(r!==i){var o=u.x,a=u.y,c=r.x-o,s=r.y-a,l=i.x-o,f=i.y-a,h=2*(c*f-s*l);if(!(h>=-La)){var g=c*c+s*s,p=l*l+f*f,v=(f*g-s*p)/h,d=(c*p-l*g)/h,f=d+a,m=Kc.pop()||new ar;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,x=Jc._;x;)if(m.y<x.y||m.y===x.y&&m.x<=x.x){if(!x.L){y=x.P;break}x=x.L}else{if(!x.R){y=x;break}x=x.R}Jc.insert(y,m),y||(Wc=m)}}}}function sr(n){var
t=n.circle;t&&(t.P||(Wc=t.N),Jc.remove(t),Kc.push(t),yr(t),n.circle=null)}function lr(n){for(var t,e=Xc,r=ue(n[0][0],n[0][1],n[1][0],n[1][1]),u=e.length;u--;)t=e[u],(!fr(t,n)||!r(t)||ca(t.a.x-t.b.x)<Na&&ca(t.a.y-t.b.y)<Na)&&(t.a=t.b=null,e.splice(u,1))}function fr(n,t){var e=n.b;if(e)return!0;var r,u,i=n.a,o=t[0][0],a=t[1][0],c=t[0][1],s=t[1][1],l=n.l,f=n.r,h=l.x,g=l.y,p=f.x,v=f.y,d=(h+p)/2,m=(g+v)/2;
+if(v===g){if(o>d||d>=a)return;if(h>p){if(i){if(i.y>=s)return}else i={x:d,y:c};e={x:d,y:s}}else{if(i){if(i.y<c)return}else i={x:d,y:s};e={x:d,y:c}}}else if(r=(h-p)/(v-g),u=m-r*d,-1>r||r>1)if(h>p){if(i){if(i.y>=s)return}else i={x:(c-u)/r,y:c};e={x:(s-u)/r,y:s}}else{if(i){if(i.y<c)return}else i={x:(s-u)/r,y:s};e={x:(c-u)/r,y:c}}else if(v>g){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.x<o)return}else i={x:a,y:r*a+u};e={x:o,y:r*o+u}}return n.a=i,n.b=e,!0}function hr(n,t){this.l=n,this.r=t,this.a=this.b=null}function gr(n,t,e,r){var u=new hr(n,t);return Xc.push(u),e&&vr(u,n,t,e),r&&vr(u,t,n,r),$c[n.i].edges.push(new dr(u,n,t)),$c[t.i].edges.push(new dr(u,t,n)),u}function pr(n,t,e){var r=new hr(n,null);return r.a=t,r.b=e,Xc.push(r),r}function vr(n,t,e,r){n.a||n.b?n.l===e?n.b=r:n.a=r:(n.a=r,n.l=t,n.r=e)}function dr(n,t,e){var r=n.a,u=n.b;this.edge=n,this.site=t,this.angle=e?Math.atan2(e.y-t.y,e.x-t.x):n.l===t?Math.atan2(u.x-r.x,r.y-u.y):Math.atan2(r.x
-u.x,u.y-r.y)}function mr(){this._=null}function yr(n){n.U=n.C=n.L=n.R=n.P=n.N=null}function xr(n,t){var e=t,r=t.R,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.R=r.L,e.R&&(e.R.U=e),r.L=e}function Mr(n,t){var e=t,r=t.L,u=e.U;u?u.L===e?u.L=r:u.R=r:n._=r,r.U=u,e.U=r,e.L=r.R,e.L&&(e.L.U=e),r.R=e}function _r(n){for(;n.L;)n=n.L;return n}function br(n,t){var e,r,u,i=n.sort(wr).pop();for(Xc=[],$c=new Array(n.length),Bc=new mr,Jc=new mr;;)if(u=Wc,i&&(!u||i.y<u.y||i.y===u.y&&i.x<u.x))(i.x!==e||i.y!==r)&&($c[i.i]=new ur(i),tr(i),e=i.x,r=i.y),i=n.pop();else{if(!u)break;nr(u.arc)}t&&(lr(t),ir(t));var o={cells:$c,edges:Xc};return Bc=Jc=Xc=$c=null,o}function wr(n,t){return t.y-n.y||t.x-n.x}function Sr(n,t,e){return(n.x-e.x)*(t.y-n.y)-(n.x-t.x)*(e.y-n.y)}function kr(n){return n.x}function Er(n){return n.y}function Ar(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}function Cr(n,t,e,r,u,i){if(!n(t,e,r,u,i)){var o=.5*(e+u),a=.5*(r+i),c=t.nodes;c[0]&&Cr(n,c[0],e,r,o,a),c[1]&&Cr(n,c[1],o
,r,u,a),c[2]&&Cr(n,c[2],e,a,o,i),c[3]&&Cr(n,c[3],o,a,u,i)}}function Nr(n,t){n=Bo.rgb(n),t=Bo.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+st(Math.round(e+i*n))+st(Math.round(r+o*n))+st(Math.round(u+a*n))}}function Lr(n,t){var e,r={},u={};for(e in n)e in t?r[e]=zr(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function Tr(n,t){return t-=n=+n,function(e){return n+t*e}}function qr(n,t){var e,r,u,i,o,a=0,c=0,s=[],l=[];for(n+="",t+="",ns.lastIndex=0,r=0;e=ns.exec(t);++r)e.index&&s.push(t.substring(a,c=e.index)),l.push({i:s.length,x:e[0]}),s.push(null),a=ns.lastIndex;for(a<t.length&&s.push(t.substring(a)),r=0,i=l.length;(e=ns.exec(n))&&i>r;++r)if(o=l[r],o.x==e[0]){if(o.i)if(null==s[o.i+1])for(s[o.i-1]+=o.x,s.splice(o.i,1),u=r+1;i>u;++u)l[u].i--;else for(s[o.i-1]+=o.x+s[o.i+1],s.splice(o.i,2),u=r+1;i>u;++u)l[u].i-=2;else if(null==s[o.i+1])s[o.i]=o.x;else for(s[o.i]=o.x+s[o.i+1],s.splice(o
.i+1,1),u=r+1;i>u;++u)l[u].i--;l.splice(r,1),i--,r--}else o.x=Tr(parseFloat(e[0]),parseFloat(o.x));for(;i>r;)o=l.pop(),null==s[o.i+1]?s[o.i]=o.x:(s[o.i]=o.x+s[o.i+1],s.splice(o.i+1,1)),i--;return 1===s.length?null==s[0]?(o=l[0].x,function(n){return o(n)+""}):function(){return t}:function(n){for(r=0;i>r;++r)s[(o=l[r]).i]=o.x(n);return s.join("")}}function zr(n,t){for(var e,r=Bo.interpolators.length;--r>=0&&!(e=Bo.interpolators[r](n,t)););return e}function Rr(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(zr(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Dr(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function Pr(n){return function(t){return 1-n(1-t)}}function Ur(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function jr(n){return n*n}function Hr(n){return n*n*n}function Fr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5
>n?e:3*(n-t)+e-.75)}function Or(n){return function(t){return Math.pow(t,n)}}function Yr(n){return 1-Math.cos(n*Ca)}function Ir(n){return Math.pow(2,10*(n-1))}function Zr(n){return 1-Math.sqrt(1-n*n)}function Vr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/Aa*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*Aa/t)}}function Xr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function $r(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Br(n,t){n=Bo.hcl(n),t=Bo.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return G(e+i*n,r+o*n,u+a*n)+""}}function Wr(n,t){n=Bo.hsl(n),t=Bo.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i
>180?i-=360:-180>i&&(i+=360),function(n){return B(e+i*n,r+o*n,u+a*n)+""}}function Jr(n,t){n=Bo.lab(n),t=Bo.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return nt(e+i*n,r+o*n,u+a*n)+""}}function Gr(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Kr(n){var t=[n.a,n.b],e=[n.c,n.d],r=nu(t),u=Qr(t,e),i=nu(tu(e,t,-u))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,u*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*qa,this.translate=[n.e,n.f],this.scale=[r,i],this.skew=i?Math.atan2(u,i)*qa:0}function Qr(n,t){return n[0]*t[0]+n[1]*t[1]}function nu(n){var t=Math.sqrt(Qr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function tu(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function eu(n,t){var e,r=[],u=[],i=Bo.transform(n),o=Bo.transform(t),a=i.translate,c=o.translate,s=i.rotate,l=o.rotate,f=i.skew,h=o.skew,g=i.scale,p=o.scale;return a[0]!=c[0]||a[1]!=c[1]?(r.push("translate(",null,",",null,")"),u.push({i:1,x:Tr(a[0],c[0])},{i:3,x:Tr(a[1
],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),s!=l?(s-l>180?l+=360:l-s>180&&(s+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:Tr(s,l)})):l&&r.push(r.pop()+"rotate("+l+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:Tr(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:Tr(g[0],p[0])},{i:e-2,x:Tr(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i<e;)r[(t=u[i]).i]=t.x(n);return r.join("")}}function ru(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function uu(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function iu(n){for(var t=n.source,e=n.target,r=au(t,e),u=[t];t!==r;)t=t.parent,u.push(t);for(var i=u.length;e!==r;)u.splice(i,0,e),e=e.parent;return u}function ou(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function au(n,t){if(n===
t)return n;for(var e=ou(n),r=ou(t),u=e.pop(),i=r.pop(),o=null;u===i;)o=u,u=e.pop(),i=r.pop();return o}function cu(n){n.fixed|=2}function su(n){n.fixed&=-7}function lu(n){n.fixed|=4,n.px=n.x,n.py=n.y}function fu(n){n.fixed&=-5}function hu(n,t,e){var r=0,u=0;if(n.charge=0,!n.leaf)for(var i,o=n.nodes,a=o.length,c=-1;++c<a;)i=o[c],null!=i&&(hu(i,t,e),n.charge+=i.charge,r+=i.charge*i.cx,u+=i.charge*i.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var s=t*e[n.point.index];n.charge+=n.pointCharge=s,r+=s*n.point.x,u+=s*n.point.y}n.cx=r/n.charge,n.cy=u/n.charge}function gu(n,t){return Bo.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=mu,n}function pu(n){return n.children}function vu(n){return n.value}function du(n,t){return t.value-n.value}function mu(n){return Bo.merge(n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function yu(n){return n.x}function xu(n){return n.y}function Mu(n,t,e){n.y0=t,n.y=e}f
unction _u(n){return Bo.range(n.length)}function bu(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function wu(n){for(var t,e=1,r=0,u=n[0][1],i=n.length;i>e;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function Su(n){return n.reduce(ku,0)}function ku(n,t){return n+t[1]}function Eu(n,t){return Au(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function Au(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function Cu(n){return[Bo.min(n),Bo.max(n)]}function Nu(n,t){return n.parent==t.parent?1:2}function Lu(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function Tu(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function qu(n,t){var e=n.children;if(e&&(u=e.length))for(var r,u,i=-1;++i<u;)t(r=qu(e[i],t),n)>0&&(n=r);return n}function zu(n,t){return n.x-t.x}function Ru(n,t){return t.x-n.x}function Du(n,t){return n.depth-t.depth}function Pu(n,t){function e(n,r){var u=n.children;if(u&&(o=u.length))for(var i,o,a=null,c=-1;++c<o;)i=u
[c],e(i,a),a=i;t(n,r)}e(n,null)}function Uu(n){for(var t,e=0,r=0,u=n.children,i=u.length;--i>=0;)t=u[i]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function ju(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function Hu(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function Fu(n,t){return n.value-t.value}function Ou(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Yu(n,t){n._pack_next=t,t._pack_prev=n}function Iu(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Zu(n){function t(n){l=Math.min(n.x-n.r,l),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(s=e.length)){var e,r,u,i,o,a,c,s,l=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(Vu),r=e[0],r.x=-r.r,r.y=0,t(r),s>1&&(u=e[1],u.x=u.r,u.y=0,t(u),s>2))for(i=e[2],Bu(r,u,i),t(i),Ou(r,i),r._pack_prev=i,Ou(i,u),u=r._pack_next,o=3;s>o;o++){Bu(r,u,i=e[o
]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(Iu(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!Iu(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.r<r.r?Yu(r,u=a):Yu(r=c,u),o--):(Ou(r,i),u=i,t(i))}var m=(l+f)/2,y=(h+g)/2,x=0;for(o=0;s>o;o++)i=e[o],i.x-=m,i.y-=y,x=Math.max(x,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=x,e.forEach(Xu)}}function Vu(n){n._pack_next=n._pack_prev=n}function Xu(n){delete n._pack_next,delete n._pack_prev}function $u(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i<o;)$u(u[i],t,e,r)}function Bu(n,t,e){var r=n.r+e.r,u=t.x-n.x,i=t.y-n.y;if(r&&(u||i)){var o=t.r+e.r,a=u*u+i*i;o*=o,r*=r;var c=.5+(r-o)/(2*a),s=Math.sqrt(Math.max(0,2*o*(r+a)-(r-=a)*r-o*o))/(2*a);e.x=n.x+c*u+s*i,e.y=n.y+c*i-s*u}else e.x=n.x+r,e.y=n.y}function Wu(n){return 1+Bo.max(n,function(n){return n.y})}function Ju(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Gu(n){var t=n.children;return t&&t.length?Gu
(t[0]):n}function Ku(n){var t,e=n.children;return e&&(t=e.length)?Ku(e[t-1]):n}function Qu(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function ni(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function ti(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function ei(n){return n.rangeExtent?n.rangeExtent():ti(n.range())}function ri(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function ui(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function ii(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ls}function oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]<n[0]&&(n=n.slice().reverse(),t=t.slice().reverse());++o<=a;)u.push(e(n[o-1],n[o])),i.push(r(t[o-1],t[o]));return function(t){var e=Bo.bisect(n,t,1,a)-1;return i[e](u[e](t))}}func
tion ai(n,t,e,r){function u(){var u=Math.min(n.length,t.length)>2?oi:ri,c=r?uu:ru;return o=u(n,t,c,e),a=u(t,n,c,zr),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Gr)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return fi(n,t)},i.tickFormat=function(t,e){return hi(n,t,e)},i.nice=function(t){return si(n,t),u()},i.copy=function(){return ai(n,t,e,r)},u()}function ci(n,t){return Bo.rebind(n,t,"range","rangeRound","interpolate","clamp")}function si(n,t){return ui(n,ii(li(n,t)[2]))}function li(n,t){null==t&&(t=10);var e=ti(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.flo
or(e[1]/u)*u+.5*u,e[2]=u,e}function fi(n,t){return Bo.range.apply(Bo,li(n,t))}function hi(n,t,e){var r=li(n,t);return Bo.format(e?e.replace(ic,function(n,t,e,u,i,o,a,c,s,l){return[t,e,u,i,o,a,c,s||"."+pi(l,r),l].join("")}):",."+gi(r[2])+"f")}function gi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function pi(n,t){var e=gi(t[2]);return n in fs?Math.abs(e-gi(Math.max(Math.abs(t[0]),Math.abs(t[1]))))+ +("e"!==n):e-2*("%"===n)}function vi(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=ui(r.map(u),e?Math:gs);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=ti(r),o=[],a=n[0],c=n[1],s=Math.floor(u(a)),l=Math.cei
l(u(c)),f=t%1?2:t;if(isFinite(l-s)){if(e){for(;l>s;s++)for(var h=1;f>h;h++)o.push(i(s)*h);o.push(i(s))}else for(o.push(i(s));s++<l;)for(var h=f-1;h>0;h--)o.push(i(s)*h);for(s=0;o[s]<a;s++);for(l=o.length;o[l-1]>c;l--);o=o.slice(s,l)}return o},o.tickFormat=function(n,t){if(!arguments.length)return hs;arguments.length<2?t=hs:"function"!=typeof t&&(t=Bo.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return vi(n.copy(),t,e,r)},ci(o,n)}function di(n,t,e){function r(t){return n(u(t))}var u=mi(t),i=mi(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return fi(e,n)},r.tickFormat=function(n,t){return hi(e,n,t)},r.nice=function(n){return r.domain(si(e,n))},r.exponent=function(o){return arguments.length?(u=mi(t=o),i=mi(1/t),n.domain(e.map(u)),r):t},r.cop
y=function(){return di(n.copy(),t,e)},ci(r,n)}function mi(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function yi(n,t){function e(e){return o[((i.get(e)||"range"===t.t&&i.set(e,n.push(e)))-1)%o.length]}function r(t,e){return Bo.range(n.length).map(function(n){return t+e*n})}var i,o,a;return e.domain=function(r){if(!arguments.length)return n;n=[],i=new u;for(var o,a=-1,c=r.length;++a<c;)i.has(o=r[a])||i.set(o,n.push(o));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(o=n,a=0,t={t:"range",a:arguments},e):o},e.rangePoints=function(u,i){arguments.length<2&&(i=0);var c=u[0],s=u[1],l=(s-c)/(Math.max(1,n.length-1)+i);return o=r(n.length<2?(c+s)/2:c+l*i/2,l),a=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=(f-l)/(n.length-i+2*c);return o=r(l+h*c,h),s&&o.reverse(),a=h*(1-i),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(
u,i,c){arguments.length<2&&(i=0),arguments.length<3&&(c=i);var s=u[1]<u[0],l=u[s-0],f=u[1-s],h=Math.floor((f-l)/(n.length-i+2*c)),g=f-l-(n.length-i)*h;return o=r(l+Math.round(g/2),h),s&&o.reverse(),a=Math.round(h*(1-i)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return a},e.rangeExtent=function(){return ti(t.a[0])},e.copy=function(){return yi(n,t)},e.domain(n)}function xi(n,t){function e(){var e=0,i=t.length;for(u=[];++e<i;)u[e-1]=Bo.quantile(n,e/i);return r}function r(n){return isNaN(n=+n)?void 0:t[Bo.bisect(u,n)]}var u;return r.domain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(Bo.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return u},r.invertExtent=function(e){return e=t.indexOf(e),0>e?[0/0,0/0]:[e>0?u[e-1]:n[0],e<u.length?u[e]:n[n.length-1]]},r.copy=function(){return xi(n,t)},e()}function Mi(n,t,e){function r(t){return e[Math.max(0,Math.min(o,Math.floor(i*(t-n)
)))]}function u(){return i=e.length/(t-n),o=e.length-1,r}var i,o;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],u()):[n,t]},r.range=function(n){return arguments.length?(e=n,u()):e},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return Mi(n,t,e)},u()}function _i(n,t){function e(e){return e>=e?t[Bo.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return _i(n,t)},e}function bi(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return fi(n,t)},t.tickFormat=function(t,e){return hi(n,t,e)},t.copy=function(){return bi(n)},t}function wi(n){return n.innerRadius}function Si(n){return n.outerRadius}function ki(n){return n.startAngle}function Ei(n){return n.en
dAngle}function Ai(n){function t(t){function o(){s.push("M",i(n(l),a))}for(var c,s=[],l=[],f=-1,h=t.length,g=vt(e),p=vt(r);++f<h;)u.call(this,c=t[f],f)?l.push([+g.call(this,c,f),+p.call(this,c,f)]):l.length&&(o(),l=[]);return l.length&&o(),s.length?s.join(""):null}var e=Ve,r=Xe,u=Vt,i=Ci,o=i.key,a=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(u=n,t):u},t.interpolate=function(n){return arguments.length?(o="function"==typeof n?i=n:(i=Ms.get(n)||Ci).key,t):o},t.tension=function(n){return arguments.length?(a=n,t):a},t}function Ci(n){return n.join("L")}function Ni(n){return Ci(n)+"Z"}function Li(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&u.push("H",r[0]),u.join("")}function Ti(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("V",(r=n[t])[1],"H",r[0]);return u.join("")}function qi(n){fo
r(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t<e;)u.push("H",(r=n[t])[0],"V",r[1]);return u.join("")}function zi(n,t){return n.length<4?Ci(n):n[1]+Pi(n.slice(1,n.length-1),Ui(n,t))}function Ri(n,t){return n.length<3?Ci(n):n[0]+Pi((n.push(n[0]),n),Ui([n[n.length-2]].concat(n,[n[1]]),t))}function Di(n,t){return n.length<3?Ci(n):n[0]+Pi(n,Ui(n,t))}function Pi(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return Ci(n);var e=n.length!=t.length,r="",u=n[0],i=n[1],o=t[0],a=o,c=1;if(e&&(r+="Q"+(i[0]-2*o[0]/3)+","+(i[1]-2*o[1]/3)+","+i[0]+","+i[1],u=n[1],c=2),t.length>1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var s=2;s<t.length;s++,c++)i=n[c],a=t[s],r+="S"+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1]}if(e){var l=n[c];r+="Q"+(i[0]+2*a[0]/3)+","+(i[1]+2*a[1]/3)+","+l[0]+","+l[1]}return r}function Ui(n,t){for(var e,r=[],u=(1-t)/2,i=n[0],o=n[1],a=1,c=n.length;++a<c;)e=i,i=o,o=n[a],r.push([u*(o[0]-e[0])
,u*(o[1]-e[1])]);return r}function ji(n){if(n.length<3)return Ci(n);var t=1,e=n.length,r=n[0],u=r[0],i=r[1],o=[u,u,u,(r=n[1])[0]],a=[i,i,i,r[1]],c=[u,",",i,"L",Yi(ws,o),",",Yi(ws,a)];for(n.push(n[e-1]);++t<=e;)r=n[t],o.shift(),o.push(r[0]),a.shift(),a.push(r[1]),Ii(c,o,a);return n.pop(),c.push("L",r),c.join("")}function Hi(n){if(n.length<4)return Ci(n);for(var t,e=[],r=-1,u=n.length,i=[0],o=[0];++r<3;)t=n[r],i.push(t[0]),o.push(t[1]);for(e.push(Yi(ws,i)+","+Yi(ws,o)),--r;++r<u;)t=n[r],i.shift(),i.push(t[0]),o.shift(),o.push(t[1]),Ii(e,i,o);return e.join("")}function Fi(n){for(var t,e,r=-1,u=n.length,i=u+4,o=[],a=[];++r<4;)e=n[r%u],o.push(e[0]),a.push(e[1]);for(t=[Yi(ws,o),",",Yi(ws,a)],--r;++r<i;)e=n[r%u],o.shift(),o.push(e[0]),a.shift(),a.push(e[1]),Ii(t,o,a);return t.join("")}function Oi(n,t){var e=n.length-1;if(e)for(var r,u,i=n[0][0],o=n[0][1],a=n[e][0]-i,c=n[e][1]-o,s=-1;++s<=e;)r=n[s],u=s/e,r[0]=t*r[0]+(1-t)*(i+u*a),r[1]=t*r[1]+(1-t)*(o+u*c);return ji(n)}function Yi(n,
t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function Ii(n,t,e){n.push("C",Yi(_s,t),",",Yi(_s,e),",",Yi(bs,t),",",Yi(bs,e),",",Yi(ws,t),",",Yi(ws,e))}function Zi(n,t){return(t[1]-n[1])/(t[0]-n[0])}function Vi(n){for(var t=0,e=n.length-1,r=[],u=n[0],i=n[1],o=r[0]=Zi(u,i);++t<e;)r[t]=(o+(o=Zi(u=i,i=n[t+1])))/2;return r[t]=o,r}function Xi(n){for(var t,e,r,u,i=[],o=Vi(n),a=-1,c=n.length-1;++a<c;)t=Zi(n[a],n[a+1]),ca(t)<Na?o[a]=o[a+1]=0:(e=o[a]/t,r=o[a+1]/t,u=e*e+r*r,u>9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function $i(n){return n.length<3?Ci(n):n[0]+Pi(n,Xi(n))}function Bi(n){for(var t,e,r,u=-1,i=n.length;++u<i;)t=n[u],e=t[0],r=t[1]+ys,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function Wi(n){function t(t){function c(){v.push("M",a(n(m),f),l,s(n(d.reverse()),f),"Z")}for(var h,g,p,v=[],d=[],m=[],y=-1,x=t.length,M=vt(e),_=vt(u),b=e===r?function(){retu
rn g}:vt(r),w=u===i?function(){return p}:vt(i);++y<x;)o.call(this,h=t[y],y)?(d.push([g=+M.call(this,h,y),p=+_.call(this,h,y)]),m.push([+b.call(this,h,y),+w.call(this,h,y)])):d.length&&(c(),d=[],m=[]);return d.length&&c(),v.length?v.join(""):null}var e=Ve,r=Ve,u=0,i=Xe,o=Vt,a=Ci,c=a.key,s=a,l="L",f=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(u=i=n,t):i},t.y0=function(n){return arguments.length?(u=n,t):u},t.y1=function(n){return arguments.length?(i=n,t):i},t.defined=function(n){return arguments.length?(o=n,t):o},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?a=n:(a=Ms.get(n)||Ci).key,s=a.reverse||a,l=a.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(f=n,t):f},t}function Ji(n){return n.radius}function Gi(n){return[n.x,n.y]}function Ki(n){return function(){var t=n.apply(t
his,arguments),e=t[0],r=t[1]+ys;return[e*Math.cos(r),e*Math.sin(r)]}}function Qi(){return 64}function no(){return"circle"}function to(n){var t=Math.sqrt(n/Ea);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function eo(n,t){return ga(n,Ns),n.id=t,n}function ro(n,t,e,r){var u=n.id;return N(n,"function"==typeof e?function(n,i,o){n.__transition__[u].tween.set(t,r(e.call(n,n.__data__,i,o)))}:(e=r(e),function(n){n.__transition__[u].tween.set(t,e)}))}function uo(n){return null==n&&(n=""),function(){this.textContent=n}}function io(n,t,e,r){var i=n.__transition__||(n.__transition__={active:0,count:0}),o=i[e];if(!o){var a=r.time;o=i[e]={tween:new u,time:a,ease:r.ease,delay:r.delay,duration:r.duration},++i.count,Bo.timer(function(r){function u(r){return i.active>e?s():(i.active=e,o.event&&o.event.start.call(n,l,t),o.tween.forEach(function(e,r){(r=r.call(n,l,t))&&v.push(r)}),Bo.timer(function(){return p.c=c(r||1)?Vt:c,1},0,a),void 0)}function c(r){if(i.active!==e
)return s();for(var u=r/g,a=f(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,l,t),s()):void 0}function s(){return--i.count?delete i[e]:delete n.__transition__,1}var l=n.__data__,f=o.ease,h=o.delay,g=o.duration,p=Ka,v=[];return p.t=h+a,r>=h?u(r-h):(p.c=u,void 0)},0,a)}}function oo(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}function ao(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function co(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function so(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new Ps(e-1)),1),e}function i(n,e){return t(n=new Ps(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{Ps=co;var r=new co;return r._=n,o(r,t,e)}finally{Ps=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n
.utc=lo(n);return c.floor=c,c.round=lo(r),c.ceil=lo(u),c.offset=lo(i),c.range=a,n}function lo(n){return function(t,e){try{Ps=co;var r=new co;return r._=t,n(r,e)._}finally{Ps=Date}}}function fo(n){function t(t){for(var r,u,i,o=[],a=-1,c=0;++a<e;)37===n.charCodeAt(a)&&(o.push(n.substring(c,a)),null!=(u=tl[r=n.charAt(++a)])&&(r=n.charAt(++a)),(i=el[r])&&(r=i(t,null==u?"e"===r?" ":"0":u)),o.push(r),c=a+1);return o.push(n.substring(c,a)),o.join("")}var e=n.length;return t.parse=function(t){var e={y:1900,m:0,d:1,H:0,M:0,S:0,L:0,Z:null},r=ho(e,n,t,0);if(r!=t.length)return null;"p"in e&&(e.H=e.H%12+12*e.p);var u=null!=e.Z&&Ps!==co,i=new(u?co:Ps);return"j"in e?i.setFullYear(e.y,0,e.j):"w"in e&&("W"in e||"U"in e)?(i.setFullYear(e.y,0,1),i.setFullYear(e.y,0,"W"in e?(e.w+6)%7+7*e.W-(i.getDay()+5)%7:e.w+7*e.U-(i.getDay()+6)%7)):i.setFullYear(e.y,e.m,e.d),i.setHours(e.H+Math.floor(e.Z/100),e.M+e.Z%100,e.S,e.L),u?i._:i},t.toString=function(){return n},t}function ho(n,t,e,r){for(var u,i,o,a
=0,c=t.length,s=e.length;c>a;){if(r>=s)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=rl[o in tl?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function go(n){return new RegExp("^(?:"+n.map(Bo.requote).join("|")+")","i")}function po(n){for(var t=new u,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function vo(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function mo(n,t,e){Bs.lastIndex=0;var r=Bs.exec(t.substring(e));return r?(n.w=Ws.get(r[0].toLowerCase()),e+r[0].length):-1}function yo(n,t,e){Xs.lastIndex=0;var r=Xs.exec(t.substring(e));return r?(n.w=$s.get(r[0].toLowerCase()),e+r[0].length):-1}function xo(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Mo(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function _o(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring
(e));return r?(n.W=+r[0],e+r[0].length):-1}function bo(n,t,e){Ks.lastIndex=0;var r=Ks.exec(t.substring(e));return r?(n.m=Qs.get(r[0].toLowerCase()),e+r[0].length):-1}function wo(n,t,e){Js.lastIndex=0;var r=Js.exec(t.substring(e));return r?(n.m=Gs.get(r[0].toLowerCase()),e+r[0].length):-1}function So(n,t,e){return ho(n,el.c.toString(),t,e)}function ko(n,t,e){return ho(n,el.x.toString(),t,e)}function Eo(n,t,e){return ho(n,el.X.toString(),t,e)}function Ao(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Co(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.y=Lo(+r[0]),e+r[0].length):-1}function No(n,t,e){return/^[+-]\d{4}$/.test(t=t.substring(e,e+5))?(n.Z=+t,e+5):-1}function Lo(n){return n+(n>68?1900:2e3)}function To(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function qo(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].leng
th):-1}function zo(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function Ro(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function Do(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function Po(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function Uo(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function jo(n,t,e){var r=il.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}function Ho(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(ca(t)/60),u=ca(t)%60;return e+vo(r,"0",2)+vo(u,"0",2)}function Fo(n,t,e){nl.lastIndex=0;var r=nl.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function Oo(n){function t(n){try{Ps=co;var t=new Ps;return t._=n,e(t)}finally{Ps=Date}}var e=fo(n);return t.parse=function(n){try{Ps=co;var t=e.par
se(n);return t&&t._}finally{Ps=Date}},t.toString=e.toString,t}function Yo(n){return n.toISOString()}function Io(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=Bo.bisect(al,u);return i==al.length?[t.year,li(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/al[i-1]<al[i]/u?i-1:i]:[fl,li(n,e)[2]]}return r.invert=function(t){return Zo(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r):n.domain().map(Zo)},r.nice=function(n,t){function e(e){return!isNaN(e)&&!n.range(e,Zo(+e+1),t).length}var i=r.domain(),o=ti(i),a=null==n?u(o,10):"number"==typeof n&&u(o,n);return a&&(n=a[0],t=a[1]),r.domain(ui(i,t>1?{floor:function(t){for(;e(t=n.floor(t));)t=Zo(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Zo(+t+1);return t}}:n))},r.ticks=function(n,t){var e=ti(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Zo(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=
function(){return Io(n.copy(),t,e)},ci(r,n)}function Zo(n){return new Date(n)}function Vo(n){return function(t){for(var e=n.length-1,r=n[e];!r[1](t);)r=n[--e];return r[0](t)}}function Xo(n){return JSON.parse(n.responseText)}function $o(n){var t=Go.createRange();return t.selectNode(Go.body),t.createContextualFragment(n.responseText)}var Bo={version:"3.3.13"};Date.now||(Date.now=function(){return+new Date});var Wo=[].slice,Jo=function(n){return Wo.call(n)},Go=document,Ko=Go.documentElement,Qo=window;try{Jo(Ko.childNodes)[0].nodeType}catch(na){Jo=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}try{Go.createElement("div").style.setProperty("opacity",0,"")}catch(ta){var ea=Qo.Element.prototype,ra=ea.setAttribute,ua=ea.setAttributeNS,ia=Qo.CSSStyleDeclaration.prototype,oa=ia.setProperty;ea.setAttribute=function(n,t){ra.call(this,n,t+"")},ea.setAttributeNS=function(n,t,e){ua.call(this,n,t,e+"")},ia.setProperty=function(n,t,e){oa.call(this,n,t+"",e)}}Bo.ascend
ing=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},Bo.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},Bo.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&e>r&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&e>r&&(e=r)}return e},Bo.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u<i&&!(null!=(e=n[u])&&e>=e);)e=void 0;for(;++u<i;)null!=(r=n[u])&&r>e&&(e=r)}else{for(;++u<i&&!(null!=(e=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<i;)null!=(r=t.call(n,n[u],u))&&r>e&&(e=r)}return e},Bo.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i<o&&!(null!=(e=u=n[i])&&e>=e);)e=u=void 0;for(;++i<o;)null!=(r=n[i])&&(e>r&&(e=r),r>u&&(u=r))}else{for(;++i<o&&!(null!=(e=u=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<o;)null!=(r=t.call(n,n[i],i))&&(e>r&&(e=r),r>u&&(u=r))}return[e,u]},Bo
.sum=function(n,t){var e,r=0,u=n.length,i=-1;if(1===arguments.length)for(;++i<u;)isNaN(e=+n[i])||(r+=e);else for(;++i<u;)isNaN(e=+t.call(n,n[i],i))||(r+=e);return r},Bo.mean=function(t,e){var r,u=t.length,i=0,o=-1,a=0;if(1===arguments.length)for(;++o<u;)n(r=t[o])&&(i+=(r-i)/++a);else for(;++o<u;)n(r=e.call(t,t[o],o))&&(i+=(r-i)/++a);return a?i:void 0},Bo.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),u=+n[r-1],i=e-r;
+return i?u+i*(n[r]-u):u},Bo.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?Bo.quantile(t.sort(Bo.ascending),.5):void 0},Bo.bisector=function(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n.call(t,t[i],i)<e?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;e<n.call(t,t[i],i)?u=i:r=i+1}return r}}};var aa=Bo.bisector(function(n){return n});Bo.bisectLeft=aa.left,Bo.bisect=Bo.bisectRight=aa.right,Bo.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},Bo.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},Bo.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},Bo.zip=function(){if(!(u=arguments.length))return[];for(var n=-1,e=Bo.min(arguments,t),r=new Array(e);++n<e
;)for(var u,i=-1,o=r[n]=new Array(u);++i<u;)o[i]=arguments[i][n];return r},Bo.transpose=function(n){return Bo.zip.apply(Bo,n)},Bo.keys=function(n){var t=[];for(var e in n)t.push(e);return t},Bo.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},Bo.entries=function(n){var t=[];for(var e in n)t.push({key:e,value:n[e]});return t},Bo.merge=function(n){for(var t,e,r,u=n.length,i=-1,o=0;++i<u;)o+=n[i].length;for(e=new Array(o);--u>=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ca=Math.abs;Bo.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var u,i=[],o=e(ca(r)),a=-1;if(n*=o,t*=o,r*=o,0>r)for(;(u=n+r*++a)>t;)i.push(u/o);else for(;(u=n+r*++a)<t;)i.push(u/o);return i},Bo.map=function(n){var t=new u;if(n instanceof u)n.forEach(function(n,e){t.set(n,e)});else for(var e in n)t.set(e,n[e]);return t},r(u,{has:function(n){return sa+n in this},get:function(n){return this[sa+n]},set:fu
nction(n,t){return this[sa+n]=t},remove:function(n){return n=sa+n,n in this&&delete this[n]},keys:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===la&&n.call(this,t.substring(1),this[t])}});var sa="\x00",la=sa.charCodeAt(0);Bo.nest=function(){function n(t,a,c){if(c>=o.length)return r?r.call(i,a):e?a.sort(e):a;for(var s,l,f,h,g=-1,p=a.length,v=o[c++],d=new u;++g<p;)(h=d.get(s=v(l=a[g])))?h.push(l):d.set(s,[l]);return t?(l=t(),f=function(e,r){l.set(e,n(t,r,c))}):(l={},f=function(e,r){l[e]=n(t,r,c)}),d.forEach(f),l}function t(n,e){if(e>=o.length)return n;var r=[],u=a[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,i={},o=[],a=[];return i.map=function(t,e){return n(e
,t,0)},i.entries=function(e){return t(n(Bo.map,e,0),0)},i.key=function(n){return o.push(n),i},i.sortKeys=function(n){return a[o.length-1]=n,i},i.sortValues=function(n){return e=n,i},i.rollup=function(n){return r=n,i},i},Bo.set=function(n){var t=new i;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},r(i,{has:function(n){return sa+n in this},add:function(n){return this[sa+n]=!0,n},remove:function(n){return n=sa+n,n in this&&delete this[n]},values:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===la&&n.call(this,t.substring(1))}}),Bo.behavior={},Bo.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r<u;)n[e=arguments[r]]=o(n,t,t[e]);return n};var fa=["webkit","ms","moz","Moz","o","O"];Bo.dispatch=function(){for(var n=new s,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=l(n);return n},s.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)retu
rn arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},Bo.event=null,Bo.requote=function(n){return n.replace(ha,"\\$&")};var ha=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ga={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},pa=function(n,t){return t.querySelector(n)},va=function(n,t){return t.querySelectorAll(n)},da=Ko[a(Ko,"matchesSelector")],ma=function(n,t){return da.call(n,t)};"function"==typeof Sizzle&&(pa=function(n,t){return Sizzle(n,t)[0]||null},va=function(n,t){return Sizzle.uniqueSort(Sizzle(n,t))},ma=Sizzle.matchesSelector),Bo.selection=function(){return _a};var ya=Bo.selection.prototype=[];ya.select=function(n){var t,e,r,u,i=[];n=v(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]),t.parentNode=(r=this[o]).parentNode;for(var c=-1,s=r.length;++c<s;)(u=r[c])?(t.push(e=n.call(u,u.__data__,c,o)),e&&"__data__"in u&&(e.__data__=u.__data__)):t.p
ush(null)}return p(i)},ya.selectAll=function(n){var t,e,r=[];n=d(n);for(var u=-1,i=this.length;++u<i;)for(var o=this[u],a=-1,c=o.length;++a<c;)(e=o[a])&&(r.push(t=Jo(n.call(e,e.__data__,a,u))),t.parentNode=e);return p(r)};var xa={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};Bo.ns={prefix:xa,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),xa.hasOwnProperty(e)?{space:xa[e],local:n}:n}},ya.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=Bo.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(m(t,n[t]));return this}return this.each(m(n,t))},ya.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=M(n)).length,u=-1;if(t=e.classList){for(;++u<r;)if(!t.contains(n[u]))ret
urn!1}else for(t=e.getAttribute("class");++u<r;)if(!x(n[u]).test(t))return!1;return!0}for(t in n)this.each(_(t,n[t]));return this}return this.each(_(n,t))},ya.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(w(e,n[e],t));return this}if(2>r)return Qo.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(w(n,t,e))},ya.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(S(t,n[t]));return this}return this.each(S(n,t))},ya.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},ya.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:f
unction(){this.innerHTML=n}):this.node().innerHTML},ya.append=function(n){return n=k(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},ya.insert=function(n,t){return n=k(n),t=v(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},ya.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},ya.data=function(n,t){function e(n,e){var r,i,o,a=n.length,f=e.length,h=Math.min(a,f),g=new Array(f),p=new Array(f),v=new Array(a);if(t){var d,m=new u,y=new u,x=[];for(r=-1;++r<a;)d=t.call(i=n[r],i.__data__,r),m.has(d)?v[r]=i:m.set(d,i),x.push(d);for(r=-1;++r<f;)d=t.call(e,o=e[r],r),(i=m.get(d))?(g[r]=i,i.__data__=o):y.has(d)||(p[r]=E(o)),y.set(d,o),m.remove(d);for(r=-1;++r<a;)m.has(x[r])&&(v[r]=n[r])}else{for(r=-1;++r<h;)i=n[r],o=e[r],i?(i.__data__=o,g[r]=i):p[r]=E(o);for(;f>r;++r)p[r]=E(e[r]);for(;a>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.par
entNode,c.push(p),s.push(g),l.push(v)}var r,i,o=-1,a=this.length;if(!arguments.length){for(n=new Array(a=(r=this[0]).length);++o<a;)(i=r[o])&&(n[o]=i.__data__);return n}var c=L([]),s=p([]),l=p([]);if("function"==typeof n)for(;++o<a;)e(r=this[o],n.call(r,r.parentNode.__data__,o));else for(;++o<a;)e(r=this[o],n);return s.enter=function(){return c},s.exit=function(){return l},s},ya.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},ya.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=A(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return p(u)},ya.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],u=r.length-1,i=r[u];--u>=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},ya.sort=function(n){n=C.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this
[t].sort(n);return this.order()},ya.each=function(n){return N(this,function(t,e,r){n.call(t,t.__data__,e,r)})},ya.call=function(n){var t=Jo(arguments);return n.apply(t[0]=this,t),this},ya.empty=function(){return!this.node()},ya.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},ya.size=function(){var n=0;return this.each(function(){++n}),n};var Ma=[];Bo.selection.enter=L,Bo.selection.enter.prototype=Ma,Ma.append=ya.append,Ma.empty=ya.empty,Ma.node=ya.node,Ma.call=ya.call,Ma.size=ya.size,Ma.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++a<c;){r=(u=this[a]).update,o.push(t=[]),t.parentNode=u.parentNode;for(var s=-1,l=u.length;++s<l;)(i=u[s])?(t.push(r[s]=e=n.call(u.parentNode,i.__data__,s,a)),e.__data__=i.__data__):t.push(null)}return p(o)},Ma.insert=function(n,t){return arguments.length<2&&(t=T(this)),ya.insert.call(this,n,t)},ya.transition=function(){for(var n,t,e=ks||++Ls,r=[],u=E
s||{time:Date.now(),ease:Fr,delay:0,duration:250},i=-1,o=this.length;++i<o;){r.push(n=[]);for(var a=this[i],c=-1,s=a.length;++c<s;)(t=a[c])&&io(t,c,e,u),n.push(t)}return eo(r,e)},ya.interrupt=function(){return this.each(q)},Bo.select=function(n){var t=["string"==typeof n?pa(n,Go):n];return t.parentNode=Ko,p([t])},Bo.selectAll=function(n){var t=Jo("string"==typeof n?va(n,Go):n);return t.parentNode=Ko,p([t])};var _a=Bo.select(Ko);ya.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(z(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(z(n,t,e))};var ba=Bo.map({mouseenter:"mouseover",mouseleave:"mouseout"});ba.forEach(function(n){"on"+n in Go&&ba.remove(n)});var wa="onselectstart"in Go?null:a(Ko.style,"userSelect"),Sa=0;Bo.mouse=function(n){return U(n,h())};var ka=/WebKit/.test(Qo.navigator.userAgent)?-1:0;Bo.touches=function(n,t){return arguments.length<2&&(t=h().touches),t?Jo(t).map(f
unction(t){var e=U(n,t);return e.identifier=t.identifier,e}):[]},Bo.behavior.drag=function(){function n(){this.on("mousedown.drag",o).on("touchstart.drag",a)}function t(){return Bo.event.changedTouches[0].identifier}function e(n,t){return Bo.touches(n).filter(function(n){return n.identifier===t})[0]}function r(n,t,e,r){return function(){function o(){var n=t(l,g),e=n[0]-v[0],r=n[1]-v[1];d|=e|r,v=n,f({type:"drag",x:n[0]+c[0],y:n[1]+c[1],dx:e,dy:r})}function a(){m.on(e+"."+p,null).on(r+"."+p,null),y(d&&Bo.event.target===h),f({type:"dragend"})}var c,s=this,l=s.parentNode,f=u.of(s,arguments),h=Bo.event.target,g=n(),p=null==g?"drag":"drag-"+g,v=t(l,g),d=0,m=Bo.select(Qo).on(e+"."+p,o).on(r+"."+p,a),y=P();i?(c=i.apply(s,arguments),c=[c.x-v[0],c.y-v[1]]):c=[0,0],f({type:"dragstart"})}}var u=g(n,"drag","dragstart","dragend"),i=null,o=r(c,Bo.mouse,"mousemove","mouseup"),a=r(t,e,"touchmove","touchend");return n.origin=function(t){return arguments.length?(i=t,n):i},Bo.rebind(n,u,"on")};
var Ea=Math.PI,Aa=2*Ea,Ca=Ea/2,Na=1e-6,La=Na*Na,Ta=Ea/180,qa=180/Ea,za=Math.SQRT2,Ra=2,Da=4;Bo.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=Y(v),o=i/(Ra*h)*(e*I(za*t+v)-O(v));return[r+o*s,u+o*l,i*e/Y(za*t+v)]}return[r+n*s,u+n*l,i*Math.exp(za*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],s=o-r,l=a-u,f=s*s+l*l,h=Math.sqrt(f),g=(c*c-i*i+Da*f)/(2*i*Ra*h),p=(c*c-i*i-Da*f)/(2*c*Ra*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/za;return e.duration=1e3*y,e},Bo.behavior.zoom=function(){function n(n){n.on(A,s).on(ja+".zoom",h).on(C,p).on("dblclick.zoom",v).on(L,l)}function t(n){return[(n[0]-S.x)/S.k,(n[1]-S.y)/S.k]}function e(n){return[n[0]*S.k+S.x,n[1]*S.k+S.y]}function r(n){S.k=Math.max(E[0],Math.min(E[1],n))}function u(n,t){t=e(t),S.x+=n[0]-t[0],S.y+=n[1]-t[1]}function i(){_&&_.domain(M.range().map(function(n){return(n-S.x)/S.k}).map(M.invert)),w&&w.domain(b.range().map(function(n){return(n-S.y)/S.k}).map(b.inve
rt))}function o(n){n({type:"zoomstart"})}function a(n){i(),n({type:"zoom",scale:S.k,translate:[S.x,S.y]})}function c(n){n({type:"zoomend"})}function s(){function n(){l=1,u(Bo.mouse(r),h),a(i)}function e(){f.on(C,Qo===r?p:null).on(N,null),g(l&&Bo.event.target===s),c(i)}var r=this,i=T.of(r,arguments),s=Bo.event.target,l=0,f=Bo.select(Qo).on(C,n).on(N,e),h=t(Bo.mouse(r)),g=P();q.call(r),o(i)}function l(){function n(){var n=Bo.touches(p);return g=S.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=t(n))}),n}function e(){for(var t=Bo.event.changedTouches,e=0,i=t.length;i>e;++e)d[t[e].identifier]=null;var o=n(),c=Date.now();if(1===o.length){if(500>c-x){var s=o[0],l=d[s.identifier];r(2*S.k),u(s,l),f(),a(v)}x=c}else if(o.length>1){var s=o[0],h=o[1],g=s[0]-h[0],p=s[1]-h[1];m=g*g+p*p}}function i(){for(var n,t,e,i,o=Bo.touches(p),c=0,s=o.length;s>c;++c,i=null)if(e=o[c],i=d[e.identifier]){if(t)break;n=e,t=i}if(i){var l=(l=e[0]-n[0])*l+(l=e[1]-n[1])*l,f=m&&Math.sqrt(l/m);n=[(n[
0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+i[0])/2,(t[1]+i[1])/2],r(f*g)}x=null,u(n,t),a(v)}function h(){if(Bo.event.touches.length){for(var t=Bo.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}b.on(M,null).on(_,null),w.on(A,s).on(L,l),k(),c(v)}var g,p=this,v=T.of(p,arguments),d={},m=0,y=Bo.event.changedTouches[0].identifier,M="touchmove.zoom-"+y,_="touchend.zoom-"+y,b=Bo.select(Qo).on(M,i).on(_,h),w=Bo.select(p).on(A,null).on(L,e),k=P();q.call(p),e(),o(v)}function h(){var n=T.of(this,arguments);y?clearTimeout(y):(q.call(this),o(n)),y=setTimeout(function(){y=null,c(n)},50),f();var e=m||Bo.mouse(this);d||(d=t(e)),r(Math.pow(2,.002*Pa())*S.k),u(e,d),a(n)}function p(){d=null}function v(){var n=T.of(this,arguments),e=Bo.mouse(this),i=t(e),s=Math.log(S.k)/Math.LN2;o(n),r(Math.pow(2,Bo.event.shiftKey?Math.ceil(s)-1:Math.floor(s)+1)),u(e,i),a(n),c(n)}var d,m,y,x,M,_,b,w,S={x:0,y:0,k:1},k=[960,500],E=Ua,A="mousedown.zoom",C="mousemove.zoo
m",N="mouseup.zoom",L="touchstart.zoom",T=g(n,"zoomstart","zoom","zoomend");return n.event=function(n){n.each(function(){var n=T.of(this,arguments),t=S;ks?Bo.select(this).transition().each("start.zoom",function(){S=this.__chart__||{x:0,y:0,k:1},o(n)}).tween("zoom:zoom",function(){var e=k[0],r=k[1],u=e/2,i=r/2,o=Bo.interpolateZoom([(u-S.x)/S.k,(i-S.y)/S.k,e/S.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),c=e/r[2];this.__chart__=S={x:u-r[0]*c,y:i-r[1]*c,k:c},a(n)}}).each("end.zoom",function(){c(n)}):(this.__chart__=S,o(n),a(n),c(n))})},n.translate=function(t){return arguments.length?(S={x:+t[0],y:+t[1],k:S.k},i(),n):[S.x,S.y]},n.scale=function(t){return arguments.length?(S={x:S.x,y:S.y,k:+t},i(),n):S.k},n.scaleExtent=function(t){return arguments.length?(E=null==t?Ua:[+t[0],+t[1]],n):E},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(k=t&&[+t[0],+t[1]],n):k},n.x=function(t){return arguments.l
ength?(_=t,M=t.copy(),S={x:0,y:0,k:1},n):_},n.y=function(t){return arguments.length?(w=t,b=t.copy(),S={x:0,y:0,k:1},n):w},Bo.rebind(n,T,"on")};var Pa,Ua=[0,1/0],ja="onwheel"in Go?(Pa=function(){return-Bo.event.deltaY*(Bo.event.deltaMode?120:1)},"wheel"):"onmousewheel"in Go?(Pa=function(){return Bo.event.wheelDelta},"mousewheel"):(Pa=function(){return-Bo.event.detail},"MozMousePixelScroll");V.prototype.toString=function(){return this.rgb()+""},Bo.hsl=function(n,t,e){return 1===arguments.length?n instanceof $?X(n.h,n.s,n.l):lt(""+n,ft,X):X(+n,+t,+e)};var Ha=$.prototype=new V;Ha.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),X(this.h,this.s,this.l/n)},Ha.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),X(this.h,this.s,n*this.l)},Ha.rgb=function(){return B(this.h,this.s,this.l)},Bo.hcl=function(n,t,e){return 1===arguments.length?n instanceof J?W(n.h,n.c,n.l):n instanceof Q?tt(n.l,n.a,n.b):tt((n=ht((n=Bo.rgb(n)).r,n.g,n.b)).l,n.a,n.b):W(+n,+t,+e)};va
r Fa=J.prototype=new V;Fa.brighter=function(n){return W(this.h,this.c,Math.min(100,this.l+Oa*(arguments.length?n:1)))},Fa.darker=function(n){return W(this.h,this.c,Math.max(0,this.l-Oa*(arguments.length?n:1)))},Fa.rgb=function(){return G(this.h,this.c,this.l).rgb()},Bo.lab=function(n,t,e){return 1===arguments.length?n instanceof Q?K(n.l,n.a,n.b):n instanceof J?G(n.l,n.c,n.h):ht((n=Bo.rgb(n)).r,n.g,n.b):K(+n,+t,+e)};var Oa=18,Ya=.95047,Ia=1,Za=1.08883,Va=Q.prototype=new V;Va.brighter=function(n){return K(Math.min(100,this.l+Oa*(arguments.length?n:1)),this.a,this.b)},Va.darker=function(n){return K(Math.max(0,this.l-Oa*(arguments.length?n:1)),this.a,this.b)},Va.rgb=function(){return nt(this.l,this.a,this.b)},Bo.rgb=function(n,t,e){return 1===arguments.length?n instanceof ct?at(n.r,n.g,n.b):lt(""+n,at,B):at(~~n,~~t,~~e)};var Xa=ct.prototype=new V;Xa.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&
(e=u),r&&u>r&&(r=u),at(Math.min(255,~~(t/n)),Math.min(255,~~(e/n)),Math.min(255,~~(r/n)))):at(u,u,u)},Xa.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),at(~~(n*this.r),~~(n*this.g),~~(n*this.b))},Xa.hsl=function(){return ft(this.r,this.g,this.b)},Xa.toString=function(){return"#"+st(this.r)+st(this.g)+st(this.b)};var $a=Bo.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslateg
ray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,medium
orchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesm
oke:16119285,yellow:16776960,yellowgreen:10145074});$a.forEach(function(n,t){$a.set(n,it(t))}),Bo.functor=vt,Bo.xhr=mt(dt),Bo.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=yt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function o(t){return t.map(a).join(n)}function a(n){return c.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var c=new RegExp('["'+n+"\n]"),s=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(l>=c)return o;if(u)return u=!1,i;var t=l;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+
1))break;++e}l=e+2;var r=n.charCodeAt(e+1);return 13===r?(u=!0,10===n.charCodeAt(e+2)&&++l):10===r&&(u=!0),n.substring(t+1,e).replace(/""/g,'"')}for(;c>l;){var r=n.charCodeAt(l++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(l)&&(++l,++a);else if(r!==s)continue;return n.substring(t,l-a)}return n.substring(t)}for(var r,u,i={},o={},a=[],c=n.length,l=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();(!t||(h=t(h,f++)))&&a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new i,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(a).join(n)].concat(t.map(function(t){return u.map(function(n){return a(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(o).join("\n")},e},Bo.csv=Bo.dsv(",","text/csv"),Bo.tsv=Bo.dsv(" ","text/tab-separated-values");var Ba,Wa,Ja,Ga,Ka,Qa=Qo[a(Qo,"requestAnimationFrame")]||function(n){setTimeout(n,17)};Bo.timer=function(n,t,e){var r=argumen
ts.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Wa?Wa.n=i:Ba=i,Wa=i,Ja||(Ga=clearTimeout(Ga),Ja=1,Qa(Mt))},Bo.timer.flush=function(){_t(),bt()};var nc=".",tc=",",ec=[3,3],rc="$",uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(wt);Bo.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=Bo.round(n,St(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),uc[8+e/3]},Bo.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)},Bo.format=function(n){var t=ic.exec(n),e=t[1]||" ",r=t[2]||">",u=t[3]||"",i=t[4]||"",o=t[5],a=+t[6],c=t[7],s=t[8],l=t[9],f=1,h="",g=!1;switch(s&&(s=+s.substring(1)),(o||"0"===e&&"="===r)&&(o=e="0",r="=",c&&(a-=Math.floor((a-1)/4))),l){case"n":c=!0,l="g";break;case"%":f=100,h="%",l="f";break;case"p":f=100,h="%",l="r";break;case"b":case"o":case"x":case"X":"#"===i&&(i="0"+l.toLowerCase());case"c":case"d":g=!0,s=0;
break;case"s":f=-1,l="r"}"#"===i?i="":"$"===i&&(i=rc),"r"!=l||s||(l="g"),null!=s&&("g"==l?s=Math.max(1,Math.min(21,s)):("e"==l||"f"==l)&&(s=Math.max(0,Math.min(20,s)))),l=oc.get(l)||kt;var p=o&&c;return function(n){if(g&&n%1)return"";var t=0>n||0===n&&0>1/n?(n=-n,"-"):u;if(0>f){var v=Bo.formatPrefix(n,s);n=v.scale(n),h=v.symbol}else n*=f;n=l(n,s);var d=n.lastIndexOf("."),m=0>d?n:n.substring(0,d),y=0>d?"":nc+n.substring(d+1);!o&&c&&(m=ac(m));var x=i.length+m.length+y.length+(p?0:t.length),M=a>x?new Array(x=a-x+1).join(e):"";return p&&(m=ac(M+m)),t+=i,n=m+y,("<"===r?t+n+M:">"===r?M+t+n:"^"===r?M.substring(0,x>>=1)+t+n+M.substring(x):t+(p?n:M+n))+h}};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=Bo.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(
t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=Bo.round(n,St(n,t))).toFixed(Math.max(0,Math.min(20,St(n*(1+1e-15),t))))}}),ac=dt;if(ec){var cc=ec.length;ac=function(n){for(var t=n.length,e=[],r=0,u=ec[0];t>0&&u>0;)e.push(n.substring(t-=u,t+u)),u=ec[r=(r+1)%cc];return e.reverse().join(tc)}}Bo.geo={},Et.prototype={s:0,t:0,add:function(n){At(n,this.t,sc),At(sc.s,this.s,this),this.s?this.t+=sc.t:this.s=sc.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var sc=new Et;Bo.geo.stream=function(n,t){n&&lc.hasOwnProperty(n.type)?lc[n.type](n,t):Ct(n,t)};var lc={Feature:function(n,t){Ct(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++r<u;)Ct(e[r].geometry,t)}},fc={Sphere:function(n,t){t.sphere()},Point:function(n,t){n=n.coordinates,t.point(n[0],n[1],n[2])},MultiPoint:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)n=e[r],t.point(n[0],n[1],n[2])},LineSt
ring:function(n,t){Nt(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)Nt(e[r],t,0)},Polygon:function(n,t){Lt(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,u=e.length;++r<u;)Lt(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,u=e.length;++r<u;)Ct(e[r],t)}};Bo.geo.area=function(n){return hc=0,Bo.geo.stream(n,pc),hc};var hc,gc=new Et,pc={sphere:function(){hc+=4*Ea},point:c,lineStart:c,lineEnd:c,polygonStart:function(){gc.reset(),pc.lineStart=Tt},polygonEnd:function(){var n=2*gc;hc+=0>n?4*Ea+n:n,pc.lineStart=pc.lineEnd=pc.point=c}};Bo.geo.bounds=function(){function n(n,t){x.push(M=[l=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=qt([t*Ta,e*Ta]);if(m){var u=Rt(m,r),i=[u[1],-u[0],0],o=Rt(i,u);Ut(o),o=jt(o);var c=t-p,s=c>0?1:-1,v=o[0]*qa*s,d=ca(c)>180;if(d^(v>s*p&&s*t>v)){var y=o[1]*qa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>s*p&&s*t>v)){var y=-o[1]*qa;f>y&&(f=y)}else f>e&&(f=e
),e>g&&(g=e);d?p>t?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t):h>=l?(l>t&&(l=t),t>h&&(h=t)):t>p?a(l,t)>a(l,h)&&(h=t):a(t,h)>a(l,h)&&(l=t)}else n(t,e);m=r,p=t}function e(){_.point=t}function r(){M[0]=l,M[1]=h,_.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ca(r)>180?r+(r>0?360:-360):r}else v=n,d=e;pc.point(n,e),t(n,e)}function i(){pc.lineStart()}function o(){u(v,d),pc.lineEnd(),ca(y)>Na&&(l=-(h=180)),M[0]=l,M[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function s(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var l,f,h,g,p,v,d,m,y,x,M,_={point:n,lineStart:e,lineEnd:r,polygonStart:function(){_.point=u,_.lineStart=i,_.lineEnd=o,y=0,pc.polygonStart()},polygonEnd:function(){pc.polygonEnd(),_.point=n,_.lineStart=e,_.lineEnd=r,0>gc?(l=-(h=180),f=-(g=90)):y>Na?g=90:-Na>y&&(f=-90),M[0]=l,M[1]=h}};return function(n){g=h=-(l=f=1/0),x=[],Bo.geo.stream(n,_);var t=x.length;if(t){x.sort(c);for(var e,r=1,u=x[0],i=[u];t>r;++r)e=x[r],s(e[0],u)
||s(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,l=e[0],h=u[1])}return x=M=null,1/0===l||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[l,f],[h,g]]}}(),Bo.geo.centroid=function(n){vc=dc=mc=yc=xc=Mc=_c=bc=wc=Sc=kc=0,Bo.geo.stream(n,Ec);var t=wc,e=Sc,r=kc,u=t*t+e*e+r*r;return La>u&&(t=Mc,e=_c,r=bc,Na>dc&&(t=mc,e=yc,r=xc),u=t*t+e*e+r*r,La>u)?[0/0,0/0]:[Math.atan2(e,t)*qa,F(r/Math.sqrt(u))*qa]};var vc,dc,mc,yc,xc,Mc,_c,bc,wc,Sc,kc,Ec={sphere:c,point:Ft,lineStart:Yt,lineEnd:It,polygonStart:function(){Ec.lineStart=Zt},polygonEnd:function(){Ec.lineStart=Yt}},Ac=Wt(Vt,ne,ee,[-Ea,-Ea/2]),Cc=1e9;Bo.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0
],[960,500]])},(Bo.geo.conicEqualArea=function(){return ae(ce)}).raw=ce,Bo.geo.albers=function(){return Bo.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},Bo.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=Bo.geo.albers(),o=Bo.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=Bo.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd()
,e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var s=i.scale(),l=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[l-.455*s,f-.238*s],[l+.455*s,f+.238*s]]).stream(c).point,r=o.translate([l-.307*s,f+.201*s]).clipExtent([[l-.425*s+Na,f+.12*s+Na],[l-.214*s-Na,f+.234*s-Na]]).stream(c).point,u=a.translate([l-.205*s,f+.212*s]).clipExtent([[l-.214*s+Na,f+.166*s+Na],[l-.115*s-Na,f+.234*s-Na]]).stream(c).point,n},n.scale(1070)};var Nc,Lc,Tc,qc,zc,Rc,Dc={point:c,lineStart:c,lineEnd:c,polygonStart:function(){Lc=0,Dc.lineStart=se},polygonEnd:function(){Dc.lineStart=Dc.l
ineEnd=Dc.point=c,Nc+=ca(Lc/2)}},Pc={point:le,lineStart:c,lineEnd:c,polygonStart:c,polygonEnd:c},Uc={point:ge,lineStart:pe,lineEnd:ve,polygonStart:function(){Uc.lineStart=de},polygonEnd:function(){Uc.point=ge,Uc.lineStart=pe,Uc.lineEnd=ve}};Bo.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),Bo.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Nc=0,Bo.geo.stream(n,u(Dc)),Nc},n.centroid=function(n){return mc=yc=xc=Mc=_c=bc=wc=Sc=kc=0,Bo.geo.stream(n,u(Uc)),kc?[wc/kc,Sc/kc]:bc?[Mc/bc,_c/bc]:xc?[mc/xc,yc/xc]:[0/0,0/0]},n.bounds=function(n){return zc=Rc=-(Tc=qc=1/0),Bo.geo.stream(n,u(Pc)),[[Tc,qc],[zc,Rc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||xe(n):dt,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new fe:new me(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){ret
urn arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(Bo.geo.albersUsa()).context(null)},Bo.geo.transform=function(n){return{stream:function(t){var e=new Me(t);for(var r in n)e[r]=n[r];return e}}},Me.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()
+},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},Bo.geo.projection=be,Bo.geo.projectionMutator=we,(Bo.geo.equirectangular=function(){return be(ke)}).raw=ke.invert=ke,Bo.geo.rotation=function(n){function t(t){return t=n(t[0]*Ta,t[1]*Ta),t[0]*=qa,t[1]*=qa,t}return n=Ae(n[0]%360*Ta,n[1]*Ta,n.length>2?n[2]*Ta:0),t.invert=function(t){return t=n.invert(t[0]*Ta,t[1]*Ta),t[0]*=qa,t[1]*=qa,t},t},Ee.invert=ke,Bo.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=Ae(-n[0]*Ta,-n[1]*Ta,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=qa,n[1]*=qa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=Te((t=+r)*Ta,u*Ta),n):t},n.precision=function(r){return arguments.length?(e=Te(t*Ta,(u=+r)*Ta),n):u},n.angle(90)},Bo.g
eo.distance=function(n,t){var e,r=(t[0]-n[0])*Ta,u=n[1]*Ta,i=t[1]*Ta,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),s=Math.cos(u),l=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=s*l-c*f*a)*e),c*l+s*f*a)},Bo.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return Bo.range(Math.ceil(i/d)*d,u,d).map(h).concat(Bo.range(Math.ceil(s/m)*m,c,m).map(g)).concat(Bo.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ca(n%d)>Na}).map(l)).concat(Bo.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ca(n%m)>Na}).map(f))}var e,r,u,i,o,a,c,s,l,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(s).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){ret
urn arguments.length?(i=+t[0][0],u=+t[1][0],s=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),s>c&&(t=s,s=c,c=t),n.precision(y)):[[i,s],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,l=ze(a,o,90),f=Re(r,e,y),h=ze(s,c,90),g=Re(i,u,y),n):y},n.majorExtent([[-180,-90+Na],[180,90-Na]]).minorExtent([[-180,-80-Na],[180,80+Na]])},Bo.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=De,u=Pe;return n.distance=function(){return Bo.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))}
,n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},Bo.geo.interpolate=function(n,t){return Ue(n[0]*Ta,n[1]*Ta,t[0]*Ta,t[1]*Ta)},Bo.geo.length=function(n){return jc=0,Bo.geo.stream(n,Hc),jc};var jc,Hc={sphere:c,point:c,lineStart:je,lineEnd:c,polygonStart:c,polygonEnd:c},Fc=He(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(Bo.geo.azimuthalEqualArea=function(){return be(Fc)}).raw=Fc;var Oc=He(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},dt);(Bo.geo.azimuthalEquidistant=function(){return be(Oc)}).raw=Oc,(Bo.geo.conicConformal=function(){return ae(Fe)}).raw=Fe,(Bo.geo.conicEquidistant=function(){return ae(Oe)}).raw=Oe;var Yc=He(function(n){return 1/n},Math.atan);(Bo.geo.gnomonic=function(){return be(Yc)}).raw=Yc,Ye.invert=function(n,t){return[n,2*Math.atan(Math.exp
(t))-Ca]},(Bo.geo.mercator=function(){return Ie(Ye)}).raw=Ye;var Ic=He(function(){return 1},Math.asin);(Bo.geo.orthographic=function(){return be(Ic)}).raw=Ic;var Zc=He(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(Bo.geo.stereographic=function(){return be(Zc)}).raw=Zc,Ze.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ca]},(Bo.geo.transverseMercator=function(){var n=Ie(Ze),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[-n[1],n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},n.rotate([0,0])}).raw=Ze,Bo.geom={},Bo.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u,i,o,a,c,s,l,f,h,g,p,v=vt(e),d=vt(r),m=n.length,y=m-1,x=[],M=[],_=0;if(v===Ve&&r===Xe)t=n;else for(i=0,t=[];m>i;++i)t.push([+v.call(this,u=n[i],i),+d.call(this,u,i)]);for(i=1;m>i;++i)(t[i][1]<t[_][1]||t[i][1]==t[_][1]&&t[i][0]<t[_][0])&&(_=i);for(i=0;m>i;++i)i!==_&&(c=t[i][1]-t[_][1],a=t
[i][0]-t[_][0],x.push({angle:Math.atan2(c,a),index:i}));for(x.sort(function(n,t){return n.angle-t.angle}),g=x[0].angle,h=x[0].index,f=0,i=1;y>i;++i){if(o=x[i].index,g==x[i].angle){if(a=t[h][0]-t[_][0],c=t[h][1]-t[_][1],s=t[o][0]-t[_][0],l=t[o][1]-t[_][1],a*a+c*c>=s*s+l*l){x[i].index=-1;continue}x[f].index=-1}g=x[i].angle,f=i,h=o}for(M.push(_),i=0,o=0;2>i;++o)x[o].index>-1&&(M.push(x[o].index),i++);for(p=M.length;y>o;++o)if(!(x[o].index<0)){for(;!$e(M[p-2],M[p-1],x[o].index,t);)--p;M[p++]=x[o].index}var b=[];for(i=p-1;i>=0;--i)b.push(n[M[i]]);return b}var e=Ve,r=Xe;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},Bo.geom.polygon=function(n){return ga(n,Vc),n};var Vc=Bo.geom.polygon.prototype=[];Vc.area=function(){for(var n,t=-1,e=this.length,r=this[e-1],u=0;++t<e;)n=r,r=this[t],u+=n[1]*r[0]-n[0]*r[1];return.5*u},Vc.centroid=function(n){var t,e,r=-1,u=this.length,i=0,o=0,a=this[u-1];for(argu
ments.length||(n=-1/(6*this.area()));++r<u;)t=a,a=this[r],e=t[0]*a[1]-a[0]*t[1],i+=(t[0]+a[0])*e,o+=(t[1]+a[1])*e;return[i*n,o*n]},Vc.clip=function(n){for(var t,e,r,u,i,o,a=Je(n),c=-1,s=this.length-Je(this),l=this[s-1];++c<s;){for(t=n.slice(),n.length=0,u=this[c],i=t[(r=t.length-a)-1],e=-1;++e<r;)o=t[e],Be(o,l,u)?(Be(i,l,u)||n.push(We(i,o,l,u)),n.push(o)):Be(i,l,u)&&n.push(We(i,o,l,u)),i=o;a&&n.push(n[0]),l=u}return n};var Xc,$c,Bc,Wc,Jc,Gc=[],Kc=[];ur.prototype.prepare=function(){for(var n,t=this.edges,e=t.length;e--;)n=t[e].edge,n.b&&n.a||t.splice(e,1);return t.sort(or),t.length},dr.prototype={start:function(){return this.edge.l===this.site?this.edge.a:this.edge.b},end:function(){return this.edge.l===this.site?this.edge.b:this.edge.a}},mr.prototype={insert:function(n,t){var e,r,u;if(n){if(t.P=n,t.N=n.N,n.N&&(n.N.P=t),n.N=t,n.R){for(n=n.R;n.L;)n=n.L;n.L=t}else n.R=t;e=n}else this._?(n=_r(this._),t.P=null,t.N=n,n.P=n.L=t,e=n):(t.P=t.N=null,this._=t,e=null);for(t.L=t.R=null,t
.U=e,t.C=!0,n=t;e&&e.C;)r=e.U,e===r.L?(u=r.R,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.R&&(xr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,Mr(this,r))):(u=r.L,u&&u.C?(e.C=u.C=!1,r.C=!0,n=r):(n===e.L&&(Mr(this,e),n=e,e=n.U),e.C=!1,r.C=!0,xr(this,r))),e=n.U;this._.C=!1},remove:function(n){n.N&&(n.N.P=n.P),n.P&&(n.P.N=n.N),n.N=n.P=null;var t,e,r,u=n.U,i=n.L,o=n.R;if(e=i?o?_r(o):i:o,u?u.L===n?u.L=e:u.R=e:this._=e,i&&o?(r=e.C,e.C=n.C,e.L=i,i.U=e,e!==o?(u=e.U,e.U=n.U,n=e.R,u.L=n,e.R=o,o.U=e):(e.U=u,u=e,n=e.R)):(r=n.C,n=e),n&&(n.U=u),!r){if(n&&n.C)return n.C=!1,void 0;do{if(n===this._)break;if(n===u.L){if(t=u.R,t.C&&(t.C=!1,u.C=!0,xr(this,u),t=u.R),t.L&&t.L.C||t.R&&t.R.C){t.R&&t.R.C||(t.L.C=!1,t.C=!0,Mr(this,t),t=u.R),t.C=u.C,u.C=t.R.C=!1,xr(this,u),n=this._;break}}else if(t=u.L,t.C&&(t.C=!1,u.C=!0,Mr(this,u),t=u.L),t.L&&t.L.C||t.R&&t.R.C){t.L&&t.L.C||(t.R.C=!1,t.C=!0,xr(this,t),t=u.L),t.C=u.C,u.C=t.L.C=!1,Mr(this,u),n=this._;break}t.C=!0,n=u,u=u.U}while(!n.C);n&&(n.C=!1)}}},Bo.geom.voronoi=functi
on(n){function t(n){var t=new Array(n.length),r=a[0][0],u=a[0][1],i=a[1][0],o=a[1][1];return br(e(n),a).cells.forEach(function(e,a){var c=e.edges,s=e.site,l=t[a]=c.length?c.map(function(n){var t=n.start();return[t.x,t.y]}):s.x>=r&&s.x<=i&&s.y>=u&&s.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];l.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Na)*Na,y:Math.round(o(n,t)/Na)*Na,i:t}})}var r=Ve,u=Xe,i=r,o=u,a=Qc;return n?t(n):(t.links=function(n){return br(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return br(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(or),c=-1,s=a.length,l=a[s-1].edge,f=l.l===o?l.r:l.l;++c<s;)u=l,i=f,l=a[c].edge,f=l.l===o?l.r:l.l,r<i.i&&r<f.i&&Sr(o,i,f)<0&&t.push([n[r],n[i.i],n[f.i]])}),t},t.x=function(n){return arguments.length?(i=vt(r=n),t):r},t.y=function(n){return arguments.length?(o=vt(u=n),t):u},t.clipExtent=fu
nction(n){return arguments.length?(a=null==n?Qc:n,t):a===Qc?null:a},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):a===Qc?null:a&&a[1]},t)};var Qc=[[-1e6,-1e6],[1e6,1e6]];Bo.geom.delaunay=function(n){return Bo.geom.voronoi().triangles(n)},Bo.geom.quadtree=function(n,t,e,r,u){function i(n){function i(n,t,e,r,u,i,o,a){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,l=n.y;if(null!=c)if(ca(c-e)+ca(l-r)<.01)s(n,t,e,r,u,i,o,a);else{var f=n.point;n.x=n.y=n.point=null,s(n,f,c,l,u,i,o,a),s(n,t,e,r,u,i,o,a)}else n.x=e,n.y=r,n.point=t}else s(n,t,e,r,u,i,o,a)}function s(n,t,e,r,u,o,a,c){var s=.5*(u+a),l=.5*(o+c),f=e>=s,h=r>=l,g=(h<<1)+f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=Ar()),f?u=s:a=s,h?o=l:c=l,i(n,t,e,r,u,o,a,c)}var l,f,h,g,p,v,d,m,y,x=vt(a),M=vt(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)l=n[g],l.x<v&&(v=l.x),l.y<d&&(d=l.y),l.x>m&&(m=l.x),l.y>y&&(y=l.y),f.push(l.x),h.push(l.y);else for(g=0;p>g;++g){var _=+x(l=n[g]
,g),b=+M(l,g);v>_&&(v=_),d>b&&(d=b),_>m&&(m=_),b>y&&(y=b),f.push(_),h.push(b)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=Ar();if(k.add=function(n){i(k,n,+x(n,++g),+M(n,g),v,d,m,y)},k.visit=function(n){Cr(n,k,v,d,m,y)},g=-1,null==t){for(;++g<p;)i(k,n[g],f[g],h[g],v,d,m,y);--g}else n.forEach(k.add);return f=h=n=l=null,k}var o,a=Ve,c=Xe;return(o=arguments.length)?(a=kr,c=Er,3===o&&(u=e,r=t,e=t=0),i(n)):(i.x=function(n){return arguments.length?(a=n,i):a},i.y=function(n){return arguments.length?(c=n,i):c},i.extent=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],u=+n[1][1]),i):null==t?null:[[t,e],[r,u]]},i.size=function(n){return arguments.length?(null==n?t=e=r=u=null:(t=e=0,r=+n[0],u=+n[1]),i):null==t?null:[r-t,u-e]},i)},Bo.interpolateRgb=Nr,Bo.interpolateObject=Lr,Bo.interpolateNumber=Tr,Bo.interpolateString=qr;var ns=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;Bo.interpolate=zr,Bo.interpolators=[function(n,t){var e=typeof t;return("strin
g"===e?$a.has(t)||/^(#|rgb\(|hsl\()/.test(t)?Nr:qr:t instanceof V?Nr:"object"===e?Array.isArray(t)?Rr:Lr:Tr)(n,t)}],Bo.interpolateArray=Rr;var ts=function(){return dt},es=Bo.map({linear:ts,poly:Or,quad:function(){return jr},cubic:function(){return Hr},sin:function(){return Yr},exp:function(){return Ir},circle:function(){return Zr},elastic:Vr,back:Xr,bounce:function(){return $r}}),rs=Bo.map({"in":dt,out:Pr,"in-out":Ur,"out-in":function(n){return Ur(Pr(n))}});Bo.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=es.get(e)||ts,r=rs.get(r)||dt,Dr(r(e.apply(null,Wo.call(arguments,1))))},Bo.interpolateHcl=Br,Bo.interpolateHsl=Wr,Bo.interpolateLab=Jr,Bo.interpolateRound=Gr,Bo.transform=function(n){var t=Go.createElementNS(Bo.ns.prefix.svg,"g");return(Bo.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Kr(e?e.matrix:us)})(n)},Kr.prototype.toString=function(){return"tr
anslate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var us={a:1,b:0,c:0,d:1,e:0,f:0};Bo.interpolateTransform=eu,Bo.layout={},Bo.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(iu(n[e]));return t}},Bo.layout.chord=function(){function n(){var n,s,f,h,g,p={},v=[],d=Bo.range(i),m=[];for(e=[],r=[],n=0,h=-1;++h<i;){for(s=0,g=-1;++g<i;)s+=u[h][g];v.push(s),m.push(Bo.range(i)),n+=s}for(o&&d.sort(function(n,t){return o(v[n],v[t])}),a&&m.forEach(function(n,t){n.sort(function(n,e){return a(u[t][n],u[t][e])})}),n=(Aa-l*i)/n,s=0,h=-1;++h<i;){for(f=s,g=-1;++g<i;){var y=d[h],x=m[y][g],M=u[y][x],_=s,b=s+=M*n;p[y+"-"+x]={index:y,subindex:x,startAngle:_,endAngle:b,value:M}}r[y]={index:y,startAngle:f,endAngle:s,value:(s-f)/n},s+=l}for(h=-1;++h<i;)for(g=h-1;++g<i;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,
t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,u,i,o,a,c,s={},l=0;return s.matrix=function(n){return arguments.length?(i=(u=n)&&u.length,e=r=null,s):u},s.padding=function(n){return arguments.length?(l=n,e=r=null,s):l},s.sortGroups=function(n){return arguments.length?(o=n,e=r=null,s):o},s.sortSubgroups=function(n){return arguments.length?(a=n,e=null,s):a},s.sortChords=function(n){return arguments.length?(c=n,e&&t(),s):c},s.chords=function(){return e||n(),e},s.groups=function(){return r||n(),r},s},Bo.layout.force=function(){function n(n){return function(t,e,r,u){if(t.point!==n){var i=t.cx-n.x,o=t.cy-n.y,a=1/Math.sqrt(i*i+o*o);if(v>(u-e)*a){var c=t.charge*a*a;return n.px-=i*c,n.py-=o*c,!0}if(t.point&&isFinite(a)){var c=t.pointCharge*a*a;n.px-=i*c,n.py-=o*c}}return!t.charge}}function t(n){n.px=Bo.event.x,n.py=Bo.event.y,a.resume()}var e,r,u,i,o,a={},c=Bo.dispatch("start","tick","end"),s=[1,1],l=.9,f=is,h=os,g=-30,p=.1,v=.8,d=[],m=[];re
turn a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,v,y,x,M,_=d.length,b=m.length;for(e=0;b>e;++e)a=m[e],f=a.source,h=a.target,x=h.x-f.x,M=h.y-f.y,(v=x*x+M*M)&&(v=r*i[e]*((v=Math.sqrt(v))-u[e])/v,x*=v,M*=v,h.x-=x*(y=f.weight/(h.weight+f.weight)),h.y-=M*y,f.x+=x*(y=1-y),f.y+=M*y);if((y=r*p)&&(x=s[0]/2,M=s[1]/2,e=-1,y))for(;++e<_;)a=d[e],a.x+=(x-a.x)*y,a.y+=(M-a.y)*y;if(g)for(hu(t=Bo.geom.quadtree(d),r,o),e=-1;++e<_;)(a=d[e]).fixed||t.visit(n(a));for(e=-1;++e<_;)a=d[e],a.fixed?(a.x=a.px,a.y=a.py):(a.x-=(a.px-(a.px=a.x))*l,a.y-=(a.py-(a.py=a.y))*l);c.tick({type:"tick",alpha:r})},a.nodes=function(n){return arguments.length?(d=n,a):d},a.links=function(n){return arguments.length?(m=n,a):m},a.size=function(n){return arguments.length?(s=n,a):s},a.linkDistance=function(n){return arguments.length?(f="function"==typeof n?n:+n,a):f},a.distance=a.linkDistance,a.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,a):h},a.
friction=function(n){return arguments.length?(l=+n,a):l},a.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,a):g},a.gravity=function(n){return arguments.length?(p=+n,a):p},a.theta=function(n){return arguments.length?(v=+n,a):v},a.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),Bo.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=m[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,s=o.length;++a<s;)if(!isNaN(i=o[a][n]))return i;return Math.random()*r}var t,e,r,c=d.length,l=m.length,p=s[0],v=s[1];for(t=0;c>t;++t)(r=d[t]).index=t,r.weight=0;for(t=0;l>t;++t)r=m[t],"number"==typeof r.source&&(r.source=d[r.source]),"number"==typeof r.target&&(r.target=d[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=d[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.p
x=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;l>t;++t)u[t]=+f.call(this,m[t],t);else for(t=0;l>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;l>t;++t)i[t]=+h.call(this,m[t],t);else for(t=0;l>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,d[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=Bo.behavior.drag().origin(dt).on("dragstart.force",cu).on("drag.force",t).on("dragend.force",su)),arguments.length?(this.on("mouseover.force",lu).on("mouseout.force",fu).call(e),void 0):e},Bo.rebind(a,c,"on")};var is=20,os=1;Bo.layout.hierarchy=function(){function n(t,o,a){var c=u.call(e,t,o);if(t.depth=o,a.push(t),c&&(s=c.length)){for(var s,l,f=-1,h=t.children=new Array(s),g=0,p=o+1;++f<s;)l=h[f]=n(c[f],p,a),l.parent=t,g+=l.value;r&&h.sort(r),i&&(t.value=g)}else delete t.children,i&&(t.value=+i.call(e,t,o)||0);return t}function
t(n,r){var u=n.children,o=0;if(u&&(a=u.length))for(var a,c=-1,s=r+1;++c<a;)o+=t(u[c],s);else i&&(o=+i.call(e,n,r)||0);return i&&(n.value=o),o}function e(t){var e=[];return n(t,0,e),e}var r=du,u=pu,i=vu;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(u=n,e):u},e.value=function(n){return arguments.length?(i=n,e):i},e.revalue=function(n){return t(n,0),n},e},Bo.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,s=-1;for(r=t.value?r/t.value:0;++s<o;)n(a=i[s],e,c=a.value*r,u),e+=c}}function t(n){var e=n.children,r=0;if(e&&(u=e.length))for(var u,i=-1;++i<u;)r=Math.max(r,t(e[i]));return 1+r}function e(e,i){var o=r.call(this,e,i);return n(o[0],0,u[0],u[1]/t(o[0])),o}var r=Bo.layout.hierarchy(),u=[1,1];return e.size=function(n){return arguments.length?(u=n,e):u},gu(e,r)},Bo.layout.pie=function(){function n(i){var o=i.map(function(e,r){return+t.cal
l(n,e,r)}),a=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof u?u.apply(this,arguments):u)-a)/Bo.sum(o),s=Bo.range(i.length);null!=e&&s.sort(e===as?function(n,t){return o[t]-o[n]}:function(n,t){return e(i[n],i[t])});var l=[];return s.forEach(function(n){var t;l[n]={data:i[n],value:t=o[n],startAngle:a,endAngle:a+=t*c}}),l}var t=Number,e=as,r=0,u=Aa;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n};var as={};Bo.layout.stack=function(){function n(a,c){var s=a.map(function(e,r){return t.call(n,e,r)}),l=s.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,l,c);s=Bo.permute(s,f),l=Bo.permute(l,f);var h,g,p,v=r.call(n,l,c),d=s.length,m=s[0].length;for(g=0;m>g;++g)for(u.call(n,s[0][g],p=v[g],l[0][g][1]),h=1;d>h;++h)u.call(n,s[h][g],p
+=l[h-1][g][1],l[h][g][1]);return a}var t=dt,e=_u,r=bu,u=Mu,i=yu,o=xu;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:cs.get(t)||_u,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:ss.get(t)||bu,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var cs=Bo.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(wu),i=n.map(Su),o=Bo.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,s=[],l=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],s.push(e)):(c+=i[e],l.push(e));return l.reverse().concat(s)},reverse:function(n){return Bo.range(n.length).reverse()},"default":_u}),ss=Bo.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c
},wiggle:function(n){var t,e,r,u,i,o,a,c,s,l=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=s=0,e=1;h>e;++e){for(t=0,u=0;l>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];l>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,s>c&&(s=c)}for(e=0;h>e;++e)g[e]-=s;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:bu});Bo.layout.histogram=function(){function n(n,i){for(var o,a,c=[],s=n.map(e,this),l=r.call(this,s,i),f=u.call(this,l,s,i),i=-1,h=s.length,g=f.length-1,p=t?1:1/h;++i<g;)o=c[i]=[],o.dx=f[i+1]-(o.x=f[i]),o.y=0;if(g>0)for(i=-1;++i<h;)a=s[i],a>=l[0]&&a<=l[1]&&(o=c[Bo.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=Cu,u=Eu;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){r
eturn arguments.length?(r=vt(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return Au(n,t)}:vt(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},Bo.layout.tree=function(){function n(n,i){function o(n,t){var r=n.children,u=n._tree;if(r&&(i=r.length)){for(var i,a,s,l=r[0],f=l,h=-1;++h<i;)s=r[h],o(s,a),f=c(s,a,f),a=s;Uu(n);var g=.5*(l._tree.prelim+s._tree.prelim);t?(u.prelim=t._tree.prelim+e(n,t),u.mod=u.prelim-g):u.prelim=g}else t&&(u.prelim=t._tree.prelim+e(n,t))}function a(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,u=-1;for(t+=n._tree.mod;++u<r;)a(e[u],t)}}function c(n,t,r){if(t){for(var u,i=n,o=n,a=t,c=n.parent.children[0],s=i._tree.mod,l=o._tree.mod,f=a._tree.mod,h=c._tree.mod;a=Tu(a),i=Lu(i),a&&i;)c=Lu(c),o=Tu(o),o._tree.ancestor=n,u=a._tree.prelim+f-i._tree.prelim-s+e(a,i),u>0&&(ju(Hu(a,n,r),n,u),s+=u,l+=u),f+=a._tree.mod,s+=i._tree.mod,h+=c._tree.mod,l+=o._tree.mod;a&&!Tu(o)&&(o._tre
e.thread=a,o._tree.mod+=f-l),i&&!Lu(c)&&(c._tree.thread=i,c._tree.mod+=s-h,r=n)}return r}var s=t.call(this,n,i),l=s[0];Pu(l,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),o(l),a(l,-l._tree.prelim);var f=qu(l,Ru),h=qu(l,zu),g=qu(l,Du),p=f.x-e(f,h)/2,v=h.x+e(h,f)/2,d=g.depth||1;return Pu(l,u?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(v-p)*r[0],n.y=n.depth/d*r[1],delete n._tree}),s}var t=Bo.layout.hierarchy().sort(null).value(null),e=Nu,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},gu(n,t)},Bo.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],s=u[1],l=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Pu(a,function(n){n.r=+l(n.value)}),Pu(a,Zu),r){var f=r*(t?1:Math.m
ax(2*a.r/c,2*a.r/s))/2;Pu(a,function(n){n.r+=f}),Pu(a,Zu),Pu(a,function(n){n.r-=f})}return $u(a,c/2,s/2,t?1:1/Math.max(2*a.r/c,2*a.r/s)),o}var t,e=Bo.layout.hierarchy().sort(Fu),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},gu(n,e)},Bo.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],s=0;Pu(c,function(n){var t=n.children;t&&t.length?(n.x=Ju(t),n.y=Wu(t)):(n.x=o?s+=e(n,o):0,n.y=0,o=n)});var l=Gu(c),f=Ku(c),h=l.x-e(l,f)/2,g=f.x+e(f,l)/2;return Pu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=Bo.layout.hierarchy().sort(null).value(null),e=Nu,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=
function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},gu(n,t)},Bo.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++u<i;)r=(e=n[u]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,s=f(e),l=[],h=i.slice(),p=1/0,v="slice"===g?s.dx:"dice"===g?s.dy:"slice-dice"===g?1&e.depth?s.dy:s.dx:Math.min(s.dx,s.dy);for(n(h,s.dx*s.dy/e.value),l.area=0;(c=h.length)>0;)l.push(o=h[c-1]),l.area+=o.area,"squarify"!==g||(a=r(l,v))<=p?(h.pop(),p=a):(l.area-=l.pop().area,u(l,v,s,!1),v=Math.min(s.dx,s.dy),l.length=l.area=0,p=1/0);l.length&&(u(l,v,s,!0),l.length=l.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++o<a;)(e=n[o].area)&&(i>e&&(i=e),e>u&&(u=e));return
r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,s=e.y,l=t?c(n.area/t):0;if(t==e.dx){for((r||l>e.dy)&&(l=e.dy);++i<o;)u=n[i],u.x=a,u.y=s,u.dy=l,a+=u.dx=Math.min(e.x+e.dx-a,l?c(u.area/l):0);u.z=!0,u.dx+=e.x+e.dx-a,e.y+=l,e.dy-=l}else{for((r||l>e.dx)&&(l=e.dx);++i<o;)u=n[i],u.x=a,u.y=s,u.dx=l,s+=u.dy=Math.min(e.y+e.dy-s,l?c(u.area/l):0);u.z=!1,u.dy+=e.y+e.dy-s,e.x+=l,e.dx-=l}}function i(r){var u=o||a(r),i=u[0];return i.x=0,i.y=0,i.dx=s[0],i.dy=s[1],o&&a.revalue(i),n([i],i.dx*i.dy/i.value),(o?e:t)(i),h&&(o=u),u}var o,a=Bo.layout.hierarchy(),c=Math.round,s=[1,1],l=null,f=Qu,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return i.size=function(n){return arguments.length?(s=n,i):s},i.padding=function(n){function t(t){var e=n.call(i,t,t.depth);return null==e?Qu(t):ni(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){return ni(t,n)}if(!arguments.length)return l;var r;return f=null==(l=n)?Qu:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,i
},i.round=function(n){return arguments.length?(c=n?Math.round:Number,i):c!=Number},i.sticky=function(n){return arguments.length?(h=n,o=null,i):h},i.ratio=function(n){return arguments.length?(p=n,i):p},i.mode=function(n){return arguments.length?(g=n+"",i):g},gu(i,a)},Bo.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=Bo.random.normal.apply(Bo,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=Bo.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},Bo.scale={};var ls={floor:dt,ceil:dt};Bo.scale.linear=function(){return ai([0,1],[0,1],zr,!1)};var fs={s:1,g:1,p:1,r:1,e:1};Bo.scale.log=function(){return vi(Bo.scale.linear().domain([0,1]),10,!0,[1,10])};var hs=Bo.format(".0e")
,gs={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};Bo.scale.pow=function(){return di(Bo.scale.linear(),1,[0,1])},Bo.scale.sqrt=function(){return Bo.scale.pow().exponent(.5)},Bo.scale.ordinal=function(){return yi([],{t:"range",a:[[]]})},Bo.scale.category10=function(){return Bo.scale.ordinal().range(ps)},Bo.scale.category20=function(){return Bo.scale.ordinal().range(vs)},Bo.scale.category20b=function(){return Bo.scale.ordinal().range(ds)},Bo.scale.category20c=function(){return Bo.scale.ordinal().range(ms)};var ps=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(ot),vs=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(ot),ds=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,1083432
4,13528509,14589654].map(ot),ms=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(ot);Bo.scale.quantile=function(){return xi([],[])},Bo.scale.quantize=function(){return Mi(0,1,[0,1])},Bo.scale.threshold=function(){return _i([.5],[0,1])},Bo.scale.identity=function(){return bi([0,1])},Bo.svg={},Bo.svg.arc=function(){function n(){var n=t.apply(this,arguments),i=e.apply(this,arguments),o=r.apply(this,arguments)+ys,a=u.apply(this,arguments)+ys,c=(o>a&&(c=o,o=a,a=c),a-o),s=Ea>c?"0":"1",l=Math.cos(o),f=Math.sin(o),h=Math.cos(a),g=Math.sin(a);return c>=xs?n?"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+i+"A"+i+","+i+" 0 1,1 0,"+-i+"A"+i+","+i+" 0 1,1 0,"+i+"Z":n?"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+s+",0 "+n*l+","+n*f+"Z"
:"M"+i*l+","+i*f+"A"+i+","+i+" 0 "+s+",1 "+i*h+","+i*g+"L0,0"+"Z"}var t=wi,e=Si,r=ki,u=Ei;return n.innerRadius=function(e){return arguments.length?(t=vt(e),n):t},n.outerRadius=function(t){return arguments.length?(e=vt(t),n):e},n.startAngle=function(t){return arguments.length?(r=vt(t),n):r},n.endAngle=function(t){return arguments.length?(u=vt(t),n):u},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,i=(r.apply(this,arguments)+u.apply(this,arguments))/2+ys;return[Math.cos(i)*n,Math.sin(i)*n]},n};var ys=-Ca,xs=Aa-Na;Bo.svg.line=function(){return Ai(dt)};var Ms=Bo.map({linear:Ci,"linear-closed":Ni,step:Li,"step-before":Ti,"step-after":qi,basis:ji,"basis-open":Hi,"basis-closed":Fi,bundle:Oi,cardinal:Di,"cardinal-open":zi,"cardinal-closed":Ri,monotone:$i});Ms.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var _s=[0,2/3,1/3,0],bs=[0,1/3,2/3,0],ws=[0,1/6,2/3,1/6];Bo.svg.line.radial=function(){var n=Ai(Bi);return n.radius=n.x,delete n.x,n.a
ngle=n.y,delete n.y,n},Ti.reverse=qi,qi.reverse=Ti,Bo.svg.area=function(){return Wi(dt)},Bo.svg.area.radial=function(){var n=Wi(Bi);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},Bo.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),s=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,s)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,s.r,s.p0)+r(s.r,s.p1,s.a1-s.a0)+u(s.r,s.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)+ys,l=s.call(n,u,r)+ys;return{r:i,a0:o,a1:l,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(l),i*Math.sin(l)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Ea)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=De,o=Pe,a=Ji,c=ki,s=Ei;return n.radius=function(t){return arguments.length?(a=vt(t),n):a},n.source=function(t){return arguments.length?(i=v
t(t),n):i},n.target=function(t){return arguments.length?(o=vt(t),n):o},n.startAngle=function(t){return arguments.length?(c=vt(t),n):c},n.endAngle=function(t){return arguments.length?(s=vt(t),n):s},n},Bo.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=De,e=Pe,r=Gi;return n.source=function(e){return arguments.length?(t=vt(e),n):t},n.target=function(t){return arguments.length?(e=vt(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},Bo.svg.diagonal.radial=function(){var n=Bo.svg.diagonal(),t=Gi,e=n.projection;return n.projection=function(n){return arguments.length?e(Ki(t=n)):t},n},Bo.svg.symbol=function(){function n(n,r){return(Ss.get(t.call(this,n,r))||to)(e.call(this,n,r))}var t=no,e=Qi;return n.type=function(e){return arguments.length?(t=vt(e),n):t},n.size=function(t){return arguments.length?(e=vt(t),n):e},n};var Ss=B
o.map({circle:to,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Cs)),e=t*Cs;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"
+},"triangle-down":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/As),e=t*As/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});Bo.svg.symbolTypes=Ss.keys();var ks,Es,As=Math.sqrt(3),Cs=Math.tan(30*Ta),Ns=[],Ls=0;Ns.call=ya.call,Ns.empty=ya.empty,Ns.node=ya.node,Ns.size=ya.size,Bo.transition=function(n){return arguments.length?ks?n.transition():n:_a.transition()},Bo.transition.prototype=Ns,Ns.select=function(n){var t,e,r,u=this.id,i=[];n=v(n);for(var o=-1,a=this.length;++o<a;){i.push(t=[]);for(var c=this[o],s=-1,l=c.length;++s<l;)(r=c[s])&&(e=n.call(r,r.__data__,s,o))?("__data__"in r&&(e.__data__=r.__data__),io(e,s,u,r.__transition__[u]),t.push(e)):t.push(null)}return eo(i,u)},Ns.selectAll=function(n){var t,e,r,u,i,o=this.id,a=[];n=d(n);for(var c=-1,s=this.length;++c<s;)for(var l=this[c],f=-1,h=l.length;++f<h;)if(r=l[f]){i=r.__transition__[o],e=n.call(r,r.__data__,f,c),a.push(t=[]);for(var g=-
1,p=e.length;++g<p;)(u=e[g])&&io(u,g,o,i),t.push(u)}return eo(a,o)},Ns.filter=function(n){var t,e,r,u=[];"function"!=typeof n&&(n=A(n));for(var i=0,o=this.length;o>i;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return eo(u,this.id)},Ns.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):N(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Ns.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(
t,n[t]);return this}var o="transform"==n?eu:zr,a=Bo.ns.qualify(n);return ro(this,"attr."+n,t,a.local?i:u)},Ns.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=Bo.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Ns.style=function(n,t,e){function r(){this.style.removeProperty(n)}function u(t){return null==t?r:(t+="",function(){var r,u=Qo.getComputedStyle(this,null).getPropertyValue(n);return u!==t&&(r=zr(u,t),function(t){this.style.setProperty(n,r(t),e)})})}var i=arguments.length;if(3>i){if("string"!=typeof n){2>i&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}return ro(this,"style."+n,t,u)},Ns.styleTween=function(n,t,e){function r(r,u){var i=t.call(this,r,u,Qo.getComputedStyle(this,null).getPropertyValue(n));return i&&function(t){th
is.style.setProperty(n,i(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Ns.text=function(n){return ro(this,"text",n,uo)},Ns.remove=function(){return this.each("end.transition",function(){var n;this.__transition__.count<2&&(n=this.parentNode)&&n.removeChild(this)})},Ns.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=Bo.ease.apply(Bo,arguments)),N(this,function(e){e.__transition__[t].ease=n}))},Ns.delay=function(n){var t=this.id;return N(this,"function"==typeof n?function(e,r,u){e.__transition__[t].delay=+n.call(e,e.__data__,r,u)}:(n=+n,function(e){e.__transition__[t].delay=n}))},Ns.duration=function(n){var t=this.id;return N(this,"function"==typeof n?function(e,r,u){e.__transition__[t].duration=Math.max(1,n.call(e,e.__data__,r,u))}:(n=Math.max(1,n),function(e){e.__transition__[t].duration=n}))},Ns.each=function(n,t){var e=this.id;if(arguments.length<2){var r=Es,u=ks;ks=e,N(this,function(
t,r,u){Es=t.__transition__[e],n.call(t,t.__data__,r,u)}),Es=r,ks=u}else N(this,function(r){var u=r.__transition__[e];(u.event||(u.event=Bo.dispatch("start","end"))).on(n,t)});return this},Ns.transition=function(){for(var n,t,e,r,u=this.id,i=++Ls,o=[],a=0,c=this.length;c>a;a++){o.push(n=[]);for(var t=this[a],s=0,l=t.length;l>s;s++)(e=t[s])&&(r=Object.create(e.__transition__[u]),r.delay+=r.duration,io(e,s,i,r)),n.push(e)}return eo(o,i)},Bo.svg.axis=function(){function n(n){n.each(function(){var n,s=Bo.select(this),l=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):dt:t,p=s.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Na),d=Bo.transition(p.exit()).style("opacity",Na).remove(),m=Bo.transition(p).style("opacity",1),y=ei(f),x=s.selectAll(".domain").data([0]),M=(x.enter().append("path").attr("class","domain"),Bo.transition(x));v.append("
line"),v.append("text");var _=v.select("line"),b=m.select("line"),w=p.select("text").text(g),S=v.select("text"),k=m.select("text");switch(r){case"bottom":n=oo,_.attr("y2",u),S.attr("y",Math.max(u,0)+o),b.attr("x2",0).attr("y2",u),k.attr("x",0).attr("y",Math.max(u,0)+o),w.attr("dy",".71em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+i+"V0H"+y[1]+"V"+i);break;case"top":n=oo,_.attr("y2",-u),S.attr("y",-(Math.max(u,0)+o)),b.attr("x2",0).attr("y2",-u),k.attr("x",0).attr("y",-(Math.max(u,0)+o)),w.attr("dy","0em").style("text-anchor","middle"),M.attr("d","M"+y[0]+","+-i+"V0H"+y[1]+"V"+-i);break;case"left":n=ao,_.attr("x2",-u),S.attr("x",-(Math.max(u,0)+o)),b.attr("x2",-u).attr("y2",0),k.attr("x",-(Math.max(u,0)+o)).attr("y",0),w.attr("dy",".32em").style("text-anchor","end"),M.attr("d","M"+-i+","+y[0]+"H0V"+y[1]+"H"+-i);break;case"right":n=ao,_.attr("x2",u),S.attr("x",Math.max(u,0)+o),b.attr("x2",u).attr("y2",0),k.attr("x",Math.max(u,0)+o).attr("y",0),w.attr("dy",".32em")
.style("text-anchor","start"),M.attr("d","M"+i+","+y[0]+"H0V"+y[1]+"H"+i)}if(f.rangeBand){var E=f,A=E.rangeBand()/2;l=f=function(n){return E(n)+A}}else l.rangeBand?l=f:d.call(n,f);v.call(n,l),m.call(n,f)})}var t,e=Bo.scale.linear(),r=Ts,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in qs?t+"":Ts,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var Ts="bottom",qs={top:1,right:1,bottom:1,left:1};Bo.svg.brush=function(){func
tion n(i){i.each(function(){var i=Bo.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",u).on("touchstart.brush",u),o=i.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),i.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=i.selectAll(".resize").data(d,dt);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return zs[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var l,f=Bo.transition(i),h=Bo.transition(o);c&&(l=ei(c),h.attr("x",l[0]).attr("width",l[1]-l[0]),e(f)),s&&(l=ei(s),h.attr("y",l[0]).attr("height",l[1]-l[0]),r(f)),t(f
)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+l[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function e(n){n.select(".extent").attr("x",l[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",l[1]-l[0])}function r(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function u(){function u(){32==Bo.event.keyCode&&(C||(x=null,L[0]-=l[1],L[1]-=h[1],C=2),f())}function g(){32==Bo.event.keyCode&&2==C&&(L[0]+=l[1],L[1]+=h[1],C=0,f())}function d(){var n=Bo.mouse(_),u=!1;M&&(n[0]+=M[0],n[1]+=M[1]),C||(Bo.event.altKey?(x||(x=[(l[0]+l[1])/2,(h[0]+h[1])/2]),L[0]=l[+(n[0]<x[0])],L[1]=h[+(n[1]<x[1])]):x=null),E&&m(n,c,0)&&(e(S),u=!0),A&&m(n,s,1)&&(r(S),u=!0),u&&(t(S),w({type:"brush",mode:C?"move":"resize"}))}function m(n,t,e){var r,u,a=ei(t),c=a[0],s=a[1],f=L[e],g=e?h:l,d=g[1]-g[0];return C&&(c-=f,s-=d+f),r=(e?v:p)?Math.max(c,Math.min(s,n[e])):n[e],C?u=(r+=f)+d:(x&&(f=Math.max(c,Math.min(s,2*x[e]-r
))),r>f?(u=r,r=f):u=f),g[0]!=r||g[1]!=u?(e?o=null:i=null,g[0]=r,g[1]=u,!0):void 0}function y(){d(),S.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),Bo.select("body").style("cursor",null),T.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),N(),w({type:"brushend"})}var x,M,_=this,b=Bo.select(Bo.event.target),w=a.of(_,arguments),S=Bo.select(_),k=b.datum(),E=!/^(n|s)$/.test(k)&&c,A=!/^(e|w)$/.test(k)&&s,C=b.classed("extent"),N=P(),L=Bo.mouse(_),T=Bo.select(Qo).on("keydown.brush",u).on("keyup.brush",g);if(Bo.event.changedTouches?T.on("touchmove.brush",d).on("touchend.brush",y):T.on("mousemove.brush",d).on("mouseup.brush",y),S.interrupt().selectAll("*").interrupt(),C)L[0]=l[0]-L[0],L[1]=h[0]-L[1];else if(k){var q=+/w$/.test(k),z=+/^n/.test(k);M=[l[1-q]-L[0],h[1-z]-L[1]],L[0]=l[q],L[1]=h[z]}else Bo.event.altKey&&(x=L.slice());S.style("poi
nter-events","none").selectAll(".resize").style("display",null),Bo.select("body").style("cursor",b.style("cursor")),w({type:"brushstart"}),d()}var i,o,a=g(n,"brushstart","brush","brushend"),c=null,s=null,l=[0,0],h=[0,0],p=!0,v=!0,d=Rs[0];return n.event=function(n){n.each(function(){var n=a.of(this,arguments),t={x:l,y:h,i:i,j:o},e=this.__chart__||t;this.__chart__=t,ks?Bo.select(this).transition().each("start.brush",function(){i=e.i,o=e.j,l=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=Rr(l,t.x),r=Rr(h,t.y);return i=o=null,function(u){l=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){i=t.i,o=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(c=t,d=Rs[!c<<1|!s],n):c},n.y=function(t){return arguments.length?(s=t,d=Rs[!c<<1|!s],n):s},n.clamp=function(t){return arguments.length?(c&&s?(p=!!t[0]
,v=!!t[1]):c?p=!!t:s&&(v=!!t),n):c&&s?[p,v]:c?p:s?v:null},n.extent=function(t){var e,r,u,a,f;return arguments.length?(c&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),i=[e,r],c.invert&&(e=c(e),r=c(r)),e>r&&(f=e,e=r,r=f),(e!=l[0]||r!=l[1])&&(l=[e,r])),s&&(u=t[0],a=t[1],c&&(u=u[1],a=a[1]),o=[u,a],s.invert&&(u=s(u),a=s(a)),u>a&&(f=u,u=a,a=f),(u!=h[0]||a!=h[1])&&(h=[u,a])),n):(c&&(i?(e=i[0],r=i[1]):(e=l[0],r=l[1],c.invert&&(e=c.invert(e),r=c.invert(r)),e>r&&(f=e,e=r,r=f))),s&&(o?(u=o[0],a=o[1]):(u=h[0],a=h[1],s.invert&&(u=s.invert(u),a=s.invert(a)),u>a&&(f=u,u=a,a=f))),c&&s?[[e,u],[r,a]]:c?[e,r]:s&&[u,a])},n.clear=function(){return n.empty()||(l=[0,0],h=[0,0],i=o=null),n},n.empty=function(){return!!c&&l[0]==l[1]||!!s&&h[0]==h[1]},Bo.rebind(n,a,"on")};var zs={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Rs=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Ds=Bo.time={},Ps=Date,Us=["Sunday","Monday","
Tuesday","Wednesday","Thursday","Friday","Saturday"];co.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){js.setUTCDate.apply(this._,arguments)},setDay:function(){js.setUTCDay.apply(this._,arguments)},setFullYear:function(){js.setUTCFullYear.apply(this._,arguments)},setHours:function(){js.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){js.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){js.setUTCMinutes.apply(this._,arguments)},set
Month:function(){js.setUTCMonth.apply(this._,arguments)},setSeconds:function(){js.setUTCSeconds.apply(this._,arguments)},setTime:function(){js.setTime.apply(this._,arguments)}};var js=Date.prototype,Hs="%a %b %e %X %Y",Fs="%m/%d/%Y",Os="%H:%M:%S",Ys=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],Is=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],Zs=["January","February","March","April","May","June","July","August","September","October","November","December"],Vs=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];Ds.year=so(function(n){return n=Ds.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),Ds.years=Ds.year.range,Ds.years.utc=Ds.year.utc.range,Ds.day=so(function(n){var t=new Ps(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),Ds.days=Ds.day.range,Ds.days.utc=Ds.day.utc.
range,Ds.dayOfYear=function(n){var t=Ds.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},Us.forEach(function(n,t){n=n.toLowerCase(),t=7-t;var e=Ds[n]=so(function(n){return(n=Ds.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=Ds.year(n).getDay();return Math.floor((Ds.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});Ds[n+"s"]=e.range,Ds[n+"s"].utc=e.utc.range,Ds[n+"OfYear"]=function(n){var e=Ds.year(n).getDay();return Math.floor((Ds.dayOfYear(n)+(e+t)%7)/7)}}),Ds.week=Ds.sunday,Ds.weeks=Ds.sunday.range,Ds.weeks.utc=Ds.sunday.utc.range,Ds.weekOfYear=Ds.sundayOfYear,Ds.format=fo;var Xs=go(Ys),$s=po(Ys),Bs=go(Is),Ws=po(Is),Js=go(Zs),Gs=po(Zs),Ks=go(Vs),Qs=po(Vs),nl=/^%/,tl={"-":"",_:" ",0:"0"},el={a:function(n){return Is[n.getDay()]},A:function(n){return Ys[n.getDay()]},b:function(n){return Vs[n.getMonth()]},B:function(n){return Zs[n.getMonth()]},c:fo(Hs),d:function(n,t){return vo(
n.getDate(),t,2)},e:function(n,t){return vo(n.getDate(),t,2)},H:function(n,t){return vo(n.getHours(),t,2)},I:function(n,t){return vo(n.getHours()%12||12,t,2)},j:function(n,t){return vo(1+Ds.dayOfYear(n),t,3)},L:function(n,t){return vo(n.getMilliseconds(),t,3)},m:function(n,t){return vo(n.getMonth()+1,t,2)},M:function(n,t){return vo(n.getMinutes(),t,2)},p:function(n){return n.getHours()>=12?"PM":"AM"},S:function(n,t){return vo(n.getSeconds(),t,2)},U:function(n,t){return vo(Ds.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return vo(Ds.mondayOfYear(n),t,2)},x:fo(Fs),X:fo(Os),y:function(n,t){return vo(n.getFullYear()%100,t,2)},Y:function(n,t){return vo(n.getFullYear()%1e4,t,4)},Z:Ho,"%":function(){return"%"}},rl={a:mo,A:yo,b:bo,B:wo,c:So,d:qo,e:qo,H:Ro,I:Ro,j:zo,L:Uo,m:To,M:Do,p:jo,S:Po,U:Mo,w:xo,W:_o,x:ko,X:Eo,y:Co,Y:Ao,Z:No,"%":Fo},ul=/^\s*\d+/,il=Bo.map({am:0,pm:1});fo.utc=Oo;var ol=Oo("%Y-%m-%dT%H:%M:%S.%LZ");fo.iso=Date.prototype.toISOString&&+new D
ate("2000-01-01T00:00:00.000Z")?Yo:ol,Yo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Yo.toString=ol.toString,Ds.second=so(function(n){return new Ps(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),Ds.seconds=Ds.second.range,Ds.seconds.utc=Ds.second.utc.range,Ds.minute=so(function(n){return new Ps(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),Ds.minutes=Ds.minute.range,Ds.minutes.utc=Ds.minute.utc.range,Ds.hour=so(function(n){var t=n.getTimezoneOffset()/60;return new Ps(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),Ds.hours=Ds.hour.range,Ds.hours.utc=Ds.hour.utc.range,Ds.month=so(function(n){return n=Ds.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),Ds.months=Ds.month.range,Ds.months.utc=Ds.month.ut
c.range;var al=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],cl=[[Ds.second,1],[Ds.second,5],[Ds.second,15],[Ds.second,30],[Ds.minute,1],[Ds.minute,5],[Ds.minute,15],[Ds.minute,30],[Ds.hour,1],[Ds.hour,3],[Ds.hour,6],[Ds.hour,12],[Ds.day,1],[Ds.day,2],[Ds.week,1],[Ds.month,1],[Ds.month,3],[Ds.year,1]],sl=[[fo("%Y"),Vt],[fo("%B"),function(n){return n.getMonth()}],[fo("%b %d"),function(n){return 1!=n.getDate()}],[fo("%a %d"),function(n){return n.getDay()&&1!=n.getDate()}],[fo("%I %p"),function(n){return n.getHours()}],[fo("%I:%M"),function(n){return n.getMinutes()}],[fo(":%S"),function(n){return n.getSeconds()}],[fo(".%L"),function(n){return n.getMilliseconds()}]],ll=Vo(sl);cl.year=Ds.year,Ds.scale=function(){return Io(Bo.scale.linear(),cl,ll)};var fl={range:function(n,t,e){return Bo.range(+n,+t,e).map(Zo)},floor:dt,ceil:dt},hl=cl.map(function(n){return[n[0].utc,n[1]]}),gl=[[Oo("%Y"),Vt],[Oo("%B"),function(n){return n.getU
TCMonth()}],[Oo("%b %d"),function(n){return 1!=n.getUTCDate()}],[Oo("%a %d"),function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],[Oo("%I %p"),function(n){return n.getUTCHours()}],[Oo("%I:%M"),function(n){return n.getUTCMinutes()}],[Oo(":%S"),function(n){return n.getUTCSeconds()}],[Oo(".%L"),function(n){return n.getUTCMilliseconds()}]],pl=Vo(gl);return hl.year=Ds.year.utc,Ds.scale.utc=function(){return Io(Bo.scale.linear(),hl,pl)},Bo.text=mt(function(n){return n.responseText}),Bo.json=function(n,t){return yt(n,"application/json",Xo,t)},Bo.html=function(n,t){return yt(n,"text/html",$o,t)},Bo.xml=mt(function(n){return n.responseXML}),Bo}();
\ No newline at end of file
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.js b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.js
deleted file mode 100644
index 61e11cc..0000000
--- a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.js
+++ /dev/null
@@ -1,8768 +0,0 @@
-d3 = function() {
- var d3 = {
- version: "3.2.2"
- };
- if (!Date.now) Date.now = function() {
- return +new Date();
- };
- var d3_document = document, d3_documentElement = d3_document.documentElement, d3_window = window;
- try {
- d3_document.createElement("div").style.setProperty("opacity", 0, "");
- } catch (error) {
- var d3_style_prototype = d3_window.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty;
- d3_style_prototype.setProperty = function(name, value, priority) {
- d3_style_setProperty.call(this, name, value + "", priority);
- };
- }
- d3.ascending = function(a, b) {
- return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
- };
- d3.descending = function(a, b) {
- return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
- };
- d3.min = function(array, f) {
- var i = -1, n = array.length, a, b;
- if (arguments.length === 1) {
- while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
- while (++i < n) if ((b = array[i]) != null && a > b) a = b;
- } else {
- while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b;
- }
- return a;
- };
- d3.max = function(array, f) {
- var i = -1, n = array.length, a, b;
- if (arguments.length === 1) {
- while (++i < n && !((a = array[i]) != null && a <= a)) a = undefined;
- while (++i < n) if ((b = array[i]) != null && b > a) a = b;
- } else {
- while (++i < n && !((a = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
- while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b;
- }
- return a;
- };
- d3.extent = function(array, f) {
- var i = -1, n = array.length, a, b, c;
- if (arguments.length === 1) {
- while (++i < n && !((a = c = array[i]) != null && a <= a)) a = c = undefined;
- while (++i < n) if ((b = array[i]) != null) {
- if (a > b) a = b;
- if (c < b) c = b;
- }
- } else {
- while (++i < n && !((a = c = f.call(array, array[i], i)) != null && a <= a)) a = undefined;
- while (++i < n) if ((b = f.call(array, array[i], i)) != null) {
- if (a > b) a = b;
- if (c < b) c = b;
- }
- }
- return [ a, c ];
- };
- d3.sum = function(array, f) {
- var s = 0, n = array.length, a, i = -1;
- if (arguments.length === 1) {
- while (++i < n) if (!isNaN(a = +array[i])) s += a;
- } else {
- while (++i < n) if (!isNaN(a = +f.call(array, array[i], i))) s += a;
- }
- return s;
- };
- function d3_number(x) {
- return x != null && !isNaN(x);
- }
- d3.mean = function(array, f) {
- var n = array.length, a, m = 0, i = -1, j = 0;
- if (arguments.length === 1) {
- while (++i < n) if (d3_number(a = array[i])) m += (a - m) / ++j;
- } else {
- while (++i < n) if (d3_number(a = f.call(array, array[i], i))) m += (a - m) / ++j;
- }
- return j ? m : undefined;
- };
- d3.quantile = function(values, p) {
- var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h;
- return e ? v + e * (values[h] - v) : v;
- };
- d3.median = function(array, f) {
- if (arguments.length > 1) array = array.map(f);
- array = array.filter(d3_number);
- return array.length ? d3.quantile(array.sort(d3.ascending), .5) : undefined;
- };
- d3.bisector = function(f) {
- return {
- left: function(a, x, lo, hi) {
- if (arguments.length < 3) lo = 0;
- if (arguments.length < 4) hi = a.length;
- while (lo < hi) {
- var mid = lo + hi >>> 1;
- if (f.call(a, a[mid], mid) < x) lo = mid + 1; else hi = mid;
- }
- return lo;
- },
- right: function(a, x, lo, hi) {
- if (arguments.length < 3) lo = 0;
- if (arguments.length < 4) hi = a.length;
- while (lo < hi) {
- var mid = lo + hi >>> 1;
- if (x < f.call(a, a[mid], mid)) hi = mid; else lo = mid + 1;
- }
- return lo;
- }
- };
- };
- var d3_bisector = d3.bisector(function(d) {
- return d;
- });
- d3.bisectLeft = d3_bisector.left;
- d3.bisect = d3.bisectRight = d3_bisector.right;
- d3.shuffle = function(array) {
- var m = array.length, t, i;
- while (m) {
- i = Math.random() * m-- | 0;
- t = array[m], array[m] = array[i], array[i] = t;
- }
- return array;
- };
- d3.permute = function(array, indexes) {
- var permutes = [], i = -1, n = indexes.length;
- while (++i < n) permutes[i] = array[indexes[i]];
- return permutes;
- };
- d3.zip = function() {
- if (!(n = arguments.length)) return [];
- for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) {
- for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) {
- zip[j] = arguments[j][i];
- }
- }
- return zips;
- };
- function d3_zipLength(d) {
- return d.length;
- }
- d3.transpose = function(matrix) {
- return d3.zip.apply(d3, matrix);
- };
- d3.keys = function(map) {
- var keys = [];
- for (var key in map) keys.push(key);
- return keys;
- };
- d3.values = function(map) {
- var values = [];
- for (var key in map) values.push(map[key]);
- return values;
- };
- d3.entries = function(map) {
- var entries = [];
- for (var key in map) entries.push({
- key: key,
- value: map[key]
- });
- return entries;
- };
- d3.merge = function(arrays) {
- return Array.prototype.concat.apply([], arrays);
- };
- d3.range = function(start, stop, step) {
- if (arguments.length < 3) {
- step = 1;
- if (arguments.length < 2) {
- stop = start;
- start = 0;
- }
- }
- if ((stop - start) / step === Infinity) throw new Error("infinite range");
- var range = [], k = d3_range_integerScale(Math.abs(step)), i = -1, j;
- start *= k, stop *= k, step *= k;
- if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k);
- return range;
- };
- function d3_range_integerScale(x) {
- var k = 1;
- while (x * k % 1) k *= 10;
- return k;
- }
- function d3_class(ctor, properties) {
- try {
- for (var key in properties) {
- Object.defineProperty(ctor.prototype, key, {
- value: properties[key],
- enumerable: false
- });
- }
- } catch (e) {
- ctor.prototype = properties;
- }
- }
- d3.map = function(object) {
- var map = new d3_Map();
- for (var key in object) map.set(key, object[key]);
- return map;
- };
- function d3_Map() {}
- d3_class(d3_Map, {
- has: function(key) {
- return d3_map_prefix + key in this;
- },
- get: function(key) {
- return this[d3_map_prefix + key];
- },
- set: function(key, value) {
- return this[d3_map_prefix + key] = value;
- },
- remove: function(key) {
- key = d3_map_prefix + key;
- return key in this && delete this[key];
- },
- keys: function() {
- var keys = [];
- this.forEach(function(key) {
- keys.push(key);
- });
- return keys;
- },
- values: function() {
- var values = [];
- this.forEach(function(key, value) {
- values.push(value);
- });
- return values;
- },
- entries: function() {
- var entries = [];
- this.forEach(function(key, value) {
- entries.push({
- key: key,
- value: value
- });
- });
- return entries;
- },
- forEach: function(f) {
- for (var key in this) {
- if (key.charCodeAt(0) === d3_map_prefixCode) {
- f.call(this, key.substring(1), this[key]);
- }
- }
- }
- });
- var d3_map_prefix = "\0", d3_map_prefixCode = d3_map_prefix.charCodeAt(0);
- d3.nest = function() {
- var nest = {}, keys = [], sortKeys = [], sortValues, rollup;
- function map(mapType, array, depth) {
- if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array;
- var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values;
- while (++i < n) {
- if (values = valuesByKey.get(keyValue = key(object = array[i]))) {
- values.push(object);
- } else {
- valuesByKey.set(keyValue, [ object ]);
- }
- }
- if (mapType) {
- object = mapType();
- setter = function(keyValue, values) {
- object.set(keyValue, map(mapType, values, depth));
- };
- } else {
- object = {};
- setter = function(keyValue, values) {
- object[keyValue] = map(mapType, values, depth);
- };
- }
- valuesByKey.forEach(setter);
- return object;
- }
- function entries(map, depth) {
- if (depth >= keys.length) return map;
- var array = [], sortKey = sortKeys[depth++];
- map.forEach(function(key, keyMap) {
- array.push({
- key: key,
- values: entries(keyMap, depth)
- });
- });
- return sortKey ? array.sort(function(a, b) {
- return sortKey(a.key, b.key);
- }) : array;
- }
- nest.map = function(array, mapType) {
- return map(mapType, array, 0);
- };
- nest.entries = function(array) {
- return entries(map(d3.map, array, 0), 0);
- };
- nest.key = function(d) {
- keys.push(d);
- return nest;
- };
- nest.sortKeys = function(order) {
- sortKeys[keys.length - 1] = order;
- return nest;
- };
- nest.sortValues = function(order) {
- sortValues = order;
- return nest;
- };
- nest.rollup = function(f) {
- rollup = f;
- return nest;
- };
- return nest;
- };
- d3.set = function(array) {
- var set = new d3_Set();
- if (array) for (var i = 0; i < array.length; i++) set.add(array[i]);
- return set;
- };
- function d3_Set() {}
- d3_class(d3_Set, {
- has: function(value) {
- return d3_map_prefix + value in this;
- },
- add: function(value) {
- this[d3_map_prefix + value] = true;
- return value;
- },
- remove: function(value) {
- value = d3_map_prefix + value;
- return value in this && delete this[value];
- },
- values: function() {
- var values = [];
- this.forEach(function(value) {
- values.push(value);
- });
- return values;
- },
- forEach: function(f) {
- for (var value in this) {
- if (value.charCodeAt(0) === d3_map_prefixCode) {
- f.call(this, value.substring(1));
- }
- }
- }
- });
- d3.behavior = {};
- d3.rebind = function(target, source) {
- var i = 1, n = arguments.length, method;
- while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]);
- return target;
- };
- function d3_rebind(target, source, method) {
- return function() {
- var value = method.apply(source, arguments);
- return value === source ? target : value;
- };
- }
- function d3_vendorSymbol(object, name) {
- if (name in object) return name;
- name = name.charAt(0).toUpperCase() + name.substring(1);
- for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) {
- var prefixName = d3_vendorPrefixes[i] + name;
- if (prefixName in object) return prefixName;
- }
- }
- var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ];
- var d3_array = d3_arraySlice;
- function d3_arrayCopy(pseudoarray) {
- var i = -1, n = pseudoarray.length, array = [];
- while (++i < n) array.push(pseudoarray[i]);
- return array;
- }
- function d3_arraySlice(pseudoarray) {
- return Array.prototype.slice.call(pseudoarray);
- }
- try {
- d3_array(d3_documentElement.childNodes)[0].nodeType;
- } catch (e) {
- d3_array = d3_arrayCopy;
- }
- var d3_arraySubclass = [].__proto__ ? function(array, prototype) {
- array.__proto__ = prototype;
- } : function(array, prototype) {
- for (var property in prototype) array[property] = prototype[property];
- };
- function d3_noop() {}
- d3.dispatch = function() {
- var dispatch = new d3_dispatch(), i = -1, n = arguments.length;
- while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
- return dispatch;
- };
- function d3_dispatch() {}
- d3_dispatch.prototype.on = function(type, listener) {
- var i = type.indexOf("."), name = "";
- if (i >= 0) {
- name = type.substring(i + 1);
- type = type.substring(0, i);
- }
- if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener);
- if (arguments.length === 2) {
- if (listener == null) for (type in this) {
- if (this.hasOwnProperty(type)) this[type].on(name, null);
- }
- return this;
- }
- };
- function d3_dispatch_event(dispatch) {
- var listeners = [], listenerByName = new d3_Map();
- function event() {
- var z = listeners, i = -1, n = z.length, l;
- while (++i < n) if (l = z[i].on) l.apply(this, arguments);
- return dispatch;
- }
- event.on = function(name, listener) {
- var l = listenerByName.get(name), i;
- if (arguments.length < 2) return l && l.on;
- if (l) {
- l.on = null;
- listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1));
- listenerByName.remove(name);
- }
- if (listener) listeners.push(listenerByName.set(name, {
- on: listener
- }));
- return dispatch;
- };
- return event;
- }
- d3.event = null;
- function d3_eventPreventDefault() {
- d3.event.preventDefault();
- }
- function d3_eventSource() {
- var e = d3.event, s;
- while (s = e.sourceEvent) e = s;
- return e;
- }
- function d3_eventDispatch(target) {
- var dispatch = new d3_dispatch(), i = 0, n = arguments.length;
- while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch);
- dispatch.of = function(thiz, argumentz) {
- return function(e1) {
- try {
- var e0 = e1.sourceEvent = d3.event;
- e1.target = target;
- d3.event = e1;
- dispatch[e1.type].apply(thiz, argumentz);
- } finally {
- d3.event = e0;
- }
- };
- };
- return dispatch;
- }
- d3.requote = function(s) {
- return s.replace(d3_requote_re, "\\$&");
- };
- var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
- function d3_selection(groups) {
- d3_arraySubclass(groups, d3_selectionPrototype);
- return groups;
- }
- var d3_select = function(s, n) {
- return n.querySelector(s);
- }, d3_selectAll = function(s, n) {
- return n.querySelectorAll(s);
- }, d3_selectMatcher = d3_documentElement[d3_vendorSymbol(d3_documentElement, "matchesSelector")], d3_selectMatches = function(n, s) {
- return d3_selectMatcher.call(n, s);
- };
- if (typeof Sizzle === "function") {
- d3_select = function(s, n) {
- return Sizzle(s, n)[0] || null;
- };
- d3_selectAll = function(s, n) {
- return Sizzle.uniqueSort(Sizzle(s, n));
- };
- d3_selectMatches = Sizzle.matchesSelector;
- }
- d3.selection = function() {
- return d3_selectionRoot;
- };
- var d3_selectionPrototype = d3.selection.prototype = [];
- d3_selectionPrototype.select = function(selector) {
- var subgroups = [], subgroup, subnode, group, node;
- if (typeof selector !== "function") selector = d3_selection_selector(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- subgroup.parentNode = (group = this[j]).parentNode;
- for (var i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroup.push(subnode = selector.call(node, node.__data__, i));
- if (subnode && "__data__" in node) subnode.__data__ = node.__data__;
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_selector(selector) {
- return function() {
- return d3_select(selector, this);
- };
- }
- d3_selectionPrototype.selectAll = function(selector) {
- var subgroups = [], subgroup, node;
- if (typeof selector !== "function") selector = d3_selection_selectorAll(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i)));
- subgroup.parentNode = node;
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_selectorAll(selector) {
- return function() {
- return d3_selectAll(selector, this);
- };
- }
- var d3_nsPrefix = {
- svg: "http://www.w3.org/2000/svg",
- xhtml: "http://www.w3.org/1999/xhtml",
- xlink: "http://www.w3.org/1999/xlink",
- xml: "http://www.w3.org/XML/1998/namespace",
- xmlns: "http://www.w3.org/2000/xmlns/"
- };
- d3.ns = {
- prefix: d3_nsPrefix,
- qualify: function(name) {
- var i = name.indexOf(":"), prefix = name;
- if (i >= 0) {
- prefix = name.substring(0, i);
- name = name.substring(i + 1);
- }
- return d3_nsPrefix.hasOwnProperty(prefix) ? {
- space: d3_nsPrefix[prefix],
- local: name
- } : name;
- }
- };
- d3_selectionPrototype.attr = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") {
- var node = this.node();
- name = d3.ns.qualify(name);
- return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name);
- }
- for (value in name) this.each(d3_selection_attr(value, name[value]));
- return this;
- }
- return this.each(d3_selection_attr(name, value));
- };
- function d3_selection_attr(name, value) {
- name = d3.ns.qualify(name);
- function attrNull() {
- this.removeAttribute(name);
- }
- function attrNullNS() {
- this.removeAttributeNS(name.space, name.local);
- }
- function attrConstant() {
- this.setAttribute(name, value);
- }
- function attrConstantNS() {
- this.setAttributeNS(name.space, name.local, value);
- }
- function attrFunction() {
- var x = value.apply(this, arguments);
- if (x == null) this.removeAttribute(name); else this.setAttribute(name, x);
- }
- function attrFunctionNS() {
- var x = value.apply(this, arguments);
- if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x);
- }
- return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant;
- }
- function d3_collapse(s) {
- return s.trim().replace(/\s+/g, " ");
- }
- d3_selectionPrototype.classed = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") {
- var node = this.node(), n = (name = name.trim().split(/^|\s+/g)).length, i = -1;
- if (value = node.classList) {
- while (++i < n) if (!value.contains(name[i])) return false;
- } else {
- value = node.getAttribute("class");
- while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false;
- }
- return true;
- }
- for (value in name) this.each(d3_selection_classed(value, name[value]));
- return this;
- }
- return this.each(d3_selection_classed(name, value));
- };
- function d3_selection_classedRe(name) {
- return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g");
- }
- function d3_selection_classed(name, value) {
- name = name.trim().split(/\s+/).map(d3_selection_classedName);
- var n = name.length;
- function classedConstant() {
- var i = -1;
- while (++i < n) name[i](this, value);
- }
- function classedFunction() {
- var i = -1, x = value.apply(this, arguments);
- while (++i < n) name[i](this, x);
- }
- return typeof value === "function" ? classedFunction : classedConstant;
- }
- function d3_selection_classedName(name) {
- var re = d3_selection_classedRe(name);
- return function(node, value) {
- if (c = node.classList) return value ? c.add(name) : c.remove(name);
- var c = node.getAttribute("class") || "";
- if (value) {
- re.lastIndex = 0;
- if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name));
- } else {
- node.setAttribute("class", d3_collapse(c.replace(re, " ")));
- }
- };
- }
- d3_selectionPrototype.style = function(name, value, priority) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof name !== "string") {
- if (n < 2) value = "";
- for (priority in name) this.each(d3_selection_style(priority, name[priority], value));
- return this;
- }
- if (n < 2) return d3_window.getComputedStyle(this.node(), null).getPropertyValue(name);
- priority = "";
- }
- return this.each(d3_selection_style(name, value, priority));
- };
- function d3_selection_style(name, value, priority) {
- function styleNull() {
- this.style.removeProperty(name);
- }
- function styleConstant() {
- this.style.setProperty(name, value, priority);
- }
- function styleFunction() {
- var x = value.apply(this, arguments);
- if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority);
- }
- return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant;
- }
- d3_selectionPrototype.property = function(name, value) {
- if (arguments.length < 2) {
- if (typeof name === "string") return this.node()[name];
- for (value in name) this.each(d3_selection_property(value, name[value]));
- return this;
- }
- return this.each(d3_selection_property(name, value));
- };
- function d3_selection_property(name, value) {
- function propertyNull() {
- delete this[name];
- }
- function propertyConstant() {
- this[name] = value;
- }
- function propertyFunction() {
- var x = value.apply(this, arguments);
- if (x == null) delete this[name]; else this[name] = x;
- }
- return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant;
- }
- d3_selectionPrototype.text = function(value) {
- return arguments.length ? this.each(typeof value === "function" ? function() {
- var v = value.apply(this, arguments);
- this.textContent = v == null ? "" : v;
- } : value == null ? function() {
- this.textContent = "";
- } : function() {
- this.textContent = value;
- }) : this.node().textContent;
- };
- d3_selectionPrototype.html = function(value) {
- return arguments.length ? this.each(typeof value === "function" ? function() {
- var v = value.apply(this, arguments);
- this.innerHTML = v == null ? "" : v;
- } : value == null ? function() {
- this.innerHTML = "";
- } : function() {
- this.innerHTML = value;
- }) : this.node().innerHTML;
- };
- d3_selectionPrototype.append = function(name) {
- name = d3.ns.qualify(name);
- function append() {
- return this.appendChild(d3_document.createElementNS(this.namespaceURI, name));
- }
- function appendNS() {
- return this.appendChild(d3_document.createElementNS(name.space, name.local));
- }
- return this.select(name.local ? appendNS : append);
- };
- d3_selectionPrototype.insert = function(name, before) {
- name = d3.ns.qualify(name);
- if (typeof before !== "function") before = d3_selection_selector(before);
- function insert(d, i) {
- return this.insertBefore(d3_document.createElementNS(this.namespaceURI, name), before.call(this, d, i));
- }
- function insertNS(d, i) {
- return this.insertBefore(d3_document.createElementNS(name.space, name.local), before.call(this, d, i));
- }
- return this.select(name.local ? insertNS : insert);
- };
- d3_selectionPrototype.remove = function() {
- return this.each(function() {
- var parent = this.parentNode;
- if (parent) parent.removeChild(this);
- });
- };
- d3_selectionPrototype.data = function(value, key) {
- var i = -1, n = this.length, group, node;
- if (!arguments.length) {
- value = new Array(n = (group = this[0]).length);
- while (++i < n) {
- if (node = group[i]) {
- value[i] = node.__data__;
- }
- }
- return value;
- }
- function bind(group, groupData) {
- var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData;
- if (key) {
- var nodeByKeyValue = new d3_Map(), dataByKeyValue = new d3_Map(), keyValues = [], keyValue;
- for (i = -1; ++i < n; ) {
- keyValue = key.call(node = group[i], node.__data__, i);
- if (nodeByKeyValue.has(keyValue)) {
- exitNodes[i] = node;
- } else {
- nodeByKeyValue.set(keyValue, node);
- }
- keyValues.push(keyValue);
- }
- for (i = -1; ++i < m; ) {
- keyValue = key.call(groupData, nodeData = groupData[i], i);
- if (node = nodeByKeyValue.get(keyValue)) {
- updateNodes[i] = node;
- node.__data__ = nodeData;
- } else if (!dataByKeyValue.has(keyValue)) {
- enterNodes[i] = d3_selection_dataNode(nodeData);
- }
- dataByKeyValue.set(keyValue, nodeData);
- nodeByKeyValue.remove(keyValue);
- }
- for (i = -1; ++i < n; ) {
- if (nodeByKeyValue.has(keyValues[i])) {
- exitNodes[i] = group[i];
- }
- }
- } else {
- for (i = -1; ++i < n0; ) {
- node = group[i];
- nodeData = groupData[i];
- if (node) {
- node.__data__ = nodeData;
- updateNodes[i] = node;
- } else {
- enterNodes[i] = d3_selection_dataNode(nodeData);
- }
- }
- for (;i < m; ++i) {
- enterNodes[i] = d3_selection_dataNode(groupData[i]);
- }
- for (;i < n; ++i) {
- exitNodes[i] = group[i];
- }
- }
- enterNodes.update = updateNodes;
- enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode;
- enter.push(enterNodes);
- update.push(updateNodes);
- exit.push(exitNodes);
- }
- var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]);
- if (typeof value === "function") {
- while (++i < n) {
- bind(group = this[i], value.call(group, group.parentNode.__data__, i));
- }
- } else {
- while (++i < n) {
- bind(group = this[i], value);
- }
- }
- update.enter = function() {
- return enter;
- };
- update.exit = function() {
- return exit;
- };
- return update;
- };
- function d3_selection_dataNode(data) {
- return {
- __data__: data
- };
- }
- d3_selectionPrototype.datum = function(value) {
- return arguments.length ? this.property("__data__", value) : this.property("__data__");
- };
- d3_selectionPrototype.filter = function(filter) {
- var subgroups = [], subgroup, group, node;
- if (typeof filter !== "function") filter = d3_selection_filter(filter);
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- subgroup.parentNode = (group = this[j]).parentNode;
- for (var i = 0, n = group.length; i < n; i++) {
- if ((node = group[i]) && filter.call(node, node.__data__, i)) {
- subgroup.push(node);
- }
- }
- }
- return d3_selection(subgroups);
- };
- function d3_selection_filter(selector) {
- return function() {
- return d3_selectMatches(this, selector);
- };
- }
- d3_selectionPrototype.order = function() {
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) {
- if (node = group[i]) {
- if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next);
- next = node;
- }
- }
- }
- return this;
- };
- d3_selectionPrototype.sort = function(comparator) {
- comparator = d3_selection_sortComparator.apply(this, arguments);
- for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator);
- return this.order();
- };
- function d3_selection_sortComparator(comparator) {
- if (!arguments.length) comparator = d3.ascending;
- return function(a, b) {
- return !a - !b || comparator(a.__data__, b.__data__);
- };
- }
- d3_selectionPrototype.each = function(callback) {
- return d3_selection_each(this, function(node, i, j) {
- callback.call(node, node.__data__, i, j);
- });
- };
- function d3_selection_each(groups, callback) {
- for (var j = 0, m = groups.length; j < m; j++) {
- for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) {
- if (node = group[i]) callback(node, i, j);
- }
- }
- return groups;
- }
- d3_selectionPrototype.call = function(callback) {
- var args = d3_array(arguments);
- callback.apply(args[0] = this, args);
- return this;
- };
- d3_selectionPrototype.empty = function() {
- return !this.node();
- };
- d3_selectionPrototype.node = function() {
- for (var j = 0, m = this.length; j < m; j++) {
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- var node = group[i];
- if (node) return node;
- }
- }
- return null;
- };
- d3_selectionPrototype.size = function() {
- var n = 0;
- this.each(function() {
- ++n;
- });
- return n;
- };
- function d3_selection_enter(selection) {
- d3_arraySubclass(selection, d3_selection_enterPrototype);
- return selection;
- }
- var d3_selection_enterPrototype = [];
- d3.selection.enter = d3_selection_enter;
- d3.selection.enter.prototype = d3_selection_enterPrototype;
- d3_selection_enterPrototype.append = d3_selectionPrototype.append;
- d3_selection_enterPrototype.insert = d3_selectionPrototype.insert;
- d3_selection_enterPrototype.empty = d3_selectionPrototype.empty;
- d3_selection_enterPrototype.node = d3_selectionPrototype.node;
- d3_selection_enterPrototype.call = d3_selectionPrototype.call;
- d3_selection_enterPrototype.size = d3_selectionPrototype.size;
- d3_selection_enterPrototype.select = function(selector) {
- var subgroups = [], subgroup, subnode, upgroup, group, node;
- for (var j = -1, m = this.length; ++j < m; ) {
- upgroup = (group = this[j]).update;
- subgroups.push(subgroup = []);
- subgroup.parentNode = group.parentNode;
- for (var i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i));
- subnode.__data__ = node.__data__;
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_selection(subgroups);
- };
- d3_selectionPrototype.transition = function() {
- var id = d3_transitionInheritId || ++d3_transitionId, subgroups = [], subgroup, node, transition = Object.create(d3_transitionInherit);
- transition.time = Date.now();
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) d3_transitionNode(node, i, id, transition);
- subgroup.push(node);
- }
- }
- return d3_transition(subgroups, id);
- };
- d3.select = function(node) {
- var group = [ typeof node === "string" ? d3_select(node, d3_document) : node ];
- group.parentNode = d3_documentElement;
- return d3_selection([ group ]);
- };
- d3.selectAll = function(nodes) {
- var group = d3_array(typeof nodes === "string" ? d3_selectAll(nodes, d3_document) : nodes);
- group.parentNode = d3_documentElement;
- return d3_selection([ group ]);
- };
- var d3_selectionRoot = d3.select(d3_documentElement);
- d3_selectionPrototype.on = function(type, listener, capture) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof type !== "string") {
- if (n < 2) listener = false;
- for (capture in type) this.each(d3_selection_on(capture, type[capture], listener));
- return this;
- }
- if (n < 2) return (n = this.node()["__on" + type]) && n._;
- capture = false;
- }
- return this.each(d3_selection_on(type, listener, capture));
- };
- function d3_selection_on(type, listener, capture) {
- var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener;
- if (i > 0) type = type.substring(0, i);
- var filter = d3_selection_onFilters.get(type);
- if (filter) type = filter, wrap = d3_selection_onFilter;
- function onRemove() {
- var l = this[name];
- if (l) {
- this.removeEventListener(type, l, l.$);
- delete this[name];
- }
- }
- function onAdd() {
- var l = wrap(listener, d3_array(arguments));
- onRemove.call(this);
- this.addEventListener(type, this[name] = l, l.$ = capture);
- l._ = listener;
- }
- function removeAll() {
- var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match;
- for (var name in this) {
- if (match = name.match(re)) {
- var l = this[name];
- this.removeEventListener(match[1], l, l.$);
- delete this[name];
- }
- }
- }
- return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll;
- }
- var d3_selection_onFilters = d3.map({
- mouseenter: "mouseover",
- mouseleave: "mouseout"
- });
- d3_selection_onFilters.forEach(function(k) {
- if ("on" + k in d3_document) d3_selection_onFilters.remove(k);
- });
- function d3_selection_onListener(listener, argumentz) {
- return function(e) {
- var o = d3.event;
- d3.event = e;
- argumentz[0] = this.__data__;
- try {
- listener.apply(this, argumentz);
- } finally {
- d3.event = o;
- }
- };
- }
- function d3_selection_onFilter(listener, argumentz) {
- var l = d3_selection_onListener(listener, argumentz);
- return function(e) {
- var target = this, related = e.relatedTarget;
- if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) {
- l.call(target, e);
- }
- };
- }
- var d3_event_dragSelect = d3_vendorSymbol(d3_documentElement.style, "userSelect");
- function d3_event_dragSuppress(type) {
- var selectstart = "selectstart." + type, dragstart = "dragstart." + type, click = "click." + type, w = d3.select(d3_window).on(selectstart, d3_eventPreventDefault).on(dragstart, d3_eventPreventDefault), style = d3_documentElement.style, select = style[d3_event_dragSelect];
- style[d3_event_dragSelect] = "none";
- return function(suppressClick) {
- w.on(selectstart, null).on(dragstart, null);
- style[d3_event_dragSelect] = select;
- if (suppressClick) {
- function off() {
- w.on(click, null);
- }
- w.on(click, function() {
- d3_eventPreventDefault();
- off();
- }, true);
- setTimeout(off, 0);
- }
- };
- }
- d3.mouse = function(container) {
- return d3_mousePoint(container, d3_eventSource());
- };
- var d3_mouse_bug44083 = /WebKit/.test(d3_window.navigator.userAgent) ? -1 : 0;
- function d3_mousePoint(container, e) {
- var svg = container.ownerSVGElement || container;
- if (svg.createSVGPoint) {
- var point = svg.createSVGPoint();
- if (d3_mouse_bug44083 < 0 && (d3_window.scrollX || d3_window.scrollY)) {
- svg = d3.select("body").append("svg").style({
- position: "absolute",
- top: 0,
- left: 0,
- margin: 0,
- padding: 0,
- border: "none"
- }, "important");
- var ctm = svg[0][0].getScreenCTM();
- d3_mouse_bug44083 = !(ctm.f || ctm.e);
- svg.remove();
- }
- if (d3_mouse_bug44083) {
- point.x = e.pageX;
- point.y = e.pageY;
- } else {
- point.x = e.clientX;
- point.y = e.clientY;
- }
- point = point.matrixTransform(container.getScreenCTM().inverse());
- return [ point.x, point.y ];
- }
- var rect = container.getBoundingClientRect();
- return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ];
- }
- d3.touches = function(container, touches) {
- if (arguments.length < 2) touches = d3_eventSource().touches;
- return touches ? d3_array(touches).map(function(touch) {
- var point = d3_mousePoint(container, touch);
- point.identifier = touch.identifier;
- return point;
- }) : [];
- };
- d3.behavior.drag = function() {
- var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null;
- function drag() {
- this.on("mousedown.drag", mousedown).on("touchstart.drag", mousedown);
- }
- function mousedown() {
- var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null, offset, origin_ = point(), moved = 0, dragRestore = d3_event_dragSuppress(touchId != null ? "drag-" + touchId : "drag");
- var w = d3.select(d3_window).on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
- if (origin) {
- offset = origin.apply(target, arguments);
- offset = [ offset.x - origin_[0], offset.y - origin_[1] ];
- } else {
- offset = [ 0, 0 ];
- }
- event_({
- type: "dragstart"
- });
- function point() {
- var p = target.parentNode;
- return touchId != null ? d3.touches(p).filter(function(p) {
- return p.identifier === touchId;
- })[0] : d3.mouse(p);
- }
- function dragmove() {
- if (!target.parentNode) return dragend();
- var p = point(), dx = p[0] - origin_[0], dy = p[1] - origin_[1];
- moved |= dx | dy;
- origin_ = p;
- event_({
- type: "drag",
- x: p[0] + offset[0],
- y: p[1] + offset[1],
- dx: dx,
- dy: dy
- });
- }
- function dragend() {
- w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null).on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
- dragRestore(moved && d3.event.target === eventTarget);
- event_({
- type: "dragend"
- });
- }
- }
- drag.origin = function(x) {
- if (!arguments.length) return origin;
- origin = x;
- return drag;
- };
- return d3.rebind(drag, event, "on");
- };
- d3.behavior.zoom = function() {
- var translate = [ 0, 0 ], translate0, scale = 1, distance0, scale0, scaleExtent = d3_behavior_zoomInfinity, event = d3_eventDispatch(zoom, "zoom"), x0, x1, y0, y1, touchtime;
- function zoom() {
- this.on("mousedown.zoom", mousedown).on("mousemove.zoom", mousemove).on(d3_behavior_zoomWheel + ".zoom", mousewheel).on("dblclick.zoom", dblclick).on("touchstart.zoom", touchstart).on("touchmove.zoom", touchmove).on("touchend.zoom", touchstart);
- }
- zoom.translate = function(x) {
- if (!arguments.length) return translate;
- translate = x.map(Number);
- rescale();
- return zoom;
- };
- zoom.scale = function(x) {
- if (!arguments.length) return scale;
- scale = +x;
- rescale();
- return zoom;
- };
- zoom.scaleExtent = function(x) {
- if (!arguments.length) return scaleExtent;
- scaleExtent = x == null ? d3_behavior_zoomInfinity : x.map(Number);
- return zoom;
- };
- zoom.x = function(z) {
- if (!arguments.length) return x1;
- x1 = z;
- x0 = z.copy();
- translate = [ 0, 0 ];
- scale = 1;
- return zoom;
- };
- zoom.y = function(z) {
- if (!arguments.length) return y1;
- y1 = z;
- y0 = z.copy();
- translate = [ 0, 0 ];
- scale = 1;
- return zoom;
- };
- function location(p) {
- return [ (p[0] - translate[0]) / scale, (p[1] - translate[1]) / scale ];
- }
- function point(l) {
- return [ l[0] * scale + translate[0], l[1] * scale + translate[1] ];
- }
- function scaleTo(s) {
- scale = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s));
- }
- function translateTo(p, l) {
- l = point(l);
- translate[0] += p[0] - l[0];
- translate[1] += p[1] - l[1];
- }
- function rescale() {
- if (x1) x1.domain(x0.range().map(function(x) {
- return (x - translate[0]) / scale;
- }).map(x0.invert));
- if (y1) y1.domain(y0.range().map(function(y) {
- return (y - translate[1]) / scale;
- }).map(y0.invert));
- }
- function dispatch(event) {
- rescale();
- d3.event.preventDefault();
- event({
- type: "zoom",
- scale: scale,
- translate: translate
- });
- }
- function mousedown() {
- var target = this, event_ = event.of(target, arguments), eventTarget = d3.event.target, moved = 0, w = d3.select(d3_window).on("mousemove.zoom", mousemove).on("mouseup.zoom", mouseup), l = location(d3.mouse(target)), dragRestore = d3_event_dragSuppress("zoom");
- function mousemove() {
- moved = 1;
- translateTo(d3.mouse(target), l);
- dispatch(event_);
- }
- function mouseup() {
- w.on("mousemove.zoom", null).on("mouseup.zoom", null);
- dragRestore(moved && d3.event.target === eventTarget);
- }
- }
- function mousewheel() {
- if (!translate0) translate0 = location(d3.mouse(this));
- scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * scale);
- translateTo(d3.mouse(this), translate0);
- dispatch(event.of(this, arguments));
- }
- function mousemove() {
- translate0 = null;
- }
- function dblclick() {
- var p = d3.mouse(this), l = location(p), k = Math.log(scale) / Math.LN2;
- scaleTo(Math.pow(2, d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1));
- translateTo(p, l);
- dispatch(event.of(this, arguments));
- }
- function touchstart() {
- var touches = d3.touches(this), now = Date.now();
- scale0 = scale;
- translate0 = {};
- distance0 = 0;
- touches.forEach(function(t) {
- translate0[t.identifier] = location(t);
- });
- if (touches.length === 1) {
- if (now - touchtime < 500) {
- var p = touches[0], l = location(touches[0]);
- scaleTo(scale * 2);
- translateTo(p, l);
- dispatch(event.of(this, arguments));
- }
- touchtime = now;
- } else if (touches.length > 1) {
- var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1];
- distance0 = dx * dx + dy * dy;
- }
- }
- function touchmove() {
- var touches = d3.touches(this), p0 = touches[0], l0 = translate0[p0.identifier];
- if (p1 = touches[1]) {
- var p1, l1 = translate0[p1.identifier], scale1 = d3.event.scale;
- if (scale1 == null) {
- var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1;
- scale1 = distance0 && Math.sqrt(distance1 / distance0);
- }
- p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ];
- l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ];
- scaleTo(scale1 * scale0);
- }
- translateTo(p0, l0);
- touchtime = null;
- dispatch(event.of(this, arguments));
- }
- return d3.rebind(zoom, event, "on");
- };
- var d3_behavior_zoomInfinity = [ 0, Infinity ];
- var d3_behavior_zoomDelta, d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() {
- return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1);
- }, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() {
- return d3.event.wheelDelta;
- }, "mousewheel") : (d3_behavior_zoomDelta = function() {
- return -d3.event.detail;
- }, "MozMousePixelScroll");
- function d3_Color() {}
- d3_Color.prototype.toString = function() {
- return this.rgb() + "";
- };
- d3.hsl = function(h, s, l) {
- return arguments.length === 1 ? h instanceof d3_Hsl ? d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : d3_hsl(+h, +s, +l);
- };
- function d3_hsl(h, s, l) {
- return new d3_Hsl(h, s, l);
- }
- function d3_Hsl(h, s, l) {
- this.h = h;
- this.s = s;
- this.l = l;
- }
- var d3_hslPrototype = d3_Hsl.prototype = new d3_Color();
- d3_hslPrototype.brighter = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return d3_hsl(this.h, this.s, this.l / k);
- };
- d3_hslPrototype.darker = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return d3_hsl(this.h, this.s, k * this.l);
- };
- d3_hslPrototype.rgb = function() {
- return d3_hsl_rgb(this.h, this.s, this.l);
- };
- function d3_hsl_rgb(h, s, l) {
- var m1, m2;
- h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h;
- s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s;
- l = l < 0 ? 0 : l > 1 ? 1 : l;
- m2 = l <= .5 ? l * (1 + s) : l + s - l * s;
- m1 = 2 * l - m2;
- function v(h) {
- if (h > 360) h -= 360; else if (h < 0) h += 360;
- if (h < 60) return m1 + (m2 - m1) * h / 60;
- if (h < 180) return m2;
- if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60;
- return m1;
- }
- function vv(h) {
- return Math.round(v(h) * 255);
- }
- return d3_rgb(vv(h + 120), vv(h), vv(h - 120));
- }
- var Ï = Math.PI, ε = 1e-6, ε2 = ε * ε, d3_radians = Ï / 180, d3_degrees = 180 / Ï;
- function d3_sgn(x) {
- return x > 0 ? 1 : x < 0 ? -1 : 0;
- }
- function d3_acos(x) {
- return x > 1 ? 0 : x < -1 ? Ï : Math.acos(x);
- }
- function d3_asin(x) {
- return x > 1 ? Ï / 2 : x < -1 ? -Ï / 2 : Math.asin(x);
- }
- function d3_sinh(x) {
- return (Math.exp(x) - Math.exp(-x)) / 2;
- }
- function d3_cosh(x) {
- return (Math.exp(x) + Math.exp(-x)) / 2;
- }
- function d3_haversin(x) {
- return (x = Math.sin(x / 2)) * x;
- }
- d3.hcl = function(h, c, l) {
- return arguments.length === 1 ? h instanceof d3_Hcl ? d3_hcl(h.h, h.c, h.l) : h instanceof d3_Lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : d3_hcl(+h, +c, +l);
- };
- function d3_hcl(h, c, l) {
- return new d3_Hcl(h, c, l);
- }
- function d3_Hcl(h, c, l) {
- this.h = h;
- this.c = c;
- this.l = l;
- }
- var d3_hclPrototype = d3_Hcl.prototype = new d3_Color();
- d3_hclPrototype.brighter = function(k) {
- return d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)));
- };
- d3_hclPrototype.darker = function(k) {
- return d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)));
- };
- d3_hclPrototype.rgb = function() {
- return d3_hcl_lab(this.h, this.c, this.l).rgb();
- };
- function d3_hcl_lab(h, c, l) {
- if (isNaN(h)) h = 0;
- if (isNaN(c)) c = 0;
- return d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c);
- }
- d3.lab = function(l, a, b) {
- return arguments.length === 1 ? l instanceof d3_Lab ? d3_lab(l.l, l.a, l.b) : l instanceof d3_Hcl ? d3_hcl_lab(l.l, l.c, l.h) : d3_rgb_lab((l = d3.rgb(l)).r, l.g, l.b) : d3_lab(+l, +a, +b);
- };
- function d3_lab(l, a, b) {
- return new d3_Lab(l, a, b);
- }
- function d3_Lab(l, a, b) {
- this.l = l;
- this.a = a;
- this.b = b;
- }
- var d3_lab_K = 18;
- var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883;
- var d3_labPrototype = d3_Lab.prototype = new d3_Color();
- d3_labPrototype.brighter = function(k) {
- return d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
- };
- d3_labPrototype.darker = function(k) {
- return d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b);
- };
- d3_labPrototype.rgb = function() {
- return d3_lab_rgb(this.l, this.a, this.b);
- };
- function d3_lab_rgb(l, a, b) {
- var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200;
- x = d3_lab_xyz(x) * d3_lab_X;
- y = d3_lab_xyz(y) * d3_lab_Y;
- z = d3_lab_xyz(z) * d3_lab_Z;
- return d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z));
- }
- function d3_lab_hcl(l, a, b) {
- return l > 0 ? d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : d3_hcl(NaN, NaN, l);
- }
- function d3_lab_xyz(x) {
- return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037;
- }
- function d3_xyz_lab(x) {
- return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29;
- }
- function d3_xyz_rgb(r) {
- return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055));
- }
- d3.rgb = function(r, g, b) {
- return arguments.length === 1 ? r instanceof d3_Rgb ? d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : d3_rgb(~~r, ~~g, ~~b);
- };
- function d3_rgb(r, g, b) {
- return new d3_Rgb(r, g, b);
- }
- function d3_Rgb(r, g, b) {
- this.r = r;
- this.g = g;
- this.b = b;
- }
- var d3_rgbPrototype = d3_Rgb.prototype = new d3_Color();
- d3_rgbPrototype.brighter = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- var r = this.r, g = this.g, b = this.b, i = 30;
- if (!r && !g && !b) return d3_rgb(i, i, i);
- if (r && r < i) r = i;
- if (g && g < i) g = i;
- if (b && b < i) b = i;
- return d3_rgb(Math.min(255, Math.floor(r / k)), Math.min(255, Math.floor(g / k)), Math.min(255, Math.floor(b / k)));
- };
- d3_rgbPrototype.darker = function(k) {
- k = Math.pow(.7, arguments.length ? k : 1);
- return d3_rgb(Math.floor(k * this.r), Math.floor(k * this.g), Math.floor(k * this.b));
- };
- d3_rgbPrototype.hsl = function() {
- return d3_rgb_hsl(this.r, this.g, this.b);
- };
- d3_rgbPrototype.toString = function() {
- return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b);
- };
- function d3_rgb_hex(v) {
- return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16);
- }
- function d3_rgb_parse(format, rgb, hsl) {
- var r = 0, g = 0, b = 0, m1, m2, name;
- m1 = /([a-z]+)\((.*)\)/i.exec(format);
- if (m1) {
- m2 = m1[2].split(",");
- switch (m1[1]) {
- case "hsl":
- {
- return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100);
- }
-
- case "rgb":
- {
- return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2]));
- }
- }
- }
- if (name = d3_rgb_names.get(format)) return rgb(name.r, name.g, name.b);
- if (format != null && format.charAt(0) === "#") {
- if (format.length === 4) {
- r = format.charAt(1);
- r += r;
- g = format.charAt(2);
- g += g;
- b = format.charAt(3);
- b += b;
- } else if (format.length === 7) {
- r = format.substring(1, 3);
- g = format.substring(3, 5);
- b = format.substring(5, 7);
- }
- r = parseInt(r, 16);
- g = parseInt(g, 16);
- b = parseInt(b, 16);
- }
- return rgb(r, g, b);
- }
- function d3_rgb_hsl(r, g, b) {
- var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2;
- if (d) {
- s = l < .5 ? d / (max + min) : d / (2 - max - min);
- if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4;
- h *= 60;
- } else {
- h = NaN;
- s = l > 0 && l < 1 ? 0 : h;
- }
- return d3_hsl(h, s, l);
- }
- function d3_rgb_lab(r, g, b) {
- r = d3_rgb_xyz(r);
- g = d3_rgb_xyz(g);
- b = d3_rgb_xyz(b);
- var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z);
- return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z));
- }
- function d3_rgb_xyz(r) {
- return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4);
- }
- function d3_rgb_parseNumber(c) {
- var f = parseFloat(c);
- return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f;
- }
- var d3_rgb_names = d3.map({
- aliceblue: "#f0f8ff",
- antiquewhite: "#faebd7",
- aqua: "#00ffff",
- aquamarine: "#7fffd4",
- azure: "#f0ffff",
- beige: "#f5f5dc",
- bisque: "#ffe4c4",
- black: "#000000",
- blanchedalmond: "#ffebcd",
- blue: "#0000ff",
- blueviolet: "#8a2be2",
- brown: "#a52a2a",
- burlywood: "#deb887",
- cadetblue: "#5f9ea0",
- chartreuse: "#7fff00",
- chocolate: "#d2691e",
- coral: "#ff7f50",
- cornflowerblue: "#6495ed",
- cornsilk: "#fff8dc",
- crimson: "#dc143c",
- cyan: "#00ffff",
- darkblue: "#00008b",
- darkcyan: "#008b8b",
- darkgoldenrod: "#b8860b",
- darkgray: "#a9a9a9",
- darkgreen: "#006400",
- darkgrey: "#a9a9a9",
- darkkhaki: "#bdb76b",
- darkmagenta: "#8b008b",
- darkolivegreen: "#556b2f",
- darkorange: "#ff8c00",
- darkorchid: "#9932cc",
- darkred: "#8b0000",
- darksalmon: "#e9967a",
- darkseagreen: "#8fbc8f",
- darkslateblue: "#483d8b",
- darkslategray: "#2f4f4f",
- darkslategrey: "#2f4f4f",
- darkturquoise: "#00ced1",
- darkviolet: "#9400d3",
- deeppink: "#ff1493",
- deepskyblue: "#00bfff",
- dimgray: "#696969",
- dimgrey: "#696969",
- dodgerblue: "#1e90ff",
- firebrick: "#b22222",
- floralwhite: "#fffaf0",
- forestgreen: "#228b22",
- fuchsia: "#ff00ff",
- gainsboro: "#dcdcdc",
- ghostwhite: "#f8f8ff",
- gold: "#ffd700",
- goldenrod: "#daa520",
- gray: "#808080",
- green: "#008000",
- greenyellow: "#adff2f",
- grey: "#808080",
- honeydew: "#f0fff0",
- hotpink: "#ff69b4",
- indianred: "#cd5c5c",
- indigo: "#4b0082",
- ivory: "#fffff0",
- khaki: "#f0e68c",
- lavender: "#e6e6fa",
- lavenderblush: "#fff0f5",
- lawngreen: "#7cfc00",
- lemonchiffon: "#fffacd",
- lightblue: "#add8e6",
- lightcoral: "#f08080",
- lightcyan: "#e0ffff",
- lightgoldenrodyellow: "#fafad2",
- lightgray: "#d3d3d3",
- lightgreen: "#90ee90",
- lightgrey: "#d3d3d3",
- lightpink: "#ffb6c1",
- lightsalmon: "#ffa07a",
- lightseagreen: "#20b2aa",
- lightskyblue: "#87cefa",
- lightslategray: "#778899",
- lightslategrey: "#778899",
- lightsteelblue: "#b0c4de",
- lightyellow: "#ffffe0",
- lime: "#00ff00",
- limegreen: "#32cd32",
- linen: "#faf0e6",
- magenta: "#ff00ff",
- maroon: "#800000",
- mediumaquamarine: "#66cdaa",
- mediumblue: "#0000cd",
- mediumorchid: "#ba55d3",
- mediumpurple: "#9370db",
- mediumseagreen: "#3cb371",
- mediumslateblue: "#7b68ee",
- mediumspringgreen: "#00fa9a",
- mediumturquoise: "#48d1cc",
- mediumvioletred: "#c71585",
- midnightblue: "#191970",
- mintcream: "#f5fffa",
- mistyrose: "#ffe4e1",
- moccasin: "#ffe4b5",
- navajowhite: "#ffdead",
- navy: "#000080",
- oldlace: "#fdf5e6",
- olive: "#808000",
- olivedrab: "#6b8e23",
- orange: "#ffa500",
- orangered: "#ff4500",
- orchid: "#da70d6",
- palegoldenrod: "#eee8aa",
- palegreen: "#98fb98",
- paleturquoise: "#afeeee",
- palevioletred: "#db7093",
- papayawhip: "#ffefd5",
- peachpuff: "#ffdab9",
- peru: "#cd853f",
- pink: "#ffc0cb",
- plum: "#dda0dd",
- powderblue: "#b0e0e6",
- purple: "#800080",
- red: "#ff0000",
- rosybrown: "#bc8f8f",
- royalblue: "#4169e1",
- saddlebrown: "#8b4513",
- salmon: "#fa8072",
- sandybrown: "#f4a460",
- seagreen: "#2e8b57",
- seashell: "#fff5ee",
- sienna: "#a0522d",
- silver: "#c0c0c0",
- skyblue: "#87ceeb",
- slateblue: "#6a5acd",
- slategray: "#708090",
- slategrey: "#708090",
- snow: "#fffafa",
- springgreen: "#00ff7f",
- steelblue: "#4682b4",
- tan: "#d2b48c",
- teal: "#008080",
- thistle: "#d8bfd8",
- tomato: "#ff6347",
- turquoise: "#40e0d0",
- violet: "#ee82ee",
- wheat: "#f5deb3",
- white: "#ffffff",
- whitesmoke: "#f5f5f5",
- yellow: "#ffff00",
- yellowgreen: "#9acd32"
- });
- d3_rgb_names.forEach(function(key, value) {
- d3_rgb_names.set(key, d3_rgb_parse(value, d3_rgb, d3_hsl_rgb));
- });
- function d3_functor(v) {
- return typeof v === "function" ? v : function() {
- return v;
- };
- }
- d3.functor = d3_functor;
- function d3_identity(d) {
- return d;
- }
- d3.xhr = d3_xhrType(d3_identity);
- function d3_xhrType(response) {
- return function(url, mimeType, callback) {
- if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType,
- mimeType = null;
- return d3_xhr(url, mimeType, response, callback);
- };
- }
- function d3_xhr(url, mimeType, response, callback) {
- var xhr = {}, dispatch = d3.dispatch("progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null;
- if (d3_window.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest();
- "onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() {
- request.readyState > 3 && respond();
- };
- function respond() {
- var status = request.status, result;
- if (!status && request.responseText || status >= 200 && status < 300 || status === 304) {
- try {
- result = response.call(xhr, request);
- } catch (e) {
- dispatch.error.call(xhr, e);
- return;
- }
- dispatch.load.call(xhr, result);
- } else {
- dispatch.error.call(xhr, request);
- }
- }
- request.onprogress = function(event) {
- var o = d3.event;
- d3.event = event;
- try {
- dispatch.progress.call(xhr, request);
- } finally {
- d3.event = o;
- }
- };
- xhr.header = function(name, value) {
- name = (name + "").toLowerCase();
- if (arguments.length < 2) return headers[name];
- if (value == null) delete headers[name]; else headers[name] = value + "";
- return xhr;
- };
- xhr.mimeType = function(value) {
- if (!arguments.length) return mimeType;
- mimeType = value == null ? null : value + "";
- return xhr;
- };
- xhr.responseType = function(value) {
- if (!arguments.length) return responseType;
- responseType = value;
- return xhr;
- };
- xhr.response = function(value) {
- response = value;
- return xhr;
- };
- [ "get", "post" ].forEach(function(method) {
- xhr[method] = function() {
- return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments)));
- };
- });
- xhr.send = function(method, data, callback) {
- if (arguments.length === 2 && typeof data === "function") callback = data, data = null;
- request.open(method, url, true);
- if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*";
- if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]);
- if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType);
- if (responseType != null) request.responseType = responseType;
- if (callback != null) xhr.on("error", callback).on("load", function(request) {
- callback(null, request);
- });
- request.send(data == null ? null : data);
- return xhr;
- };
- xhr.abort = function() {
- request.abort();
- return xhr;
- };
- d3.rebind(xhr, dispatch, "on");
- return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback));
- }
- function d3_xhr_fixCallback(callback) {
- return callback.length === 1 ? function(error, request) {
- callback(error == null ? request : null);
- } : callback;
- }
- d3.dsv = function(delimiter, mimeType) {
- var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0);
- function dsv(url, row, callback) {
- if (arguments.length < 3) callback = row, row = null;
- var xhr = d3.xhr(url, mimeType, callback);
- xhr.row = function(_) {
- return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row;
- };
- return xhr.row(row);
- }
- function response(request) {
- return dsv.parse(request.responseText);
- }
- function typedResponse(f) {
- return function(request) {
- return dsv.parse(request.responseText, f);
- };
- }
- dsv.parse = function(text, f) {
- var o;
- return dsv.parseRows(text, function(row, i) {
- if (o) return o(row, i - 1);
- var a = new Function("d", "return {" + row.map(function(name, i) {
- return JSON.stringify(name) + ": d[" + i + "]";
- }).join(",") + "}");
- o = f ? function(row, i) {
- return f(a(row), i);
- } : a;
- });
- };
- dsv.parseRows = function(text, f) {
- var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol;
- function token() {
- if (I >= N) return EOF;
- if (eol) return eol = false, EOL;
- var j = I;
- if (text.charCodeAt(j) === 34) {
- var i = j;
- while (i++ < N) {
- if (text.charCodeAt(i) === 34) {
- if (text.charCodeAt(i + 1) !== 34) break;
- ++i;
- }
- }
- I = i + 2;
- var c = text.charCodeAt(i + 1);
- if (c === 13) {
- eol = true;
- if (text.charCodeAt(i + 2) === 10) ++I;
- } else if (c === 10) {
- eol = true;
- }
- return text.substring(j + 1, i).replace(/""/g, '"');
- }
- while (I < N) {
- var c = text.charCodeAt(I++), k = 1;
- if (c === 10) eol = true; else if (c === 13) {
- eol = true;
- if (text.charCodeAt(I) === 10) ++I, ++k;
- } else if (c !== delimiterCode) continue;
- return text.substring(j, I - k);
- }
- return text.substring(j);
- }
- while ((t = token()) !== EOF) {
- var a = [];
- while (t !== EOL && t !== EOF) {
- a.push(t);
- t = token();
- }
- if (f && !(a = f(a, n++))) continue;
- rows.push(a);
- }
- return rows;
- };
- dsv.format = function(rows) {
- if (Array.isArray(rows[0])) return dsv.formatRows(rows);
- var fieldSet = new d3_Set(), fields = [];
- rows.forEach(function(row) {
- for (var field in row) {
- if (!fieldSet.has(field)) {
- fields.push(fieldSet.add(field));
- }
- }
- });
- return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) {
- return fields.map(function(field) {
- return formatValue(row[field]);
- }).join(delimiter);
- })).join("\n");
- };
- dsv.formatRows = function(rows) {
- return rows.map(formatRow).join("\n");
- };
- function formatRow(row) {
- return row.map(formatValue).join(delimiter);
- }
- function formatValue(text) {
- return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text;
- }
- return dsv;
- };
- d3.csv = d3.dsv(",", "text/csv");
- d3.tsv = d3.dsv(" ", "text/tab-separated-values");
- var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout;
- d3.timer = function(callback, delay, then) {
- if (arguments.length < 3) {
- if (arguments.length < 2) delay = 0; else if (!isFinite(delay)) return;
- then = Date.now();
- }
- var time = then + delay;
- var timer = {
- callback: callback,
- time: time,
- next: null
- };
- if (d3_timer_queueTail) d3_timer_queueTail.next = timer; else d3_timer_queueHead = timer;
- d3_timer_queueTail = timer;
- if (!d3_timer_interval) {
- d3_timer_timeout = clearTimeout(d3_timer_timeout);
- d3_timer_interval = 1;
- d3_timer_frame(d3_timer_step);
- }
- };
- function d3_timer_step() {
- var now = d3_timer_mark(), delay = d3_timer_sweep() - now;
- if (delay > 24) {
- if (isFinite(delay)) {
- clearTimeout(d3_timer_timeout);
- d3_timer_timeout = setTimeout(d3_timer_step, delay);
- }
- d3_timer_interval = 0;
- } else {
- d3_timer_interval = 1;
- d3_timer_frame(d3_timer_step);
- }
- }
- d3.timer.flush = function() {
- d3_timer_mark();
- d3_timer_sweep();
- };
- function d3_timer_mark() {
- var now = Date.now(), timer = d3_timer_queueHead;
- while (timer) {
- if (now >= timer.time) timer.flush = timer.callback(now - timer.time);
- timer = timer.next;
- }
- return now;
- }
- function d3_timer_sweep() {
- var t0, t1 = d3_timer_queueHead, time = Infinity;
- while (t1) {
- if (t1.flush) {
- t1 = t0 ? t0.next = t1.next : d3_timer_queueHead = t1.next;
- } else {
- if (t1.time < time) time = t1.time;
- t1 = (t0 = t1).next;
- }
- }
- d3_timer_queueTail = t0;
- return time;
- }
- var d3_timer_frame = d3_window[d3_vendorSymbol(d3_window, "requestAnimationFrame")] || function(callback) {
- setTimeout(callback, 17);
- };
- var d3_format_decimalPoint = ".", d3_format_thousandsSeparator = ",", d3_format_grouping = [ 3, 3 ];
- var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix);
- d3.formatPrefix = function(value, precision) {
- var i = 0;
- if (value) {
- if (value < 0) value *= -1;
- if (precision) value = d3.round(value, d3_format_precision(value, precision));
- i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
- i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
- }
- return d3_formatPrefixes[8 + i / 3];
- };
- function d3_formatPrefix(d, i) {
- var k = Math.pow(10, Math.abs(8 - i) * 3);
- return {
- scale: i > 8 ? function(d) {
- return d / k;
- } : function(d) {
- return d * k;
- },
- symbol: d
- };
- }
- d3.round = function(x, n) {
- return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x);
- };
- d3.format = function(specifier) {
- var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "", basePrefix = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, suffix = "", integer = false;
- if (precision) precision = +precision.substring(1);
- if (zfill || fill === "0" && align === "=") {
- zfill = fill = "0";
- align = "=";
- if (comma) width -= Math.floor((width - 1) / 4);
- }
- switch (type) {
- case "n":
- comma = true;
- type = "g";
- break;
-
- case "%":
- scale = 100;
- suffix = "%";
- type = "f";
- break;
-
- case "p":
- scale = 100;
- suffix = "%";
- type = "r";
- break;
-
- case "b":
- case "o":
- case "x":
- case "X":
- if (basePrefix) basePrefix = "0" + type.toLowerCase();
-
- case "c":
- case "d":
- integer = true;
- precision = 0;
- break;
-
- case "s":
- scale = -1;
- type = "r";
- break;
- }
- if (basePrefix === "#") basePrefix = "";
- if (type == "r" && !precision) type = "g";
- if (precision != null) {
- if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision));
- }
- type = d3_format_types.get(type) || d3_format_typeDefault;
- var zcomma = zfill && comma;
- return function(value) {
- if (integer && value % 1) return "";
- var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign;
- if (scale < 0) {
- var prefix = d3.formatPrefix(value, precision);
- value = prefix.scale(value);
- suffix = prefix.symbol;
- } else {
- value *= scale;
- }
- value = type(value, precision);
- if (!zfill && comma) value = d3_format_group(value);
- var length = basePrefix.length + value.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : "";
- if (zcomma) value = d3_format_group(padding + value);
- if (d3_format_decimalPoint) value.replace(".", d3_format_decimalPoint);
- negative += basePrefix;
- return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + suffix;
- };
- };
- var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
- var d3_format_types = d3.map({
- b: function(x) {
- return x.toString(2);
- },
- c: function(x) {
- return String.fromCharCode(x);
- },
- o: function(x) {
- return x.toString(8);
- },
- x: function(x) {
- return x.toString(16);
- },
- X: function(x) {
- return x.toString(16).toUpperCase();
- },
- g: function(x, p) {
- return x.toPrecision(p);
- },
- e: function(x, p) {
- return x.toExponential(p);
- },
- f: function(x, p) {
- return x.toFixed(p);
- },
- r: function(x, p) {
- return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p))));
- }
- });
- function d3_format_precision(x, p) {
- return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1);
- }
- function d3_format_typeDefault(x) {
- return x + "";
- }
- var d3_format_group = d3_identity;
- if (d3_format_grouping) {
- var d3_format_groupingLength = d3_format_grouping.length;
- d3_format_group = function(value) {
- var i = value.lastIndexOf("."), f = i >= 0 ? "." + value.substring(i + 1) : (i = value.length,
- ""), t = [], j = 0, g = d3_format_grouping[0];
- while (i > 0 && g > 0) {
- t.push(value.substring(i -= g, i + g));
- g = d3_format_grouping[j = (j + 1) % d3_format_groupingLength];
- }
- return t.reverse().join(d3_format_thousandsSeparator || "") + f;
- };
- }
- d3.geo = {};
- function d3_adder() {}
- d3_adder.prototype = {
- s: 0,
- t: 0,
- add: function(y) {
- d3_adderSum(y, this.t, d3_adderTemp);
- d3_adderSum(d3_adderTemp.s, this.s, this);
- if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t;
- },
- reset: function() {
- this.s = this.t = 0;
- },
- valueOf: function() {
- return this.s;
- }
- };
- var d3_adderTemp = new d3_adder();
- function d3_adderSum(a, b, o) {
- var x = o.s = a + b, bv = x - a, av = x - bv;
- o.t = a - av + (b - bv);
- }
- d3.geo.stream = function(object, listener) {
- if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) {
- d3_geo_streamObjectType[object.type](object, listener);
- } else {
- d3_geo_streamGeometry(object, listener);
- }
- };
- function d3_geo_streamGeometry(geometry, listener) {
- if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) {
- d3_geo_streamGeometryType[geometry.type](geometry, listener);
- }
- }
- var d3_geo_streamObjectType = {
- Feature: function(feature, listener) {
- d3_geo_streamGeometry(feature.geometry, listener);
- },
- FeatureCollection: function(object, listener) {
- var features = object.features, i = -1, n = features.length;
- while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener);
- }
- };
- var d3_geo_streamGeometryType = {
- Sphere: function(object, listener) {
- listener.sphere();
- },
- Point: function(object, listener) {
- var coordinate = object.coordinates;
- listener.point(coordinate[0], coordinate[1]);
- },
- MultiPoint: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length, coordinate;
- while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]);
- },
- LineString: function(object, listener) {
- d3_geo_streamLine(object.coordinates, listener, 0);
- },
- MultiLineString: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length;
- while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0);
- },
- Polygon: function(object, listener) {
- d3_geo_streamPolygon(object.coordinates, listener);
- },
- MultiPolygon: function(object, listener) {
- var coordinates = object.coordinates, i = -1, n = coordinates.length;
- while (++i < n) d3_geo_streamPolygon(coordinates[i], listener);
- },
- GeometryCollection: function(object, listener) {
- var geometries = object.geometries, i = -1, n = geometries.length;
- while (++i < n) d3_geo_streamGeometry(geometries[i], listener);
- }
- };
- function d3_geo_streamLine(coordinates, listener, closed) {
- var i = -1, n = coordinates.length - closed, coordinate;
- listener.lineStart();
- while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1]);
- listener.lineEnd();
- }
- function d3_geo_streamPolygon(coordinates, listener) {
- var i = -1, n = coordinates.length;
- listener.polygonStart();
- while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1);
- listener.polygonEnd();
- }
- d3.geo.area = function(object) {
- d3_geo_areaSum = 0;
- d3.geo.stream(object, d3_geo_area);
- return d3_geo_areaSum;
- };
- var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder();
- var d3_geo_area = {
- sphere: function() {
- d3_geo_areaSum += 4 * Ï;
- },
- point: d3_noop,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: function() {
- d3_geo_areaRingSum.reset();
- d3_geo_area.lineStart = d3_geo_areaRingStart;
- },
- polygonEnd: function() {
- var area = 2 * d3_geo_areaRingSum;
- d3_geo_areaSum += area < 0 ? 4 * Ï + area : area;
- d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop;
- }
- };
- function d3_geo_areaRingStart() {
- var λ00, Ï00, λ0, cosÏ0, sinÏ0;
- d3_geo_area.point = function(λ, Ï) {
- d3_geo_area.point = nextPoint;
- λ0 = (λ00 = λ) * d3_radians, cosÏ0 = Math.cos(Ï = (Ï00 = Ï) * d3_radians / 2 + Ï / 4),
- sinÏ0 = Math.sin(Ï);
- };
- function nextPoint(λ, Ï) {
- λ *= d3_radians;
- Ï = Ï * d3_radians / 2 + Ï / 4;
- var dλ = λ - λ0, cosÏ = Math.cos(Ï), sinÏ = Math.sin(Ï), k = sinÏ0 * sinÏ, u = cosÏ0 * cosÏ + k * Math.cos(dλ), v = k * Math.sin(dλ);
- d3_geo_areaRingSum.add(Math.atan2(v, u));
- λ0 = λ, cosÏ0 = cosÏ, sinÏ0 = sinÏ;
- }
- d3_geo_area.lineEnd = function() {
- nextPoint(λ00, Ï00);
- };
- }
- function d3_geo_cartesian(spherical) {
- var λ = spherical[0], Ï = spherical[1], cosÏ = Math.cos(Ï);
- return [ cosÏ * Math.cos(λ), cosÏ * Math.sin(λ), Math.sin(Ï) ];
- }
- function d3_geo_cartesianDot(a, b) {
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
- }
- function d3_geo_cartesianCross(a, b) {
- return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ];
- }
- function d3_geo_cartesianAdd(a, b) {
- a[0] += b[0];
- a[1] += b[1];
- a[2] += b[2];
- }
- function d3_geo_cartesianScale(vector, k) {
- return [ vector[0] * k, vector[1] * k, vector[2] * k ];
- }
- function d3_geo_cartesianNormalize(d) {
- var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
- d[0] /= l;
- d[1] /= l;
- d[2] /= l;
- }
- function d3_geo_spherical(cartesian) {
- return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ];
- }
- function d3_geo_sphericalEqual(a, b) {
- return Math.abs(a[0] - b[0]) < ε && Math.abs(a[1] - b[1]) < ε;
- }
- d3.geo.bounds = function() {
- var λ0, Ï0, λ1, Ï1, λ_, λ__, Ï__, p0, dλSum, ranges, range;
- var bound = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- bound.point = ringPoint;
- bound.lineStart = ringStart;
- bound.lineEnd = ringEnd;
- dλSum = 0;
- d3_geo_area.polygonStart();
- },
- polygonEnd: function() {
- d3_geo_area.polygonEnd();
- bound.point = point;
- bound.lineStart = lineStart;
- bound.lineEnd = lineEnd;
- if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), Ï0 = -(Ï1 = 90); else if (dλSum > ε) Ï1 = 90; else if (dλSum < -ε) Ï0 = -90;
- range[0] = λ0, range[1] = λ1;
- }
- };
- function point(λ, Ï) {
- ranges.push(range = [ λ0 = λ, λ1 = λ ]);
- if (Ï < Ï0) Ï0 = Ï;
- if (Ï > Ï1) Ï1 = Ï;
- }
- function linePoint(λ, Ï) {
- var p = d3_geo_cartesian([ λ * d3_radians, Ï * d3_radians ]);
- if (p0) {
- var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal);
- d3_geo_cartesianNormalize(inflection);
- inflection = d3_geo_spherical(inflection);
- var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = Math.abs(dλ) > 180;
- if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
- var Ïi = inflection[1] * d3_degrees;
- if (Ïi > Ï1) Ï1 = Ïi;
- } else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) {
- var Ïi = -inflection[1] * d3_degrees;
- if (Ïi < Ï0) Ï0 = Ïi;
- } else {
- if (Ï < Ï0) Ï0 = Ï;
- if (Ï > Ï1) Ï1 = Ï;
- }
- if (antimeridian) {
- if (λ < λ_) {
- if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
- } else {
- if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
- }
- } else {
- if (λ1 >= λ0) {
- if (λ < λ0) λ0 = λ;
- if (λ > λ1) λ1 = λ;
- } else {
- if (λ > λ_) {
- if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ;
- } else {
- if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ;
- }
- }
- }
- } else {
- point(λ, Ï);
- }
- p0 = p, λ_ = λ;
- }
- function lineStart() {
- bound.point = linePoint;
- }
- function lineEnd() {
- range[0] = λ0, range[1] = λ1;
- bound.point = point;
- p0 = null;
- }
- function ringPoint(λ, Ï) {
- if (p0) {
- var dλ = λ - λ_;
- dλSum += Math.abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ;
- } else λ__ = λ, Ï__ = Ï;
- d3_geo_area.point(λ, Ï);
- linePoint(λ, Ï);
- }
- function ringStart() {
- d3_geo_area.lineStart();
- }
- function ringEnd() {
- ringPoint(λ__, Ï__);
- d3_geo_area.lineEnd();
- if (Math.abs(dλSum) > ε) λ0 = -(λ1 = 180);
- range[0] = λ0, range[1] = λ1;
- p0 = null;
- }
- function angle(λ0, λ1) {
- return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1;
- }
- function compareRanges(a, b) {
- return a[0] - b[0];
- }
- function withinRange(x, range) {
- return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x;
- }
- return function(feature) {
- Ï1 = λ1 = -(λ0 = Ï0 = Infinity);
- ranges = [];
- d3.geo.stream(feature, bound);
- var n = ranges.length;
- if (n) {
- ranges.sort(compareRanges);
- for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) {
- b = ranges[i];
- if (withinRange(b[0], a) || withinRange(b[1], a)) {
- if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1];
- if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0];
- } else {
- merged.push(a = b);
- }
- }
- var best = -Infinity, dλ;
- for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) {
- b = merged[i];
- if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1];
- }
- }
- ranges = range = null;
- return λ0 === Infinity || Ï0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, Ï0 ], [ λ1, Ï1 ] ];
- };
- }();
- d3.geo.centroid = function(object) {
- d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
- d3.geo.stream(object, d3_geo_centroid);
- var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z;
- if (m < ε2) {
- x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1;
- if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0;
- m = x * x + y * y + z * z;
- if (m < ε2) return [ NaN, NaN ];
- }
- return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ];
- };
- var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2;
- var d3_geo_centroid = {
- sphere: d3_noop,
- point: d3_geo_centroidPoint,
- lineStart: d3_geo_centroidLineStart,
- lineEnd: d3_geo_centroidLineEnd,
- polygonStart: function() {
- d3_geo_centroid.lineStart = d3_geo_centroidRingStart;
- },
- polygonEnd: function() {
- d3_geo_centroid.lineStart = d3_geo_centroidLineStart;
- }
- };
- function d3_geo_centroidPoint(λ, Ï) {
- λ *= d3_radians;
- var cosÏ = Math.cos(Ï *= d3_radians);
- d3_geo_centroidPointXYZ(cosÏ * Math.cos(λ), cosÏ * Math.sin(λ), Math.sin(Ï));
- }
- function d3_geo_centroidPointXYZ(x, y, z) {
- ++d3_geo_centroidW0;
- d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0;
- d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0;
- d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0;
- }
- function d3_geo_centroidLineStart() {
- var x0, y0, z0;
- d3_geo_centroid.point = function(λ, Ï) {
- λ *= d3_radians;
- var cosÏ = Math.cos(Ï *= d3_radians);
- x0 = cosÏ * Math.cos(λ);
- y0 = cosÏ * Math.sin(λ);
- z0 = Math.sin(Ï);
- d3_geo_centroid.point = nextPoint;
- d3_geo_centroidPointXYZ(x0, y0, z0);
- };
- function nextPoint(λ, Ï) {
- λ *= d3_radians;
- var cosÏ = Math.cos(Ï *= d3_radians), x = cosÏ * Math.cos(λ), y = cosÏ * Math.sin(λ), z = Math.sin(Ï), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z);
- d3_geo_centroidW1 += w;
- d3_geo_centroidX1 += w * (x0 + (x0 = x));
- d3_geo_centroidY1 += w * (y0 + (y0 = y));
- d3_geo_centroidZ1 += w * (z0 + (z0 = z));
- d3_geo_centroidPointXYZ(x0, y0, z0);
- }
- }
- function d3_geo_centroidLineEnd() {
- d3_geo_centroid.point = d3_geo_centroidPoint;
- }
- function d3_geo_centroidRingStart() {
- var λ00, Ï00, x0, y0, z0;
- d3_geo_centroid.point = function(λ, Ï) {
- λ00 = λ, Ï00 = Ï;
- d3_geo_centroid.point = nextPoint;
- λ *= d3_radians;
- var cosÏ = Math.cos(Ï *= d3_radians);
- x0 = cosÏ * Math.cos(λ);
- y0 = cosÏ * Math.sin(λ);
- z0 = Math.sin(Ï);
- d3_geo_centroidPointXYZ(x0, y0, z0);
- };
- d3_geo_centroid.lineEnd = function() {
- nextPoint(λ00, Ï00);
- d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd;
- d3_geo_centroid.point = d3_geo_centroidPoint;
- };
- function nextPoint(λ, Ï) {
- λ *= d3_radians;
- var cosÏ = Math.cos(Ï *= d3_radians), x = cosÏ * Math.cos(λ), y = cosÏ * Math.sin(λ), z = Math.sin(Ï), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u);
- d3_geo_centroidX2 += v * cx;
- d3_geo_centroidY2 += v * cy;
- d3_geo_centroidZ2 += v * cz;
- d3_geo_centroidW1 += w;
- d3_geo_centroidX1 += w * (x0 + (x0 = x));
- d3_geo_centroidY1 += w * (y0 + (y0 = y));
- d3_geo_centroidZ1 += w * (z0 + (z0 = z));
- d3_geo_centroidPointXYZ(x0, y0, z0);
- }
- }
- function d3_true() {
- return true;
- }
- function d3_geo_clipPolygon(segments, compare, inside, interpolate, listener) {
- var subject = [], clip = [];
- segments.forEach(function(segment) {
- if ((n = segment.length - 1) <= 0) return;
- var n, p0 = segment[0], p1 = segment[n];
- if (d3_geo_sphericalEqual(p0, p1)) {
- listener.lineStart();
- for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]);
- listener.lineEnd();
- return;
- }
- var a = {
- point: p0,
- points: segment,
- other: null,
- visited: false,
- entry: true,
- subject: true
- }, b = {
- point: p0,
- points: [ p0 ],
- other: a,
- visited: false,
- entry: false,
- subject: false
- };
- a.other = b;
- subject.push(a);
- clip.push(b);
- a = {
- point: p1,
- points: [ p1 ],
- other: null,
- visited: false,
- entry: false,
- subject: true
- };
- b = {
- point: p1,
- points: [ p1 ],
- other: a,
- visited: false,
- entry: true,
- subject: false
- };
- a.other = b;
- subject.push(a);
- clip.push(b);
- });
- clip.sort(compare);
- d3_geo_clipPolygonLinkCircular(subject);
- d3_geo_clipPolygonLinkCircular(clip);
- if (!subject.length) return;
- if (inside) for (var i = 1, e = !inside(clip[0].point), n = clip.length; i < n; ++i) {
- clip[i].entry = e = !e;
- }
- var start = subject[0], current, points, point;
- while (1) {
- current = start;
- while (current.visited) if ((current = current.next) === start) return;
- points = current.points;
- listener.lineStart();
- do {
- current.visited = current.other.visited = true;
- if (current.entry) {
- if (current.subject) {
- for (var i = 0; i < points.length; i++) listener.point((point = points[i])[0], point[1]);
- } else {
- interpolate(current.point, current.next.point, 1, listener);
- }
- current = current.next;
- } else {
- if (current.subject) {
- points = current.prev.points;
- for (var i = points.length; --i >= 0; ) listener.point((point = points[i])[0], point[1]);
- } else {
- interpolate(current.point, current.prev.point, -1, listener);
- }
- current = current.prev;
- }
- current = current.other;
- points = current.points;
- } while (!current.visited);
- listener.lineEnd();
- }
- }
- function d3_geo_clipPolygonLinkCircular(array) {
- if (!(n = array.length)) return;
- var n, i = 0, a = array[0], b;
- while (++i < n) {
- a.next = b = array[i];
- b.prev = a;
- a = b;
- }
- a.next = b = array[0];
- b.prev = a;
- }
- function d3_geo_clip(pointVisible, clipLine, interpolate, polygonContains) {
- return function(listener) {
- var line = clipLine(listener);
- var clip = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- clip.point = pointRing;
- clip.lineStart = ringStart;
- clip.lineEnd = ringEnd;
- segments = [];
- polygon = [];
- listener.polygonStart();
- },
- polygonEnd: function() {
- clip.point = point;
- clip.lineStart = lineStart;
- clip.lineEnd = lineEnd;
- segments = d3.merge(segments);
- if (segments.length) {
- d3_geo_clipPolygon(segments, d3_geo_clipSort, null, interpolate, listener);
- } else if (polygonContains(polygon)) {
- listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd();
- }
- listener.polygonEnd();
- segments = polygon = null;
- },
- sphere: function() {
- listener.polygonStart();
- listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd();
- listener.polygonEnd();
- }
- };
- function point(λ, Ï) {
- if (pointVisible(λ, Ï)) listener.point(λ, Ï);
- }
- function pointLine(λ, Ï) {
- line.point(λ, Ï);
- }
- function lineStart() {
- clip.point = pointLine;
- line.lineStart();
- }
- function lineEnd() {
- clip.point = point;
- line.lineEnd();
- }
- var segments;
- var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygon, ring;
- function pointRing(λ, Ï) {
- ringListener.point(λ, Ï);
- ring.push([ λ, Ï ]);
- }
- function ringStart() {
- ringListener.lineStart();
- ring = [];
- }
- function ringEnd() {
- pointRing(ring[0][0], ring[0][1]);
- ringListener.lineEnd();
- var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length;
- ring.pop();
- polygon.push(ring);
- ring = null;
- if (!n) return;
- if (clean & 1) {
- segment = ringSegments[0];
- var n = segment.length - 1, i = -1, point;
- listener.lineStart();
- while (++i < n) listener.point((point = segment[i])[0], point[1]);
- listener.lineEnd();
- return;
- }
- if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift()));
- segments.push(ringSegments.filter(d3_geo_clipSegmentLength1));
- }
- return clip;
- };
- }
- function d3_geo_clipSegmentLength1(segment) {
- return segment.length > 1;
- }
- function d3_geo_clipBufferListener() {
- var lines = [], line;
- return {
- lineStart: function() {
- lines.push(line = []);
- },
- point: function(λ, Ï) {
- line.push([ λ, Ï ]);
- },
- lineEnd: d3_noop,
- buffer: function() {
- var buffer = lines;
- lines = [];
- line = null;
- return buffer;
- },
- rejoin: function() {
- if (lines.length > 1) lines.push(lines.pop().concat(lines.shift()));
- }
- };
- }
- function d3_geo_clipSort(a, b) {
- return ((a = a.point)[0] < 0 ? a[1] - Ï / 2 - ε : Ï / 2 - a[1]) - ((b = b.point)[0] < 0 ? b[1] - Ï / 2 - ε : Ï / 2 - b[1]);
- }
- function d3_geo_pointInPolygon(point, polygon) {
- var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, polar = false, southPole = false, winding = 0;
- d3_geo_areaRingSum.reset();
- for (var i = 0, n = polygon.length; i < n; ++i) {
- var ring = polygon[i], m = ring.length;
- if (!m) continue;
- var point0 = ring[0], λ0 = point0[0], Ï0 = point0[1] / 2 + Ï / 4, sinÏ0 = Math.sin(Ï0), cosÏ0 = Math.cos(Ï0), j = 1;
- while (true) {
- if (j === m) j = 0;
- point = ring[j];
- var λ = point[0], Ï = point[1] / 2 + Ï / 4, sinÏ = Math.sin(Ï), cosÏ = Math.cos(Ï), dλ = λ - λ0, antimeridian = Math.abs(dλ) > Ï, k = sinÏ0 * sinÏ;
- d3_geo_areaRingSum.add(Math.atan2(k * Math.sin(dλ), cosÏ0 * cosÏ + k * Math.cos(dλ)));
- if (Math.abs(Ï) < ε) southPole = true;
- polarAngle += antimeridian ? dλ + (dλ >= 0 ? 2 : -2) * Ï : dλ;
- if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) {
- var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point));
- d3_geo_cartesianNormalize(arc);
- var intersection = d3_geo_cartesianCross(meridianNormal, arc);
- d3_geo_cartesianNormalize(intersection);
- var Ïarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]);
- if (parallel > Ïarc) {
- winding += antimeridian ^ dλ >= 0 ? 1 : -1;
- }
- }
- if (!j++) break;
- λ0 = λ, sinÏ0 = sinÏ, cosÏ0 = cosÏ, point0 = point;
- }
- if (Math.abs(polarAngle) > ε) polar = true;
- }
- return (!southPole && !polar && d3_geo_areaRingSum < 0 || polarAngle < -ε) ^ winding & 1;
- }
- var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, d3_geo_clipAntimeridianPolygonContains);
- function d3_geo_clipAntimeridianLine(listener) {
- var λ0 = NaN, Ï0 = NaN, sλ0 = NaN, clean;
- return {
- lineStart: function() {
- listener.lineStart();
- clean = 1;
- },
- point: function(λ1, Ï1) {
- var sλ1 = λ1 > 0 ? Ï : -Ï, dλ = Math.abs(λ1 - λ0);
- if (Math.abs(dλ - Ï) < ε) {
- listener.point(λ0, Ï0 = (Ï0 + Ï1) / 2 > 0 ? Ï / 2 : -Ï / 2);
- listener.point(sλ0, Ï0);
- listener.lineEnd();
- listener.lineStart();
- listener.point(sλ1, Ï0);
- listener.point(λ1, Ï0);
- clean = 0;
- } else if (sλ0 !== sλ1 && dλ >= Ï) {
- if (Math.abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε;
- if (Math.abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε;
- Ï0 = d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1);
- listener.point(sλ0, Ï0);
- listener.lineEnd();
- listener.lineStart();
- listener.point(sλ1, Ï0);
- clean = 0;
- }
- listener.point(λ0 = λ1, Ï0 = Ï1);
- sλ0 = sλ1;
- },
- lineEnd: function() {
- listener.lineEnd();
- λ0 = Ï0 = NaN;
- },
- clean: function() {
- return 2 - clean;
- }
- };
- }
- function d3_geo_clipAntimeridianIntersect(λ0, Ï0, λ1, Ï1) {
- var cosÏ0, cosÏ1, sinλ0_λ1 = Math.sin(λ0 - λ1);
- return Math.abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(Ï0) * (cosÏ1 = Math.cos(Ï1)) * Math.sin(λ1) - Math.sin(Ï1) * (cosÏ0 = Math.cos(Ï0)) * Math.sin(λ0)) / (cosÏ0 * cosÏ1 * sinλ0_λ1)) : (Ï0 + Ï1) / 2;
- }
- function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) {
- var Ï;
- if (from == null) {
- Ï = direction * Ï / 2;
- listener.point(-Ï, Ï);
- listener.point(0, Ï);
- listener.point(Ï, Ï);
- listener.point(Ï, 0);
- listener.point(Ï, -Ï);
- listener.point(0, -Ï);
- listener.point(-Ï, -Ï);
- listener.point(-Ï, 0);
- listener.point(-Ï, Ï);
- } else if (Math.abs(from[0] - to[0]) > ε) {
- var s = (from[0] < to[0] ? 1 : -1) * Ï;
- Ï = direction * s / 2;
- listener.point(-s, Ï);
- listener.point(0, Ï);
- listener.point(s, Ï);
- } else {
- listener.point(to[0], to[1]);
- }
- }
- var d3_geo_clipAntimeridianPoint = [ -Ï, 0 ];
- function d3_geo_clipAntimeridianPolygonContains(polygon) {
- return d3_geo_pointInPolygon(d3_geo_clipAntimeridianPoint, polygon);
- }
- function d3_geo_clipCircle(radius) {
- var cr = Math.cos(radius), smallRadius = cr > 0, point = [ radius, 0 ], notHemisphere = Math.abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians);
- return d3_geo_clip(visible, clipLine, interpolate, polygonContains);
- function visible(λ, Ï) {
- return Math.cos(λ) * Math.cos(Ï) > cr;
- }
- function clipLine(listener) {
- var point0, c0, v0, v00, clean;
- return {
- lineStart: function() {
- v00 = v0 = false;
- clean = 1;
- },
- point: function(λ, Ï) {
- var point1 = [ λ, Ï ], point2, v = visible(λ, Ï), c = smallRadius ? v ? 0 : code(λ, Ï) : v ? code(λ + (λ < 0 ? Ï : -Ï), Ï) : 0;
- if (!point0 && (v00 = v0 = v)) listener.lineStart();
- if (v !== v0) {
- point2 = intersect(point0, point1);
- if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) {
- point1[0] += ε;
- point1[1] += ε;
- v = visible(point1[0], point1[1]);
- }
- }
- if (v !== v0) {
- clean = 0;
- if (v) {
- listener.lineStart();
- point2 = intersect(point1, point0);
- listener.point(point2[0], point2[1]);
- } else {
- point2 = intersect(point0, point1);
- listener.point(point2[0], point2[1]);
- listener.lineEnd();
- }
- point0 = point2;
- } else if (notHemisphere && point0 && smallRadius ^ v) {
- var t;
- if (!(c & c0) && (t = intersect(point1, point0, true))) {
- clean = 0;
- if (smallRadius) {
- listener.lineStart();
- listener.point(t[0][0], t[0][1]);
- listener.point(t[1][0], t[1][1]);
- listener.lineEnd();
- } else {
- listener.point(t[1][0], t[1][1]);
- listener.lineEnd();
- listener.lineStart();
- listener.point(t[0][0], t[0][1]);
- }
- }
- }
- if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) {
- listener.point(point1[0], point1[1]);
- }
- point0 = point1, v0 = v, c0 = c;
- },
- lineEnd: function() {
- if (v0) listener.lineEnd();
- point0 = null;
- },
- clean: function() {
- return clean | (v00 && v0) << 1;
- }
- };
- }
- function intersect(a, b, two) {
- var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b);
- var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2;
- if (!determinant) return !two && a;
- var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2);
- d3_geo_cartesianAdd(A, B);
- var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1);
- if (t2 < 0) return;
- var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu);
- d3_geo_cartesianAdd(q, A);
- q = d3_geo_spherical(q);
- if (!two) return q;
- var λ0 = a[0], λ1 = b[0], Ï0 = a[1], Ï1 = b[1], z;
- if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z;
- var Ύλ = λ1 - λ0, polar = Math.abs(Ύλ - Ï) < ε, meridian = polar || Ύλ < ε;
- if (!polar && Ï1 < Ï0) z = Ï0, Ï0 = Ï1, Ï1 = z;
- if (meridian ? polar ? Ï0 + Ï1 > 0 ^ q[1] < (Math.abs(q[0] - λ0) < ε ? Ï0 : Ï1) : Ï0 <= q[1] && q[1] <= Ï1 : Ύλ > Ï ^ (λ0 <= q[0] && q[0] <= λ1)) {
- var q1 = d3_geo_cartesianScale(u, (-w + t) / uu);
- d3_geo_cartesianAdd(q1, A);
- return [ q, d3_geo_spherical(q1) ];
- }
- }
- function code(λ, Ï) {
- var r = smallRadius ? radius : Ï - radius, code = 0;
- if (λ < -r) code |= 1; else if (λ > r) code |= 2;
- if (Ï < -r) code |= 4; else if (Ï > r) code |= 8;
- return code;
- }
- function polygonContains(polygon) {
- return d3_geo_pointInPolygon(point, polygon);
- }
- }
- var d3_geo_clipViewMAX = 1e9;
- function d3_geo_clipView(x0, y0, x1, y1) {
- return function(listener) {
- var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), segments, polygon, ring;
- var clip = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- listener = bufferListener;
- segments = [];
- polygon = [];
- },
- polygonEnd: function() {
- listener = listener_;
- if ((segments = d3.merge(segments)).length) {
- listener.polygonStart();
- d3_geo_clipPolygon(segments, compare, inside, interpolate, listener);
- listener.polygonEnd();
- } else if (insidePolygon([ x0, y0 ])) {
- listener.polygonStart(), listener.lineStart();
- interpolate(null, null, 1, listener);
- listener.lineEnd(), listener.polygonEnd();
- }
- segments = polygon = ring = null;
- }
- };
- function inside(point) {
- var a = corner(point, -1), i = insidePolygon([ a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0 ]);
- return i;
- }
- function insidePolygon(p) {
- var wn = 0, n = polygon.length, y = p[1];
- for (var i = 0; i < n; ++i) {
- for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) {
- b = v[j];
- if (a[1] <= y) {
- if (b[1] > y && isLeft(a, b, p) > 0) ++wn;
- } else {
- if (b[1] <= y && isLeft(a, b, p) < 0) --wn;
- }
- a = b;
- }
- }
- return wn !== 0;
- }
- function isLeft(a, b, c) {
- return (b[0] - a[0]) * (c[1] - a[1]) - (c[0] - a[0]) * (b[1] - a[1]);
- }
- function interpolate(from, to, direction, listener) {
- var a = 0, a1 = 0;
- if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) {
- do {
- listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0);
- } while ((a = (a + direction + 4) % 4) !== a1);
- } else {
- listener.point(to[0], to[1]);
- }
- }
- function visible(x, y) {
- return x0 <= x && x <= x1 && y0 <= y && y <= y1;
- }
- function point(x, y) {
- if (visible(x, y)) listener.point(x, y);
- }
- var x__, y__, v__, x_, y_, v_, first;
- function lineStart() {
- clip.point = linePoint;
- if (polygon) polygon.push(ring = []);
- first = true;
- v_ = false;
- x_ = y_ = NaN;
- }
- function lineEnd() {
- if (segments) {
- linePoint(x__, y__);
- if (v__ && v_) bufferListener.rejoin();
- segments.push(bufferListener.buffer());
- }
- clip.point = point;
- if (v_) listener.lineEnd();
- }
- function linePoint(x, y) {
- x = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, x));
- y = Math.max(-d3_geo_clipViewMAX, Math.min(d3_geo_clipViewMAX, y));
- var v = visible(x, y);
- if (polygon) ring.push([ x, y ]);
- if (first) {
- x__ = x, y__ = y, v__ = v;
- first = false;
- if (v) {
- listener.lineStart();
- listener.point(x, y);
- }
- } else {
- if (v && v_) listener.point(x, y); else {
- var a = [ x_, y_ ], b = [ x, y ];
- if (clipLine(a, b)) {
- if (!v_) {
- listener.lineStart();
- listener.point(a[0], a[1]);
- }
- listener.point(b[0], b[1]);
- if (!v) listener.lineEnd();
- } else if (v) {
- listener.lineStart();
- listener.point(x, y);
- }
- }
- }
- x_ = x, y_ = y, v_ = v;
- }
- return clip;
- };
- function corner(p, direction) {
- return Math.abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : Math.abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : Math.abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2;
- }
- function compare(a, b) {
- return comparePoints(a.point, b.point);
- }
- function comparePoints(a, b) {
- var ca = corner(a, 1), cb = corner(b, 1);
- return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0];
- }
- function clipLine(a, b) {
- var dx = b[0] - a[0], dy = b[1] - a[1], t = [ 0, 1 ];
- if (Math.abs(dx) < ε && Math.abs(dy) < ε) return x0 <= a[0] && a[0] <= x1 && y0 <= a[1] && a[1] <= y1;
- if (d3_geo_clipViewT(x0 - a[0], dx, t) && d3_geo_clipViewT(a[0] - x1, -dx, t) && d3_geo_clipViewT(y0 - a[1], dy, t) && d3_geo_clipViewT(a[1] - y1, -dy, t)) {
- if (t[1] < 1) {
- b[0] = a[0] + t[1] * dx;
- b[1] = a[1] + t[1] * dy;
- }
- if (t[0] > 0) {
- a[0] += t[0] * dx;
- a[1] += t[0] * dy;
- }
- return true;
- }
- return false;
- }
- }
- function d3_geo_clipViewT(num, denominator, t) {
- if (Math.abs(denominator) < ε) return num <= 0;
- var u = num / denominator;
- if (denominator > 0) {
- if (u > t[1]) return false;
- if (u > t[0]) t[0] = u;
- } else {
- if (u < t[0]) return false;
- if (u < t[1]) t[1] = u;
- }
- return true;
- }
- function d3_geo_compose(a, b) {
- function compose(x, y) {
- return x = a(x, y), b(x[0], x[1]);
- }
- if (a.invert && b.invert) compose.invert = function(x, y) {
- return x = b.invert(x, y), x && a.invert(x[0], x[1]);
- };
- return compose;
- }
- function d3_geo_conic(projectAt) {
- var Ï0 = 0, Ï1 = Ï / 3, m = d3_geo_projectionMutator(projectAt), p = m(Ï0, Ï1);
- p.parallels = function(_) {
- if (!arguments.length) return [ Ï0 / Ï * 180, Ï1 / Ï * 180 ];
- return m(Ï0 = _[0] * Ï / 180, Ï1 = _[1] * Ï / 180);
- };
- return p;
- }
- function d3_geo_conicEqualArea(Ï0, Ï1) {
- var sinÏ0 = Math.sin(Ï0), n = (sinÏ0 + Math.sin(Ï1)) / 2, C = 1 + sinÏ0 * (2 * n - sinÏ0), Ï0 = Math.sqrt(C) / n;
- function forward(λ, Ï) {
- var Ï = Math.sqrt(C - 2 * n * Math.sin(Ï)) / n;
- return [ Ï * Math.sin(λ *= n), Ï0 - Ï * Math.cos(λ) ];
- }
- forward.invert = function(x, y) {
- var Ï0_y = Ï0 - y;
- return [ Math.atan2(x, Ï0_y) / n, d3_asin((C - (x * x + Ï0_y * Ï0_y) * n * n) / (2 * n)) ];
- };
- return forward;
- }
- (d3.geo.conicEqualArea = function() {
- return d3_geo_conic(d3_geo_conicEqualArea);
- }).raw = d3_geo_conicEqualArea;
- d3.geo.albers = function() {
- return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070);
- };
- d3.geo.albersUsa = function() {
- var lower48 = d3.geo.albers();
- var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]);
- var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]);
- var point, pointStream = {
- point: function(x, y) {
- point = [ x, y ];
- }
- }, lower48Point, alaskaPoint, hawaiiPoint;
- function albersUsa(coordinates) {
- var x = coordinates[0], y = coordinates[1];
- point = null;
- (lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y);
- return point;
- }
- albersUsa.invert = function(coordinates) {
- var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k;
- return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates);
- };
- albersUsa.stream = function(stream) {
- var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream);
- return {
- point: function(x, y) {
- lower48Stream.point(x, y);
- alaskaStream.point(x, y);
- hawaiiStream.point(x, y);
- },
- sphere: function() {
- lower48Stream.sphere();
- alaskaStream.sphere();
- hawaiiStream.sphere();
- },
- lineStart: function() {
- lower48Stream.lineStart();
- alaskaStream.lineStart();
- hawaiiStream.lineStart();
- },
- lineEnd: function() {
- lower48Stream.lineEnd();
- alaskaStream.lineEnd();
- hawaiiStream.lineEnd();
- },
- polygonStart: function() {
- lower48Stream.polygonStart();
- alaskaStream.polygonStart();
- hawaiiStream.polygonStart();
- },
- polygonEnd: function() {
- lower48Stream.polygonEnd();
- alaskaStream.polygonEnd();
- hawaiiStream.polygonEnd();
- }
- };
- };
- albersUsa.precision = function(_) {
- if (!arguments.length) return lower48.precision();
- lower48.precision(_);
- alaska.precision(_);
- hawaii.precision(_);
- return albersUsa;
- };
- albersUsa.scale = function(_) {
- if (!arguments.length) return lower48.scale();
- lower48.scale(_);
- alaska.scale(_ * .35);
- hawaii.scale(_);
- return albersUsa.translate(lower48.translate());
- };
- albersUsa.translate = function(_) {
- if (!arguments.length) return lower48.translate();
- var k = lower48.scale(), x = +_[0], y = +_[1];
- lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point;
- alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
- hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point;
- return albersUsa;
- };
- return albersUsa.scale(1070);
- };
- var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = {
- point: d3_noop,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: function() {
- d3_geo_pathAreaPolygon = 0;
- d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart;
- },
- polygonEnd: function() {
- d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop;
- d3_geo_pathAreaSum += Math.abs(d3_geo_pathAreaPolygon / 2);
- }
- };
- function d3_geo_pathAreaRingStart() {
- var x00, y00, x0, y0;
- d3_geo_pathArea.point = function(x, y) {
- d3_geo_pathArea.point = nextPoint;
- x00 = x0 = x, y00 = y0 = y;
- };
- function nextPoint(x, y) {
- d3_geo_pathAreaPolygon += y0 * x - x0 * y;
- x0 = x, y0 = y;
- }
- d3_geo_pathArea.lineEnd = function() {
- nextPoint(x00, y00);
- };
- }
- var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1;
- var d3_geo_pathBounds = {
- point: d3_geo_pathBoundsPoint,
- lineStart: d3_noop,
- lineEnd: d3_noop,
- polygonStart: d3_noop,
- polygonEnd: d3_noop
- };
- function d3_geo_pathBoundsPoint(x, y) {
- if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x;
- if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x;
- if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y;
- if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y;
- }
- function d3_geo_pathBuffer() {
- var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = [];
- var stream = {
- point: point,
- lineStart: function() {
- stream.point = pointLineStart;
- },
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.lineEnd = lineEndPolygon;
- },
- polygonEnd: function() {
- stream.lineEnd = lineEnd;
- stream.point = point;
- },
- pointRadius: function(_) {
- pointCircle = d3_geo_pathBufferCircle(_);
- return stream;
- },
- result: function() {
- if (buffer.length) {
- var result = buffer.join("");
- buffer = [];
- return result;
- }
- }
- };
- function point(x, y) {
- buffer.push("M", x, ",", y, pointCircle);
- }
- function pointLineStart(x, y) {
- buffer.push("M", x, ",", y);
- stream.point = pointLine;
- }
- function pointLine(x, y) {
- buffer.push("L", x, ",", y);
- }
- function lineEnd() {
- stream.point = point;
- }
- function lineEndPolygon() {
- buffer.push("Z");
- }
- return stream;
- }
- function d3_geo_pathBufferCircle(radius) {
- return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z";
- }
- var d3_geo_pathCentroid = {
- point: d3_geo_pathCentroidPoint,
- lineStart: d3_geo_pathCentroidLineStart,
- lineEnd: d3_geo_pathCentroidLineEnd,
- polygonStart: function() {
- d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart;
- },
- polygonEnd: function() {
- d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
- d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart;
- d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd;
- }
- };
- function d3_geo_pathCentroidPoint(x, y) {
- d3_geo_centroidX0 += x;
- d3_geo_centroidY0 += y;
- ++d3_geo_centroidZ0;
- }
- function d3_geo_pathCentroidLineStart() {
- var x0, y0;
- d3_geo_pathCentroid.point = function(x, y) {
- d3_geo_pathCentroid.point = nextPoint;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- };
- function nextPoint(x, y) {
- var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
- d3_geo_centroidX1 += z * (x0 + x) / 2;
- d3_geo_centroidY1 += z * (y0 + y) / 2;
- d3_geo_centroidZ1 += z;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- }
- }
- function d3_geo_pathCentroidLineEnd() {
- d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint;
- }
- function d3_geo_pathCentroidRingStart() {
- var x00, y00, x0, y0;
- d3_geo_pathCentroid.point = function(x, y) {
- d3_geo_pathCentroid.point = nextPoint;
- d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y);
- };
- function nextPoint(x, y) {
- var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy);
- d3_geo_centroidX1 += z * (x0 + x) / 2;
- d3_geo_centroidY1 += z * (y0 + y) / 2;
- d3_geo_centroidZ1 += z;
- z = y0 * x - x0 * y;
- d3_geo_centroidX2 += z * (x0 + x);
- d3_geo_centroidY2 += z * (y0 + y);
- d3_geo_centroidZ2 += z * 3;
- d3_geo_pathCentroidPoint(x0 = x, y0 = y);
- }
- d3_geo_pathCentroid.lineEnd = function() {
- nextPoint(x00, y00);
- };
- }
- function d3_geo_pathContext(context) {
- var pointRadius = 4.5;
- var stream = {
- point: point,
- lineStart: function() {
- stream.point = pointLineStart;
- },
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.lineEnd = lineEndPolygon;
- },
- polygonEnd: function() {
- stream.lineEnd = lineEnd;
- stream.point = point;
- },
- pointRadius: function(_) {
- pointRadius = _;
- return stream;
- },
- result: d3_noop
- };
- function point(x, y) {
- context.moveTo(x, y);
- context.arc(x, y, pointRadius, 0, 2 * Ï);
- }
- function pointLineStart(x, y) {
- context.moveTo(x, y);
- stream.point = pointLine;
- }
- function pointLine(x, y) {
- context.lineTo(x, y);
- }
- function lineEnd() {
- stream.point = point;
- }
- function lineEndPolygon() {
- context.closePath();
- }
- return stream;
- }
- function d3_geo_resample(project) {
- var ÎŽ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16;
- function resample(stream) {
- var λ00, Ï00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0;
- var resample = {
- point: point,
- lineStart: lineStart,
- lineEnd: lineEnd,
- polygonStart: function() {
- stream.polygonStart();
- resample.lineStart = ringStart;
- },
- polygonEnd: function() {
- stream.polygonEnd();
- resample.lineStart = lineStart;
- }
- };
- function point(x, y) {
- x = project(x, y);
- stream.point(x[0], x[1]);
- }
- function lineStart() {
- x0 = NaN;
- resample.point = linePoint;
- stream.lineStart();
- }
- function linePoint(λ, Ï) {
- var c = d3_geo_cartesian([ λ, Ï ]), p = project(λ, Ï);
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream);
- stream.point(x0, y0);
- }
- function lineEnd() {
- resample.point = point;
- stream.lineEnd();
- }
- function ringStart() {
- lineStart();
- resample.point = ringPoint;
- resample.lineEnd = ringEnd;
- }
- function ringPoint(λ, Ï) {
- linePoint(λ00 = λ, Ï00 = Ï), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0;
- resample.point = linePoint;
- }
- function ringEnd() {
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream);
- resample.lineEnd = lineEnd;
- lineEnd();
- }
- return resample;
- }
- function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) {
- var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy;
- if (d2 > 4 * ÎŽ2 && depth--) {
- var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), Ï2 = Math.asin(c /= m), λ2 = Math.abs(Math.abs(c) - 1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, Ï2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2;
- if (dz * dz / d2 > ÎŽ2 || Math.abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) {
- resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream);
- stream.point(x2, y2);
- resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream);
- }
- }
- }
- resample.precision = function(_) {
- if (!arguments.length) return Math.sqrt(ÎŽ2);
- maxDepth = (ÎŽ2 = _ * _) > 0 && 16;
- return resample;
- };
- return resample;
- }
- d3.geo.path = function() {
- var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream;
- function path(object) {
- if (object) {
- if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments));
- if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream);
- d3.geo.stream(object, cacheStream);
- }
- return contextStream.result();
- }
- path.area = function(object) {
- d3_geo_pathAreaSum = 0;
- d3.geo.stream(object, projectStream(d3_geo_pathArea));
- return d3_geo_pathAreaSum;
- };
- path.centroid = function(object) {
- d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0;
- d3.geo.stream(object, projectStream(d3_geo_pathCentroid));
- return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ];
- };
- path.bounds = function(object) {
- d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity);
- d3.geo.stream(object, projectStream(d3_geo_pathBounds));
- return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ];
- };
- path.projection = function(_) {
- if (!arguments.length) return projection;
- projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity;
- return reset();
- };
- path.context = function(_) {
- if (!arguments.length) return context;
- contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_);
- if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius);
- return reset();
- };
- path.pointRadius = function(_) {
- if (!arguments.length) return pointRadius;
- pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_);
- return path;
- };
- function reset() {
- cacheStream = null;
- return path;
- }
- return path.projection(d3.geo.albersUsa()).context(null);
- };
- function d3_geo_pathProjectStream(project) {
- var resample = d3_geo_resample(function(λ, Ï) {
- return project([ λ * d3_degrees, Ï * d3_degrees ]);
- });
- return function(stream) {
- stream = resample(stream);
- return {
- point: function(λ, Ï) {
- stream.point(λ * d3_radians, Ï * d3_radians);
- },
- sphere: function() {
- stream.sphere();
- },
- lineStart: function() {
- stream.lineStart();
- },
- lineEnd: function() {
- stream.lineEnd();
- },
- polygonStart: function() {
- stream.polygonStart();
- },
- polygonEnd: function() {
- stream.polygonEnd();
- }
- };
- };
- }
- d3.geo.projection = d3_geo_projection;
- d3.geo.projectionMutator = d3_geo_projectionMutator;
- function d3_geo_projection(project) {
- return d3_geo_projectionMutator(function() {
- return project;
- })();
- }
- function d3_geo_projectionMutator(projectAt) {
- var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) {
- x = project(x, y);
- return [ x[0] * k + ÎŽx, ÎŽy - x[1] * k ];
- }), k = 150, x = 480, y = 250, λ = 0, Ï = 0, Ύλ = 0, ÎŽÏ = 0, Ύγ = 0, ÎŽx, ÎŽy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream;
- function projection(point) {
- point = projectRotate(point[0] * d3_radians, point[1] * d3_radians);
- return [ point[0] * k + ÎŽx, ÎŽy - point[1] * k ];
- }
- function invert(point) {
- point = projectRotate.invert((point[0] - ÎŽx) / k, (ÎŽy - point[1]) / k);
- return point && [ point[0] * d3_degrees, point[1] * d3_degrees ];
- }
- projection.stream = function(output) {
- if (stream) stream.valid = false;
- stream = d3_geo_projectionRadiansRotate(rotate, preclip(projectResample(postclip(output))));
- stream.valid = true;
- return stream;
- };
- projection.clipAngle = function(_) {
- if (!arguments.length) return clipAngle;
- preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians);
- return invalidate();
- };
- projection.clipExtent = function(_) {
- if (!arguments.length) return clipExtent;
- clipExtent = _;
- postclip = _ == null ? d3_identity : d3_geo_clipView(_[0][0], _[0][1], _[1][0], _[1][1]);
- return invalidate();
- };
- projection.scale = function(_) {
- if (!arguments.length) return k;
- k = +_;
- return reset();
- };
- projection.translate = function(_) {
- if (!arguments.length) return [ x, y ];
- x = +_[0];
- y = +_[1];
- return reset();
- };
- projection.center = function(_) {
- if (!arguments.length) return [ λ * d3_degrees, Ï * d3_degrees ];
- λ = _[0] % 360 * d3_radians;
- Ï = _[1] % 360 * d3_radians;
- return reset();
- };
- projection.rotate = function(_) {
- if (!arguments.length) return [ Ύλ * d3_degrees, ÎŽÏ * d3_degrees, Ύγ * d3_degrees ];
- Ύλ = _[0] % 360 * d3_radians;
- ÎŽÏ = _[1] % 360 * d3_radians;
- Ύγ = _.length > 2 ? _[2] % 360 * d3_radians : 0;
- return reset();
- };
- d3.rebind(projection, projectResample, "precision");
- function reset() {
- projectRotate = d3_geo_compose(rotate = d3_geo_rotation(Ύλ, ÎŽÏ, Ύγ), project);
- var center = project(λ, Ï);
- ÎŽx = x - center[0] * k;
- ÎŽy = y + center[1] * k;
- return invalidate();
- }
- function invalidate() {
- if (stream) {
- stream.valid = false;
- stream = null;
- }
- return projection;
- }
- return function() {
- project = projectAt.apply(this, arguments);
- projection.invert = project.invert && invert;
- return reset();
- };
- }
- function d3_geo_projectionRadiansRotate(rotate, stream) {
- return {
- point: function(x, y) {
- y = rotate(x * d3_radians, y * d3_radians), x = y[0];
- stream.point(x > Ï ? x - 2 * Ï : x < -Ï ? x + 2 * Ï : x, y[1]);
- },
- sphere: function() {
- stream.sphere();
- },
- lineStart: function() {
- stream.lineStart();
- },
- lineEnd: function() {
- stream.lineEnd();
- },
- polygonStart: function() {
- stream.polygonStart();
- },
- polygonEnd: function() {
- stream.polygonEnd();
- }
- };
- }
- function d3_geo_equirectangular(λ, Ï) {
- return [ λ, Ï ];
- }
- (d3.geo.equirectangular = function() {
- return d3_geo_projection(d3_geo_equirectangular);
- }).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular;
- d3.geo.rotation = function(rotate) {
- rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0);
- function forward(coordinates) {
- coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
- return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
- }
- forward.invert = function(coordinates) {
- coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians);
- return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates;
- };
- return forward;
- };
- function d3_geo_rotation(Ύλ, ÎŽÏ, Ύγ) {
- return Ύλ ? ÎŽÏ || Ύγ ? d3_geo_compose(d3_geo_rotationλ(Ύλ), d3_geo_rotationÏγ(ÎŽÏ, Ύγ)) : d3_geo_rotationλ(Ύλ) : ÎŽÏ || Ύγ ? d3_geo_rotationÏγ(ÎŽÏ, Ύγ) : d3_geo_equirectangular;
- }
- function d3_geo_forwardRotationλ(Ύλ) {
- return function(λ, Ï) {
- return λ += Ύλ, [ λ > Ï ? λ - 2 * Ï : λ < -Ï ? λ + 2 * Ï : λ, Ï ];
- };
- }
- function d3_geo_rotationλ(Ύλ) {
- var rotation = d3_geo_forwardRotationλ(Ύλ);
- rotation.invert = d3_geo_forwardRotationλ(-Ύλ);
- return rotation;
- }
- function d3_geo_rotationÏγ(ÎŽÏ, Ύγ) {
- var cosÎŽÏ = Math.cos(ÎŽÏ), sinÎŽÏ = Math.sin(ÎŽÏ), cosΎγ = Math.cos(Ύγ), sinΎγ = Math.sin(Ύγ);
- function rotation(λ, Ï) {
- var cosÏ = Math.cos(Ï), x = Math.cos(λ) * cosÏ, y = Math.sin(λ) * cosÏ, z = Math.sin(Ï), k = z * cosÎŽÏ + x * sinÎŽÏ;
- return [ Math.atan2(y * cosΎγ - k * sinΎγ, x * cosÎŽÏ - z * sinÎŽÏ), d3_asin(k * cosΎγ + y * sinΎγ) ];
- }
- rotation.invert = function(λ, Ï) {
- var cosÏ = Math.cos(Ï), x = Math.cos(λ) * cosÏ, y = Math.sin(λ) * cosÏ, z = Math.sin(Ï), k = z * cosΎγ - y * sinΎγ;
- return [ Math.atan2(y * cosΎγ + z * sinΎγ, x * cosÎŽÏ + k * sinÎŽÏ), d3_asin(k * cosÎŽÏ - x * sinÎŽÏ) ];
- };
- return rotation;
- }
- d3.geo.circle = function() {
- var origin = [ 0, 0 ], angle, precision = 6, interpolate;
- function circle() {
- var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = [];
- interpolate(null, null, 1, {
- point: function(x, y) {
- ring.push(x = rotate(x, y));
- x[0] *= d3_degrees, x[1] *= d3_degrees;
- }
- });
- return {
- type: "Polygon",
- coordinates: [ ring ]
- };
- }
- circle.origin = function(x) {
- if (!arguments.length) return origin;
- origin = x;
- return circle;
- };
- circle.angle = function(x) {
- if (!arguments.length) return angle;
- interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians);
- return circle;
- };
- circle.precision = function(_) {
- if (!arguments.length) return precision;
- interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians);
- return circle;
- };
- return circle.angle(90);
- };
- function d3_geo_circleInterpolate(radius, precision) {
- var cr = Math.cos(radius), sr = Math.sin(radius);
- return function(from, to, direction, listener) {
- if (from != null) {
- from = d3_geo_circleAngle(cr, from);
- to = d3_geo_circleAngle(cr, to);
- if (direction > 0 ? from < to : from > to) from += direction * 2 * Ï;
- } else {
- from = radius + direction * 2 * Ï;
- to = radius;
- }
- var point;
- for (var step = direction * precision, t = from; direction > 0 ? t > to : t < to; t -= step) {
- listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]);
- }
- };
- }
- function d3_geo_circleAngle(cr, point) {
- var a = d3_geo_cartesian(point);
- a[0] -= cr;
- d3_geo_cartesianNormalize(a);
- var angle = d3_acos(-a[1]);
- return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI);
- }
- d3.geo.distance = function(a, b) {
- var Îλ = (b[0] - a[0]) * d3_radians, Ï0 = a[1] * d3_radians, Ï1 = b[1] * d3_radians, sinÎλ = Math.sin(Îλ), cosÎλ = Math.cos(Îλ), sinÏ0 = Math.sin(Ï0), cosÏ0 = Math.cos(Ï0), sinÏ1 = Math.sin(Ï1), cosÏ1 = Math.cos(Ï1), t;
- return Math.atan2(Math.sqrt((t = cosÏ1 * sinÎλ) * t + (t = cosÏ0 * sinÏ1 - sinÏ0 * cosÏ1 * cosÎλ) * t), sinÏ0 * sinÏ1 + cosÏ0 * cosÏ1 * cosÎλ);
- };
- d3.geo.graticule = function() {
- var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5;
- function graticule() {
- return {
- type: "MultiLineString",
- coordinates: lines()
- };
- }
- function lines() {
- return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) {
- return Math.abs(x % DX) > ε;
- }).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) {
- return Math.abs(y % DY) > ε;
- }).map(y));
- }
- graticule.lines = function() {
- return lines().map(function(coordinates) {
- return {
- type: "LineString",
- coordinates: coordinates
- };
- });
- };
- graticule.outline = function() {
- return {
- type: "Polygon",
- coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ]
- };
- };
- graticule.extent = function(_) {
- if (!arguments.length) return graticule.minorExtent();
- return graticule.majorExtent(_).minorExtent(_);
- };
- graticule.majorExtent = function(_) {
- if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ];
- X0 = +_[0][0], X1 = +_[1][0];
- Y0 = +_[0][1], Y1 = +_[1][1];
- if (X0 > X1) _ = X0, X0 = X1, X1 = _;
- if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _;
- return graticule.precision(precision);
- };
- graticule.minorExtent = function(_) {
- if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ];
- x0 = +_[0][0], x1 = +_[1][0];
- y0 = +_[0][1], y1 = +_[1][1];
- if (x0 > x1) _ = x0, x0 = x1, x1 = _;
- if (y0 > y1) _ = y0, y0 = y1, y1 = _;
- return graticule.precision(precision);
- };
- graticule.step = function(_) {
- if (!arguments.length) return graticule.minorStep();
- return graticule.majorStep(_).minorStep(_);
- };
- graticule.majorStep = function(_) {
- if (!arguments.length) return [ DX, DY ];
- DX = +_[0], DY = +_[1];
- return graticule;
- };
- graticule.minorStep = function(_) {
- if (!arguments.length) return [ dx, dy ];
- dx = +_[0], dy = +_[1];
- return graticule;
- };
- graticule.precision = function(_) {
- if (!arguments.length) return precision;
- precision = +_;
- x = d3_geo_graticuleX(y0, y1, 90);
- y = d3_geo_graticuleY(x0, x1, precision);
- X = d3_geo_graticuleX(Y0, Y1, 90);
- Y = d3_geo_graticuleY(X0, X1, precision);
- return graticule;
- };
- return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]);
- };
- function d3_geo_graticuleX(y0, y1, dy) {
- var y = d3.range(y0, y1 - ε, dy).concat(y1);
- return function(x) {
- return y.map(function(y) {
- return [ x, y ];
- });
- };
- }
- function d3_geo_graticuleY(x0, x1, dx) {
- var x = d3.range(x0, x1 - ε, dx).concat(x1);
- return function(y) {
- return x.map(function(x) {
- return [ x, y ];
- });
- };
- }
- function d3_source(d) {
- return d.source;
- }
- function d3_target(d) {
- return d.target;
- }
- d3.geo.greatArc = function() {
- var source = d3_source, source_, target = d3_target, target_;
- function greatArc() {
- return {
- type: "LineString",
- coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ]
- };
- }
- greatArc.distance = function() {
- return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments));
- };
- greatArc.source = function(_) {
- if (!arguments.length) return source;
- source = _, source_ = typeof _ === "function" ? null : _;
- return greatArc;
- };
- greatArc.target = function(_) {
- if (!arguments.length) return target;
- target = _, target_ = typeof _ === "function" ? null : _;
- return greatArc;
- };
- greatArc.precision = function() {
- return arguments.length ? greatArc : 0;
- };
- return greatArc;
- };
- d3.geo.interpolate = function(source, target) {
- return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians);
- };
- function d3_geo_interpolate(x0, y0, x1, y1) {
- var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d);
- var interpolate = d ? function(t) {
- var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1;
- return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ];
- } : function() {
- return [ x0 * d3_degrees, y0 * d3_degrees ];
- };
- interpolate.distance = d;
- return interpolate;
- }
- d3.geo.length = function(object) {
- d3_geo_lengthSum = 0;
- d3.geo.stream(object, d3_geo_length);
- return d3_geo_lengthSum;
- };
- var d3_geo_lengthSum;
- var d3_geo_length = {
- sphere: d3_noop,
- point: d3_noop,
- lineStart: d3_geo_lengthLineStart,
- lineEnd: d3_noop,
- polygonStart: d3_noop,
- polygonEnd: d3_noop
- };
- function d3_geo_lengthLineStart() {
- var λ0, sinÏ0, cosÏ0;
- d3_geo_length.point = function(λ, Ï) {
- λ0 = λ * d3_radians, sinÏ0 = Math.sin(Ï *= d3_radians), cosÏ0 = Math.cos(Ï);
- d3_geo_length.point = nextPoint;
- };
- d3_geo_length.lineEnd = function() {
- d3_geo_length.point = d3_geo_length.lineEnd = d3_noop;
- };
- function nextPoint(λ, Ï) {
- var sinÏ = Math.sin(Ï *= d3_radians), cosÏ = Math.cos(Ï), t = Math.abs((λ *= d3_radians) - λ0), cosÎλ = Math.cos(t);
- d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosÏ * Math.sin(t)) * t + (t = cosÏ0 * sinÏ - sinÏ0 * cosÏ * cosÎλ) * t), sinÏ0 * sinÏ + cosÏ0 * cosÏ * cosÎλ);
- λ0 = λ, sinÏ0 = sinÏ, cosÏ0 = cosÏ;
- }
- }
- function d3_geo_azimuthal(scale, angle) {
- function azimuthal(λ, Ï) {
- var cosλ = Math.cos(λ), cosÏ = Math.cos(Ï), k = scale(cosλ * cosÏ);
- return [ k * cosÏ * Math.sin(λ), k * Math.sin(Ï) ];
- }
- azimuthal.invert = function(x, y) {
- var Ï = Math.sqrt(x * x + y * y), c = angle(Ï), sinc = Math.sin(c), cosc = Math.cos(c);
- return [ Math.atan2(x * sinc, Ï * cosc), Math.asin(Ï && y * sinc / Ï) ];
- };
- return azimuthal;
- }
- var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosÏ) {
- return Math.sqrt(2 / (1 + cosλcosÏ));
- }, function(Ï) {
- return 2 * Math.asin(Ï / 2);
- });
- (d3.geo.azimuthalEqualArea = function() {
- return d3_geo_projection(d3_geo_azimuthalEqualArea);
- }).raw = d3_geo_azimuthalEqualArea;
- var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosÏ) {
- var c = Math.acos(cosλcosÏ);
- return c && c / Math.sin(c);
- }, d3_identity);
- (d3.geo.azimuthalEquidistant = function() {
- return d3_geo_projection(d3_geo_azimuthalEquidistant);
- }).raw = d3_geo_azimuthalEquidistant;
- function d3_geo_conicConformal(Ï0, Ï1) {
- var cosÏ0 = Math.cos(Ï0), t = function(Ï) {
- return Math.tan(Ï / 4 + Ï / 2);
- }, n = Ï0 === Ï1 ? Math.sin(Ï0) : Math.log(cosÏ0 / Math.cos(Ï1)) / Math.log(t(Ï1) / t(Ï0)), F = cosÏ0 * Math.pow(t(Ï0), n) / n;
- if (!n) return d3_geo_mercator;
- function forward(λ, Ï) {
- var Ï = Math.abs(Math.abs(Ï) - Ï / 2) < ε ? 0 : F / Math.pow(t(Ï), n);
- return [ Ï * Math.sin(n * λ), F - Ï * Math.cos(n * λ) ];
- }
- forward.invert = function(x, y) {
- var Ï0_y = F - y, Ï = d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y);
- return [ Math.atan2(x, Ï0_y) / n, 2 * Math.atan(Math.pow(F / Ï, 1 / n)) - Ï / 2 ];
- };
- return forward;
- }
- (d3.geo.conicConformal = function() {
- return d3_geo_conic(d3_geo_conicConformal);
- }).raw = d3_geo_conicConformal;
- function d3_geo_conicEquidistant(Ï0, Ï1) {
- var cosÏ0 = Math.cos(Ï0), n = Ï0 === Ï1 ? Math.sin(Ï0) : (cosÏ0 - Math.cos(Ï1)) / (Ï1 - Ï0), G = cosÏ0 / n + Ï0;
- if (Math.abs(n) < ε) return d3_geo_equirectangular;
- function forward(λ, Ï) {
- var Ï = G - Ï;
- return [ Ï * Math.sin(n * λ), G - Ï * Math.cos(n * λ) ];
- }
- forward.invert = function(x, y) {
- var Ï0_y = G - y;
- return [ Math.atan2(x, Ï0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + Ï0_y * Ï0_y) ];
- };
- return forward;
- }
- (d3.geo.conicEquidistant = function() {
- return d3_geo_conic(d3_geo_conicEquidistant);
- }).raw = d3_geo_conicEquidistant;
- var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosÏ) {
- return 1 / cosλcosÏ;
- }, Math.atan);
- (d3.geo.gnomonic = function() {
- return d3_geo_projection(d3_geo_gnomonic);
- }).raw = d3_geo_gnomonic;
- function d3_geo_mercator(λ, Ï) {
- return [ λ, Math.log(Math.tan(Ï / 4 + Ï / 2)) ];
- }
- d3_geo_mercator.invert = function(x, y) {
- return [ x, 2 * Math.atan(Math.exp(y)) - Ï / 2 ];
- };
- function d3_geo_mercatorProjection(project) {
- var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto;
- m.scale = function() {
- var v = scale.apply(m, arguments);
- return v === m ? clipAuto ? m.clipExtent(null) : m : v;
- };
- m.translate = function() {
- var v = translate.apply(m, arguments);
- return v === m ? clipAuto ? m.clipExtent(null) : m : v;
- };
- m.clipExtent = function(_) {
- var v = clipExtent.apply(m, arguments);
- if (v === m) {
- if (clipAuto = _ == null) {
- var k = Ï * scale(), t = translate();
- clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]);
- }
- } else if (clipAuto) {
- v = null;
- }
- return v;
- };
- return m.clipExtent(null);
- }
- (d3.geo.mercator = function() {
- return d3_geo_mercatorProjection(d3_geo_mercator);
- }).raw = d3_geo_mercator;
- var d3_geo_orthographic = d3_geo_azimuthal(function() {
- return 1;
- }, Math.asin);
- (d3.geo.orthographic = function() {
- return d3_geo_projection(d3_geo_orthographic);
- }).raw = d3_geo_orthographic;
- var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosÏ) {
- return 1 / (1 + cosλcosÏ);
- }, function(Ï) {
- return 2 * Math.atan(Ï);
- });
- (d3.geo.stereographic = function() {
- return d3_geo_projection(d3_geo_stereographic);
- }).raw = d3_geo_stereographic;
- function d3_geo_transverseMercator(λ, Ï) {
- var B = Math.cos(Ï) * Math.sin(λ);
- return [ Math.log((1 + B) / (1 - B)) / 2, Math.atan2(Math.tan(Ï), Math.cos(λ)) ];
- }
- d3_geo_transverseMercator.invert = function(x, y) {
- return [ Math.atan2(d3_sinh(x), Math.cos(y)), d3_asin(Math.sin(y) / d3_cosh(x)) ];
- };
- (d3.geo.transverseMercator = function() {
- return d3_geo_mercatorProjection(d3_geo_transverseMercator);
- }).raw = d3_geo_transverseMercator;
- d3.geom = {};
- d3.svg = {};
- function d3_svg_line(projection) {
- var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
- function line(data) {
- var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
- function segment() {
- segments.push("M", interpolate(projection(points), tension));
- }
- while (++i < n) {
- if (defined.call(this, d = data[i], i)) {
- points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
- } else if (points.length) {
- segment();
- points = [];
- }
- }
- if (points.length) segment();
- return segments.length ? segments.join("") : null;
- }
- line.x = function(_) {
- if (!arguments.length) return x;
- x = _;
- return line;
- };
- line.y = function(_) {
- if (!arguments.length) return y;
- y = _;
- return line;
- };
- line.defined = function(_) {
- if (!arguments.length) return defined;
- defined = _;
- return line;
- };
- line.interpolate = function(_) {
- if (!arguments.length) return interpolateKey;
- if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
- return line;
- };
- line.tension = function(_) {
- if (!arguments.length) return tension;
- tension = _;
- return line;
- };
- return line;
- }
- d3.svg.line = function() {
- return d3_svg_line(d3_identity);
- };
- function d3_svg_lineX(d) {
- return d[0];
- }
- function d3_svg_lineY(d) {
- return d[1];
- }
- var d3_svg_lineInterpolators = d3.map({
- linear: d3_svg_lineLinear,
- "linear-closed": d3_svg_lineLinearClosed,
- step: d3_svg_lineStep,
- "step-before": d3_svg_lineStepBefore,
- "step-after": d3_svg_lineStepAfter,
- basis: d3_svg_lineBasis,
- "basis-open": d3_svg_lineBasisOpen,
- "basis-closed": d3_svg_lineBasisClosed,
- bundle: d3_svg_lineBundle,
- cardinal: d3_svg_lineCardinal,
- "cardinal-open": d3_svg_lineCardinalOpen,
- "cardinal-closed": d3_svg_lineCardinalClosed,
- monotone: d3_svg_lineMonotone
- });
- d3_svg_lineInterpolators.forEach(function(key, value) {
- value.key = key;
- value.closed = /-closed$/.test(key);
- });
- function d3_svg_lineLinear(points) {
- return points.join("L");
- }
- function d3_svg_lineLinearClosed(points) {
- return d3_svg_lineLinear(points) + "Z";
- }
- function d3_svg_lineStep(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]);
- if (n > 1) path.push("H", p[0]);
- return path.join("");
- }
- function d3_svg_lineStepBefore(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
- return path.join("");
- }
- function d3_svg_lineStepAfter(points) {
- var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
- while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
- return path.join("");
- }
- function d3_svg_lineCardinalOpen(points, tension) {
- return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
- }
- function d3_svg_lineCardinalClosed(points, tension) {
- return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
- points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
- }
- function d3_svg_lineCardinal(points, tension) {
- return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
- }
- function d3_svg_lineHermite(points, tangents) {
- if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
- return d3_svg_lineLinear(points);
- }
- var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
- if (quad) {
- path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
- p0 = points[1];
- pi = 2;
- }
- if (tangents.length > 1) {
- t = tangents[1];
- p = points[pi];
- pi++;
- path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
- for (var i = 2; i < tangents.length; i++, pi++) {
- p = points[pi];
- t = tangents[i];
- path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
- }
- }
- if (quad) {
- var lp = points[pi];
- path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
- }
- return path;
- }
- function d3_svg_lineCardinalTangents(points, tension) {
- var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
- while (++i < n) {
- p0 = p1;
- p1 = p2;
- p2 = points[i];
- tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
- }
- return tangents;
- }
- function d3_svg_lineBasis(points) {
- if (points.length < 3) return d3_svg_lineLinear(points);
- var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ];
- d3_svg_lineBasisBezier(path, px, py);
- while (++i < n) {
- pi = points[i];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- i = -1;
- while (++i < 2) {
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- return path.join("");
- }
- function d3_svg_lineBasisOpen(points) {
- if (points.length < 4) return d3_svg_lineLinear(points);
- var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
- while (++i < 3) {
- pi = points[i];
- px.push(pi[0]);
- py.push(pi[1]);
- }
- path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
- --i;
- while (++i < n) {
- pi = points[i];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- return path.join("");
- }
- function d3_svg_lineBasisClosed(points) {
- var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
- while (++i < 4) {
- pi = points[i % n];
- px.push(pi[0]);
- py.push(pi[1]);
- }
- path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
- --i;
- while (++i < m) {
- pi = points[i % n];
- px.shift();
- px.push(pi[0]);
- py.shift();
- py.push(pi[1]);
- d3_svg_lineBasisBezier(path, px, py);
- }
- return path.join("");
- }
- function d3_svg_lineBundle(points, tension) {
- var n = points.length - 1;
- if (n) {
- var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
- while (++i <= n) {
- p = points[i];
- t = i / n;
- p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
- p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
- }
- }
- return d3_svg_lineBasis(points);
- }
- function d3_svg_lineDot4(a, b) {
- return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
- }
- var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
- function d3_svg_lineBasisBezier(path, x, y) {
- path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
- }
- function d3_svg_lineSlope(p0, p1) {
- return (p1[1] - p0[1]) / (p1[0] - p0[0]);
- }
- function d3_svg_lineFiniteDifferences(points) {
- var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
- while (++i < j) {
- m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
- }
- m[i] = d;
- return m;
- }
- function d3_svg_lineMonotoneTangents(points) {
- var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
- while (++i < j) {
- d = d3_svg_lineSlope(points[i], points[i + 1]);
- if (Math.abs(d) < 1e-6) {
- m[i] = m[i + 1] = 0;
- } else {
- a = m[i] / d;
- b = m[i + 1] / d;
- s = a * a + b * b;
- if (s > 9) {
- s = d * 3 / Math.sqrt(s);
- m[i] = s * a;
- m[i + 1] = s * b;
- }
- }
- }
- i = -1;
- while (++i <= j) {
- s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
- tangents.push([ s || 0, m[i] * s || 0 ]);
- }
- return tangents;
- }
- function d3_svg_lineMonotone(points) {
- return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
- }
- d3.geom.hull = function(vertices) {
- var x = d3_svg_lineX, y = d3_svg_lineY;
- if (arguments.length) return hull(vertices);
- function hull(data) {
- if (data.length < 3) return [];
- var fx = d3_functor(x), fy = d3_functor(y), n = data.length, vertices, plen = n - 1, points = [], stack = [], d, i, j, h = 0, x1, y1, x2, y2, u, v, a, sp;
- if (fx === d3_svg_lineX && y === d3_svg_lineY) vertices = data; else for (i = 0,
- vertices = []; i < n; ++i) {
- vertices.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
- }
- for (i = 1; i < n; ++i) {
- if (vertices[i][1] < vertices[h][1] || vertices[i][1] == vertices[h][1] && vertices[i][0] < vertices[h][0]) h = i;
- }
- for (i = 0; i < n; ++i) {
- if (i === h) continue;
- y1 = vertices[i][1] - vertices[h][1];
- x1 = vertices[i][0] - vertices[h][0];
- points.push({
- angle: Math.atan2(y1, x1),
- index: i
- });
- }
- points.sort(function(a, b) {
- return a.angle - b.angle;
- });
- a = points[0].angle;
- v = points[0].index;
- u = 0;
- for (i = 1; i < plen; ++i) {
- j = points[i].index;
- if (a == points[i].angle) {
- x1 = vertices[v][0] - vertices[h][0];
- y1 = vertices[v][1] - vertices[h][1];
- x2 = vertices[j][0] - vertices[h][0];
- y2 = vertices[j][1] - vertices[h][1];
- if (x1 * x1 + y1 * y1 >= x2 * x2 + y2 * y2) {
- points[i].index = -1;
- continue;
- } else {
- points[u].index = -1;
- }
- }
- a = points[i].angle;
- u = i;
- v = j;
- }
- stack.push(h);
- for (i = 0, j = 0; i < 2; ++j) {
- if (points[j].index > -1) {
- stack.push(points[j].index);
- i++;
- }
- }
- sp = stack.length;
- for (;j < plen; ++j) {
- if (points[j].index < 0) continue;
- while (!d3_geom_hullCCW(stack[sp - 2], stack[sp - 1], points[j].index, vertices)) {
- --sp;
- }
- stack[sp++] = points[j].index;
- }
- var poly = [];
- for (i = sp - 1; i >= 0; --i) poly.push(data[stack[i]]);
- return poly;
- }
- hull.x = function(_) {
- return arguments.length ? (x = _, hull) : x;
- };
- hull.y = function(_) {
- return arguments.length ? (y = _, hull) : y;
- };
- return hull;
- };
- function d3_geom_hullCCW(i1, i2, i3, v) {
- var t, a, b, c, d, e, f;
- t = v[i1];
- a = t[0];
- b = t[1];
- t = v[i2];
- c = t[0];
- d = t[1];
- t = v[i3];
- e = t[0];
- f = t[1];
- return (f - b) * (c - a) - (d - b) * (e - a) > 0;
- }
- d3.geom.polygon = function(coordinates) {
- coordinates.area = function() {
- var i = 0, n = coordinates.length, area = coordinates[n - 1][1] * coordinates[0][0] - coordinates[n - 1][0] * coordinates[0][1];
- while (++i < n) {
- area += coordinates[i - 1][1] * coordinates[i][0] - coordinates[i - 1][0] * coordinates[i][1];
- }
- return area * .5;
- };
- coordinates.centroid = function(k) {
- var i = -1, n = coordinates.length, x = 0, y = 0, a, b = coordinates[n - 1], c;
- if (!arguments.length) k = -1 / (6 * coordinates.area());
- while (++i < n) {
- a = b;
- b = coordinates[i];
- c = a[0] * b[1] - b[0] * a[1];
- x += (a[0] + b[0]) * c;
- y += (a[1] + b[1]) * c;
- }
- return [ x * k, y * k ];
- };
- coordinates.clip = function(subject) {
- var input, i = -1, n = coordinates.length, j, m, a = coordinates[n - 1], b, c, d;
- while (++i < n) {
- input = subject.slice();
- subject.length = 0;
- b = coordinates[i];
- c = input[(m = input.length) - 1];
- j = -1;
- while (++j < m) {
- d = input[j];
- if (d3_geom_polygonInside(d, a, b)) {
- if (!d3_geom_polygonInside(c, a, b)) {
- subject.push(d3_geom_polygonIntersect(c, d, a, b));
- }
- subject.push(d);
- } else if (d3_geom_polygonInside(c, a, b)) {
- subject.push(d3_geom_polygonIntersect(c, d, a, b));
- }
- c = d;
- }
- a = b;
- }
- return subject;
- };
- return coordinates;
- };
- function d3_geom_polygonInside(p, a, b) {
- return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]);
- }
- function d3_geom_polygonIntersect(c, d, a, b) {
- var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
- return [ x1 + ua * x21, y1 + ua * y21 ];
- }
- d3.geom.delaunay = function(vertices) {
- var edges = vertices.map(function() {
- return [];
- }), triangles = [];
- d3_geom_voronoiTessellate(vertices, function(e) {
- edges[e.region.l.index].push(vertices[e.region.r.index]);
- });
- edges.forEach(function(edge, i) {
- var v = vertices[i], cx = v[0], cy = v[1];
- edge.forEach(function(v) {
- v.angle = Math.atan2(v[0] - cx, v[1] - cy);
- });
- edge.sort(function(a, b) {
- return a.angle - b.angle;
- });
- for (var j = 0, m = edge.length - 1; j < m; j++) {
- triangles.push([ v, edge[j], edge[j + 1] ]);
- }
- });
- return triangles;
- };
- d3.geom.voronoi = function(points) {
- var x = d3_svg_lineX, y = d3_svg_lineY, clipPolygon = null;
- if (arguments.length) return voronoi(points);
- function voronoi(data) {
- var points, polygons = data.map(function() {
- return [];
- }), fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length, Z = 1e6;
- if (fx === d3_svg_lineX && fy === d3_svg_lineY) points = data; else for (points = new Array(n),
- i = 0; i < n; ++i) {
- points[i] = [ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ];
- }
- d3_geom_voronoiTessellate(points, function(e) {
- var s1, s2, x1, x2, y1, y2;
- if (e.a === 1 && e.b >= 0) {
- s1 = e.ep.r;
- s2 = e.ep.l;
- } else {
- s1 = e.ep.l;
- s2 = e.ep.r;
- }
- if (e.a === 1) {
- y1 = s1 ? s1.y : -Z;
- x1 = e.c - e.b * y1;
- y2 = s2 ? s2.y : Z;
- x2 = e.c - e.b * y2;
- } else {
- x1 = s1 ? s1.x : -Z;
- y1 = e.c - e.a * x1;
- x2 = s2 ? s2.x : Z;
- y2 = e.c - e.a * x2;
- }
- var v1 = [ x1, y1 ], v2 = [ x2, y2 ];
- polygons[e.region.l.index].push(v1, v2);
- polygons[e.region.r.index].push(v1, v2);
- });
- polygons = polygons.map(function(polygon, i) {
- var cx = points[i][0], cy = points[i][1], angle = polygon.map(function(v) {
- return Math.atan2(v[0] - cx, v[1] - cy);
- }), order = d3.range(polygon.length).sort(function(a, b) {
- return angle[a] - angle[b];
- });
- return order.filter(function(d, i) {
- return !i || angle[d] - angle[order[i - 1]] > ε;
- }).map(function(d) {
- return polygon[d];
- });
- });
- polygons.forEach(function(polygon, i) {
- var n = polygon.length;
- if (!n) return polygon.push([ -Z, -Z ], [ -Z, Z ], [ Z, Z ], [ Z, -Z ]);
- if (n > 2) return;
- var p0 = points[i], p1 = polygon[0], p2 = polygon[1], x0 = p0[0], y0 = p0[1], x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], dx = Math.abs(x2 - x1), dy = y2 - y1;
- if (Math.abs(dy) < ε) {
- var y = y0 < y1 ? -Z : Z;
- polygon.push([ -Z, y ], [ Z, y ]);
- } else if (dx < ε) {
- var x = x0 < x1 ? -Z : Z;
- polygon.push([ x, -Z ], [ x, Z ]);
- } else {
- var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z, z = Math.abs(dy) - dx;
- if (Math.abs(z) < ε) {
- polygon.push([ dy < 0 ? y : -y, y ]);
- } else {
- if (z > 0) y *= -1;
- polygon.push([ -Z, y ], [ Z, y ]);
- }
- }
- });
- if (clipPolygon) for (i = 0; i < n; ++i) clipPolygon.clip(polygons[i]);
- for (i = 0; i < n; ++i) polygons[i].point = data[i];
- return polygons;
- }
- voronoi.x = function(_) {
- return arguments.length ? (x = _, voronoi) : x;
- };
- voronoi.y = function(_) {
- return arguments.length ? (y = _, voronoi) : y;
- };
- voronoi.clipExtent = function(_) {
- if (!arguments.length) return clipPolygon && [ clipPolygon[0], clipPolygon[2] ];
- if (_ == null) clipPolygon = null; else {
- var x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], y2 = +_[1][1];
- clipPolygon = d3.geom.polygon([ [ x1, y1 ], [ x1, y2 ], [ x2, y2 ], [ x2, y1 ] ]);
- }
- return voronoi;
- };
- voronoi.size = function(_) {
- if (!arguments.length) return clipPolygon && clipPolygon[2];
- return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]);
- };
- voronoi.links = function(data) {
- var points, graph = data.map(function() {
- return [];
- }), links = [], fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
- if (fx === d3_svg_lineX && fy === d3_svg_lineY) points = data; else for (points = new Array(n),
- i = 0; i < n; ++i) {
- points[i] = [ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ];
- }
- d3_geom_voronoiTessellate(points, function(e) {
- var l = e.region.l.index, r = e.region.r.index;
- if (graph[l][r]) return;
- graph[l][r] = graph[r][l] = true;
- links.push({
- source: data[l],
- target: data[r]
- });
- });
- return links;
- };
- voronoi.triangles = function(data) {
- if (x === d3_svg_lineX && y === d3_svg_lineY) return d3.geom.delaunay(data);
- var points = new Array(n), fx = d3_functor(x), fy = d3_functor(y), d, i = -1, n = data.length;
- while (++i < n) {
- (points[i] = [ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]).data = d;
- }
- return d3.geom.delaunay(points).map(function(triangle) {
- return triangle.map(function(point) {
- return point.data;
- });
- });
- };
- return voronoi;
- };
- var d3_geom_voronoiOpposite = {
- l: "r",
- r: "l"
- };
- function d3_geom_voronoiTessellate(points, callback) {
- var Sites = {
- list: points.map(function(v, i) {
- return {
- index: i,
- x: v[0],
- y: v[1]
- };
- }).sort(function(a, b) {
- return a.y < b.y ? -1 : a.y > b.y ? 1 : a.x < b.x ? -1 : a.x > b.x ? 1 : 0;
- }),
- bottomSite: null
- };
- var EdgeList = {
- list: [],
- leftEnd: null,
- rightEnd: null,
- init: function() {
- EdgeList.leftEnd = EdgeList.createHalfEdge(null, "l");
- EdgeList.rightEnd = EdgeList.createHalfEdge(null, "l");
- EdgeList.leftEnd.r = EdgeList.rightEnd;
- EdgeList.rightEnd.l = EdgeList.leftEnd;
- EdgeList.list.unshift(EdgeList.leftEnd, EdgeList.rightEnd);
- },
- createHalfEdge: function(edge, side) {
- return {
- edge: edge,
- side: side,
- vertex: null,
- l: null,
- r: null
- };
- },
- insert: function(lb, he) {
- he.l = lb;
- he.r = lb.r;
- lb.r.l = he;
- lb.r = he;
- },
- leftBound: function(p) {
- var he = EdgeList.leftEnd;
- do {
- he = he.r;
- } while (he != EdgeList.rightEnd && Geom.rightOf(he, p));
- he = he.l;
- return he;
- },
- del: function(he) {
- he.l.r = he.r;
- he.r.l = he.l;
- he.edge = null;
- },
- right: function(he) {
- return he.r;
- },
- left: function(he) {
- return he.l;
- },
- leftRegion: function(he) {
- return he.edge == null ? Sites.bottomSite : he.edge.region[he.side];
- },
- rightRegion: function(he) {
- return he.edge == null ? Sites.bottomSite : he.edge.region[d3_geom_voronoiOpposite[he.side]];
- }
- };
- var Geom = {
- bisect: function(s1, s2) {
- var newEdge = {
- region: {
- l: s1,
- r: s2
- },
- ep: {
- l: null,
- r: null
- }
- };
- var dx = s2.x - s1.x, dy = s2.y - s1.y, adx = dx > 0 ? dx : -dx, ady = dy > 0 ? dy : -dy;
- newEdge.c = s1.x * dx + s1.y * dy + (dx * dx + dy * dy) * .5;
- if (adx > ady) {
- newEdge.a = 1;
- newEdge.b = dy / dx;
- newEdge.c /= dx;
- } else {
- newEdge.b = 1;
- newEdge.a = dx / dy;
- newEdge.c /= dy;
- }
- return newEdge;
- },
- intersect: function(el1, el2) {
- var e1 = el1.edge, e2 = el2.edge;
- if (!e1 || !e2 || e1.region.r == e2.region.r) {
- return null;
- }
- var d = e1.a * e2.b - e1.b * e2.a;
- if (Math.abs(d) < 1e-10) {
- return null;
- }
- var xint = (e1.c * e2.b - e2.c * e1.b) / d, yint = (e2.c * e1.a - e1.c * e2.a) / d, e1r = e1.region.r, e2r = e2.region.r, el, e;
- if (e1r.y < e2r.y || e1r.y == e2r.y && e1r.x < e2r.x) {
- el = el1;
- e = e1;
- } else {
- el = el2;
- e = e2;
- }
- var rightOfSite = xint >= e.region.r.x;
- if (rightOfSite && el.side === "l" || !rightOfSite && el.side === "r") {
- return null;
- }
- return {
- x: xint,
- y: yint
- };
- },
- rightOf: function(he, p) {
- var e = he.edge, topsite = e.region.r, rightOfSite = p.x > topsite.x;
- if (rightOfSite && he.side === "l") {
- return 1;
- }
- if (!rightOfSite && he.side === "r") {
- return 0;
- }
- if (e.a === 1) {
- var dyp = p.y - topsite.y, dxp = p.x - topsite.x, fast = 0, above = 0;
- if (!rightOfSite && e.b < 0 || rightOfSite && e.b >= 0) {
- above = fast = dyp >= e.b * dxp;
- } else {
- above = p.x + p.y * e.b > e.c;
- if (e.b < 0) {
- above = !above;
- }
- if (!above) {
- fast = 1;
- }
- }
- if (!fast) {
- var dxs = topsite.x - e.region.l.x;
- above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp * (1 + 2 * dxp / dxs + e.b * e.b);
- if (e.b < 0) {
- above = !above;
- }
- }
- } else {
- var yl = e.c - e.a * p.x, t1 = p.y - yl, t2 = p.x - topsite.x, t3 = yl - topsite.y;
- above = t1 * t1 > t2 * t2 + t3 * t3;
- }
- return he.side === "l" ? above : !above;
- },
- endPoint: function(edge, side, site) {
- edge.ep[side] = site;
- if (!edge.ep[d3_geom_voronoiOpposite[side]]) return;
- callback(edge);
- },
- distance: function(s, t) {
- var dx = s.x - t.x, dy = s.y - t.y;
- return Math.sqrt(dx * dx + dy * dy);
- }
- };
- var EventQueue = {
- list: [],
- insert: function(he, site, offset) {
- he.vertex = site;
- he.ystar = site.y + offset;
- for (var i = 0, list = EventQueue.list, l = list.length; i < l; i++) {
- var next = list[i];
- if (he.ystar > next.ystar || he.ystar == next.ystar && site.x > next.vertex.x) {
- continue;
- } else {
- break;
- }
- }
- list.splice(i, 0, he);
- },
- del: function(he) {
- for (var i = 0, ls = EventQueue.list, l = ls.length; i < l && ls[i] != he; ++i) {}
- ls.splice(i, 1);
- },
- empty: function() {
- return EventQueue.list.length === 0;
- },
- nextEvent: function(he) {
- for (var i = 0, ls = EventQueue.list, l = ls.length; i < l; ++i) {
- if (ls[i] == he) return ls[i + 1];
- }
- return null;
- },
- min: function() {
- var elem = EventQueue.list[0];
- return {
- x: elem.vertex.x,
- y: elem.ystar
- };
- },
- extractMin: function() {
- return EventQueue.list.shift();
- }
- };
- EdgeList.init();
- Sites.bottomSite = Sites.list.shift();
- var newSite = Sites.list.shift(), newIntStar;
- var lbnd, rbnd, llbnd, rrbnd, bisector;
- var bot, top, temp, p, v;
- var e, pm;
- while (true) {
- if (!EventQueue.empty()) {
- newIntStar = EventQueue.min();
- }
- if (newSite && (EventQueue.empty() || newSite.y < newIntStar.y || newSite.y == newIntStar.y && newSite.x < newIntStar.x)) {
- lbnd = EdgeList.leftBound(newSite);
- rbnd = EdgeList.right(lbnd);
- bot = EdgeList.rightRegion(lbnd);
- e = Geom.bisect(bot, newSite);
- bisector = EdgeList.createHalfEdge(e, "l");
- EdgeList.insert(lbnd, bisector);
- p = Geom.intersect(lbnd, bisector);
- if (p) {
- EventQueue.del(lbnd);
- EventQueue.insert(lbnd, p, Geom.distance(p, newSite));
- }
- lbnd = bisector;
- bisector = EdgeList.createHalfEdge(e, "r");
- EdgeList.insert(lbnd, bisector);
- p = Geom.intersect(bisector, rbnd);
- if (p) {
- EventQueue.insert(bisector, p, Geom.distance(p, newSite));
- }
- newSite = Sites.list.shift();
- } else if (!EventQueue.empty()) {
- lbnd = EventQueue.extractMin();
- llbnd = EdgeList.left(lbnd);
- rbnd = EdgeList.right(lbnd);
- rrbnd = EdgeList.right(rbnd);
- bot = EdgeList.leftRegion(lbnd);
- top = EdgeList.rightRegion(rbnd);
- v = lbnd.vertex;
- Geom.endPoint(lbnd.edge, lbnd.side, v);
- Geom.endPoint(rbnd.edge, rbnd.side, v);
- EdgeList.del(lbnd);
- EventQueue.del(rbnd);
- EdgeList.del(rbnd);
- pm = "l";
- if (bot.y > top.y) {
- temp = bot;
- bot = top;
- top = temp;
- pm = "r";
- }
- e = Geom.bisect(bot, top);
- bisector = EdgeList.createHalfEdge(e, pm);
- EdgeList.insert(llbnd, bisector);
- Geom.endPoint(e, d3_geom_voronoiOpposite[pm], v);
- p = Geom.intersect(llbnd, bisector);
- if (p) {
- EventQueue.del(llbnd);
- EventQueue.insert(llbnd, p, Geom.distance(p, bot));
- }
- p = Geom.intersect(bisector, rrbnd);
- if (p) {
- EventQueue.insert(bisector, p, Geom.distance(p, bot));
- }
- } else {
- break;
- }
- }
- for (lbnd = EdgeList.right(EdgeList.leftEnd); lbnd != EdgeList.rightEnd; lbnd = EdgeList.right(lbnd)) {
- callback(lbnd.edge);
- }
- }
- d3.geom.quadtree = function(points, x1, y1, x2, y2) {
- var x = d3_svg_lineX, y = d3_svg_lineY, compat;
- if (compat = arguments.length) {
- x = d3_geom_quadtreeCompatX;
- y = d3_geom_quadtreeCompatY;
- if (compat === 3) {
- y2 = y1;
- x2 = x1;
- y1 = x1 = 0;
- }
- return quadtree(points);
- }
- function quadtree(data) {
- var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_;
- if (x1 != null) {
- x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2;
- } else {
- x2_ = y2_ = -(x1_ = y1_ = Infinity);
- xs = [], ys = [];
- n = data.length;
- if (compat) for (i = 0; i < n; ++i) {
- d = data[i];
- if (d.x < x1_) x1_ = d.x;
- if (d.y < y1_) y1_ = d.y;
- if (d.x > x2_) x2_ = d.x;
- if (d.y > y2_) y2_ = d.y;
- xs.push(d.x);
- ys.push(d.y);
- } else for (i = 0; i < n; ++i) {
- var x_ = +fx(d = data[i], i), y_ = +fy(d, i);
- if (x_ < x1_) x1_ = x_;
- if (y_ < y1_) y1_ = y_;
- if (x_ > x2_) x2_ = x_;
- if (y_ > y2_) y2_ = y_;
- xs.push(x_);
- ys.push(y_);
- }
- }
- var dx = x2_ - x1_, dy = y2_ - y1_;
- if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy;
- function insert(n, d, x, y, x1, y1, x2, y2) {
- if (isNaN(x) || isNaN(y)) return;
- if (n.leaf) {
- var nx = n.x, ny = n.y;
- if (nx != null) {
- if (Math.abs(nx - x) + Math.abs(ny - y) < .01) {
- insertChild(n, d, x, y, x1, y1, x2, y2);
- } else {
- var nPoint = n.point;
- n.x = n.y = n.point = null;
- insertChild(n, nPoint, nx, ny, x1, y1, x2, y2);
- insertChild(n, d, x, y, x1, y1, x2, y2);
- }
- } else {
- n.x = x, n.y = y, n.point = d;
- }
- } else {
- insertChild(n, d, x, y, x1, y1, x2, y2);
- }
- }
- function insertChild(n, d, x, y, x1, y1, x2, y2) {
- var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, right = x >= sx, bottom = y >= sy, i = (bottom << 1) + right;
- n.leaf = false;
- n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode());
- if (right) x1 = sx; else x2 = sx;
- if (bottom) y1 = sy; else y2 = sy;
- insert(n, d, x, y, x1, y1, x2, y2);
- }
- var root = d3_geom_quadtreeNode();
- root.add = function(d) {
- insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_);
- };
- root.visit = function(f) {
- d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_);
- };
- i = -1;
- if (x1 == null) {
- while (++i < n) {
- insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_);
- }
- --i;
- } else data.forEach(root.add);
- xs = ys = data = d = null;
- return root;
- }
- quadtree.x = function(_) {
- return arguments.length ? (x = _, quadtree) : x;
- };
- quadtree.y = function(_) {
- return arguments.length ? (y = _, quadtree) : y;
- };
- quadtree.extent = function(_) {
- if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ];
- if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0],
- y2 = +_[1][1];
- return quadtree;
- };
- quadtree.size = function(_) {
- if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ];
- if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1];
- return quadtree;
- };
- return quadtree;
- };
- function d3_geom_quadtreeCompatX(d) {
- return d.x;
- }
- function d3_geom_quadtreeCompatY(d) {
- return d.y;
- }
- function d3_geom_quadtreeNode() {
- return {
- leaf: true,
- nodes: [],
- point: null,
- x: null,
- y: null
- };
- }
- function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) {
- if (!f(node, x1, y1, x2, y2)) {
- var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes;
- if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy);
- if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy);
- if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2);
- if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2);
- }
- }
- d3.interpolateRgb = d3_interpolateRgb;
- function d3_interpolateRgb(a, b) {
- a = d3.rgb(a);
- b = d3.rgb(b);
- var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab;
- return function(t) {
- return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t));
- };
- }
- d3.transform = function(string) {
- var g = d3_document.createElementNS(d3.ns.prefix.svg, "g");
- return (d3.transform = function(string) {
- if (string != null) {
- g.setAttribute("transform", string);
- var t = g.transform.baseVal.consolidate();
- }
- return new d3_transform(t ? t.matrix : d3_transformIdentity);
- })(string);
- };
- function d3_transform(m) {
- var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0;
- if (r0[0] * r1[1] < r1[0] * r0[1]) {
- r0[0] *= -1;
- r0[1] *= -1;
- kx *= -1;
- kz *= -1;
- }
- this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees;
- this.translate = [ m.e, m.f ];
- this.scale = [ kx, ky ];
- this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0;
- }
- d3_transform.prototype.toString = function() {
- return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")";
- };
- function d3_transformDot(a, b) {
- return a[0] * b[0] + a[1] * b[1];
- }
- function d3_transformNormalize(a) {
- var k = Math.sqrt(d3_transformDot(a, a));
- if (k) {
- a[0] /= k;
- a[1] /= k;
- }
- return k;
- }
- function d3_transformCombine(a, b, k) {
- a[0] += k * b[0];
- a[1] += k * b[1];
- return a;
- }
- var d3_transformIdentity = {
- a: 1,
- b: 0,
- c: 0,
- d: 1,
- e: 0,
- f: 0
- };
- d3.interpolateNumber = d3_interpolateNumber;
- function d3_interpolateNumber(a, b) {
- b -= a = +a;
- return function(t) {
- return a + b * t;
- };
- }
- d3.interpolateTransform = d3_interpolateTransform;
- function d3_interpolateTransform(a, b) {
- var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
- if (ta[0] != tb[0] || ta[1] != tb[1]) {
- s.push("translate(", null, ",", null, ")");
- q.push({
- i: 1,
- x: d3_interpolateNumber(ta[0], tb[0])
- }, {
- i: 3,
- x: d3_interpolateNumber(ta[1], tb[1])
- });
- } else if (tb[0] || tb[1]) {
- s.push("translate(" + tb + ")");
- } else {
- s.push("");
- }
- if (ra != rb) {
- if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360;
- q.push({
- i: s.push(s.pop() + "rotate(", null, ")") - 2,
- x: d3_interpolateNumber(ra, rb)
- });
- } else if (rb) {
- s.push(s.pop() + "rotate(" + rb + ")");
- }
- if (wa != wb) {
- q.push({
- i: s.push(s.pop() + "skewX(", null, ")") - 2,
- x: d3_interpolateNumber(wa, wb)
- });
- } else if (wb) {
- s.push(s.pop() + "skewX(" + wb + ")");
- }
- if (ka[0] != kb[0] || ka[1] != kb[1]) {
- n = s.push(s.pop() + "scale(", null, ",", null, ")");
- q.push({
- i: n - 4,
- x: d3_interpolateNumber(ka[0], kb[0])
- }, {
- i: n - 2,
- x: d3_interpolateNumber(ka[1], kb[1])
- });
- } else if (kb[0] != 1 || kb[1] != 1) {
- s.push(s.pop() + "scale(" + kb + ")");
- }
- n = q.length;
- return function(t) {
- var i = -1, o;
- while (++i < n) s[(o = q[i]).i] = o.x(t);
- return s.join("");
- };
- }
- d3.interpolateObject = d3_interpolateObject;
- function d3_interpolateObject(a, b) {
- var i = {}, c = {}, k;
- for (k in a) {
- if (k in b) {
- i[k] = d3_interpolateByName(k)(a[k], b[k]);
- } else {
- c[k] = a[k];
- }
- }
- for (k in b) {
- if (!(k in a)) {
- c[k] = b[k];
- }
- }
- return function(t) {
- for (k in i) c[k] = i[k](t);
- return c;
- };
- }
- d3.interpolateString = d3_interpolateString;
- function d3_interpolateString(a, b) {
- var m, i, j, s0 = 0, s1 = 0, s = [], q = [], n, o;
- a = a + "", b = b + "";
- d3_interpolate_number.lastIndex = 0;
- for (i = 0; m = d3_interpolate_number.exec(b); ++i) {
- if (m.index) s.push(b.substring(s0, s1 = m.index));
- q.push({
- i: s.length,
- x: m[0]
- });
- s.push(null);
- s0 = d3_interpolate_number.lastIndex;
- }
- if (s0 < b.length) s.push(b.substring(s0));
- for (i = 0, n = q.length; (m = d3_interpolate_number.exec(a)) && i < n; ++i) {
- o = q[i];
- if (o.x == m[0]) {
- if (o.i) {
- if (s[o.i + 1] == null) {
- s[o.i - 1] += o.x;
- s.splice(o.i, 1);
- for (j = i + 1; j < n; ++j) q[j].i--;
- } else {
- s[o.i - 1] += o.x + s[o.i + 1];
- s.splice(o.i, 2);
- for (j = i + 1; j < n; ++j) q[j].i -= 2;
- }
- } else {
- if (s[o.i + 1] == null) {
- s[o.i] = o.x;
- } else {
- s[o.i] = o.x + s[o.i + 1];
- s.splice(o.i + 1, 1);
- for (j = i + 1; j < n; ++j) q[j].i--;
- }
- }
- q.splice(i, 1);
- n--;
- i--;
- } else {
- o.x = d3_interpolateNumber(parseFloat(m[0]), parseFloat(o.x));
- }
- }
- while (i < n) {
- o = q.pop();
- if (s[o.i + 1] == null) {
- s[o.i] = o.x;
- } else {
- s[o.i] = o.x + s[o.i + 1];
- s.splice(o.i + 1, 1);
- }
- n--;
- }
- if (s.length === 1) {
- return s[0] == null ? (o = q[0].x, function(t) {
- return o(t) + "";
- }) : function() {
- return b;
- };
- }
- return function(t) {
- for (i = 0; i < n; ++i) s[(o = q[i]).i] = o.x(t);
- return s.join("");
- };
- }
- var d3_interpolate_number = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;
- d3.interpolate = d3_interpolate;
- function d3_interpolate(a, b) {
- var i = d3.interpolators.length, f;
- while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ;
- return f;
- }
- function d3_interpolateByName(name) {
- return name == "transform" ? d3_interpolateTransform : d3_interpolate;
- }
- d3.interpolators = [ function(a, b) {
- var t = typeof b;
- return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_Color ? d3_interpolateRgb : t === "object" ? Array.isArray(b) ? d3_interpolateArray : d3_interpolateObject : d3_interpolateNumber)(a, b);
- } ];
- d3.interpolateArray = d3_interpolateArray;
- function d3_interpolateArray(a, b) {
- var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i;
- for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i]));
- for (;i < na; ++i) c[i] = a[i];
- for (;i < nb; ++i) c[i] = b[i];
- return function(t) {
- for (i = 0; i < n0; ++i) c[i] = x[i](t);
- return c;
- };
- }
- var d3_ease_default = function() {
- return d3_identity;
- };
- var d3_ease = d3.map({
- linear: d3_ease_default,
- poly: d3_ease_poly,
- quad: function() {
- return d3_ease_quad;
- },
- cubic: function() {
- return d3_ease_cubic;
- },
- sin: function() {
- return d3_ease_sin;
- },
- exp: function() {
- return d3_ease_exp;
- },
- circle: function() {
- return d3_ease_circle;
- },
- elastic: d3_ease_elastic,
- back: d3_ease_back,
- bounce: function() {
- return d3_ease_bounce;
- }
- });
- var d3_ease_mode = d3.map({
- "in": d3_identity,
- out: d3_ease_reverse,
- "in-out": d3_ease_reflect,
- "out-in": function(f) {
- return d3_ease_reflect(d3_ease_reverse(f));
- }
- });
- d3.ease = function(name) {
- var i = name.indexOf("-"), t = i >= 0 ? name.substring(0, i) : name, m = i >= 0 ? name.substring(i + 1) : "in";
- t = d3_ease.get(t) || d3_ease_default;
- m = d3_ease_mode.get(m) || d3_identity;
- return d3_ease_clamp(m(t.apply(null, Array.prototype.slice.call(arguments, 1))));
- };
- function d3_ease_clamp(f) {
- return function(t) {
- return t <= 0 ? 0 : t >= 1 ? 1 : f(t);
- };
- }
- function d3_ease_reverse(f) {
- return function(t) {
- return 1 - f(1 - t);
- };
- }
- function d3_ease_reflect(f) {
- return function(t) {
- return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t));
- };
- }
- function d3_ease_quad(t) {
- return t * t;
- }
- function d3_ease_cubic(t) {
- return t * t * t;
- }
- function d3_ease_cubicInOut(t) {
- if (t <= 0) return 0;
- if (t >= 1) return 1;
- var t2 = t * t, t3 = t2 * t;
- return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
- }
- function d3_ease_poly(e) {
- return function(t) {
- return Math.pow(t, e);
- };
- }
- function d3_ease_sin(t) {
- return 1 - Math.cos(t * Ï / 2);
- }
- function d3_ease_exp(t) {
- return Math.pow(2, 10 * (t - 1));
- }
- function d3_ease_circle(t) {
- return 1 - Math.sqrt(1 - t * t);
- }
- function d3_ease_elastic(a, p) {
- var s;
- if (arguments.length < 2) p = .45;
- if (arguments.length) s = p / (2 * Ï) * Math.asin(1 / a); else a = 1, s = p / 4;
- return function(t) {
- return 1 + a * Math.pow(2, 10 * -t) * Math.sin((t - s) * 2 * Ï / p);
- };
- }
- function d3_ease_back(s) {
- if (!s) s = 1.70158;
- return function(t) {
- return t * t * ((s + 1) * t - s);
- };
- }
- function d3_ease_bounce(t) {
- return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375;
- }
- d3.interpolateHcl = d3_interpolateHcl;
- function d3_interpolateHcl(a, b) {
- a = d3.hcl(a);
- b = d3.hcl(b);
- var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al;
- if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac;
- if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
- return function(t) {
- return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + "";
- };
- }
- d3.interpolateHsl = d3_interpolateHsl;
- function d3_interpolateHsl(a, b) {
- a = d3.hsl(a);
- b = d3.hsl(b);
- var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al;
- if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as;
- if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360;
- return function(t) {
- return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + "";
- };
- }
- d3.interpolateLab = d3_interpolateLab;
- function d3_interpolateLab(a, b) {
- a = d3.lab(a);
- b = d3.lab(b);
- var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab;
- return function(t) {
- return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + "";
- };
- }
- d3.interpolateRound = d3_interpolateRound;
- function d3_interpolateRound(a, b) {
- b -= a;
- return function(t) {
- return Math.round(a + b * t);
- };
- }
- function d3_uninterpolateNumber(a, b) {
- b = b - (a = +a) ? 1 / (b - a) : 0;
- return function(x) {
- return (x - a) * b;
- };
- }
- function d3_uninterpolateClamp(a, b) {
- b = b - (a = +a) ? 1 / (b - a) : 0;
- return function(x) {
- return Math.max(0, Math.min(1, (x - a) * b));
- };
- }
- d3.layout = {};
- d3.layout.bundle = function() {
- return function(links) {
- var paths = [], i = -1, n = links.length;
- while (++i < n) paths.push(d3_layout_bundlePath(links[i]));
- return paths;
- };
- };
- function d3_layout_bundlePath(link) {
- var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ];
- while (start !== lca) {
- start = start.parent;
- points.push(start);
- }
- var k = points.length;
- while (end !== lca) {
- points.splice(k, 0, end);
- end = end.parent;
- }
- return points;
- }
- function d3_layout_bundleAncestors(node) {
- var ancestors = [], parent = node.parent;
- while (parent != null) {
- ancestors.push(node);
- node = parent;
- parent = parent.parent;
- }
- ancestors.push(node);
- return ancestors;
- }
- function d3_layout_bundleLeastCommonAncestor(a, b) {
- if (a === b) return a;
- var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null;
- while (aNode === bNode) {
- sharedNode = aNode;
- aNode = aNodes.pop();
- bNode = bNodes.pop();
- }
- return sharedNode;
- }
- d3.layout.chord = function() {
- var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords;
- function relayout() {
- var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j;
- chords = [];
- groups = [];
- k = 0, i = -1;
- while (++i < n) {
- x = 0, j = -1;
- while (++j < n) {
- x += matrix[i][j];
- }
- groupSums.push(x);
- subgroupIndex.push(d3.range(n));
- k += x;
- }
- if (sortGroups) {
- groupIndex.sort(function(a, b) {
- return sortGroups(groupSums[a], groupSums[b]);
- });
- }
- if (sortSubgroups) {
- subgroupIndex.forEach(function(d, i) {
- d.sort(function(a, b) {
- return sortSubgroups(matrix[i][a], matrix[i][b]);
- });
- });
- }
- k = (2 * Ï - padding * n) / k;
- x = 0, i = -1;
- while (++i < n) {
- x0 = x, j = -1;
- while (++j < n) {
- var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k;
- subgroups[di + "-" + dj] = {
- index: di,
- subindex: dj,
- startAngle: a0,
- endAngle: a1,
- value: v
- };
- }
- groups[di] = {
- index: di,
- startAngle: x0,
- endAngle: x,
- value: (x - x0) / k
- };
- x += padding;
- }
- i = -1;
- while (++i < n) {
- j = i - 1;
- while (++j < n) {
- var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i];
- if (source.value || target.value) {
- chords.push(source.value < target.value ? {
- source: target,
- target: source
- } : {
- source: source,
- target: target
- });
- }
- }
- }
- if (sortChords) resort();
- }
- function resort() {
- chords.sort(function(a, b) {
- return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2);
- });
- }
- chord.matrix = function(x) {
- if (!arguments.length) return matrix;
- n = (matrix = x) && matrix.length;
- chords = groups = null;
- return chord;
- };
- chord.padding = function(x) {
- if (!arguments.length) return padding;
- padding = x;
- chords = groups = null;
- return chord;
- };
- chord.sortGroups = function(x) {
- if (!arguments.length) return sortGroups;
- sortGroups = x;
- chords = groups = null;
- return chord;
- };
- chord.sortSubgroups = function(x) {
- if (!arguments.length) return sortSubgroups;
- sortSubgroups = x;
- chords = null;
- return chord;
- };
- chord.sortChords = function(x) {
- if (!arguments.length) return sortChords;
- sortChords = x;
- if (chords) resort();
- return chord;
- };
- chord.chords = function() {
- if (!chords) relayout();
- return chords;
- };
- chord.groups = function() {
- if (!groups) relayout();
- return groups;
- };
- return chord;
- };
- d3.layout.force = function() {
- var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, gravity = .1, theta = .8, nodes = [], links = [], distances, strengths, charges;
- function repulse(node) {
- return function(quad, x1, _, x2) {
- if (quad.point !== node) {
- var dx = quad.cx - node.x, dy = quad.cy - node.y, dn = 1 / Math.sqrt(dx * dx + dy * dy);
- if ((x2 - x1) * dn < theta) {
- var k = quad.charge * dn * dn;
- node.px -= dx * k;
- node.py -= dy * k;
- return true;
- }
- if (quad.point && isFinite(dn)) {
- var k = quad.pointCharge * dn * dn;
- node.px -= dx * k;
- node.py -= dy * k;
- }
- }
- return !quad.charge;
- };
- }
- force.tick = function() {
- if ((alpha *= .99) < .005) {
- event.end({
- type: "end",
- alpha: alpha = 0
- });
- return true;
- }
- var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y;
- for (i = 0; i < m; ++i) {
- o = links[i];
- s = o.source;
- t = o.target;
- x = t.x - s.x;
- y = t.y - s.y;
- if (l = x * x + y * y) {
- l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l;
- x *= l;
- y *= l;
- t.x -= x * (k = s.weight / (t.weight + s.weight));
- t.y -= y * k;
- s.x += x * (k = 1 - k);
- s.y += y * k;
- }
- }
- if (k = alpha * gravity) {
- x = size[0] / 2;
- y = size[1] / 2;
- i = -1;
- if (k) while (++i < n) {
- o = nodes[i];
- o.x += (x - o.x) * k;
- o.y += (y - o.y) * k;
- }
- }
- if (charge) {
- d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges);
- i = -1;
- while (++i < n) {
- if (!(o = nodes[i]).fixed) {
- q.visit(repulse(o));
- }
- }
- }
- i = -1;
- while (++i < n) {
- o = nodes[i];
- if (o.fixed) {
- o.x = o.px;
- o.y = o.py;
- } else {
- o.x -= (o.px - (o.px = o.x)) * friction;
- o.y -= (o.py - (o.py = o.y)) * friction;
- }
- }
- event.tick({
- type: "tick",
- alpha: alpha
- });
- };
- force.nodes = function(x) {
- if (!arguments.length) return nodes;
- nodes = x;
- return force;
- };
- force.links = function(x) {
- if (!arguments.length) return links;
- links = x;
- return force;
- };
- force.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return force;
- };
- force.linkDistance = function(x) {
- if (!arguments.length) return linkDistance;
- linkDistance = typeof x === "function" ? x : +x;
- return force;
- };
- force.distance = force.linkDistance;
- force.linkStrength = function(x) {
- if (!arguments.length) return linkStrength;
- linkStrength = typeof x === "function" ? x : +x;
- return force;
- };
- force.friction = function(x) {
- if (!arguments.length) return friction;
- friction = +x;
- return force;
- };
- force.charge = function(x) {
- if (!arguments.length) return charge;
- charge = typeof x === "function" ? x : +x;
- return force;
- };
- force.gravity = function(x) {
- if (!arguments.length) return gravity;
- gravity = +x;
- return force;
- };
- force.theta = function(x) {
- if (!arguments.length) return theta;
- theta = +x;
- return force;
- };
- force.alpha = function(x) {
- if (!arguments.length) return alpha;
- x = +x;
- if (alpha) {
- if (x > 0) alpha = x; else alpha = 0;
- } else if (x > 0) {
- event.start({
- type: "start",
- alpha: alpha = x
- });
- d3.timer(force.tick);
- }
- return force;
- };
- force.start = function() {
- var i, j, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o;
- for (i = 0; i < n; ++i) {
- (o = nodes[i]).index = i;
- o.weight = 0;
- }
- for (i = 0; i < m; ++i) {
- o = links[i];
- if (typeof o.source == "number") o.source = nodes[o.source];
- if (typeof o.target == "number") o.target = nodes[o.target];
- ++o.source.weight;
- ++o.target.weight;
- }
- for (i = 0; i < n; ++i) {
- o = nodes[i];
- if (isNaN(o.x)) o.x = position("x", w);
- if (isNaN(o.y)) o.y = position("y", h);
- if (isNaN(o.px)) o.px = o.x;
- if (isNaN(o.py)) o.py = o.y;
- }
- distances = [];
- if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance;
- strengths = [];
- if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength;
- charges = [];
- if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge;
- function position(dimension, size) {
- var neighbors = neighbor(i), j = -1, m = neighbors.length, x;
- while (++j < m) if (!isNaN(x = neighbors[j][dimension])) return x;
- return Math.random() * size;
- }
- function neighbor() {
- if (!neighbors) {
- neighbors = [];
- for (j = 0; j < n; ++j) {
- neighbors[j] = [];
- }
- for (j = 0; j < m; ++j) {
- var o = links[j];
- neighbors[o.source.index].push(o.target);
- neighbors[o.target.index].push(o.source);
- }
- }
- return neighbors[i];
- }
- return force.resume();
- };
- force.resume = function() {
- return force.alpha(.1);
- };
- force.stop = function() {
- return force.alpha(0);
- };
- force.drag = function() {
- if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend);
- if (!arguments.length) return drag;
- this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag);
- };
- function dragmove(d) {
- d.px = d3.event.x, d.py = d3.event.y;
- force.resume();
- }
- return d3.rebind(force, event, "on");
- };
- function d3_layout_forceDragstart(d) {
- d.fixed |= 2;
- }
- function d3_layout_forceDragend(d) {
- d.fixed &= ~6;
- }
- function d3_layout_forceMouseover(d) {
- d.fixed |= 4;
- d.px = d.x, d.py = d.y;
- }
- function d3_layout_forceMouseout(d) {
- d.fixed &= ~4;
- }
- function d3_layout_forceAccumulate(quad, alpha, charges) {
- var cx = 0, cy = 0;
- quad.charge = 0;
- if (!quad.leaf) {
- var nodes = quad.nodes, n = nodes.length, i = -1, c;
- while (++i < n) {
- c = nodes[i];
- if (c == null) continue;
- d3_layout_forceAccumulate(c, alpha, charges);
- quad.charge += c.charge;
- cx += c.charge * c.cx;
- cy += c.charge * c.cy;
- }
- }
- if (quad.point) {
- if (!quad.leaf) {
- quad.point.x += Math.random() - .5;
- quad.point.y += Math.random() - .5;
- }
- var k = alpha * charges[quad.point.index];
- quad.charge += quad.pointCharge = k;
- cx += k * quad.point.x;
- cy += k * quad.point.y;
- }
- quad.cx = cx / quad.charge;
- quad.cy = cy / quad.charge;
- }
- var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1;
- d3.layout.hierarchy = function() {
- var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue;
- function recurse(node, depth, nodes) {
- var childs = children.call(hierarchy, node, depth);
- node.depth = depth;
- nodes.push(node);
- if (childs && (n = childs.length)) {
- var i = -1, n, c = node.children = [], v = 0, j = depth + 1, d;
- while (++i < n) {
- d = recurse(childs[i], j, nodes);
- d.parent = node;
- c.push(d);
- v += d.value;
- }
- if (sort) c.sort(sort);
- if (value) node.value = v;
- } else if (value) {
- node.value = +value.call(hierarchy, node, depth) || 0;
- }
- return node;
- }
- function revalue(node, depth) {
- var children = node.children, v = 0;
- if (children && (n = children.length)) {
- var i = -1, n, j = depth + 1;
- while (++i < n) v += revalue(children[i], j);
- } else if (value) {
- v = +value.call(hierarchy, node, depth) || 0;
- }
- if (value) node.value = v;
- return v;
- }
- function hierarchy(d) {
- var nodes = [];
- recurse(d, 0, nodes);
- return nodes;
- }
- hierarchy.sort = function(x) {
- if (!arguments.length) return sort;
- sort = x;
- return hierarchy;
- };
- hierarchy.children = function(x) {
- if (!arguments.length) return children;
- children = x;
- return hierarchy;
- };
- hierarchy.value = function(x) {
- if (!arguments.length) return value;
- value = x;
- return hierarchy;
- };
- hierarchy.revalue = function(root) {
- revalue(root, 0);
- return root;
- };
- return hierarchy;
- };
- function d3_layout_hierarchyRebind(object, hierarchy) {
- d3.rebind(object, hierarchy, "sort", "children", "value");
- object.nodes = object;
- object.links = d3_layout_hierarchyLinks;
- return object;
- }
- function d3_layout_hierarchyChildren(d) {
- return d.children;
- }
- function d3_layout_hierarchyValue(d) {
- return d.value;
- }
- function d3_layout_hierarchySort(a, b) {
- return b.value - a.value;
- }
- function d3_layout_hierarchyLinks(nodes) {
- return d3.merge(nodes.map(function(parent) {
- return (parent.children || []).map(function(child) {
- return {
- source: parent,
- target: child
- };
- });
- }));
- }
- d3.layout.partition = function() {
- var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ];
- function position(node, x, dx, dy) {
- var children = node.children;
- node.x = x;
- node.y = node.depth * dy;
- node.dx = dx;
- node.dy = dy;
- if (children && (n = children.length)) {
- var i = -1, n, c, d;
- dx = node.value ? dx / node.value : 0;
- while (++i < n) {
- position(c = children[i], x, d = c.value * dx, dy);
- x += d;
- }
- }
- }
- function depth(node) {
- var children = node.children, d = 0;
- if (children && (n = children.length)) {
- var i = -1, n;
- while (++i < n) d = Math.max(d, depth(children[i]));
- }
- return 1 + d;
- }
- function partition(d, i) {
- var nodes = hierarchy.call(this, d, i);
- position(nodes[0], 0, size[0], size[1] / depth(nodes[0]));
- return nodes;
- }
- partition.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return partition;
- };
- return d3_layout_hierarchyRebind(partition, hierarchy);
- };
- d3.layout.pie = function() {
- var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = 2 * Ï;
- function pie(data) {
- var values = data.map(function(d, i) {
- return +value.call(pie, d, i);
- });
- var a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle);
- var k = ((typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a) / d3.sum(values);
- var index = d3.range(data.length);
- if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) {
- return values[j] - values[i];
- } : function(i, j) {
- return sort(data[i], data[j]);
- });
- var arcs = [];
- index.forEach(function(i) {
- var d;
- arcs[i] = {
- data: data[i],
- value: d = values[i],
- startAngle: a,
- endAngle: a += d * k
- };
- });
- return arcs;
- }
- pie.value = function(x) {
- if (!arguments.length) return value;
- value = x;
- return pie;
- };
- pie.sort = function(x) {
- if (!arguments.length) return sort;
- sort = x;
- return pie;
- };
- pie.startAngle = function(x) {
- if (!arguments.length) return startAngle;
- startAngle = x;
- return pie;
- };
- pie.endAngle = function(x) {
- if (!arguments.length) return endAngle;
- endAngle = x;
- return pie;
- };
- return pie;
- };
- var d3_layout_pieSortByValue = {};
- d3.layout.stack = function() {
- var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY;
- function stack(data, index) {
- var series = data.map(function(d, i) {
- return values.call(stack, d, i);
- });
- var points = series.map(function(d) {
- return d.map(function(v, i) {
- return [ x.call(stack, v, i), y.call(stack, v, i) ];
- });
- });
- var orders = order.call(stack, points, index);
- series = d3.permute(series, orders);
- points = d3.permute(points, orders);
- var offsets = offset.call(stack, points, index);
- var n = series.length, m = series[0].length, i, j, o;
- for (j = 0; j < m; ++j) {
- out.call(stack, series[0][j], o = offsets[j], points[0][j][1]);
- for (i = 1; i < n; ++i) {
- out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]);
- }
- }
- return data;
- }
- stack.values = function(x) {
- if (!arguments.length) return values;
- values = x;
- return stack;
- };
- stack.order = function(x) {
- if (!arguments.length) return order;
- order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault;
- return stack;
- };
- stack.offset = function(x) {
- if (!arguments.length) return offset;
- offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero;
- return stack;
- };
- stack.x = function(z) {
- if (!arguments.length) return x;
- x = z;
- return stack;
- };
- stack.y = function(z) {
- if (!arguments.length) return y;
- y = z;
- return stack;
- };
- stack.out = function(z) {
- if (!arguments.length) return out;
- out = z;
- return stack;
- };
- return stack;
- };
- function d3_layout_stackX(d) {
- return d.x;
- }
- function d3_layout_stackY(d) {
- return d.y;
- }
- function d3_layout_stackOut(d, y0, y) {
- d.y0 = y0;
- d.y = y;
- }
- var d3_layout_stackOrders = d3.map({
- "inside-out": function(data) {
- var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) {
- return max[a] - max[b];
- }), top = 0, bottom = 0, tops = [], bottoms = [];
- for (i = 0; i < n; ++i) {
- j = index[i];
- if (top < bottom) {
- top += sums[j];
- tops.push(j);
- } else {
- bottom += sums[j];
- bottoms.push(j);
- }
- }
- return bottoms.reverse().concat(tops);
- },
- reverse: function(data) {
- return d3.range(data.length).reverse();
- },
- "default": d3_layout_stackOrderDefault
- });
- var d3_layout_stackOffsets = d3.map({
- silhouette: function(data) {
- var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = [];
- for (j = 0; j < m; ++j) {
- for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
- if (o > max) max = o;
- sums.push(o);
- }
- for (j = 0; j < m; ++j) {
- y0[j] = (max - sums[j]) / 2;
- }
- return y0;
- },
- wiggle: function(data) {
- var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = [];
- y0[0] = o = o0 = 0;
- for (j = 1; j < m; ++j) {
- for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1];
- for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) {
- for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) {
- s3 += (data[k][j][1] - data[k][j - 1][1]) / dx;
- }
- s2 += s3 * data[i][j][1];
- }
- y0[j] = o -= s1 ? s2 / s1 * dx : 0;
- if (o < o0) o0 = o;
- }
- for (j = 0; j < m; ++j) y0[j] -= o0;
- return y0;
- },
- expand: function(data) {
- var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = [];
- for (j = 0; j < m; ++j) {
- for (i = 0, o = 0; i < n; i++) o += data[i][j][1];
- if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k;
- }
- for (j = 0; j < m; ++j) y0[j] = 0;
- return y0;
- },
- zero: d3_layout_stackOffsetZero
- });
- function d3_layout_stackOrderDefault(data) {
- return d3.range(data.length);
- }
- function d3_layout_stackOffsetZero(data) {
- var j = -1, m = data[0].length, y0 = [];
- while (++j < m) y0[j] = 0;
- return y0;
- }
- function d3_layout_stackMaxIndex(array) {
- var i = 1, j = 0, v = array[0][1], k, n = array.length;
- for (;i < n; ++i) {
- if ((k = array[i][1]) > v) {
- j = i;
- v = k;
- }
- }
- return j;
- }
- function d3_layout_stackReduceSum(d) {
- return d.reduce(d3_layout_stackSum, 0);
- }
- function d3_layout_stackSum(p, d) {
- return p + d[1];
- }
- d3.layout.histogram = function() {
- var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges;
- function histogram(data, i) {
- var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x;
- while (++i < m) {
- bin = bins[i] = [];
- bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]);
- bin.y = 0;
- }
- if (m > 0) {
- i = -1;
- while (++i < n) {
- x = values[i];
- if (x >= range[0] && x <= range[1]) {
- bin = bins[d3.bisect(thresholds, x, 1, m) - 1];
- bin.y += k;
- bin.push(data[i]);
- }
- }
- }
- return bins;
- }
- histogram.value = function(x) {
- if (!arguments.length) return valuer;
- valuer = x;
- return histogram;
- };
- histogram.range = function(x) {
- if (!arguments.length) return ranger;
- ranger = d3_functor(x);
- return histogram;
- };
- histogram.bins = function(x) {
- if (!arguments.length) return binner;
- binner = typeof x === "number" ? function(range) {
- return d3_layout_histogramBinFixed(range, x);
- } : d3_functor(x);
- return histogram;
- };
- histogram.frequency = function(x) {
- if (!arguments.length) return frequency;
- frequency = !!x;
- return histogram;
- };
- return histogram;
- };
- function d3_layout_histogramBinSturges(range, values) {
- return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1));
- }
- function d3_layout_histogramBinFixed(range, n) {
- var x = -1, b = +range[0], m = (range[1] - b) / n, f = [];
- while (++x <= n) f[x] = m * x + b;
- return f;
- }
- function d3_layout_histogramRange(values) {
- return [ d3.min(values), d3.max(values) ];
- }
- d3.layout.tree = function() {
- var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
- function tree(d, i) {
- var nodes = hierarchy.call(this, d, i), root = nodes[0];
- function firstWalk(node, previousSibling) {
- var children = node.children, layout = node._tree;
- if (children && (n = children.length)) {
- var n, firstChild = children[0], previousChild, ancestor = firstChild, child, i = -1;
- while (++i < n) {
- child = children[i];
- firstWalk(child, previousChild);
- ancestor = apportion(child, previousChild, ancestor);
- previousChild = child;
- }
- d3_layout_treeShift(node);
- var midpoint = .5 * (firstChild._tree.prelim + child._tree.prelim);
- if (previousSibling) {
- layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
- layout.mod = layout.prelim - midpoint;
- } else {
- layout.prelim = midpoint;
- }
- } else {
- if (previousSibling) {
- layout.prelim = previousSibling._tree.prelim + separation(node, previousSibling);
- }
- }
- }
- function secondWalk(node, x) {
- node.x = node._tree.prelim + x;
- var children = node.children;
- if (children && (n = children.length)) {
- var i = -1, n;
- x += node._tree.mod;
- while (++i < n) {
- secondWalk(children[i], x);
- }
- }
- }
- function apportion(node, previousSibling, ancestor) {
- if (previousSibling) {
- var vip = node, vop = node, vim = previousSibling, vom = node.parent.children[0], sip = vip._tree.mod, sop = vop._tree.mod, sim = vim._tree.mod, som = vom._tree.mod, shift;
- while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) {
- vom = d3_layout_treeLeft(vom);
- vop = d3_layout_treeRight(vop);
- vop._tree.ancestor = node;
- shift = vim._tree.prelim + sim - vip._tree.prelim - sip + separation(vim, vip);
- if (shift > 0) {
- d3_layout_treeMove(d3_layout_treeAncestor(vim, node, ancestor), node, shift);
- sip += shift;
- sop += shift;
- }
- sim += vim._tree.mod;
- sip += vip._tree.mod;
- som += vom._tree.mod;
- sop += vop._tree.mod;
- }
- if (vim && !d3_layout_treeRight(vop)) {
- vop._tree.thread = vim;
- vop._tree.mod += sim - sop;
- }
- if (vip && !d3_layout_treeLeft(vom)) {
- vom._tree.thread = vip;
- vom._tree.mod += sip - som;
- ancestor = node;
- }
- }
- return ancestor;
- }
- d3_layout_treeVisitAfter(root, function(node, previousSibling) {
- node._tree = {
- ancestor: node,
- prelim: 0,
- mod: 0,
- change: 0,
- shift: 0,
- number: previousSibling ? previousSibling._tree.number + 1 : 0
- };
- });
- firstWalk(root);
- secondWalk(root, -root._tree.prelim);
- var left = d3_layout_treeSearch(root, d3_layout_treeLeftmost), right = d3_layout_treeSearch(root, d3_layout_treeRightmost), deep = d3_layout_treeSearch(root, d3_layout_treeDeepest), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2, y1 = deep.depth || 1;
- d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
- node.x *= size[0];
- node.y = node.depth * size[1];
- delete node._tree;
- } : function(node) {
- node.x = (node.x - x0) / (x1 - x0) * size[0];
- node.y = node.depth / y1 * size[1];
- delete node._tree;
- });
- return nodes;
- }
- tree.separation = function(x) {
- if (!arguments.length) return separation;
- separation = x;
- return tree;
- };
- tree.size = function(x) {
- if (!arguments.length) return nodeSize ? null : size;
- nodeSize = (size = x) == null;
- return tree;
- };
- tree.nodeSize = function(x) {
- if (!arguments.length) return nodeSize ? size : null;
- nodeSize = (size = x) != null;
- return tree;
- };
- return d3_layout_hierarchyRebind(tree, hierarchy);
- };
- function d3_layout_treeSeparation(a, b) {
- return a.parent == b.parent ? 1 : 2;
- }
- function d3_layout_treeLeft(node) {
- var children = node.children;
- return children && children.length ? children[0] : node._tree.thread;
- }
- function d3_layout_treeRight(node) {
- var children = node.children, n;
- return children && (n = children.length) ? children[n - 1] : node._tree.thread;
- }
- function d3_layout_treeSearch(node, compare) {
- var children = node.children;
- if (children && (n = children.length)) {
- var child, n, i = -1;
- while (++i < n) {
- if (compare(child = d3_layout_treeSearch(children[i], compare), node) > 0) {
- node = child;
- }
- }
- }
- return node;
- }
- function d3_layout_treeRightmost(a, b) {
- return a.x - b.x;
- }
- function d3_layout_treeLeftmost(a, b) {
- return b.x - a.x;
- }
- function d3_layout_treeDeepest(a, b) {
- return a.depth - b.depth;
- }
- function d3_layout_treeVisitAfter(node, callback) {
- function visit(node, previousSibling) {
- var children = node.children;
- if (children && (n = children.length)) {
- var child, previousChild = null, i = -1, n;
- while (++i < n) {
- child = children[i];
- visit(child, previousChild);
- previousChild = child;
- }
- }
- callback(node, previousSibling);
- }
- visit(node, null);
- }
- function d3_layout_treeShift(node) {
- var shift = 0, change = 0, children = node.children, i = children.length, child;
- while (--i >= 0) {
- child = children[i]._tree;
- child.prelim += shift;
- child.mod += shift;
- shift += child.shift + (change += child.change);
- }
- }
- function d3_layout_treeMove(ancestor, node, shift) {
- ancestor = ancestor._tree;
- node = node._tree;
- var change = shift / (node.number - ancestor.number);
- ancestor.change += change;
- node.change -= change;
- node.shift += shift;
- node.prelim += shift;
- node.mod += shift;
- }
- function d3_layout_treeAncestor(vim, node, ancestor) {
- return vim._tree.ancestor.parent == node.parent ? vim._tree.ancestor : ancestor;
- }
- d3.layout.pack = function() {
- var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius;
- function pack(d, i) {
- var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius || Math.sqrt;
- root.x = root.y = 0;
- d3_layout_treeVisitAfter(root, function(d) {
- d.r = r(d.value);
- });
- d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
- if (padding) {
- var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2;
- d3_layout_treeVisitAfter(root, function(d) {
- d.r += dr;
- });
- d3_layout_treeVisitAfter(root, d3_layout_packSiblings);
- d3_layout_treeVisitAfter(root, function(d) {
- d.r -= dr;
- });
- }
- d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h));
- return nodes;
- }
- pack.size = function(_) {
- if (!arguments.length) return size;
- size = _;
- return pack;
- };
- pack.radius = function(_) {
- if (!arguments.length) return radius;
- radius = _;
- return pack;
- };
- pack.padding = function(_) {
- if (!arguments.length) return padding;
- padding = +_;
- return pack;
- };
- return d3_layout_hierarchyRebind(pack, hierarchy);
- };
- function d3_layout_packSort(a, b) {
- return a.value - b.value;
- }
- function d3_layout_packInsert(a, b) {
- var c = a._pack_next;
- a._pack_next = b;
- b._pack_prev = a;
- b._pack_next = c;
- c._pack_prev = b;
- }
- function d3_layout_packSplice(a, b) {
- a._pack_next = b;
- b._pack_prev = a;
- }
- function d3_layout_packIntersects(a, b) {
- var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r;
- return .999 * dr * dr > dx * dx + dy * dy;
- }
- function d3_layout_packSiblings(node) {
- if (!(nodes = node.children) || !(n = nodes.length)) return;
- var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n;
- function bound(node) {
- xMin = Math.min(node.x - node.r, xMin);
- xMax = Math.max(node.x + node.r, xMax);
- yMin = Math.min(node.y - node.r, yMin);
- yMax = Math.max(node.y + node.r, yMax);
- }
- nodes.forEach(d3_layout_packLink);
- a = nodes[0];
- a.x = -a.r;
- a.y = 0;
- bound(a);
- if (n > 1) {
- b = nodes[1];
- b.x = b.r;
- b.y = 0;
- bound(b);
- if (n > 2) {
- c = nodes[2];
- d3_layout_packPlace(a, b, c);
- bound(c);
- d3_layout_packInsert(a, c);
- a._pack_prev = c;
- d3_layout_packInsert(c, b);
- b = a._pack_next;
- for (i = 3; i < n; i++) {
- d3_layout_packPlace(a, b, c = nodes[i]);
- var isect = 0, s1 = 1, s2 = 1;
- for (j = b._pack_next; j !== b; j = j._pack_next, s1++) {
- if (d3_layout_packIntersects(j, c)) {
- isect = 1;
- break;
- }
- }
- if (isect == 1) {
- for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) {
- if (d3_layout_packIntersects(k, c)) {
- break;
- }
- }
- }
- if (isect) {
- if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b);
- i--;
- } else {
- d3_layout_packInsert(a, c);
- b = c;
- bound(c);
- }
- }
- }
- }
- var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0;
- for (i = 0; i < n; i++) {
- c = nodes[i];
- c.x -= cx;
- c.y -= cy;
- cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y));
- }
- node.r = cr;
- nodes.forEach(d3_layout_packUnlink);
- }
- function d3_layout_packLink(node) {
- node._pack_next = node._pack_prev = node;
- }
- function d3_layout_packUnlink(node) {
- delete node._pack_next;
- delete node._pack_prev;
- }
- function d3_layout_packTransform(node, x, y, k) {
- var children = node.children;
- node.x = x += k * node.x;
- node.y = y += k * node.y;
- node.r *= k;
- if (children) {
- var i = -1, n = children.length;
- while (++i < n) d3_layout_packTransform(children[i], x, y, k);
- }
- }
- function d3_layout_packPlace(a, b, c) {
- var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y;
- if (db && (dx || dy)) {
- var da = b.r + c.r, dc = dx * dx + dy * dy;
- da *= da;
- db *= db;
- var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc);
- c.x = a.x + x * dx + y * dy;
- c.y = a.y + x * dy - y * dx;
- } else {
- c.x = a.x + db;
- c.y = a.y;
- }
- }
- d3.layout.cluster = function() {
- var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false;
- function cluster(d, i) {
- var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0;
- d3_layout_treeVisitAfter(root, function(node) {
- var children = node.children;
- if (children && children.length) {
- node.x = d3_layout_clusterX(children);
- node.y = d3_layout_clusterY(children);
- } else {
- node.x = previousNode ? x += separation(node, previousNode) : 0;
- node.y = 0;
- previousNode = node;
- }
- });
- var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2;
- d3_layout_treeVisitAfter(root, nodeSize ? function(node) {
- node.x = (node.x - root.x) * size[0];
- node.y = (root.y - node.y) * size[1];
- } : function(node) {
- node.x = (node.x - x0) / (x1 - x0) * size[0];
- node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1];
- });
- return nodes;
- }
- cluster.separation = function(x) {
- if (!arguments.length) return separation;
- separation = x;
- return cluster;
- };
- cluster.size = function(x) {
- if (!arguments.length) return nodeSize ? null : size;
- nodeSize = (size = x) == null;
- return cluster;
- };
- cluster.nodeSize = function(x) {
- if (!arguments.length) return nodeSize ? size : null;
- nodeSize = (size = x) != null;
- return cluster;
- };
- return d3_layout_hierarchyRebind(cluster, hierarchy);
- };
- function d3_layout_clusterY(children) {
- return 1 + d3.max(children, function(child) {
- return child.y;
- });
- }
- function d3_layout_clusterX(children) {
- return children.reduce(function(x, child) {
- return x + child.x;
- }, 0) / children.length;
- }
- function d3_layout_clusterLeft(node) {
- var children = node.children;
- return children && children.length ? d3_layout_clusterLeft(children[0]) : node;
- }
- function d3_layout_clusterRight(node) {
- var children = node.children, n;
- return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node;
- }
- d3.layout.treemap = function() {
- var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5));
- function scale(children, k) {
- var i = -1, n = children.length, child, area;
- while (++i < n) {
- area = (child = children[i]).value * (k < 0 ? 0 : k);
- child.area = isNaN(area) || area <= 0 ? 0 : area;
- }
- }
- function squarify(node) {
- var children = node.children;
- if (children && children.length) {
- var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n;
- scale(remaining, rect.dx * rect.dy / node.value);
- row.area = 0;
- while ((n = remaining.length) > 0) {
- row.push(child = remaining[n - 1]);
- row.area += child.area;
- if (mode !== "squarify" || (score = worst(row, u)) <= best) {
- remaining.pop();
- best = score;
- } else {
- row.area -= row.pop().area;
- position(row, u, rect, false);
- u = Math.min(rect.dx, rect.dy);
- row.length = row.area = 0;
- best = Infinity;
- }
- }
- if (row.length) {
- position(row, u, rect, true);
- row.length = row.area = 0;
- }
- children.forEach(squarify);
- }
- }
- function stickify(node) {
- var children = node.children;
- if (children && children.length) {
- var rect = pad(node), remaining = children.slice(), child, row = [];
- scale(remaining, rect.dx * rect.dy / node.value);
- row.area = 0;
- while (child = remaining.pop()) {
- row.push(child);
- row.area += child.area;
- if (child.z != null) {
- position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length);
- row.length = row.area = 0;
- }
- }
- children.forEach(stickify);
- }
- }
- function worst(row, u) {
- var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length;
- while (++i < n) {
- if (!(r = row[i].area)) continue;
- if (r < rmin) rmin = r;
- if (r > rmax) rmax = r;
- }
- s *= s;
- u *= u;
- return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity;
- }
- function position(row, u, rect, flush) {
- var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o;
- if (u == rect.dx) {
- if (flush || v > rect.dy) v = rect.dy;
- while (++i < n) {
- o = row[i];
- o.x = x;
- o.y = y;
- o.dy = v;
- x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0);
- }
- o.z = true;
- o.dx += rect.x + rect.dx - x;
- rect.y += v;
- rect.dy -= v;
- } else {
- if (flush || v > rect.dx) v = rect.dx;
- while (++i < n) {
- o = row[i];
- o.x = x;
- o.y = y;
- o.dx = v;
- y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0);
- }
- o.z = false;
- o.dy += rect.y + rect.dy - y;
- rect.x += v;
- rect.dx -= v;
- }
- }
- function treemap(d) {
- var nodes = stickies || hierarchy(d), root = nodes[0];
- root.x = 0;
- root.y = 0;
- root.dx = size[0];
- root.dy = size[1];
- if (stickies) hierarchy.revalue(root);
- scale([ root ], root.dx * root.dy / root.value);
- (stickies ? stickify : squarify)(root);
- if (sticky) stickies = nodes;
- return nodes;
- }
- treemap.size = function(x) {
- if (!arguments.length) return size;
- size = x;
- return treemap;
- };
- treemap.padding = function(x) {
- if (!arguments.length) return padding;
- function padFunction(node) {
- var p = x.call(treemap, node, node.depth);
- return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p);
- }
- function padConstant(node) {
- return d3_layout_treemapPad(node, x);
- }
- var type;
- pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ],
- padConstant) : padConstant;
- return treemap;
- };
- treemap.round = function(x) {
- if (!arguments.length) return round != Number;
- round = x ? Math.round : Number;
- return treemap;
- };
- treemap.sticky = function(x) {
- if (!arguments.length) return sticky;
- sticky = x;
- stickies = null;
- return treemap;
- };
- treemap.ratio = function(x) {
- if (!arguments.length) return ratio;
- ratio = x;
- return treemap;
- };
- treemap.mode = function(x) {
- if (!arguments.length) return mode;
- mode = x + "";
- return treemap;
- };
- return d3_layout_hierarchyRebind(treemap, hierarchy);
- };
- function d3_layout_treemapPadNull(node) {
- return {
- x: node.x,
- y: node.y,
- dx: node.dx,
- dy: node.dy
- };
- }
- function d3_layout_treemapPad(node, padding) {
- var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2];
- if (dx < 0) {
- x += dx / 2;
- dx = 0;
- }
- if (dy < 0) {
- y += dy / 2;
- dy = 0;
- }
- return {
- x: x,
- y: y,
- dx: dx,
- dy: dy
- };
- }
- d3.random = {
- normal: function(µ, Ï) {
- var n = arguments.length;
- if (n < 2) Ï = 1;
- if (n < 1) µ = 0;
- return function() {
- var x, y, r;
- do {
- x = Math.random() * 2 - 1;
- y = Math.random() * 2 - 1;
- r = x * x + y * y;
- } while (!r || r > 1);
- return µ + Ï * x * Math.sqrt(-2 * Math.log(r) / r);
- };
- },
- logNormal: function() {
- var random = d3.random.normal.apply(d3, arguments);
- return function() {
- return Math.exp(random());
- };
- },
- irwinHall: function(m) {
- return function() {
- for (var s = 0, j = 0; j < m; j++) s += Math.random();
- return s / m;
- };
- }
- };
- d3.scale = {};
- function d3_scaleExtent(domain) {
- var start = domain[0], stop = domain[domain.length - 1];
- return start < stop ? [ start, stop ] : [ stop, start ];
- }
- function d3_scaleRange(scale) {
- return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range());
- }
- function d3_scale_bilinear(domain, range, uninterpolate, interpolate) {
- var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]);
- return function(x) {
- return i(u(x));
- };
- }
- function d3_scale_nice(domain, nice) {
- var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx;
- if (x1 < x0) {
- dx = i0, i0 = i1, i1 = dx;
- dx = x0, x0 = x1, x1 = dx;
- }
- domain[i0] = nice.floor(x0);
- domain[i1] = nice.ceil(x1);
- return domain;
- }
- function d3_scale_niceStep(step) {
- return step ? {
- floor: function(x) {
- return Math.floor(x / step) * step;
- },
- ceil: function(x) {
- return Math.ceil(x / step) * step;
- }
- } : d3_scale_niceIdentity;
- }
- var d3_scale_niceIdentity = {
- floor: d3_identity,
- ceil: d3_identity
- };
- function d3_scale_polylinear(domain, range, uninterpolate, interpolate) {
- var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1;
- if (domain[k] < domain[0]) {
- domain = domain.slice().reverse();
- range = range.slice().reverse();
- }
- while (++j <= k) {
- u.push(uninterpolate(domain[j - 1], domain[j]));
- i.push(interpolate(range[j - 1], range[j]));
- }
- return function(x) {
- var j = d3.bisect(domain, x, 1, k) - 1;
- return i[j](u[j](x));
- };
- }
- d3.scale.linear = function() {
- return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false);
- };
- function d3_scale_linear(domain, range, interpolate, clamp) {
- var output, input;
- function rescale() {
- var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber;
- output = linear(domain, range, uninterpolate, interpolate);
- input = linear(range, domain, uninterpolate, d3_interpolate);
- return scale;
- }
- function scale(x) {
- return output(x);
- }
- scale.invert = function(y) {
- return input(y);
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = x.map(Number);
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.rangeRound = function(x) {
- return scale.range(x).interpolate(d3_interpolateRound);
- };
- scale.clamp = function(x) {
- if (!arguments.length) return clamp;
- clamp = x;
- return rescale();
- };
- scale.interpolate = function(x) {
- if (!arguments.length) return interpolate;
- interpolate = x;
- return rescale();
- };
- scale.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- scale.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- scale.nice = function(m) {
- d3_scale_linearNice(domain, m);
- return rescale();
- };
- scale.copy = function() {
- return d3_scale_linear(domain, range, interpolate, clamp);
- };
- return rescale();
- }
- function d3_scale_linearRebind(scale, linear) {
- return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp");
- }
- function d3_scale_linearNice(domain, m) {
- return d3_scale_nice(domain, d3_scale_niceStep(m ? d3_scale_linearTickRange(domain, m)[2] : d3_scale_linearNiceStep(domain)));
- }
- function d3_scale_linearNiceStep(domain) {
- var extent = d3_scaleExtent(domain), span = extent[1] - extent[0];
- return Math.pow(10, Math.round(Math.log(span) / Math.LN10) - 1);
- }
- function d3_scale_linearTickRange(domain, m) {
- var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step;
- if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2;
- extent[0] = Math.ceil(extent[0] / step) * step;
- extent[1] = Math.floor(extent[1] / step) * step + step * .5;
- extent[2] = step;
- return extent;
- }
- function d3_scale_linearTicks(domain, m) {
- return d3.range.apply(d3, d3_scale_linearTickRange(domain, m));
- }
- function d3_scale_linearTickFormat(domain, m, format) {
- var precision = -Math.floor(Math.log(d3_scale_linearTickRange(domain, m)[2]) / Math.LN10 + .01);
- return d3.format(format ? format.replace(d3_format_re, function(a, b, c, d, e, f, g, h, i, j) {
- return [ b, c, d, e, f, g, h, i || "." + (precision - (j === "%") * 2), j ].join("");
- }) : ",." + precision + "f");
- }
- d3.scale.log = function() {
- return d3_scale_log(d3.scale.linear().domain([ 0, Math.LN10 ]), 10, d3_scale_logp, d3_scale_powp, [ 1, 10 ]);
- };
- function d3_scale_log(linear, base, log, pow, domain) {
- function scale(x) {
- return linear(log(x));
- }
- scale.invert = function(x) {
- return pow(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- if (x[0] < 0) log = d3_scale_logn, pow = d3_scale_pown; else log = d3_scale_logp,
- pow = d3_scale_powp;
- linear.domain((domain = x.map(Number)).map(log));
- return scale;
- };
- scale.base = function(_) {
- if (!arguments.length) return base;
- base = +_;
- return scale;
- };
- scale.nice = function() {
- function floor(x) {
- return Math.pow(base, Math.floor(Math.log(x) / Math.log(base)));
- }
- function ceil(x) {
- return Math.pow(base, Math.ceil(Math.log(x) / Math.log(base)));
- }
- linear.domain(d3_scale_nice(domain, log === d3_scale_logp ? {
- floor: floor,
- ceil: ceil
- } : {
- floor: function(x) {
- return -ceil(-x);
- },
- ceil: function(x) {
- return -floor(-x);
- }
- }).map(log));
- return scale;
- };
- scale.ticks = function() {
- var extent = d3_scaleExtent(linear.domain()), ticks = [];
- if (extent.every(isFinite)) {
- var b = Math.log(base), i = Math.floor(extent[0] / b), j = Math.ceil(extent[1] / b), u = pow(extent[0]), v = pow(extent[1]), n = base % 1 ? 2 : base;
- if (log === d3_scale_logn) {
- ticks.push(-Math.pow(base, -i));
- for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(-Math.pow(base, -i) * k);
- } else {
- for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(Math.pow(base, i) * k);
- ticks.push(Math.pow(base, i));
- }
- for (i = 0; ticks[i] < u; i++) {}
- for (j = ticks.length; ticks[j - 1] > v; j--) {}
- ticks = ticks.slice(i, j);
- }
- return ticks;
- };
- scale.tickFormat = function(n, format) {
- if (!arguments.length) return d3_scale_logFormat;
- if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format);
- var b = Math.log(base), k = Math.max(.1, n / scale.ticks().length), f = log === d3_scale_logn ? (e = -1e-12,
- Math.floor) : (e = 1e-12, Math.ceil), e;
- return function(d) {
- return d / pow(b * f(log(d) / b + e)) <= k ? format(d) : "";
- };
- };
- scale.copy = function() {
- return d3_scale_log(linear.copy(), base, log, pow, domain);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- var d3_scale_logFormat = d3.format(".0e");
- function d3_scale_logp(x) {
- return Math.log(x < 0 ? 0 : x);
- }
- function d3_scale_powp(x) {
- return Math.exp(x);
- }
- function d3_scale_logn(x) {
- return -Math.log(x > 0 ? 0 : -x);
- }
- function d3_scale_pown(x) {
- return -Math.exp(-x);
- }
- d3.scale.pow = function() {
- return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]);
- };
- function d3_scale_pow(linear, exponent, domain) {
- var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent);
- function scale(x) {
- return linear(powp(x));
- }
- scale.invert = function(x) {
- return powb(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- linear.domain((domain = x.map(Number)).map(powp));
- return scale;
- };
- scale.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- scale.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- scale.nice = function(m) {
- return scale.domain(d3_scale_linearNice(domain, m));
- };
- scale.exponent = function(x) {
- if (!arguments.length) return exponent;
- powp = d3_scale_powPow(exponent = x);
- powb = d3_scale_powPow(1 / exponent);
- linear.domain(domain.map(powp));
- return scale;
- };
- scale.copy = function() {
- return d3_scale_pow(linear.copy(), exponent, domain);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- function d3_scale_powPow(e) {
- return function(x) {
- return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e);
- };
- }
- d3.scale.sqrt = function() {
- return d3.scale.pow().exponent(.5);
- };
- d3.scale.ordinal = function() {
- return d3_scale_ordinal([], {
- t: "range",
- a: [ [] ]
- });
- };
- function d3_scale_ordinal(domain, ranger) {
- var index, range, rangeBand;
- function scale(x) {
- return range[((index.get(x) || index.set(x, domain.push(x))) - 1) % range.length];
- }
- function steps(start, step) {
- return d3.range(domain.length).map(function(i) {
- return start + step * i;
- });
- }
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = [];
- index = new d3_Map();
- var i = -1, n = x.length, xi;
- while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi));
- return scale[ranger.t].apply(scale, ranger.a);
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- rangeBand = 0;
- ranger = {
- t: "range",
- a: arguments
- };
- return scale;
- };
- scale.rangePoints = function(x, padding) {
- if (arguments.length < 2) padding = 0;
- var start = x[0], stop = x[1], step = (stop - start) / (Math.max(1, domain.length - 1) + padding);
- range = steps(domain.length < 2 ? (start + stop) / 2 : start + step * padding / 2, step);
- rangeBand = 0;
- ranger = {
- t: "rangePoints",
- a: arguments
- };
- return scale;
- };
- scale.rangeBands = function(x, padding, outerPadding) {
- if (arguments.length < 2) padding = 0;
- if (arguments.length < 3) outerPadding = padding;
- var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding);
- range = steps(start + step * outerPadding, step);
- if (reverse) range.reverse();
- rangeBand = step * (1 - padding);
- ranger = {
- t: "rangeBands",
- a: arguments
- };
- return scale;
- };
- scale.rangeRoundBands = function(x, padding, outerPadding) {
- if (arguments.length < 2) padding = 0;
- if (arguments.length < 3) outerPadding = padding;
- var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)), error = stop - start - (domain.length - padding) * step;
- range = steps(start + Math.round(error / 2), step);
- if (reverse) range.reverse();
- rangeBand = Math.round(step * (1 - padding));
- ranger = {
- t: "rangeRoundBands",
- a: arguments
- };
- return scale;
- };
- scale.rangeBand = function() {
- return rangeBand;
- };
- scale.rangeExtent = function() {
- return d3_scaleExtent(ranger.a[0]);
- };
- scale.copy = function() {
- return d3_scale_ordinal(domain, ranger);
- };
- return scale.domain(domain);
- }
- d3.scale.category10 = function() {
- return d3.scale.ordinal().range(d3_category10);
- };
- d3.scale.category20 = function() {
- return d3.scale.ordinal().range(d3_category20);
- };
- d3.scale.category20b = function() {
- return d3.scale.ordinal().range(d3_category20b);
- };
- d3.scale.category20c = function() {
- return d3.scale.ordinal().range(d3_category20c);
- };
- var d3_category10 = [ "#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf" ];
- var d3_category20 = [ "#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5", "#8c564b", "#c49c94", "#e377c2", "#f7b6d2", "#7f7f7f", "#c7c7c7", "#bcbd22", "#dbdb8d", "#17becf", "#9edae5" ];
- var d3_category20b = [ "#393b79", "#5254a3", "#6b6ecf", "#9c9ede", "#637939", "#8ca252", "#b5cf6b", "#cedb9c", "#8c6d31", "#bd9e39", "#e7ba52", "#e7cb94", "#843c39", "#ad494a", "#d6616b", "#e7969c", "#7b4173", "#a55194", "#ce6dbd", "#de9ed6" ];
- var d3_category20c = [ "#3182bd", "#6baed6", "#9ecae1", "#c6dbef", "#e6550d", "#fd8d3c", "#fdae6b", "#fdd0a2", "#31a354", "#74c476", "#a1d99b", "#c7e9c0", "#756bb1", "#9e9ac8", "#bcbddc", "#dadaeb", "#636363", "#969696", "#bdbdbd", "#d9d9d9" ];
- d3.scale.quantile = function() {
- return d3_scale_quantile([], []);
- };
- function d3_scale_quantile(domain, range) {
- var thresholds;
- function rescale() {
- var k = 0, q = range.length;
- thresholds = [];
- while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q);
- return scale;
- }
- function scale(x) {
- if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)];
- }
- scale.domain = function(x) {
- if (!arguments.length) return domain;
- domain = x.filter(function(d) {
- return !isNaN(d);
- }).sort(d3.ascending);
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.quantiles = function() {
- return thresholds;
- };
- scale.copy = function() {
- return d3_scale_quantile(domain, range);
- };
- return rescale();
- }
- d3.scale.quantize = function() {
- return d3_scale_quantize(0, 1, [ 0, 1 ]);
- };
- function d3_scale_quantize(x0, x1, range) {
- var kx, i;
- function scale(x) {
- return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))];
- }
- function rescale() {
- kx = range.length / (x1 - x0);
- i = range.length - 1;
- return scale;
- }
- scale.domain = function(x) {
- if (!arguments.length) return [ x0, x1 ];
- x0 = +x[0];
- x1 = +x[x.length - 1];
- return rescale();
- };
- scale.range = function(x) {
- if (!arguments.length) return range;
- range = x;
- return rescale();
- };
- scale.copy = function() {
- return d3_scale_quantize(x0, x1, range);
- };
- scale.invertExtent = function(y) {
- y = range.indexOf(y);
- y = y < 0 ? NaN : y / kx + x0;
- return [ y, y + 1 / kx ];
- };
- return rescale();
- }
- d3.scale.threshold = function() {
- return d3_scale_threshold([ .5 ], [ 0, 1 ]);
- };
- function d3_scale_threshold(domain, range) {
- function scale(x) {
- if (x <= x) return range[d3.bisect(domain, x)];
- }
- scale.domain = function(_) {
- if (!arguments.length) return domain;
- domain = _;
- return scale;
- };
- scale.range = function(_) {
- if (!arguments.length) return range;
- range = _;
- return scale;
- };
- scale.invertExtent = function(y) {
- y = range.indexOf(y);
- return [ domain[y - 1], domain[y] ];
- };
- scale.copy = function() {
- return d3_scale_threshold(domain, range);
- };
- return scale;
- }
- d3.scale.identity = function() {
- return d3_scale_identity([ 0, 1 ]);
- };
- function d3_scale_identity(domain) {
- function identity(x) {
- return +x;
- }
- identity.invert = identity;
- identity.domain = identity.range = function(x) {
- if (!arguments.length) return domain;
- domain = x.map(identity);
- return identity;
- };
- identity.ticks = function(m) {
- return d3_scale_linearTicks(domain, m);
- };
- identity.tickFormat = function(m, format) {
- return d3_scale_linearTickFormat(domain, m, format);
- };
- identity.copy = function() {
- return d3_scale_identity(domain);
- };
- return identity;
- }
- d3.svg.arc = function() {
- var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
- function arc() {
- var r0 = innerRadius.apply(this, arguments), r1 = outerRadius.apply(this, arguments), a0 = startAngle.apply(this, arguments) + d3_svg_arcOffset, a1 = endAngle.apply(this, arguments) + d3_svg_arcOffset, da = (a1 < a0 && (da = a0,
- a0 = a1, a1 = da), a1 - a0), df = da < Ï ? "0" : "1", c0 = Math.cos(a0), s0 = Math.sin(a0), c1 = Math.cos(a1), s1 = Math.sin(a1);
- return da >= d3_svg_arcMax ? r0 ? "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "M0," + r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + -r0 + "A" + r0 + "," + r0 + " 0 1,0 0," + r0 + "Z" : "M0," + r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + -r1 + "A" + r1 + "," + r1 + " 0 1,1 0," + r1 + "Z" : r0 ? "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L" + r0 * c1 + "," + r0 * s1 + "A" + r0 + "," + r0 + " 0 " + df + ",0 " + r0 * c0 + "," + r0 * s0 + "Z" : "M" + r1 * c0 + "," + r1 * s0 + "A" + r1 + "," + r1 + " 0 " + df + ",1 " + r1 * c1 + "," + r1 * s1 + "L0,0" + "Z";
- }
- arc.innerRadius = function(v) {
- if (!arguments.length) return innerRadius;
- innerRadius = d3_functor(v);
- return arc;
- };
- arc.outerRadius = function(v) {
- if (!arguments.length) return outerRadius;
- outerRadius = d3_functor(v);
- return arc;
- };
- arc.startAngle = function(v) {
- if (!arguments.length) return startAngle;
- startAngle = d3_functor(v);
- return arc;
- };
- arc.endAngle = function(v) {
- if (!arguments.length) return endAngle;
- endAngle = d3_functor(v);
- return arc;
- };
- arc.centroid = function() {
- var r = (innerRadius.apply(this, arguments) + outerRadius.apply(this, arguments)) / 2, a = (startAngle.apply(this, arguments) + endAngle.apply(this, arguments)) / 2 + d3_svg_arcOffset;
- return [ Math.cos(a) * r, Math.sin(a) * r ];
- };
- return arc;
- };
- var d3_svg_arcOffset = -Ï / 2, d3_svg_arcMax = 2 * Ï - 1e-6;
- function d3_svg_arcInnerRadius(d) {
- return d.innerRadius;
- }
- function d3_svg_arcOuterRadius(d) {
- return d.outerRadius;
- }
- function d3_svg_arcStartAngle(d) {
- return d.startAngle;
- }
- function d3_svg_arcEndAngle(d) {
- return d.endAngle;
- }
- d3.svg.line.radial = function() {
- var line = d3_svg_line(d3_svg_lineRadial);
- line.radius = line.x, delete line.x;
- line.angle = line.y, delete line.y;
- return line;
- };
- function d3_svg_lineRadial(points) {
- var point, i = -1, n = points.length, r, a;
- while (++i < n) {
- point = points[i];
- r = point[0];
- a = point[1] + d3_svg_arcOffset;
- point[0] = r * Math.cos(a);
- point[1] = r * Math.sin(a);
- }
- return points;
- }
- function d3_svg_area(projection) {
- var x0 = d3_svg_lineX, x1 = d3_svg_lineX, y0 = 0, y1 = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7;
- function area(data) {
- var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() {
- return x;
- } : d3_functor(x1), fy1 = y0 === y1 ? function() {
- return y;
- } : d3_functor(y1), x, y;
- function segment() {
- segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z");
- }
- while (++i < n) {
- if (defined.call(this, d = data[i], i)) {
- points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]);
- points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]);
- } else if (points0.length) {
- segment();
- points0 = [];
- points1 = [];
- }
- }
- if (points0.length) segment();
- return segments.length ? segments.join("") : null;
- }
- area.x = function(_) {
- if (!arguments.length) return x1;
- x0 = x1 = _;
- return area;
- };
- area.x0 = function(_) {
- if (!arguments.length) return x0;
- x0 = _;
- return area;
- };
- area.x1 = function(_) {
- if (!arguments.length) return x1;
- x1 = _;
- return area;
- };
- area.y = function(_) {
- if (!arguments.length) return y1;
- y0 = y1 = _;
- return area;
- };
- area.y0 = function(_) {
- if (!arguments.length) return y0;
- y0 = _;
- return area;
- };
- area.y1 = function(_) {
- if (!arguments.length) return y1;
- y1 = _;
- return area;
- };
- area.defined = function(_) {
- if (!arguments.length) return defined;
- defined = _;
- return area;
- };
- area.interpolate = function(_) {
- if (!arguments.length) return interpolateKey;
- if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
- interpolateReverse = interpolate.reverse || interpolate;
- L = interpolate.closed ? "M" : "L";
- return area;
- };
- area.tension = function(_) {
- if (!arguments.length) return tension;
- tension = _;
- return area;
- };
- return area;
- }
- d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter;
- d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore;
- d3.svg.area = function() {
- return d3_svg_area(d3_identity);
- };
- d3.svg.area.radial = function() {
- var area = d3_svg_area(d3_svg_lineRadial);
- area.radius = area.x, delete area.x;
- area.innerRadius = area.x0, delete area.x0;
- area.outerRadius = area.x1, delete area.x1;
- area.angle = area.y, delete area.y;
- area.startAngle = area.y0, delete area.y0;
- area.endAngle = area.y1, delete area.y1;
- return area;
- };
- d3.svg.chord = function() {
- var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle;
- function chord(d, i) {
- var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i);
- return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z";
- }
- function subgroup(self, f, d, i) {
- var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) + d3_svg_arcOffset, a1 = endAngle.call(self, subgroup, i) + d3_svg_arcOffset;
- return {
- r: r,
- a0: a0,
- a1: a1,
- p0: [ r * Math.cos(a0), r * Math.sin(a0) ],
- p1: [ r * Math.cos(a1), r * Math.sin(a1) ]
- };
- }
- function equals(a, b) {
- return a.a0 == b.a0 && a.a1 == b.a1;
- }
- function arc(r, p, a) {
- return "A" + r + "," + r + " 0 " + +(a > Ï) + ",1 " + p;
- }
- function curve(r0, p0, r1, p1) {
- return "Q 0,0 " + p1;
- }
- chord.radius = function(v) {
- if (!arguments.length) return radius;
- radius = d3_functor(v);
- return chord;
- };
- chord.source = function(v) {
- if (!arguments.length) return source;
- source = d3_functor(v);
- return chord;
- };
- chord.target = function(v) {
- if (!arguments.length) return target;
- target = d3_functor(v);
- return chord;
- };
- chord.startAngle = function(v) {
- if (!arguments.length) return startAngle;
- startAngle = d3_functor(v);
- return chord;
- };
- chord.endAngle = function(v) {
- if (!arguments.length) return endAngle;
- endAngle = d3_functor(v);
- return chord;
- };
- return chord;
- };
- function d3_svg_chordRadius(d) {
- return d.radius;
- }
- d3.svg.diagonal = function() {
- var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection;
- function diagonal(d, i) {
- var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, {
- x: p0.x,
- y: m
- }, {
- x: p3.x,
- y: m
- }, p3 ];
- p = p.map(projection);
- return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3];
- }
- diagonal.source = function(x) {
- if (!arguments.length) return source;
- source = d3_functor(x);
- return diagonal;
- };
- diagonal.target = function(x) {
- if (!arguments.length) return target;
- target = d3_functor(x);
- return diagonal;
- };
- diagonal.projection = function(x) {
- if (!arguments.length) return projection;
- projection = x;
- return diagonal;
- };
- return diagonal;
- };
- function d3_svg_diagonalProjection(d) {
- return [ d.x, d.y ];
- }
- d3.svg.diagonal.radial = function() {
- var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection;
- diagonal.projection = function(x) {
- return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection;
- };
- return diagonal;
- };
- function d3_svg_diagonalRadialProjection(projection) {
- return function() {
- var d = projection.apply(this, arguments), r = d[0], a = d[1] + d3_svg_arcOffset;
- return [ r * Math.cos(a), r * Math.sin(a) ];
- };
- }
- d3.svg.symbol = function() {
- var type = d3_svg_symbolType, size = d3_svg_symbolSize;
- function symbol(d, i) {
- return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i));
- }
- symbol.type = function(x) {
- if (!arguments.length) return type;
- type = d3_functor(x);
- return symbol;
- };
- symbol.size = function(x) {
- if (!arguments.length) return size;
- size = d3_functor(x);
- return symbol;
- };
- return symbol;
- };
- function d3_svg_symbolSize() {
- return 64;
- }
- function d3_svg_symbolType() {
- return "circle";
- }
- function d3_svg_symbolCircle(size) {
- var r = Math.sqrt(size / Ï);
- return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z";
- }
- var d3_svg_symbols = d3.map({
- circle: d3_svg_symbolCircle,
- cross: function(size) {
- var r = Math.sqrt(size / 5) / 2;
- return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z";
- },
- diamond: function(size) {
- var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30;
- return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z";
- },
- square: function(size) {
- var r = Math.sqrt(size) / 2;
- return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z";
- },
- "triangle-down": function(size) {
- var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
- return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z";
- },
- "triangle-up": function(size) {
- var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2;
- return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z";
- }
- });
- d3.svg.symbolTypes = d3_svg_symbols.keys();
- var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians);
- function d3_transition(groups, id) {
- d3_arraySubclass(groups, d3_transitionPrototype);
- groups.id = id;
- return groups;
- }
- var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit = {
- ease: d3_ease_cubicInOut,
- delay: 0,
- duration: 250
- };
- d3_transitionPrototype.call = d3_selectionPrototype.call;
- d3_transitionPrototype.empty = d3_selectionPrototype.empty;
- d3_transitionPrototype.node = d3_selectionPrototype.node;
- d3_transitionPrototype.size = d3_selectionPrototype.size;
- d3.transition = function(selection) {
- return arguments.length ? d3_transitionInheritId ? selection.transition() : selection : d3_selectionRoot.transition();
- };
- d3.transition.prototype = d3_transitionPrototype;
- d3_transitionPrototype.select = function(selector) {
- var id = this.id, subgroups = [], subgroup, subnode, node;
- if (typeof selector !== "function") selector = d3_selection_selector(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i))) {
- if ("__data__" in node) subnode.__data__ = node.__data__;
- d3_transitionNode(subnode, i, id, node.__transition__[id]);
- subgroup.push(subnode);
- } else {
- subgroup.push(null);
- }
- }
- }
- return d3_transition(subgroups, id);
- };
- d3_transitionPrototype.selectAll = function(selector) {
- var id = this.id, subgroups = [], subgroup, subnodes, node, subnode, transition;
- if (typeof selector !== "function") selector = d3_selection_selectorAll(selector);
- for (var j = -1, m = this.length; ++j < m; ) {
- for (var group = this[j], i = -1, n = group.length; ++i < n; ) {
- if (node = group[i]) {
- transition = node.__transition__[id];
- subnodes = selector.call(node, node.__data__, i);
- subgroups.push(subgroup = []);
- for (var k = -1, o = subnodes.length; ++k < o; ) {
- if (subnode = subnodes[k]) d3_transitionNode(subnode, k, id, transition);
- subgroup.push(subnode);
- }
- }
- }
- }
- return d3_transition(subgroups, id);
- };
- d3_transitionPrototype.filter = function(filter) {
- var subgroups = [], subgroup, group, node;
- if (typeof filter !== "function") filter = d3_selection_filter(filter);
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- if ((node = group[i]) && filter.call(node, node.__data__, i)) {
- subgroup.push(node);
- }
- }
- }
- return d3_transition(subgroups, this.id, this.time).ease(this.ease());
- };
- d3_transitionPrototype.tween = function(name, tween) {
- var id = this.id;
- if (arguments.length < 2) return this.node().__transition__[id].tween.get(name);
- return d3_selection_each(this, tween == null ? function(node) {
- node.__transition__[id].tween.remove(name);
- } : function(node) {
- node.__transition__[id].tween.set(name, tween);
- });
- };
- function d3_transition_tween(groups, name, value, tween) {
- var id = groups.id;
- return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) {
- node.__transition__[id].tween.set(name, tween(value.call(node, node.__data__, i, j)));
- } : (value = tween(value), function(node) {
- node.__transition__[id].tween.set(name, value);
- }));
- }
- d3_transitionPrototype.attr = function(nameNS, value) {
- if (arguments.length < 2) {
- for (value in nameNS) this.attr(value, nameNS[value]);
- return this;
- }
- var interpolate = d3_interpolateByName(nameNS), name = d3.ns.qualify(nameNS);
- function attrNull() {
- this.removeAttribute(name);
- }
- function attrNullNS() {
- this.removeAttributeNS(name.space, name.local);
- }
- function attrTween(b) {
- return b == null ? attrNull : (b += "", function() {
- var a = this.getAttribute(name), i;
- return a !== b && (i = interpolate(a, b), function(t) {
- this.setAttribute(name, i(t));
- });
- });
- }
- function attrTweenNS(b) {
- return b == null ? attrNullNS : (b += "", function() {
- var a = this.getAttributeNS(name.space, name.local), i;
- return a !== b && (i = interpolate(a, b), function(t) {
- this.setAttributeNS(name.space, name.local, i(t));
- });
- });
- }
- return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween);
- };
- d3_transitionPrototype.attrTween = function(nameNS, tween) {
- var name = d3.ns.qualify(nameNS);
- function attrTween(d, i) {
- var f = tween.call(this, d, i, this.getAttribute(name));
- return f && function(t) {
- this.setAttribute(name, f(t));
- };
- }
- function attrTweenNS(d, i) {
- var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local));
- return f && function(t) {
- this.setAttributeNS(name.space, name.local, f(t));
- };
- }
- return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween);
- };
- d3_transitionPrototype.style = function(name, value, priority) {
- var n = arguments.length;
- if (n < 3) {
- if (typeof name !== "string") {
- if (n < 2) value = "";
- for (priority in name) this.style(priority, name[priority], value);
- return this;
- }
- priority = "";
- }
- var interpolate = d3_interpolateByName(name);
- function styleNull() {
- this.style.removeProperty(name);
- }
- function styleString(b) {
- return b == null ? styleNull : (b += "", function() {
- var a = d3_window.getComputedStyle(this, null).getPropertyValue(name), i;
- return a !== b && (i = interpolate(a, b), function(t) {
- this.style.setProperty(name, i(t), priority);
- });
- });
- }
- return d3_transition_tween(this, "style." + name, value, styleString);
- };
- d3_transitionPrototype.styleTween = function(name, tween, priority) {
- if (arguments.length < 3) priority = "";
- function styleTween(d, i) {
- var f = tween.call(this, d, i, d3_window.getComputedStyle(this, null).getPropertyValue(name));
- return f && function(t) {
- this.style.setProperty(name, f(t), priority);
- };
- }
- return this.tween("style." + name, styleTween);
- };
- d3_transitionPrototype.text = function(value) {
- return d3_transition_tween(this, "text", value, d3_transition_text);
- };
- function d3_transition_text(b) {
- if (b == null) b = "";
- return function() {
- this.textContent = b;
- };
- }
- d3_transitionPrototype.remove = function() {
- return this.each("end.transition", function() {
- var p;
- if (!this.__transition__ && (p = this.parentNode)) p.removeChild(this);
- });
- };
- d3_transitionPrototype.ease = function(value) {
- var id = this.id;
- if (arguments.length < 1) return this.node().__transition__[id].ease;
- if (typeof value !== "function") value = d3.ease.apply(d3, arguments);
- return d3_selection_each(this, function(node) {
- node.__transition__[id].ease = value;
- });
- };
- d3_transitionPrototype.delay = function(value) {
- var id = this.id;
- return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
- node.__transition__[id].delay = value.call(node, node.__data__, i, j) | 0;
- } : (value |= 0, function(node) {
- node.__transition__[id].delay = value;
- }));
- };
- d3_transitionPrototype.duration = function(value) {
- var id = this.id;
- return d3_selection_each(this, typeof value === "function" ? function(node, i, j) {
- node.__transition__[id].duration = Math.max(1, value.call(node, node.__data__, i, j) | 0);
- } : (value = Math.max(1, value | 0), function(node) {
- node.__transition__[id].duration = value;
- }));
- };
- d3_transitionPrototype.each = function(type, listener) {
- var id = this.id;
- if (arguments.length < 2) {
- var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId;
- d3_transitionInheritId = id;
- d3_selection_each(this, function(node, i, j) {
- d3_transitionInherit = node.__transition__[id];
- type.call(node, node.__data__, i, j);
- });
- d3_transitionInherit = inherit;
- d3_transitionInheritId = inheritId;
- } else {
- d3_selection_each(this, function(node) {
- node.__transition__[id].event.on(type, listener);
- });
- }
- return this;
- };
- d3_transitionPrototype.transition = function() {
- var id0 = this.id, id1 = ++d3_transitionId, subgroups = [], subgroup, group, node, transition;
- for (var j = 0, m = this.length; j < m; j++) {
- subgroups.push(subgroup = []);
- for (var group = this[j], i = 0, n = group.length; i < n; i++) {
- if (node = group[i]) {
- transition = Object.create(node.__transition__[id0]);
- transition.delay += transition.duration;
- d3_transitionNode(node, i, id1, transition);
- }
- subgroup.push(node);
- }
- }
- return d3_transition(subgroups, id1);
- };
- function d3_transitionNode(node, i, id, inherit) {
- var lock = node.__transition__ || (node.__transition__ = {
- active: 0,
- count: 0
- }), transition = lock[id];
- if (!transition) {
- var time = inherit.time;
- transition = lock[id] = {
- tween: new d3_Map(),
- event: d3.dispatch("start", "end"),
- time: time,
- ease: inherit.ease,
- delay: inherit.delay,
- duration: inherit.duration
- };
- ++lock.count;
- d3.timer(function(elapsed) {
- var d = node.__data__, ease = transition.ease, event = transition.event, delay = transition.delay, duration = transition.duration, tweened = [];
- return delay <= elapsed ? start(elapsed) : d3.timer(start, delay, time), 1;
- function start(elapsed) {
- if (lock.active > id) return stop();
- lock.active = id;
- event.start.call(node, d, i);
- transition.tween.forEach(function(key, value) {
- if (value = value.call(node, d, i)) {
- tweened.push(value);
- }
- });
- if (!tick(elapsed)) d3.timer(tick, 0, time);
- return 1;
- }
- function tick(elapsed) {
- if (lock.active !== id) return stop();
- var t = (elapsed - delay) / duration, e = ease(t), n = tweened.length;
- while (n > 0) {
- tweened[--n].call(node, e);
- }
- if (t >= 1) {
- stop();
- event.end.call(node, d, i);
- return 1;
- }
- }
- function stop() {
- if (--lock.count) delete lock[id]; else delete node.__transition__;
- return 1;
- }
- }, 0, time);
- return transition;
- }
- }
- d3.svg.axis = function() {
- var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, tickMajorSize = 6, tickMinorSize = 6, tickEndSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_, tickSubdivide = 0;
- function axis(g) {
- g.each(function() {
- var g = d3.select(this);
- var ticks = tickValues == null ? scale.ticks ? scale.ticks.apply(scale, tickArguments_) : scale.domain() : tickValues, tickFormat = tickFormat_ == null ? scale.tickFormat ? scale.tickFormat.apply(scale, tickArguments_) : String : tickFormat_;
- var subticks = d3_svg_axisSubdivide(scale, ticks, tickSubdivide), subtick = g.selectAll(".tick.minor").data(subticks, String), subtickEnter = subtick.enter().insert("line", ".tick").attr("class", "tick minor").style("opacity", 1e-6), subtickExit = d3.transition(subtick.exit()).style("opacity", 1e-6).remove(), subtickUpdate = d3.transition(subtick).style("opacity", 1);
- var tick = g.selectAll(".tick.major").data(ticks, String), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick major").style("opacity", 1e-6), tickExit = d3.transition(tick.exit()).style("opacity", 1e-6).remove(), tickUpdate = d3.transition(tick).style("opacity", 1), tickTransform;
- var range = d3_scaleRange(scale), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"),
- d3.transition(path));
- var scale1 = scale.copy(), scale0 = this.__chart__ || scale1;
- this.__chart__ = scale1;
- tickEnter.append("line");
- tickEnter.append("text");
- var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text");
- switch (orient) {
- case "bottom":
- {
- tickTransform = d3_svg_axisX;
- subtickEnter.attr("y2", tickMinorSize);
- subtickUpdate.attr("x2", 0).attr("y2", tickMinorSize);
- lineEnter.attr("y2", tickMajorSize);
- textEnter.attr("y", Math.max(tickMajorSize, 0) + tickPadding);
- lineUpdate.attr("x2", 0).attr("y2", tickMajorSize);
- textUpdate.attr("x", 0).attr("y", Math.max(tickMajorSize, 0) + tickPadding);
- text.attr("dy", ".71em").style("text-anchor", "middle");
- pathUpdate.attr("d", "M" + range[0] + "," + tickEndSize + "V0H" + range[1] + "V" + tickEndSize);
- break;
- }
-
- case "top":
- {
- tickTransform = d3_svg_axisX;
- subtickEnter.attr("y2", -tickMinorSize);
- subtickUpdate.attr("x2", 0).attr("y2", -tickMinorSize);
- lineEnter.attr("y2", -tickMajorSize);
- textEnter.attr("y", -(Math.max(tickMajorSize, 0) + tickPadding));
- lineUpdate.attr("x2", 0).attr("y2", -tickMajorSize);
- textUpdate.attr("x", 0).attr("y", -(Math.max(tickMajorSize, 0) + tickPadding));
- text.attr("dy", "0em").style("text-anchor", "middle");
- pathUpdate.attr("d", "M" + range[0] + "," + -tickEndSize + "V0H" + range[1] + "V" + -tickEndSize);
- break;
- }
-
- case "left":
- {
- tickTransform = d3_svg_axisY;
- subtickEnter.attr("x2", -tickMinorSize);
- subtickUpdate.attr("x2", -tickMinorSize).attr("y2", 0);
- lineEnter.attr("x2", -tickMajorSize);
- textEnter.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding));
- lineUpdate.attr("x2", -tickMajorSize).attr("y2", 0);
- textUpdate.attr("x", -(Math.max(tickMajorSize, 0) + tickPadding)).attr("y", 0);
- text.attr("dy", ".32em").style("text-anchor", "end");
- pathUpdate.attr("d", "M" + -tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + -tickEndSize);
- break;
- }
-
- case "right":
- {
- tickTransform = d3_svg_axisY;
- subtickEnter.attr("x2", tickMinorSize);
- subtickUpdate.attr("x2", tickMinorSize).attr("y2", 0);
- lineEnter.attr("x2", tickMajorSize);
- textEnter.attr("x", Math.max(tickMajorSize, 0) + tickPadding);
- lineUpdate.attr("x2", tickMajorSize).attr("y2", 0);
- textUpdate.attr("x", Math.max(tickMajorSize, 0) + tickPadding).attr("y", 0);
- text.attr("dy", ".32em").style("text-anchor", "start");
- pathUpdate.attr("d", "M" + tickEndSize + "," + range[0] + "H0V" + range[1] + "H" + tickEndSize);
- break;
- }
- }
- if (scale.ticks) {
- tickEnter.call(tickTransform, scale0);
- tickUpdate.call(tickTransform, scale1);
- tickExit.call(tickTransform, scale1);
- subtickEnter.call(tickTransform, scale0);
- subtickUpdate.call(tickTransform, scale1);
- subtickExit.call(tickTransform, scale1);
- } else {
- var dx = scale1.rangeBand() / 2, x = function(d) {
- return scale1(d) + dx;
- };
- tickEnter.call(tickTransform, x);
- tickUpdate.call(tickTransform, x);
- }
- });
- }
- axis.scale = function(x) {
- if (!arguments.length) return scale;
- scale = x;
- return axis;
- };
- axis.orient = function(x) {
- if (!arguments.length) return orient;
- orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient;
- return axis;
- };
- axis.ticks = function() {
- if (!arguments.length) return tickArguments_;
- tickArguments_ = arguments;
- return axis;
- };
- axis.tickValues = function(x) {
- if (!arguments.length) return tickValues;
- tickValues = x;
- return axis;
- };
- axis.tickFormat = function(x) {
- if (!arguments.length) return tickFormat_;
- tickFormat_ = x;
- return axis;
- };
- axis.tickSize = function(x, y) {
- if (!arguments.length) return tickMajorSize;
- var n = arguments.length - 1;
- tickMajorSize = +x;
- tickMinorSize = n > 1 ? +y : tickMajorSize;
- tickEndSize = n > 0 ? +arguments[n] : tickMajorSize;
- return axis;
- };
- axis.tickPadding = function(x) {
- if (!arguments.length) return tickPadding;
- tickPadding = +x;
- return axis;
- };
- axis.tickSubdivide = function(x) {
- if (!arguments.length) return tickSubdivide;
- tickSubdivide = +x;
- return axis;
- };
- return axis;
- };
- var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = {
- top: 1,
- right: 1,
- bottom: 1,
- left: 1
- };
- function d3_svg_axisX(selection, x) {
- selection.attr("transform", function(d) {
- return "translate(" + x(d) + ",0)";
- });
- }
- function d3_svg_axisY(selection, y) {
- selection.attr("transform", function(d) {
- return "translate(0," + y(d) + ")";
- });
- }
- function d3_svg_axisSubdivide(scale, ticks, m) {
- subticks = [];
- if (m && ticks.length > 1) {
- var extent = d3_scaleExtent(scale.domain()), subticks, i = -1, n = ticks.length, d = (ticks[1] - ticks[0]) / ++m, j, v;
- while (++i < n) {
- for (j = m; --j > 0; ) {
- if ((v = +ticks[i] - j * d) >= extent[0]) {
- subticks.push(v);
- }
- }
- }
- for (--i, j = 0; ++j < m && (v = +ticks[i] + j * d) < extent[1]; ) {
- subticks.push(v);
- }
- }
- return subticks;
- }
- d3.svg.brush = function() {
- var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, resizes = d3_svg_brushResizes[0], extent = [ [ 0, 0 ], [ 0, 0 ] ], clamp = [ true, true ], extentDomain;
- function brush(g) {
- g.each(function() {
- var g = d3.select(this), bg = g.selectAll(".background").data([ 0 ]), fg = g.selectAll(".extent").data([ 0 ]), tz = g.selectAll(".resize").data(resizes, String), e;
- g.style("pointer-events", "all").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart);
- bg.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair");
- fg.enter().append("rect").attr("class", "extent").style("cursor", "move");
- tz.enter().append("g").attr("class", function(d) {
- return "resize " + d;
- }).style("cursor", function(d) {
- return d3_svg_brushCursor[d];
- }).append("rect").attr("x", function(d) {
- return /[ew]$/.test(d) ? -3 : null;
- }).attr("y", function(d) {
- return /^[ns]/.test(d) ? -3 : null;
- }).attr("width", 6).attr("height", 6).style("visibility", "hidden");
- tz.style("display", brush.empty() ? "none" : null);
- tz.exit().remove();
- if (x) {
- e = d3_scaleRange(x);
- bg.attr("x", e[0]).attr("width", e[1] - e[0]);
- redrawX(g);
- }
- if (y) {
- e = d3_scaleRange(y);
- bg.attr("y", e[0]).attr("height", e[1] - e[0]);
- redrawY(g);
- }
- redraw(g);
- });
- }
- function redraw(g) {
- g.selectAll(".resize").attr("transform", function(d) {
- return "translate(" + extent[+/e$/.test(d)][0] + "," + extent[+/^s/.test(d)][1] + ")";
- });
- }
- function redrawX(g) {
- g.select(".extent").attr("x", extent[0][0]);
- g.selectAll(".extent,.n>rect,.s>rect").attr("width", extent[1][0] - extent[0][0]);
- }
- function redrawY(g) {
- g.select(".extent").attr("y", extent[0][1]);
- g.selectAll(".extent,.e>rect,.w>rect").attr("height", extent[1][1] - extent[0][1]);
- }
- function brushstart() {
- var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress("brush"), center, origin = mouse(), offset;
- var w = d3.select(d3_window).on("keydown.brush", keydown).on("keyup.brush", keyup);
- if (d3.event.changedTouches) {
- w.on("touchmove.brush", brushmove).on("touchend.brush", brushend);
- } else {
- w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend);
- }
- if (dragging) {
- origin[0] = extent[0][0] - origin[0];
- origin[1] = extent[0][1] - origin[1];
- } else if (resizing) {
- var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing);
- offset = [ extent[1 - ex][0] - origin[0], extent[1 - ey][1] - origin[1] ];
- origin[0] = extent[ex][0];
- origin[1] = extent[ey][1];
- } else if (d3.event.altKey) center = origin.slice();
- g.style("pointer-events", "none").selectAll(".resize").style("display", null);
- d3.select("body").style("cursor", eventTarget.style("cursor"));
- event_({
- type: "brushstart"
- });
- brushmove();
- function mouse() {
- var touches = d3.event.changedTouches;
- return touches ? d3.touches(target, touches)[0] : d3.mouse(target);
- }
- function keydown() {
- if (d3.event.keyCode == 32) {
- if (!dragging) {
- center = null;
- origin[0] -= extent[1][0];
- origin[1] -= extent[1][1];
- dragging = 2;
- }
- d3_eventPreventDefault();
- }
- }
- function keyup() {
- if (d3.event.keyCode == 32 && dragging == 2) {
- origin[0] += extent[1][0];
- origin[1] += extent[1][1];
- dragging = 0;
- d3_eventPreventDefault();
- }
- }
- function brushmove() {
- var point = mouse(), moved = false;
- if (offset) {
- point[0] += offset[0];
- point[1] += offset[1];
- }
- if (!dragging) {
- if (d3.event.altKey) {
- if (!center) center = [ (extent[0][0] + extent[1][0]) / 2, (extent[0][1] + extent[1][1]) / 2 ];
- origin[0] = extent[+(point[0] < center[0])][0];
- origin[1] = extent[+(point[1] < center[1])][1];
- } else center = null;
- }
- if (resizingX && move1(point, x, 0)) {
- redrawX(g);
- moved = true;
- }
- if (resizingY && move1(point, y, 1)) {
- redrawY(g);
- moved = true;
- }
- if (moved) {
- redraw(g);
- event_({
- type: "brush",
- mode: dragging ? "move" : "resize"
- });
- }
- }
- function move1(point, scale, i) {
- var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], size = extent[1][i] - extent[0][i], min, max;
- if (dragging) {
- r0 -= position;
- r1 -= size + position;
- }
- min = clamp[i] ? Math.max(r0, Math.min(r1, point[i])) : point[i];
- if (dragging) {
- max = (min += position) + size;
- } else {
- if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min));
- if (position < min) {
- max = min;
- min = position;
- } else {
- max = position;
- }
- }
- if (extent[0][i] !== min || extent[1][i] !== max) {
- extentDomain = null;
- extent[0][i] = min;
- extent[1][i] = max;
- return true;
- }
- }
- function brushend() {
- brushmove();
- g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null);
- d3.select("body").style("cursor", null);
- w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null);
- dragRestore();
- event_({
- type: "brushend"
- });
- }
- }
- brush.x = function(z) {
- if (!arguments.length) return x;
- x = z;
- resizes = d3_svg_brushResizes[!x << 1 | !y];
- return brush;
- };
- brush.y = function(z) {
- if (!arguments.length) return y;
- y = z;
- resizes = d3_svg_brushResizes[!x << 1 | !y];
- return brush;
- };
- brush.clamp = function(z) {
- if (!arguments.length) return x && y ? clamp : x || y ? clamp[+!x] : null;
- if (x && y) clamp = [ !!z[0], !!z[1] ]; else if (x || y) clamp[+!x] = !!z;
- return brush;
- };
- brush.extent = function(z) {
- var x0, x1, y0, y1, t;
- if (!arguments.length) {
- z = extentDomain || extent;
- if (x) {
- x0 = z[0][0], x1 = z[1][0];
- if (!extentDomain) {
- x0 = extent[0][0], x1 = extent[1][0];
- if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1);
- if (x1 < x0) t = x0, x0 = x1, x1 = t;
- }
- }
- if (y) {
- y0 = z[0][1], y1 = z[1][1];
- if (!extentDomain) {
- y0 = extent[0][1], y1 = extent[1][1];
- if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1);
- if (y1 < y0) t = y0, y0 = y1, y1 = t;
- }
- }
- return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ];
- }
- extentDomain = [ [ 0, 0 ], [ 0, 0 ] ];
- if (x) {
- x0 = z[0], x1 = z[1];
- if (y) x0 = x0[0], x1 = x1[0];
- extentDomain[0][0] = x0, extentDomain[1][0] = x1;
- if (x.invert) x0 = x(x0), x1 = x(x1);
- if (x1 < x0) t = x0, x0 = x1, x1 = t;
- extent[0][0] = x0 | 0, extent[1][0] = x1 | 0;
- }
- if (y) {
- y0 = z[0], y1 = z[1];
- if (x) y0 = y0[1], y1 = y1[1];
- extentDomain[0][1] = y0, extentDomain[1][1] = y1;
- if (y.invert) y0 = y(y0), y1 = y(y1);
- if (y1 < y0) t = y0, y0 = y1, y1 = t;
- extent[0][1] = y0 | 0, extent[1][1] = y1 | 0;
- }
- return brush;
- };
- brush.clear = function() {
- extentDomain = null;
- extent[0][0] = extent[0][1] = extent[1][0] = extent[1][1] = 0;
- return brush;
- };
- brush.empty = function() {
- return x && extent[0][0] === extent[1][0] || y && extent[0][1] === extent[1][1];
- };
- return d3.rebind(brush, event, "on");
- };
- var d3_svg_brushCursor = {
- n: "ns-resize",
- e: "ew-resize",
- s: "ns-resize",
- w: "ew-resize",
- nw: "nwse-resize",
- ne: "nesw-resize",
- se: "nwse-resize",
- sw: "nesw-resize"
- };
- var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ];
- d3.time = {};
- var d3_time = Date, d3_time_daySymbols = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ];
- function d3_time_utc() {
- this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]);
- }
- d3_time_utc.prototype = {
- getDate: function() {
- return this._.getUTCDate();
- },
- getDay: function() {
- return this._.getUTCDay();
- },
- getFullYear: function() {
- return this._.getUTCFullYear();
- },
- getHours: function() {
- return this._.getUTCHours();
- },
- getMilliseconds: function() {
- return this._.getUTCMilliseconds();
- },
- getMinutes: function() {
- return this._.getUTCMinutes();
- },
- getMonth: function() {
- return this._.getUTCMonth();
- },
- getSeconds: function() {
- return this._.getUTCSeconds();
- },
- getTime: function() {
- return this._.getTime();
- },
- getTimezoneOffset: function() {
- return 0;
- },
- valueOf: function() {
- return this._.valueOf();
- },
- setDate: function() {
- d3_time_prototype.setUTCDate.apply(this._, arguments);
- },
- setDay: function() {
- d3_time_prototype.setUTCDay.apply(this._, arguments);
- },
- setFullYear: function() {
- d3_time_prototype.setUTCFullYear.apply(this._, arguments);
- },
- setHours: function() {
- d3_time_prototype.setUTCHours.apply(this._, arguments);
- },
- setMilliseconds: function() {
- d3_time_prototype.setUTCMilliseconds.apply(this._, arguments);
- },
- setMinutes: function() {
- d3_time_prototype.setUTCMinutes.apply(this._, arguments);
- },
- setMonth: function() {
- d3_time_prototype.setUTCMonth.apply(this._, arguments);
- },
- setSeconds: function() {
- d3_time_prototype.setUTCSeconds.apply(this._, arguments);
- },
- setTime: function() {
- d3_time_prototype.setTime.apply(this._, arguments);
- }
- };
- var d3_time_prototype = Date.prototype;
- var d3_time_formatDateTime = "%a %b %e %X %Y", d3_time_formatDate = "%m/%d/%Y", d3_time_formatTime = "%H:%M:%S";
- var d3_time_days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], d3_time_dayAbbreviations = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], d3_time_months = [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], d3_time_monthAbbreviations = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
- function d3_time_interval(local, step, number) {
- function round(date) {
- var d0 = local(date), d1 = offset(d0, 1);
- return date - d0 < d1 - date ? d0 : d1;
- }
- function ceil(date) {
- step(date = local(new d3_time(date - 1)), 1);
- return date;
- }
- function offset(date, k) {
- step(date = new d3_time(+date), k);
- return date;
- }
- function range(t0, t1, dt) {
- var time = ceil(t0), times = [];
- if (dt > 1) {
- while (time < t1) {
- if (!(number(time) % dt)) times.push(new Date(+time));
- step(time, 1);
- }
- } else {
- while (time < t1) times.push(new Date(+time)), step(time, 1);
- }
- return times;
- }
- function range_utc(t0, t1, dt) {
- try {
- d3_time = d3_time_utc;
- var utc = new d3_time_utc();
- utc._ = t0;
- return range(utc, t1, dt);
- } finally {
- d3_time = Date;
- }
- }
- local.floor = local;
- local.round = round;
- local.ceil = ceil;
- local.offset = offset;
- local.range = range;
- var utc = local.utc = d3_time_interval_utc(local);
- utc.floor = utc;
- utc.round = d3_time_interval_utc(round);
- utc.ceil = d3_time_interval_utc(ceil);
- utc.offset = d3_time_interval_utc(offset);
- utc.range = range_utc;
- return local;
- }
- function d3_time_interval_utc(method) {
- return function(date, k) {
- try {
- d3_time = d3_time_utc;
- var utc = new d3_time_utc();
- utc._ = date;
- return method(utc, k)._;
- } finally {
- d3_time = Date;
- }
- };
- }
- d3.time.year = d3_time_interval(function(date) {
- date = d3.time.day(date);
- date.setMonth(0, 1);
- return date;
- }, function(date, offset) {
- date.setFullYear(date.getFullYear() + offset);
- }, function(date) {
- return date.getFullYear();
- });
- d3.time.years = d3.time.year.range;
- d3.time.years.utc = d3.time.year.utc.range;
- d3.time.day = d3_time_interval(function(date) {
- var day = new d3_time(2e3, 0);
- day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
- return day;
- }, function(date, offset) {
- date.setDate(date.getDate() + offset);
- }, function(date) {
- return date.getDate() - 1;
- });
- d3.time.days = d3.time.day.range;
- d3.time.days.utc = d3.time.day.utc.range;
- d3.time.dayOfYear = function(date) {
- var year = d3.time.year(date);
- return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5);
- };
- d3_time_daySymbols.forEach(function(day, i) {
- day = day.toLowerCase();
- i = 7 - i;
- var interval = d3.time[day] = d3_time_interval(function(date) {
- (date = d3.time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7);
- return date;
- }, function(date, offset) {
- date.setDate(date.getDate() + Math.floor(offset) * 7);
- }, function(date) {
- var day = d3.time.year(date).getDay();
- return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i);
- });
- d3.time[day + "s"] = interval.range;
- d3.time[day + "s"].utc = interval.utc.range;
- d3.time[day + "OfYear"] = function(date) {
- var day = d3.time.year(date).getDay();
- return Math.floor((d3.time.dayOfYear(date) + (day + i) % 7) / 7);
- };
- });
- d3.time.week = d3.time.sunday;
- d3.time.weeks = d3.time.sunday.range;
- d3.time.weeks.utc = d3.time.sunday.utc.range;
- d3.time.weekOfYear = d3.time.sundayOfYear;
- d3.time.format = function(template) {
- var n = template.length;
- function format(date) {
- var string = [], i = -1, j = 0, c, p, f;
- while (++i < n) {
- if (template.charCodeAt(i) === 37) {
- string.push(template.substring(j, i));
- if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i);
- if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p);
- string.push(c);
- j = i + 1;
- }
- }
- string.push(template.substring(j, i));
- return string.join("");
- }
- format.parse = function(string) {
- var d = {
- y: 1900,
- m: 0,
- d: 1,
- H: 0,
- M: 0,
- S: 0,
- L: 0
- }, i = d3_time_parse(d, template, string, 0);
- if (i != string.length) return null;
- if ("p" in d) d.H = d.H % 12 + d.p * 12;
- var date = new d3_time();
- if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) {
- date.setFullYear(d.y, 0, 1);
- date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7);
- } else date.setFullYear(d.y, d.m, d.d);
- date.setHours(d.H, d.M, d.S, d.L);
- return date;
- };
- format.toString = function() {
- return template;
- };
- return format;
- };
- function d3_time_parse(date, template, string, j) {
- var c, p, i = 0, n = template.length, m = string.length;
- while (i < n) {
- if (j >= m) return -1;
- c = template.charCodeAt(i++);
- if (c === 37) {
- p = d3_time_parsers[template.charAt(i++)];
- if (!p || (j = p(date, string, j)) < 0) return -1;
- } else if (c != string.charCodeAt(j++)) {
- return -1;
- }
- }
- return j;
- }
- function d3_time_formatRe(names) {
- return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i");
- }
- function d3_time_formatLookup(names) {
- var map = new d3_Map(), i = -1, n = names.length;
- while (++i < n) map.set(names[i].toLowerCase(), i);
- return map;
- }
- function d3_time_formatPad(value, fill, width) {
- var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length;
- return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string);
- }
- var d3_time_dayRe = d3_time_formatRe(d3_time_days), d3_time_dayLookup = d3_time_formatLookup(d3_time_days), d3_time_dayAbbrevRe = d3_time_formatRe(d3_time_dayAbbreviations), d3_time_dayAbbrevLookup = d3_time_formatLookup(d3_time_dayAbbreviations), d3_time_monthRe = d3_time_formatRe(d3_time_months), d3_time_monthLookup = d3_time_formatLookup(d3_time_months), d3_time_monthAbbrevRe = d3_time_formatRe(d3_time_monthAbbreviations), d3_time_monthAbbrevLookup = d3_time_formatLookup(d3_time_monthAbbreviations), d3_time_percentRe = /^%/;
- var d3_time_formatPads = {
- "-": "",
- _: " ",
- "0": "0"
- };
- var d3_time_formats = {
- a: function(d) {
- return d3_time_dayAbbreviations[d.getDay()];
- },
- A: function(d) {
- return d3_time_days[d.getDay()];
- },
- b: function(d) {
- return d3_time_monthAbbreviations[d.getMonth()];
- },
- B: function(d) {
- return d3_time_months[d.getMonth()];
- },
- c: d3.time.format(d3_time_formatDateTime),
- d: function(d, p) {
- return d3_time_formatPad(d.getDate(), p, 2);
- },
- e: function(d, p) {
- return d3_time_formatPad(d.getDate(), p, 2);
- },
- H: function(d, p) {
- return d3_time_formatPad(d.getHours(), p, 2);
- },
- I: function(d, p) {
- return d3_time_formatPad(d.getHours() % 12 || 12, p, 2);
- },
- j: function(d, p) {
- return d3_time_formatPad(1 + d3.time.dayOfYear(d), p, 3);
- },
- L: function(d, p) {
- return d3_time_formatPad(d.getMilliseconds(), p, 3);
- },
- m: function(d, p) {
- return d3_time_formatPad(d.getMonth() + 1, p, 2);
- },
- M: function(d, p) {
- return d3_time_formatPad(d.getMinutes(), p, 2);
- },
- p: function(d) {
- return d.getHours() >= 12 ? "PM" : "AM";
- },
- S: function(d, p) {
- return d3_time_formatPad(d.getSeconds(), p, 2);
- },
- U: function(d, p) {
- return d3_time_formatPad(d3.time.sundayOfYear(d), p, 2);
- },
- w: function(d) {
- return d.getDay();
- },
- W: function(d, p) {
- return d3_time_formatPad(d3.time.mondayOfYear(d), p, 2);
- },
- x: d3.time.format(d3_time_formatDate),
- X: d3.time.format(d3_time_formatTime),
- y: function(d, p) {
- return d3_time_formatPad(d.getFullYear() % 100, p, 2);
- },
- Y: function(d, p) {
- return d3_time_formatPad(d.getFullYear() % 1e4, p, 4);
- },
- Z: d3_time_zone,
- "%": function() {
- return "%";
- }
- };
- var d3_time_parsers = {
- a: d3_time_parseWeekdayAbbrev,
- A: d3_time_parseWeekday,
- b: d3_time_parseMonthAbbrev,
- B: d3_time_parseMonth,
- c: d3_time_parseLocaleFull,
- d: d3_time_parseDay,
- e: d3_time_parseDay,
- H: d3_time_parseHour24,
- I: d3_time_parseHour24,
- j: d3_time_parseDayOfYear,
- L: d3_time_parseMilliseconds,
- m: d3_time_parseMonthNumber,
- M: d3_time_parseMinutes,
- p: d3_time_parseAmPm,
- S: d3_time_parseSeconds,
- U: d3_time_parseWeekNumberSunday,
- w: d3_time_parseWeekdayNumber,
- W: d3_time_parseWeekNumberMonday,
- x: d3_time_parseLocaleDate,
- X: d3_time_parseLocaleTime,
- y: d3_time_parseYear,
- Y: d3_time_parseFullYear,
- "%": d3_time_parseLiteralPercent
- };
- function d3_time_parseWeekdayAbbrev(date, string, i) {
- d3_time_dayAbbrevRe.lastIndex = 0;
- var n = d3_time_dayAbbrevRe.exec(string.substring(i));
- return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseWeekday(date, string, i) {
- d3_time_dayRe.lastIndex = 0;
- var n = d3_time_dayRe.exec(string.substring(i));
- return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseWeekdayNumber(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 1));
- return n ? (date.w = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseWeekNumberSunday(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i));
- return n ? (date.U = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseWeekNumberMonday(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i));
- return n ? (date.W = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseMonthAbbrev(date, string, i) {
- d3_time_monthAbbrevRe.lastIndex = 0;
- var n = d3_time_monthAbbrevRe.exec(string.substring(i));
- return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseMonth(date, string, i) {
- d3_time_monthRe.lastIndex = 0;
- var n = d3_time_monthRe.exec(string.substring(i));
- return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1;
- }
- function d3_time_parseLocaleFull(date, string, i) {
- return d3_time_parse(date, d3_time_formats.c.toString(), string, i);
- }
- function d3_time_parseLocaleDate(date, string, i) {
- return d3_time_parse(date, d3_time_formats.x.toString(), string, i);
- }
- function d3_time_parseLocaleTime(date, string, i) {
- return d3_time_parse(date, d3_time_formats.X.toString(), string, i);
- }
- function d3_time_parseFullYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 4));
- return n ? (date.y = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1;
- }
- function d3_time_expandYear(d) {
- return d + (d > 68 ? 1900 : 2e3);
- }
- function d3_time_parseMonthNumber(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.m = n[0] - 1, i + n[0].length) : -1;
- }
- function d3_time_parseDay(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.d = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseDayOfYear(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 3));
- return n ? (date.j = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseHour24(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.H = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseMinutes(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.M = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseSeconds(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 2));
- return n ? (date.S = +n[0], i + n[0].length) : -1;
- }
- function d3_time_parseMilliseconds(date, string, i) {
- d3_time_numberRe.lastIndex = 0;
- var n = d3_time_numberRe.exec(string.substring(i, i + 3));
- return n ? (date.L = +n[0], i + n[0].length) : -1;
- }
- var d3_time_numberRe = /^\s*\d+/;
- function d3_time_parseAmPm(date, string, i) {
- var n = d3_time_amPmLookup.get(string.substring(i, i += 2).toLowerCase());
- return n == null ? -1 : (date.p = n, i);
- }
- var d3_time_amPmLookup = d3.map({
- am: 0,
- pm: 1
- });
- function d3_time_zone(d) {
- var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = ~~(Math.abs(z) / 60), zm = Math.abs(z) % 60;
- return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2);
- }
- function d3_time_parseLiteralPercent(date, string, i) {
- d3_time_percentRe.lastIndex = 0;
- var n = d3_time_percentRe.exec(string.substring(i, i + 1));
- return n ? i + n[0].length : -1;
- }
- d3.time.format.utc = function(template) {
- var local = d3.time.format(template);
- function format(date) {
- try {
- d3_time = d3_time_utc;
- var utc = new d3_time();
- utc._ = date;
- return local(utc);
- } finally {
- d3_time = Date;
- }
- }
- format.parse = function(string) {
- try {
- d3_time = d3_time_utc;
- var date = local.parse(string);
- return date && date._;
- } finally {
- d3_time = Date;
- }
- };
- format.toString = local.toString;
- return format;
- };
- var d3_time_formatIso = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");
- d3.time.format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso;
- function d3_time_formatIsoNative(date) {
- return date.toISOString();
- }
- d3_time_formatIsoNative.parse = function(string) {
- var date = new Date(string);
- return isNaN(date) ? null : date;
- };
- d3_time_formatIsoNative.toString = d3_time_formatIso.toString;
- d3.time.second = d3_time_interval(function(date) {
- return new d3_time(Math.floor(date / 1e3) * 1e3);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 1e3);
- }, function(date) {
- return date.getSeconds();
- });
- d3.time.seconds = d3.time.second.range;
- d3.time.seconds.utc = d3.time.second.utc.range;
- d3.time.minute = d3_time_interval(function(date) {
- return new d3_time(Math.floor(date / 6e4) * 6e4);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 6e4);
- }, function(date) {
- return date.getMinutes();
- });
- d3.time.minutes = d3.time.minute.range;
- d3.time.minutes.utc = d3.time.minute.utc.range;
- d3.time.hour = d3_time_interval(function(date) {
- var timezone = date.getTimezoneOffset() / 60;
- return new d3_time((Math.floor(date / 36e5 - timezone) + timezone) * 36e5);
- }, function(date, offset) {
- date.setTime(date.getTime() + Math.floor(offset) * 36e5);
- }, function(date) {
- return date.getHours();
- });
- d3.time.hours = d3.time.hour.range;
- d3.time.hours.utc = d3.time.hour.utc.range;
- d3.time.month = d3_time_interval(function(date) {
- date = d3.time.day(date);
- date.setDate(1);
- return date;
- }, function(date, offset) {
- date.setMonth(date.getMonth() + offset);
- }, function(date) {
- return date.getMonth();
- });
- d3.time.months = d3.time.month.range;
- d3.time.months.utc = d3.time.month.utc.range;
- function d3_time_scale(linear, methods, format) {
- function scale(x) {
- return linear(x);
- }
- scale.invert = function(x) {
- return d3_time_scaleDate(linear.invert(x));
- };
- scale.domain = function(x) {
- if (!arguments.length) return linear.domain().map(d3_time_scaleDate);
- linear.domain(x);
- return scale;
- };
- scale.nice = function(m) {
- return scale.domain(d3_scale_nice(scale.domain(), m));
- };
- scale.ticks = function(m, k) {
- var extent = d3_scaleExtent(scale.domain());
- if (typeof m !== "function") {
- var span = extent[1] - extent[0], target = span / m, i = d3.bisect(d3_time_scaleSteps, target);
- if (i == d3_time_scaleSteps.length) return methods.year(extent, m);
- if (!i) return linear.ticks(m).map(d3_time_scaleDate);
- if (Math.log(target / d3_time_scaleSteps[i - 1]) < Math.log(d3_time_scaleSteps[i] / target)) --i;
- m = methods[i];
- k = m[1];
- m = m[0].range;
- }
- return m(extent[0], new Date(+extent[1] + 1), k);
- };
- scale.tickFormat = function() {
- return format;
- };
- scale.copy = function() {
- return d3_time_scale(linear.copy(), methods, format);
- };
- return d3_scale_linearRebind(scale, linear);
- }
- function d3_time_scaleDate(t) {
- return new Date(t);
- }
- function d3_time_scaleFormat(formats) {
- return function(date) {
- var i = formats.length - 1, f = formats[i];
- while (!f[1](date)) f = formats[--i];
- return f[0](date);
- };
- }
- function d3_time_scaleSetYear(y) {
- var d = new Date(y, 0, 1);
- d.setFullYear(y);
- return d;
- }
- function d3_time_scaleGetYear(d) {
- var y = d.getFullYear(), d0 = d3_time_scaleSetYear(y), d1 = d3_time_scaleSetYear(y + 1);
- return y + (d - d0) / (d1 - d0);
- }
- var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ];
- var d3_time_scaleLocalMethods = [ [ d3.time.second, 1 ], [ d3.time.second, 5 ], [ d3.time.second, 15 ], [ d3.time.second, 30 ], [ d3.time.minute, 1 ], [ d3.time.minute, 5 ], [ d3.time.minute, 15 ], [ d3.time.minute, 30 ], [ d3.time.hour, 1 ], [ d3.time.hour, 3 ], [ d3.time.hour, 6 ], [ d3.time.hour, 12 ], [ d3.time.day, 1 ], [ d3.time.day, 2 ], [ d3.time.week, 1 ], [ d3.time.month, 1 ], [ d3.time.month, 3 ], [ d3.time.year, 1 ] ];
- var d3_time_scaleLocalFormats = [ [ d3.time.format("%Y"), d3_true ], [ d3.time.format("%B"), function(d) {
- return d.getMonth();
- } ], [ d3.time.format("%b %d"), function(d) {
- return d.getDate() != 1;
- } ], [ d3.time.format("%a %d"), function(d) {
- return d.getDay() && d.getDate() != 1;
- } ], [ d3.time.format("%I %p"), function(d) {
- return d.getHours();
- } ], [ d3.time.format("%I:%M"), function(d) {
- return d.getMinutes();
- } ], [ d3.time.format(":%S"), function(d) {
- return d.getSeconds();
- } ], [ d3.time.format(".%L"), function(d) {
- return d.getMilliseconds();
- } ] ];
- var d3_time_scaleLinear = d3.scale.linear(), d3_time_scaleLocalFormat = d3_time_scaleFormat(d3_time_scaleLocalFormats);
- d3_time_scaleLocalMethods.year = function(extent, m) {
- return d3_time_scaleLinear.domain(extent.map(d3_time_scaleGetYear)).ticks(m).map(d3_time_scaleSetYear);
- };
- d3.time.scale = function() {
- return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat);
- };
- var d3_time_scaleUTCMethods = d3_time_scaleLocalMethods.map(function(m) {
- return [ m[0].utc, m[1] ];
- });
- var d3_time_scaleUTCFormats = [ [ d3.time.format.utc("%Y"), d3_true ], [ d3.time.format.utc("%B"), function(d) {
- return d.getUTCMonth();
- } ], [ d3.time.format.utc("%b %d"), function(d) {
- return d.getUTCDate() != 1;
- } ], [ d3.time.format.utc("%a %d"), function(d) {
- return d.getUTCDay() && d.getUTCDate() != 1;
- } ], [ d3.time.format.utc("%I %p"), function(d) {
- return d.getUTCHours();
- } ], [ d3.time.format.utc("%I:%M"), function(d) {
- return d.getUTCMinutes();
- } ], [ d3.time.format.utc(":%S"), function(d) {
- return d.getUTCSeconds();
- } ], [ d3.time.format.utc(".%L"), function(d) {
- return d.getUTCMilliseconds();
- } ] ];
- var d3_time_scaleUTCFormat = d3_time_scaleFormat(d3_time_scaleUTCFormats);
- function d3_time_scaleUTCSetYear(y) {
- var d = new Date(Date.UTC(y, 0, 1));
- d.setUTCFullYear(y);
- return d;
- }
- function d3_time_scaleUTCGetYear(d) {
- var y = d.getUTCFullYear(), d0 = d3_time_scaleUTCSetYear(y), d1 = d3_time_scaleUTCSetYear(y + 1);
- return y + (d - d0) / (d1 - d0);
- }
- d3_time_scaleUTCMethods.year = function(extent, m) {
- return d3_time_scaleLinear.domain(extent.map(d3_time_scaleUTCGetYear)).ticks(m).map(d3_time_scaleUTCSetYear);
- };
- d3.time.scale.utc = function() {
- return d3_time_scale(d3.scale.linear(), d3_time_scaleUTCMethods, d3_time_scaleUTCFormat);
- };
- d3.text = d3_xhrType(function(request) {
- return request.responseText;
- });
- d3.json = function(url, callback) {
- return d3_xhr(url, "application/json", d3_json, callback);
- };
- function d3_json(request) {
- return JSON.parse(request.responseText);
- }
- d3.html = function(url, callback) {
- return d3_xhr(url, "text/html", d3_html, callback);
- };
- function d3_html(request) {
- var range = d3_document.createRange();
- range.selectNode(d3_document.body);
- return range.createContextualFragment(request.responseText);
- }
- d3.xml = d3_xhrType(function(request) {
- return request.responseXML;
- });
- return d3;
-}();
\ No newline at end of file
diff --git a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.min.js b/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.min.js
deleted file mode 100644
index 0ae1d23..0000000
--- a/modules/enterprise/gui/coregui/src/main/webapp/js/d3.v3.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-d3=function(){function n(n){return null!=n&&!isNaN(n)}function t(n){return n.length}function e(n){for(var t=1;n*t%1;)t*=10;return t}function r(n,t){try{for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}catch(r){n.prototype=t}}function i(){}function u(){}function a(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function o(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.substring(1);for(var e=0,r=Aa.length;r>e;++e){var i=Aa[e]+t;if(i in n)return i}}function c(n){for(var t=-1,e=n.length,r=[];++t<e;)r.push(n[t]);return r}function l(n){return Array.prototype.slice.call(n)}function f(){}function s(){}function h(n){function t(){for(var t,r=e,i=-1,u=r.length;++i<u;)(t=r[i].on)&&t.apply(this,arguments);return n}var e=[],r=new i;return t.on=function(t,i){var u,a=r.get(t);return arguments.length<2?a&&a.on:(a&&(a.on=null,e=e.slice(0,u=e.indexOf(a)).concat(e.slice(u+1)),r.remove(t)),i&&e.push(r.set(t,{on:i})),n)},t}function g(){va
.event.preventDefault()}function p(){for(var n,t=va.event;n=t.sourceEvent;)t=n;return t}function d(n){for(var t=new s,e=0,r=arguments.length;++e<r;)t[arguments[e]]=h(t);return t.of=function(e,r){return function(i){try{var u=i.sourceEvent=va.event;i.target=n,va.event=i,t[i.type].apply(e,r)}finally{va.event=u}}},t}function m(n){return Ta(n,Ha),n}function v(n){return function(){return za(n,this)}}function y(n){return function(){return Da(n,this)}}function M(n,t){function e(){this.removeAttribute(n)}function r(){this.removeAttributeNS(n.space,n.local)}function i(){this.setAttribute(n,t)}function u(){this.setAttributeNS(n.space,n.local,t)}function a(){var e=t.apply(this,arguments);null==e?this.removeAttribute(n):this.setAttribute(n,e)}function o(){var e=t.apply(this,arguments);null==e?this.removeAttributeNS(n.space,n.local):this.setAttributeNS(n.space,n.local,e)}return n=va.ns.qualify(n),null==t?n.local?r:e:"function"==typeof t?n.local?o:a:n.local?u:i}function x(n){return n.trim(
).replace(/\s+/g," ")}function b(n){return new RegExp("(?:^|\\s+)"+va.requote(n)+"(?:\\s+|$)","g")}function _(n,t){function e(){for(var e=-1;++e<i;)n[e](this,t)}function r(){for(var e=-1,r=t.apply(this,arguments);++e<i;)n[e](this,r)}n=n.trim().split(/\s+/).map(w);var i=n.length;return"function"==typeof t?r:e}function w(n){var t=b(n);return function(e,r){if(i=e.classList)return r?i.add(n):i.remove(n);var i=e.getAttribute("class")||"";r?(t.lastIndex=0,t.test(i)||e.setAttribute("class",x(i+" "+n))):e.setAttribute("class",x(i.replace(t," ")))}}function S(n,t,e){function r(){this.style.removeProperty(n)}function i(){this.style.setProperty(n,t,e)}function u(){var r=t.apply(this,arguments);null==r?this.style.removeProperty(n):this.style.setProperty(n,r,e)}return null==t?r:"function"==typeof t?u:i}function E(n,t){function e(){delete this[n]}function r(){this[n]=t}function i(){var e=t.apply(this,arguments);null==e?delete this[n]:this[n]=e}return null==t?e:"function"==typeof t?i:r}fun
ction k(n){return{__data__:n}}function A(n){return function(){return La(this,n)}}function N(n){return arguments.length||(n=va.ascending),function(t,e){return!t-!e||n(t.__data__,e.__data__)}}function q(n,t){for(var e=0,r=n.length;r>e;e++)for(var i,u=n[e],a=0,o=u.length;o>a;a++)(i=u[a])&&t(i,a,e);return n}function T(n){return Ta(n,Pa),n}function C(n,t,e){function r(){var t=this[a];t&&(this.removeEventListener(n,t,t.$),delete this[a])}function i(){var i=c(t,Na(arguments));r.call(this),this.addEventListener(n,this[a]=i,i.$=e),i._=t}function u(){var t,e=new RegExp("^__on([^.]+)"+va.requote(n)+"$");for(var r in this)if(t=r.match(e)){var i=this[r];this.removeEventListener(t[1],i,i.$),delete this[r]}}var a="__on"+n,o=n.indexOf("."),c=z;o>0&&(n=n.substring(0,o));var l=Ra.get(n);return l&&(n=l,c=D),o?t?i:r:t?f:u}function z(n,t){return function(e){var r=va.event;va.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{va.event=r}}}function D(n,t){var e=z(n,t);return function(n){var t=
this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function j(n){var t="selectstart."+n,e="dragstart."+n,r="click."+n,i=va.select(xa).on(t,g).on(e,g),u=Ma.style,a=u[Ya];return u[Ya]="none",function(n){function o(){i.on(r,null)}i.on(t,null).on(e,null),u[Ya]=a,n&&(i.on(r,function(){g(),o()},!0),setTimeout(o,0))}}function L(n,t){var e=n.ownerSVGElement||n;if(e.createSVGPoint){var r=e.createSVGPoint();if(0>Ua&&(xa.scrollX||xa.scrollY)){e=va.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var i=e[0][0].getScreenCTM();Ua=!(i.f||i.e),e.remove()}return Ua?(r.x=t.pageX,r.y=t.pageY):(r.x=t.clientX,r.y=t.clientY),r=r.matrixTransform(n.getScreenCTM().inverse()),[r.x,r.y]}var u=n.getBoundingClientRect();return[t.clientX-u.left-n.clientLeft,t.clientY-u.top-n.clientTop]}function H(){}function F(n,t,e){return new P(n,t,e)}function P(n,t,e){this.h=n,this.s=t,this.l=e}function O(n,t,e){function r(
n){return n>360?n-=360:0>n&&(n+=360),60>n?u+(a-u)*n/60:180>n?a:240>n?u+(a-u)*(240-n)/60:u}function i(n){return Math.round(255*r(n))}var u,a;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,a=.5>=e?e*(1+t):e+t-e*t,u=2*e-a,et(i(n+120),i(n),i(n-120))}function R(n){return n>0?1:0>n?-1:0}function Y(n){return n>1?0:-1>n?Ba:Math.acos(n)}function U(n){return n>1?Ba/2:-1>n?-Ba/2:Math.asin(n)}function I(n){return(Math.exp(n)-Math.exp(-n))/2}function V(n){return(Math.exp(n)+Math.exp(-n))/2}function X(n){return(n=Math.sin(n/2))*n}function Z(n,t,e){return new B(n,t,e)}function B(n,t,e){this.h=n,this.c=t,this.l=e}function $(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),W(e,Math.cos(n*=Ja)*t,Math.sin(n)*t)}function W(n,t,e){return new J(n,t,e)}function J(n,t,e){this.l=n,this.a=t,this.b=e}function G(n,t,e){var r=(n+16)/116,i=r+t/500,u=r-e/200;return i=Q(i)*no,r=Q(r)*to,u=Q(u)*eo,et(tt(3.2404542*i-1.5371385*r-.4985314*u),tt(-.969266*i+1.8760108*r+.041556*u),t
t(.0556434*i-.2040259*r+1.0572252*u))}function K(n,t,e){return n>0?Z(Math.atan2(e,t)*Ga,Math.sqrt(t*t+e*e),n):Z(0/0,0/0,n)}function Q(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function nt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function tt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function et(n,t,e){return new rt(n,t,e)}function rt(n,t,e){this.r=n,this.g=t,this.b=e}function it(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function ut(n,t,e){var r,i,u,a=0,o=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(i=r[2].split(","),r[1]){case"hsl":return e(parseFloat(i[0]),parseFloat(i[1])/100,parseFloat(i[2])/100);case"rgb":return t(lt(i[0]),lt(i[1]),lt(i[2]))}return(u=uo.get(n))?t(u.r,u.g,u.b):(null!=n&&"#"===n.charAt(0)&&(4===n.length?(a=n.charAt(1),a+=a,o=n.charAt(2),o+=o,c=n.charAt(3),c+=c):7===n.length&&(a=n.substring(1,3),o=n.substring(3,5),c=n.substring(5,7)),a=parseInt(a,16),o=parseInt(o,16),c=parseIn
t(c,16)),t(a,o,c))}function at(n,t,e){var r,i,u=Math.min(n/=255,t/=255,e/=255),a=Math.max(n,t,e),o=a-u,c=(a+u)/2;return o?(i=.5>c?o/(a+u):o/(2-a-u),r=n==a?(t-e)/o+(e>t?6:0):t==a?(e-n)/o+2:(n-t)/o+4,r*=60):(r=0/0,i=c>0&&1>c?0:r),F(r,i,c)}function ot(n,t,e){n=ct(n),t=ct(t),e=ct(e);var r=nt((.4124564*n+.3575761*t+.1804375*e)/no),i=nt((.2126729*n+.7151522*t+.072175*e)/to),u=nt((.0193339*n+.119192*t+.9503041*e)/eo);return W(116*i-16,500*(r-i),200*(i-u))}function ct(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function lt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function ft(n){return"function"==typeof n?n:function(){return n}}function st(n){return n}function ht(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),gt(t,e,n,r)}}function gt(n,t,e,r){function i(){var n,t=c.status;if(!t&&c.responseText||t>=200&&300>t||304===t){try{n=e.call(u,c)}catch(r){return a.error.call(u,r),void 0}a.load.call
(u,n)}else a.error.call(u,c)}var u={},a=va.dispatch("progress","load","error"),o={},c=new XMLHttpRequest,l=null;return!xa.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=i:c.onreadystatechange=function(){c.readyState>3&&i()},c.onprogress=function(n){var t=va.event;va.event=n;try{a.progress.call(u,c)}finally{va.event=t}},u.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?o[n]:(null==t?delete o[n]:o[n]=t+"",u)},u.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",u):t},u.responseType=function(n){return arguments.length?(l=n,u):l},u.response=function(n){return e=n,u},["get","post"].forEach(function(n){u[n]=function(){return u.send.apply(u,[n].concat(Na(arguments)))}}),u.send=function(e,r,i){if(2===arguments.length&&"function"==typeof r&&(i=r,r=null),c.open(e,n,!0),null==t||"accept"in o||(o.accept=t+",*/*"),c.setRequestHeader)for(var a in o)c.setRequestHeader(a,o[a]);r
eturn null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=i&&u.on("error",i).on("load",function(n){i(null,n)}),c.send(null==r?null:r),u},u.abort=function(){return c.abort(),u},va.rebind(u,a,"on"),null==r?u:u.get(pt(r))}function pt(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function dt(){var n=mt(),t=vt()-n;t>24?(isFinite(t)&&(clearTimeout(lo),lo=setTimeout(dt,t)),co=0):(co=1,fo(dt))}function mt(){for(var n=Date.now(),t=ao;t;)n>=t.time&&(t.flush=t.callback(n-t.time)),t=t.next;return n}function vt(){for(var n,t=ao,e=1/0;t;)t.flush?t=n?n.next=t.next:ao=t.next:(t.time<e&&(e=t.time),t=(n=t).next);return oo=n,e}function yt(n,t){var e=Math.pow(10,3*Math.abs(8-t));return{scale:t>8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Mt(n,t){return t-(n?Math.ceil(Math.log(n)/Math.LN10):1)}function xt(n){return n+""}function bt(){}function _t(n,t,e){var r=e.s=n+t,i=r-n,u=r-i;e.t=n-u+(t-i)}function wt(n,t){n&&_o.hasOwnProper
ty(n.type)&&_o[n.type](n,t)}function St(n,t,e){var r,i=-1,u=n.length-e;for(t.lineStart();++i<u;)r=n[i],t.point(r[0],r[1]);t.lineEnd()}function Et(n,t){var e=-1,r=n.length;for(t.polygonStart();++e<r;)St(n[e],t,1);t.polygonEnd()}function kt(){function n(n,t){n*=Ja,t=t*Ja/2+Ba/4;var e=n-r,a=Math.cos(t),o=Math.sin(t),c=u*o,l=i*a+c*Math.cos(e),f=c*Math.sin(e);So.add(Math.atan2(f,l)),r=n,i=a,u=o}var t,e,r,i,u;Eo.point=function(a,o){Eo.point=n,r=(t=a)*Ja,i=Math.cos(o=(e=o)*Ja/2+Ba/4),u=Math.sin(o)},Eo.lineEnd=function(){n(t,e)}}function At(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function Nt(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function qt(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function Tt(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function Ct(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function zt(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function Dt(n){return[Math.atan2(n[1],n[0]),U(
n[2])]}function jt(n,t){return Math.abs(n[0]-t[0])<$a&&Math.abs(n[1]-t[1])<$a}function Lt(n,t){n*=Ja;var e=Math.cos(t*=Ja);Ht(e*Math.cos(n),e*Math.sin(n),Math.sin(t))}function Ht(n,t,e){++ko,No+=(n-No)/ko,qo+=(t-qo)/ko,To+=(e-To)/ko}function Ft(){function n(n,i){n*=Ja;var u=Math.cos(i*=Ja),a=u*Math.cos(n),o=u*Math.sin(n),c=Math.sin(i),l=Math.atan2(Math.sqrt((l=e*c-r*o)*l+(l=r*a-t*c)*l+(l=t*o-e*a)*l),t*a+e*o+r*c);Ao+=l,Co+=l*(t+(t=a)),zo+=l*(e+(e=o)),Do+=l*(r+(r=c)),Ht(t,e,r)}var t,e,r;Fo.point=function(i,u){i*=Ja;var a=Math.cos(u*=Ja);t=a*Math.cos(i),e=a*Math.sin(i),r=Math.sin(u),Fo.point=n,Ht(t,e,r)}}function Pt(){Fo.point=Lt}function Ot(){function n(n,t){n*=Ja;var e=Math.cos(t*=Ja),a=e*Math.cos(n),o=e*Math.sin(n),c=Math.sin(t),l=i*c-u*o,f=u*a-r*c,s=r*o-i*a,h=Math.sqrt(l*l+f*f+s*s),g=r*a+i*o+u*c,p=h&&-Y(g)/h,d=Math.atan2(h,g);jo+=p*l,Lo+=p*f,Ho+=p*s,Ao+=d,Co+=d*(r+(r=a)),zo+=d*(i+(i=o)),Do+=d*(u+(u=c)),Ht(r,i,u)}var t,e,r,i,u;Fo.point=function(a,o){t=a,e=o,Fo.point=n,a*=Ja;
var c=Math.cos(o*=Ja);r=c*Math.cos(a),i=c*Math.sin(a),u=Math.sin(o),Ht(r,i,u)},Fo.lineEnd=function(){n(t,e),Fo.lineEnd=Pt,Fo.point=Lt}}function Rt(){return!0}function Yt(n,t,e,r,i){var u=[],a=[];if(n.forEach(function(n){if(!((t=n.length-1)<=0)){var t,e=n[0],r=n[t];if(jt(e,r)){i.lineStart();for(var o=0;t>o;++o)i.point((e=n[o])[0],e[1]);return i.lineEnd(),void 0}var c={point:e,points:n,other:null,visited:!1,entry:!0,subject:!0},l={point:e,points:[e],other:c,visited:!1,entry:!1,subject:!1};c.other=l,u.push(c),a.push(l),c={point:r,points:[r],other:null,visited:!1,entry:!1,subject:!0},l={point:r,points:[r],other:c,visited:!1,entry:!0,subject:!1},c.other=l,u.push(c),a.push(l)}}),a.sort(t),Ut(u),Ut(a),u.length){if(e)for(var o=1,c=!e(a[0].point),l=a.length;l>o;++o)a[o].entry=c=!c;for(var f,s,h,g=u[0];;){for(f=g;f.visited;)if((f=f.next)===g)return;s=f.points,i.lineStart();do{if(f.visited=f.other.visited=!0,f.entry){if(f.subject)for(var o=0;o<s.length;o++)i.point((h=s[o])[0],h[1]);els
e r(f.point,f.next.point,1,i);f=f.next}else{if(f.subject){s=f.prev.points;for(var o=s.length;--o>=0;)i.point((h=s[o])[0],h[1])}else r(f.point,f.prev.point,-1,i);f=f.prev}f=f.other,s=f.points}while(!f.visited);i.lineEnd()}}}function Ut(n){if(t=n.length){for(var t,e,r=0,i=n[0];++r<t;)i.next=e=n[r],e.prev=i,i=e;i.next=e=n[0],e.prev=i}}function It(n,t,e,r){return function(i){function u(t,e){n(t,e)&&i.point(t,e)}function a(n,t){d.point(n,t)}function o(){m.point=a,d.lineStart()}function c(){m.point=u,d.lineEnd()}function l(n,t){y.point(n,t),p.push([n,t])}function f(){y.lineStart(),p=[]}function s(){l(p[0][0],p[0][1]),y.lineEnd();var n,t=y.clean(),e=v.buffer(),r=e.length;if(p.pop(),g.push(p),p=null,r){if(1&t){n=e[0];var u,r=n.length-1,a=-1;for(i.lineStart();++a<r;)i.point((u=n[a])[0],u[1]);return i.lineEnd(),void 0}r>1&&2&t&&e.push(e.pop().concat(e.shift())),h.push(e.filter(Vt))}}var h,g,p,d=t(i),m={point:u,lineStart:o,lineEnd:c,polygonStart:function(){m.point=l,m.lineStart=f,m.lin
eEnd=s,h=[],g=[],i.polygonStart()},polygonEnd:function(){m.point=u,m.lineStart=o,m.lineEnd=c,h=va.merge(h),h.length?Yt(h,Zt,null,e,i):r(g)&&(i.lineStart(),e(null,null,1,i),i.lineEnd()),i.polygonEnd(),h=g=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},v=Xt(),y=t(v);return m}}function Vt(n){return n.length>1}function Xt(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:f,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function Zt(n,t){return((n=n.point)[0]<0?n[1]-Ba/2-$a:Ba/2-n[1])-((t=t.point)[0]<0?t[1]-Ba/2-$a:Ba/2-t[1])}function Bt(n,t){var e=n[0],r=n[1],i=[Math.sin(e),-Math.cos(e),0],u=0,a=!1,o=!1,c=0;So.reset();for(var l=0,f=t.length;f>l;++l){var s=t[l],h=s.length;if(h){for(var g=s[0],p=g[0],d=g[1]/2+Ba/4,m=Math.sin(d),v=Math.cos(d),y=1;;){y===h&&(y=0),n=s[y];var M=n[0],x=n[1]/2+Ba/4,b=Math.sin(x),_=Math.co
s(x),w=M-p,S=Math.abs(w)>Ba,E=m*b;if(So.add(Math.atan2(E*Math.sin(w),v*_+E*Math.cos(w))),Math.abs(x)<$a&&(o=!0),u+=S?w+(w>=0?2:-2)*Ba:w,S^p>=e^M>=e){var k=qt(At(g),At(n));zt(k);var A=qt(i,k);zt(A);var N=(S^w>=0?-1:1)*U(A[2]);r>N&&(c+=S^w>=0?1:-1)}if(!y++)break;p=M,m=b,v=_,g=n}Math.abs(u)>$a&&(a=!0)}}return(!o&&!a&&0>So||-$a>u)^1&c}function $t(n){var t,e=0/0,r=0/0,i=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(u,a){var o=u>0?Ba:-Ba,c=Math.abs(u-e);Math.abs(c-Ba)<$a?(n.point(e,r=(r+a)/2>0?Ba/2:-Ba/2),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(o,r),n.point(u,r),t=0):i!==o&&c>=Ba&&(Math.abs(e-i)<$a&&(e-=i*$a),Math.abs(u-o)<$a&&(u-=o*$a),r=Wt(e,r,u,a),n.point(i,r),n.lineEnd(),n.lineStart(),n.point(o,r),t=0),n.point(e=u,r=a),i=o},lineEnd:function(){n.lineEnd(),e=r=0/0},clean:function(){return 2-t}}}function Wt(n,t,e,r){var i,u,a=Math.sin(n-e);return Math.abs(a)>$a?Math.atan((Math.sin(t)*(u=Math.cos(r))*Math.sin(e)-Math.sin(r)*(i=Math.cos(t))*Math.sin(n))/(
i*u*a)):(t+r)/2}function Jt(n,t,e,r){var i;if(null==n)i=e*Ba/2,r.point(-Ba,i),r.point(0,i),r.point(Ba,i),r.point(Ba,0),r.point(Ba,-i),r.point(0,-i),r.point(-Ba,-i),r.point(-Ba,0),r.point(-Ba,i);else if(Math.abs(n[0]-t[0])>$a){var u=(n[0]<t[0]?1:-1)*Ba;i=e*u/2,r.point(-u,i),r.point(0,i),r.point(u,i)}else r.point(t[0],t[1])}function Gt(n){return Bt(Oo,n)}function Kt(n){function t(n,t){return Math.cos(n)*Math.cos(t)>a}function e(n){var e,u,a,c,f;return{lineStart:function(){c=a=!1,f=1},point:function(s,h){var g,p=[s,h],d=t(s,h),m=o?d?0:i(s,h):d?i(s+(0>s?Ba:-Ba),h):0;if(!e&&(c=a=d)&&n.lineStart(),d!==a&&(g=r(e,p),(jt(e,g)||jt(p,g))&&(p[0]+=$a,p[1]+=$a,d=t(p[0],p[1]))),d!==a)f=0,d?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(l&&e&&o^d){var v;m&u||!(v=r(p,e,!0))||(f=0,o?(n.lineStart(),n.point(v[0][0],v[0][1]),n.point(v[1][0],v[1][1]),n.lineEnd()):(n.point(v[1][0],v[1][1]),n.lineEnd(),n.lineStart(),n.point(v[0][0],v[0][1])))}!d||e
&&jt(e,p)||n.point(p[0],p[1]),e=p,a=d,u=m},lineEnd:function(){a&&n.lineEnd(),e=null},clean:function(){return f|(c&&a)<<1}}}function r(n,t,e){var r=At(n),i=At(t),u=[1,0,0],o=qt(r,i),c=Nt(o,o),l=o[0],f=c-l*l;if(!f)return!e&&n;var s=a*c/f,h=-a*l/f,g=qt(u,o),p=Ct(u,s),d=Ct(o,h);Tt(p,d);var m=g,v=Nt(p,m),y=Nt(m,m),M=v*v-y*(Nt(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=Ct(m,(-v-x)/y);if(Tt(b,p),b=Dt(b),!e)return b;var _,w=n[0],S=t[0],E=n[1],k=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=Math.abs(A-Ba)<$a,q=N||$a>A;if(!N&&E>k&&(_=E,E=k,k=_),q?N?E+k>0^b[1]<(Math.abs(b[0]-w)<$a?E:k):E<=b[1]&&b[1]<=k:A>Ba^(w<=b[0]&&b[0]<=S)){var T=Ct(m,(-v+x)/y);return Tt(T,p),[b,Dt(T)]}}}function i(t,e){var r=o?n:Ba-n,i=0;return-r>t?i|=1:t>r&&(i|=2),-r>e?i|=4:e>r&&(i|=8),i}function u(n){return Bt(c,n)}var a=Math.cos(n),o=a>0,c=[n,0],l=Math.abs(a)>$a,f=we(n,6*Ja);return It(t,e,f,u)}function Qt(n,t,e,r){function i(r,i){return Math.abs(r[0]-n)<$a?i>0?0:3:Math.abs(r[0]-e)<$a?i>0?2:1:Math.abs(r[1]-t)<$a?i>0?1:0:i>0?3
:2}function u(n,t){return a(n.point,t.point)}function a(n,t){var e=i(n,1),r=i(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}function o(i,u){var a=u[0]-i[0],o=u[1]-i[1],c=[0,1];return Math.abs(a)<$a&&Math.abs(o)<$a?n<=i[0]&&i[0]<=e&&t<=i[1]&&i[1]<=r:ne(n-i[0],a,c)&&ne(i[0]-e,-a,c)&&ne(t-i[1],o,c)&&ne(i[1]-r,-o,c)?(c[1]<1&&(u[0]=i[0]+c[1]*a,u[1]=i[1]+c[1]*o),c[0]>0&&(i[0]+=c[0]*a,i[1]+=c[0]*o),!0):!1}return function(c){function l(u){var a=i(u,-1),o=f([0===a||3===a?n:e,a>1?r:t]);return o}function f(n){for(var t=0,e=M.length,r=n[1],i=0;e>i;++i)for(var u,a=1,o=M[i],c=o.length,l=o[0];c>a;++a)u=o[a],l[1]<=r?u[1]>r&&s(l,u,n)>0&&++t:u[1]<=r&&s(l,u,n)<0&&--t,l=u;return 0!==t}function s(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(e[0]-n[0])*(t[1]-n[1])}function h(u,o,c,l){var f=0,s=0;if(null==u||(f=i(u,c))!==(s=i(o,c))||a(u,o)<0^c>0){do l.point(0===f||3===f?n:e,f>1?r:t);while((f=(f+c+4)%4)!==s)}else l.point(o[0],o[1])}function g(i,u){return i>=n&&e>=i&&u>=t&
&r>=u}function p(n,t){g(n,t)&&c.point(n,t)}function d(){T.point=v,M&&M.push(x=[]),A=!0,k=!1,S=E=0/0}function m(){y&&(v(b,_),w&&k&&q.rejoin(),y.push(q.buffer())),T.point=p,k&&c.lineEnd()}function v(n,t){n=Math.max(-Ro,Math.min(Ro,n)),t=Math.max(-Ro,Math.min(Ro,t));var e=g(n,t);if(M&&x.push([n,t]),A)b=n,_=t,w=e,A=!1,e&&(c.lineStart(),c.point(n,t));else if(e&&k)c.point(n,t);else{var r=[S,E],i=[n,t];o(r,i)?(k||(c.lineStart(),c.point(r[0],r[1])),c.point(i[0],i[1]),e||c.lineEnd()):e&&(c.lineStart(),c.point(n,t))}S=n,E=t,k=e}var y,M,x,b,_,w,S,E,k,A,N=c,q=Xt(),T={point:p,lineStart:d,lineEnd:m,polygonStart:function(){c=q,y=[],M=[]},polygonEnd:function(){c=N,(y=va.merge(y)).length?(c.polygonStart(),Yt(y,u,l,h,c),c.polygonEnd()):f([n,t])&&(c.polygonStart(),c.lineStart(),h(null,null,1,c),c.lineEnd(),c.polygonEnd()),y=M=x=null}};return T}}function ne(n,t,e){if(Math.abs(t)<$a)return 0>=n;var r=n/t;if(t>0){if(r>e[1])return!1;r>e[0]&&(e[0]=r)}else{if(r<e[0])return!1;r<e[1]&&(e[1]=r)}return!
0}function te(n,t){function e(e,r){return e=n(e,r),t(e[0],e[1])}return n.invert&&t.invert&&(e.invert=function(e,r){return e=t.invert(e,r),e&&n.invert(e[0],e[1])}),e}function ee(n){var t=0,e=Ba/3,r=me(n),i=r(t,e);return i.parallels=function(n){return arguments.length?r(t=n[0]*Ba/180,e=n[1]*Ba/180):[180*(t/Ba),180*(e/Ba)]},i}function re(n,t){function e(n,t){var e=Math.sqrt(u-2*i*Math.sin(t))/i;return[e*Math.sin(n*=i),a-e*Math.cos(n)]}var r=Math.sin(n),i=(r+Math.sin(t))/2,u=1+r*(2*i-r),a=Math.sqrt(u)/i;return e.invert=function(n,t){var e=a-t;return[Math.atan2(n,e)/i,U((u-(n*n+e*e)*i*i)/(2*i))]},e}function ie(){function n(n,t){Uo+=i*n-r*t,r=n,i=t}var t,e,r,i;Bo.point=function(u,a){Bo.point=n,t=r=u,e=i=a},Bo.lineEnd=function(){n(t,e)}}function ue(n,t){Io>n&&(Io=n),n>Xo&&(Xo=n),Vo>t&&(Vo=t),t>Zo&&(Zo=t)}function ae(){function n(n,t){a.push("M",n,",",t,u)}function t(n,t){a.push("M",n,",",t),o.point=e}function e(n,t){a.push("L",n,",",t)}function r(){o.point=n}function i(){a.push("Z"
)}var u=oe(4.5),a=[],o={point:n,lineStart:function(){o.point=t},lineEnd:r,polygonStart:function(){o.lineEnd=i},polygonEnd:function(){o.lineEnd=r,o.point=n},pointRadius:function(n){return u=oe(n),o},result:function(){if(a.length){var n=a.join("");return a=[],n}}};return o}function oe(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function ce(n,t){No+=n,qo+=t,++To}function le(){function n(n,r){var i=n-t,u=r-e,a=Math.sqrt(i*i+u*u);Co+=a*(t+n)/2,zo+=a*(e+r)/2,Do+=a,ce(t=n,e=r)}var t,e;Wo.point=function(r,i){Wo.point=n,ce(t=r,e=i)}}function fe(){Wo.point=ce}function se(){function n(n,t){var e=n-r,u=t-i,a=Math.sqrt(e*e+u*u);Co+=a*(r+n)/2,zo+=a*(i+t)/2,Do+=a,a=i*n-r*t,jo+=a*(r+n),Lo+=a*(i+t),Ho+=3*a,ce(r=n,i=t)}var t,e,r,i;Wo.point=function(u,a){Wo.point=n,ce(t=r=u,e=i=a)},Wo.lineEnd=function(){n(t,e)}}function he(n){function t(t,e){n.moveTo(t,e),n.arc(t,e,a,0,2*Ba)}function e(t,e){n.moveTo(t,e),o.point=r}function r(t,e){n.lineTo(t,e)}function i(){o.p
oint=t}function u(){n.closePath()}var a=4.5,o={point:t,lineStart:function(){o.point=e},lineEnd:i,polygonStart:function(){o.lineEnd=u},polygonEnd:function(){o.lineEnd=i,o.point=t},pointRadius:function(n){return a=n,o},result:f};return o}function ge(n){function t(t){function r(e,r){e=n(e,r),t.point(e[0],e[1])}function i(){M=0/0,S.point=a,t.lineStart()}function a(r,i){var a=At([r,i]),o=n(r,i);e(M,x,y,b,_,w,M=o[0],x=o[1],y=r,b=a[0],_=a[1],w=a[2],u,t),t.point(M,x)}function o(){S.point=r,t.lineEnd()}function c(){i(),S.point=l,S.lineEnd=f}function l(n,t){a(s=n,h=t),g=M,p=x,d=b,m=_,v=w,S.point=a}function f(){e(M,x,y,b,_,w,g,p,s,d,m,v,u,t),S.lineEnd=o,o()}var s,h,g,p,d,m,v,y,M,x,b,_,w,S={point:r,lineStart:i,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c},polygonEnd:function(){t.polygonEnd(),S.lineStart=i}};return S}function e(t,u,a,o,c,l,f,s,h,g,p,d,m,v){var y=f-t,M=s-u,x=y*y+M*M;if(x>4*r&&m--){var b=o+g,_=c+p,w=l+d,S=Math.sqrt(b*b+_*_+w*w),E=Math.asin(w/=S),k=Math.
abs(Math.abs(w)-1)<$a?(a+h)/2:Math.atan2(_,b),A=n(k,E),N=A[0],q=A[1],T=N-t,C=q-u,z=M*T-y*C;(z*z/x>r||Math.abs((y*T+M*C)/x-.5)>.3||i>o*g+c*p+l*d)&&(e(t,u,a,o,c,l,N,q,k,b/=S,_/=S,w,m,v),v.point(N,q),e(N,q,k,b,_,w,f,s,h,g,p,d,m,v))}}var r=.5,i=Math.cos(30*Ja),u=16;return t.precision=function(n){return arguments.length?(u=(r=n*n)>0&&16,t):Math.sqrt(r)},t}function pe(n){var t=ge(function(t,e){return n([t*Ga,e*Ga])});return function(n){return n=t(n),{point:function(t,e){n.point(t*Ja,e*Ja)},sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}}function de(n){return me(function(){return n})()}function me(n){function t(n){return n=o(n[0]*Ja,n[1]*Ja),[n[0]*h+c,l-n[1]*h]}function e(n){return n=o.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Ga,n[1]*Ga]}function r(){o=te(a=Me(v,y,M),u);var n=u(d,m);return c=g-n[0]*h,l=p+n[1]*h,i()}function i(){return f&&(f.valid=!1,f=nu
ll),t}var u,a,o,c,l,f,s=ge(function(n,t){return n=u(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,d=0,m=0,v=0,y=0,M=0,x=Po,b=st,_=null,w=null;return t.stream=function(n){return f&&(f.valid=!1),f=ve(a,x(s(b(n)))),f.valid=!0,f},t.clipAngle=function(n){return arguments.length?(x=null==n?(_=n,Po):Kt((_=+n)*Ja),i()):_},t.clipExtent=function(n){return arguments.length?(w=n,b=null==n?st:Qt(n[0][0],n[0][1],n[1][0],n[1][1]),i()):w},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(d=n[0]%360*Ja,m=n[1]%360*Ja,r()):[d*Ga,m*Ga]},t.rotate=function(n){return arguments.length?(v=n[0]%360*Ja,y=n[1]%360*Ja,M=n.length>2?n[2]%360*Ja:0,r()):[v*Ga,y*Ga,M*Ga]},va.rebind(t,s,"precision"),function(){return u=n.apply(this,arguments),t.invert=u.invert&&e,r()}}function ve(n,t){return{point:function(e,r){r=n(e*Ja,r*Ja),e=r[0],t.point(e>Ba?e-2*Ba:-Ba>e?e+2*Ba:e,r[1])},sphere:
function(){t.sphere()},lineStart:function(){t.lineStart()},lineEnd:function(){t.lineEnd()},polygonStart:function(){t.polygonStart()},polygonEnd:function(){t.polygonEnd()}}}function ye(n,t){return[n,t]}function Me(n,t,e){return n?t||e?te(be(n),_e(t,e)):be(n):t||e?_e(t,e):ye}function xe(n){return function(t,e){return t+=n,[t>Ba?t-2*Ba:-Ba>t?t+2*Ba:t,e]}}function be(n){var t=xe(n);return t.invert=xe(-n),t}function _e(n,t){function e(n,t){var e=Math.cos(t),o=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),f=l*r+o*i;return[Math.atan2(c*u-f*a,o*r-l*i),U(f*u+c*a)]}var r=Math.cos(n),i=Math.sin(n),u=Math.cos(t),a=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),o=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),f=l*u-c*a;return[Math.atan2(c*u+l*a,o*r+f*i),U(f*r-o*i)]},e}function we(n,t){var e=Math.cos(n),r=Math.sin(n);return function(i,u,a,o){null!=i?(i=Se(e,i),u=Se(e,u),(a>0?u>i:i>u)&&(i+=2*a*Ba)):(i=n+2*a*Ba,u=n);for(var c,l=a*t,f=i;a>0?f>u:u>f;f-=l)o.point((c=Dt([e,-r*Math.cos(f
),-r*Math.sin(f)]))[0],c[1])}}function Se(n,t){var e=At(t);e[0]-=n,zt(e);var r=Y(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-$a)%(2*Math.PI)}function Ee(n,t,e){var r=va.range(n,t-$a,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function ke(n,t,e){var r=va.range(n,t-$a,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function Ae(n){return n.source}function Ne(n){return n.target}function qe(n,t,e,r){var i=Math.cos(t),u=Math.sin(t),a=Math.cos(r),o=Math.sin(r),c=i*Math.cos(n),l=i*Math.sin(n),f=a*Math.cos(e),s=a*Math.sin(e),h=2*Math.asin(Math.sqrt(X(r-t)+i*a*X(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*f,i=e*l+t*s,a=e*u+t*o;return[Math.atan2(i,r)*Ga,Math.atan2(a,Math.sqrt(r*r+i*i))*Ga]}:function(){return[n*Ga,t*Ga]};return p.distance=h,p}function Te(){function n(n,i){var u=Math.sin(i*=Ja),a=Math.cos(i),o=Math.abs((n*=Ja)-t),c=Math.cos(o);Jo+=Math.atan2(Math.sqrt((o=a*Math.sin(o))*o+(o=r*u-e*a
*c)*o),e*u+r*a*c),t=n,e=u,r=a}var t,e,r;Go.point=function(i,u){t=i*Ja,e=Math.sin(u*=Ja),r=Math.cos(u),Go.point=n},Go.lineEnd=function(){Go.point=Go.lineEnd=f}}function Ce(n,t){function e(t,e){var r=Math.cos(t),i=Math.cos(e),u=n(r*i);return[u*i*Math.sin(t),u*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),i=t(r),u=Math.sin(i),a=Math.cos(i);return[Math.atan2(n*u,r*a),Math.asin(r&&e*u/r)]},e}function ze(n,t){function e(n,t){var e=Math.abs(Math.abs(t)-Ba/2)<$a?0:a/Math.pow(i(t),u);return[e*Math.sin(u*n),a-e*Math.cos(u*n)]}var r=Math.cos(n),i=function(n){return Math.tan(Ba/4+n/2)},u=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(i(t)/i(n)),a=r*Math.pow(i(n),u)/u;return u?(e.invert=function(n,t){var e=a-t,r=R(u)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/u,2*Math.atan(Math.pow(a/r,1/u))-Ba/2]},e):je}function De(n,t){function e(n,t){var e=u-t;return[e*Math.sin(i*n),u-e*Math.cos(i*n)]}var r=Math.cos(n),i=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),u=r/i+n;return M
ath.abs(i)<$a?ye:(e.invert=function(n,t){var e=u-t;return[Math.atan2(n,e)/i,u-R(i)*Math.sqrt(n*n+e*e)]},e)}function je(n,t){return[n,Math.log(Math.tan(Ba/4+t/2))]}function Le(n){var t,e=de(n),r=e.scale,i=e.translate,u=e.clipExtent;return e.scale=function(){var n=r.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.translate=function(){var n=i.apply(e,arguments);return n===e?t?e.clipExtent(null):e:n},e.clipExtent=function(n){var a=u.apply(e,arguments);if(a===e){if(t=null==n){var o=Ba*r(),c=i();u([[c[0]-o,c[1]-o],[c[0]+o,c[1]+o]])}}else t&&(a=null);return a},e.clipExtent(null)}function He(n,t){var e=Math.cos(t)*Math.sin(n);return[Math.log((1+e)/(1-e))/2,Math.atan2(Math.tan(t),Math.cos(n))]}function Fe(n){function t(t){function a(){l.push("M",u(n(f),o))}for(var c,l=[],f=[],s=-1,h=t.length,g=ft(e),p=ft(r);++s<h;)i.call(this,c=t[s],s)?f.push([+g.call(this,c,s),+p.call(this,c,s)]):f.length&&(a(),f=[]);return f.length&&a(),l.length?l.join(""):null}var e=Pe,r=Oe,i=Rt,u=Re,a
=u.key,o=.7;return t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.defined=function(n){return arguments.length?(i=n,t):i},t.interpolate=function(n){return arguments.length?(a="function"==typeof n?u=n:(u=rc.get(n)||Re).key,t):a},t.tension=function(n){return arguments.length?(o=n,t):o},t}function Pe(n){return n[0]}function Oe(n){return n[1]}function Re(n){return n.join("L")}function Ye(n){return Re(n)+"Z"}function Ue(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r[0]+(r=n[t])[0])/2,"V",r[1]);return e>1&&i.push("H",r[0]),i.join("")}function Ie(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("V",(r=n[t])[1],"H",r[0]);return i.join("")}function Ve(n){for(var t=0,e=n.length,r=n[0],i=[r[0],",",r[1]];++t<e;)i.push("H",(r=n[t])[0],"V",r[1]);return i.join("")}function Xe(n,t){return n.length<4?Re(n):n[1]+$e(n.slice(1,n.length-1),We(n,t))}function Ze(n,t){return n.length<3?Re(n):n[0]+$e((n.
push(n[0]),n),We([n[n.length-2]].concat(n,[n[1]]),t))}function Be(n,t){return n.length<3?Re(n):n[0]+$e(n,We(n,t))}function $e(n,t){if(t.length<1||n.length!=t.length&&n.length!=t.length+2)return Re(n);var e=n.length!=t.length,r="",i=n[0],u=n[1],a=t[0],o=a,c=1;if(e&&(r+="Q"+(u[0]-2*a[0]/3)+","+(u[1]-2*a[1]/3)+","+u[0]+","+u[1],i=n[1],c=2),t.length>1){o=t[1],u=n[c],c++,r+="C"+(i[0]+a[0])+","+(i[1]+a[1])+","+(u[0]-o[0])+","+(u[1]-o[1])+","+u[0]+","+u[1];for(var l=2;l<t.length;l++,c++)u=n[c],o=t[l],r+="S"+(u[0]-o[0])+","+(u[1]-o[1])+","+u[0]+","+u[1]}if(e){var f=n[c];r+="Q"+(u[0]+2*o[0]/3)+","+(u[1]+2*o[1]/3)+","+f[0]+","+f[1]}return r}function We(n,t){for(var e,r=[],i=(1-t)/2,u=n[0],a=n[1],o=1,c=n.length;++o<c;)e=u,u=a,a=n[o],r.push([i*(a[0]-e[0]),i*(a[1]-e[1])]);return r}function Je(n){if(n.length<3)return Re(n);var t=1,e=n.length,r=n[0],i=r[0],u=r[1],a=[i,i,i,(r=n[1])[0]],o=[u,u,u,r[1]],c=[i,",",u];for(tr(c,a,o);++t<e;)r=n[t],a.shift(),a.push(r[0]),o.shift(),o.push(r[1]),tr(c,
a,o);for(t=-1;++t<2;)a.shift(),a.push(r[0]),o.shift(),o.push(r[1]),tr(c,a,o);return c.join("")}function Ge(n){if(n.length<4)return Re(n);for(var t,e=[],r=-1,i=n.length,u=[0],a=[0];++r<3;)t=n[r],u.push(t[0]),a.push(t[1]);for(e.push(nr(ac,u)+","+nr(ac,a)),--r;++r<i;)t=n[r],u.shift(),u.push(t[0]),a.shift(),a.push(t[1]),tr(e,u,a);return e.join("")}function Ke(n){for(var t,e,r=-1,i=n.length,u=i+4,a=[],o=[];++r<4;)e=n[r%i],a.push(e[0]),o.push(e[1]);for(t=[nr(ac,a),",",nr(ac,o)],--r;++r<u;)e=n[r%i],a.shift(),a.push(e[0]),o.shift(),o.push(e[1]),tr(t,a,o);return t.join("")}function Qe(n,t){var e=n.length-1;if(e)for(var r,i,u=n[0][0],a=n[0][1],o=n[e][0]-u,c=n[e][1]-a,l=-1;++l<=e;)r=n[l],i=l/e,r[0]=t*r[0]+(1-t)*(u+i*o),r[1]=t*r[1]+(1-t)*(a+i*c);return Je(n)}function nr(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]+n[3]*t[3]}function tr(n,t,e){n.push("C",nr(ic,t),",",nr(ic,e),",",nr(uc,t),",",nr(uc,e),",",nr(ac,t),",",nr(ac,e))}function er(n,t){return(t[1]-n[1])/(t[0]-n[0])}function rr(n){f
or(var t=0,e=n.length-1,r=[],i=n[0],u=n[1],a=r[0]=er(i,u);++t<e;)r[t]=(a+(a=er(i=u,u=n[t+1])))/2;return r[t]=a,r}function ir(n){for(var t,e,r,i,u=[],a=rr(n),o=-1,c=n.length-1;++o<c;)t=er(n[o],n[o+1]),Math.abs(t)<1e-6?a[o]=a[o+1]=0:(e=a[o]/t,r=a[o+1]/t,i=e*e+r*r,i>9&&(i=3*t/Math.sqrt(i),a[o]=i*e,a[o+1]=i*r));for(o=-1;++o<=c;)i=(n[Math.min(c,o+1)][0]-n[Math.max(0,o-1)][0])/(6*(1+a[o]*a[o])),u.push([i||0,a[o]*i||0]);
-return u}function ur(n){return n.length<3?Re(n):n[0]+$e(n,ir(n))}function ar(n,t,e,r){var i,u,a,o,c,l,f;return i=r[n],u=i[0],a=i[1],i=r[t],o=i[0],c=i[1],i=r[e],l=i[0],f=i[1],(f-a)*(o-u)-(c-a)*(l-u)>0}function or(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function cr(n,t,e,r){var i=n[0],u=e[0],a=t[0]-i,o=r[0]-u,c=n[1],l=e[1],f=t[1]-c,s=r[1]-l,h=(o*(c-l)-s*(i-u))/(s*a-o*f);return[i+h*a,c+h*f]}function lr(n,t){var e={list:n.map(function(n,t){return{index:t,x:n[0],y:n[1]}}).sort(function(n,t){return n.y<t.y?-1:n.y>t.y?1:n.x<t.x?-1:n.x>t.x?1:0}),bottomSite:null},r={list:[],leftEnd:null,rightEnd:null,init:function(){r.leftEnd=r.createHalfEdge(null,"l"),r.rightEnd=r.createHalfEdge(null,"l"),r.leftEnd.r=r.rightEnd,r.rightEnd.l=r.leftEnd,r.list.unshift(r.leftEnd,r.rightEnd)},createHalfEdge:function(n,t){return{edge:n,side:t,vertex:null,l:null,r:null}},insert:function(n,t){t.l=n,t.r=n.r,n.r.l=t,n.r=t},leftBound:function(n){var t=r.leftEnd;do t=t.r;while(t!=r.rightEnd
&&i.rightOf(t,n));return t=t.l},del:function(n){n.l.r=n.r,n.r.l=n.l,n.edge=null},right:function(n){return n.r},left:function(n){return n.l},leftRegion:function(n){return null==n.edge?e.bottomSite:n.edge.region[n.side]},rightRegion:function(n){return null==n.edge?e.bottomSite:n.edge.region[oc[n.side]]}},i={bisect:function(n,t){var e={region:{l:n,r:t},ep:{l:null,r:null}},r=t.x-n.x,i=t.y-n.y,u=r>0?r:-r,a=i>0?i:-i;return e.c=n.x*r+n.y*i+.5*(r*r+i*i),u>a?(e.a=1,e.b=i/r,e.c/=r):(e.b=1,e.a=r/i,e.c/=i),e},intersect:function(n,t){var e=n.edge,r=t.edge;if(!e||!r||e.region.r==r.region.r)return null;var i=e.a*r.b-e.b*r.a;if(Math.abs(i)<1e-10)return null;var u,a,o=(e.c*r.b-r.c*e.b)/i,c=(r.c*e.a-e.c*r.a)/i,l=e.region.r,f=r.region.r;l.y<f.y||l.y==f.y&&l.x<f.x?(u=n,a=e):(u=t,a=r);var s=o>=a.region.r.x;return s&&"l"===u.side||!s&&"r"===u.side?null:{x:o,y:c}},rightOf:function(n,t){var e=n.edge,r=e.region.r,i=t.x>r.x;if(i&&"l"===n.side)return 1;if(!i&&"r"===n.side)return 0;if(1===e.a){var u=t.
y-r.y,a=t.x-r.x,o=0,c=0;if(!i&&e.b<0||i&&e.b>=0?c=o=u>=e.b*a:(c=t.x+t.y*e.b>e.c,e.b<0&&(c=!c),c||(o=1)),!o){var l=r.x-e.region.l.x;c=e.b*(a*a-u*u)<l*u*(1+2*a/l+e.b*e.b),e.b<0&&(c=!c)}}else{var f=e.c-e.a*t.x,s=t.y-f,h=t.x-r.x,g=f-r.y;c=s*s>h*h+g*g}return"l"===n.side?c:!c},endPoint:function(n,e,r){n.ep[e]=r,n.ep[oc[e]]&&t(n)},distance:function(n,t){var e=n.x-t.x,r=n.y-t.y;return Math.sqrt(e*e+r*r)}},u={list:[],insert:function(n,t,e){n.vertex=t,n.ystar=t.y+e;for(var r=0,i=u.list,a=i.length;a>r;r++){var o=i[r];if(!(n.ystar>o.ystar||n.ystar==o.ystar&&t.x>o.vertex.x))break}i.splice(r,0,n)},del:function(n){for(var t=0,e=u.list,r=e.length;r>t&&e[t]!=n;++t);e.splice(t,1)},empty:function(){return 0===u.list.length},nextEvent:function(n){for(var t=0,e=u.list,r=e.length;r>t;++t)if(e[t]==n)return e[t+1];return null},min:function(){var n=u.list[0];return{x:n.vertex.x,y:n.ystar}},extractMin:function(){return u.list.shift()}};r.init(),e.bottomSite=e.list.shift();for(var a,o,c,l,f,s,h,g,p,d,
m,v,y,M=e.list.shift();;)if(u.empty()||(a=u.min()),M&&(u.empty()||M.y<a.y||M.y==a.y&&M.x<a.x))o=r.leftBound(M),c=r.right(o),h=r.rightRegion(o),v=i.bisect(h,M),s=r.createHalfEdge(v,"l"),r.insert(o,s),d=i.intersect(o,s),d&&(u.del(o),u.insert(o,d,i.distance(d,M))),o=s,s=r.createHalfEdge(v,"r"),r.insert(o,s),d=i.intersect(s,c),d&&u.insert(s,d,i.distance(d,M)),M=e.list.shift();else{if(u.empty())break;o=u.extractMin(),l=r.left(o),c=r.right(o),f=r.right(c),h=r.leftRegion(o),g=r.rightRegion(c),m=o.vertex,i.endPoint(o.edge,o.side,m),i.endPoint(c.edge,c.side,m),r.del(o),u.del(c),r.del(c),y="l",h.y>g.y&&(p=h,h=g,g=p,y="r"),v=i.bisect(h,g),s=r.createHalfEdge(v,y),r.insert(l,s),i.endPoint(v,oc[y],m),d=i.intersect(l,s),d&&(u.del(l),u.insert(l,d,i.distance(d,h))),d=i.intersect(s,f),d&&u.insert(s,d,i.distance(d,h))}for(o=r.right(r.leftEnd);o!=r.rightEnd;o=r.right(o))t(o.edge)}function fr(n){return n.x}function sr(n){return n.y}function hr(){return{leaf:!0,nodes:[],point:null,x:null,y:null}}
function gr(n,t,e,r,i,u){if(!n(t,e,r,i,u)){var a=.5*(e+i),o=.5*(r+u),c=t.nodes;c[0]&&gr(n,c[0],e,r,a,o),c[1]&&gr(n,c[1],a,r,i,o),c[2]&&gr(n,c[2],e,o,a,u),c[3]&&gr(n,c[3],a,o,i,u)}}function pr(n,t){n=va.rgb(n),t=va.rgb(t);var e=n.r,r=n.g,i=n.b,u=t.r-e,a=t.g-r,o=t.b-i;return function(n){return"#"+it(Math.round(e+u*n))+it(Math.round(r+a*n))+it(Math.round(i+o*n))}}function dr(n){var t=[n.a,n.b],e=[n.c,n.d],r=vr(t),i=mr(t,e),u=vr(yr(e,t,-i))||0;t[0]*e[1]<e[0]*t[1]&&(t[0]*=-1,t[1]*=-1,r*=-1,i*=-1),this.rotate=(r?Math.atan2(t[1],t[0]):Math.atan2(-e[0],e[1]))*Ga,this.translate=[n.e,n.f],this.scale=[r,u],this.skew=u?Math.atan2(i,u)*Ga:0}function mr(n,t){return n[0]*t[0]+n[1]*t[1]}function vr(n){var t=Math.sqrt(mr(n,n));return t&&(n[0]/=t,n[1]/=t),t}function yr(n,t,e){return n[0]+=e*t[0],n[1]+=e*t[1],n}function Mr(n,t){return t-=n=+n,function(e){return n+t*e}}function xr(n,t){var e,r=[],i=[],u=va.transform(n),a=va.transform(t),o=u.translate,c=a.translate,l=u.rotate,f=a.rotate,s=u.skew
,h=a.skew,g=u.scale,p=a.scale;return o[0]!=c[0]||o[1]!=c[1]?(r.push("translate(",null,",",null,")"),i.push({i:1,x:Mr(o[0],c[0])},{i:3,x:Mr(o[1],c[1])})):c[0]||c[1]?r.push("translate("+c+")"):r.push(""),l!=f?(l-f>180?f+=360:f-l>180&&(l+=360),i.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:Mr(l,f)})):f&&r.push(r.pop()+"rotate("+f+")"),s!=h?i.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:Mr(s,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),i.push({i:e-4,x:Mr(g[0],p[0])},{i:e-2,x:Mr(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=i.length,function(n){for(var t,u=-1;++u<e;)r[(t=i[u]).i]=t.x(n);return r.join("")}}function br(n,t){var e,r={},i={};for(e in n)e in t?r[e]=Sr(e)(n[e],t[e]):i[e]=n[e];for(e in t)e in n||(i[e]=t[e]);return function(n){for(e in r)i[e]=r[e](n);return i}}function _r(n,t){var e,r,i,u,a,o=0,c=0,l=[],f=[];for(n+="",t+="",lc.lastIndex=0,r=0;e=lc.exec(t);++r)e.index&&l.push(t.substrin
g(o,c=e.index)),f.push({i:l.length,x:e[0]}),l.push(null),o=lc.lastIndex;for(o<t.length&&l.push(t.substring(o)),r=0,u=f.length;(e=lc.exec(n))&&u>r;++r)if(a=f[r],a.x==e[0]){if(a.i)if(null==l[a.i+1])for(l[a.i-1]+=a.x,l.splice(a.i,1),i=r+1;u>i;++i)f[i].i--;else for(l[a.i-1]+=a.x+l[a.i+1],l.splice(a.i,2),i=r+1;u>i;++i)f[i].i-=2;else if(null==l[a.i+1])l[a.i]=a.x;else for(l[a.i]=a.x+l[a.i+1],l.splice(a.i+1,1),i=r+1;u>i;++i)f[i].i--;f.splice(r,1),u--,r--}else a.x=Mr(parseFloat(e[0]),parseFloat(a.x));for(;u>r;)a=f.pop(),null==l[a.i+1]?l[a.i]=a.x:(l[a.i]=a.x+l[a.i+1],l.splice(a.i+1,1)),u--;return 1===l.length?null==l[0]?(a=f[0].x,function(n){return a(n)+""}):function(){return t}:function(n){for(r=0;u>r;++r)l[(a=f[r]).i]=a.x(n);return l.join("")}}function wr(n,t){for(var e,r=va.interpolators.length;--r>=0&&!(e=va.interpolators[r](n,t)););return e}function Sr(n){return"transform"==n?xr:wr}function Er(n,t){var e,r=[],i=[],u=n.length,a=t.length,o=Math.min(n.length,t.length);for(e=0;o>e;++
e)r.push(wr(n[e],t[e]));for(;u>e;++e)i[e]=n[e];for(;a>e;++e)i[e]=t[e];return function(n){for(e=0;o>e;++e)i[e]=r[e](n);return i}}function kr(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function Ar(n){return function(t){return 1-n(1-t)}}function Nr(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function qr(n){return n*n}function Tr(n){return n*n*n}function Cr(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function zr(n){return function(t){return Math.pow(t,n)}}function Dr(n){return 1-Math.cos(n*Ba/2)}function jr(n){return Math.pow(2,10*(n-1))}function Lr(n){return 1-Math.sqrt(1-n*n)}function Hr(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/(2*Ba)*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,10*-r)*Math.sin(2*(r-e)*Ba/t)}}function Fr(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function Pr(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(
n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Or(n,t){n=va.hcl(n),t=va.hcl(t);var e=n.h,r=n.c,i=n.l,u=t.h-e,a=t.c-r,o=t.l-i;return isNaN(a)&&(a=0,r=isNaN(r)?t.c:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return $(e+u*n,r+a*n,i+o*n)+""}}function Rr(n,t){n=va.hsl(n),t=va.hsl(t);var e=n.h,r=n.s,i=n.l,u=t.h-e,a=t.s-r,o=t.l-i;return isNaN(a)&&(a=0,r=isNaN(r)?t.s:r),isNaN(u)?(u=0,e=isNaN(e)?t.h:e):u>180?u-=360:-180>u&&(u+=360),function(n){return O(e+u*n,r+a*n,i+o*n)+""}}function Yr(n,t){n=va.lab(n),t=va.lab(t);var e=n.l,r=n.a,i=n.b,u=t.l-e,a=t.a-r,o=t.b-i;return function(n){return G(e+u*n,r+a*n,i+o*n)+""}}function Ur(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Ir(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return(e-n)*t}}function Vr(n,t){return t=t-(n=+n)?1/(t-n):0,function(e){return Math.max(0,Math.min(1,(e-n)*t))}}function Xr(n){for(var t=n.source,e=n.target,r=Br(t,e),i=[t];t!==r;)t=t.parent,i.push(t);for(v
ar u=i.length;e!==r;)i.splice(u,0,e),e=e.parent;return i}function Zr(n){for(var t=[],e=n.parent;null!=e;)t.push(n),n=e,e=e.parent;return t.push(n),t}function Br(n,t){if(n===t)return n;for(var e=Zr(n),r=Zr(t),i=e.pop(),u=r.pop(),a=null;i===u;)a=i,i=e.pop(),u=r.pop();return a}function $r(n){n.fixed|=2}function Wr(n){n.fixed&=-7}function Jr(n){n.fixed|=4,n.px=n.x,n.py=n.y}function Gr(n){n.fixed&=-5}function Kr(n,t,e){var r=0,i=0;if(n.charge=0,!n.leaf)for(var u,a=n.nodes,o=a.length,c=-1;++c<o;)u=a[c],null!=u&&(Kr(u,t,e),n.charge+=u.charge,r+=u.charge*u.cx,i+=u.charge*u.cy);if(n.point){n.leaf||(n.point.x+=Math.random()-.5,n.point.y+=Math.random()-.5);var l=t*e[n.point.index];n.charge+=n.pointCharge=l,r+=l*n.point.x,i+=l*n.point.y}n.cx=r/n.charge,n.cy=i/n.charge}function Qr(n,t){return va.rebind(n,t,"sort","children","value"),n.nodes=n,n.links=ri,n}function ni(n){return n.children}function ti(n){return n.value}function ei(n,t){return t.value-n.value}function ri(n){return va.merge(
n.map(function(n){return(n.children||[]).map(function(t){return{source:n,target:t}})}))}function ii(n){return n.x}function ui(n){return n.y}function ai(n,t,e){n.y0=t,n.y=e}function oi(n){return va.range(n.length)}function ci(n){for(var t=-1,e=n[0].length,r=[];++t<e;)r[t]=0;return r}function li(n){for(var t,e=1,r=0,i=n[0][1],u=n.length;u>e;++e)(t=n[e][1])>i&&(r=e,i=t);return r}function fi(n){return n.reduce(si,0)}function si(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],i=(n[1]-r)/t,u=[];++e<=t;)u[e]=i*e+r;return u}function pi(n){return[va.min(n),va.max(n)]}function di(n,t){return n.parent==t.parent?1:2}function mi(n){var t=n.children;return t&&t.length?t[0]:n._tree.thread}function vi(n){var t,e=n.children;return e&&(t=e.length)?e[t-1]:n._tree.thread}function yi(n,t){var e=n.children;if(e&&(i=e.length))for(var r,i,u=-1;++u<i;)t(r=yi(e[u],t),n)>0&&(n=r);return n}function Mi(n,t){return n.x-t.x}fu
nction xi(n,t){return t.x-n.x}function bi(n,t){return n.depth-t.depth}function _i(n,t){function e(n,r){var i=n.children;if(i&&(a=i.length))for(var u,a,o=null,c=-1;++c<a;)u=i[c],e(u,o),o=u;t(n,r)}e(n,null)}function wi(n){for(var t,e=0,r=0,i=n.children,u=i.length;--u>=0;)t=i[u]._tree,t.prelim+=e,t.mod+=e,e+=t.shift+(r+=t.change)}function Si(n,t,e){n=n._tree,t=t._tree;var r=e/(t.number-n.number);n.change+=r,t.change-=r,t.shift+=e,t.prelim+=e,t.mod+=e}function Ei(n,t,e){return n._tree.ancestor.parent==t.parent?n._tree.ancestor:e}function ki(n,t){return n.value-t.value}function Ai(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function Ni(n,t){n._pack_next=t,t._pack_prev=n}function qi(n,t){var e=t.x-n.x,r=t.y-n.y,i=n.r+t.r;return.999*i*i>e*e+r*r}function Ti(n){function t(n){f=Math.min(n.x-n.r,f),s=Math.max(n.x+n.r,s),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,i,u,a,o,c,l,f=1/0,s=-1/0,h=1/0,g=-1/0;if(e.
forEach(Ci),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(i=e[1],i.x=i.r,i.y=0,t(i),l>2))for(u=e[2],ji(r,i,u),t(u),Ai(r,u),r._pack_prev=u,Ai(u,i),i=r._pack_next,a=3;l>a;a++){ji(r,i,u=e[a]);var p=0,d=1,m=1;for(o=i._pack_next;o!==i;o=o._pack_next,d++)if(qi(o,u)){p=1;break}if(1==p)for(c=r._pack_prev;c!==o._pack_prev&&!qi(c,u);c=c._pack_prev,m++);p?(m>d||d==m&&i.r<r.r?Ni(r,i=o):Ni(r=c,i),a--):(Ai(r,u),i=u,t(u))}var v=(f+s)/2,y=(h+g)/2,M=0;for(a=0;l>a;a++)u=e[a],u.x-=v,u.y-=y,M=Math.max(M,u.r+Math.sqrt(u.x*u.x+u.y*u.y));n.r=M,e.forEach(zi)}}function Ci(n){n._pack_next=n._pack_prev=n}function zi(n){delete n._pack_next,delete n._pack_prev}function Di(n,t,e,r){var i=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,i)for(var u=-1,a=i.length;++u<a;)Di(i[u],t,e,r)}function ji(n,t,e){var r=n.r+e.r,i=t.x-n.x,u=t.y-n.y;if(r&&(i||u)){var a=t.r+e.r,o=i*i+u*u;a*=a,r*=r;var c=.5+(r-a)/(2*o),l=Math.sqrt(Math.max(0,2*a*(r+o)-(r-=o)*r-a*a))/(2*o);e.x=n.x+c*i+l*u,e.y=n.y+c*u-l*i}else e.x=n.x+r,e.y=n.y}function
Li(n){return 1+va.max(n,function(n){return n.y})}function Hi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Fi(n){var t=n.children;return t&&t.length?Fi(t[0]):n}function Pi(n){var t,e=n.children;return e&&(t=e.length)?Pi(e[t-1]):n}function Oi(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Ri(n,t){var e=n.x+t[3],r=n.y+t[0],i=n.dx-t[1]-t[3],u=n.dy-t[0]-t[2];return 0>i&&(e+=i/2,i=0),0>u&&(r+=u/2,u=0),{x:e,y:r,dx:i,dy:u}}function Yi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Yi(n.range())}function Ii(n,t,e,r){var i=e(n[0],n[1]),u=r(t[0],t[1]);return function(n){return u(i(n))}}function Vi(n,t){var e,r=0,i=n.length-1,u=n[r],a=n[i];return u>a&&(e=r,r=i,i=e,e=u,u=a,a=e),n[r]=t.floor(u),n[i]=t.ceil(a),n}function Xi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:yc}function Zi(n,t,e,r){var i=[],u=[],a=0,o=Math.min(n.length,t.length)-1;for(n[o]<n[0]&
&(n=n.slice().reverse(),t=t.slice().reverse());++a<=o;)i.push(e(n[a-1],n[a])),u.push(r(t[a-1],t[a]));return function(t){var e=va.bisect(n,t,1,o)-1;return u[e](i[e](t))}}function Bi(n,t,e,r){function i(){var i=Math.min(n.length,t.length)>2?Zi:Ii,c=r?Vr:Ir;return a=i(n,t,c,e),o=i(t,n,c,wr),u}function u(n){return a(n)}var a,o;return u.invert=function(n){return o(n)},u.domain=function(t){return arguments.length?(n=t.map(Number),i()):n},u.range=function(n){return arguments.length?(t=n,i()):t},u.rangeRound=function(n){return u.range(n).interpolate(Ur)},u.clamp=function(n){return arguments.length?(r=n,i()):r},u.interpolate=function(n){return arguments.length?(e=n,i()):e},u.ticks=function(t){return Ki(n,t)},u.tickFormat=function(t,e){return Qi(n,t,e)},u.nice=function(t){return Wi(n,t),i()},u.copy=function(){return Bi(n,t,e,r)},i()}function $i(n,t){return va.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Wi(n,t){return Vi(n,Xi(t?Gi(n,t)[2]:Ji(n)))}function Ji(n){var
t=Yi(n),e=t[1]-t[0];return Math.pow(10,Math.round(Math.log(e)/Math.LN10)-1)}function Gi(n,t){var e=Yi(n),r=e[1]-e[0],i=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),u=t/r*i;return.15>=u?i*=10:.35>=u?i*=5:.75>=u&&(i*=2),e[0]=Math.ceil(e[0]/i)*i,e[1]=Math.floor(e[1]/i)*i+.5*i,e[2]=i,e}function Ki(n,t){return va.range.apply(va,Gi(n,t))}function Qi(n,t,e){var r=-Math.floor(Math.log(Gi(n,t)[2])/Math.LN10+.01);return va.format(e?e.replace(mo,function(n,t,e,i,u,a,o,c,l,f){return[t,e,i,u,a,o,c,l||"."+(r-2*("%"===f)),f].join("")}):",."+r+"f")}function nu(n,t,e,r,i){function u(t){return n(e(t))}return u.invert=function(t){return r(n.invert(t))},u.domain=function(t){return arguments.length?(t[0]<0?(e=ru,r=iu):(e=tu,r=eu),n.domain((i=t.map(Number)).map(e)),u):i},u.base=function(n){return arguments.length?(t=+n,u):t},u.nice=function(){function r(n){return Math.pow(t,Math.floor(Math.log(n)/Math.log(t)))}function a(n){return Math.pow(t,Math.ceil(Math.log(n)/Math.log(t)))}return n.domain
(Vi(i,e===tu?{floor:r,ceil:a}:{floor:function(n){return-a(-n)},ceil:function(n){return-r(-n)}}).map(e)),u},u.ticks=function(){var i=Yi(n.domain()),u=[];if(i.every(isFinite)){var a=Math.log(t),o=Math.floor(i[0]/a),c=Math.ceil(i[1]/a),l=r(i[0]),f=r(i[1]),s=t%1?2:t;if(e===ru)for(u.push(-Math.pow(t,-o));o++<c;)for(var h=s-1;h>0;h--)u.push(-Math.pow(t,-o)*h);else{for(;c>o;o++)for(var h=1;s>h;h++)u.push(Math.pow(t,o)*h);u.push(Math.pow(t,o))}for(o=0;u[o]<l;o++);for(c=u.length;u[c-1]>f;c--);u=u.slice(o,c)}return u},u.tickFormat=function(n,i){if(!arguments.length)return Mc;arguments.length<2?i=Mc:"function"!=typeof i&&(i=va.format(i));var a,o=Math.log(t),c=Math.max(.1,n/u.ticks().length),l=e===ru?(a=-1e-12,Math.floor):(a=1e-12,Math.ceil);return function(n){return n/r(o*l(e(n)/o+a))<=c?i(n):""}},u.copy=function(){return nu(n.copy(),t,e,r,i)},$i(u,n)}function tu(n){return Math.log(0>n?0:n)}function eu(n){return Math.exp(n)}function ru(n){return-Math.log(n>0?0:-n)}function iu(n){return
-Math.exp(-n)}function uu(n,t,e){function r(t){return n(i(t))}var i=au(t),u=au(1/t);return r.invert=function(t){return u(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(i)),r):e},r.ticks=function(n){return Ki(e,n)},r.tickFormat=function(n,t){return Qi(e,n,t)},r.nice=function(n){return r.domain(Wi(e,n))},r.exponent=function(a){return arguments.length?(i=au(t=a),u=au(1/t),n.domain(e.map(i)),r):t},r.copy=function(){return uu(n.copy(),t,e)},$i(r,n)}function au(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function ou(n,t){function e(t){return a[((u.get(t)||u.set(t,n.push(t)))-1)%a.length]}function r(t,e){return va.range(n.length).map(function(n){return t+e*n})}var u,a,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new i;for(var a,o=-1,c=r.length;++o<c;)u.has(a=r[o])||u.set(a,n.push(a));return e[t.t].apply(e,t.a)},e.range=function(n){return arguments.length?(a=n,o=0,t={t:"range",a:arguments},e):a},e.
rangePoints=function(i,u){arguments.length<2&&(u=0);var c=i[0],l=i[1],f=(l-c)/(Math.max(1,n.length-1)+u);return a=r(n.length<2?(c+l)/2:c+f*u/2,f),o=0,t={t:"rangePoints",a:arguments},e},e.rangeBands=function(i,u,c){arguments.length<2&&(u=0),arguments.length<3&&(c=u);var l=i[1]<i[0],f=i[l-0],s=i[1-l],h=(s-f)/(n.length-u+2*c);return a=r(f+h*c,h),l&&a.reverse(),o=h*(1-u),t={t:"rangeBands",a:arguments},e},e.rangeRoundBands=function(i,u,c){arguments.length<2&&(u=0),arguments.length<3&&(c=u);var l=i[1]<i[0],f=i[l-0],s=i[1-l],h=Math.floor((s-f)/(n.length-u+2*c)),g=s-f-(n.length-u)*h;return a=r(f+Math.round(g/2),h),l&&a.reverse(),o=Math.round(h*(1-u)),t={t:"rangeRoundBands",a:arguments},e},e.rangeBand=function(){return o},e.rangeExtent=function(){return Yi(t.a[0])},e.copy=function(){return ou(n,t)},e.domain(n)}function cu(n,t){function e(){var e=0,u=t.length;for(i=[];++e<u;)i[e-1]=va.quantile(n,e/u);return r}function r(n){return isNaN(n=+n)?void 0:t[va.bisect(i,n)]}var i;return r.dom
ain=function(t){return arguments.length?(n=t.filter(function(n){return!isNaN(n)}).sort(va.ascending),e()):n},r.range=function(n){return arguments.length?(t=n,e()):t},r.quantiles=function(){return i},r.copy=function(){return cu(n,t)},e()}function lu(n,t,e){function r(t){return e[Math.max(0,Math.min(a,Math.floor(u*(t-n))))]}function i(){return u=e.length/(t-n),a=e.length-1,r}var u,a;return r.domain=function(e){return arguments.length?(n=+e[0],t=+e[e.length-1],i()):[n,t]},r.range=function(n){return arguments.length?(e=n,i()):e},r.copy=function(){return lu(n,t,e)},r.invertExtent=function(t){return t=e.indexOf(t),t=0>t?0/0:t/u+n,[t,t+1/u]},i()}function fu(n,t){function e(e){return e>=e?t[va.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return fu(n,t)},e}function su(n){function t(n){return+n}return t.invert=t,
t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Ki(n,t)},t.tickFormat=function(t,e){return Qi(n,t,e)},t.copy=function(){return su(n)},t}function hu(n){return n.innerRadius}function gu(n){return n.outerRadius}function pu(n){return n.startAngle}function du(n){return n.endAngle}function mu(n){for(var t,e,r,i=-1,u=n.length;++i<u;)t=n[i],e=t[0],r=t[1]+Sc,t[0]=e*Math.cos(r),t[1]=e*Math.sin(r);return n}function vu(n){function t(t){function c(){d.push("M",o(n(v),s),f,l(n(m.reverse()),s),"Z")}for(var h,g,p,d=[],m=[],v=[],y=-1,M=t.length,x=ft(e),b=ft(i),_=e===r?function(){return g}:ft(r),w=i===u?function(){return p}:ft(u);++y<M;)a.call(this,h=t[y],y)?(m.push([g=+x.call(this,h,y),p=+b.call(this,h,y)]),v.push([+_.call(this,h,y),+w.call(this,h,y)])):m.length&&(c(),m=[],v=[]);return m.length&&c(),d.length?d.join(""):null}var e=Pe,r=Pe,i=0,u=Oe,a=Rt,o=Re,c=o.key,l=o,f="L",s=.7;return t.x=function(n){return arguments.length?(e=r=n,t):r},t.x0
=function(n){return arguments.length?(e=n,t):e},t.x1=function(n){return arguments.length?(r=n,t):r},t.y=function(n){return arguments.length?(i=u=n,t):u},t.y0=function(n){return arguments.length?(i=n,t):i},t.y1=function(n){return arguments.length?(u=n,t):u},t.defined=function(n){return arguments.length?(a=n,t):a},t.interpolate=function(n){return arguments.length?(c="function"==typeof n?o=n:(o=rc.get(n)||Re).key,l=o.reverse||o,f=o.closed?"M":"L",t):c},t.tension=function(n){return arguments.length?(s=n,t):s},t}function yu(n){return n.radius}function Mu(n){return[n.x,n.y]}function xu(n){return function(){var t=n.apply(this,arguments),e=t[0],r=t[1]+Sc;return[e*Math.cos(r),e*Math.sin(r)]}}function bu(){return 64}function _u(){return"circle"}function wu(n){var t=Math.sqrt(n/Ba);return"M0,"+t+"A"+t+","+t+" 0 1,1 0,"+-t+"A"+t+","+t+" 0 1,1 0,"+t+"Z"}function Su(n,t){return Ta(n,Tc),n.id=t,n}function Eu(n,t,e,r){var i=n.id;return q(n,"function"==typeof e?function(n,u,a){n.__transition
__[i].tween.set(t,r(e.call(n,n.__data__,u,a)))}:(e=r(e),function(n){n.__transition__[i].tween.set(t,e)}))}function ku(n){return null==n&&(n=""),function(){this.textContent=n}}function Au(n,t,e,r){var u=n.__transition__||(n.__transition__={active:0,count:0}),a=u[e];if(!a){var o=r.time;return a=u[e]={tween:new i,event:va.dispatch("start","end"),time:o,ease:r.ease,delay:r.delay,duration:r.duration},++u.count,va.timer(function(r){function i(r){return u.active>e?l():(u.active=e,h.start.call(n,f,t),a.tween.forEach(function(e,r){(r=r.call(n,f,t))&&d.push(r)}),c(r)||va.timer(c,0,o),1)}function c(r){if(u.active!==e)return l();for(var i=(r-g)/p,a=s(i),o=d.length;o>0;)d[--o].call(n,a);return i>=1?(l(),h.end.call(n,f,t),1):void 0}function l(){return--u.count?delete u[e]:delete n.__transition__,1}var f=n.__data__,s=a.ease,h=a.event,g=a.delay,p=a.duration,d=[];return r>=g?i(r):va.timer(i,g,o),1},0,o),a}}function Nu(n,t){n.attr("transform",function(n){return"translate("+t(n)+",0)"})}functi
on qu(n,t){n.attr("transform",function(n){return"translate(0,"+t(n)+")"})}function Tu(n,t,e){if(r=[],e&&t.length>1){for(var r,i,u,a=Yi(n.domain()),o=-1,c=t.length,l=(t[1]-t[0])/++e;++o<c;)for(i=e;--i>0;)(u=+t[o]-i*l)>=a[0]&&r.push(u);for(--o,i=0;++i<e&&(u=+t[o]+i*l)<a[1];)r.push(u)}return r}function Cu(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function zu(n,t,e){function r(t){var e=n(t),r=u(e,1);return r-t>t-e?e:r}function i(e){return t(e=n(new Fc(e-1)),1),e}function u(n,e){return t(n=new Fc(+n),e),n}function a(n,r,u){var a=i(n),o=[];if(u>1)for(;r>a;)e(a)%u||o.push(new Date(+a)),t(a,1);else for(;r>a;)o.push(new Date(+a)),t(a,1);return o}function o(n,t,e){try{Fc=Cu;var r=new Cu;return r._=n,a(r,t,e)}finally{Fc=Date}}n.floor=n,n.round=r,n.ceil=i,n.offset=u,n.range=a;var c=n.utc=Du(n);return c.floor=c,c.round=Du(r),c.ceil=Du(i),c.offset=Du(u),c.range=o,n}function Du(n){return function(t,e){try{Fc=Cu;var r=new Cu;return r._=t,n(r,e)._}fina
lly{Fc=Date}}}function ju(n,t,e,r){for(var i,u,a=0,o=t.length,c=e.length;o>a;){if(r>=c)return-1;if(i=t.charCodeAt(a++),37===i){if(u=il[t.charAt(a++)],!u||(r=u(n,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}function Lu(n){return new RegExp("^(?:"+n.map(va.requote).join("|")+")","i")}function Hu(n){for(var t=new i,e=-1,r=n.length;++e<r;)t.set(n[e].toLowerCase(),e);return t}function Fu(n,t,e){var r=0>n?"-":"",i=(r?-n:n)+"",u=i.length;return r+(e>u?new Array(e-u+1).join(t)+i:i)}function Pu(n,t,e){Wc.lastIndex=0;var r=Wc.exec(t.substring(e));return r?(n.w=Jc.get(r[0].toLowerCase()),e+r[0].length):-1}function Ou(n,t,e){Bc.lastIndex=0;var r=Bc.exec(t.substring(e));return r?(n.w=$c.get(r[0].toLowerCase()),e+r[0].length):-1}function Ru(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+1));return r?(n.w=+r[0],e+r[0].length):-1}function Yu(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e));return r?(n.U=+r[0],e+r[0].length):-1}function Uu(n,t,e){ul.lastIndex=0;var
r=ul.exec(t.substring(e));return r?(n.W=+r[0],e+r[0].length):-1}function Iu(n,t,e){Qc.lastIndex=0;var r=Qc.exec(t.substring(e));return r?(n.m=nl.get(r[0].toLowerCase()),e+r[0].length):-1}function Vu(n,t,e){Gc.lastIndex=0;var r=Gc.exec(t.substring(e));return r?(n.m=Kc.get(r[0].toLowerCase()),e+r[0].length):-1}function Xu(n,t,e){return ju(n,rl.c.toString(),t,e)}function Zu(n,t,e){return ju(n,rl.x.toString(),t,e)}function Bu(n,t,e){return ju(n,rl.X.toString(),t,e)}function $u(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+4));return r?(n.y=+r[0],e+r[0].length):-1}function Wu(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.y=Ju(+r[0]),e+r[0].length):-1}function Ju(n){return n+(n>68?1900:2e3)}function Gu(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Ku(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function Qu(n,t,e){ul.lastIndex=0;var r=ul.exec(t.sub
string(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function na(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ta(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function ea(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ra(n,t,e){ul.lastIndex=0;var r=ul.exec(t.substring(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ia(n,t,e){var r=al.get(t.substring(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}function ua(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=~~(Math.abs(t)/60),i=Math.abs(t)%60;return e+Fu(r,"0",2)+Fu(i,"0",2)}function aa(n,t,e){tl.lastIndex=0;var r=tl.exec(t.substring(e,e+1));return r?e+r[0].length:-1}function oa(n){return n.toISOString()}function ca(n,t,e){function r(t){return n(t)}return r.invert=function(t){return la(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain(t),r)
:n.domain().map(la)},r.nice=function(n){return r.domain(Vi(r.domain(),n))},r.ticks=function(e,i){var u=Yi(r.domain());if("function"!=typeof e){var a=u[1]-u[0],o=a/e,c=va.bisect(cl,o);if(c==cl.length)return t.year(u,e);if(!c)return n.ticks(e).map(la);Math.log(o/cl[c-1])<Math.log(cl[c]/o)&&--c,e=t[c],i=e[1],e=e[0].range}return e(u[0],new Date(+u[1]+1),i)},r.tickFormat=function(){return e},r.copy=function(){return ca(n.copy(),t,e)},$i(r,n)}function la(n){return new Date(n)}function fa(n){return function(t){for(var e=n.length-1,r=n[e];!r[1](t);)r=n[--e];return r[0](t)}}function sa(n){var t=new Date(n,0,1);return t.setFullYear(n),t}function ha(n){var t=n.getFullYear(),e=sa(t),r=sa(t+1);return t+(n-e)/(r-e)}function ga(n){var t=new Date(Date.UTC(n,0,1));return t.setUTCFullYear(n),t}function pa(n){var t=n.getUTCFullYear(),e=ga(t),r=ga(t+1);return t+(n-e)/(r-e)}function da(n){return JSON.parse(n.responseText)}function ma(n){var t=ya.createRange();return t.selectNode(ya.body),t.creat
eContextualFragment(n.responseText)}var va={version:"3.2.2"};Date.now||(Date.now=function(){return+new Date});var ya=document,Ma=ya.documentElement,xa=window;try{ya.createElement("div").style.setProperty("opacity",0,"")}catch(ba){var _a=xa.CSSStyleDeclaration.prototype,wa=_a.setProperty;_a.setProperty=function(n,t,e){wa.call(this,n,t+"",e)}}va.ascending=function(n,t){return t>n?-1:n>t?1:n>=t?0:0/0},va.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},va.min=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u&&!(null!=(e=n[i])&&e>=e);)e=void 0;for(;++i<u;)null!=(r=n[i])&&e>r&&(e=r)}else{for(;++i<u&&!(null!=(e=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<u;)null!=(r=t.call(n,n[i],i))&&e>r&&(e=r)}return e},va.max=function(n,t){var e,r,i=-1,u=n.length;if(1===arguments.length){for(;++i<u&&!(null!=(e=n[i])&&e>=e);)e=void 0;for(;++i<u;)null!=(r=n[i])&&r>e&&(e=r)}else{for(;++i<u&&!(null!=(e=t.call(n,n[i],i))&&e>=e);)e=void 0;for(;++i<u;)null!=(r=t.call(n,
n[i],i))&&r>e&&(e=r)}return e},va.extent=function(n,t){var e,r,i,u=-1,a=n.length;if(1===arguments.length){for(;++u<a&&!(null!=(e=i=n[u])&&e>=e);)e=i=void 0;for(;++u<a;)null!=(r=n[u])&&(e>r&&(e=r),r>i&&(i=r))}else{for(;++u<a&&!(null!=(e=i=t.call(n,n[u],u))&&e>=e);)e=void 0;for(;++u<a;)null!=(r=t.call(n,n[u],u))&&(e>r&&(e=r),r>i&&(i=r))}return[e,i]},va.sum=function(n,t){var e,r=0,i=n.length,u=-1;if(1===arguments.length)for(;++u<i;)isNaN(e=+n[u])||(r+=e);else for(;++u<i;)isNaN(e=+t.call(n,n[u],u))||(r+=e);return r},va.mean=function(t,e){var r,i=t.length,u=0,a=-1,o=0;if(1===arguments.length)for(;++a<i;)n(r=t[a])&&(u+=(r-u)/++o);else for(;++a<i;)n(r=e.call(t,t[a],a))&&(u+=(r-u)/++o);return o?u:void 0},va.quantile=function(n,t){var e=(n.length-1)*t+1,r=Math.floor(e),i=+n[r-1],u=e-r;return u?i+u*(n[r]-i):i},va.median=function(t,e){return arguments.length>1&&(t=t.map(e)),t=t.filter(n),t.length?va.quantile(t.sort(va.ascending),.5):void 0},va.bisector=function(n){return{left:function(
t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;n.call(t,t[u],u)<e?r=u+1:i=u}return r},right:function(t,e,r,i){for(arguments.length<3&&(r=0),arguments.length<4&&(i=t.length);i>r;){var u=r+i>>>1;e<n.call(t,t[u],u)?i=u:r=u+1}return r}}};var Sa=va.bisector(function(n){return n});va.bisectLeft=Sa.left,va.bisect=va.bisectRight=Sa.right,va.shuffle=function(n){for(var t,e,r=n.length;r;)e=0|Math.random()*r--,t=n[r],n[r]=n[e],n[e]=t;return n},va.permute=function(n,t){for(var e=[],r=-1,i=t.length;++r<i;)e[r]=n[t[r]];return e},va.zip=function(){if(!(i=arguments.length))return[];for(var n=-1,e=va.min(arguments,t),r=new Array(e);++n<e;)for(var i,u=-1,a=r[n]=new Array(i);++u<i;)a[u]=arguments[u][n];return r},va.transpose=function(n){return va.zip.apply(va,n)},va.keys=function(n){var t=[];for(var e in n)t.push(e);return t},va.values=function(n){var t=[];for(var e in n)t.push(n[e]);return t},va.entries=function(n){var t=[];for(var e in n)t.push({k
ey:e,value:n[e]});return t},va.merge=function(n){return Array.prototype.concat.apply([],n)},va.range=function(n,t,r){if(arguments.length<3&&(r=1,arguments.length<2&&(t=n,n=0)),1/0===(t-n)/r)throw new Error("infinite range");var i,u=[],a=e(Math.abs(r)),o=-1;if(n*=a,t*=a,r*=a,0>r)for(;(i=n+r*++o)>t;)u.push(i/a);else for(;(i=n+r*++o)<t;)u.push(i/a);return u},va.map=function(n){var t=new i;for(var e in n)t.set(e,n[e]);return t},r(i,{has:function(n){return Ea+n in this},get:function(n){return this[Ea+n]},set:function(n,t){return this[Ea+n]=t},remove:function(n){return n=Ea+n,n in this&&delete this[n]},keys:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},values:function(){var n=[];return this.forEach(function(t,e){n.push(e)}),n},entries:function(){var n=[];return this.forEach(function(t,e){n.push({key:t,value:e})}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===ka&&n.call(this,t.substring(1),this[t])}});var Ea="\0",ka=Ea.charCodeAt(0);va.nest=functio
n(){function n(t,o,c){if(c>=a.length)return r?r.call(u,o):e?o.sort(e):o;for(var l,f,s,h,g=-1,p=o.length,d=a[c++],m=new i;++g<p;)(h=m.get(l=d(f=o[g])))?h.push(f):m.set(l,[f]);return t?(f=t(),s=function(e,r){f.set(e,n(t,r,c))}):(f={},s=function(e,r){f[e]=n(t,r,c)}),m.forEach(s),f}function t(n,e){if(e>=a.length)return n;var r=[],i=o[e++];return n.forEach(function(n,i){r.push({key:n,values:t(i,e)})
-}),i?r.sort(function(n,t){return i(n.key,t.key)}):r}var e,r,u={},a=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(va.map,e,0),0)},u.key=function(n){return a.push(n),u},u.sortKeys=function(n){return o[a.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},va.set=function(n){var t=new u;if(n)for(var e=0;e<n.length;e++)t.add(n[e]);return t},r(u,{has:function(n){return Ea+n in this},add:function(n){return this[Ea+n]=!0,n},remove:function(n){return n=Ea+n,n in this&&delete this[n]},values:function(){var n=[];return this.forEach(function(t){n.push(t)}),n},forEach:function(n){for(var t in this)t.charCodeAt(0)===ka&&n.call(this,t.substring(1))}}),va.behavior={},va.rebind=function(n,t){for(var e,r=1,i=arguments.length;++r<i;)n[e=arguments[r]]=a(n,t,t[e]);return n};var Aa=["webkit","ms","moz","Moz","o","O"],Na=l;try{Na(Ma.childNodes)[0].nodeType}catch(qa){Na=c}var Ta=[].__proto__?function(n,t){n.__proto__=t}:
function(n,t){for(var e in t)n[e]=t[e]};va.dispatch=function(){for(var n=new s,t=-1,e=arguments.length;++t<e;)n[arguments[t]]=h(n);return n},s.prototype.on=function(n,t){var e=n.indexOf("."),r="";if(e>=0&&(r=n.substring(e+1),n=n.substring(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},va.event=null,va.requote=function(n){return n.replace(Ca,"\\$&")};var Ca=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,za=function(n,t){return t.querySelector(n)},Da=function(n,t){return t.querySelectorAll(n)},ja=Ma[o(Ma,"matchesSelector")],La=function(n,t){return ja.call(n,t)};"function"==typeof Sizzle&&(za=function(n,t){return Sizzle(n,t)[0]||null},Da=function(n,t){return Sizzle.uniqueSort(Sizzle(n,t))},La=Sizzle.matchesSelector),va.selection=function(){return Oa};var Ha=va.selection.prototype=[];Ha.select=function(n){var t,e,r,i,u=[];"function"!=typeof n&&(n=v(n));for(var a=-1,o=th
is.length;++a<o;){u.push(t=[]),t.parentNode=(r=this[a]).parentNode;for(var c=-1,l=r.length;++c<l;)(i=r[c])?(t.push(e=n.call(i,i.__data__,c)),e&&"__data__"in i&&(e.__data__=i.__data__)):t.push(null)}return m(u)},Ha.selectAll=function(n){var t,e,r=[];"function"!=typeof n&&(n=y(n));for(var i=-1,u=this.length;++i<u;)for(var a=this[i],o=-1,c=a.length;++o<c;)(e=a[o])&&(r.push(t=Na(n.call(e,e.__data__,o))),t.parentNode=e);return m(r)};var Fa={svg:"http://www.w3.org/2000/svg",xhtml:"http://www.w3.org/1999/xhtml",xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};va.ns={prefix:Fa,qualify:function(n){var t=n.indexOf(":"),e=n;return t>=0&&(e=n.substring(0,t),n=n.substring(t+1)),Fa.hasOwnProperty(e)?{space:Fa[e],local:n}:n}},Ha.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=va.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(M(t,n
[t]));return this}return this.each(M(n,t))},Ha.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=n.trim().split(/^|\s+/g)).length,i=-1;if(t=e.classList){for(;++i<r;)if(!t.contains(n[i]))return!1}else for(t=e.getAttribute("class");++i<r;)if(!b(n[i]).test(t))return!1;return!0}for(t in n)this.each(_(t,n[t]));return this}return this.each(_(n,t))},Ha.style=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t="");for(e in n)this.each(S(e,n[e],t));return this}if(2>r)return xa.getComputedStyle(this.node(),null).getPropertyValue(n);e=""}return this.each(S(n,t,e))},Ha.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(E(t,n[t]));return this}return this.each(E(n,t))},Ha.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){
this.textContent=n}):this.node().textContent},Ha.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},Ha.append=function(n){function t(){return this.appendChild(ya.createElementNS(this.namespaceURI,n))}function e(){return this.appendChild(ya.createElementNS(n.space,n.local))}return n=va.ns.qualify(n),this.select(n.local?e:t)},Ha.insert=function(n,t){function e(e,r){return this.insertBefore(ya.createElementNS(this.namespaceURI,n),t.call(this,e,r))}function r(e,r){return this.insertBefore(ya.createElementNS(n.space,n.local),t.call(this,e,r))}return n=va.ns.qualify(n),"function"!=typeof t&&(t=v(t)),this.select(n.local?r:e)},Ha.remove=function(){return this.each(function(){var n=this.parentNode;n&&n.removeChild(this)})},Ha.data=function(n,t){function e(n,e){var r,u,a,o=n.length,s=e.length,h=Math.min(o,
s),g=new Array(s),p=new Array(s),d=new Array(o);if(t){var m,v=new i,y=new i,M=[];for(r=-1;++r<o;)m=t.call(u=n[r],u.__data__,r),v.has(m)?d[r]=u:v.set(m,u),M.push(m);for(r=-1;++r<s;)m=t.call(e,a=e[r],r),(u=v.get(m))?(g[r]=u,u.__data__=a):y.has(m)||(p[r]=k(a)),y.set(m,a),v.remove(m);for(r=-1;++r<o;)v.has(M[r])&&(d[r]=n[r])}else{for(r=-1;++r<h;)u=n[r],a=e[r],u?(u.__data__=a,g[r]=u):p[r]=k(a);for(;s>r;++r)p[r]=k(e[r]);for(;o>r;++r)d[r]=n[r]}p.update=g,p.parentNode=g.parentNode=d.parentNode=n.parentNode,c.push(p),l.push(g),f.push(d)}var r,u,a=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++a<o;)(u=r[a])&&(n[a]=u.__data__);return n}var c=T([]),l=m([]),f=m([]);if("function"==typeof n)for(;++a<o;)e(r=this[a],n.call(r,r.parentNode.__data__,a));else for(;++a<o;)e(r=this[a],n);return l.enter=function(){return c},l.exit=function(){return f},l},Ha.datum=function(n){return arguments.length?this.property("__data__",n):this.property("__data__")},Ha.filter=funct
ion(n){var t,e,r,i=[];"function"!=typeof n&&(n=A(n));for(var u=0,a=this.length;a>u;u++){i.push(t=[]),t.parentNode=(e=this[u]).parentNode;for(var o=0,c=e.length;c>o;o++)(r=e[o])&&n.call(r,r.__data__,o)&&t.push(r)}return m(i)},Ha.order=function(){for(var n=-1,t=this.length;++n<t;)for(var e,r=this[n],i=r.length-1,u=r[i];--i>=0;)(e=r[i])&&(u&&u!==e.nextSibling&&u.parentNode.insertBefore(e,u),u=e);return this},Ha.sort=function(n){n=N.apply(this,arguments);for(var t=-1,e=this.length;++t<e;)this[t].sort(n);return this.order()},Ha.each=function(n){return q(this,function(t,e,r){n.call(t,t.__data__,e,r)})},Ha.call=function(n){var t=Na(arguments);return n.apply(t[0]=this,t),this},Ha.empty=function(){return!this.node()},Ha.node=function(){for(var n=0,t=this.length;t>n;n++)for(var e=this[n],r=0,i=e.length;i>r;r++){var u=e[r];if(u)return u}return null},Ha.size=function(){var n=0;return this.each(function(){++n}),n};var Pa=[];va.selection.enter=T,va.selection.enter.prototype=Pa,Pa.append=H
a.append,Pa.insert=Ha.insert,Pa.empty=Ha.empty,Pa.node=Ha.node,Pa.call=Ha.call,Pa.size=Ha.size,Pa.select=function(n){for(var t,e,r,i,u,a=[],o=-1,c=this.length;++o<c;){r=(i=this[o]).update,a.push(t=[]),t.parentNode=i.parentNode;for(var l=-1,f=i.length;++l<f;)(u=i[l])?(t.push(r[l]=e=n.call(i.parentNode,u.__data__,l)),e.__data__=u.__data__):t.push(null)}return m(a)},Ha.transition=function(){var n,t,e=Ac||++Cc,r=[],i=Object.create(zc);i.time=Date.now();for(var u=-1,a=this.length;++u<a;){r.push(n=[]);for(var o=this[u],c=-1,l=o.length;++c<l;)(t=o[c])&&Au(t,c,e,i),n.push(t)}return Su(r,e)},va.select=function(n){var t=["string"==typeof n?za(n,ya):n];return t.parentNode=Ma,m([t])},va.selectAll=function(n){var t=Na("string"==typeof n?Da(n,ya):n);return t.parentNode=Ma,m([t])};var Oa=va.select(Ma);Ha.on=function(n,t,e){var r=arguments.length;if(3>r){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(C(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return t
his.each(C(n,t,e))};var Ra=va.map({mouseenter:"mouseover",mouseleave:"mouseout"});Ra.forEach(function(n){"on"+n in ya&&Ra.remove(n)});var Ya=o(Ma.style,"userSelect");va.mouse=function(n){return L(n,p())};var Ua=/WebKit/.test(xa.navigator.userAgent)?-1:0;va.touches=function(n,t){return arguments.length<2&&(t=p().touches),t?Na(t).map(function(t){var e=L(n,t);return e.identifier=t.identifier,e}):[]},va.behavior.drag=function(){function n(){this.on("mousedown.drag",t).on("touchstart.drag",t)}function t(){function n(){var n=a.parentNode;return null!=l?va.touches(n).filter(function(n){return n.identifier===l})[0]:va.mouse(n)}function t(){if(!a.parentNode)return i();var t=n(),e=t[0]-f[0],r=t[1]-f[1];s|=e|r,f=t,o({type:"drag",x:t[0]+u[0],y:t[1]+u[1],dx:e,dy:r})}function i(){g.on(null!=l?"touchmove.drag-"+l:"mousemove.drag",null).on(null!=l?"touchend.drag-"+l:"mouseup.drag",null),h(s&&va.event.target===c),o({type:"dragend"})}var u,a=this,o=e.of(a,arguments),c=va.event.target,l=va.eve
nt.touches?va.event.changedTouches[0].identifier:null,f=n(),s=0,h=j(null!=l?"drag-"+l:"drag"),g=va.select(xa).on(null!=l?"touchmove.drag-"+l:"mousemove.drag",t).on(null!=l?"touchend.drag-"+l:"mouseup.drag",i,!0);r?(u=r.apply(a,arguments),u=[u.x-f[0],u.y-f[1]]):u=[0,0],o({type:"dragstart"})}var e=d(n,"drag","dragstart","dragend"),r=null;return n.origin=function(t){return arguments.length?(r=t,n):r},va.rebind(n,e,"on")},va.behavior.zoom=function(){function n(){this.on("mousedown.zoom",o).on("mousemove.zoom",l).on(Xa+".zoom",c).on("dblclick.zoom",f).on("touchstart.zoom",s).on("touchmove.zoom",h).on("touchend.zoom",s)}function t(n){return[(n[0]-_[0])/w,(n[1]-_[1])/w]}function e(n){return[n[0]*w+_[0],n[1]*w+_[1]]}function r(n){w=Math.max(S[0],Math.min(S[1],n))}function i(n,t){t=e(t),_[0]+=n[0]-t[0],_[1]+=n[1]-t[1]}function u(){y&&y.domain(v.range().map(function(n){return(n-_[0])/w}).map(v.invert)),x&&x.domain(M.range().map(function(n){return(n-_[1])/w}).map(M.invert))}function a(
n){u(),va.event.preventDefault(),n({type:"zoom",scale:w,translate:_})}function o(){function n(){c=1,i(va.mouse(r),f),a(u)}function e(){l.on("mousemove.zoom",null).on("mouseup.zoom",null),s(c&&va.event.target===o)}var r=this,u=E.of(r,arguments),o=va.event.target,c=0,l=va.select(xa).on("mousemove.zoom",n).on("mouseup.zoom",e),f=t(va.mouse(r)),s=j("zoom")}function c(){g||(g=t(va.mouse(this))),r(Math.pow(2,.002*Ia())*w),i(va.mouse(this),g),a(E.of(this,arguments))}function l(){g=null}function f(){var n=va.mouse(this),e=t(n),u=Math.log(w)/Math.LN2;r(Math.pow(2,va.event.shiftKey?Math.ceil(u)-1:Math.floor(u)+1)),i(n,e),a(E.of(this,arguments))}function s(){var n=va.touches(this),e=Date.now();if(m=w,g={},p=0,n.forEach(function(n){g[n.identifier]=t(n)}),1===n.length){if(500>e-b){var u=n[0],o=t(n[0]);r(2*w),i(u,o),a(E.of(this,arguments))}b=e}else if(n.length>1){var u=n[0],c=n[1],l=u[0]-c[0],f=u[1]-c[1];p=l*l+f*f}}function h(){var n=va.touches(this),t=n[0],e=g[t.identifier];if(u=n[1]){va
r u,o=g[u.identifier],c=va.event.scale;if(null==c){var l=(l=u[0]-t[0])*l+(l=u[1]-t[1])*l;c=p&&Math.sqrt(l/p)}t=[(t[0]+u[0])/2,(t[1]+u[1])/2],e=[(e[0]+o[0])/2,(e[1]+o[1])/2],r(c*m)}i(t,e),b=null,a(E.of(this,arguments))}var g,p,m,v,y,M,x,b,_=[0,0],w=1,S=Va,E=d(n,"zoom");return n.translate=function(t){return arguments.length?(_=t.map(Number),u(),n):_},n.scale=function(t){return arguments.length?(w=+t,u(),n):w},n.scaleExtent=function(t){return arguments.length?(S=null==t?Va:t.map(Number),n):S},n.x=function(t){return arguments.length?(y=t,v=t.copy(),_=[0,0],w=1,n):y},n.y=function(t){return arguments.length?(x=t,M=t.copy(),_=[0,0],w=1,n):x},va.rebind(n,E,"on")};var Ia,Va=[0,1/0],Xa="onwheel"in ya?(Ia=function(){return-va.event.deltaY*(va.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ya?(Ia=function(){return va.event.wheelDelta},"mousewheel"):(Ia=function(){return-va.event.detail},"MozMousePixelScroll");H.prototype.toString=function(){return this.rgb()+""},va.hsl=function(n,t,e
){return 1===arguments.length?n instanceof P?F(n.h,n.s,n.l):ut(""+n,at,F):F(+n,+t,+e)};var Za=P.prototype=new H;Za.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),F(this.h,this.s,this.l/n)},Za.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),F(this.h,this.s,n*this.l)},Za.rgb=function(){return O(this.h,this.s,this.l)};var Ba=Math.PI,$a=1e-6,Wa=$a*$a,Ja=Ba/180,Ga=180/Ba;va.hcl=function(n,t,e){return 1===arguments.length?n instanceof B?Z(n.h,n.c,n.l):n instanceof J?K(n.l,n.a,n.b):K((n=ot((n=va.rgb(n)).r,n.g,n.b)).l,n.a,n.b):Z(+n,+t,+e)};var Ka=B.prototype=new H;Ka.brighter=function(n){return Z(this.h,this.c,Math.min(100,this.l+Qa*(arguments.length?n:1)))},Ka.darker=function(n){return Z(this.h,this.c,Math.max(0,this.l-Qa*(arguments.length?n:1)))},Ka.rgb=function(){return $(this.h,this.c,this.l).rgb()},va.lab=function(n,t,e){return 1===arguments.length?n instanceof J?W(n.l,n.a,n.b):n instanceof B?$(n.l,n.c,n.h):ot((n=va.rgb(n)).r,n.g,n.b):W(+n,+t,+e)}
;var Qa=18,no=.95047,to=1,eo=1.08883,ro=J.prototype=new H;ro.brighter=function(n){return W(Math.min(100,this.l+Qa*(arguments.length?n:1)),this.a,this.b)},ro.darker=function(n){return W(Math.max(0,this.l-Qa*(arguments.length?n:1)),this.a,this.b)},ro.rgb=function(){return G(this.l,this.a,this.b)},va.rgb=function(n,t,e){return 1===arguments.length?n instanceof rt?et(n.r,n.g,n.b):ut(""+n,et,O):et(~~n,~~t,~~e)};var io=rt.prototype=new H;io.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,i=30;return t||e||r?(t&&i>t&&(t=i),e&&i>e&&(e=i),r&&i>r&&(r=i),et(Math.min(255,Math.floor(t/n)),Math.min(255,Math.floor(e/n)),Math.min(255,Math.floor(r/n)))):et(i,i,i)},io.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),et(Math.floor(n*this.r),Math.floor(n*this.g),Math.floor(n*this.b))},io.hsl=function(){return at(this.r,this.g,this.b)},io.toString=function(){return"#"+it(this.r)+it(this.g)+it(this.b)};var uo=va.map({aliceblue:"#f0f8ff",antique
white:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#
f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f
5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"});uo.forEach(function(n,t){uo.set(n,ut(t,et,O))}),va.functor=ft,va.x
hr=ht(st),va.dsv=function(n,t){function e(n,e,u){arguments.length<3&&(u=e,e=null);var a=va.xhr(n,t,u);return a.row=function(n){return arguments.length?a.response(null==(e=n)?r:i(n)):e},a.row(e)}function r(n){return e.parse(n.responseText)}function i(n){return function(t){return e.parse(t.responseText,n)}}function a(t){return t.map(o).join(n)}function o(n){return c.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var c=new RegExp('["'+n+"\n]"),l=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var i=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(i(n),e)}:i})},e.parseRows=function(n,t){function e(){if(f>=c)return a;if(i)return i=!1,u;var t=f;if(34===n.charCodeAt(t)){for(var e=t;e++<c;)if(34===n.charCodeAt(e)){if(34!==n.charCodeAt(e+1))break;++e}f=e+2;var r=n.charCodeAt(e+1);return 13===r?(i=!0,10===n.charCodeAt(e+2)&&++f):10===r&&(i=!0),n.substrin
g(t+1,e).replace(/""/g,'"')}for(;c>f;){var r=n.charCodeAt(f++),o=1;if(10===r)i=!0;else if(13===r)i=!0,10===n.charCodeAt(f)&&(++f,++o);else if(r!==l)continue;return n.substring(t,f-o)}return n.substring(t)}for(var r,i,u={},a={},o=[],c=n.length,f=0,s=0;(r=e())!==a;){for(var h=[];r!==u&&r!==a;)h.push(r),r=e();(!t||(h=t(h,s++)))&&o.push(h)}return o},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new u,i=[];return t.forEach(function(n){for(var t in n)r.has(t)||i.push(r.add(t))}),[i.map(o).join(n)].concat(t.map(function(t){return i.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(a).join("\n")},e},va.csv=va.dsv(",","text/csv"),va.tsv=va.dsv(" ","text/tab-separated-values");var ao,oo,co,lo;va.timer=function(n,t,e){if(arguments.length<3){if(arguments.length<2)t=0;else if(!isFinite(t))return;e=Date.now()}var r=e+t,i={callback:n,time:r,next:null};oo?oo.next=i:ao=i,oo=i,co||(lo=clearTimeout(lo),co=1,fo(dt))},va.time
r.flush=function(){mt(),vt()};var fo=xa[o(xa,"requestAnimationFrame")]||function(n){setTimeout(n,17)},so=".",ho=",",go=[3,3],po=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"].map(yt);va.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=va.round(n,Mt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((0>=e?e+1:e-1)/3)))),po[8+e/3]},va.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)},va.format=function(n){var t=mo.exec(n),e=t[1]||" ",r=t[2]||">",i=t[3]||"",u=t[4]||"",a=t[5],o=+t[6],c=t[7],l=t[8],f=t[9],s=1,h="",g=!1;switch(l&&(l=+l.substring(1)),(a||"0"===e&&"="===r)&&(a=e="0",r="=",c&&(o-=Math.floor((o-1)/4))),f){case"n":c=!0,f="g";break;case"%":s=100,h="%",f="f";break;case"p":s=100,h="%",f="r";break;case"b":case"o":case"x":case"X":u&&(u="0"+f.toLowerCase());case"c":case"d":g=!0,l=0;break;case"s":s=-1,f="r"}"#"===u&&(u=""),"r"!=f||l||(f="g"),null!=l&&("g"==f?l=Math.max(
1,Math.min(21,l)):("e"==f||"f"==f)&&(l=Math.max(0,Math.min(20,l)))),f=vo.get(f)||xt;var p=a&&c;return function(n){if(g&&n%1)return"";var t=0>n||0===n&&0>1/n?(n=-n,"-"):i;if(0>s){var d=va.formatPrefix(n,l);n=d.scale(n),h=d.symbol}else n*=s;n=f(n,l),!a&&c&&(n=yo(n));var m=u.length+n.length+(p?0:t.length),v=o>m?new Array(m=o-m+1).join(e):"";return p&&(n=yo(v+n)),so&&n.replace(".",so),t+=u,("<"===r?t+n+v:">"===r?v+t+n:"^"===r?v.substring(0,m>>=1)+t+n+v.substring(m):t+(p?n:v+n))+h}};var mo=/(?:([^{])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,vo=va.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=va.round(n,Mt(n,t))).toFixed(Math.max(0,Math.min(20,Mt(n*(1+1e-15),
t))))}}),yo=st;if(go){var Mo=go.length;yo=function(n){for(var t=n.lastIndexOf("."),e=t>=0?"."+n.substring(t+1):(t=n.length,""),r=[],i=0,u=go[0];t>0&&u>0;)r.push(n.substring(t-=u,t+u)),u=go[i=(i+1)%Mo];return r.reverse().join(ho||"")+e}}va.geo={},bt.prototype={s:0,t:0,add:function(n){_t(n,this.t,xo),_t(xo.s,this.s,this),this.s?this.t+=xo.t:this.s=xo.t},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var xo=new bt;va.geo.stream=function(n,t){n&&bo.hasOwnProperty(n.type)?bo[n.type](n,t):wt(n,t)};var bo={Feature:function(n,t){wt(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,i=e.length;++r<i;)wt(e[r].geometry,t)}},_o={Sphere:function(n,t){t.sphere()},Point:function(n,t){var e=n.coordinates;t.point(e[0],e[1])},MultiPoint:function(n,t){for(var e,r=n.coordinates,i=-1,u=r.length;++i<u;)e=r[i],t.point(e[0],e[1])},LineString:function(n,t){St(n.coordinates,t,0)},MultiLineString:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)S
t(e[r],t,0)},Polygon:function(n,t){Et(n.coordinates,t)},MultiPolygon:function(n,t){for(var e=n.coordinates,r=-1,i=e.length;++r<i;)Et(e[r],t)},GeometryCollection:function(n,t){for(var e=n.geometries,r=-1,i=e.length;++r<i;)wt(e[r],t)}};va.geo.area=function(n){return wo=0,va.geo.stream(n,Eo),wo};var wo,So=new bt,Eo={sphere:function(){wo+=4*Ba},point:f,lineStart:f,lineEnd:f,polygonStart:function(){So.reset(),Eo.lineStart=kt},polygonEnd:function(){var n=2*So;wo+=0>n?4*Ba+n:n,Eo.lineStart=Eo.lineEnd=Eo.point=f}};va.geo.bounds=function(){function n(n,t){M.push(x=[f=n,h=n]),s>t&&(s=t),t>g&&(g=t)}function t(t,e){var r=At([t*Ja,e*Ja]);if(v){var i=qt(v,r),u=[i[1],-i[0],0],a=qt(u,i);zt(a),a=Dt(a);var c=t-p,l=c>0?1:-1,d=a[0]*Ga*l,m=Math.abs(c)>180;if(m^(d>l*p&&l*t>d)){var y=a[1]*Ga;y>g&&(g=y)}else if(d=(d+360)%360-180,m^(d>l*p&&l*t>d)){var y=-a[1]*Ga;s>y&&(s=y)}else s>e&&(s=e),e>g&&(g=e);m?p>t?o(f,t)>o(f,h)&&(h=t):o(t,h)>o(f,h)&&(f=t):h>=f?(f>t&&(f=t),t>h&&(h=t)):t>p?o(f,t)>o(f,h)&&(h=t)
:o(t,h)>o(f,h)&&(f=t)}else n(t,e);v=r,p=t}function e(){b.point=t}function r(){x[0]=f,x[1]=h,b.point=n,v=null}function i(n,e){if(v){var r=n-p;y+=Math.abs(r)>180?r+(r>0?360:-360):r}else d=n,m=e;Eo.point(n,e),t(n,e)}function u(){Eo.lineStart()}function a(){i(d,m),Eo.lineEnd(),Math.abs(y)>$a&&(f=-(h=180)),x[0]=f,x[1]=h,v=null}function o(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:n<t[0]||t[1]<n}var f,s,h,g,p,d,m,v,y,M,x,b={point:n,lineStart:e,lineEnd:r,polygonStart:function(){b.point=i,b.lineStart=u,b.lineEnd=a,y=0,Eo.polygonStart()},polygonEnd:function(){Eo.polygonEnd(),b.point=n,b.lineStart=e,b.lineEnd=r,0>So?(f=-(h=180),s=-(g=90)):y>$a?g=90:-$a>y&&(s=-90),x[0]=f,x[1]=h}};return function(n){g=h=-(f=s=1/0),M=[],va.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,i=M[0],u=[i];t>r;++r)e=M[r],l(e[0],i)||l(e[1],i)?(o(i[0],e[1])>o(i[0],i[1])&&(i[1]=e[1]),o(e[0],i[1])>o(i[0],i[1])&&(i[0]=e[0])):u.push(i=e
);for(var a,e,p=-1/0,t=u.length-1,r=0,i=u[t];t>=r;i=e,++r)e=u[r],(a=o(i[1],e[0]))>p&&(p=a,f=e[0],h=i[1])}return M=x=null,1/0===f||1/0===s?[[0/0,0/0],[0/0,0/0]]:[[f,s],[h,g]]}}(),va.geo.centroid=function(n){ko=Ao=No=qo=To=Co=zo=Do=jo=Lo=Ho=0,va.geo.stream(n,Fo);var t=jo,e=Lo,r=Ho,i=t*t+e*e+r*r;return Wa>i&&(t=Co,e=zo,r=Do,$a>Ao&&(t=No,e=qo,r=To),i=t*t+e*e+r*r,Wa>i)?[0/0,0/0]:[Math.atan2(e,t)*Ga,U(r/Math.sqrt(i))*Ga]};var ko,Ao,No,qo,To,Co,zo,Do,jo,Lo,Ho,Fo={sphere:f,point:Lt,lineStart:Ft,lineEnd:Pt,polygonStart:function(){Fo.lineStart=Ot},polygonEnd:function(){Fo.lineStart=Ft}},Po=It(Rt,$t,Jt,Gt),Oo=[-Ba,0],Ro=1e9;(va.geo.conicEqualArea=function(){return ee(re)}).raw=re,va.geo.albers=function(){return va.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},va.geo.albersUsa=function(){function n(n){var u=n[0],a=n[1];return t=null,e(u,a),t||(r(u,a),t)||i(u,a),t}var t,e,r,i,u=va.geo.albers(),a=va.geo.conicEqualArea().rotate([154,0]).center([
-2,58.5]).parallels([55,65]),o=va.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=u.scale(),e=u.translate(),r=(n[0]-e[0])/t,i=(n[1]-e[1])/t;return(i>=.12&&.234>i&&r>=-.425&&-.214>r?a:i>=.166&&.234>i&&r>=-.214&&-.115>r?o:u).invert(n)},n.stream=function(n){var t=u.stream(n),e=a.stream(n),r=o.stream(n);return{point:function(n,i){t.point(n,i),e.point(n,i),r.point(n,i)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(u.precision(t),a.precision(t),o.precision(t),n):u.precision()},n.scale=function(t){return arguments.length?(u.scale(t),a.scale(.35*t),o.scale(t),n.translate(u.tra
nslate())):u.scale()},n.translate=function(t){if(!arguments.length)return u.translate();var l=u.scale(),f=+t[0],s=+t[1];return e=u.translate(t).clipExtent([[f-.455*l,s-.238*l],[f+.455*l,s+.238*l]]).stream(c).point,r=a.translate([f-.307*l,s+.201*l]).clipExtent([[f-.425*l+$a,s+.12*l+$a],[f-.214*l-$a,s+.234*l-$a]]).stream(c).point,i=o.translate([f-.205*l,s+.212*l]).clipExtent([[f-.214*l+$a,s+.166*l+$a],[f-.115*l-$a,s+.234*l-$a]]).stream(c).point,n},n.scale(1070)};var Yo,Uo,Io,Vo,Xo,Zo,Bo={point:f,lineStart:f,lineEnd:f,polygonStart:function(){Uo=0,Bo.lineStart=ie},polygonEnd:function(){Bo.lineStart=Bo.lineEnd=Bo.point=f,Yo+=Math.abs(Uo/2)}},$o={point:ue,lineStart:f,lineEnd:f,polygonStart:f,polygonEnd:f},Wo={point:ce,lineStart:le,lineEnd:fe,polygonStart:function(){Wo.lineStart=se},polygonEnd:function(){Wo.point=ce,Wo.lineStart=le,Wo.lineEnd=fe}};va.geo.path=function(){function n(n){return n&&("function"==typeof o&&u.pointRadius(+o.apply(this,arguments)),a&&a.valid||(a=i(u)),va.ge
o.stream(n,a)),u.result()}function t(){return a=null,n}var e,r,i,u,a,o=4.5;return n.area=function(n){return Yo=0,va.geo.stream(n,i(Bo)),Yo},n.centroid=function(n){return No=qo=To=Co=zo=Do=jo=Lo=Ho=0,va.geo.stream(n,i(Wo)),Ho?[jo/Ho,Lo/Ho]:Do?[Co/Do,zo/Do]:To?[No/To,qo/To]:[0/0,0/0]},n.bounds=function(n){return Xo=Zo=-(Io=Vo=1/0),va.geo.stream(n,i($o)),[[Io,Vo],[Xo,Zo]]},n.projection=function(n){return arguments.length?(i=(e=n)?n.stream||pe(n):st,t()):e},n.context=function(n){return arguments.length?(u=null==(r=n)?new ae:new he(n),"function"!=typeof o&&u.pointRadius(o),t()):r},n.pointRadius=function(t){return arguments.length?(o="function"==typeof t?t:(u.pointRadius(+t),+t),n):o},n.projection(va.geo.albersUsa()).context(null)},va.geo.projection=de,va.geo.projectionMutator=me,(va.geo.equirectangular=function(){return de(ye)}).raw=ye.invert=ye,va.geo.rotation=function(n){function t(t){return t=n(t[0]*Ja,t[1]*Ja),t[0]*=Ga,t[1]*=Ga,t}return n=Me(n[0]%360*Ja,n[1]*Ja,n.length>2?n[2
]*Ja:0),t.invert=function(t){return t=n.invert(t[0]*Ja,t[1]*Ja),t[0]*=Ga,t[1]*=Ga,t},t},va.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=Me(-n[0]*Ja,-n[1]*Ja,0).invert,i=[];return e(null,null,1,{point:function(n,e){i.push(n=t(n,e)),n[0]*=Ga,n[1]*=Ga}}),{type:"Polygon",coordinates:[i]}}var t,e,r=[0,0],i=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=we((t=+r)*Ja,i*Ja),n):t},n.precision=function(r){return arguments.length?(e=we(t*Ja,(i=+r)*Ja),n):i},n.angle(90)},va.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Ja,i=n[1]*Ja,u=t[1]*Ja,a=Math.sin(r),o=Math.cos(r),c=Math.sin(i),l=Math.cos(i),f=Math.sin(u),s=Math.cos(u);return Math.atan2(Math.sqrt((e=s*a)*e+(e=l*f-c*s*o)*e),c*f+l*s*o)},va.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return va.range(Math.ceil(u/m)*m,i,m).map(h).concat(va.range(Math.ceil(l/v)*v,c,v).map(g))
.concat(va.range(Math.ceil(r/p)*p,e,p).filter(function(n){return Math.abs(n%m)>$a}).map(f)).concat(va.range(Math.ceil(o/d)*d,a,d).filter(function(n){return Math.abs(n%v)>$a}).map(s))}var e,r,i,u,a,o,c,l,f,s,h,g,p=10,d=p,m=90,v=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(u).concat(g(c).slice(1),h(i).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(u=+t[0][0],i=+t[1][0],l=+t[0][1],c=+t[1][1],u>i&&(t=u,u=i,i=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[u,l],[i,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],o=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),o>a&&(t=o,o=a,a=t),n.precision(y)):[[r,o],[e,a]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=fu
nction(t){return arguments.length?(m=+t[0],v=+t[1],n):[m,v]},n.minorStep=function(t){return arguments.length?(p=+t[0],d=+t[1],n):[p,d]},n.precision=function(t){return arguments.length?(y=+t,f=Ee(o,a,90),s=ke(r,e,y),h=Ee(l,c,90),g=ke(u,i,y),n):y},n.majorExtent([[-180,-90+$a],[180,90-$a]]).minorExtent([[-180,-80-$a],[180,80+$a]])},va.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||i.apply(this,arguments)]}}var t,e,r=Ae,i=Ne;return n.distance=function(){return va.geo.distance(t||r.apply(this,arguments),e||i.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(i=t,e="function"==typeof t?null:t,n):i},n.precision=function(){return arguments.length?n:0},n},va.geo.interpolate=function(n,t){return qe(n[0]*Ja,n[1]*Ja,t[0]*Ja,t[1]*Ja)},va.geo.length=function(n){return Jo=0,va.geo.stream(n,Go),Jo};var Jo,Go={sphere:f,point:f,line
Start:Te,lineEnd:f,polygonStart:f,polygonEnd:f},Ko=Ce(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(va.geo.azimuthalEqualArea=function(){return de(Ko)}).raw=Ko;var Qo=Ce(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},st);(va.geo.azimuthalEquidistant=function(){return de(Qo)}).raw=Qo,(va.geo.conicConformal=function(){return ee(ze)}).raw=ze,(va.geo.conicEquidistant=function(){return ee(De)}).raw=De;var nc=Ce(function(n){return 1/n},Math.atan);(va.geo.gnomonic=function(){return de(nc)}).raw=nc,je.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ba/2]},(va.geo.mercator=function(){return Le(je)}).raw=je;var tc=Ce(function(){return 1},Math.asin);(va.geo.orthographic=function(){return de(tc)}).raw=tc;var ec=Ce(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(va.geo.stereographic=function(){return de(ec)}).raw=ec,He.invert=function(n,t){return[Math.atan2(I(n),Math.cos(t)),U(Math.sin(t)/V(n))]},(va.geo.transverseMercator=
function(){return Le(He)}).raw=He,va.geom={},va.svg={},va.svg.line=function(){return Fe(st)};var rc=va.map({linear:Re,"linear-closed":Ye,step:Ue,"step-before":Ie,"step-after":Ve,basis:Je,"basis-open":Ge,"basis-closed":Ke,bundle:Qe,cardinal:Be,"cardinal-open":Xe,"cardinal-closed":Ze,monotone:ur});rc.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)
-});var ic=[0,2/3,1/3,0],uc=[0,1/3,2/3,0],ac=[0,1/6,2/3,1/6];va.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,i,u,a,o,c,l,f,s,h,g,p,d=ft(e),m=ft(r),v=n.length,y=v-1,M=[],x=[],b=0;if(d===Pe&&r===Oe)t=n;else for(u=0,t=[];v>u;++u)t.push([+d.call(this,i=n[u],u),+m.call(this,i,u)]);for(u=1;v>u;++u)(t[u][1]<t[b][1]||t[u][1]==t[b][1]&&t[u][0]<t[b][0])&&(b=u);for(u=0;v>u;++u)u!==b&&(c=t[u][1]-t[b][1],o=t[u][0]-t[b][0],M.push({angle:Math.atan2(c,o),index:u}));for(M.sort(function(n,t){return n.angle-t.angle}),g=M[0].angle,h=M[0].index,s=0,u=1;y>u;++u){if(a=M[u].index,g==M[u].angle){if(o=t[h][0]-t[b][0],c=t[h][1]-t[b][1],l=t[a][0]-t[b][0],f=t[a][1]-t[b][1],o*o+c*c>=l*l+f*f){M[u].index=-1;continue}M[s].index=-1}g=M[u].angle,s=u,h=a}for(x.push(b),u=0,a=0;2>u;++a)M[a].index>-1&&(x.push(M[a].index),u++);for(p=x.length;y>a;++a)if(!(M[a].index<0)){for(;!ar(x[p-2],x[p-1],M[a].index,t);)--p;x[p++]=M[a].index}var _=[];for(u=p-1;u>=0;--u)_.push(n[x[u]]);return _}var e=Pe,r=Oe;r
eturn arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t)},va.geom.polygon=function(n){return n.area=function(){for(var t=0,e=n.length,r=n[e-1][1]*n[0][0]-n[e-1][0]*n[0][1];++t<e;)r+=n[t-1][1]*n[t][0]-n[t-1][0]*n[t][1];return.5*r},n.centroid=function(t){var e,r,i=-1,u=n.length,a=0,o=0,c=n[u-1];for(arguments.length||(t=-1/(6*n.area()));++i<u;)e=c,c=n[i],r=e[0]*c[1]-c[0]*e[1],a+=(e[0]+c[0])*r,o+=(e[1]+c[1])*r;return[a*t,o*t]},n.clip=function(t){for(var e,r,i,u,a,o,c=-1,l=n.length,f=n[l-1];++c<l;){for(e=t.slice(),t.length=0,u=n[c],a=e[(i=e.length)-1],r=-1;++r<i;)o=e[r],or(o,f,u)?(or(a,f,u)||t.push(cr(a,o,f,u)),t.push(o)):or(a,f,u)&&t.push(cr(a,o,f,u)),a=o;f=u}return t},n},va.geom.delaunay=function(n){var t=n.map(function(){return[]}),e=[];return lr(n,function(e){t[e.region.l.index].push(n[e.region.r.index])}),t.forEach(function(t,r){var i=n[r],u=i[0],a=i[1];t.forEach(function(n){n.angle=Math.atan2(n[0]-
u,n[1]-a)}),t.sort(function(n,t){return n.angle-t.angle});for(var o=0,c=t.length-1;c>o;o++)e.push([i,t[o],t[o+1]])}),e},va.geom.voronoi=function(n){function t(n){var t,u,a,o=n.map(function(){return[]}),c=ft(e),l=ft(r),f=n.length,s=1e6;if(c===Pe&&l===Oe)t=n;else for(t=new Array(f),a=0;f>a;++a)t[a]=[+c.call(this,u=n[a],a),+l.call(this,u,a)];if(lr(t,function(n){var t,e,r,i,u,a;1===n.a&&n.b>=0?(t=n.ep.r,e=n.ep.l):(t=n.ep.l,e=n.ep.r),1===n.a?(u=t?t.y:-s,r=n.c-n.b*u,a=e?e.y:s,i=n.c-n.b*a):(r=t?t.x:-s,u=n.c-n.a*r,i=e?e.x:s,a=n.c-n.a*i);var c=[r,u],l=[i,a];o[n.region.l.index].push(c,l),o[n.region.r.index].push(c,l)}),o=o.map(function(n,e){var r=t[e][0],i=t[e][1],u=n.map(function(n){return Math.atan2(n[0]-r,n[1]-i)}),a=va.range(n.length).sort(function(n,t){return u[n]-u[t]});return a.filter(function(n,t){return!t||u[n]-u[a[t-1]]>$a}).map(function(t){return n[t]})}),o.forEach(function(n,e){var r=n.length;if(!r)return n.push([-s,-s],[-s,s],[s,s],[s,-s]);if(!(r>2)){var i=t[e],u=n[0],a=n
[1],o=i[0],c=i[1],l=u[0],f=u[1],h=a[0],g=a[1],p=Math.abs(h-l),d=g-f;if(Math.abs(d)<$a){var m=f>c?-s:s;n.push([-s,m],[s,m])}else if($a>p){var v=l>o?-s:s;n.push([v,-s],[v,s])}else{var m=(l-o)*(g-f)>(h-l)*(f-c)?s:-s,y=Math.abs(d)-p;Math.abs(y)<$a?n.push([0>d?m:-m,m]):(y>0&&(m*=-1),n.push([-s,m],[s,m]))}}}),i)for(a=0;f>a;++a)i.clip(o[a]);for(a=0;f>a;++a)o[a].point=n[a];return o}var e=Pe,r=Oe,i=null;return arguments.length?t(n):(t.x=function(n){return arguments.length?(e=n,t):e},t.y=function(n){return arguments.length?(r=n,t):r},t.clipExtent=function(n){if(!arguments.length)return i&&[i[0],i[2]];if(null==n)i=null;else{var e=+n[0][0],r=+n[0][1],u=+n[1][0],a=+n[1][1];i=va.geom.polygon([[e,r],[e,a],[u,a],[u,r]])}return t},t.size=function(n){return arguments.length?t.clipExtent(n&&[[0,0],n]):i&&i[2]},t.links=function(n){var t,i,u,a=n.map(function(){return[]}),o=[],c=ft(e),l=ft(r),f=n.length;if(c===Pe&&l===Oe)t=n;else for(t=new Array(f),u=0;f>u;++u)t[u]=[+c.call(this,i=n[u],u),+l.call
(this,i,u)];return lr(t,function(t){var e=t.region.l.index,r=t.region.r.index;a[e][r]||(a[e][r]=a[r][e]=!0,o.push({source:n[e],target:n[r]}))}),o},t.triangles=function(n){if(e===Pe&&r===Oe)return va.geom.delaunay(n);for(var t,i=new Array(c),u=ft(e),a=ft(r),o=-1,c=n.length;++o<c;)(i[o]=[+u.call(this,t=n[o],o),+a.call(this,t,o)]).data=t;return va.geom.delaunay(i).map(function(n){return n.map(function(n){return n.data})})},t)};var oc={l:"r",r:"l"};va.geom.quadtree=function(n,t,e,r,i){function u(n){function u(n,t,e,r,i,u,a,o){if(!isNaN(e)&&!isNaN(r))if(n.leaf){var c=n.x,f=n.y;if(null!=c)if(Math.abs(c-e)+Math.abs(f-r)<.01)l(n,t,e,r,i,u,a,o);else{var s=n.point;n.x=n.y=n.point=null,l(n,s,c,f,i,u,a,o),l(n,t,e,r,i,u,a,o)}else n.x=e,n.y=r,n.point=t}else l(n,t,e,r,i,u,a,o)}function l(n,t,e,r,i,a,o,c){var l=.5*(i+o),f=.5*(a+c),s=e>=l,h=r>=f,g=(h<<1)+s;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=hr()),s?i=l:o=l,h?a=f:c=f,u(n,t,e,r,i,a,o,c)}var f,s,h,g,p,d,m,v,y,M=ft(o),x=ft(c);if(null!=t)d=t,m=e
,v=r,y=i;else if(v=y=-(d=m=1/0),s=[],h=[],p=n.length,a)for(g=0;p>g;++g)f=n[g],f.x<d&&(d=f.x),f.y<m&&(m=f.y),f.x>v&&(v=f.x),f.y>y&&(y=f.y),s.push(f.x),h.push(f.y);else for(g=0;p>g;++g){var b=+M(f=n[g],g),_=+x(f,g);d>b&&(d=b),m>_&&(m=_),b>v&&(v=b),_>y&&(y=_),s.push(b),h.push(_)}var w=v-d,S=y-m;w>S?y=m+w:v=d+S;var E=hr();if(E.add=function(n){u(E,n,+M(n,++g),+x(n,g),d,m,v,y)},E.visit=function(n){gr(n,E,d,m,v,y)},g=-1,null==t){for(;++g<p;)u(E,n[g],s[g],h[g],d,m,v,y);--g}else n.forEach(E.add);return s=h=n=f=null,E}var a,o=Pe,c=Oe;return(a=arguments.length)?(o=fr,c=sr,3===a&&(i=e,r=t,e=t=0),u(n)):(u.x=function(n){return arguments.length?(o=n,u):o},u.y=function(n){return arguments.length?(c=n,u):c},u.extent=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=+n[0][0],e=+n[0][1],r=+n[1][0],i=+n[1][1]),u):null==t?null:[[t,e],[r,i]]},u.size=function(n){return arguments.length?(null==n?t=e=r=i=null:(t=e=0,r=+n[0],i=+n[1]),u):null==t?null:[r-t,i-e]},u)},va.interpolateRgb=pr,va.t
ransform=function(n){var t=ya.createElementNS(va.ns.prefix.svg,"g");return(va.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new dr(e?e.matrix:cc)})(n)},dr.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var cc={a:1,b:0,c:0,d:1,e:0,f:0};va.interpolateNumber=Mr,va.interpolateTransform=xr,va.interpolateObject=br,va.interpolateString=_r;var lc=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g;va.interpolate=wr,va.interpolators=[function(n,t){var e=typeof t;return("string"===e?uo.has(t)||/^(#|rgb\(|hsl\()/.test(t)?pr:_r:t instanceof H?pr:"object"===e?Array.isArray(t)?Er:br:Mr)(n,t)}],va.interpolateArray=Er;var fc=function(){return st},sc=va.map({linear:fc,poly:zr,quad:function(){return qr},cubic:function(){return Tr},sin:function(){return Dr},exp:function(){return jr},circle:function(){return Lr},elastic:Hr,back:Fr,bounce:function(){ret
urn Pr}}),hc=va.map({"in":st,out:Ar,"in-out":Nr,"out-in":function(n){return Nr(Ar(n))}});va.ease=function(n){var t=n.indexOf("-"),e=t>=0?n.substring(0,t):n,r=t>=0?n.substring(t+1):"in";return e=sc.get(e)||fc,r=hc.get(r)||st,kr(r(e.apply(null,Array.prototype.slice.call(arguments,1))))},va.interpolateHcl=Or,va.interpolateHsl=Rr,va.interpolateLab=Yr,va.interpolateRound=Ur,va.layout={},va.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++e<r;)t.push(Xr(n[e]));return t}},va.layout.chord=function(){function n(){var n,l,s,h,g,p={},d=[],m=va.range(u),v=[];for(e=[],r=[],n=0,h=-1;++h<u;){for(l=0,g=-1;++g<u;)l+=i[h][g];d.push(l),v.push(va.range(u)),n+=l}for(a&&m.sort(function(n,t){return a(d[n],d[t])}),o&&v.forEach(function(n,t){n.sort(function(n,e){return o(i[t][n],i[t][e])})}),n=(2*Ba-f*u)/n,l=0,h=-1;++h<u;){for(s=l,g=-1;++g<u;){var y=m[h],M=v[y][g],x=i[y][M],b=l,_=l+=x*n;p[y+"-"+M]={index:y,subindex:M,startAngle:b,endAngle:_,value:x}}r[y]={index:y,startAngle
:s,endAngle:l,value:(l-s)/n},l+=f}for(h=-1;++h<u;)for(g=h-1;++g<u;){var w=p[h+"-"+g],S=p[g+"-"+h];(w.value||S.value)&&e.push(w.value<S.value?{source:S,target:w}:{source:w,target:S})}c&&t()}function t(){e.sort(function(n,t){return c((n.source.value+n.target.value)/2,(t.source.value+t.target.value)/2)})}var e,r,i,u,a,o,c,l={},f=0;return l.matrix=function(n){return arguments.length?(u=(i=n)&&i.length,e=r=null,l):i},l.padding=function(n){return arguments.length?(f=n,e=r=null,l):f},l.sortGroups=function(n){return arguments.length?(a=n,e=r=null,l):a},l.sortSubgroups=function(n){return arguments.length?(o=n,e=null,l):o},l.sortChords=function(n){return arguments.length?(c=n,e&&t(),l):c},l.chords=function(){return e||n(),e},l.groups=function(){return r||n(),r},l},va.layout.force=function(){function n(n){return function(t,e,r,i){if(t.point!==n){var u=t.cx-n.x,a=t.cy-n.y,o=1/Math.sqrt(u*u+a*a);if(d>(i-e)*o){var c=t.charge*o*o;return n.px-=u*c,n.py-=a*c,!0}if(t.point&&isFinite(o)){var c
=t.pointCharge*o*o;n.px-=u*c,n.py-=a*c}}return!t.charge}}function t(n){n.px=va.event.x,n.py=va.event.y,o.resume()}var e,r,i,u,a,o={},c=va.dispatch("start","tick","end"),l=[1,1],f=.9,s=gc,h=pc,g=-30,p=.1,d=.8,m=[],v=[];return o.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,o,s,h,d,y,M,x,b=m.length,_=v.length;for(e=0;_>e;++e)o=v[e],s=o.source,h=o.target,M=h.x-s.x,x=h.y-s.y,(d=M*M+x*x)&&(d=r*u[e]*((d=Math.sqrt(d))-i[e])/d,M*=d,x*=d,h.x-=M*(y=s.weight/(h.weight+s.weight)),h.y-=x*y,s.x+=M*(y=1-y),s.y+=x*y);if((y=r*p)&&(M=l[0]/2,x=l[1]/2,e=-1,y))for(;++e<b;)o=m[e],o.x+=(M-o.x)*y,o.y+=(x-o.y)*y;if(g)for(Kr(t=va.geom.quadtree(m),r,a),e=-1;++e<b;)(o=m[e]).fixed||t.visit(n(o));for(e=-1;++e<b;)o=m[e],o.fixed?(o.x=o.px,o.y=o.py):(o.x-=(o.px-(o.px=o.x))*f,o.y-=(o.py-(o.py=o.y))*f);c.tick({type:"tick",alpha:r})},o.nodes=function(n){return arguments.length?(m=n,o):m},o.links=function(n){return arguments.length?(v=n,o):v},o.size=function(n){return argument
s.length?(l=n,o):l},o.linkDistance=function(n){return arguments.length?(s="function"==typeof n?n:+n,o):s},o.distance=o.linkDistance,o.linkStrength=function(n){return arguments.length?(h="function"==typeof n?n:+n,o):h},o.friction=function(n){return arguments.length?(f=+n,o):f},o.charge=function(n){return arguments.length?(g="function"==typeof n?n:+n,o):g},o.gravity=function(n){return arguments.length?(p=+n,o):p},o.theta=function(n){return arguments.length?(d=+n,o):d},o.alpha=function(n){return arguments.length?(n=+n,r?r=n>0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),va.timer(o.tick)),o):r},o.start=function(){function n(n,r){for(var i,u=t(e),a=-1,o=u.length;++a<o;)if(!isNaN(i=u[a][n]))return i;return Math.random()*r}function t(){if(!c){for(c=[],r=0;p>r;++r)c[r]=[];for(r=0;d>r;++r){var n=v[r];c[n.source.index].push(n.target),c[n.target.index].push(n.source)}}return c[e]}var e,r,c,f,p=m.length,d=v.length,y=l[0],M=l[1];for(e=0;p>e;++e)(f=m[e]).index=e,f.weight=0;for(e=0;d>e;++e)
f=v[e],"number"==typeof f.source&&(f.source=m[f.source]),"number"==typeof f.target&&(f.target=m[f.target]),++f.source.weight,++f.target.weight;for(e=0;p>e;++e)f=m[e],isNaN(f.x)&&(f.x=n("x",y)),isNaN(f.y)&&(f.y=n("y",M)),isNaN(f.px)&&(f.px=f.x),isNaN(f.py)&&(f.py=f.y);if(i=[],"function"==typeof s)for(e=0;d>e;++e)i[e]=+s.call(this,v[e],e);else for(e=0;d>e;++e)i[e]=s;if(u=[],"function"==typeof h)for(e=0;d>e;++e)u[e]=+h.call(this,v[e],e);else for(e=0;d>e;++e)u[e]=h;if(a=[],"function"==typeof g)for(e=0;p>e;++e)a[e]=+g.call(this,m[e],e);else for(e=0;p>e;++e)a[e]=g;return o.resume()},o.resume=function(){return o.alpha(.1)},o.stop=function(){return o.alpha(0)},o.drag=function(){return e||(e=va.behavior.drag().origin(st).on("dragstart.force",$r).on("drag.force",t).on("dragend.force",Wr)),arguments.length?(this.on("mouseover.force",Jr).on("mouseout.force",Gr).call(e),void 0):e},va.rebind(o,c,"on")};var gc=20,pc=1;va.layout.hierarchy=function(){function n(t,a,o){var c=i.call(e,t,a);if(
t.depth=a,o.push(t),c&&(l=c.length)){for(var l,f,s=-1,h=t.children=[],g=0,p=a+1;++s<l;)f=n(c[s],p,o),f.parent=t,h.push(f),g+=f.value;r&&h.sort(r),u&&(t.value=g)}else u&&(t.value=+u.call(e,t,a)||0);return t}function t(n,r){var i=n.children,a=0;if(i&&(o=i.length))for(var o,c=-1,l=r+1;++c<o;)a+=t(i[c],l);else u&&(a=+u.call(e,n,r)||0);return u&&(n.value=a),a}function e(t){var e=[];return n(t,0,e),e}var r=ei,i=ni,u=ti;return e.sort=function(n){return arguments.length?(r=n,e):r},e.children=function(n){return arguments.length?(i=n,e):i},e.value=function(n){return arguments.length?(u=n,e):u},e.revalue=function(n){return t(n,0),n},e},va.layout.partition=function(){function n(t,e,r,i){var u=t.children;if(t.x=e,t.y=t.depth*i,t.dx=r,t.dy=i,u&&(a=u.length)){var a,o,c,l=-1;for(r=t.value?r/t.value:0;++l<a;)n(o=u[l],e,c=o.value*r,i),e+=c}}function t(n){var e=n.children,r=0;if(e&&(i=e.length))for(var i,u=-1;++u<i;)r=Math.max(r,t(e[u]));return 1+r}function e(e,u){var a=r.call(this,e,u);return
n(a[0],0,i[0],i[1]/t(a[0])),a}var r=va.layout.hierarchy(),i=[1,1];return e.size=function(n){return arguments.length?(i=n,e):i},Qr(e,r)},va.layout.pie=function(){function n(u){var a=u.map(function(e,r){return+t.call(n,e,r)}),o=+("function"==typeof r?r.apply(this,arguments):r),c=(("function"==typeof i?i.apply(this,arguments):i)-o)/va.sum(a),l=va.range(u.length);null!=e&&l.sort(e===dc?function(n,t){return a[t]-a[n]}:function(n,t){return e(u[n],u[t])});var f=[];return l.forEach(function(n){var t;f[n]={data:u[n],value:t=a[n],startAngle:o,endAngle:o+=t*c}}),f}var t=Number,e=dc,r=0,i=2*Ba;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(i=t,n):i},n};var dc={};va.layout.stack=function(){function n(o,c){var l=o.map(function(e,r){return t.call(n,e,r)}),f=l.map(function(t){return t.map(function(t,e){return[u.call
(n,t,e),a.call(n,t,e)]})}),s=e.call(n,f,c);l=va.permute(l,s),f=va.permute(f,s);var h,g,p,d=r.call(n,f,c),m=l.length,v=l[0].length;for(g=0;v>g;++g)for(i.call(n,l[0][g],p=d[g],f[0][g][1]),h=1;m>h;++h)i.call(n,l[h][g],p+=f[h-1][g][1],f[h][g][1]);return o}var t=st,e=oi,r=ci,i=ai,u=ii,a=ui;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:mc.get(t)||oi,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:vc.get(t)||ci,n):r},n.x=function(t){return arguments.length?(u=t,n):u},n.y=function(t){return arguments.length?(a=t,n):a},n.out=function(t){return arguments.length?(i=t,n):i},n};var mc=va.map({"inside-out":function(n){var t,e,r=n.length,i=n.map(li),u=n.map(fi),a=va.range(r).sort(function(n,t){return i[n]-i[t]}),o=0,c=0,l=[],f=[];for(t=0;r>t;++t)e=a[t],c>o?(o+=u[e],l.push(e)):(c+=u[e],f.push(e));return f.reverse().concat(l)},reverse:function(n){return va.range(n.length).rever
se()},"default":oi}),vc=va.map({silhouette:function(n){var t,e,r,i=n.length,u=n[0].length,a=[],o=0,c=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];r>o&&(o=r),a.push(r)}for(e=0;u>e;++e)c[e]=(o-a[e])/2;return c},wiggle:function(n){var t,e,r,i,u,a,o,c,l,f=n.length,s=n[0],h=s.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,i=0;f>t;++t)i+=n[t][e][1];for(t=0,u=0,o=s[e][0]-s[e-1][0];f>t;++t){for(r=0,a=(n[t][e][1]-n[t][e-1][1])/(2*o);t>r;++r)a+=(n[r][e][1]-n[r][e-1][1])/o;u+=a*n[t][e][1]}g[e]=c-=i?u/i*o:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,i=n.length,u=n[0].length,a=1/i,o=[];for(e=0;u>e;++e){for(t=0,r=0;i>t;t++)r+=n[t][e][1];if(r)for(t=0;i>t;t++)n[t][e][1]/=r;else for(t=0;i>t;t++)n[t][e][1]=a}for(e=0;u>e;++e)o[e]=0;return o},zero:ci});va.layout.histogram=function(){function n(n,u){for(var a,o,c=[],l=n.map(e,this),f=r.call(this,l,u),s=i.call(this,f,l,u),u=-1,h=l.length,g=s.length-1,p=t?1:1/h;++u<g;)a=c[u]=[],a.dx=s[u+1]-(a.x=s[u]),a.y=0;
if(g>0)for(u=-1;++u<h;)o=l[u],o>=f[0]&&o<=f[1]&&(a=c[va.bisect(s,o,1,g)-1],a.y+=p,a.push(n[u]));return c}var t=!0,e=Number,r=pi,i=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=ft(t),n):r},n.bins=function(t){return arguments.length?(i="number"==typeof t?function(n){return gi(n,t)}:ft(t),n):i},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},va.layout.tree=function(){function n(n,u){function a(n,t){var r=n.children,i=n._tree;if(r&&(u=r.length)){for(var u,o,l,f=r[0],s=f,h=-1;++h<u;)l=r[h],a(l,o),s=c(l,o,s),o=l;wi(n);var g=.5*(f._tree.prelim+l._tree.prelim);t?(i.prelim=t._tree.prelim+e(n,t),i.mod=i.prelim-g):i.prelim=g}else t&&(i.prelim=t._tree.prelim+e(n,t))}function o(n,t){n.x=n._tree.prelim+t;var e=n.children;if(e&&(r=e.length)){var r,i=-1;for(t+=n._tree.mod;++i<r;)o(e[i],t)}}function c(n,t,r){if(t){for(var i,u=n,a=n,o=t,c=n.parent.children[0],l=u._tree.mod,f=a._tree.mod,s=o._tree.mod,h=c._tree.m
od;o=vi(o),u=mi(u),o&&u;)c=mi(c),a=vi(a),a._tree.ancestor=n,i=o._tree.prelim+s-u._tree.prelim-l+e(o,u),i>0&&(Si(Ei(o,n,r),n,i),l+=i,f+=i),s+=o._tree.mod,l+=u._tree.mod,h+=c._tree.mod,f+=a._tree.mod;o&&!vi(a)&&(a._tree.thread=o,a._tree.mod+=s-f),u&&!mi(c)&&(c._tree.thread=u,c._tree.mod+=l-h,r=n)}return r}var l=t.call(this,n,u),f=l[0];_i(f,function(n,t){n._tree={ancestor:n,prelim:0,mod:0,change:0,shift:0,number:t?t._tree.number+1:0}}),a(f),o(f,-f._tree.prelim);var s=yi(f,xi),h=yi(f,Mi),g=yi(f,bi),p=s.x-e(s,h)/2,d=h.x+e(h,s)/2,m=g.depth||1;return _i(f,i?function(n){n.x*=r[0],n.y=n.depth*r[1],delete n._tree}:function(n){n.x=(n.x-p)/(d-p)*r[0],n.y=n.depth/m*r[1],delete n._tree}),l}var t=va.layout.hierarchy().sort(null).value(null),e=di,r=[1,1],i=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},Qr(n,t)},va.layout.pac
k=function(){function n(n,u){var a=e.call(this,n,u),o=a[0],c=i[0],l=i[1],f=t||Math.sqrt;if(o.x=o.y=0,_i(o,function(n){n.r=f(n.value)}),_i(o,Ti),r){var s=r*(t?1:Math.max(2*o.r/c,2*o.r/l))/2;_i(o,function(n){n.r+=s}),_i(o,Ti),_i(o,function(n){n.r-=s})}return Di(o,c/2,l/2,t?1:1/Math.max(2*o.r/c,2*o.r/l)),a}var t,e=va.layout.hierarchy().sort(ki),r=0,i=[1,1];return n.size=function(t){return arguments.length?(i=t,n):i},n.radius=function(e){return arguments.length?(t=e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Qr(n,e)},va.layout.cluster=function(){function n(n,u){var a,o=t.call(this,n,u),c=o[0],l=0;_i(c,function(n){var t=n.children;t&&t.length?(n.x=Hi(t),n.y=Li(t)):(n.x=a?l+=e(n,a):0,n.y=0,a=n)});var f=Fi(c),s=Pi(c),h=f.x-e(f,s)/2,g=s.x+e(s,f)/2;return _i(c,i?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),o}var t=va.layout.hierarchy().sort(null).value(null),e=di,r=[1,1],i=!1;return n.separat
ion=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(i=null==(r=t),n):i?null:r},n.nodeSize=function(t){return arguments.length?(i=null!=(r=t),n):i?r:null},Qr(n,t)},va.layout.treemap=function(){function n(n,t){for(var e,r,i=-1,u=n.length;++i<u;)r=(e=n[i]).value*(0>t?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var u=e.children;if(u&&u.length){var a,o,c,l=s(e),f=[],h=u.slice(),p=1/0,d="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),f.area=0;(c=h.length)>0;)f.push(a=h[c-1]),f.area+=a.area,"squarify"!==g||(o=r(f,d))<=p?(h.pop(),p=o):(f.area-=f.pop().area,i(f,d,l,!1),d=Math.min(l.dx,l.dy),f.length=f.area=0,p=1/0);f.length&&(i(f,d,l,!0),f.length=f.area=0),u.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var u,a=s(t),o=r.slice(),c=[];for(n(o,a.dx*a.dy/t.value),c.area=0;u=o.pop();)c.push(u),c.area+=u.area,null!=u.z&&(i(c,u.z?a.dx:a.dy,a,!o.length),c.length=c.are
a=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,i=0,u=1/0,a=-1,o=n.length;++a<o;)(e=n[a].area)&&(u>e&&(u=e),e>i&&(i=e));return r*=r,t*=t,r?Math.max(t*i*p/r,r/(t*u*p)):1/0}function i(n,t,e,r){var i,u=-1,a=n.length,o=e.x,l=e.y,f=t?c(n.area/t):0;if(t==e.dx){for((r||f>e.dy)&&(f=e.dy);++u<a;)i=n[u],i.x=o,i.y=l,i.dy=f,o+=i.dx=Math.min(e.x+e.dx-o,f?c(i.area/f):0);i.z=!0,i.dx+=e.x+e.dx-o,e.y+=f,e.dy-=f}else{for((r||f>e.dx)&&(f=e.dx);++u<a;)i=n[u],i.x=o,i.y=l,i.dx=f,l+=i.dy=Math.min(e.y+e.dy-l,f?c(i.area/f):0);i.z=!1,i.dy+=e.y+e.dy-l,e.x+=f,e.dx-=f}}function u(r){var i=a||o(r),u=i[0];return u.x=0,u.y=0,u.dx=l[0],u.dy=l[1],a&&o.revalue(u),n([u],u.dx*u.dy/u.value),(a?e:t)(u),h&&(a=i),i}var a,o=va.layout.hierarchy(),c=Math.round,l=[1,1],f=null,s=Oi,h=!1,g="squarify",p=.5*(1+Math.sqrt(5));return u.size=function(n){return arguments.length?(l=n,u):l},u.padding=function(n){function t(t){var e=n.call(u,t,t.depth);return null==e?Oi(t):Ri(t,"number"==typeof e?[e,e,e,e]:e)}function e(t){r
eturn Ri(t,n)}if(!arguments.length)return f;var r;return s=null==(f=n)?Oi:"function"==(r=typeof n)?t:"number"===r?(n=[n,n,n,n],e):e,u},u.round=function(n){return arguments.length?(c=n?Math.round:Number,u):c!=Number},u.sticky=function(n){return arguments.length?(h=n,a=null,u):h},u.ratio=function(n){return arguments.length?(p=n,u):p},u.mode=function(n){return arguments.length?(g=n+"",u):g},Qr(u,o)},va.random={normal:function(n,t){var e=arguments.length;return 2>e&&(t=1),1>e&&(n=0),function(){var e,r,i;do e=2*Math.random()-1,r=2*Math.random()-1,i=e*e+r*r;while(!i||i>1);return n+t*e*Math.sqrt(-2*Math.log(i)/i)}},logNormal:function(){var n=va.random.normal.apply(va,arguments);return function(){return Math.exp(n())}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t/n}}},va.scale={};var yc={floor:st,ceil:st};va.scale.linear=function(){return Bi([0,1],[0,1],wr,!1)},va.scale.log=function(){return nu(va.scale.linear().domain([0,Math.LN10]),10,t
u,eu,[1,10])};var Mc=va.format(".0e");va.scale.pow=function(){return uu(va.scale.linear(),1,[0,1])},va.scale.sqrt=function(){return va.scale.pow().exponent(.5)},va.scale.ordinal=function(){return ou([],{t:"range",a:[[]]})},va.scale.category10=function(){return va.scale.ordinal().range(xc)},va.scale.category20=function(){return va.scale.ordinal().range(bc)},va.scale.category20b=function(){return va.scale.ordinal().range(_c)},va.scale.category20c=function(){return va.scale.ordinal().range(wc)};var xc=["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"],bc=["#1f77b4","#aec7e8","#ff7f0e","#ffbb78","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5","#8c564b","#c49c94","#e377c2","#f7b6d2","#7f7f7f","#c7c7c7","#bcbd22","#dbdb8d","#17becf","#9edae5"],_c=["#393b79","#5254a3","#6b6ecf","#9c9ede","#637939","#8ca252","#b5cf6b","#cedb9c","#8c6d31","#bd9e39","#e7ba52","#e7cb94","#843c39","#ad494a","#d6616b","#e7969c","#7b4173","#a5
5194","#ce6dbd","#de9ed6"],wc=["#3182bd","#6baed6","#9ecae1","#c6dbef","#e6550d","#fd8d3c","#fdae6b","#fdd0a2","#31a354","#74c476","#a1d99b","#c7e9c0","#756bb1","#9e9ac8","#bcbddc","#dadaeb","#636363","#969696","#bdbdbd","#d9d9d9"];va.scale.quantile=function(){return cu([],[])},va.scale.quantize=function(){return lu(0,1,[0,1])},va.scale.threshold=function(){return fu([.5],[0,1])},va.scale.identity=function(){return su([0,1])},va.svg.arc=function(){function n(){var n=t.apply(this,arguments),u=e.apply(this,arguments),a=r.apply(this,arguments)+Sc,o=i.apply(this,arguments)+Sc,c=(a>o&&(c=a,a=o,o=c),o-a),l=Ba>c?"0":"1",f=Math.cos(a),s=Math.sin(a),h=Math.cos(o),g=Math.sin(o);return c>=Ec?n?"M0,"+u+"A"+u+","+u+" 0 1,1 0,"+-u+"A"+u+","+u+" 0 1,1 0,"+u+"M0,"+n+"A"+n+","+n+" 0 1,0 0,"+-n+"A"+n+","+n+" 0 1,0 0,"+n+"Z":"M0,"+u+"A"+u+","+u+" 0 1,1 0,"+-u+"A"+u+","+u+" 0 1,1 0,"+u+"Z":n?"M"+u*f+","+u*s+"A"+u+","+u+" 0 "+l+",1 "+u*h+","+u*g+"L"+n*h+","+n*g+"A"+n+","+n+" 0 "+l+",0 "+n*f+","+
n*s+"Z":"M"+u*f+","+u*s+"A"+u+","+u+" 0 "+l+",1 "+u*h+","+u*g+"L0,0"+"Z"}var t=hu,e=gu,r=pu,i=du;return n.innerRadius=function(e){return arguments.length?(t=ft(e),n):t},n.outerRadius=function(t){return arguments.length?(e=ft(t),n):e},n.startAngle=function(t){return arguments.length?(r=ft(t),n):r},n.endAngle=function(t){return arguments.length?(i=ft(t),n):i},n.centroid=function(){var n=(t.apply(this,arguments)+e.apply(this,arguments))/2,u=(r.apply(this,arguments)+i.apply(this,arguments))/2+Sc;return[Math.cos(u)*n,Math.sin(u)*n]},n};var Sc=-Ba/2,Ec=2*Ba-1e-6;va.svg.line.radial=function(){var n=Fe(mu);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},Ie.reverse=Ve,Ve.reverse=Ie,va.svg.area=function(){return vu(st)},va.svg.area.radial=function(){var n=vu(mu);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},va.svg.chord=function(){function n(n,o){
var c=t(this,u,n,o),l=t(this,a,n,o);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?i(c.r,c.p1,c.r,c.p0):i(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+i(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var i=t.call(n,e,r),u=o.call(n,i,r),a=c.call(n,i,r)+Sc,f=l.call(n,i,r)+Sc;return{r:u,a0:a,a1:f,p0:[u*Math.cos(a),u*Math.sin(a)],p1:[u*Math.cos(f),u*Math.sin(f)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>Ba)+",1 "+t}function i(n,t,e,r){return"Q 0,0 "+r}var u=Ae,a=Ne,o=yu,c=pu,l=du;return n.radius=function(t){return arguments.length?(o=ft(t),n):o},n.source=function(t){return arguments.length?(u=ft(t),n):u},n.target=function(t){return arguments.length?(a=ft(t),n):a},n.startAngle=function(t){return arguments.length?(c=ft(t),n):c},n.endAngle=function(t){return arguments.length?(l=ft(t),n):l},n},va.svg.diagonal=function(){function n(n,i){var u=t.call(this,n,i),a=e.call(this,n,i),o=(u.y+a.y)/2,c=[u,{x:u.x,y:o},{x:a.x,y:o},a];return c=c.map(r),
"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=Ae,e=Ne,r=Mu;return n.source=function(e){return arguments.length?(t=ft(e),n):t},n.target=function(t){return arguments.length?(e=ft(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},va.svg.diagonal.radial=function(){var n=va.svg.diagonal(),t=Mu,e=n.projection;return n.projection=function(n){return arguments.length?e(xu(t=n)):t},n},va.svg.symbol=function(){function n(n,r){return(kc.get(t.call(this,n,r))||wu)(e.call(this,n,r))}var t=_u,e=bu;return n.type=function(e){return arguments.length?(t=ft(e),n):t},n.size=function(t){return arguments.length?(e=ft(t),n):e},n};var kc=va.map({circle:wu,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*qc)),e=t*qc;return"M0,"+-t+"L"+e+",0"+" 0,"+t+" "+-e+",0"+"Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+",
"+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/Nc),e=t*Nc/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/Nc),e=t*Nc/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});va.svg.symbolTypes=kc.keys();var Ac,Nc=Math.sqrt(3),qc=Math.tan(30*Ja),Tc=[],Cc=0,zc={ease:Cr,delay:0,duration:250};Tc.call=Ha.call,Tc.empty=Ha.empty,Tc.node=Ha.node,Tc.size=Ha.size,va.transition=function(n){return arguments.length?Ac?n.transition():n:Oa.transition()},va.transition.prototype=Tc,Tc.select=function(n){var t,e,r,i=this.id,u=[];"function"!=typeof n&&(n=v(n));for(var a=-1,o=this.length;++a<o;){u.push(t=[]);for(var c=this[a],l=-1,f=c.length;++l<f;)(r=c[l])&&(e=n.call(r,r.__data__,l))?("__data__"in r&&(e.__data__=r.__data__),Au(e,l,i,r.__transition__[i]),t.push(e)):t.push(null)}return Su(u,i)},Tc.selectAll=function(n){var t,e,r,i,u,a=this.id,o=[];"function"!=typeof n&&(n=y(n));for(var c=-1,l=this.length;++c<l;)for(var f=this[c],s=-1,h=f.l
ength;++s<h;)if(r=f[s]){u=r.__transition__[a],e=n.call(r,r.__data__,s),o.push(t=[]);for(var g=-1,p=e.length;++g<p;)(i=e[g])&&Au(i,g,a,u),t.push(i)}return Su(o,a)},Tc.filter=function(n){var t,e,r,i=[];"function"!=typeof n&&(n=A(n));for(var u=0,a=this.length;a>u;u++){i.push(t=[]);for(var e=this[u],o=0,c=e.length;c>o;o++)(r=e[o])&&n.call(r,r.__data__,o)&&t.push(r)}return Su(i,this.id,this.time).ease(this.ease())},Tc.tween=function(n,t){var e=this.id;return arguments.length<2?this.node().__transition__[e].tween.get(n):q(this,null==t?function(t){t.__transition__[e].tween.remove(n)}:function(r){r.__transition__[e].tween.set(n,t)})},Tc.attr=function(n,t){function e(){this.removeAttribute(o)}function r(){this.removeAttributeNS(o.space,o.local)}function i(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(o);return e!==n&&(t=a(e,n),function(n){this.setAttribute(o,t(n))})})}function u(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(o.space,o.local);retur
n e!==n&&(t=a(e,n),function(n){this.setAttributeNS(o.space,o.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var a=Sr(n),o=va.ns.qualify(n);return Eu(this,"attr."+n,t,o.local?u:i)},Tc.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(i));return r&&function(n){this.setAttribute(i,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(i.space,i.local));return r&&function(n){this.setAttributeNS(i.space,i.local,r(n))}}var i=va.ns.qualify(n);return this.tween("attr."+n,i.local?r:e)},Tc.style=function(n,t,e){function r(){this.style.removeProperty(n)}function i(t){return null==t?r:(t+="",function(){var r,i=xa.getComputedStyle(this,null).getPropertyValue(n);return i!==t&&(r=a(i,t),function(t){this.style.setProperty(n,r(t),e)})})}var u=arguments.length;if(3>u){if("string"!=typeof n){2>u&&(t="");for(e in n)this.style(e,n[e],t);return this}e=""}var a=Sr(n);return Eu(this,"style."+n,t,i)},Tc.styleTween=function(n,t,
e){function r(r,i){var u=t.call(this,r,i,xa.getComputedStyle(this,null).getPropertyValue(n));return u&&function(t){this.style.setProperty(n,u(t),e)}}return arguments.length<3&&(e=""),this.tween("style."+n,r)},Tc.text=function(n){return Eu(this,"text",n,ku)},Tc.remove=function(){return this.each("end.transition",function(){var n;!this.__transition__&&(n=this.parentNode)&&n.removeChild(this)})},Tc.ease=function(n){var t=this.id;return arguments.length<1?this.node().__transition__[t].ease:("function"!=typeof n&&(n=va.ease.apply(va,arguments)),q(this,function(e){e.__transition__[t].ease=n}))},Tc.delay=function(n){var t=this.id;return q(this,"function"==typeof n?function(e,r,i){e.__transition__[t].delay=0|n.call(e,e.__data__,r,i)}:(n|=0,function(e){e.__transition__[t].delay=n}))},Tc.duration=function(n){var t=this.id;return q(this,"function"==typeof n?function(e,r,i){e.__transition__[t].duration=Math.max(1,0|n.call(e,e.__data__,r,i))}:(n=Math.max(1,0|n),function(e){e.__transition
__[t].duration=n}))},Tc.each=function(n,t){var e=this.id;if(arguments.length<2){var r=zc,i=Ac;Ac=e,q(this,function(t,r,i){zc=t.__transition__[e],n.call(t,t.__data__,r,i)}),zc=r,Ac=i}else q(this,function(r){r.__transition__[e].event.on(n,t)});return this},Tc.transition=function(){for(var n,t,e,r,i=this.id,u=++Cc,a=[],o=0,c=this.length;c>o;o++){a.push(n=[]);for(var t=this[o],l=0,f=t.length;f>l;l++)(e=t[l])&&(r=Object.create(e.__transition__[i]),r.delay+=r.duration,Au(e,l,u,r)),n.push(e)}return Su(a,u)},va.svg.axis=function(){function n(n){n.each(function(){var n,s=va.select(this),h=null==l?e.ticks?e.ticks.apply(e,c):e.domain():l,g=null==t?e.tickFormat?e.tickFormat.apply(e,c):String:t,p=Tu(e,h,f),d=s.selectAll(".tick.minor").data(p,String),m=d.enter().insert("line",".tick").attr("class","tick minor").style("opacity",1e-6),v=va.transition(d.exit()).style("opacity",1e-6).remove(),y=va.transition(d).style("opacity",1),M=s.selectAll(".tick.major").data(h,String),x=M.enter().insert(
"g",".domain").attr("class","tick major").style("opacity",1e-6),b=va.transition(M.exit()).style("opacity",1e-6).remove(),_=va.transition(M).style("opacity",1),w=Ui(e),S=s.selectAll(".domain").data([0]),E=(S.enter().append("path").attr("class","domain"),va.transition(S)),k=e.copy(),A=this.__chart__||k;this.__chart__=k,x.append("line"),x.append("text");
-var N=x.select("line"),q=_.select("line"),T=M.select("text").text(g),C=x.select("text"),z=_.select("text");switch(r){case"bottom":n=Nu,m.attr("y2",u),y.attr("x2",0).attr("y2",u),N.attr("y2",i),C.attr("y",Math.max(i,0)+o),q.attr("x2",0).attr("y2",i),z.attr("x",0).attr("y",Math.max(i,0)+o),T.attr("dy",".71em").style("text-anchor","middle"),E.attr("d","M"+w[0]+","+a+"V0H"+w[1]+"V"+a);break;case"top":n=Nu,m.attr("y2",-u),y.attr("x2",0).attr("y2",-u),N.attr("y2",-i),C.attr("y",-(Math.max(i,0)+o)),q.attr("x2",0).attr("y2",-i),z.attr("x",0).attr("y",-(Math.max(i,0)+o)),T.attr("dy","0em").style("text-anchor","middle"),E.attr("d","M"+w[0]+","+-a+"V0H"+w[1]+"V"+-a);break;case"left":n=qu,m.attr("x2",-u),y.attr("x2",-u).attr("y2",0),N.attr("x2",-i),C.attr("x",-(Math.max(i,0)+o)),q.attr("x2",-i).attr("y2",0),z.attr("x",-(Math.max(i,0)+o)).attr("y",0),T.attr("dy",".32em").style("text-anchor","end"),E.attr("d","M"+-a+","+w[0]+"H0V"+w[1]+"H"+-a);break;case"right":n=qu,m.attr("x2",u),y.attr(
"x2",u).attr("y2",0),N.attr("x2",i),C.attr("x",Math.max(i,0)+o),q.attr("x2",i).attr("y2",0),z.attr("x",Math.max(i,0)+o).attr("y",0),T.attr("dy",".32em").style("text-anchor","start"),E.attr("d","M"+a+","+w[0]+"H0V"+w[1]+"H"+a)}if(e.ticks)x.call(n,A),_.call(n,k),b.call(n,k),m.call(n,A),y.call(n,k),v.call(n,k);else{var D=k.rangeBand()/2,j=function(n){return k(n)+D};x.call(n,j),_.call(n,j)}})}var t,e=va.scale.linear(),r=Dc,i=6,u=6,a=6,o=3,c=[10],l=null,f=0;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in jc?t+"":Dc,n):r},n.ticks=function(){return arguments.length?(c=arguments,n):c},n.tickValues=function(t){return arguments.length?(l=t,n):l},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t,e){if(!arguments.length)return i;var r=arguments.length-1;return i=+t,u=r>1?+e:i,a=r>0?+arguments[r]:i,n},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(t){
return arguments.length?(f=+t,n):f},n};var Dc="bottom",jc={top:1,right:1,bottom:1,left:1};va.svg.brush=function(){function n(u){u.each(function(){var u,a=va.select(this),f=a.selectAll(".background").data([0]),s=a.selectAll(".extent").data([0]),h=a.selectAll(".resize").data(l,String);a.style("pointer-events","all").on("mousedown.brush",i).on("touchstart.brush",i),f.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),s.enter().append("rect").attr("class","extent").style("cursor","move"),h.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Lc[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),h.style("display",n.empty()?"none":null),h.exit().remove(),o&&(u=Ui(o),f.attr("x",u[0]).attr("width",u[1]-u[0]),e(a)),c&&(u=Ui(c),f.attr("y",u[0]).attr(
"height",u[1]-u[0]),r(a)),t(a)})}function t(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)][0]+","+f[+/^s/.test(n)][1]+")"})}function e(n){n.select(".extent").attr("x",f[0][0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1][0]-f[0][0])}function r(n){n.select(".extent").attr("y",f[0][1]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",f[1][1]-f[0][1])}function i(){function i(){var n=va.event.changedTouches;return n?va.touches(M,n)[0]:va.mouse(M)}function l(){32==va.event.keyCode&&(k||(v=null,N[0]-=f[1][0],N[1]-=f[1][1],k=2),g())}function h(){32==va.event.keyCode&&2==k&&(N[0]+=f[1][0],N[1]+=f[1][1],k=0,g())}function p(){var n=i(),u=!1;y&&(n[0]+=y[0],n[1]+=y[1]),k||(va.event.altKey?(v||(v=[(f[0][0]+f[1][0])/2,(f[0][1]+f[1][1])/2]),N[0]=f[+(n[0]<v[0])][0],N[1]=f[+(n[1]<v[1])][1]):v=null),S&&d(n,o,0)&&(e(_),u=!0),E&&d(n,c,1)&&(r(_),u=!0),u&&(t(_),b({type:"brush",mode:k?"move":"resize"}))}function d(n,t,e){var r,i,a=Ui(t),o
=a[0],c=a[1],l=N[e],h=f[1][e]-f[0][e];return k&&(o-=l,c-=h+l),r=s[e]?Math.max(o,Math.min(c,n[e])):n[e],k?i=(r+=l)+h:(v&&(l=Math.max(o,Math.min(c,2*v[e]-r))),r>l?(i=r,r=l):i=l),f[0][e]!==r||f[1][e]!==i?(u=null,f[0][e]=r,f[1][e]=i,!0):void 0}function m(){p(),_.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),va.select("body").style("cursor",null),q.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),A(),b({type:"brushend"})}var v,y,M=this,x=va.select(va.event.target),b=a.of(M,arguments),_=va.select(M),w=x.datum(),S=!/^(n|s)$/.test(w)&&o,E=!/^(e|w)$/.test(w)&&c,k=x.classed("extent"),A=j("brush"),N=i(),q=va.select(xa).on("keydown.brush",l).on("keyup.brush",h);if(va.event.changedTouches?q.on("touchmove.brush",p).on("touchend.brush",m):q.on("mousemove.brush",p).on("mouseup.brush",m),k)N[0]=f[0][0]-N[0],N[1]=f[0][1]-N[1];else if(w){var T=+/w$
/.test(w),C=+/^n/.test(w);y=[f[1-T][0]-N[0],f[1-C][1]-N[1]],N[0]=f[T][0],N[1]=f[C][1]}else va.event.altKey&&(v=N.slice());_.style("pointer-events","none").selectAll(".resize").style("display",null),va.select("body").style("cursor",x.style("cursor")),b({type:"brushstart"}),p()}var u,a=d(n,"brushstart","brush","brushend"),o=null,c=null,l=Hc[0],f=[[0,0],[0,0]],s=[!0,!0];return n.x=function(t){return arguments.length?(o=t,l=Hc[!o<<1|!c],n):o},n.y=function(t){return arguments.length?(c=t,l=Hc[!o<<1|!c],n):c},n.clamp=function(t){return arguments.length?(o&&c?s=[!!t[0],!!t[1]]:(o||c)&&(s[+!o]=!!t),n):o&&c?s:o||c?s[+!o]:null},n.extent=function(t){var e,r,i,a,l;return arguments.length?(u=[[0,0],[0,0]],o&&(e=t[0],r=t[1],c&&(e=e[0],r=r[0]),u[0][0]=e,u[1][0]=r,o.invert&&(e=o(e),r=o(r)),e>r&&(l=e,e=r,r=l),f[0][0]=0|e,f[1][0]=0|r),c&&(i=t[0],a=t[1],o&&(i=i[1],a=a[1]),u[0][1]=i,u[1][1]=a,c.invert&&(i=c(i),a=c(a)),i>a&&(l=i,i=a,a=l),f[0][1]=0|i,f[1][1]=0|a),n):(t=u||f,o&&(e=t[0][0],r=t[1][0
],u||(e=f[0][0],r=f[1][0],o.invert&&(e=o.invert(e),r=o.invert(r)),e>r&&(l=e,e=r,r=l))),c&&(i=t[0][1],a=t[1][1],u||(i=f[0][1],a=f[1][1],c.invert&&(i=c.invert(i),a=c.invert(a)),i>a&&(l=i,i=a,a=l))),o&&c?[[e,i],[r,a]]:o?[e,r]:c&&[i,a])},n.clear=function(){return u=null,f[0][0]=f[0][1]=f[1][0]=f[1][1]=0,n},n.empty=function(){return o&&f[0][0]===f[1][0]||c&&f[0][1]===f[1][1]},va.rebind(n,a,"on")};var Lc={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Hc=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]];va.time={};var Fc=Date,Pc=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];Cu.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){r
eturn this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){Oc.setUTCDate.apply(this._,arguments)},setDay:function(){Oc.setUTCDay.apply(this._,arguments)},setFullYear:function(){Oc.setUTCFullYear.apply(this._,arguments)},setHours:function(){Oc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){Oc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){Oc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){Oc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){Oc.setUTCSeconds.apply(this._,arguments)},setTime:function(){Oc.setTime.apply(this._,arguments)}};var Oc=Date.prototype,Rc="%a %b %e %X %Y",Yc="%m/%d/%Y",Uc="%H:%M:%S",Ic=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],Vc=["Sun","Mon","Tue","Wed
","Thu","Fri","Sat"],Xc=["January","February","March","April","May","June","July","August","September","October","November","December"],Zc=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];va.time.year=zu(function(n){return n=va.time.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),va.time.years=va.time.year.range,va.time.years.utc=va.time.year.utc.range,va.time.day=zu(function(n){var t=new Fc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),va.time.days=va.time.day.range,va.time.days.utc=va.time.day.utc.range,va.time.dayOfYear=function(n){var t=va.time.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},Pc.forEach(function(n,t){n=n.toLowerCase(),t=7-t;var e=va.time[n]=zu(function(n){return(n=va.time.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},functio
n(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=va.time.year(n).getDay();return Math.floor((va.time.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});va.time[n+"s"]=e.range,va.time[n+"s"].utc=e.utc.range,va.time[n+"OfYear"]=function(n){var e=va.time.year(n).getDay();return Math.floor((va.time.dayOfYear(n)+(e+t)%7)/7)}}),va.time.week=va.time.sunday,va.time.weeks=va.time.sunday.range,va.time.weeks.utc=va.time.sunday.utc.range,va.time.weekOfYear=va.time.sundayOfYear,va.time.format=function(n){function t(t){for(var r,i,u,a=[],o=-1,c=0;++o<e;)37===n.charCodeAt(o)&&(a.push(n.substring(c,o)),null!=(i=el[r=n.charAt(++o)])&&(r=n.charAt(++o)),(u=rl[r])&&(r=u(t,null==i?"e"===r?" ":"0":i)),a.push(r),c=o+1);return a.push(n.substring(c,o)),a.join("")}var e=n.length;return t.parse=function(t){var e={y:1900,m:0,d:1,H:0,M:0,S:0,L:0},r=ju(e,n,t,0);if(r!=t.length)return null;"p"in e&&(e.H=e.H%12+12*e.p);var i=new Fc;return"j"in e?i.setFullYear(e.y,0,e.j):"w"in e&&("W"in e||"U"in e)?(i.setFul
lYear(e.y,0,1),i.setFullYear(e.y,0,"W"in e?(e.w+6)%7+7*e.W-(i.getDay()+5)%7:e.w+7*e.U-(i.getDay()+6)%7)):i.setFullYear(e.y,e.m,e.d),i.setHours(e.H,e.M,e.S,e.L),i},t.toString=function(){return n},t};var Bc=Lu(Ic),$c=Hu(Ic),Wc=Lu(Vc),Jc=Hu(Vc),Gc=Lu(Xc),Kc=Hu(Xc),Qc=Lu(Zc),nl=Hu(Zc),tl=/^%/,el={"-":"",_:" ",0:"0"},rl={a:function(n){return Vc[n.getDay()]},A:function(n){return Ic[n.getDay()]},b:function(n){return Zc[n.getMonth()]},B:function(n){return Xc[n.getMonth()]},c:va.time.format(Rc),d:function(n,t){return Fu(n.getDate(),t,2)},e:function(n,t){return Fu(n.getDate(),t,2)},H:function(n,t){return Fu(n.getHours(),t,2)},I:function(n,t){return Fu(n.getHours()%12||12,t,2)},j:function(n,t){return Fu(1+va.time.dayOfYear(n),t,3)},L:function(n,t){return Fu(n.getMilliseconds(),t,3)},m:function(n,t){return Fu(n.getMonth()+1,t,2)},M:function(n,t){return Fu(n.getMinutes(),t,2)},p:function(n){return n.getHours()>=12?"PM":"AM"},S:function(n,t){return Fu(n.getSeconds(),t,2)},U:function(n,t){
return Fu(va.time.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return Fu(va.time.mondayOfYear(n),t,2)},x:va.time.format(Yc),X:va.time.format(Uc),y:function(n,t){return Fu(n.getFullYear()%100,t,2)},Y:function(n,t){return Fu(n.getFullYear()%1e4,t,4)},Z:ua,"%":function(){return"%"}},il={a:Pu,A:Ou,b:Iu,B:Vu,c:Xu,d:Ku,e:Ku,H:na,I:na,j:Qu,L:ra,m:Gu,M:ta,p:ia,S:ea,U:Yu,w:Ru,W:Uu,x:Zu,X:Bu,y:Wu,Y:$u,"%":aa},ul=/^\s*\d+/,al=va.map({am:0,pm:1});va.time.format.utc=function(n){function t(n){try{Fc=Cu;var t=new Fc;return t._=n,e(t)}finally{Fc=Date}}var e=va.time.format(n);return t.parse=function(n){try{Fc=Cu;var t=e.parse(n);return t&&t._}finally{Fc=Date}},t.toString=e.toString,t};var ol=va.time.format.utc("%Y-%m-%dT%H:%M:%S.%LZ");va.time.format.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?oa:ol,oa.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},oa.toString=ol.toString,va.time.second=zu(function(n){return new Fc(1e3*Math.floo
r(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),va.time.seconds=va.time.second.range,va.time.seconds.utc=va.time.second.utc.range,va.time.minute=zu(function(n){return new Fc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),va.time.minutes=va.time.minute.range,va.time.minutes.utc=va.time.minute.utc.range,va.time.hour=zu(function(n){var t=n.getTimezoneOffset()/60;return new Fc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),va.time.hours=va.time.hour.range,va.time.hours.utc=va.time.hour.utc.range,va.time.month=zu(function(n){return n=va.time.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),va.time.months=va.time.month.range,va.time.months.utc=va.time.month.utc.range;var cl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e
5,1728e5,6048e5,2592e6,7776e6,31536e6],ll=[[va.time.second,1],[va.time.second,5],[va.time.second,15],[va.time.second,30],[va.time.minute,1],[va.time.minute,5],[va.time.minute,15],[va.time.minute,30],[va.time.hour,1],[va.time.hour,3],[va.time.hour,6],[va.time.hour,12],[va.time.day,1],[va.time.day,2],[va.time.week,1],[va.time.month,1],[va.time.month,3],[va.time.year,1]],fl=[[va.time.format("%Y"),Rt],[va.time.format("%B"),function(n){return n.getMonth()}],[va.time.format("%b %d"),function(n){return 1!=n.getDate()}],[va.time.format("%a %d"),function(n){return n.getDay()&&1!=n.getDate()}],[va.time.format("%I %p"),function(n){return n.getHours()}],[va.time.format("%I:%M"),function(n){return n.getMinutes()}],[va.time.format(":%S"),function(n){return n.getSeconds()}],[va.time.format(".%L"),function(n){return n.getMilliseconds()}]],sl=va.scale.linear(),hl=fa(fl);ll.year=function(n,t){return sl.domain(n.map(ha)).ticks(t).map(sa)},va.time.scale=function(){return ca(va.scale.linear(),ll
,hl)};var gl=ll.map(function(n){return[n[0].utc,n[1]]}),pl=[[va.time.format.utc("%Y"),Rt],[va.time.format.utc("%B"),function(n){return n.getUTCMonth()}],[va.time.format.utc("%b %d"),function(n){return 1!=n.getUTCDate()}],[va.time.format.utc("%a %d"),function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],[va.time.format.utc("%I %p"),function(n){return n.getUTCHours()}],[va.time.format.utc("%I:%M"),function(n){return n.getUTCMinutes()}],[va.time.format.utc(":%S"),function(n){return n.getUTCSeconds()}],[va.time.format.utc(".%L"),function(n){return n.getUTCMilliseconds()}]],dl=fa(pl);return gl.year=function(n,t){return sl.domain(n.map(pa)).ticks(t).map(ga)},va.time.scale.utc=function(){return ca(va.scale.linear(),gl,dl)},va.text=ht(function(n){return n.responseText}),va.json=function(n,t){return gt(n,"application/json",da,t)},va.html=function(n,t){return gt(n,"text/html",ma,t)},va.xml=ht(function(n){return n.responseXML}),va}();
\ No newline at end of file
commit 8df7802718bf3b73c7f5c4748cce27d6522d3c8e
Author: John Sanda <jsanda(a)redhat.com>
Date: Fri Dec 20 15:31:58 2013 -0500
[BZ 1042663] upgrade datastax C* to 1.0.5
diff --git a/modules/enterprise/server/server-metrics/pom.xml b/modules/enterprise/server/server-metrics/pom.xml
index 122d7a5..866582d 100644
--- a/modules/enterprise/server/server-metrics/pom.xml
+++ b/modules/enterprise/server/server-metrics/pom.xml
@@ -70,6 +70,7 @@
<version>2.1</version>
</dependency>
+ <!-- cassandra driver dep -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
diff --git a/pom.xml b/pom.xml
index 77a3dcd..e6ab31a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -181,8 +181,8 @@
<!-- cassandra dependency versions -->
<cassandra.version>1.2.9</cassandra.version>
<cassandra.thrift.version>0.7.0</cassandra.thrift.version>
- <cassandra.driver.version>1.0.2</cassandra.driver.version>
- <cassandra.driver.netty.version>3.6.3.Final</cassandra.driver.netty.version>
+ <cassandra.driver.version>1.0.5</cassandra.driver.version>
+ <cassandra.driver.netty.version>3.7.0.Final</cassandra.driver.netty.version>
<cassandra.snappy.version>1.0.4.1-rhq-p1</cassandra.snappy.version>
<cassandra.snakeyaml.version>1.6</cassandra.snakeyaml.version>
commit 4b20611ece3aa37ede9b89ca90383eba5647b799
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Oct 2 17:36:21 2013 -0400
[BZ 1009945] squash commit of jsanda/aggregation branch
commit 1 - Initial commit for new Aggregator class
This is a first pass at computing multiple aggregates concurrently.
Conflicts:
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
commit 2 - update 6 hr index after inserting 1 hr data
commit 3 -initial support for generating 6 hr and 24 hr data
Conflicts:
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
commit 4 - delete 1 hr index entries when aggregation of time slice data is finished
commit 5 - handle scenarios for when there is no 1 hr and/or 6 hr data
This commit also includes some major test changes. AggregationTests is a more
thorough set (of not yet complete) tests to provide better coverage.
commit 6 - finishing test for 24 hour data
Conflicts:
modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/AggregationTests.java
commit 7 - adding initial test for failure scenario
This test is for when we fail to fetch the raw data index.
commit 8 - This is a big refactoring of the initial implementation of Aggregator.java
The overall design is the same in terms of the fan out approach for doing the
calculations, but some changes were necessary after an extensive performance
analysis. Initially the aggregation tasks were to granular. Tasks were
submitted to the thread pool to process a single schedule. Schedules are now
processed in batches of 250. I did a lot of performance testing with various
batch sizes, and 250 seems optimal both in terms of execution time as well as
memory consumption. The other major change involves throttling. The initial
implementation had no throttling in place which immediately caused read and/or
write timeouts. Then I put throttling in place using RateLimiters, but there
was separate throttling that is used in MetricsServer for inserting raw data.
The same throttling is now used by both MetricsServer and Aggregator. There
are actually separate rate limiters for reads and for writes. Both are
configurable as well.
Conflicts:
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
commit 9 - adding back support for aggregating 6 hour data
commit 10 - fix logic for determining if aggregation has finished
We check the respective counters to see if raw, 1 hr, and 6 hr data aggregation
has finished. Prior to checking the counters though, we need to make sure that
they have been initialized. They do not get initialized until the respective
index entries arrive. I have refactored the logic into a new method,
isAggregationFinished. It first waits for the arrival of all the index entries
and then checks the counters.
commit 11 - set counters for remaining data when we fail to get index entries for 1 and 6 hr data
commit 12 - refactoring some duplicate code and fixing when tasks are scheduled
Aggregation tasks for 1 and 6 hour data were getting schedule too early. They
cannot get scheduled until both their respective index entries have arrived and
aggregation for the previous buckets has completed.
commit 13 - make connection pool sizes and rate limits configurable
Connection pools per host and rate limits are configurable via system
properties. While the connection pool sizes are only set at start up, I plan to
make the rate limits self-tuning.
This commit also adds the missing method body to kick off 6 hour data
aggregation in Aggregate1HourData.java.
Conflicts:
modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
commit 14 - add support for running async aggregation in simulator
commit 15 - eliminate a couple possible race condition when processing index entries
commit 16 - cleaning up logging and adding some javadocs
Also moving all aggregation related classes into the org.rhq.server.metrics.aggregation
package. All classes except Aggregator now have package-level access.
commit 17 - updating test with package refactoring
commit 18 - turn on async aggregation by default
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
index dc8c554..7d390f4 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/storage/StorageClientManagerBean.java
@@ -41,6 +41,8 @@ import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import com.datastax.driver.core.Cluster;
+import com.datastax.driver.core.HostDistance;
+import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.ProtocolOptions;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
@@ -355,6 +357,16 @@ public class StorageClientManagerBean {
.withRetryPolicy(new LoggingRetryPolicy(DefaultRetryPolicy.INSTANCE)).withCompression(
ProtocolOptions.Compression.NONE).build();
+ PoolingOptions poolingOptions = cluster.getConfiguration().getPoolingOptions();
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.LOCAL, Integer.parseInt(
+ System.getProperty("rhq.storage.client.local-connections", "24")));
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.REMOTE, Integer.parseInt(
+ System.getProperty("rhq.storage.client.remote-connections", "16")));
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.LOCAL, Integer.parseInt(
+ System.getProperty("rhq.storage.client.max-local-connections", "32")));
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.REMOTE, Integer.parseInt(
+ System.getProperty("rhq.storage.client.max-remote-connections", "24")));
+
return cluster.connect(RHQ_KEYSPACE);
}
diff --git a/modules/enterprise/server/server-metrics/pom.xml b/modules/enterprise/server/server-metrics/pom.xml
index 0b4c141..122d7a5 100644
--- a/modules/enterprise/server/server-metrics/pom.xml
+++ b/modules/enterprise/server/server-metrics/pom.xml
@@ -157,6 +157,9 @@
<rhq.storage.cluster.dir>${rhq.storage.cluster.dir}</rhq.storage.cluster.dir>
<rhq.storage.cluster.deploy>${deployCluster}</rhq.storage.cluster.deploy>
</systemPropertyVariables>
+ <excludes>
+ <exclude>**/MetricsPerfTests.java</exclude>
+ </excludes>
</configuration>
</plugin>
</plugins>
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/AbortedException.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/AbortedException.java
new file mode 100644
index 0000000..cab2ab0
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/AbortedException.java
@@ -0,0 +1,23 @@
+package org.rhq.server.metrics;
+
+/**
+ * @author John Sanda
+ */
+public class AbortedException extends Exception {
+
+ public AbortedException() {
+ super();
+ }
+
+ public AbortedException(String message) {
+ super(message);
+ }
+
+ public AbortedException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AbortedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
index e62545e..66d0493 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsDAO.java
@@ -154,17 +154,36 @@ public class MetricsDAO {
return storageSession.execute(statement);
}
+ public StorageResultSetFuture insertOneHourDataAsync(int scheduleId, long timestamp, AggregateType type,
+ double value) {
+ BoundStatement statement = insertOneHourData.bind(scheduleId, new Date(timestamp), type.ordinal(), value);
+ return storageSession.executeAsync(statement);
+ }
+
public ResultSet insertSixHourData(int scheduleId, long timestamp, AggregateType type, double value) {
BoundStatement statement = insertSixHourData.bind(scheduleId, new Date(timestamp), type.ordinal(), value);
return storageSession.execute(statement);
}
+ public StorageResultSetFuture insertSixHourDataAsync(int scheduleId, long timestamp, AggregateType type,
+ double value) {
+ BoundStatement statement = insertSixHourData.bind(scheduleId, new Date(timestamp), type.ordinal(), value);
+ return storageSession.executeAsync(statement);
+ }
+
public ResultSet insertTwentyFourHourData(int scheduleId, long timestamp, AggregateType type, double value) {
BoundStatement statement = insertTwentyFourHourData.bind(scheduleId, new Date(timestamp), type.ordinal(),
value);
return storageSession.execute(statement);
}
+ public StorageResultSetFuture insertTwentyFourHourDataAsync(int scheduleId, long timestamp, AggregateType type,
+ double value) {
+ BoundStatement statement = insertTwentyFourHourData.bind(scheduleId, new Date(timestamp), type.ordinal(),
+ value);
+ return storageSession.executeAsync(statement);
+ }
+
public Iterable<RawNumericMetric> findRawMetrics(int scheduleId, long startTime, long endTime) {
try {
BoundStatement boundStatement = rawMetricsQuery.bind(scheduleId, new Date(startTime), new Date(endTime));
@@ -175,6 +194,11 @@ public class MetricsDAO {
}
}
+ public ResultSet findRawMetricsSync(int scheduleId, long startTime, long endTime) {
+ BoundStatement boundStatement = rawMetricsQuery.bind(scheduleId, new Date(startTime), new Date(endTime));
+ return storageSession.execute(boundStatement);
+ }
+
public StorageResultSetFuture findRawMetricsAsync(int scheduleId, long startTime, long endTime) {
BoundStatement boundStatement = rawMetricsQuery.bind(scheduleId, new Date(startTime), new Date(endTime));
return storageSession.executeAsync(boundStatement);
@@ -212,8 +236,7 @@ public class MetricsDAO {
}
public StorageResultSetFuture findSixHourMetricsAsync(int scheduleId, long startTime, long endTime) {
- BoundStatement statement = findSixHourMetricsByDateRange.bind(scheduleId, new Date(startTime),
- new Date(endTime));
+ BoundStatement statement = findSixHourMetricsByDateRange.bind(scheduleId, new Date(startTime), new Date(endTime));
return storageSession.executeAsync(statement);
}
@@ -260,6 +283,11 @@ public class MetricsDAO {
return new SimplePagedResult<MetricsIndexEntry>(statement, new MetricsIndexEntryMapper(table), storageSession);
}
+ public StorageResultSetFuture findMetricsIndexEntriesAsync(MetricsTable table, long timestamp) {
+ BoundStatement statement = findIndexEntries.bind(table.toString(), new Date(timestamp));
+ return storageSession.executeAsync(statement);
+ }
+
public ResultSet setFindTimeSliceForIndex(MetricsTable table, long timestamp) {
BoundStatement statement = findTimeSliceForIndex.bind(table.toString(), new Date(timestamp));
return storageSession.execute(statement);
@@ -282,4 +310,9 @@ public class MetricsDAO {
BoundStatement statement = deleteIndexEntries.bind(table.getTableName(), new Date(timestamp));
storageSession.execute(statement);
}
+
+ public StorageResultSetFuture deleteMetricsIndexEntriesAsync(MetricsTable table, long timestamp) {
+ BoundStatement statement = deleteIndexEntries.bind(table.getTableName(), new Date(timestamp));
+ return storageSession.executeAsync(statement);
+ }
}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
index 33528e6..d319146 100644
--- a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/MetricsServer.java
@@ -32,7 +32,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import java.util.concurrent.Semaphore;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -43,6 +43,9 @@ import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.RateLimiter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -52,6 +55,7 @@ import org.joda.time.Duration;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.server.metrics.aggregation.Aggregator;
import org.rhq.server.metrics.domain.AggregateNumericMetric;
import org.rhq.server.metrics.domain.AggregateType;
import org.rhq.server.metrics.domain.MetricsIndexEntry;
@@ -71,7 +75,11 @@ public class MetricsServer {
private MetricsConfiguration configuration;
- private Semaphore semaphore = new Semaphore(100);
+ private RateLimiter readPermits = RateLimiter.create(Integer.parseInt(
+ System.getProperty("rhq.storage.read-limit", "1000")), 3, TimeUnit.MINUTES);
+
+ private RateLimiter writePermits = RateLimiter.create(Integer.parseInt(
+ System.getProperty("rhq.storage.write-limit", "2500")), 3, TimeUnit.MINUTES);
private boolean pastAggregationMissed;
@@ -79,6 +87,13 @@ public class MetricsServer {
private AtomicLong totalAggregationTime = new AtomicLong();
+ private ListeningExecutorService aggregationWorkers = MoreExecutors.listeningDecorator(
+ Executors.newFixedThreadPool(5));
+
+ private int aggregationBatchSize;
+
+ private boolean useAsyncAggregation = Boolean.valueOf(System.getProperty("rhq.metrics.aggregation.async", "true"));
+
public void setDAO(MetricsDAO dao) {
this.dao = dao;
}
@@ -91,7 +106,34 @@ public class MetricsServer {
this.dateTimeService = dateTimeService;
}
+ public void setAggregationBatchSize(int batchSize) {
+ aggregationBatchSize = batchSize;
+ }
+
+ public void setUseAsyncAggregation(boolean useAsyncAggregation) {
+ this.useAsyncAggregation = useAsyncAggregation;
+ }
+
+ public RateLimiter getReadPermits() {
+ return readPermits;
+ }
+
+ public void setReadPermits(RateLimiter readPermits) {
+ this.readPermits = readPermits;
+ }
+
+ public RateLimiter getWritePermits() {
+ return writePermits;
+ }
+
+ public void setWritePermits(RateLimiter writePermits) {
+ this.writePermits = writePermits;
+ }
+
public void init() {
+ if (log.isDebugEnabled() && useAsyncAggregation) {
+ log.debug("Async aggregation is enabled");
+ }
determineMostRecentRawDataSinceLastShutdown();
}
@@ -131,6 +173,11 @@ public class MetricsServer {
}
}
+ private boolean hasTimeSliceEnded(DateTime startTime, Duration duration) {
+ DateTime endTime = startTime.plus(duration);
+ return DateTimeComparator.getInstance().compare(currentHour(), endTime) >= 0;
+ }
+
protected DateTime currentHour() {
return dateTimeService.getTimeSlice(dateTimeService.now(), configuration.getRawTimeSliceDuration());
}
@@ -140,6 +187,7 @@ public class MetricsServer {
}
public void shutdown() {
+ aggregationWorkers.shutdown();
}
public RawNumericMetric findLatestValueForResource(int scheduleId) {
@@ -363,7 +411,7 @@ public class MetricsServer {
final AtomicInteger remainingInserts = new AtomicInteger(dataSet.size());
for (final MeasurementDataNumeric data : dataSet) {
- semaphore.acquire();
+ writePermits.acquire();
StorageResultSetFuture resultSetFuture = dao.insertRawData(data);
Futures.addCallback(resultSetFuture, new FutureCallback<ResultSet>() {
@Override
@@ -381,7 +429,6 @@ public class MetricsServer {
throwable.getClass().getName() + ": " + throwable.getMessage());
}
callback.onFailure(throwable);
- semaphore.release();
}
});
}
@@ -396,6 +443,7 @@ public class MetricsServer {
long timeSlice = dateTimeService.getTimeSlice(new DateTime(rawData.getTimestamp()),
configuration.getRawTimeSliceDuration()).getMillis();
+ writePermits.acquire();
StorageResultSetFuture resultSetFuture = dao.updateMetricsIndex(MetricsTable.ONE_HOUR, rawData.getScheduleId(),
timeSlice);
Futures.addCallback(resultSetFuture, new FutureCallback<ResultSet>() {
@@ -409,7 +457,6 @@ public class MetricsServer {
}
callback.onFinish();
}
- semaphore.release();
}
@Override
@@ -417,7 +464,6 @@ public class MetricsServer {
log.error("An error occurred while trying to update " + MetricsTable.INDEX + " for raw data " +
rawData);
callback.onFailure(throwable);
- semaphore.release();
}
});
}
@@ -431,14 +477,24 @@ public class MetricsServer {
* for subsequently computing baselines.
*/
public Iterable<AggregateNumericMetric> calculateAggregates() {
- DateTime theHour = currentHour();
+ long start = System.currentTimeMillis();
+ try {
+ DateTime theHour = currentHour();
- if (pastAggregationMissed) {
- calculateAggregates(roundDownToHour(mostRecentRawDataPriorToStartup).plusHours(1).getMillis());
- pastAggregationMissed = false;
- return calculateAggregates(theHour.getMillis());
- } else {
- return calculateAggregates(theHour.getMillis());
+ if (pastAggregationMissed) {
+ theHour = roundDownToHour(mostRecentRawDataPriorToStartup).plusHours(1);
+ pastAggregationMissed = false;
+ }
+
+ if (useAsyncAggregation) {
+ DateTime timeSlice = theHour.minus(configuration.getRawTimeSliceDuration());
+ return new Aggregator(aggregationWorkers, dao, configuration, dateTimeService, timeSlice,
+ aggregationBatchSize, writePermits, readPermits).run();
+ } else {
+ return calculateAggregates(theHour.getMillis());
+ }
+ } finally {
+ log.info("Finished metrics aggregation in " + (System.currentTimeMillis() - start) + " ms");
}
}
@@ -460,16 +516,6 @@ public class MetricsServer {
long twentyFourHourTimeSlice = dateTimeService.getTimeSlice(lastHour,
configuration.getSixHourTimeSliceDuration()).getMillis();
- // We first query the metrics index table to determine which schedules have data to
- // be aggregated. Then we retrieve the metric data and aggregate or compress the
- // data, writing the compressed values into the next wider (i.e., longer life span
- // for data) bucket/table. At this point we remove the index entries for the data
- // that has already been processed. We purge the entire row in the index table.
- // We can safely do this because the row wi..
- //
- // The last step in the work flow is to update the metrics
- // index for the newly persisted aggregates.
-
List<AggregateNumericMetric> newOneHourAggregates = null;
Stopwatch stopwatch = new Stopwatch().start();
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/SignalingCountDownLatch.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/SignalingCountDownLatch.java
new file mode 100644
index 0000000..2d0d190
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/SignalingCountDownLatch.java
@@ -0,0 +1,34 @@
+package org.rhq.server.metrics;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * @author John Sanda
+ */
+public class SignalingCountDownLatch {
+
+ private boolean aborted;
+
+ private CountDownLatch latch;
+
+ public SignalingCountDownLatch(CountDownLatch latch) {
+ this.latch = latch;
+ }
+
+ public void await() throws InterruptedException, AbortedException {
+ latch.await();
+ if (aborted) {
+ throw new AbortedException();
+ }
+ }
+
+ public void abort() {
+ aborted = true;
+ latch.countDown();
+ }
+
+ public void countDown() {
+ latch.countDown();
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate1HourData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate1HourData.java
new file mode 100644
index 0000000..1698b28
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate1HourData.java
@@ -0,0 +1,127 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.server.metrics.AbortedException;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+
+/**
+ * Generates 6 hour data for a batch of 1 hour data futures. After data is inserted for the batch, aggregation of 6 hour
+ * data will start immediately for the batch if the 24 hour time slice has finished.
+ *
+ * @see Compute6HourData
+ * @author John Sanda
+ */
+class Aggregate1HourData implements Runnable {
+
+ private final Log log = LogFactory.getLog(Aggregate1HourData.class);
+
+ private MetricsDAO dao;
+
+ private AggregationState state;
+
+ private Set<Integer> scheduleIds;
+
+ private List<StorageResultSetFuture> queryFutures;
+
+ public Aggregate1HourData(MetricsDAO dao, AggregationState state, Set<Integer> scheduleIds,
+ List<StorageResultSetFuture> queryFutures) {
+ this.dao = dao;
+ this.state = state;
+ this.scheduleIds = scheduleIds;
+ this.queryFutures = queryFutures;
+ }
+
+ @Override
+ public void run() {
+ final Stopwatch stopwatch = new Stopwatch().start();
+ ListenableFuture<List<ResultSet>> queriesFuture = Futures.successfulAsList(queryFutures);
+ Futures.withFallback(queriesFuture, new FutureFallback<List<ResultSet>>() {
+ @Override
+ public ListenableFuture<List<ResultSet>> create(Throwable t) throws Exception {
+ log.error("An error occurred while fetching one hour data", t);
+ return Futures.immediateFailedFuture(t);
+ }
+ });
+ ListenableFuture<List<ResultSet>> computeFutures = Futures.transform(queriesFuture,
+ state.getCompute6HourData(), state.getAggregationTasks());
+ Futures.addCallback(computeFutures, new FutureCallback<List<ResultSet>>() {
+ @Override
+ public void onSuccess(List<ResultSet> result) {
+ stopwatch.stop();
+ log.debug("Finished aggregating 1 hour data for " + result.size() + " schedules in " +
+ stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ start6HourDataAggregationIfNecessary();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ // TODO should we log the schedule ids?
+ log.debug("Failed to aggregate 1 hour data for " + scheduleIds.size() + " schedules. An " +
+ "unexpected error occurred.", t);
+ } else {
+ log.warn("Failed to aggregate 1 hour data for " + scheduleIds.size() + " schedules. An " +
+ "unexpected error occurred: " + ThrowableUtil.getRootMessage(t));
+ }
+ start6HourDataAggregationIfNecessary();
+ }
+ });
+ }
+
+ private void start6HourDataAggregationIfNecessary() {
+ try {
+ if (state.is24HourTimeSliceFinished()) {
+ update6HourIndexEntries();
+ List<StorageResultSetFuture> queryFutures = new ArrayList<StorageResultSetFuture>(scheduleIds.size());
+ for (Integer scheduleId : scheduleIds) {
+ queryFutures.add(dao.findSixHourMetricsAsync(scheduleId, state.getTwentyFourHourTimeSlice().getMillis(),
+ state.getTwentyFourHourTimeSliceEnd().getMillis()));
+ }
+ state.getAggregationTasks().submit(new Aggregate6HourData(dao, state, scheduleIds, queryFutures));
+ }
+ } catch (InterruptedException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("An interrupt occurred while waiting for 6 hour data index entries. Aborting data aggregation",
+ e);
+ } else {
+ log.info("An interrupt occurred while waiting for 6 hour data index entries. Aborting data " +
+ "aggregation: " + e.getMessage());
+ }
+ } finally {
+ state.getRemaining1HourData().addAndGet(-scheduleIds.size());
+ }
+ }
+
+ private void update6HourIndexEntries() throws InterruptedException {
+ try {
+ state.getSixHourIndexEntriesArrival().await();
+ try {
+ state.getSixHourIndexEntriesLock().writeLock().lock();
+ state.getSixHourIndexEntries().removeAll(scheduleIds);
+ } finally {
+ state.getSixHourIndexEntriesLock().writeLock().unlock();
+ }
+ } catch (AbortedException e) {
+ // This means we failed to retrieve the index entries. We can however
+ // continue generating 6 hour data because we do not need the index
+ // here since we already have 6 hour data to aggregate along with the
+ // schedule ids.
+ }
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate6HourData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate6HourData.java
new file mode 100644
index 0000000..fbd5057
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregate6HourData.java
@@ -0,0 +1,84 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+
+/**
+ * Generates 24 hour data for a batch of 1 hour data futures. After data is inserted for the batch, aggregation of 6
+ * hour data will start immediately for the batch if the 24 hour time slice has finished.
+ *
+ * @see Compute24HourData
+ * @author John Sanda
+ */
+class Aggregate6HourData implements Runnable {
+
+ private final Log log = LogFactory.getLog(Aggregate6HourData.class);
+
+ private MetricsDAO dao;
+
+ private AggregationState state;
+
+ private Set<Integer> scheduleIds;
+
+ private List<StorageResultSetFuture> queryFutures;
+
+ public Aggregate6HourData(MetricsDAO dao, AggregationState state, Set<Integer> scheduleIds,
+ List<StorageResultSetFuture> queryFutures) {
+ this.dao = dao;
+ this.state = state;
+ this.scheduleIds = scheduleIds;
+ this.queryFutures = queryFutures;
+ }
+
+ @Override
+ public void run() {
+ final Stopwatch stopwatch = new Stopwatch().start();
+ ListenableFuture<List<ResultSet>> queriesFuture = Futures.successfulAsList(queryFutures);
+ Futures.withFallback(queriesFuture, new FutureFallback<List<ResultSet>>() {
+ @Override
+ public ListenableFuture<List<ResultSet>> create(Throwable t) throws Exception {
+ log.error("An error occurred while fetching 6 hour data", t);
+ return Futures.immediateFailedFuture(t);
+ }
+ });
+ ListenableFuture<List<ResultSet>> computeFutures = Futures.transform(queriesFuture,
+ state.getCompute24HourData(), state.getAggregationTasks());
+ Futures.addCallback(computeFutures, new FutureCallback<List<ResultSet>>() {
+ @Override
+ public void onSuccess(List<ResultSet> result) {
+ stopwatch.stop();
+ log.debug("Finished aggregating 6 hour data for " + result.size() + " schedules in " +
+ stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ state.getRemaining6HourData().addAndGet(-scheduleIds.size());
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ // TODO should we log the schedule ids?
+ log.debug("Failed to aggregate 6 hour data for " + scheduleIds.size() + " schedules. An " +
+ "unexpected error occurred.", t);
+ } else {
+ log.warn("Failed to aggregate 6 hour data for " + scheduleIds.size() + " schedules. An " +
+ "unexpected error occurred: " + ThrowableUtil.getRootMessage(t));
+ }
+ state.getRemaining6HourData().addAndGet(-scheduleIds.size());
+ }
+ });
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateIndexEntriesHandler.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateIndexEntriesHandler.java
new file mode 100644
index 0000000..cb64fcf
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateIndexEntriesHandler.java
@@ -0,0 +1,73 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.server.metrics.SignalingCountDownLatch;
+
+/**
+* @author John Sanda
+*/
+class AggregateIndexEntriesHandler implements FutureCallback<ResultSet> {
+
+ private final Log log = LogFactory.getLog(AggregateIndexEntriesHandler.class);
+
+ private Set<Integer> indexEntries;
+
+ private AtomicInteger remainingData;
+
+ private SignalingCountDownLatch indexEntriesArrival;
+
+ private Stopwatch stopwatch;
+
+ private String src;
+
+ private String dest;
+
+ public AggregateIndexEntriesHandler(Set<Integer> indexEntries, AtomicInteger remainingData,
+ SignalingCountDownLatch indexEntriesArrival, Stopwatch stopwatch, String src, String dest) {
+ this.indexEntries = indexEntries;
+ this.remainingData = remainingData;
+ this.indexEntriesArrival = indexEntriesArrival;
+ this.stopwatch = stopwatch;
+ this.src = src;
+ this.dest = dest;
+ }
+
+ @Override
+ public void onSuccess(ResultSet resultSet) {
+ for (Row row : resultSet) {
+ indexEntries.add(row.getInt(1));
+ }
+ remainingData.set(indexEntries.size());
+ indexEntriesArrival.countDown();
+ stopwatch.stop();
+ if (log.isDebugEnabled()) {
+ log.debug("Finished loading " + indexEntries.size() + " " + src + " index entries in " +
+ stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Some " + dest + " aggregates may not get computed. An unexpected error occurred while " +
+ "retrieving " + src + " index entries", t);
+ } else {
+ log.warn("Some " + dest + " aggregates may not get computed. An unexpected error occurred while " +
+ "retrieving " + src + " index entries: " + ThrowableUtil.getRootMessage(t));
+ }
+ remainingData.set(0);
+ indexEntriesArrival.abort();
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateRawData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateRawData.java
new file mode 100644
index 0000000..87a7266
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregateRawData.java
@@ -0,0 +1,133 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.FutureFallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.server.metrics.AbortedException;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+
+/**
+ * Generates 1 hour data for a batch of raw data futures. After data is inserted for the batch, aggregation of 1 hour
+ * data will start immediately for the batch if the 6 hour time slice has finished.
+ *
+ * @see Compute1HourData
+ * @author John Sanda
+ */
+class AggregateRawData implements Runnable {
+
+ private final Log log = LogFactory.getLog(AggregateRawData.class);
+
+ private MetricsDAO dao;
+
+ private AggregationState state;
+
+ private Set<Integer> scheduleIds;
+
+ private List<StorageResultSetFuture> queryFutures;
+
+ public AggregateRawData(MetricsDAO dao, AggregationState state, Set<Integer> scheduleIds,
+ List<StorageResultSetFuture> queryFutures) {
+ this.dao = dao;
+ this.state = state;
+ this.scheduleIds = scheduleIds;
+ this.queryFutures = queryFutures;
+ }
+
+ @Override
+ public void run() {
+ final Stopwatch stopwatch = new Stopwatch().start();
+ ListenableFuture<List<ResultSet>> rawDataFutures = Futures.successfulAsList(queryFutures);
+ Futures.withFallback(rawDataFutures, new FutureFallback<List<ResultSet>>() {
+ @Override
+ public ListenableFuture<List<ResultSet>> create(Throwable t) throws Exception {
+ log.error("An error occurred while fetching raw data", t);
+ return Futures.immediateFailedFuture(t);
+ }
+ });
+
+ final ListenableFuture<List<ResultSet>> insert1HourDataFutures = Futures.transform(rawDataFutures,
+ state.getCompute1HourData(), state.getAggregationTasks());
+ Futures.addCallback(insert1HourDataFutures, new FutureCallback<List<ResultSet>>() {
+ @Override
+ public void onSuccess(List<ResultSet> resultSets) {
+ stopwatch.stop();
+ log.debug("Finished aggregating raw data for " + resultSets.size() + " schedules in " +
+ stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ start1HourDataAggregationIfNecessary();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ // TODO should we log the schedule ids?
+ log.debug("Failed to aggregate raw data for " + scheduleIds.size() + " schedules. An unexpected " +
+ "error occurred.", t);
+ } else {
+ log.warn("Failed to aggregate raw data for " + scheduleIds.size() + " schedules. An " +
+ "unexpected error occurred: " + ThrowableUtil.getRootMessage(t));
+ }
+ start1HourDataAggregationIfNecessary();
+ }
+ }, state.getAggregationTasks());
+ }
+
+ private void start1HourDataAggregationIfNecessary() {
+ try {
+ if (state.is6HourTimeSliceFinished()) {
+ update1HourIndexEntries();
+ List<StorageResultSetFuture> oneHourDataQueryFutures = new ArrayList<StorageResultSetFuture>(
+ scheduleIds.size());
+ for (Integer scheduleId : scheduleIds) {
+ oneHourDataQueryFutures.add(dao.findOneHourMetricsAsync(scheduleId,
+ state.getSixHourTimeSlice().getMillis(), state.getSixHourTimeSliceEnd().getMillis()));
+ }
+ state.getAggregationTasks().submit(new Aggregate1HourData(dao, state, scheduleIds,
+ oneHourDataQueryFutures));
+ }
+ } catch (InterruptedException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("An interrupt occurred while waiting for 1 hour data index entries. Aborting data aggregation",
+ e);
+ } else {
+ log.info("An interrupt occurred while waiting for 1 hour data index entries. Aborting data " +
+ "aggregation: " + e.getMessage());
+ }
+ } finally {
+ state.getRemainingRawData().addAndGet(-scheduleIds.size());
+ }
+ }
+
+ private void update1HourIndexEntries() throws InterruptedException {
+ try {
+ // Wait for the arrival so that we can remove the schedules ids in this
+ // batch from the one hour index entries. This will prevent duplicate tasks
+ // being submitted to process the same 1 hour data.
+ state.getOneHourIndexEntriesArrival().await();
+ try {
+ state.getOneHourIndexEntriesLock().writeLock().lock();
+ state.getOneHourIndexEntries().removeAll(scheduleIds);
+ } finally {
+ state.getOneHourIndexEntriesLock().writeLock().unlock();
+ }
+ } catch (AbortedException e) {
+ // This means we failed to retrieve the index entries. We can however
+ // continue generating 1 hour data because we do not need the index
+ // here since we already have 1 hour data to aggregate along with the
+ // schedule ids.
+ }
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregationState.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregationState.java
new file mode 100644
index 0000000..345e53a
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/AggregationState.java
@@ -0,0 +1,257 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.google.common.util.concurrent.ListeningExecutorService;
+
+import org.joda.time.DateTime;
+
+import org.rhq.server.metrics.SignalingCountDownLatch;
+
+/**
+ * @author John Sanda
+ */
+class AggregationState {
+
+ private ListeningExecutorService aggregationTasks;
+
+ private SignalingCountDownLatch oneHourIndexEntriesArrival;
+
+ private SignalingCountDownLatch sixHourIndexEntriesArrival;
+
+ private AtomicInteger remainingRawData;
+
+ private AtomicInteger remaining1HourData;
+
+ private AtomicInteger remaining6HourData;
+
+ private Set<Integer> oneHourIndexEntries;
+
+ private Set<Integer> sixHourIndexEntries;
+
+ private ReentrantReadWriteLock oneHourIndexEntriesLock;
+
+ private ReentrantReadWriteLock sixHourIndexEntriesLock;
+
+ private DateTime oneHourTimeSlice;
+
+ private DateTime sixHourTimeSlice;
+
+ private DateTime sixHourTimeSliceEnd;
+
+ private DateTime twentyFourHourTimeSlice;
+
+ private DateTime twentyFourHourTimeSliceEnd;
+
+ private boolean sixHourTimeSliceFinished;
+
+ private boolean twentyFourHourTimeSliceFinished;
+
+ private Compute1HourData compute1HourData;
+
+ private Compute6HourData compute6HourData;
+
+ private Compute24HourData compute24HourData;
+
+ public ListeningExecutorService getAggregationTasks() {
+ return aggregationTasks;
+ }
+
+ public AggregationState setAggregationTasks(ListeningExecutorService aggregationTasks) {
+ this.aggregationTasks = aggregationTasks;
+ return this;
+ }
+
+ /**
+ * @return A {@link SignalingCountDownLatch} to signal the arrival of index entries for schedules with 1 hour
+ * data to be aggregated
+ */
+ public SignalingCountDownLatch getOneHourIndexEntriesArrival() {
+ return oneHourIndexEntriesArrival;
+ }
+
+ public AggregationState setOneHourIndexEntriesArrival(SignalingCountDownLatch oneHourIndexEntriesArrival) {
+ this.oneHourIndexEntriesArrival = oneHourIndexEntriesArrival;
+ return this;
+ }
+
+ /**
+ * @return A {@link SignalingCountDownLatch} to signal the arrival of index entries for schedules with 6 hour
+ * data to be aggregated
+ */
+ public SignalingCountDownLatch getSixHourIndexEntriesArrival() {
+ return sixHourIndexEntriesArrival;
+ }
+
+ public AggregationState setSixHourIndexEntriesArrival(SignalingCountDownLatch sixHourIndexEntriesArrival) {
+ this.sixHourIndexEntriesArrival = sixHourIndexEntriesArrival;
+ return this;
+ }
+
+ /**
+ * @return The remaining number of schedules with raw data to be aggregated
+ */
+ public AtomicInteger getRemainingRawData() {
+ return remainingRawData;
+ }
+
+ public AggregationState setRemainingRawData(AtomicInteger remainingRawData) {
+ this.remainingRawData = remainingRawData;
+ return this;
+ }
+
+ /**
+ * @return The remaining number of schedules with 1 hour data to be aggregated
+ */
+ public AtomicInteger getRemaining1HourData() {
+ return remaining1HourData;
+ }
+
+ public AggregationState setRemaining1HourData(AtomicInteger remaining1HourData) {
+ this.remaining1HourData = remaining1HourData;
+ return this;
+ }
+
+ /**
+ * @return The remaining number of schedules with 6 hour data to be aggregated
+ */
+ public AtomicInteger getRemaining6HourData() {
+ return remaining6HourData;
+ }
+
+ public AggregationState setRemaining6HourData(AtomicInteger remaining6HourData) {
+ this.remaining6HourData = remaining6HourData;
+ return this;
+ }
+
+ /**
+ * @return The schedule ids with 1 hour data to be aggregated
+ */
+ public Set<Integer> getOneHourIndexEntries() {
+ return oneHourIndexEntries;
+ }
+
+ public AggregationState setOneHourIndexEntries(Set<Integer> oneHourIndexEntries) {
+ this.oneHourIndexEntries = oneHourIndexEntries;
+ return this;
+ }
+
+ public Set<Integer> getSixHourIndexEntries() {
+ return sixHourIndexEntries;
+ }
+
+ public AggregationState setSixHourIndexEntries(Set<Integer> sixHourIndexEntries) {
+ this.sixHourIndexEntries = sixHourIndexEntries;
+ return this;
+ }
+
+ public ReentrantReadWriteLock getOneHourIndexEntriesLock() {
+ return oneHourIndexEntriesLock;
+ }
+
+ public AggregationState setOneHourIndexEntriesLock(ReentrantReadWriteLock oneHourIndexEntriesLock) {
+ this.oneHourIndexEntriesLock = oneHourIndexEntriesLock;
+ return this;
+ }
+
+ public ReentrantReadWriteLock getSixHourIndexEntriesLock() {
+ return sixHourIndexEntriesLock;
+ }
+
+ public AggregationState setSixHourIndexEntriesLock(ReentrantReadWriteLock sixHourIndexEntriesLock) {
+ this.sixHourIndexEntriesLock = sixHourIndexEntriesLock;
+ return this;
+ }
+
+ public DateTime getOneHourTimeSlice() {
+ return oneHourTimeSlice;
+ }
+
+ public AggregationState setOneHourTimeSlice(DateTime oneHourTimeSlice) {
+ this.oneHourTimeSlice = oneHourTimeSlice;
+ return this;
+ }
+
+ public DateTime getSixHourTimeSlice() {
+ return sixHourTimeSlice;
+ }
+
+ public AggregationState setSixHourTimeSlice(DateTime sixHourTimeSlice) {
+ this.sixHourTimeSlice = sixHourTimeSlice;
+ return this;
+ }
+
+ public DateTime getSixHourTimeSliceEnd() {
+ return sixHourTimeSliceEnd;
+ }
+
+ public AggregationState setSixHourTimeSliceEnd(DateTime sixHourTimeSliceEnd) {
+ this.sixHourTimeSliceEnd = sixHourTimeSliceEnd;
+ return this;
+ }
+
+ public DateTime getTwentyFourHourTimeSlice() {
+ return twentyFourHourTimeSlice;
+ }
+
+ public AggregationState setTwentyFourHourTimeSlice(DateTime twentyFourHourTimeSlice) {
+ this.twentyFourHourTimeSlice = twentyFourHourTimeSlice;
+ return this;
+ }
+
+ public DateTime getTwentyFourHourTimeSliceEnd() {
+ return twentyFourHourTimeSliceEnd;
+ }
+
+ public AggregationState setTwentyFourHourTimeSliceEnd(DateTime twentyFourHourTimeSliceEnd) {
+ this.twentyFourHourTimeSliceEnd = twentyFourHourTimeSliceEnd;
+ return this;
+ }
+
+ public boolean is6HourTimeSliceFinished() {
+ return sixHourTimeSliceFinished;
+ }
+
+ public AggregationState set6HourTimeSliceFinished(boolean is6HourTimeSliceFinished) {
+ this.sixHourTimeSliceFinished = is6HourTimeSliceFinished;
+ return this;
+ }
+
+ public boolean is24HourTimeSliceFinished() {
+ return twentyFourHourTimeSliceFinished;
+ }
+
+ public AggregationState set24HourTimeSliceFinished(boolean is24HourTimeSliceFinished) {
+ this.twentyFourHourTimeSliceFinished = is24HourTimeSliceFinished;
+ return this;
+ }
+
+ public Compute1HourData getCompute1HourData() {
+ return compute1HourData;
+ }
+
+ public AggregationState setCompute1HourData(Compute1HourData compute1HourData) {
+ this.compute1HourData = compute1HourData;
+ return this;
+ }
+
+ public Compute6HourData getCompute6HourData() {
+ return compute6HourData;
+ }
+
+ public AggregationState setCompute6HourData(Compute6HourData compute6HourData) {
+ this.compute6HourData = compute6HourData;
+ return this;
+ }
+
+ public Compute24HourData getCompute24HourData() {
+ return compute24HourData;
+ }
+
+ public AggregationState setCompute24HourData(Compute24HourData compute24HourData) {
+ this.compute24HourData = compute24HourData;
+ return this;
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregator.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregator.java
new file mode 100644
index 0000000..bf0bc1a
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Aggregator.java
@@ -0,0 +1,378 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.RateLimiter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeComparator;
+import org.joda.time.Duration;
+
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.server.metrics.AbortedException;
+import org.rhq.server.metrics.DateTimeService;
+import org.rhq.server.metrics.MetricsConfiguration;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.SignalingCountDownLatch;
+import org.rhq.server.metrics.StorageResultSetFuture;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.MetricsTable;
+
+/**
+ * This class provides the main interface for metric data aggregation.
+ *
+ * @author John Sanda
+ */
+public class Aggregator {
+
+ private static final Comparator<AggregateNumericMetric> AGGREGATE_COMPARATOR = new Comparator<AggregateNumericMetric>() {
+ @Override
+ public int compare(AggregateNumericMetric left, AggregateNumericMetric right) {
+ return (left.getScheduleId() < right.getScheduleId()) ? -1 : ((left.getScheduleId() == right.getScheduleId()) ? 0 : 1);
+ }
+ };
+
+ private final Log log = LogFactory.getLog(Aggregator.class);
+
+ private MetricsDAO dao;
+
+ private MetricsConfiguration configuration;
+
+ private DateTimeService dtService;
+
+ private DateTime startTime;
+
+ /**
+ * Signals when raw data index entries (in metrics_index) can be deleted. We cannot delete the row in metrics_index
+ * until we know that it has been read, successfully or otherwise.
+ */
+ private SignalingCountDownLatch rawDataIndexEntriesArrival;
+
+ private RateLimiter readPermits;
+ private RateLimiter writePermits;
+
+ private int batchSize;
+
+ private AggregationState state;
+
+ private Set<AggregateNumericMetric> oneHourData;
+
+ private AtomicInteger remainingIndexEntries;
+
+ public Aggregator(ListeningExecutorService aggregationTasks, MetricsDAO dao, MetricsConfiguration configuration,
+ DateTimeService dtService, DateTime startTime, int batchSize, RateLimiter writePermits,
+ RateLimiter readPermits) {
+ this.dao = dao;
+ this.configuration = configuration;
+ this.dtService = dtService;
+ this.startTime = startTime;
+ this.readPermits = readPermits;
+ this.writePermits = writePermits;
+ this.batchSize = batchSize;
+ oneHourData = new ConcurrentSkipListSet<AggregateNumericMetric>(AGGREGATE_COMPARATOR);
+ rawDataIndexEntriesArrival = new SignalingCountDownLatch(new CountDownLatch(1));
+ remainingIndexEntries = new AtomicInteger(1);
+
+ DateTime sixHourTimeSlice = get6HourTimeSlice();
+ DateTime twentyFourHourTimeSlice = get24HourTimeSlice();
+
+ state = new AggregationState()
+ .setAggregationTasks(aggregationTasks)
+ .setOneHourTimeSlice(startTime)
+ .setSixHourTimeSlice(sixHourTimeSlice)
+ .setSixHourTimeSliceEnd(sixHourTimeSlice.plus(configuration.getOneHourTimeSliceDuration()))
+ .setTwentyFourHourTimeSlice(twentyFourHourTimeSlice)
+ .setTwentyFourHourTimeSliceEnd(twentyFourHourTimeSlice.plus(configuration.getSixHourTimeSliceDuration()))
+ .setCompute1HourData(new Compute1HourData(startTime, sixHourTimeSlice, writePermits, dao, oneHourData))
+ .setCompute6HourData(new Compute6HourData(sixHourTimeSlice, twentyFourHourTimeSlice, writePermits, dao))
+ .setCompute24HourData(new Compute24HourData(twentyFourHourTimeSlice, writePermits, dao))
+ .set6HourTimeSliceFinished(hasTimeSliceEnded(sixHourTimeSlice, configuration.getOneHourTimeSliceDuration()))
+ .set24HourTimeSliceFinished(hasTimeSliceEnded(twentyFourHourTimeSlice,
+ configuration.getSixHourTimeSliceDuration()))
+ .setRemainingRawData(new AtomicInteger(0))
+ .setRemaining1HourData(new AtomicInteger(0))
+ .setRemaining6HourData(new AtomicInteger(0))
+ .setOneHourIndexEntries(new TreeSet<Integer>())
+ .setSixHourIndexEntries(new TreeSet<Integer>())
+ .setOneHourIndexEntriesLock(new ReentrantReadWriteLock())
+ .setSixHourIndexEntriesLock(new ReentrantReadWriteLock());
+
+ if (state.is6HourTimeSliceFinished()) {
+ state.setOneHourIndexEntriesArrival(new SignalingCountDownLatch(new CountDownLatch(1)));
+ remainingIndexEntries.incrementAndGet();
+ } else {
+ state.setOneHourIndexEntriesArrival(new SignalingCountDownLatch(new CountDownLatch(0)));
+ state.setRemaining1HourData(new AtomicInteger(0));
+ }
+
+ if (state.is24HourTimeSliceFinished()) {
+ state.setSixHourIndexEntriesArrival(new SignalingCountDownLatch(new CountDownLatch(1)));
+ remainingIndexEntries.incrementAndGet();
+ } else {
+ state.setSixHourIndexEntriesArrival(new SignalingCountDownLatch(new CountDownLatch(0)));
+ state.setRemaining6HourData(new AtomicInteger(0));
+ }
+ }
+
+ private DateTime get24HourTimeSlice() {
+ return dtService.getTimeSlice(startTime, configuration.getSixHourTimeSliceDuration());
+ }
+
+ private DateTime get6HourTimeSlice() {
+ return dtService.getTimeSlice(startTime, configuration.getOneHourTimeSliceDuration());
+ }
+
+ private boolean hasTimeSliceEnded(DateTime startTime, Duration duration) {
+ DateTime endTime = startTime.plus(duration);
+ return DateTimeComparator.getInstance().compare(currentHour(), endTime) >= 0;
+ }
+
+ protected DateTime currentHour() {
+ return dtService.getTimeSlice(dtService.now(), configuration.getRawTimeSliceDuration());
+ }
+
+ public Set<AggregateNumericMetric> run() {
+ log.info("Starting aggregation for time slice " + startTime);
+ readPermits.acquire();
+ StorageResultSetFuture rawFuture = dao.findMetricsIndexEntriesAsync(MetricsTable.ONE_HOUR,
+ startTime.getMillis());
+ Futures.addCallback(rawFuture, new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(ResultSet result) {
+ List<Row> rows = result.all();
+ state.getRemainingRawData().set(rows.size());
+ rawDataIndexEntriesArrival.countDown();
+
+ Stopwatch stopwatch = new Stopwatch().start();
+
+ final DateTime endTime = startTime.plus(configuration.getRawTimeSliceDuration());
+ Set<Integer> scheduleIds = new TreeSet<Integer>();
+ List<StorageResultSetFuture> rawDataFutures = new ArrayList<StorageResultSetFuture>(batchSize);
+ for (final Row row : rows) {
+ scheduleIds.add(row.getInt(1));
+ readPermits.acquire();
+ rawDataFutures.add(dao.findRawMetricsAsync(row.getInt(1), startTime.getMillis(),
+ endTime.getMillis()));
+ if (rawDataFutures.size() == batchSize) {
+ state.getAggregationTasks().submit(new AggregateRawData(dao, state, scheduleIds,
+ rawDataFutures));
+ rawDataFutures = new ArrayList<StorageResultSetFuture>();
+ scheduleIds = new TreeSet<Integer>();
+ }
+ }
+ if (!rawDataFutures.isEmpty()) {
+ state.getAggregationTasks().submit(new AggregateRawData(dao, state, scheduleIds,
+ rawDataFutures));
+ }
+
+ if (log.isDebugEnabled()) {
+ stopwatch.stop();
+ log.debug("Finished scheduling raw data aggregation tasks for " + rows.size() + " schedules in " +
+ stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Aggregation for time slice [" + startTime + "] cannot proceed. There was an " +
+ "unexpected error while retrieving raw data index entries.", t);
+ } else {
+ log.warn("Aggregation for time slice [" + startTime + "] cannot proceed. There was an " +
+ "unexpected error while retrieving raw data index entries: " + ThrowableUtil.getRootMessage(t));
+ }
+ state.setRemainingRawData(new AtomicInteger(0));
+ rawDataIndexEntriesArrival.abort();
+ deleteIndexEntries(MetricsTable.ONE_HOUR);
+ }
+ }, state.getAggregationTasks());
+
+ if (state.is6HourTimeSliceFinished()) {
+ log.debug("Fetching 1 hour index entries");
+ Stopwatch stopwatch = new Stopwatch().start();
+ StorageResultSetFuture oneHourFuture = dao.findMetricsIndexEntriesAsync(MetricsTable.SIX_HOUR,
+ state.getSixHourTimeSlice().getMillis());
+ Futures.addCallback(oneHourFuture, new AggregateIndexEntriesHandler(state.getOneHourIndexEntries(),
+ state.getRemaining1HourData(), state.getOneHourIndexEntriesArrival(), stopwatch, "1 hour", "6 hour"),
+ state.getAggregationTasks());
+ }
+
+ if (state.is24HourTimeSliceFinished()) {
+ log.debug("Fetching 6 hour index entries");
+ Stopwatch stopwatch = new Stopwatch().start();
+ StorageResultSetFuture sixHourFuture = dao.findMetricsIndexEntriesAsync(MetricsTable.TWENTY_FOUR_HOUR,
+ state.getTwentyFourHourTimeSlice().getMillis());
+ Futures.addCallback(sixHourFuture, new AggregateIndexEntriesHandler(state.getSixHourIndexEntries(),
+ state.getRemaining6HourData(), state.getSixHourIndexEntriesArrival(), stopwatch, "6 hour", "24 hour"),
+ state.getAggregationTasks());
+ }
+
+ try {
+ try {
+ rawDataIndexEntriesArrival.await();
+ deleteIndexEntries(MetricsTable.ONE_HOUR);
+ } catch (AbortedException e) {
+ }
+
+ if (state.is6HourTimeSliceFinished()) {
+ waitFor(state.getRemainingRawData());
+ try {
+ state.getOneHourIndexEntriesArrival().await();
+ deleteIndexEntries(MetricsTable.SIX_HOUR);
+
+ List<StorageResultSetFuture> queryFutures = new ArrayList<StorageResultSetFuture>(batchSize);
+ Set<Integer> scheduleIds = new TreeSet<Integer>();
+ state.getOneHourIndexEntriesLock().writeLock().lock();
+ log.debug("Preparing to submit 1 hour data aggregation tasks for " +
+ state.getOneHourIndexEntries().size() + " schedules");
+ for (Integer scheduleId : state.getOneHourIndexEntries()) {
+ queryFutures.add(dao.findOneHourMetricsAsync(scheduleId, state.getSixHourTimeSlice().getMillis(),
+ state.getSixHourTimeSliceEnd().getMillis()));
+ scheduleIds.add(scheduleId);
+ if (queryFutures.size() == batchSize) {
+ state.getAggregationTasks().submit(new Aggregate1HourData(dao, state, scheduleIds,
+ queryFutures));
+ queryFutures = new ArrayList<StorageResultSetFuture>(batchSize);
+ scheduleIds = new TreeSet<Integer>();
+ }
+ }
+ if (!queryFutures.isEmpty()) {
+ state.getAggregationTasks().submit(new Aggregate1HourData(dao, state, scheduleIds,
+ queryFutures));
+ queryFutures = null;
+ scheduleIds = null;
+ }
+ } catch (AbortedException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Some 6 hour aggregates may not get generated. There was an unexpected error while " +
+ "loading 1 hour index entries", e);
+ } else {
+ log.warn("Some 6 hour aggregates may not get generated. There was an unexpected error while " +
+ "loading 1 hour index entries: " + ThrowableUtil.getRootMessage(e));
+ }
+ } finally {
+ state.getOneHourIndexEntriesLock().writeLock().unlock();
+ }
+ }
+
+ if (state.is24HourTimeSliceFinished()) {
+ waitFor(state.getRemaining1HourData());
+ try {
+ state.getSixHourIndexEntriesArrival().await();
+ deleteIndexEntries(MetricsTable.TWENTY_FOUR_HOUR);
+
+ List<StorageResultSetFuture> queryFutures = new ArrayList<StorageResultSetFuture>(batchSize);
+ Set<Integer> scheduleIds = new TreeSet<Integer>();
+ state.getSixHourIndexEntriesLock().writeLock().lock();
+ log.debug("Preparing to submit 6 hour data aggregation tasks for " +
+ state.getSixHourIndexEntries().size() + " schedules");
+ for (Integer scheduleId : state.getSixHourIndexEntries()) {
+ queryFutures.add(dao.findSixHourMetricsAsync(scheduleId, state.getTwentyFourHourTimeSlice().getMillis(),
+ state.getTwentyFourHourTimeSliceEnd().getMillis()));
+ scheduleIds.add(scheduleId);
+ if (queryFutures.size() == batchSize) {
+ state.getAggregationTasks().submit(new Aggregate6HourData(dao, state, scheduleIds,
+ queryFutures));
+ queryFutures = new ArrayList<StorageResultSetFuture>(batchSize);
+ scheduleIds = new TreeSet<Integer>();
+ }
+ }
+ if (!queryFutures.isEmpty()) {
+ state.getAggregationTasks().submit(new Aggregate6HourData(dao, state, scheduleIds,
+ queryFutures));
+ queryFutures = null;
+ scheduleIds = null;
+ }
+ } catch (AbortedException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("Some 24 hour aggregates may not get generated. There was an unexpected error while " +
+ "loading 6 hour index entries", e);
+ } else {
+ log.warn("Some 24 hour aggregates may not get generated. There was an unexpected error while " +
+ "loading 6 hour index entries: " + ThrowableUtil.getRootMessage(e));
+ }
+ } finally {
+ state.getSixHourIndexEntriesLock().writeLock().unlock();
+ }
+ }
+
+ while (!isAggregationFinished()) {
+ Thread.sleep(50);
+ }
+ } catch (InterruptedException e) {
+ if (log.isDebugEnabled()) {
+ log.debug("An interrupt occurred while waiting for aggregation to finish. Aborting remaining work.", e);
+ } else {
+ log.warn("An interrupt occurred while waiting for aggregation to finish. Aborting remaining work: " +
+ ThrowableUtil.getRootMessage(e));
+ }
+ log.warn("An interrupt occurred while waiting for aggregation to finish", e);
+ }
+ return oneHourData;
+ }
+
+ private void waitFor(AtomicInteger remainingData) throws InterruptedException {
+ while (remainingData.get() > 0) {
+ Thread.sleep(50);
+ }
+ }
+
+ private boolean isAggregationFinished() {
+ return state.getRemainingRawData().get() <= 0 && state.getRemaining1HourData().get() <= 0 &&
+ state.getRemaining6HourData().get() <= 0 && remainingIndexEntries.get() <= 0;
+ }
+
+ private void deleteIndexEntries(final MetricsTable table) {
+ final DateTime time;
+ switch (table) {
+ case ONE_HOUR:
+ time = startTime;
+ break;
+ case SIX_HOUR:
+ time = state.getSixHourTimeSlice();
+ break;
+ default:
+ time = state.getTwentyFourHourTimeSlice();
+ break;
+ }
+ log.debug("Deleting " + table + " index entries for time slice " + time);
+ writePermits.acquire();
+ StorageResultSetFuture future = dao.deleteMetricsIndexEntriesAsync(table, time.getMillis());
+ Futures.addCallback(future, new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(ResultSet result) {
+ remainingIndexEntries.decrementAndGet();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ if (log.isDebugEnabled()) {
+ log.debug("Failed to delete index entries for table " + table + " at time [" + time + "]. An " +
+ "unexpected error occurred.", t);
+ } else {
+ log.warn("Failed to delete index entries for table " + table + " at time [" + time + "]. An " +
+ "unexpected error occurred: " + ThrowableUtil.getRootMessage(t));
+ }
+ remainingIndexEntries.decrementAndGet();
+ }
+ });
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute1HourData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute1HourData.java
new file mode 100644
index 0000000..f130f75
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute1HourData.java
@@ -0,0 +1,113 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.RateLimiter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.DateTime;
+
+import org.rhq.server.metrics.ArithmeticMeanCalculator;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.AggregateType;
+import org.rhq.server.metrics.domain.MetricsTable;
+
+/**
+ * Computes 1 hour data for a batch of raw data result sets. The generated 1 hour aggregates are inserted along with
+ * their corresponding index updates.
+ *
+ * @author John Sanda
+ */
+class Compute1HourData implements AsyncFunction<List<ResultSet>, List<ResultSet>> {
+
+ private final Log log = LogFactory.getLog(Compute1HourData.class);
+
+ private DateTime startTime;
+
+ private RateLimiter writePermits;
+
+ private MetricsDAO dao;
+
+ private DateTime sixHourTimeSlice;
+
+ private Set<AggregateNumericMetric> oneHourData;
+
+ public Compute1HourData(DateTime startTime, DateTime sixHourTimeSlice, RateLimiter writePermits, MetricsDAO dao,
+ Set<AggregateNumericMetric> oneHourData) {
+ this.startTime = startTime;
+ this.sixHourTimeSlice = sixHourTimeSlice;
+ this.writePermits = writePermits;
+ this.dao = dao;
+ this.oneHourData = oneHourData;
+ }
+
+ @Override
+ public ListenableFuture<List<ResultSet>> apply(List<ResultSet> rawDataResultSets) throws Exception {
+ if (log.isDebugEnabled()) {
+ log.debug("Computing and storing 1 hour data for " + rawDataResultSets.size() + " schedules");
+ }
+ Stopwatch stopwatch = new Stopwatch().start();
+ try {
+ List<StorageResultSetFuture> insertFutures = new ArrayList<StorageResultSetFuture>(rawDataResultSets.size());
+ for (ResultSet resultSet : rawDataResultSets) {
+ AggregateNumericMetric aggregate = calculateAggregatedRaw(resultSet);
+ oneHourData.add(aggregate);
+ writePermits.acquire(4);
+ insertFutures.add(dao.insertOneHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MIN, aggregate.getMin()));
+ insertFutures.add(dao.insertOneHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MAX, aggregate.getMax()));
+ insertFutures.add(dao.insertOneHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.AVG, aggregate.getAvg()));
+ insertFutures.add(dao.updateMetricsIndex(MetricsTable.SIX_HOUR, aggregate.getScheduleId(),
+ sixHourTimeSlice.getMillis()));
+ }
+ return Futures.successfulAsList(insertFutures);
+ } finally {
+ if (log.isDebugEnabled()) {
+ stopwatch.stop();
+ log.debug("Finished computing and storing 1 hour data for " + rawDataResultSets.size() +
+ " schedules in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ }
+ }
+ }
+
+ private AggregateNumericMetric calculateAggregatedRaw(ResultSet resultSet) {
+ double min = Double.NaN;
+ double max = min;
+ int count = 0;
+ ArithmeticMeanCalculator mean = new ArithmeticMeanCalculator();
+ double value;
+ List<Row> rows = resultSet.all();
+
+ for (Row row : rows) {
+ value = row.getDouble(2);
+ if (count == 0) {
+ min = value;
+ max = min;
+ }
+ if (value < min) {
+ min = value;
+ } else if (value > max) {
+ max = value;
+ }
+ mean.add(value);
+ ++count;
+ }
+
+ return new AggregateNumericMetric(rows.get(0).getInt(0), mean.getArithmeticMean(), min, max,
+ startTime.getMillis());
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute24HourData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute24HourData.java
new file mode 100644
index 0000000..6fe9d79
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute24HourData.java
@@ -0,0 +1,99 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.RateLimiter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.DateTime;
+
+import org.rhq.server.metrics.ArithmeticMeanCalculator;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.AggregateType;
+
+/**
+ * Computes 24 hour data for a batch of raw data result sets. The generated 6 hour aggregates are inserted.
+ *
+ * @author John Sanda
+ */
+class Compute24HourData implements AsyncFunction<List<ResultSet>, List<ResultSet>> {
+
+ private final Log log = LogFactory.getLog(Compute24HourData.class);
+
+ private DateTime startTime;
+
+ private RateLimiter writePermits;
+
+ private MetricsDAO dao;
+
+ public Compute24HourData(DateTime startTime, RateLimiter writePermits, MetricsDAO dao) {
+ this.startTime = startTime;
+ this.writePermits = writePermits;
+ this.dao = dao;
+ }
+
+ @Override
+ public ListenableFuture<List<ResultSet>> apply(List<ResultSet> sixHourDataResultSets) throws Exception {
+ if (log.isDebugEnabled()) {
+ log.debug("Computing and storing 24 hour data for " + sixHourDataResultSets.size() + " schedules");
+ }
+ Stopwatch stopwatch = new Stopwatch().start();
+ try {
+ List<StorageResultSetFuture> insertFutures =
+ new ArrayList<StorageResultSetFuture>(sixHourDataResultSets.size());
+ for (ResultSet resultSet : sixHourDataResultSets) {
+ AggregateNumericMetric aggregate = calculateAggregate(resultSet);
+ writePermits.acquire(3);
+ insertFutures.add(dao.insertTwentyFourHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MIN, aggregate.getMin()));
+ insertFutures.add(dao.insertTwentyFourHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MAX, aggregate.getMax()));
+ insertFutures.add(dao.insertTwentyFourHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.AVG, aggregate.getAvg()));
+ }
+ return Futures.successfulAsList(insertFutures);
+ } finally {
+ if (log.isDebugEnabled()) {
+ stopwatch.stop();
+ log.debug("Finished computing and storing 24 hour data for " + sixHourDataResultSets.size() +
+ " schedules in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ }
+ }
+ }
+
+ private AggregateNumericMetric calculateAggregate(ResultSet resultSet) {
+ double min = Double.NaN;
+ double max = min;
+ ArithmeticMeanCalculator mean = new ArithmeticMeanCalculator();
+ List<Row> rows = resultSet.all();
+
+ for (int i = 0; i < rows.size(); i += 3) {
+ if (i == 0) {
+ min = rows.get(i + 1).getDouble(3);
+ max = rows.get(i).getDouble(3);
+ } else {
+ if (rows.get(i + 1).getDouble(3) < min) {
+ min = rows.get(i + 1).getDouble(3);
+ }
+ if (rows.get(i).getDouble(3) > max) {
+ max = rows.get(i).getDouble(3);
+ }
+ }
+ mean.add(rows.get(i + 2).getDouble(3));
+ }
+ return new AggregateNumericMetric(rows.get(0).getInt(0), mean.getArithmeticMean(), min, max,
+ startTime.getMillis());
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute6HourData.java b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute6HourData.java
new file mode 100644
index 0000000..ec1ee26
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/main/java/org/rhq/server/metrics/aggregation/Compute6HourData.java
@@ -0,0 +1,106 @@
+package org.rhq.server.metrics.aggregation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.AsyncFunction;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.RateLimiter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.DateTime;
+
+import org.rhq.server.metrics.ArithmeticMeanCalculator;
+import org.rhq.server.metrics.MetricsDAO;
+import org.rhq.server.metrics.StorageResultSetFuture;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.AggregateType;
+import org.rhq.server.metrics.domain.MetricsTable;
+
+/**
+ * Computes 6 hour data for a batch of raw data result sets. The generated 6 hour aggregates are inserted along with
+ * their corresponding index updates.
+ *
+ * @author John Sanda
+ */
+class Compute6HourData implements AsyncFunction<List<ResultSet>, List<ResultSet>> {
+
+ private final Log log = LogFactory.getLog(Compute6HourData.class);
+
+ private DateTime startTime;
+
+ private RateLimiter writePermits;
+
+ private MetricsDAO dao;
+
+ private DateTime twentyFourHourTimeSlice;
+
+ public Compute6HourData(DateTime startTime, DateTime twentyFourHourTimeSlice, RateLimiter writePermits,
+ MetricsDAO dao) {
+ this.startTime = startTime;
+ this.twentyFourHourTimeSlice = twentyFourHourTimeSlice;
+ this.writePermits = writePermits;
+ this.dao = dao;
+ }
+
+ @Override
+ public ListenableFuture<List<ResultSet>> apply(List<ResultSet> oneHourDataResultSets) throws Exception {
+ if (log.isDebugEnabled()) {
+ log.debug("Computing and storing 6 hour data for " + oneHourDataResultSets.size() + " schedules");
+ }
+ Stopwatch stopwatch = new Stopwatch().start();
+ try {
+ List<StorageResultSetFuture> insertFutures =
+ new ArrayList<StorageResultSetFuture>(oneHourDataResultSets.size());
+ for (ResultSet resultSet : oneHourDataResultSets) {
+ AggregateNumericMetric aggregate = calculateAggregate(resultSet);
+ writePermits.acquire(4);
+ insertFutures.add(dao.insertSixHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MIN, aggregate.getMin()));
+ insertFutures.add(dao.insertSixHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.MAX, aggregate.getMax()));
+ insertFutures.add(dao.insertSixHourDataAsync(aggregate.getScheduleId(), aggregate.getTimestamp(),
+ AggregateType.AVG, aggregate.getAvg()));
+ insertFutures.add(dao.updateMetricsIndex(MetricsTable.TWENTY_FOUR_HOUR, aggregate.getScheduleId(),
+ twentyFourHourTimeSlice.getMillis()));
+ }
+ return Futures.successfulAsList(insertFutures);
+ } finally {
+ if (log.isDebugEnabled()) {
+ stopwatch.stop();
+ log.debug("Finished computing and storing 6 hour data for " + oneHourDataResultSets.size() +
+ " schedules in " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms");
+ }
+ }
+ }
+
+ private AggregateNumericMetric calculateAggregate(ResultSet resultSet) {
+ double min = Double.NaN;
+ double max = min;
+ ArithmeticMeanCalculator mean = new ArithmeticMeanCalculator();
+ List<Row> rows = resultSet.all();
+
+ for (int i = 0; i < rows.size(); i += 3) {
+ if (i == 0) {
+ min = rows.get(i + 1).getDouble(3);
+ max = rows.get(i).getDouble(3);
+ } else {
+ if (rows.get(i + 1).getDouble(3) < min) {
+ min = rows.get(i + 1).getDouble(3);
+ }
+ if (rows.get(i).getDouble(3) > max) {
+ max = rows.get(i).getDouble(3);
+ }
+ }
+ mean.add(rows.get(i + 2).getDouble(3));
+ }
+ return new AggregateNumericMetric(rows.get(0).getInt(0), mean.getArithmeticMean(), min, max,
+ startTime.getMillis());
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/AggregationTests.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/AggregationTests.java
new file mode 100644
index 0000000..7d66cef
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/AggregationTests.java
@@ -0,0 +1,498 @@
+package org.rhq.server.metrics;
+
+import static java.util.Arrays.asList;
+import static org.rhq.test.AssertUtils.assertCollectionEqualsNoOrder;
+import static org.testng.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.RateLimiter;
+import com.google.common.util.concurrent.SettableFuture;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.measurement.MeasurementDataNumeric;
+import org.rhq.server.metrics.aggregation.Aggregator;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.AggregateType;
+import org.rhq.server.metrics.domain.MetricsIndexEntry;
+import org.rhq.server.metrics.domain.MetricsTable;
+
+/**
+ * @author John Sanda
+ */
+public class AggregationTests extends MetricsTest {
+
+ private Aggregates schedule1 = new Aggregates();
+ private Aggregates schedule2 = new Aggregates();
+ private Aggregates schedule3 = new Aggregates();
+ private Aggregates schedule4 = new Aggregates();
+ private Aggregates schedule5 = new Aggregates();
+
+ private ListeningExecutorService aggregationTasks;
+
+ private DateTime currentHour;
+
+ private RateLimiter writePermits;
+ private RateLimiter readPermits;
+
+ @BeforeClass
+ public void setUp() throws Exception {
+ purgeDB();
+
+ schedule1.id = 100;
+ schedule2.id = 101;
+ schedule3.id = 102;
+ schedule4.id = 104;
+ schedule5.id = 105;
+
+ aggregationTasks = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
+ writePermits = RateLimiter.create(500);
+ readPermits = RateLimiter.create(350);
+ }
+
+ @Test
+ public void insertRawDataDuringHour16() throws Exception {
+ insertRawData(
+ new MeasurementDataNumeric(hour(16).plusMinutes(20).getMillis(), schedule1.id, 3.0),
+ new MeasurementDataNumeric(hour(16).plusMinutes(40).getMillis(), schedule1.id, 5.0),
+ new MeasurementDataNumeric(hour(16).plusMinutes(15).getMillis(), schedule2.id, 0.0032),
+ new MeasurementDataNumeric(hour(16).plusMinutes(30).getMillis(), schedule2.id, 0.104),
+ new MeasurementDataNumeric(hour(16).plusMinutes(7).getMillis(), schedule3.id, 3.14)
+ ).await("Failed to insert raw data");
+
+ updateIndex(
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule1.id, hour(16)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule2.id, hour(16)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule3.id, hour(16))
+ ).await("Failed to update raw data index");
+ }
+
+ @Test(dependsOnMethods = "insertRawDataDuringHour16")
+ public void runAggregationForHour16() throws Exception {
+ currentHour = hour(17);
+ AggregatorTestStub aggregator = new AggregatorTestStub(hour(16));
+
+ Set<AggregateNumericMetric> oneHourData = aggregator.run();
+
+ schedule1.oneHourData.put(hour(16), new AggregateNumericMetric(schedule1.id, avg(3.0, 5.0), 3.0, 5.0,
+ hour(16).getMillis()));
+ schedule2.oneHourData.put(hour(16), new AggregateNumericMetric(schedule2.id, avg(0.0032, 0.104), 0.0032, 0.104,
+ hour(16).getMillis()));
+ schedule3.oneHourData.put(hour(16), new AggregateNumericMetric(schedule3.id, 3.14, 3.14, 3.14,
+ hour(16).getMillis()));
+
+ List<AggregateNumericMetric> expected = asList(schedule1.oneHourData.get(hour(16)),
+ schedule2.oneHourData.get(hour(16)), schedule3.oneHourData.get(hour(16)));
+ assertCollectionEqualsNoOrder(expected, oneHourData, "The returned one hour aggregates are wrong");
+ // verify values in the db
+ assert1HourDataEquals(schedule1.id, schedule1.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule2.id, schedule2.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule3.id, schedule3.oneHourData.get(hour(16)));
+ assert6HourIndexEquals(hour(12), schedule1.id, schedule2.id, schedule3.id);
+ assert6HourDataEmpty(schedule1.id);
+ assert6HourDataEmpty(schedule2.id);
+ assert6HourDataEmpty(schedule3.id);
+ assert24HourMetricsIndexEmpty(hour(0));
+ assert24HourMetricsIndexEmpty(hour(0));
+ assert1HourMetricsIndexEmpty(hour(16));
+ }
+
+ @Test(dependsOnMethods = "runAggregationForHour16")
+ public void insertRawDataDuringHour17() throws Exception {
+ insertRawData(
+ new MeasurementDataNumeric(hour(17).plusMinutes(20).getMillis(), schedule1.id, 11.0),
+ new MeasurementDataNumeric(hour(17).plusMinutes(40).getMillis(), schedule1.id, 16.0),
+ new MeasurementDataNumeric(hour(17).plusMinutes(30).getMillis(), schedule2.id, 0.092),
+ new MeasurementDataNumeric(hour(17).plusMinutes(45).getMillis(), schedule2.id, 0.0733)
+ ).await("Failed to insert raw data");
+
+ updateIndex(
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule1.id, hour(17)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule2.id, hour(17))
+ ).await("Failed to update raw data index");
+ }
+
+ @Test(dependsOnMethods = "insertRawDataDuringHour17")
+ public void runAggregationForHour17() throws Exception {
+ currentHour = hour(18);
+ AggregatorTestStub aggregator = new AggregatorTestStub(hour(17));
+
+ Set<AggregateNumericMetric> oneHourData = aggregator.run();
+
+ schedule1.oneHourData.put(hour(17), new AggregateNumericMetric(schedule1.id, avg(11.0, 16.0), 11.0, 16.0,
+ hour(17).getMillis()));
+ schedule2.oneHourData.put(hour(17), new AggregateNumericMetric(schedule2.id, avg(0.092, 0.0733), 0.0733, 0.092,
+ hour(17).getMillis()));
+
+ schedule1.sixHourData.put(hour(12), new AggregateNumericMetric(schedule1.id,
+ avg(schedule1.oneHourData, hour(16), hour(17)), min(schedule1.oneHourData, hour(16), hour(17)),
+ max(schedule1.oneHourData, hour(16), hour(17)), hour(12).getMillis()));
+ schedule2.sixHourData.put(hour(12), new AggregateNumericMetric(schedule2.id,
+ avg(schedule2.oneHourData, hour(16), hour(17)), min(schedule2.oneHourData, hour(16), hour(17)),
+ max(schedule2.oneHourData, hour(16), hour(17)), hour(12).getMillis()));
+ schedule3.sixHourData.put(hour(12), new AggregateNumericMetric(schedule3.id, 3.14, 3.14, 3.14,
+ hour(12).getMillis()));
+
+ List<AggregateNumericMetric> expected = asList(schedule1.oneHourData.get(hour(17)),
+ schedule2.oneHourData.get(hour(17)));
+ assertCollectionEqualsNoOrder(expected, oneHourData, "The returned one hour data is wrong");
+ // verify values in the db
+ assert1HourDataEquals(schedule1.id, schedule1.oneHourData.get(hour(16)), schedule1.oneHourData.get(hour(17)));
+ assert1HourDataEquals(schedule2.id, schedule2.oneHourData.get(hour(16)), schedule2.oneHourData.get(hour(17)));
+ assert1HourDataEquals(schedule3.id, schedule3.oneHourData.get(hour(16)));
+ assert6HourDataEquals(schedule1.id, schedule1.sixHourData.get(hour(12)));
+ assert6HourDataEquals(schedule2.id, schedule2.sixHourData.get(hour(12)));
+ assert6HourDataEquals(schedule3.id, schedule3.sixHourData.get(hour(12)));
+ assert24HourDataEmpty(schedule1.id);
+ assert24HourDataEmpty(schedule2.id);
+ assert24HourDataEmpty(schedule3.id);
+ assert1HourMetricsIndexEmpty(hour(17));
+ assert6HourMetricsIndexEmpty(hour(12));
+ assert24HourIndexEquals(hour(0), schedule1.id, schedule2.id, schedule3.id);
+ }
+
+ @Test(dependsOnMethods = "runAggregationForHour17")
+ public void insertRawDataDuringHour18() throws Exception {
+ insertRawData(
+ new MeasurementDataNumeric(hour(18).plusMinutes(20).getMillis(), schedule1.id, 22.0),
+ new MeasurementDataNumeric(hour(18).plusMinutes(40).getMillis(), schedule1.id, 26.0),
+ new MeasurementDataNumeric(hour(18).plusMinutes(15).getMillis(), schedule2.id, 0.205),
+ new MeasurementDataNumeric(hour(18).plusMinutes(15).getMillis(), schedule3.id, 2.42)
+ ).await("Failed to insert raw data");
+
+ updateIndex(
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule1.id, hour(18)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule2.id, hour(18)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule3.id, hour(18))
+ ).await("Failed to update raw data index");
+ }
+
+ @Test(dependsOnMethods = "insertRawDataDuringHour18")
+ public void runAggregationForHour18() throws Exception {
+ currentHour = hour(19);
+ AggregatorTestStub aggregator = new AggregatorTestStub(hour(18));
+
+ Set<AggregateNumericMetric> oneHourData = aggregator.run();
+
+ schedule1.oneHourData.put(hour(18), new AggregateNumericMetric(schedule1.id, avg(22.0, 26.0), 22.0, 26.0,
+ hour(18).getMillis()));
+ schedule2.oneHourData.put(hour(18), new AggregateNumericMetric(schedule2.id, 0.205, 0.205, 0.205,
+ hour(18).getMillis()));
+ schedule3.oneHourData.put(hour(18), new AggregateNumericMetric(schedule3.id, 2.42, 2.42, 2.42,
+ hour(18).getMillis()));
+
+ List<AggregateNumericMetric> expected = asList(schedule1.oneHourData.get(hour(18)),
+ schedule2.oneHourData.get(hour(18)), schedule3.oneHourData.get(hour(18)));
+ assertCollectionEqualsNoOrder(expected, oneHourData, "The returned one hour data is wrong");
+ // verify values in db
+ assert1HourDataEquals(schedule1.id, schedule1.oneHourData.get(hour(18)), schedule1.oneHourData.get(hour(17)),
+ schedule1.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule2.id, schedule2.oneHourData.get(hour(18)), schedule2.oneHourData.get(hour(17)),
+ schedule2.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule3.id, schedule3.oneHourData.get(hour(18)), schedule3.oneHourData.get(hour(16)));
+ assert6HourDataEquals(schedule1.id, schedule1.sixHourData.get(hour(12)));
+ assert6HourDataEquals(schedule2.id, schedule2.sixHourData.get(hour(12)));
+ assert6HourDataEquals(schedule3.id, schedule3.sixHourData.get(hour(12)));
+ assert6HourIndexEquals(hour(18), schedule1.id, schedule2.id, schedule3.id);
+ assert24HourDataEmpty(schedule1.id);
+ assert24HourDataEmpty(schedule2.id);
+ assert24HourDataEmpty(schedule3.id);
+ assert24HourIndexEquals(hour(0), schedule1.id, schedule2.id, schedule3.id);
+ assert1HourMetricsIndexEmpty(hour(18));
+ }
+
+ @Test(dependsOnMethods = "runAggregationForHour18")
+ public void insertRawDataDuringHour23() throws Exception {
+ insertRawData(
+ new MeasurementDataNumeric(hour(23).plusMinutes(25).getMillis(), schedule1.id, 34.0),
+ new MeasurementDataNumeric(hour(23).plusMinutes(30).getMillis(), schedule2.id, 0.322)
+ ).await("Failed to insert raw data");
+
+ updateIndex(
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule1.id, hour(23)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule2.id, hour(23))
+ ).await("Failed to update raw data index");
+ }
+
+ @Test(dependsOnMethods = "insertRawDataDuringHour23")
+ public void runAggregationForHour24() throws Exception {
+ currentHour = hour(24);
+ AggregatorTestStub aggregator = new AggregatorTestStub(hour(23));
+
+ Set<AggregateNumericMetric> oneHourData = aggregator.run();
+
+ schedule1.oneHourData.put(hour(23), new AggregateNumericMetric(schedule1.id, 34.0, 34.0, 34.0,
+ hour(23).getMillis()));
+ schedule1.sixHourData.put(hour(18), new AggregateNumericMetric(schedule1.id,
+ avg(schedule1.oneHourData, hour(18), hour(23)),
+ min(schedule1.oneHourData, hour(18), hour(23)),
+ max(schedule1.oneHourData, hour(18), hour(23)),
+ hour(18).getMillis()));
+ schedule1.twentyFourHourData.put(hour(0),
+ new AggregateNumericMetric(schedule1.id,
+ avg(schedule1.sixHourData, hour(12), hour(18)),
+ min(schedule1.sixHourData, hour(12), hour(18)),
+ max(schedule1.sixHourData, hour(12), hour(18)),
+ hour(0).getMillis()));
+ schedule2.oneHourData.put(hour(23), new AggregateNumericMetric(schedule2.id, 0.322, 0.322, 0.322,
+ hour(23).getMillis()));
+ schedule2.sixHourData.put(hour(18), new AggregateNumericMetric(schedule2.id,
+ avg(schedule2.oneHourData, hour(18), hour(23)),
+ min(schedule2.oneHourData, hour(18), hour(23)),
+ max(schedule2.oneHourData, hour(18), hour(23)),
+ hour(18).getMillis()));
+ schedule2.twentyFourHourData.put(hour(0), new AggregateNumericMetric(schedule2.id,
+ avg(schedule2.sixHourData, hour(12), hour(18)),
+ min(schedule2.sixHourData, hour(12), hour(18)),
+ max(schedule2.sixHourData, hour(12), hour(18)),
+ hour(0).getMillis()));
+ schedule3.sixHourData.put(hour(18), new AggregateNumericMetric(schedule3.id, 2.42, 2.42, 2.42,
+ hour(18).getMillis()));
+ schedule3.twentyFourHourData.put(hour(0), new AggregateNumericMetric(schedule3.id,
+ avg(schedule3.sixHourData, hour(12), hour(18)),
+ min(schedule3.sixHourData, hour(12), hour(18)),
+ max(schedule3.sixHourData, hour(12), hour(18)),
+ hour(0).getMillis()));
+
+ List<AggregateNumericMetric> expected = asList(schedule1.oneHourData.get(hour(23)),
+ schedule2.oneHourData.get(hour(23)));
+
+ assertCollectionEqualsNoOrder(expected, oneHourData, "The returned one hour data is wrong");
+ // verify values in db
+ assert1HourDataEquals(schedule1.id, schedule1.oneHourData.get(hour(23)), schedule1.oneHourData.get(hour(18)),
+ schedule1.oneHourData.get(hour(17)), schedule1.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule2.id, schedule2.oneHourData.get(hour(23)), schedule2.oneHourData.get(hour(18)),
+ schedule2.oneHourData.get(hour(17)), schedule2.oneHourData.get(hour(16)));
+ assert1HourDataEquals(schedule3.id, schedule3.oneHourData.get(hour(18)), schedule3.oneHourData.get(hour(16)));
+ assert6HourDataEquals(schedule1.id, schedule1.sixHourData.get(hour(12)), schedule1.sixHourData.get(hour(18)));
+ assert6HourDataEquals(schedule2.id, schedule2.sixHourData.get(hour(12)), schedule2.sixHourData.get(hour(18)));
+ assert6HourDataEquals(schedule3.id, schedule3.sixHourData.get(hour(12)), schedule3.sixHourData.get(hour(18)));
+ assert24HourDataEquals(schedule1.id, schedule1.twentyFourHourData.get(hour(0)));
+ assert24HourDataEquals(schedule2.id, schedule2.twentyFourHourData.get(hour(0)));
+ assert24HourDataEquals(schedule3.id, schedule3.twentyFourHourData.get(hour(0)));
+ assert1HourMetricsIndexEmpty(hour(23));
+ assert6HourMetricsIndexEmpty(hour(18));
+ assert24HourMetricsIndexEmpty(hour(0));
+ }
+
+ @Test(dependsOnMethods = "runAggregationForHour24")
+ public void resetDBForFailureScenarios() throws Exception {
+ purgeDB();
+ }
+
+ @Test(dependsOnMethods = "resetDBForFailureScenarios")
+ public void failToFetchRawDataIndexDuringAggregationForHour12() throws Exception {
+ currentHour = hour(12);
+ AggregatorTestStub aggregator = new AggregatorTestStub(hour(11), new MetricsDAO(storageSession, configuration) {
+ @Override
+ public StorageResultSetFuture findMetricsIndexEntriesAsync(MetricsTable table, long timestamp) {
+ if (table == MetricsTable.ONE_HOUR) {
+ return new FailedStorageResultSetFuture(new Exception("Failed to fetch raw data index"));
+ } else {
+ return super.findMetricsIndexEntriesAsync(table,
+ timestamp);
+ }
+ }
+ });
+
+ insertRawData(
+ new MeasurementDataNumeric(hour(12).plusMinutes(10).getMillis(), schedule4.id, 7.456),
+ new MeasurementDataNumeric(hour(12).plusMinutes(14).getMillis(), schedule5.id, 29.3)
+ ).await("Failed to insert raw data");
+
+ updateIndex(
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule4.id, hour(12)),
+ new IndexUpdate(MetricsTable.ONE_HOUR, schedule5.id, hour(12))
+ ).await("Failed to update raw data index");
+
+ insert1HourData(
+ new AggregateNumericMetric(schedule4.id, 26.6, 18.33, 29.02, hour(10).getMillis()),
+ new AggregateNumericMetric(schedule4.id, 25.2, 21.12, 28.05, hour(11).getMillis())
+ ).await("Failed to insert 1 hour data");
+
+ updateIndex(new IndexUpdate(MetricsTable.SIX_HOUR, schedule4.id, hour(6)))
+ .await("Failed to update 1 hr data index");
+
+ schedule4.oneHourData.put(hour(10), new AggregateNumericMetric(schedule4.id, 26.6, 18.33, 29.02,
+ hour(10).getMillis()));
+ schedule4.oneHourData.put(hour(11), new AggregateNumericMetric(schedule4.id, 25.2, 21.12, 28.05,
+ hour(11).getMillis()));
+ schedule4.sixHourData.put(hour(6), new AggregateNumericMetric(schedule4.id,
+ avg(schedule4.oneHourData, hour(10), hour(11)),
+ min(schedule4.oneHourData, hour(10), hour(11)),
+ max(schedule4.oneHourData, hour(10), hour(11)),
+ hour(6).getMillis()));
+
+ Set<AggregateNumericMetric> oneHourData = aggregator.run();
+ List<AggregateNumericMetric> emptyAggregates = Collections.emptyList();
+
+ assertTrue(oneHourData.isEmpty(), "Did not expect to get back any one hour aggregates");
+ // verify values in db
+ assert1HourDataEquals(schedule4.id, schedule4.oneHourData.get(hour(10)), schedule4.oneHourData.get(hour(11)));
+ assert1HourDataEquals(schedule5.id, emptyAggregates);
+ assert6HourDataEquals(schedule4.id, schedule4.sixHourData.get(hour(6)));
+ assert6HourDataEmpty(schedule5.id);
+ assert24HourDataEmpty(schedule4.id);
+ assert24HourDataEmpty(schedule5.id);
+ assert1HourMetricsIndexEmpty(hour(11));
+ assert6HourMetricsIndexEmpty(hour(6));
+ assert24HourIndexEquals(hour(0), schedule4.id);
+ }
+
+ private WaitForWrite insertRawData(MeasurementDataNumeric... data) {
+ WaitForWrite waitForRawInserts = new WaitForWrite(data.length);
+ for (MeasurementDataNumeric raw : data) {
+ StorageResultSetFuture resultSetFuture = dao.insertRawData(raw);
+ Futures.addCallback(resultSetFuture, waitForRawInserts);
+ }
+ return waitForRawInserts;
+ }
+
+ private WaitForWrite insert1HourData(AggregateNumericMetric... data) {
+ WaitForWrite waitForWrite = new WaitForWrite(data.length * 3);
+ for (AggregateNumericMetric datum : data) {
+ StorageResultSetFuture future = dao.insertOneHourDataAsync(datum.getScheduleId(), datum.getTimestamp(),
+ AggregateType.AVG, datum.getAvg());
+ Futures.addCallback(future, waitForWrite);
+
+ future = dao.insertOneHourDataAsync(datum.getScheduleId(), datum.getTimestamp(), AggregateType.MIN,
+ datum.getMin());
+ Futures.addCallback(future, waitForWrite);
+
+ future = dao.insertOneHourDataAsync(datum.getScheduleId(), datum.getTimestamp(), AggregateType.MAX,
+ datum.getMax());
+ Futures.addCallback(future, waitForWrite);
+ }
+ return waitForWrite;
+ }
+
+ private WaitForWrite updateIndex(IndexUpdate... updates) {
+ WaitForWrite waitForWrite = new WaitForWrite(updates.length);
+ for (IndexUpdate update : updates) {
+ StorageResultSetFuture future = dao.updateMetricsIndex(update.table, update.scheduleId,
+ update.time.getMillis());
+ Futures.addCallback(future, waitForWrite);
+ }
+ return waitForWrite;
+ }
+
+ private double avg(Map<DateTime, AggregateNumericMetric> data, DateTime... times) {
+ double[] values = new double[times.length];
+ for (int i = 0; i < times.length; ++i) {
+ values[i] = data.get(times[i]).getAvg();
+ }
+ return avg(values);
+ }
+
+ private double min(Map<DateTime, AggregateNumericMetric> data, DateTime... times) {
+ double min = data.get(times[0]).getMin();
+ for (DateTime time : times) {
+ if (data.get(time).getMin() < min) {
+ min = data.get(time).getMin();
+ }
+ }
+ return min;
+ }
+
+ private double max(Map<DateTime, AggregateNumericMetric> data, DateTime... times) {
+ double max = data.get(times[0]).getMin();
+ for (DateTime time : times) {
+ if (data.get(time).getMax() > max) {
+ max = data.get(time).getMax();
+ }
+ }
+ return max;
+ }
+
+ protected void assert6HourIndexEquals(DateTime timeSlice, int... scheduleIds) {
+ List<MetricsIndexEntry> indexEntries = new ArrayList<MetricsIndexEntry>(scheduleIds.length);
+ for (int scheduleId : scheduleIds) {
+ indexEntries.add(new MetricsIndexEntry(MetricsTable.SIX_HOUR, timeSlice, scheduleId));
+ }
+ assertMetricsIndexEquals(MetricsTable.SIX_HOUR, timeSlice.getMillis(), indexEntries,
+ "The 6 hour index is wrong");
+ }
+
+ protected void assert24HourIndexEquals(DateTime timeSlice, int... scheduleIds) {
+ List<MetricsIndexEntry> indexEntries = new ArrayList<MetricsIndexEntry>(scheduleIds.length);
+ for (int scheduleId : scheduleIds) {
+ indexEntries.add(new MetricsIndexEntry(MetricsTable.TWENTY_FOUR_HOUR, timeSlice, scheduleId));
+ }
+ assertMetricsIndexEquals(MetricsTable.TWENTY_FOUR_HOUR, timeSlice.getMillis(), indexEntries,
+ "The 24 hour index is wrong");
+ }
+
+ private class AggregatorTestStub extends Aggregator {
+
+ public AggregatorTestStub(DateTime startTime) {
+ super(aggregationTasks, dao, configuration, dateTimeService, startTime, 250, writePermits, readPermits);
+ }
+
+ public AggregatorTestStub(DateTime startTime, MetricsDAO dao) {
+ super(aggregationTasks, dao, configuration, dateTimeService, startTime, 250, writePermits, readPermits);
+ }
+
+ @Override
+ protected DateTime currentHour() {
+ return currentHour;
+ }
+ }
+
+ private class IndexUpdate {
+ MetricsTable table;
+ int scheduleId;
+ DateTime time;
+
+ public IndexUpdate(MetricsTable table, int scheduleId, DateTime time) {
+ this.table = table;
+ this.scheduleId = scheduleId;
+ this.time = time;
+ }
+ }
+
+ private class Aggregates {
+ int id; // schedule id
+ Map<DateTime, AggregateNumericMetric> oneHourData = new HashMap<DateTime, AggregateNumericMetric>();
+ Map<DateTime, AggregateNumericMetric> sixHourData = new HashMap<DateTime, AggregateNumericMetric>();
+ Map<DateTime, AggregateNumericMetric> twentyFourHourData = new HashMap<DateTime, AggregateNumericMetric>();
+ }
+
+ private class FailedStorageResultSetFuture extends StorageResultSetFuture implements ListenableFuture<ResultSet> {
+
+ private SettableFuture future;
+
+ private Throwable t;
+
+ public FailedStorageResultSetFuture(Throwable t) {
+ super(null, null);
+ future = SettableFuture.create();
+ this.t = t;
+ assertTrue(future.setException(t), "Failed to set exception for future");
+ }
+
+ @Override
+ public void addListener(Runnable listener, Executor executor) {
+ future.addListener(listener, executor);
+ }
+
+ @Override
+ public ResultSet get() {
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
index f7125e5..4d48cbb 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/CassandraIntegrationTest.java
@@ -28,6 +28,8 @@ import java.util.concurrent.CountDownLatch;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Host;
+import com.datastax.driver.core.HostDistance;
+import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
@@ -79,6 +81,12 @@ public class CassandraIntegrationTest {
.withCredentialsObfuscated(RHQADMIN, RHQADMIN_PASSWORD)
.build();
+ PoolingOptions poolingOptions = cluster.getConfiguration().getPoolingOptions();
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.LOCAL, 24);
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.REMOTE, 24);
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.LOCAL, 32);
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.REMOTE, 32);
+
cluster.register(new Host.StateListener() {
@Override
public void onAdd(Host host) {
@@ -145,39 +153,6 @@ public class CassandraIntegrationTest {
}
}
- protected static class WaitForWrite implements FutureCallback<ResultSet> {
-
- private final Log log = LogFactory.getLog(WaitForWrite.class);
-
- private CountDownLatch latch;
-
- private Throwable throwable;
-
- public WaitForWrite(int numResults) {
- latch = new CountDownLatch(numResults);
- }
-
- @Override
- public void onSuccess(ResultSet rows) {
- latch.countDown();
- }
-
- @Override
- public void onFailure(Throwable throwable) {
- latch.countDown();
- this.throwable = throwable;
- log.error("An async operation failed", throwable);
- }
-
- public void await(String errorMsg) throws InterruptedException {
- latch.await();
- if (throwable != null) {
- fail(errorMsg, Throwables.getRootCause(throwable));
- }
- }
-
- }
-
protected static class WaitForRead<T> implements FutureCallback<ResultSet> {
private final Log log = LogFactory.getLog(WaitForRead.class);
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
index b06dfda..fff0485 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsDAOTest.java
@@ -29,6 +29,7 @@ import static java.util.Arrays.asList;
import static org.rhq.test.AssertUtils.assertCollectionMatchesNoOrder;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
import java.util.ArrayList;
import java.util.HashMap;
@@ -38,7 +39,9 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
+import com.datastax.driver.core.ResultSet;
import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import org.apache.commons.logging.Log;
@@ -54,6 +57,7 @@ import org.rhq.server.metrics.domain.AggregateNumericMetric;
import org.rhq.server.metrics.domain.AggregateSimpleNumericMetric;
import org.rhq.server.metrics.domain.AggregateType;
import org.rhq.server.metrics.domain.MetricsIndexEntry;
+import org.rhq.server.metrics.domain.MetricsIndexEntryMapper;
import org.rhq.server.metrics.domain.MetricsTable;
import org.rhq.server.metrics.domain.RawNumericMetric;
import org.rhq.server.metrics.domain.RawNumericMetricMapper;
@@ -352,12 +356,24 @@ public class MetricsDAOTest extends CassandraIntegrationTest {
updates.put(scheduleId2, hour0.getMillis());
dao.updateMetricsIndex(MetricsTable.ONE_HOUR, updates);
- List<MetricsIndexEntry> actual = Lists.newArrayList(dao.findMetricsIndexEntries(MetricsTable.ONE_HOUR,
- hour0.getMillis()));
-
- List<MetricsIndexEntry> expected = asList(new MetricsIndexEntry(MetricsTable.ONE_HOUR, hour0, scheduleId1),
+ final List<MetricsIndexEntry> expected = asList(new MetricsIndexEntry(MetricsTable.ONE_HOUR, hour0, scheduleId1),
new MetricsIndexEntry(MetricsTable.ONE_HOUR, hour0, scheduleId2));
- assertCollectionMatchesNoOrder(expected, actual, "Failed to update or retrieve metrics index entries");
+
+ StorageResultSetFuture future = dao.findMetricsIndexEntriesAsync(MetricsTable.ONE_HOUR, hour0.getMillis());
+ Futures.addCallback(future, new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(ResultSet result) {
+ MetricsIndexEntryMapper mapper = new MetricsIndexEntryMapper(MetricsTable.ONE_HOUR);
+ List<MetricsIndexEntry> actual = mapper.mapAll(result);
+
+ assertCollectionMatchesNoOrder(expected, actual, "Failed to update or retrieve metrics index entries");
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ fail("Failed to retrieve one hour index entries", t);
+ }
+ });
}
@Test(enabled = ENABLED)
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsPerfTests.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsPerfTests.java
new file mode 100644
index 0000000..6e93fec
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsPerfTests.java
@@ -0,0 +1,191 @@
+package org.rhq.server.metrics;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.RateLimiter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import org.rhq.core.domain.measurement.MeasurementDataNumeric;
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.RawNumericMetric;
+import org.rhq.server.metrics.domain.RawNumericMetricMapper;
+
+/**
+ * @author John Sanda
+ */
+public class MetricsPerfTests extends MetricsTest {
+
+ private class MetricsServerStub extends MetricsServer {
+
+ DateTime currentHour;
+
+ @Override
+ public DateTime currentHour() {
+ return currentHour;
+ }
+
+ public void setCurrentHour(DateTime currentHour) {
+ this.currentHour = currentHour;
+ }
+ }
+
+ private class DateTimeServiceStub extends DateTimeService {
+
+ DateTime currentHour;
+
+ long startTime;
+
+ public DateTimeServiceStub(DateTime currentHour, long startTime) {
+ this.currentHour = currentHour;
+ this.startTime = startTime;
+ }
+
+ @Override
+ public DateTime now() {
+ return currentHour.plus(System.currentTimeMillis() - startTime);
+ }
+
+ @Override
+ public long nowInMillis() {
+ return now().getMillis();
+ }
+ }
+
+ private final Log log = LogFactory.getLog(MetricsPerfTests.class);
+
+ private MetricsServerStub metricsServer;
+
+ private final int NUM_SCHEDULES = 1000;
+
+ private RateLimiter writePermits;
+ private RateLimiter readPermits;
+
+ @BeforeClass
+ public void setupClass() throws Exception {
+ purgeDB();
+ log.info("Sleeping while table truncation completes...");
+ Thread.sleep(3000);
+ metricsServer = new MetricsServerStub();
+ metricsServer.setConfiguration(configuration);
+ metricsServer.setDAO(dao);
+ metricsServer.setDateTimeService(dateTimeService);
+ writePermits = metricsServer.getWritePermits();
+ readPermits = metricsServer.getReadPermits();
+ }
+
+ private void resetRateLimits() {
+ metricsServer.setWritePermits(writePermits);
+ metricsServer.setReadPermits(readPermits);
+ }
+
+ @Test
+ public void insertRawData() throws Exception {
+ Random random = new Random();
+ DateTime currentHour = hour(3);
+ metricsServer.setWritePermits(RateLimiter.create(3500));
+ metricsServer.setCurrentHour(currentHour);
+ Set<MeasurementDataNumeric> data = new HashSet<MeasurementDataNumeric>();
+ for (int i = 0; i < NUM_SCHEDULES; ++i) {
+ DateTime time = currentHour;
+ for (int j = 0; j < 120; ++j) {
+ data.add(new MeasurementDataNumeric(time.getMillis(), i, random.nextDouble()));
+ time = time.plusSeconds(30);
+ }
+ }
+ WaitForRawInserts waitForRawInserts = new WaitForRawInserts(data.size());
+ metricsServer.addNumericData(data, waitForRawInserts);
+ waitForRawInserts.await("Failed to add raw data");
+ }
+
+ //@Test(dependsOnMethods = "insertRawData")
+ public void queryRawDataAsync() throws Exception {
+ RateLimiter readPermits = RateLimiter.create(50);
+
+ log.info("Running queryRawDataAsync");
+ long start = System.currentTimeMillis();
+
+ DateTime startTime = hour(3).minusHours(1).minusSeconds(1);
+ DateTime endTime = hour(3);
+ final CountDownLatch rawDataArrival = new CountDownLatch(100);
+ final RawNumericMetricMapper mapper = new RawNumericMetricMapper();
+ final Map<Integer, List<RawNumericMetric>> rawDataMap =
+ new ConcurrentHashMap<Integer, List<RawNumericMetric>>(100);
+
+ for (int i = 0; i < NUM_SCHEDULES; ++i) {
+ final int scheduleId = i;
+// readPermits.acquire();
+ StorageResultSetFuture rawDataFuture = dao.findRawMetricsAsync(scheduleId, startTime.getMillis(),
+ endTime.getMillis());
+ Futures.addCallback(rawDataFuture, new FutureCallback<ResultSet>() {
+ @Override
+ public void onSuccess(ResultSet result) {
+ List<RawNumericMetric> rawData = mapper.mapAll(result);
+ rawDataMap.put(scheduleId, rawData);
+ rawDataArrival.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.warn("Failed to retrieve raw data for schedule id " + scheduleId, t);
+ }
+ });
+ }
+
+ rawDataArrival.await();
+ log.info("Finished raw data aysnc query in " + (System.currentTimeMillis() - start) + " ms");
+ }
+
+ //@Test(dependsOnMethods = "insertRawData")
+ public void queryDataSync() throws Exception {
+ log.info("Running queryDataSync");
+
+ long start = System.currentTimeMillis();
+ DateTime startTime = hour(3).minusHours(1).minusSeconds(1);
+ DateTime endTime = hour(3);
+ RawNumericMetricMapper mapper = new RawNumericMetricMapper();
+ Map<Integer, List<RawNumericMetric>> rawDataMp = new HashMap<Integer, List<RawNumericMetric>>(100);
+
+ for (int i = 0; i < NUM_SCHEDULES; ++i) {
+ ResultSet resultSet = dao.findRawMetricsSync(i, startTime.getMillis(), endTime.getMillis());
+ rawDataMp.put(i, mapper.mapAll(resultSet));
+ }
+
+ log.info("Finished raw data sync query in " + (System.currentTimeMillis() - start) + " ms");
+ }
+
+ @Test(dependsOnMethods = "insertRawData")
+ public void runAggregation() {
+ log.info("Running aggregation");
+
+ resetRateLimits();
+
+ long start = System.currentTimeMillis();
+ DateTime currentHour = hour(4);
+ metricsServer.setCurrentHour(currentHour);
+ metricsServer.setAggregationBatchSize(250);
+ metricsServer.setUseAsyncAggregation(false);
+ metricsServer.setDateTimeService(new DateTimeServiceStub(hour(4), start));
+ Collection<AggregateNumericMetric> oneHourData =
+ (Collection<AggregateNumericMetric>) metricsServer.calculateAggregates();
+
+ log.info("Finished computing " + oneHourData.size() + " one hour aggregates in " +
+ (System.currentTimeMillis() - start) + " ms");
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
index 95ada4c..b3ffb66 100644
--- a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsServerTest.java
@@ -31,7 +31,6 @@ 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.assertTrue;
-import static org.testng.Assert.fail;
import java.math.BigDecimal;
import java.math.MathContext;
@@ -40,10 +39,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
-import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
@@ -1011,39 +1008,4 @@ public class MetricsServerTest extends CassandraIntegrationTest {
return new SimplePagedResult<RawNumericMetric>(cql, new RawNumericMetricMapper(true), storageSession);
}
- private static class WaitForRawInserts implements RawDataInsertedCallback {
-
- private final Log log = LogFactory.getLog(WaitForRawInserts.class);
-
- private CountDownLatch latch;
-
- private Throwable throwable;
-
- public WaitForRawInserts(int numInserts) {
- latch = new CountDownLatch(numInserts);
- }
-
- @Override
- public void onFinish() {
- }
-
- @Override
- public void onSuccess(MeasurementDataNumeric measurementDataNumeric) {
- latch.countDown();
- }
-
- @Override
- public void onFailure(Throwable throwable) {
- latch.countDown();
- this.throwable = throwable;
- log.error("An async operation failed", throwable);
- }
-
- public void await(String errorMsg) throws InterruptedException {
- latch.await();
- if (throwable != null) {
- fail(errorMsg, Throwables.getRootCause(throwable));
- }
- }
- }
}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsTest.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsTest.java
new file mode 100644
index 0000000..59e0b77
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/MetricsTest.java
@@ -0,0 +1,138 @@
+package org.rhq.server.metrics;
+
+import static java.util.Arrays.asList;
+import static org.rhq.test.AssertUtils.assertCollectionMatchesNoOrder;
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import org.joda.time.DateTime;
+import org.testng.annotations.BeforeClass;
+
+import org.rhq.server.metrics.domain.AggregateNumericMetric;
+import org.rhq.server.metrics.domain.MetricsIndexEntry;
+import org.rhq.server.metrics.domain.MetricsTable;
+
+/**
+ * @author John Sanda
+ */
+public class MetricsTest extends CassandraIntegrationTest {
+
+ private static final double TEST_PRECISION = Math.pow(10, -9);
+
+ protected MetricsDAO dao;
+ protected MetricsConfiguration configuration = new MetricsConfiguration();
+ protected DateTimeService dateTimeService;
+
+ @BeforeClass
+ public void initClass() throws Exception {
+ dao = new MetricsDAO(storageSession, configuration);
+ dateTimeService = new DateTimeService();
+ dateTimeService.setConfiguration(configuration);
+ }
+
+ protected DateTime hour(int hourOfDay) {
+ return hour0().plusHours(hourOfDay);
+ }
+
+ protected double avg(double... values) {
+ double sum = 0;
+ for (double value : values) {
+ sum += value;
+ }
+ return divide(sum, values.length);
+ }
+
+ protected double divide(double dividend, int divisor) {
+ return new BigDecimal(Double.toString(dividend)).divide(new BigDecimal(Integer.toString(divisor)),
+ MathContext.DECIMAL64).doubleValue();
+ }
+
+ protected void purgeDB() {
+ session.execute("TRUNCATE " + MetricsTable.RAW);
+ session.execute("TRUNCATE " + MetricsTable.ONE_HOUR);
+ session.execute("TRUNCATE " + MetricsTable.SIX_HOUR);
+ session.execute("TRUNCATE " + MetricsTable.TWENTY_FOUR_HOUR);
+ session.execute("TRUNCATE " + MetricsTable.INDEX);
+ }
+
+ protected void assert1HourDataEquals(int scheduleId, AggregateNumericMetric... expected) {
+ assert1HourDataEquals(scheduleId, asList(expected));
+ }
+
+ protected void assert1HourDataEquals(int scheduleId, Collection<AggregateNumericMetric> expected) {
+ assert1HourDataEquals(scheduleId, new ArrayList<AggregateNumericMetric>(expected));
+ }
+
+ protected void assert1HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) {
+ assertMetricDataEquals(MetricsTable.ONE_HOUR, scheduleId, expected);
+ }
+
+ protected void assert6HourDataEquals(int scheduleId, AggregateNumericMetric... expected) {
+ assert6HourDataEquals(scheduleId, asList(expected));
+ }
+
+ protected void assert6HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) {
+ assertMetricDataEquals(MetricsTable.SIX_HOUR, scheduleId, expected);
+ }
+
+ protected void assert24HourDataEquals(int scheduleId, List<AggregateNumericMetric> expected) {
+ assertMetricDataEquals(MetricsTable.TWENTY_FOUR_HOUR, scheduleId, expected);
+ }
+
+ protected void assert24HourDataEquals(int scheduleId, AggregateNumericMetric... expected) {
+ assertMetricDataEquals(MetricsTable.TWENTY_FOUR_HOUR, scheduleId, asList(expected));
+ }
+
+ private void assertMetricDataEquals(MetricsTable columnFamily, int scheduleId,
+ List<AggregateNumericMetric> expected) {
+ List<AggregateNumericMetric> actual = Lists.newArrayList(findAggregateMetrics(columnFamily, scheduleId));
+ assertCollectionMatchesNoOrder("Metric data for schedule id " + scheduleId + " in table " + columnFamily +
+ " does not match expected values", expected, actual, TEST_PRECISION);
+ }
+
+ protected void assertMetricsIndexEquals(MetricsTable table, long timeSlice, List<MetricsIndexEntry> expected,
+ String msg) {
+ List<MetricsIndexEntry> actual = Lists.newArrayList(dao.findMetricsIndexEntries(table, timeSlice));
+ assertCollectionMatchesNoOrder(msg + ": " + table + " index does not match expected values.",
+ expected, actual);
+ }
+
+ protected void assert6HourDataEmpty(int scheduleId) {
+ assertMetricDataEmpty(scheduleId, MetricsTable.SIX_HOUR);
+ }
+
+ protected void assert24HourDataEmpty(int scheduleId) {
+ assertMetricDataEmpty(scheduleId, MetricsTable.TWENTY_FOUR_HOUR);
+ }
+
+ private void assertMetricDataEmpty(int scheduleId, MetricsTable columnFamily) {
+ List<AggregateNumericMetric> metrics = Lists.newArrayList(findAggregateMetrics(columnFamily, scheduleId));
+ assertEquals(metrics.size(), 0, "Expected " + columnFamily + " to be empty for schedule id " + scheduleId +
+ " but found " + metrics);
+ }
+
+ protected void assert1HourMetricsIndexEmpty(DateTime timeSlice) {
+ assertMetricsIndexEmpty(MetricsTable.ONE_HOUR, timeSlice);
+ }
+
+ protected void assert6HourMetricsIndexEmpty(DateTime timeSlice) {
+ assertMetricsIndexEmpty(MetricsTable.SIX_HOUR, timeSlice);
+ }
+
+ protected void assert24HourMetricsIndexEmpty(DateTime timeSlice) {
+ assertMetricsIndexEmpty(MetricsTable.TWENTY_FOUR_HOUR, timeSlice);
+ }
+
+ private void assertMetricsIndexEmpty(MetricsTable table, DateTime timeSlice) {
+ List<MetricsIndexEntry> index = Lists.newArrayList(dao.findMetricsIndexEntries(table, timeSlice.getMillis()));
+ assertEquals(index.size(), 0, "Expected metrics index for " + table + " to be empty but found " + index);
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForRawInserts.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForRawInserts.java
new file mode 100644
index 0000000..5feffda
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForRawInserts.java
@@ -0,0 +1,51 @@
+package org.rhq.server.metrics;
+
+import static org.testng.Assert.fail;
+
+import java.util.concurrent.CountDownLatch;
+
+import com.google.common.base.Throwables;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.measurement.MeasurementDataNumeric;
+
+/**
+* @author John Sanda
+*/
+class WaitForRawInserts implements RawDataInsertedCallback {
+
+ private final Log log = LogFactory.getLog(WaitForRawInserts.class);
+
+ private CountDownLatch latch;
+
+ private Throwable throwable;
+
+ public WaitForRawInserts(int numInserts) {
+ latch = new CountDownLatch(numInserts);
+ }
+
+ @Override
+ public void onFinish() {
+ }
+
+ @Override
+ public void onSuccess(MeasurementDataNumeric measurementDataNumeric) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ latch.countDown();
+ this.throwable = throwable;
+ log.error("An async operation failed", throwable);
+ }
+
+ public void await(String errorMsg) throws InterruptedException {
+ latch.await();
+ if (throwable != null) {
+ fail(errorMsg, Throwables.getRootCause(throwable));
+ }
+ }
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForWrite.java b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForWrite.java
new file mode 100644
index 0000000..b67c72d
--- /dev/null
+++ b/modules/enterprise/server/server-metrics/src/test/java/org/rhq/server/metrics/WaitForWrite.java
@@ -0,0 +1,48 @@
+package org.rhq.server.metrics;
+
+import static org.testng.Assert.fail;
+
+import java.util.concurrent.CountDownLatch;
+
+import com.datastax.driver.core.ResultSet;
+import com.google.common.base.Throwables;
+import com.google.common.util.concurrent.FutureCallback;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+* @author John Sanda
+*/
+class WaitForWrite implements FutureCallback<ResultSet> {
+
+ private final Log log = LogFactory.getLog(WaitForWrite.class);
+
+ private CountDownLatch latch;
+
+ private Throwable throwable;
+
+ public WaitForWrite(int numResults) {
+ latch = new CountDownLatch(numResults);
+ }
+
+ @Override
+ public void onSuccess(ResultSet rows) {
+ latch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ latch.countDown();
+ this.throwable = throwable;
+ log.error("An async operation failed", throwable);
+ }
+
+ public void await(String errorMsg) throws InterruptedException {
+ latch.await();
+ if (throwable != null) {
+ fail(errorMsg, Throwables.getRootCause(throwable));
+ }
+ }
+
+}
diff --git a/modules/enterprise/server/server-metrics/src/test/resources/log4j.xml b/modules/enterprise/server/server-metrics/src/test/resources/log4j.xml
index d93f284..4ce4d9e 100644
--- a/modules/enterprise/server/server-metrics/src/test/resources/log4j.xml
+++ b/modules/enterprise/server/server-metrics/src/test/resources/log4j.xml
@@ -8,7 +8,7 @@
<appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
- <param name="Threshold" value="WARN" />
+ <param name="Threshold" value="DEBUG" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{dd-MM HH:mm:ss,SSS} (%F:%M:%L) - %m%n" />
</layout>
diff --git a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/MeasurementAggregator.java b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/MeasurementAggregator.java
index 7a7c6b9..b5f61f3 100644
--- a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/MeasurementAggregator.java
+++ b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/MeasurementAggregator.java
@@ -49,12 +49,15 @@ public class MeasurementAggregator implements Runnable {
private ShutdownManager shutdownManager;
+ private int numSchedules;
+
public MeasurementAggregator(MetricsServer metricsServer, ShutdownManager shutdownManager, Metrics metrics,
- ExecutorService aggregationQueue) {
+ ExecutorService aggregationQueue, int numSchedules) {
this.metricsServer = metricsServer;
this.shutdownManager = shutdownManager;
this.metrics = metrics;
this.aggregationQueue = aggregationQueue;
+ this.numSchedules = numSchedules;
}
public void run() {
@@ -62,6 +65,7 @@ public class MeasurementAggregator implements Runnable {
@Override
public void run() {
Timer.Context context = metrics.totalAggregationTime.time();
+ long start = System.currentTimeMillis();
try {
log.debug("Starting metrics aggregation");
metricsServer.calculateAggregates();
@@ -71,6 +75,7 @@ public class MeasurementAggregator implements Runnable {
shutdownManager.shutdown(1);
} finally {
context.stop();
+ log.debug("Finished metrics aggregation in " + (System.currentTimeMillis() - start) + " ms");
metrics.totalAggregationRuns.inc();
}
}
diff --git a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
index 63bdbea..3e9b4af 100644
--- a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
+++ b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/Simulator.java
@@ -31,6 +31,8 @@ import java.util.concurrent.TimeUnit;
import com.codahale.metrics.ConsoleReporter;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Host;
+import com.datastax.driver.core.HostDistance;
+import com.datastax.driver.core.PoolingOptions;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
@@ -65,31 +67,39 @@ public class Simulator implements ShutdownManager {
Metrics metrics = new Metrics();
final ConsoleReporter consoleReporter = createConsoleReporter(metrics, plan.getMetricsReportInterval());
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- shutdown(collectors, "collectors", 5);
- shutdown(readers, "readers", 5);
- shutdown(aggregators, "aggregators", 1);
- shutdown(aggregationQueue, "aggregationQueue", Integer.MAX_VALUE);
- consoleReporter.stop();
- }
- });
-
createSchema(plan.getNodes(), plan.getCqlPort());
Session session = createSession(plan.getNodes(), plan.getCqlPort());
StorageSession storageSession = new StorageSession(session);
MetricsDAO metricsDAO = new MetricsDAO(storageSession, plan.getMetricsServerConfiguration());
- MetricsServer metricsServer = new MetricsServer();
+ final MetricsServer metricsServer = new MetricsServer();
metricsServer.setDAO(metricsDAO);
metricsServer.setConfiguration(plan.getMetricsServerConfiguration());
+ metricsServer.setAggregationBatchSize(plan.getAggregationBatchSize());
+ metricsServer.setUseAsyncAggregation(plan.getAggregationType() == SimulationPlan.AggregationType.ASYNC);
metricsServer.setDateTimeService(plan.getDateTimeService());
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ shutdown(collectors, "collectors", 5);
+ shutdown(readers, "readers", 5);
+ shutdown(aggregators, "aggregators", 1);
+ shutdown(aggregationQueue, "aggregationQueue", Integer.MAX_VALUE);
+ metricsServer.shutdown();
+ log.info("Wait for console reporter...");
+ try {
+ Thread.sleep(181000);
+ } catch (InterruptedException e) {
+ }
+ consoleReporter.stop();
+ }
+ });
+
MeasurementAggregator measurementAggregator = new MeasurementAggregator(metricsServer, this, metrics,
- aggregationQueue);
+ aggregationQueue, plan.getNumMeasurementCollectors() * plan.getBatchSize());
for (int i = 0; i < plan.getNumMeasurementCollectors(); ++i) {
collectors.scheduleAtFixedRate(new MeasurementCollector(plan.getBatchSize(),
@@ -169,6 +179,11 @@ public class Simulator implements ShutdownManager {
Cluster cluster = new ClusterBuilder().addContactPoints(nodes).withPort(cqlPort)
.withCredentials("rhqadmin", "rhqadmin")
.build();
+ PoolingOptions poolingOptions = cluster.getConfiguration().getPoolingOptions();
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.LOCAL, 24);
+ poolingOptions.setCoreConnectionsPerHost(HostDistance.REMOTE, 24);
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.LOCAL, 32);
+ poolingOptions.setMaxConnectionsPerHost(HostDistance.REMOTE, 32);
log.debug("Created cluster object with " + cluster.getConfiguration().getProtocolOptions().getCompression()
+ " compression.");
diff --git a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlan.java b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlan.java
index 33b3bbf..7ad50d1 100644
--- a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlan.java
+++ b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlan.java
@@ -56,6 +56,26 @@ public class SimulationPlan {
}
}
+ public static enum AggregationType {
+ SYNC("sync"), ASYNC("async");
+
+ private final String text;
+
+ AggregationType(String text) {
+ this.text = text;
+ }
+
+ public static AggregationType fromText(String text) {
+ if (text.equals("sync")) {
+ return SYNC;
+ }
+ if (text.equals("async")) {
+ return ASYNC;
+ }
+ throw new IllegalArgumentException(text + " is not a valid aggregation type");
+ }
+ }
+
private long collectionInterval;
private long aggregationInterval;
@@ -84,6 +104,10 @@ public class SimulationPlan {
private long simulationRate;
+ private int aggregationBatchSize;
+
+ private AggregationType aggregationType;
+
public DateTimeService getDateTimeService() {
return dateTimeService;
}
@@ -195,4 +219,20 @@ public class SimulationPlan {
public void setSimulationRate(long simulationRate) {
this.simulationRate = simulationRate;
}
+
+ public int getAggregationBatchSize() {
+ return aggregationBatchSize;
+ }
+
+ public void setAggregationBatchSize(int aggregationBatchSize) {
+ this.aggregationBatchSize = aggregationBatchSize;
+ }
+
+ public AggregationType getAggregationType() {
+ return aggregationType;
+ }
+
+ public void setAggregationType(AggregationType aggregationType) {
+ this.aggregationType = aggregationType;
+ }
}
diff --git a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlanner.java b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlanner.java
index e601212..ff1d83f 100644
--- a/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlanner.java
+++ b/modules/helpers/metrics-simulator/src/main/java/org/rhq/metrics/simulator/plan/SimulationPlanner.java
@@ -98,6 +98,9 @@ public class SimulationPlanner {
simulation.setNodes(nodes);
simulation.setCqlPort(getInt(root.get("cqlPort"), 9142));
+ simulation.setAggregationBatchSize(getInt(root.get("aggregationBatchSize"), 250));
+ simulation.setAggregationType(SimulationPlan.AggregationType.fromText(getString(root.get("aggregationType"),
+ "sync")));
return simulation;
}
commit c400fed353c5824d3ea3c40a47f05cd9f69d3af0
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Mon Dec 16 19:23:37 2013 +0100
[BZ 970181] - RHQ doesn't work after restart with open GUI in web browser. - if the server restarts at the background, the session id is lost on the server therefore we can't allow the logged user to continue sending the requests without a new AuthN. It would be a security risk to allow it. Now, if the server restarts the UI requires the new log in (the login form is displayed).
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/util/rpc/TrackingRequestCallback.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/util/rpc/TrackingRequestCallback.java
index 07893f6..b443c1a 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/util/rpc/TrackingRequestCallback.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/util/rpc/TrackingRequestCallback.java
@@ -23,6 +23,7 @@ import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import org.rhq.coregui.client.CoreGUI;
+import org.rhq.coregui.client.LoginView;
import org.rhq.coregui.client.UserSessionManager;
import org.rhq.coregui.client.util.Log;
@@ -86,6 +87,11 @@ public class TrackingRequestCallback implements RequestCallback {
switch (statusCode) {
case STATUS_CODE_OK:
+ if (response != null && response.getText() != null && response.getText().isEmpty()
+ && !LoginView.isLoginShowing()) { // this happen when the RHQ server was restarted
+ new LoginView().showLoginDialog();
+ break;
+ }
RPCTracker.getInstance().succeedCall(this);
callback.onResponseReceived(request, response);
break;
commit 27668cb9cfce7eeec83967978f11435c69c86918
Author: Mike Thompson <mithomps(a)redhat.com>
Date: Fri Dec 13 15:51:56 2013 -0800
[BZ 1034512] Update Group Metric Graphs to be more like new Resource Metrics Graphs
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/D3GroupGraphListView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
index d0b56a7..f6805e2 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/D3GroupGraphListView.java
@@ -189,6 +189,7 @@ public final class D3GroupGraphListView extends AbstractD3GraphListView implemen
private void buildIndividualGraph(MeasurementDefinition measurementDefinition,
List<MeasurementDataNumericHighLowComposite> data) {
+ Log.debug("\n***** D3GroupGraphListView.MD: "+measurementDefinition);
MetricGraphData metricGraphData = MetricGraphData.createForResourceGroup(resourceGroup.getId(),
resourceGroup.getName(), measurementDefinition, data);
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/ResourceGroupDetailView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/ResourceGroupDetailView.java
index 9ddbb77..8394217 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/ResourceGroupDetailView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/ResourceGroupDetailView.java
@@ -63,6 +63,7 @@ import org.rhq.coregui.client.inventory.groups.detail.configuration.HistoryGroup
import org.rhq.coregui.client.inventory.groups.detail.inventory.GroupPluginConfigurationEditView;
import org.rhq.coregui.client.inventory.groups.detail.inventory.HistoryGroupPluginConfigurationView;
import org.rhq.coregui.client.inventory.groups.detail.inventory.MembersView;
+import org.rhq.coregui.client.inventory.groups.detail.monitoring.metric.MetricsGroupView;
import org.rhq.coregui.client.inventory.groups.detail.monitoring.schedules.ResourceGroupSchedulesView;
import org.rhq.coregui.client.inventory.groups.detail.monitoring.table.GroupMonitoringTablesView;
import org.rhq.coregui.client.inventory.groups.detail.monitoring.traits.TraitsView;
@@ -332,7 +333,7 @@ public class ResourceGroupDetailView extends
viewFactory = (!showOnPage) ? null : new ViewFactory() {
@Override
public Canvas createView() {
- return createD3GraphListView();
+ return MetricsGroupView.create(groupComposite.getResourceGroup());
}
};
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupTableView.java
new file mode 100644
index 0000000..89d4f67
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupTableView.java
@@ -0,0 +1,408 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.coregui.client.inventory.groups.detail.monitoring.metric;
+
+import static org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricsGridFieldName.METRIC_DEF_ID;
+import static org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricsGridFieldName.RESOURCE_GROUP_ID;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.ExpansionMode;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.HTMLFlow;
+import com.smartgwt.client.widgets.IButton;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.form.fields.SelectItem;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.DataArrivedEvent;
+import com.smartgwt.client.widgets.grid.events.DataArrivedHandler;
+import com.smartgwt.client.widgets.grid.events.RecordCollapseEvent;
+import com.smartgwt.client.widgets.grid.events.RecordCollapseHandler;
+import com.smartgwt.client.widgets.grid.events.RecordExpandEvent;
+import com.smartgwt.client.widgets.grid.events.RecordExpandHandler;
+import com.smartgwt.client.widgets.grid.events.SelectionChangedHandler;
+import com.smartgwt.client.widgets.grid.events.SelectionEvent;
+import com.smartgwt.client.widgets.grid.events.SortChangedHandler;
+import com.smartgwt.client.widgets.grid.events.SortEvent;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.toolbar.ToolStrip;
+
+import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.criteria.DashboardCriteria;
+import org.rhq.core.domain.dashboard.Dashboard;
+import org.rhq.core.domain.dashboard.DashboardPortlet;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.core.domain.resource.group.GroupCategory;
+import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.core.domain.util.PageList;
+import org.rhq.coregui.client.CoreGUI;
+import org.rhq.coregui.client.components.table.Table;
+import org.rhq.coregui.client.dashboard.portlets.inventory.groups.graph.ResourceGroupD3GraphPortlet;
+import org.rhq.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.coregui.client.inventory.common.AbstractD3GraphListView;
+import org.rhq.coregui.client.inventory.common.graph.CustomDateRangeState;
+import org.rhq.coregui.client.inventory.common.graph.MetricGraphData;
+import org.rhq.coregui.client.inventory.common.graph.Refreshable;
+import org.rhq.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
+import org.rhq.coregui.client.inventory.resource.detail.monitoring.MetricD3Graph;
+import org.rhq.coregui.client.util.BrowserUtility;
+import org.rhq.coregui.client.util.Log;
+import org.rhq.coregui.client.util.message.Message;
+
+/**
+ * Views a resource's metrics in a tabular view with sparkline graph and optional detailed d3 graph.
+ *
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class MetricsGroupTableView extends Table<MetricsGroupViewDataSource> implements Refreshable {
+
+ private final ResourceGroup resourceGroup;
+ private final AbstractD3GraphListView abstractD3GraphListView;
+ private ToolStrip toolStrip;
+ private SelectItem dashboardSelectItem;
+ private Dashboard selectedDashboard;
+ private IButton addToDashboardButton;
+ private LinkedHashMap<String, String> dashboardMenuMap;
+ private LinkedHashMap<Integer, Dashboard> dashboardMap;
+ private Set<Integer> expandedRows;
+ private MetricsTableListGrid metricsTableListGrid;
+ private int selectedMetricDefinitionId;
+
+ public MetricsGroupTableView(ResourceGroup resourceGroup, AbstractD3GraphListView abstractD3GraphListView,
+ Set<Integer> expandedRows) {
+ super();
+ this.resourceGroup = resourceGroup;
+ this.abstractD3GraphListView = abstractD3GraphListView;
+ dashboardMenuMap = new LinkedHashMap<String, String>();
+ dashboardMap = new LinkedHashMap<Integer, Dashboard>();
+ setDataSource(new MetricsGroupViewDataSource(resourceGroup));
+ this.expandedRows = expandedRows;
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ }
+
+ /**
+ * Creates this Table's list grid (called by onInit()). Subclasses can override this if they require a custom
+ * subclass of ListGrid.
+ *
+ * @return this Table's list grid (must be an instance of ListGrid)
+ */
+ @Override
+ protected ListGrid createListGrid() {
+ metricsTableListGrid = new MetricsTableListGrid(this, resourceGroup);
+ metricsTableListGrid.addSelectionChangedHandler(new SelectionChangedHandler() {
+ @Override
+ public void onSelectionChanged(SelectionEvent selectionEvent) {
+ if (resourceGroup.getGroupCategory() == GroupCategory.COMPATIBLE) {
+ addToDashboardButton.enable();
+ ListGridRecord selectedRecord = selectionEvent.getSelectedRecord();
+ if (null != selectedRecord) {
+ selectedMetricDefinitionId = selectedRecord.getAttributeAsInt(METRIC_DEF_ID.getValue());
+ }
+ }
+ }
+ });
+
+ if (null == toolStrip) {
+ toolStrip = createToolstrip();
+ }
+ addExtraWidget(toolStrip, false);
+ addToDashboardButton.disable();
+ return metricsTableListGrid;
+ }
+
+ protected void configureTable() {
+ ArrayList<ListGridField> fields = getDataSource().getListGridFields();
+ setListGridFields(fields.toArray(new ListGridField[0]));
+ }
+
+ private ToolStrip createToolstrip() {
+ toolStrip = new ToolStrip();
+ toolStrip.setWidth(300);
+ toolStrip.setMembersMargin(15);
+ toolStrip.setPadding(5);
+ toolStrip.addSpacer(10);
+ addToDashboardButton = new IButton(MSG.chart_metrics_add_to_dashboard_button());
+ addToDashboardButton.setWidth(80);
+ dashboardSelectItem = new SelectItem();
+ dashboardSelectItem.setTitle(MSG.chart_metrics_add_to_dashboard_label());
+ dashboardSelectItem.setWidth(240);
+ dashboardSelectItem.setWrapTitle(false);
+ populateDashboardMenu();
+ toolStrip.addFormItem(dashboardSelectItem);
+ toolStrip.addMember(addToDashboardButton);
+
+ dashboardSelectItem.addChangeHandler(new ChangeHandler() {
+ @Override
+ public void onChange(ChangeEvent changeEvent) {
+ Integer selectedDashboardId = Integer.valueOf((String) changeEvent.getValue());
+ selectedDashboard = dashboardMap.get(selectedDashboardId);
+ }
+ });
+ addToDashboardButton.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
+ @Override
+ public void onClick(ClickEvent clickEvent) {
+ for (MeasurementDefinition measurementDefinition : resourceGroup.getResourceType()
+ .getMetricDefinitions()) {
+ if (measurementDefinition.getId() == selectedMetricDefinitionId) {
+ Log.debug("Add to Dashboard -- Storing: " + measurementDefinition.getDisplayName() + " in "
+ + selectedDashboard.getName());
+
+ storeDashboardMetric(selectedDashboard, resourceGroup.getId(), measurementDefinition);
+ break;
+ }
+ }
+ }
+ });
+ return toolStrip;
+ }
+
+ @Override
+ /**
+ * Redraw Graphs in this context means to refresh the table and redraw open graphs.
+ */
+ public void refreshData() {
+ new Timer() {
+ @Override
+ public void run() {
+ metricsTableListGrid.expandOpenedRows();
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+
+ }
+
+ @Override
+ public void refresh() {
+ super.refresh(false);
+ metricsTableListGrid.expandOpenedRows();
+ }
+
+ private void populateDashboardMenu() {
+ dashboardMenuMap.clear();
+ dashboardMap.clear();
+
+ DashboardCriteria criteria = new DashboardCriteria();
+ GWTServiceLookup.getDashboardService().findDashboardsByCriteria(criteria,
+ new AsyncCallback<PageList<Dashboard>>() {
+
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_loadFailed_dashboard(),
+ caught);
+ }
+
+ public void onSuccess(PageList<Dashboard> dashboards) {
+ if (dashboards.size() > 0) {
+ for (final Dashboard dashboard : dashboards) {
+ dashboardMenuMap.put(String.valueOf(dashboard.getId()), dashboard.getName());
+ dashboardMap.put(dashboard.getId(), dashboard);
+ }
+ selectedDashboard = dashboards.get(0);
+ dashboardSelectItem.setValueMap(dashboardMenuMap);
+ dashboardSelectItem.setValue(selectedDashboard.getId());
+ }
+ }
+ });
+ }
+
+ private void storeDashboardMetric(Dashboard dashboard, int resourceGroupId, MeasurementDefinition definition) {
+
+ DashboardPortlet dashboardPortlet = new DashboardPortlet(MSG.view_tree_common_contextMenu_groupGraph(),
+ ResourceGroupD3GraphPortlet.KEY, 260);
+ dashboardPortlet.getConfiguration().put(
+ new PropertySimple(ResourceGroupD3GraphPortlet.CFG_RESOURCE_GROUP_ID, resourceGroupId));
+ dashboardPortlet.getConfiguration().put(
+ new PropertySimple(ResourceGroupD3GraphPortlet.CFG_DEFINITION_ID, definition.getId()));
+
+ dashboard.addPortlet(dashboardPortlet);
+
+ GWTServiceLookup.getDashboardService().storeDashboard(dashboard, new AsyncCallback<Dashboard>() {
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_tree_common_contextMenu_saveChartToDashboardFailure(),
+ caught);
+ }
+
+ public void onSuccess(Dashboard result) {
+ String msg = MSG.view_tree_common_contextMenu_saveChartToDashboardSuccessful(result.getName());
+ CoreGUI.getMessageCenter().notify(new Message(msg, Message.Severity.Info));
+ }
+ });
+ }
+
+ public class MetricsTableListGrid extends ListGrid {
+
+ private static final int TREEVIEW_DETAIL_CHART_HEIGHT = 205;
+ private static final int NUM_METRIC_POINTS = 60;
+ final MetricsGroupTableView metricsTableView;
+ private ResourceGroup group;
+
+ public MetricsTableListGrid(final MetricsGroupTableView metricsTableView, final ResourceGroup group) {
+ super();
+ this.group = group;
+ this.metricsTableView = metricsTableView;
+ setCanExpandRecords(true);
+ setSelectionType(SelectionStyle.SINGLE);
+ setCanExpandMultipleRecords(true);
+ setExpansionMode(ExpansionMode.DETAIL_FIELD);
+
+ addRecordExpandHandler(new RecordExpandHandler() {
+ @Override
+ public void onRecordExpand(RecordExpandEvent recordExpandEvent) {
+ metricsTableView.expandedRows.add(recordExpandEvent.getRecord().getAttributeAsInt(
+ METRIC_DEF_ID.getValue()));
+ refreshData();
+ }
+
+ });
+ addRecordCollapseHandler(new RecordCollapseHandler() {
+ @Override
+ public void onRecordCollapse(RecordCollapseEvent recordCollapseEvent) {
+ metricsTableView.expandedRows.remove(recordCollapseEvent.getRecord().getAttributeAsInt(
+ METRIC_DEF_ID.getValue()));
+ refresh();
+ new Timer() {
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+ });
+ addSortChangedHandler(new SortChangedHandler() {
+ @Override
+ public void onSortChanged(SortEvent sortEvent) {
+ refreshData();
+ }
+ });
+
+ addDataArrivedHandler(new DataArrivedHandler() {
+ @Override
+ public void onDataArrived(DataArrivedEvent dataArrivedEvent) {
+ expandOpenedRows();
+ }
+ });
+
+ }
+
+ public void expandOpenedRows() {
+ int startRow = 0;
+ int endRow = this.getRecords().length;
+ for (int i = startRow; i < endRow; i++) {
+ ListGridRecord listGridRecord = getRecord(i);
+ if (null != listGridRecord) {
+ int metricDefinitionId = listGridRecord.getAttributeAsInt(METRIC_DEF_ID.getValue());
+ if (null != metricsTableView && null != expandedRows
+ && metricsTableView.expandedRows.contains(metricDefinitionId)) {
+ expandRecord(listGridRecord);
+ }
+ }
+ }
+ }
+
+ @Override
+ /**
+ * If you expand a grid row then create a graph.
+ */
+ protected Canvas getExpansionComponent(final ListGridRecord record) {
+ final Integer definitionId = record.getAttributeAsInt(METRIC_DEF_ID.getValue());
+ final Integer resourceGroupId = record.getAttributeAsInt(RESOURCE_GROUP_ID.getValue());
+ VLayout vLayout = new VLayout();
+ vLayout.setPadding(5);
+
+ final String chartId = "rChart-" + resourceGroupId + "-" + definitionId;
+ HTMLFlow htmlFlow = new HTMLFlow(MetricD3Graph.createGraphMarkerTemplate(chartId,
+ TREEVIEW_DETAIL_CHART_HEIGHT));
+ vLayout.addMember(htmlFlow);
+
+ int[] definitionArrayIds = new int[1];
+ definitionArrayIds[0] = definitionId;
+ GWTServiceLookup.getMeasurementDataService().findDataForCompatibleGroup(resourceGroupId,
+ definitionArrayIds, CustomDateRangeState.getInstance().getStartTime(),
+ CustomDateRangeState.getInstance().getEndTime(), NUM_METRIC_POINTS,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.warn("Error retrieving recent metrics charting data for resource group [" + resourceGroupId
+ + "]:" + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> results) {
+ if (!results.isEmpty()) {
+
+ //load the data results for the given metric definition
+ List<MeasurementDataNumericHighLowComposite> measurementList = results.get(0);
+
+ MeasurementDefinition measurementDefinition = null;
+ for (MeasurementDefinition definition : group.getResourceType().getMetricDefinitions()) {
+ if (definition.getId() == definitionId) {
+ measurementDefinition = definition;
+ break;
+ }
+ }
+
+ MetricGraphData metricGraphData = MetricGraphData.createForResourceGroup(group.getId(),
+ group.getName(), measurementDefinition, measurementList);
+ metricGraphData.setHideLegend(true);
+
+ StackedBarMetricGraphImpl graph = GWT.create(StackedBarMetricGraphImpl.class);
+ graph.setMetricGraphData(metricGraphData);
+ final MetricD3Graph graphView = new MetricD3Graph(graph, abstractD3GraphListView);
+ new Timer() {
+ @Override
+ public void run() {
+ graphView.drawJsniChart();
+ new Timer() {
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+ }.schedule(150);
+
+ } else {
+ Log.warn("No chart data retrieving for resource group [" + resourceGroupId + "-"
+ + definitionId + "]");
+ }
+ }
+ });
+
+ return vLayout;
+ }
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupView.java
new file mode 100644
index 0000000..1e07e7a
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupView.java
@@ -0,0 +1,193 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.rhq.coregui.client.inventory.groups.detail.monitoring.metric;
+
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.types.Overflow;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+
+import org.rhq.core.domain.common.EntityContext;
+import org.rhq.core.domain.measurement.Availability;
+import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.coregui.client.CoreGUI;
+import org.rhq.coregui.client.IconEnum;
+import org.rhq.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.coregui.client.inventory.common.AbstractD3GraphListView;
+import org.rhq.coregui.client.inventory.common.detail.AbstractTwoLevelTabSetView;
+import org.rhq.coregui.client.inventory.common.graph.CustomDateRangeState;
+import org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
+import org.rhq.coregui.client.inventory.resource.detail.monitoring.ExpandedRowsMomento;
+import org.rhq.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
+import org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricAvailabilityView;
+import org.rhq.coregui.client.util.BrowserUtility;
+import org.rhq.coregui.client.util.async.CountDownLatch;
+import org.rhq.coregui.client.util.enhanced.EnhancedHLayout;
+
+/**
+ * The consolidated metrics view showing metric graphs and availability data both in graphical and tabular form.
+ * @author Mike Thompson
+ */
+public class MetricsGroupView extends AbstractD3GraphListView implements
+ AbstractTwoLevelTabSetView.ViewRenderedListener {
+
+ private static final String COLLAPSED_TOOLTIP = MSG.chart_metrics_collapse_tooltip();
+ private static final String EXPANDED_TOOLTIP = MSG.chart_metrics_expand_tooltip();
+
+ private final ResourceGroup resourceGroup;
+ private EnhancedHLayout expandCollapseHLayout;
+ private MetricsGroupTableView metricsTableView;
+ private static Integer lastResourceGroupId = 0;
+
+ /**
+ * Encapsulate the creation logic and not let it leak out into other objects.
+ * Clear the expanded rows set when changing resources as well.
+ * @see ExpandedRowsMomento
+ * @param group
+ * @return MetricsGroupView
+ */
+ public static MetricsGroupView create(ResourceGroup group ){
+
+ boolean isDifferentResource = (group.getId() != lastResourceGroupId);
+
+ if(isDifferentResource){
+ ExpandedRowsMomento.getInstance().clear();
+ }
+
+ return new MetricsGroupView(group, ExpandedRowsMomento.getInstance().getExpandedRows());
+
+ }
+
+ private MetricsGroupView(ResourceGroup resourceGroup, Set<Integer> expandedRows) {
+ super();
+ setOverflow(Overflow.AUTO);
+ setWidth100();
+ setHeight100();
+ this.resourceGroup = resourceGroup;
+ metricsTableView = new MetricsGroupTableView(resourceGroup, this, expandedRows);
+
+ final MetricAvailabilityView availabilityDetails = new MetricAvailabilityView(resourceGroup.getId());
+ availabilityDetails.hide();
+
+ metricsTableView.setHeight100();
+
+ availabilityGraph = AvailabilityD3GraphView.create( new AvailabilityOverUnderGraphType(resourceGroup.getId()));
+
+ expandCollapseHLayout = new EnhancedHLayout();
+ //add expand/collapse icon
+ final Img expandCollapseArrow = new Img(IconEnum.COLLAPSED_ICON.getIcon16x16Path(), 16, 16);
+ expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP);
+ expandCollapseArrow.setLayoutAlign(VerticalAlignment.BOTTOM);
+ expandCollapseArrow.addClickHandler(new ClickHandler() {
+ private boolean collapsed = true;
+
+ @Override
+ public void onClick(ClickEvent event) {
+ collapsed = !collapsed;
+ if (collapsed) {
+ expandCollapseArrow.setSrc(IconEnum.COLLAPSED_ICON.getIcon16x16Path());
+ expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP);
+ availabilityDetails.hide();
+ } else {
+ expandCollapseArrow.setSrc(IconEnum.EXPANDED_ICON.getIcon16x16Path());
+ expandCollapseArrow.setTooltip(EXPANDED_TOOLTIP);
+ availabilityDetails.show();
+
+ }
+ drawAvailabilityGraphAndSparklines();
+ }
+ });
+ expandCollapseHLayout.addMember(expandCollapseArrow);
+ addAvailabilityGraph();
+
+ addMember(buttonBarDateTimeRangeEditor);
+ addMember(expandCollapseHLayout);
+ addMember(availabilityDetails);
+ addMember(metricsTableView);
+ lastResourceGroupId = resourceGroup.getId();
+ }
+
+
+ private void addAvailabilityGraph() {
+ expandCollapseHLayout.removeMember(availabilityGraph);
+ availabilityGraph.destroy();
+
+ availabilityGraph = AvailabilityD3GraphView.create(new AvailabilityOverUnderGraphType(resourceGroup.getId()));
+
+ expandCollapseHLayout.addMember(availabilityGraph);
+
+ queryAvailability(EntityContext.forGroup(resourceGroup.getId()), CustomDateRangeState.getInstance().getStartTime(),
+ CustomDateRangeState.getInstance().getEndTime(), null);
+ }
+
+
+ @Override
+ protected void queryAvailability(final EntityContext context, Long startTime, Long endTime, CountDownLatch notUsed) {
+
+ // now return the availability
+ GWTServiceLookup.getAvailabilityService().getAvailabilitiesForResource(context.getGroupId(), startTime,
+ endTime, new AsyncCallback<List<Availability>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_availability_loadFailed(), caught);
+ }
+
+ @Override
+ public void onSuccess(List<Availability> availList) {
+ availabilityGraph.setAvailabilityList(availList);
+ new Timer() {
+ @Override
+ public void run() {
+ buttonBarDateTimeRangeEditor.updateTimeRangeToNow();
+ availabilityGraph.drawJsniChart();
+
+ }
+ }.schedule(150);
+ }
+ });
+ }
+
+ private void drawAvailabilityGraphAndSparklines() {
+ new Timer() {
+ @Override
+ public void run() {
+ availabilityGraph.drawJsniChart();
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+
+ @Override
+ public void refreshData() {
+ addAvailabilityGraph();
+ metricsTableView.refresh();
+ }
+
+ @Override
+ public void onViewRendered() {
+ // refresh the graphs on subtab nav because we are a cached view not new
+ refreshData();
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupViewDataSource.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupViewDataSource.java
new file mode 100644
index 0000000..a5f1353
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/groups/detail/monitoring/metric/MetricsGroupViewDataSource.java
@@ -0,0 +1,314 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.coregui.client.inventory.groups.detail.monitoring.metric;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.data.DSRequest;
+import com.smartgwt.client.data.DSResponse;
+import com.smartgwt.client.data.Record;
+import com.smartgwt.client.widgets.grid.CellFormatter;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import org.rhq.core.domain.criteria.Criteria;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
+import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
+import org.rhq.core.domain.measurement.ui.MetricDisplayValue;
+import org.rhq.core.domain.resource.group.ResourceGroup;
+import org.rhq.coregui.client.CoreGUI;
+import org.rhq.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.coregui.client.inventory.common.graph.CustomDateRangeState;
+import org.rhq.coregui.client.util.BrowserUtility;
+import org.rhq.coregui.client.util.Log;
+import org.rhq.coregui.client.util.MeasurementUtility;
+import org.rhq.coregui.client.util.RPCDataSource;
+import org.rhq.coregui.client.util.async.Command;
+import org.rhq.coregui.client.util.async.CountDownLatch;
+
+import static org.rhq.core.domain.measurement.DataType.COMPLEX;
+import static org.rhq.core.domain.measurement.DataType.MEASUREMENT;
+import static org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricsGridFieldName.*;
+
+/**
+ * A simple data source to read in metric data summaries for a resource.
+ * This doesn't support paging - everything is returned in one query. Since
+ * the number of metrics per resource is relatively small (never more than tens of them),
+ * we just load them all in at once.
+ *
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class MetricsGroupViewDataSource extends RPCDataSource<MetricDisplaySummary, Criteria> {
+
+ private static final int NUMBER_OF_METRIC_POINTS = 60;
+
+ private final ResourceGroup resourceGroup;
+ private List<MetricDisplaySummary> metricDisplaySummaries;
+ private List<List<MeasurementDataNumericHighLowComposite>> metricsDataList;
+ private int[] definitionArrayIds;
+
+ public MetricsGroupViewDataSource(ResourceGroup resourceGroup) {
+ this.resourceGroup = resourceGroup;
+ }
+
+ /**
+ * The view that contains the list grid which will display this datasource's data will call this
+ * method to get the field information which is used to control the display of the data.
+ *
+ * @return list grid fields used to display the datasource data
+ */
+ public ArrayList<ListGridField> getListGridFields() {
+ ArrayList<ListGridField> fields = new ArrayList<ListGridField>(7);
+
+ ListGridField sparklineField = new ListGridField(SPARKLINE.getValue(), MSG.chart_metrics_sparkline_header());
+ sparklineField.setCellFormatter(new CellFormatter() {
+ @Override
+ public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
+ if (value == null) {
+ return "";
+ }
+ String contents = "<span id='sparkline_" + resourceGroup.getId() + "-"
+ + record.getAttributeAsInt(METRIC_DEF_ID.getValue()) + "' class='dynamicsparkline' width='70' "
+ + "values='" + record.getAttribute(SPARKLINE.getValue()) + "'></span>";
+ return contents;
+
+ }
+ });
+
+ sparklineField.setWidth(80);
+ fields.add(sparklineField);
+
+ ListGridField nameField = new ListGridField(METRIC_LABEL.getValue(), METRIC_LABEL.getLabel());
+ nameField.setWidth("30%");
+ fields.add(nameField);
+
+ ListGridField minField = new ListGridField(MIN_VALUE.getValue(), MIN_VALUE.getLabel());
+ minField.setWidth("15%");
+ fields.add(minField);
+
+ ListGridField maxField = new ListGridField(MAX_VALUE.getValue(), MAX_VALUE.getLabel());
+ maxField.setWidth("15%");
+ fields.add(maxField);
+
+ ListGridField avgField = new ListGridField(AVG_VALUE.getValue(), AVG_VALUE.getLabel());
+ avgField.setWidth("15%");
+ fields.add(avgField);
+
+ ListGridField alertsField = new ListGridField(ALERT_COUNT.getValue(), ALERT_COUNT.getLabel());
+ alertsField.setWidth("10%");
+ fields.add(alertsField);
+
+ return fields;
+ }
+
+ @Override
+ public MetricDisplaySummary copyValues(Record from) {
+ // we should never need this method - we only go in one direction
+ // if we ever need this, just have copyValues store an "object" attribute whose value is "from"
+ // which this method then just reads out. Since we don't need this now, save memory by not
+ // keeping the MetricDisplayValue around
+ return null;
+ }
+
+ @Override
+ public ListGridRecord copyValues(MetricDisplaySummary from) {
+ MeasurementUtility.formatSimpleMetrics(from);
+
+ ListGridRecord record = new ListGridRecord();
+ record.setAttribute(SPARKLINE.getValue(), getCsvMetricsForSparkline(from.getDefinitionId()));
+ record.setAttribute(METRIC_LABEL.getValue(), from.getLabel());
+ record.setAttribute(ALERT_COUNT.getValue(), String.valueOf(from.getAlertCount()));
+ record.setAttribute(MIN_VALUE.getValue(), getMetricStringValue(from.getMinMetric()));
+ record.setAttribute(MAX_VALUE.getValue(), getMetricStringValue(from.getMaxMetric()));
+ record.setAttribute(AVG_VALUE.getValue(), getMetricStringValue(from.getAvgMetric()));
+ record.setAttribute(METRIC_DEF_ID.getValue(), from.getDefinitionId());
+ record.setAttribute(METRIC_SCHEDULE_ID.getValue(), from.getScheduleId());
+ record.setAttribute(METRIC_UNITS.getValue(), from.getUnits());
+ record.setAttribute(METRIC_NAME.getValue(), from.getMetricName());
+ record.setAttribute(RESOURCE_GROUP_ID.getValue(), resourceGroup.getId());
+ return record;
+ }
+
+ private String getCsvMetricsForSparkline(int definitionId) {
+ StringBuilder sb = new StringBuilder();
+ List<MeasurementDataNumericHighLowComposite> selectedMetricsList = getMeasurementsForMeasurementDefId(definitionId);
+
+ for (MeasurementDataNumericHighLowComposite measurementData : selectedMetricsList) {
+ if (!Double.isNaN(measurementData.getValue())) {
+ sb.append((int) measurementData.getValue());
+ sb.append(",");
+ }
+ }
+
+ if (sb.toString().endsWith(",")) {
+ sb.setLength(sb.length() - 1);
+ }
+ // handle the case where we have just installed the server so not much history
+ // and our date range is set such that only one value returns which the
+ // sparkline graph will not plot anything, so we need at least 2 values
+ if (!sb.toString().contains(",")) {
+ // append another value just so we have 2 values and it will graph
+ return "0," + sb.toString();
+ }
+
+ return sb.toString();
+ }
+
+ private List<MeasurementDataNumericHighLowComposite> getMeasurementsForMeasurementDefId(int definitionId) {
+ int selectedIndex = 0;
+
+ // find the ordinal position as specified when querying the metrics
+ for (int i = 0; i < definitionArrayIds.length; i++) {
+ if (definitionArrayIds[i] == definitionId) {
+ selectedIndex = i;
+ break;
+ }
+ }
+
+ return metricsDataList.get(selectedIndex);
+ }
+
+ protected String getMetricStringValue(MetricDisplayValue value) {
+ return (value != null) ? value.toString() : "";
+ }
+
+ @Override
+ protected Criteria getFetchCriteria(DSRequest request) {
+ // NOTE: we don't use criterias for this datasource, just return null
+ return null;
+ }
+
+ @Override
+ protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
+
+ // This latch is the last thing that gets executed after we have executed the
+ // 1 query
+ final CountDownLatch countDownLatch = CountDownLatch.create(1, new Command() {
+
+ @Override
+ public void execute() {
+
+ // NOTE: this runs after the queryMetricDisplaySummaries is complete
+ queryGroupMetrics(resourceGroup, request, response);
+ }
+ });
+
+ organizeMeasurementDefinitionOrder(resourceGroup);
+ queryMetricDisplaySummaries(definitionArrayIds, CustomDateRangeState.getInstance().getStartTime(),
+ CustomDateRangeState.getInstance().getEndTime(), countDownLatch);
+
+ }
+
+ private void queryGroupMetrics(final ResourceGroup resourceGroup, final DSRequest request, final DSResponse response) {
+
+ GWTServiceLookup.getMeasurementDataService().findDataForCompatibleGroup(resourceGroup.getId(),
+ definitionArrayIds, CustomDateRangeState.getInstance().getStartTime(),
+ CustomDateRangeState.getInstance().getEndTime(), NUMBER_OF_METRIC_POINTS,
+ new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
+ @Override
+ public void onFailure(Throwable caught) {
+ Log.warn("Error retrieving recent metrics charting data for resource [" + resourceGroup.getId()
+ + "]:" + caught.getMessage());
+ }
+
+ @Override
+ public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> measurementDataList) {
+ if (null != measurementDataList && !measurementDataList.isEmpty()) {
+ metricsDataList = measurementDataList;
+ response.setData(buildRecords(metricDisplaySummaries));
+ processResponse(request.getRequestId(), response);
+ new Timer() {
+ @Override
+ public void run() {
+ BrowserUtility.graphSparkLines();
+ }
+ }.schedule(150);
+ }
+ }
+ });
+
+ }
+
+ private void organizeMeasurementDefinitionOrder(ResourceGroup resourceGroup) {
+ Set<MeasurementDefinition> definitions = getMetricDefinitions(resourceGroup);
+
+ //build id mapping for measurementDefinition instances Ex. Free Memory -> MeasurementDefinition[100071]
+ final HashMap<String, MeasurementDefinition> measurementDefMap = new HashMap<String, MeasurementDefinition>();
+ for (MeasurementDefinition definition : definitions) {
+ measurementDefMap.put(definition.getDisplayName(), definition);
+ }
+ //bundle definition ids for asynch call.
+ definitionArrayIds = new int[definitions.size()];
+ final String[] displayOrder = new String[definitions.size()];
+ measurementDefMap.keySet().toArray(displayOrder);
+ //sort the charting data ex. Free Memory, Free Swap Space,..System Load
+ Arrays.sort(displayOrder);
+
+ //organize definitionArrayIds for ordered request on server.
+ int index = 0;
+ for (String definitionToDisplay : displayOrder) {
+ definitionArrayIds[index++] = measurementDefMap.get(definitionToDisplay).getId();
+ }
+ }
+
+ private void queryMetricDisplaySummaries(int[] measurementDefIds, Long startTime, Long endTime,
+ final CountDownLatch countDownLatch) {
+ GWTServiceLookup.getMeasurementChartsService().getMetricDisplaySummariesForCompatibleGroup(
+ resourceGroup.getId(), measurementDefIds, startTime, endTime, false,
+ new AsyncCallback<ArrayList<MetricDisplaySummary>>() {
+ @Override
+ public void onSuccess(ArrayList<MetricDisplaySummary> metricDisplaySummaries) {
+ setMetricDisplaySummaries(metricDisplaySummaries);
+ countDownLatch.countDown();
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ CoreGUI.getErrorHandler().handleError("Cannot load metrics", caught);
+ countDownLatch.countDown();
+ }
+ }
+
+ );
+ }
+
+ private void setMetricDisplaySummaries(List<MetricDisplaySummary> metricDisplaySummaries) {
+ this.metricDisplaySummaries = metricDisplaySummaries;
+ }
+
+ private Set<MeasurementDefinition> getMetricDefinitions(ResourceGroup resourceGroup) {
+ Set<MeasurementDefinition> definitions = new HashSet<MeasurementDefinition>();
+ for (MeasurementDefinition measurementDefinition : resourceGroup.getResourceType().getMetricDefinitions()) {
+ if (measurementDefinition.getDataType() == MEASUREMENT || measurementDefinition.getDataType() == COMPLEX) {
+ definitions.add(measurementDefinition);
+ }
+ }
+ return definitions;
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricAvailabilityView.java
new file mode 100644
index 0000000..4b542c9
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricAvailabilityView.java
@@ -0,0 +1,223 @@
+/*
+ * RHQ Management Platform
+ * Copyright 2012, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.coregui.client.inventory.resource.detail.monitoring.table;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+
+import org.rhq.core.domain.measurement.AvailabilityType;
+import org.rhq.core.domain.measurement.MeasurementUnits;
+import org.rhq.core.domain.resource.composite.ResourceAvailabilitySummary;
+import org.rhq.coregui.client.CoreGUI;
+import org.rhq.coregui.client.components.table.TimestampCellFormatter;
+import org.rhq.coregui.client.gwt.GWTServiceLookup;
+import org.rhq.coregui.client.util.MeasurementConverterClient;
+import org.rhq.coregui.client.util.enhanced.EnhancedVLayout;
+
+/**
+ * This shows the availability history for a resource.
+ *
+ * @author Jay Shaughnessy
+ * @author John Mazzitelli
+ * @author Mike Thompson
+ */
+public class MetricAvailabilityView extends EnhancedVLayout {
+
+ private int resourceId;
+ private StaticTextItem currentField;
+ private StaticTextItem availField;
+ private StaticTextItem availTimeField;
+ private StaticTextItem downField;
+ private StaticTextItem downTimeField;
+ private StaticTextItem disabledField;
+ private StaticTextItem disabledTimeField;
+ private StaticTextItem failureCountField;
+ private StaticTextItem disabledCountField;
+ private StaticTextItem mtbfField;
+ private StaticTextItem mttrField;
+ private StaticTextItem unknownField;
+ private StaticTextItem currentTimeField;
+
+ public MetricAvailabilityView(int resourceId) {
+ super();
+
+ this.resourceId = resourceId;
+
+ setWidth100();
+ setHeight(165);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+
+ addMember(createSummaryForm());
+ }
+
+ private DynamicForm createSummaryForm() {
+ DynamicForm form = new DynamicForm();
+ form.setWidth100();
+ form.setAutoHeight();
+ form.setMargin(10);
+ form.setNumCols(4);
+
+ // row 1
+ currentField = new StaticTextItem("current", MSG.view_resource_monitor_availability_currentStatus());
+ currentField.setWrapTitle(false);
+ currentField.setColSpan(4);
+
+ // row 2
+ availField = new StaticTextItem("avail", MSG.common_title_availability());
+ availField.setWrapTitle(false);
+ prepareTooltip(availField, MSG.view_resource_monitor_availability_tooltip_up());
+
+ availTimeField = new StaticTextItem("availTime", MSG.view_resource_monitor_availability_uptime());
+ availTimeField.setWrapTitle(false);
+ prepareTooltip(availTimeField, MSG.view_resource_monitor_availability_uptime_tooltip());
+
+ // row 3
+ downField = new StaticTextItem("down", MSG.common_status_avail_down_lower());
+ downField.setWrapTitle(false);
+ prepareTooltip(downField, MSG.view_resource_monitor_availability_tooltip_down());
+
+ downTimeField = new StaticTextItem("downTime", MSG.view_resource_monitor_availability_downtime());
+ downTimeField.setWrapTitle(false);
+ prepareTooltip(downTimeField, MSG.view_resource_monitor_availability_downtime_tooltip());
+
+ // row 4
+ disabledField = new StaticTextItem("disabled", MSG.common_status_avail_disabled_lower());
+ disabledField.setWrapTitle(false);
+ prepareTooltip(disabledField, MSG.view_resource_monitor_availability_tooltip_disabled());
+
+ disabledTimeField = new StaticTextItem("disabledTime", MSG.view_resource_monitor_availability_disabledTime());
+ disabledTimeField.setWrapTitle(false);
+ prepareTooltip(disabledTimeField, MSG.view_resource_monitor_availability_disabledTime_tooltip());
+
+ // row 5
+ failureCountField = new StaticTextItem("failureCount", MSG.view_resource_monitor_availability_numFailures());
+ failureCountField.setWrapTitle(false);
+ prepareTooltip(failureCountField, MSG.view_resource_monitor_availability_numFailures_tooltip());
+
+ disabledCountField = new StaticTextItem("disabledCount", MSG.view_resource_monitor_availability_numDisabled());
+ disabledCountField.setWrapTitle(false);
+ prepareTooltip(disabledCountField, MSG.view_resource_monitor_availability_numDisabled_tooltip());
+
+ // row 6
+ mtbfField = new StaticTextItem("mtbf", MSG.view_resource_monitor_availability_mtbf());
+ mtbfField.setWrapTitle(false);
+ prepareTooltip(mtbfField, MSG.view_resource_monitor_availability_mtbf_tooltip());
+
+ mttrField = new StaticTextItem("mttr", MSG.view_resource_monitor_availability_mttr());
+ mttrField.setWrapTitle(false);
+ prepareTooltip(mttrField, MSG.view_resource_monitor_availability_mttr_tooltip());
+
+ // row 7
+ unknownField = new StaticTextItem("unknown");
+ unknownField.setWrapTitle(false);
+ unknownField.setColSpan(4);
+ unknownField.setShowTitle(false);
+
+ // row 8
+ currentTimeField = new StaticTextItem("currentTime");
+ currentTimeField.setWrapTitle(false);
+ currentTimeField.setColSpan(4);
+ currentTimeField.setShowTitle(false);
+
+ form.setItems(currentField, availField, availTimeField, downField, downTimeField, disabledField,
+ disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
+ currentTimeField);
+
+ reloadSummaryData();
+
+ return form;
+ }
+
+ private void reloadSummaryData() {
+ GWTServiceLookup.getResourceService().getResourceAvailabilitySummary(resourceId,
+ new AsyncCallback<ResourceAvailabilitySummary>() {
+
+ @Override
+ public void onSuccess(ResourceAvailabilitySummary result) {
+
+ currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(
+ getAvailabilityTypeMessage(result.getCurrent()),
+ TimestampCellFormatter.format(result.getLastChange().getTime())));
+ availField.setValue(MeasurementConverterClient.format(result.getUpPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ availTimeField.setValue(MeasurementConverterClient.format((double) result.getUpTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ downField.setValue(MeasurementConverterClient.format(result.getDownPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ downTimeField.setValue(MeasurementConverterClient.format((double) result.getDownTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ disabledField.setValue(MeasurementConverterClient.format(result.getDisabledPercentage(),
+ MeasurementUnits.PERCENTAGE, true));
+ disabledTimeField.setValue(MeasurementConverterClient.format((double) result.getDisabledTime(),
+ MeasurementUnits.MILLISECONDS, true));
+ failureCountField.setValue(result.getFailures());
+ disabledCountField.setValue(result.getDisabled());
+ mtbfField.setValue(MeasurementConverterClient.format((double) result.getMTBF(),
+ MeasurementUnits.MILLISECONDS, true));
+ mttrField.setValue(MeasurementConverterClient.format((double) result.getMTTR(),
+ MeasurementUnits.MILLISECONDS, true));
+
+ if (result.getUnknownTime() > 0L) {
+ unknownField.setValue(MSG.view_resource_monitor_availability_unknown(MeasurementConverterClient
+ .format((double) result.getUnknownTime(), MeasurementUnits.MILLISECONDS, true)));
+ } else {
+ unknownField.setValue("");
+ }
+
+ currentTimeField.setValue(MSG.view_resource_monitor_availability_currentAsOf(TimestampCellFormatter
+ .format(result.getCurrentTime())));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ currentField.setValue(MSG.common_label_error());
+ CoreGUI.getErrorHandler()
+ .handleError(MSG.view_resource_monitor_availability_summaryError(), caught);
+ }
+ });
+ }
+
+ private String getAvailabilityTypeMessage(AvailabilityType availabilityType) {
+ switch (availabilityType) {
+ case UP:
+ return MSG.common_status_avail_up();
+ case DOWN:
+ return MSG.common_status_avail_down();
+ case DISABLED:
+ return MSG.common_status_avail_disabled();
+ case UNKNOWN:
+ default:
+ return MSG.common_status_avail_unknown();
+ }
+ }
+
+ private void prepareTooltip(FormItem item, String tooltip) {
+ item.setHoverWidth(400);
+ item.setPrompt(tooltip);
+ }
+
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsGridFieldName.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsGridFieldName.java
new file mode 100644
index 0000000..f912dee
--- /dev/null
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsGridFieldName.java
@@ -0,0 +1,63 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2014 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.coregui.client.inventory.resource.detail.monitoring.table;
+
+import static org.rhq.core.domain.measurement.ui.MetricDisplayConstants.AVERAGE_KEY;
+import static org.rhq.core.domain.measurement.ui.MetricDisplayConstants.MAX_KEY;
+import static org.rhq.core.domain.measurement.ui.MetricDisplayConstants.MIN_KEY;
+
+import org.rhq.coregui.client.CoreGUI;
+
+/**
+ * Typesafe field names used in consolidated metrics screen grids for Resource and ResourceGroup.
+ * Also associates the proper label with the value.
+ *
+ * @author Mike Thompson
+ */
+@SuppressWarnings("GwtInconsistentSerializableClass")
+public enum MetricsGridFieldName {
+
+ SPARKLINE("sparkline"), METRIC_LABEL("label", CoreGUI.getMessages().common_title_name()), ALERT_COUNT("alertCount",
+ CoreGUI.getMessages().common_title_alerts()), MAX_VALUE(MAX_KEY, CoreGUI.getMessages()
+ .common_title_monitor_maximum()), MIN_VALUE(MIN_KEY, CoreGUI.getMessages().common_title_monitor_minimum()), AVG_VALUE(
+ AVERAGE_KEY, CoreGUI.getMessages().common_title_monitor_average()), METRIC_DEF_ID("defId"), METRIC_SCHEDULE_ID(
+ "schedId"), METRIC_UNITS("units"), METRIC_NAME("name"), RESOURCE_GROUP_ID("resourceGroupId"),
+ RESOURCE_ID("resourceId"), LIVE_VALUE("live", CoreGUI.getMessages().view_resource_monitor_table_live());
+
+ private final String value;
+ private final String label;
+
+ MetricsGridFieldName(String value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ MetricsGridFieldName(String value) {
+ this.value = value;
+ this.label = "";
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java
index efba122..7e84c3d 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsResourceView.java
@@ -41,7 +41,6 @@ import org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverU
import org.rhq.coregui.client.inventory.resource.detail.monitoring.ExpandedRowsMomento;
import org.rhq.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
import org.rhq.coregui.client.util.BrowserUtility;
-import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.async.CountDownLatch;
import org.rhq.coregui.client.util.enhanced.EnhancedHLayout;
@@ -87,7 +86,7 @@ public class MetricsResourceView extends AbstractD3GraphListView implements
this.resource = resource;
metricsTableView = new MetricsTableView(resource, this, expandedRows);
- final ResourceMetricAvailabilityView availabilityDetails = new ResourceMetricAvailabilityView(resource);
+ final MetricAvailabilityView availabilityDetails = new MetricAvailabilityView(resource.getId());
availabilityDetails.hide();
metricsTableView.setHeight100();
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
index 63a6619..6f0f7f7 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsTableView.java
@@ -74,6 +74,8 @@ import org.rhq.coregui.client.util.BrowserUtility;
import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.message.Message;
+import static org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricsGridFieldName.*;
+
/**
* Views a resource's metrics in a tabular view with sparkline graph and optional detailed d3 graph.
*
@@ -125,10 +127,8 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
addToDashboardButton.enable();
ListGridRecord selectedRecord = selectionEvent.getSelectedRecord();
if (null != selectedRecord) {
- //Log.debug("Selected Metric Label: "
- // + selectedRecord.getAttribute(MetricsViewDataSource.FIELD_METRIC_LABEL));
- selectedMetricDefinitionId = selectedRecord
- .getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
+ selectedMetricDefinitionId = selectedRecord.getAttributeAsInt(METRIC_DEF_ID
+ .getValue());
}
}
});
@@ -176,7 +176,7 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
if (measurementDefinition.getId() == selectedMetricDefinitionId) {
Log.debug("Add to Dashboard -- Storing: " + measurementDefinition.getDisplayName() + " in "
+ selectedDashboard.getName());
- storeDashboardMetric(selectedDashboard, resource, measurementDefinition);
+ storeDashboardMetric(selectedDashboard, resource.getId(), measurementDefinition);
break;
}
}
@@ -233,11 +233,11 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
});
}
- private void storeDashboardMetric(Dashboard dashboard, Resource resource, MeasurementDefinition definition) {
+ private void storeDashboardMetric(Dashboard dashboard, int resourceId, MeasurementDefinition definition) {
DashboardPortlet dashboardPortlet = new DashboardPortlet(MSG.view_tree_common_contextMenu_resourceGraph(),
- ResourceD3GraphPortlet.KEY, 200);
+ ResourceD3GraphPortlet.KEY, 260);
dashboardPortlet.getConfiguration().put(
- new PropertySimple(ResourceD3GraphPortlet.CFG_RESOURCE_ID, resource.getId()));
+ new PropertySimple(ResourceD3GraphPortlet.CFG_RESOURCE_ID, resourceId));
dashboardPortlet.getConfiguration().put(
new PropertySimple(ResourceD3GraphPortlet.CFG_DEFINITION_ID, definition.getId()));
@@ -278,7 +278,7 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
@Override
public void onRecordExpand(RecordExpandEvent recordExpandEvent) {
metricsTableView.expandedRows.add(recordExpandEvent.getRecord().getAttributeAsInt(
- MetricsViewDataSource.FIELD_METRIC_DEF_ID));
+ METRIC_DEF_ID.getValue()));
refreshData();
}
@@ -287,7 +287,7 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
@Override
public void onRecordCollapse(RecordCollapseEvent recordCollapseEvent) {
metricsTableView.expandedRows.remove(recordCollapseEvent.getRecord().getAttributeAsInt(
- MetricsViewDataSource.FIELD_METRIC_DEF_ID));
+ METRIC_DEF_ID.getValue()));
refresh();
new Timer() {
@Override
@@ -321,7 +321,7 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
ListGridRecord listGridRecord = getRecord(i);
if (null != listGridRecord) {
int metricDefinitionId = listGridRecord
- .getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
+ .getAttributeAsInt(METRIC_DEF_ID.getValue());
if (null != metricsTableView && null != expandedRows
&& metricsTableView.expandedRows.contains(metricDefinitionId)) {
expandRecord(listGridRecord);
@@ -330,18 +330,14 @@ public class MetricsTableView extends Table<MetricsViewDataSource> implements Re
}
}
- public void expandOpenedRows(Set<Integer> selectedRows) {
- expandedRows = selectedRows;
- expandOpenedRows();
- }
@Override
/**
* If you expand a grid row then create a graph.
*/
protected Canvas getExpansionComponent(final ListGridRecord record) {
- final Integer definitionId = record.getAttributeAsInt(MetricsViewDataSource.FIELD_METRIC_DEF_ID);
- final Integer resourceId = record.getAttributeAsInt(MetricsViewDataSource.FIELD_RESOURCE_ID);
+ final Integer definitionId = record.getAttributeAsInt(METRIC_DEF_ID.getValue());
+ final Integer resourceId = record.getAttributeAsInt(RESOURCE_ID.getValue());
VLayout vLayout = new VLayout();
vLayout.setPadding(5);
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java
index 4c9c7c1..248eee0 100644
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java
+++ b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/MetricsViewDataSource.java
@@ -61,6 +61,7 @@ import org.rhq.coregui.client.util.RPCDataSource;
import org.rhq.coregui.client.util.async.Command;
import org.rhq.coregui.client.util.async.CountDownLatch;
import org.rhq.coregui.client.util.preferences.MeasurementUserPreferences;
+import static org.rhq.coregui.client.inventory.resource.detail.monitoring.table.MetricsGridFieldName.*;
/**
* A simple data source to read in metric data summaries for a resource.
@@ -75,19 +76,6 @@ public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, C
private static final int NUMBER_OF_METRIC_POINTS = 60;
- public static final String FIELD_SPARKLINE = "sparkline";
- public static final String FIELD_METRIC_LABEL = "label";
- public static final String FIELD_ALERT_COUNT = "alertCount";
- public static final String FIELD_MIN_VALUE = "min";
- public static final String FIELD_MAX_VALUE = "max";
- public static final String FIELD_AVG_VALUE = "avg";
- public static final String FIELD_LIVE_VALUE = "live";
- public static final String FIELD_METRIC_DEF_ID = "defId";
- public static final String FIELD_METRIC_SCHED_ID = "schedId";
- public static final String FIELD_METRIC_UNITS = "units";
- public static final String FIELD_METRIC_NAME = "name";
- public static final String FIELD_RESOURCE_ID = "resourceId";
-
private final Resource resource;
private List<MetricDisplaySummary> metricDisplaySummaries;
private List<List<MeasurementDataNumericHighLowComposite>> metricsDataList;
@@ -111,7 +99,7 @@ public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, C
public ArrayList<ListGridField> getListGridFields() {
ArrayList<ListGridField> fields = new ArrayList<ListGridField>(7);
- ListGridField sparklineField = new ListGridField(FIELD_SPARKLINE, MSG.chart_metrics_sparkline_header());
+ ListGridField sparklineField = new ListGridField(SPARKLINE.getValue(), MSG.chart_metrics_sparkline_header());
sparklineField.setCellFormatter(new CellFormatter() {
@Override
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
@@ -119,8 +107,8 @@ public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, C
return "";
}
String contents = "<span id='sparkline_" + resource.getId() + "-"
- + record.getAttributeAsInt(FIELD_METRIC_DEF_ID) + "' class='dynamicsparkline' width='70' "
- + "values='" + record.getAttribute(FIELD_SPARKLINE) + "'></span>";
+ + record.getAttributeAsInt(METRIC_DEF_ID.getValue()) + "' class='dynamicsparkline' width='70' "
+ + "values='" + record.getAttribute(SPARKLINE.getValue()) + "'></span>";
return contents;
}
@@ -129,27 +117,27 @@ public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, C
sparklineField.setWidth(80);
fields.add(sparklineField);
- ListGridField nameField = new ListGridField(FIELD_METRIC_LABEL, MSG.common_title_name());
+ ListGridField nameField = new ListGridField(METRIC_LABEL.getValue(), METRIC_LABEL.getLabel());
nameField.setWidth("30%");
fields.add(nameField);
- ListGridField minField = new ListGridField(FIELD_MIN_VALUE, MSG.common_title_monitor_minimum());
+ ListGridField minField = new ListGridField(MIN_VALUE.getValue(), MIN_VALUE.getLabel());
minField.setWidth("15%");
fields.add(minField);
- ListGridField maxField = new ListGridField(FIELD_MAX_VALUE, MSG.common_title_monitor_maximum());
+ ListGridField maxField = new ListGridField(MAX_VALUE.getValue(), MAX_VALUE.getLabel());
maxField.setWidth("15%");
fields.add(maxField);
- ListGridField avgField = new ListGridField(FIELD_AVG_VALUE, MSG.common_title_monitor_average());
+ ListGridField avgField = new ListGridField(AVG_VALUE.getValue(), AVG_VALUE.getLabel());
avgField.setWidth("15%");
fields.add(avgField);
- ListGridField liveField = new ListGridField(FIELD_LIVE_VALUE, MSG.view_resource_monitor_table_live());
+ ListGridField liveField = new ListGridField(LIVE_VALUE.getValue(), LIVE_VALUE.getLabel());
liveField.setWidth("15%");
fields.add(liveField);
- ListGridField alertsField = new ListGridField(FIELD_ALERT_COUNT, MSG.common_title_alerts());
+ ListGridField alertsField = new ListGridField(ALERT_COUNT.getValue(), ALERT_COUNT.getLabel());
alertsField.setWidth("10%");
fields.add(alertsField);
@@ -170,18 +158,18 @@ public class MetricsViewDataSource extends RPCDataSource<MetricDisplaySummary, C
MeasurementUtility.formatSimpleMetrics(from);
ListGridRecord record = new ListGridRecord();
- record.setAttribute(FIELD_SPARKLINE, getCsvMetricsForSparkline(from.getDefinitionId()));
- record.setAttribute(FIELD_METRIC_LABEL, from.getLabel());
- record.setAttribute(FIELD_ALERT_COUNT, String.valueOf(from.getAlertCount()));
- record.setAttribute(FIELD_MIN_VALUE, getMetricStringValue(from.getMinMetric()));
- record.setAttribute(FIELD_MAX_VALUE, getMetricStringValue(from.getMaxMetric()));
- record.setAttribute(FIELD_AVG_VALUE, getMetricStringValue(from.getAvgMetric()));
- record.setAttribute(FIELD_LIVE_VALUE, buildLiveValue(from));
- record.setAttribute(FIELD_METRIC_DEF_ID, from.getDefinitionId());
- record.setAttribute(FIELD_METRIC_SCHED_ID, from.getScheduleId());
- record.setAttribute(FIELD_METRIC_UNITS, from.getUnits());
- record.setAttribute(FIELD_METRIC_NAME, from.getMetricName());
- record.setAttribute(FIELD_RESOURCE_ID, resource.getId());
+ record.setAttribute(SPARKLINE.getValue(), getCsvMetricsForSparkline(from.getDefinitionId()));
+ record.setAttribute(METRIC_LABEL.getValue(), from.getLabel());
+ record.setAttribute(ALERT_COUNT.getValue(), String.valueOf(from.getAlertCount()));
+ record.setAttribute(MIN_VALUE.getValue(), getMetricStringValue(from.getMinMetric()));
+ record.setAttribute(MAX_VALUE.getValue(), getMetricStringValue(from.getMaxMetric()));
+ record.setAttribute(AVG_VALUE.getValue(), getMetricStringValue(from.getAvgMetric()));
+ record.setAttribute(LIVE_VALUE.getValue(), buildLiveValue(from));
+ record.setAttribute(METRIC_DEF_ID.getValue(), from.getDefinitionId());
+ record.setAttribute(METRIC_SCHEDULE_ID.getValue(), from.getScheduleId());
+ record.setAttribute(METRIC_UNITS.getValue(), from.getUnits());
+ record.setAttribute(METRIC_NAME.getValue(), from.getMetricName());
+ record.setAttribute(RESOURCE_ID.getValue(), resource.getId());
return record;
}
diff --git a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java b/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
deleted file mode 100644
index 5919f83..0000000
--- a/modules/enterprise/gui/coregui/src/main/java/org/rhq/coregui/client/inventory/resource/detail/monitoring/table/ResourceMetricAvailabilityView.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * RHQ Management Platform
- * Copyright 2012, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-package org.rhq.coregui.client.inventory.resource.detail.monitoring.table;
-
-import com.google.gwt.user.client.rpc.AsyncCallback;
-import com.smartgwt.client.widgets.form.DynamicForm;
-import com.smartgwt.client.widgets.form.fields.FormItem;
-import com.smartgwt.client.widgets.form.fields.StaticTextItem;
-
-import org.rhq.core.domain.measurement.AvailabilityType;
-import org.rhq.core.domain.measurement.MeasurementUnits;
-import org.rhq.core.domain.resource.Resource;
-import org.rhq.core.domain.resource.composite.ResourceAvailabilitySummary;
-import org.rhq.coregui.client.CoreGUI;
-import org.rhq.coregui.client.components.table.TimestampCellFormatter;
-import org.rhq.coregui.client.gwt.GWTServiceLookup;
-import org.rhq.coregui.client.util.MeasurementConverterClient;
-import org.rhq.coregui.client.util.enhanced.EnhancedVLayout;
-
-/**
- * This shows the availability history for a resource.
- *
- * @author Jay Shaughnessy
- * @author John Mazzitelli
- * @author Mike Thompson
- */
-public class ResourceMetricAvailabilityView extends EnhancedVLayout {
-
- private Resource resource;
- private StaticTextItem currentField;
- private StaticTextItem availField;
- private StaticTextItem availTimeField;
- private StaticTextItem downField;
- private StaticTextItem downTimeField;
- private StaticTextItem disabledField;
- private StaticTextItem disabledTimeField;
- private StaticTextItem failureCountField;
- private StaticTextItem disabledCountField;
- private StaticTextItem mtbfField;
- private StaticTextItem mttrField;
- private StaticTextItem unknownField;
- private StaticTextItem currentTimeField;
-
- public ResourceMetricAvailabilityView(Resource resource) {
- super();
-
- this.resource = resource;
-
- setWidth100();
- setHeight(165);
- }
-
- @Override
- protected void onInit() {
- super.onInit();
-
- addMember(createSummaryForm());
- }
-
- private DynamicForm createSummaryForm() {
- DynamicForm form = new DynamicForm();
- form.setWidth100();
- form.setAutoHeight();
- form.setMargin(10);
- form.setNumCols(4);
-
- // row 1
- currentField = new StaticTextItem("current", MSG.view_resource_monitor_availability_currentStatus());
- currentField.setWrapTitle(false);
- currentField.setColSpan(4);
-
- // row 2
- availField = new StaticTextItem("avail", MSG.common_title_availability());
- availField.setWrapTitle(false);
- prepareTooltip(availField, MSG.view_resource_monitor_availability_tooltip_up());
-
- availTimeField = new StaticTextItem("availTime", MSG.view_resource_monitor_availability_uptime());
- availTimeField.setWrapTitle(false);
- prepareTooltip(availTimeField, MSG.view_resource_monitor_availability_uptime_tooltip());
-
- // row 3
- downField = new StaticTextItem("down", MSG.common_status_avail_down_lower());
- downField.setWrapTitle(false);
- prepareTooltip(downField, MSG.view_resource_monitor_availability_tooltip_down());
-
- downTimeField = new StaticTextItem("downTime", MSG.view_resource_monitor_availability_downtime());
- downTimeField.setWrapTitle(false);
- prepareTooltip(downTimeField, MSG.view_resource_monitor_availability_downtime_tooltip());
-
- // row 4
- disabledField = new StaticTextItem("disabled", MSG.common_status_avail_disabled_lower());
- disabledField.setWrapTitle(false);
- prepareTooltip(disabledField, MSG.view_resource_monitor_availability_tooltip_disabled());
-
- disabledTimeField = new StaticTextItem("disabledTime", MSG.view_resource_monitor_availability_disabledTime());
- disabledTimeField.setWrapTitle(false);
- prepareTooltip(disabledTimeField, MSG.view_resource_monitor_availability_disabledTime_tooltip());
-
- // row 5
- failureCountField = new StaticTextItem("failureCount", MSG.view_resource_monitor_availability_numFailures());
- failureCountField.setWrapTitle(false);
- prepareTooltip(failureCountField, MSG.view_resource_monitor_availability_numFailures_tooltip());
-
- disabledCountField = new StaticTextItem("disabledCount", MSG.view_resource_monitor_availability_numDisabled());
- disabledCountField.setWrapTitle(false);
- prepareTooltip(disabledCountField, MSG.view_resource_monitor_availability_numDisabled_tooltip());
-
- // row 6
- mtbfField = new StaticTextItem("mtbf", MSG.view_resource_monitor_availability_mtbf());
- mtbfField.setWrapTitle(false);
- prepareTooltip(mtbfField, MSG.view_resource_monitor_availability_mtbf_tooltip());
-
- mttrField = new StaticTextItem("mttr", MSG.view_resource_monitor_availability_mttr());
- mttrField.setWrapTitle(false);
- prepareTooltip(mttrField, MSG.view_resource_monitor_availability_mttr_tooltip());
-
- // row 7
- unknownField = new StaticTextItem("unknown");
- unknownField.setWrapTitle(false);
- unknownField.setColSpan(4);
- unknownField.setShowTitle(false);
-
- // row 8
- currentTimeField = new StaticTextItem("currentTime");
- currentTimeField.setWrapTitle(false);
- currentTimeField.setColSpan(4);
- currentTimeField.setShowTitle(false);
-
- form.setItems(currentField, availField, availTimeField, downField, downTimeField, disabledField,
- disabledTimeField, failureCountField, disabledCountField, mtbfField, mttrField, unknownField,
- currentTimeField);
-
- reloadSummaryData();
-
- return form;
- }
-
- private void reloadSummaryData() {
- GWTServiceLookup.getResourceService().getResourceAvailabilitySummary(resource.getId(),
- new AsyncCallback<ResourceAvailabilitySummary>() {
-
- @Override
- public void onSuccess(ResourceAvailabilitySummary result) {
-
- currentField.setValue(MSG.view_resource_monitor_availability_currentStatus_value(
- getAvailabilityTypeMessage(result.getCurrent()),
- TimestampCellFormatter.format(result.getLastChange().getTime())));
- availField.setValue(MeasurementConverterClient.format(result.getUpPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- availTimeField.setValue(MeasurementConverterClient.format((double) result.getUpTime(),
- MeasurementUnits.MILLISECONDS, true));
- downField.setValue(MeasurementConverterClient.format(result.getDownPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- downTimeField.setValue(MeasurementConverterClient.format((double) result.getDownTime(),
- MeasurementUnits.MILLISECONDS, true));
- disabledField.setValue(MeasurementConverterClient.format(result.getDisabledPercentage(),
- MeasurementUnits.PERCENTAGE, true));
- disabledTimeField.setValue(MeasurementConverterClient.format((double) result.getDisabledTime(),
- MeasurementUnits.MILLISECONDS, true));
- failureCountField.setValue(result.getFailures());
- disabledCountField.setValue(result.getDisabled());
- mtbfField.setValue(MeasurementConverterClient.format((double) result.getMTBF(),
- MeasurementUnits.MILLISECONDS, true));
- mttrField.setValue(MeasurementConverterClient.format((double) result.getMTTR(),
- MeasurementUnits.MILLISECONDS, true));
-
- if (result.getUnknownTime() > 0L) {
- unknownField.setValue(MSG.view_resource_monitor_availability_unknown(MeasurementConverterClient
- .format((double) result.getUnknownTime(), MeasurementUnits.MILLISECONDS, true)));
- } else {
- unknownField.setValue("");
- }
-
- currentTimeField.setValue(MSG.view_resource_monitor_availability_currentAsOf(TimestampCellFormatter
- .format(result.getCurrentTime())));
- }
-
- @Override
- public void onFailure(Throwable caught) {
- currentField.setValue(MSG.common_label_error());
- CoreGUI.getErrorHandler()
- .handleError(MSG.view_resource_monitor_availability_summaryError(), caught);
- }
- });
- }
-
- private String getAvailabilityTypeMessage(AvailabilityType availabilityType) {
- switch (availabilityType) {
- case UP:
- return MSG.common_status_avail_up();
- case DOWN:
- return MSG.common_status_avail_down();
- case DISABLED:
- return MSG.common_status_avail_disabled();
- case UNKNOWN:
- default:
- return MSG.common_status_avail_unknown();
- }
- }
-
- private void prepareTooltip(FormItem item, String tooltip) {
- item.setHoverWidth(400);
- item.setPrompt(tooltip);
- }
-
-}
commit 20fccd49f29550e39fa89918f7e0c03aef65fb17
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Fri Dec 13 17:39:57 2013 +0100
[BZ 1042892] Don't use ${} notation for shell variables in CLI scripts.
As of BZ 959603, the files are subject to maven resource filtering and
so it can happen that the build server passes a variable to the build
process that clashes with the variable name defined in the script.
(Yes, this actually happened on our Jenkins server that passed
"-DCLASSPATH=" to the maven process for some reason, which resulted in
wonderful corruption of the rhq-cli.sh script).
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
index 112ec66..80f24c5 100755
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
@@ -3,6 +3,14 @@
#===========================================================================
#
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
+# notation. This file is subject to maven resource variable expansion
+# during the build and so it can happen that the build environment
+# could corrupt this file by expanding variables that clash with
+# the names defined herein.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# RHQ_CLI_DEBUG - If this is defined, the script will emit debug
# messages. It will also enable debug
# messages to be emitted from the cli itself.
@@ -41,7 +49,7 @@
# to the CLI's defaults, then you will want to
# use RHQ_CLI_ADDITIONAL_JAVA_OPTS instead.
#
-#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
+#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
# RHQ_CLI_JAVA_ENDORSED_DIRS - Java VM command line option to set the
# endorsed dirs for the CLI's VM. If this
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
index 8399b50..b0980e6 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
@@ -11,6 +11,14 @@
# set via rhq-client-env.sh, which is sourced by this script.
# =============================================================================
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+# IMPORTANT: Avoid enclosing shell variables in braces using the ${XXX}
+# notation. This file is subject to maven resource variable expansion
+# during the build and so it can happen that the build environment
+# could corrupt this file by expanding variables that clash with
+# the names defined herein.
+# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
# ----------------------------------------------------------------------
# Subroutine that simply dumps a message iff debug mode is enabled
# ----------------------------------------------------------------------
@@ -42,16 +50,17 @@ esac
_DOLLARZERO=`readlink "$0" 2>/dev/null || echo "$0"`
RHQ_CLI_BIN_DIR_PATH=`dirname "$_DOLLARZERO"`
-if [ -f "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" ]; then
- debug_msg "Loading environment script: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
- . "${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh" $*
+if [ -f "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" ]; then
+ debug_msg "Loading environment script: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
+ . "$RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh" $*
else
- debug_msg "No environment script found at: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
+ debug_msg "No environment script found at: $RHQ_CLI_BIN_DIR_PATH/rhq-cli-env.sh"
fi
# this variable is set during the build and defines the desired default behavior for directory changing
# we do want to change the dir in RHQ, but possibly don't want to do that in JBoss ON for backwards compatibility
# reasons.
+# (yes, this USES ${} in the source (not in the distribution) because we seed the default value from the build)
RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT=${rhq.cli.change-dir-on-start-default}
if [ -z "$RHQ_CLI_CHANGE_DIR_ON_START" ]; then
RHQ_CLI_CHANGE_DIR_ON_START="$RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT"
@@ -61,24 +70,24 @@ fi
# Previous versions always changed directory.
if [ -n "$RHQ_CLI_CHANGE_DIR_ON_START" -a "$RHQ_CLI_CHANGE_DIR_ON_START" != "false" ]; then
if [ -z "$RHQ_CLI_HOME" ]; then
- cd "${RHQ_CLI_BIN_DIR_PATH}/.."
+ cd "$RHQ_CLI_BIN_DIR_PATH/.."
else
- cd "${RHQ_CLI_HOME}" || {
- echo "Cannot go to the RHQ_CLI_HOME directory: ${RHQ_CLI_HOME}"
+ cd "$RHQ_CLI_HOME" || {
+ echo "Cannot go to the RHQ_CLI_HOME directory: $RHQ_CLI_HOME"
exit 1
}
fi
RHQ_CLI_HOME=`pwd`
else
if [ -z "$RHQ_CLI_HOME" ]; then
- RHQ_CLI_HOME="${RHQ_CLI_BIN_DIR_PATH}/.."
+ RHQ_CLI_HOME="$RHQ_CLI_BIN_DIR_PATH/.."
fi
#get an absolute path
RHQ_CLI_HOME=`readlink -f "$RHQ_CLI_HOME"`
if [ ! -d "$RHQ_CLI_HOME" ]; then
- echo "RHQ_CLI_HOME detected or defined as [${RHQ_CLI_HOME}] doesn't seem to exist or is not a directory"
+ echo "RHQ_CLI_HOME detected or defined as [$RHQ_CLI_HOME] doesn't seem to exist or is not a directory"
exit 1
fi
fi
@@ -90,7 +99,7 @@ debug_msg "RHQ_CLI_HOME: $RHQ_CLI_HOME"
# sample modules.
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_MODULES_DIR" ]; then
- RHQ_CLI_MODULES_DIR="${RHQ_CLI_HOME}/samples/modules"
+ RHQ_CLI_MODULES_DIR="$RHQ_CLI_HOME/samples/modules"
fi
# ----------------------------------------------------------------------
@@ -110,7 +119,7 @@ fi
if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
if [ -z "$RHQ_CLI_JAVA_HOME" ]; then
- RHQ_CLI_JAVA_HOME="${RHQ_CLI_HOME}/jre"
+ RHQ_CLI_JAVA_HOME="$RHQ_CLI_HOME/jre"
if [ -d "$RHQ_CLI_JAVA_HOME" ]; then
debug_msg "Using the embedded JRE"
else
@@ -119,7 +128,7 @@ if [ -z "$RHQ_CLI_JAVA_EXE_FILE_PATH" ]; then
fi
fi
debug_msg "RHQ_CLI_JAVA_HOME: $RHQ_CLI_JAVA_HOME"
- RHQ_CLI_JAVA_EXE_FILE_PATH=${RHQ_CLI_JAVA_HOME}/bin/java
+ RHQ_CLI_JAVA_EXE_FILE_PATH="$RHQ_CLI_JAVA_HOME/bin/java"
fi
debug_msg "RHQ_CLI_JAVA_EXE_FILE_PATH: $RHQ_CLI_JAVA_EXE_FILE_PATH"
@@ -133,14 +142,14 @@ fi
# Prepare the classpath (take into account possible spaces in dir names)
# ----------------------------------------------------------------------
-CLASSPATH="${RHQ_CLI_HOME}/conf"
-_JAR_FILES=`cd "${RHQ_CLI_HOME}/lib";ls -1 *.jar`
+CLASSPATH="$RHQ_CLI_HOME/conf"
+_JAR_FILES=`cd "$RHQ_CLI_HOME/lib";ls -1 *.jar`
for _JAR in $_JAR_FILES ; do
- _JAR="${RHQ_CLI_HOME}/lib/${_JAR}"
+ _JAR="$RHQ_CLI_HOME/lib/$_JAR"
if [ -z "$CLASSPATH" ]; then
- CLASSPATH="${_JAR}"
+ CLASSPATH="$_JAR"
else
- CLASSPATH="${CLASSPATH}:${_JAR}"
+ CLASSPATH="$CLASSPATH:$_JAR"
fi
debug_msg "CLASSPATH entry: $_JAR"
done
@@ -151,7 +160,7 @@ debug_msg "CLASSPATH entry: $_JAR"
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_JAVA_OPTS" ]; then
- RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
+ RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=$RHQ_CLI_MODULES_DIR"
fi
debug_msg "RHQ_CLI_JAVA_OPTS: $RHQ_CLI_JAVA_OPTS"
@@ -159,7 +168,7 @@ if [ "$RHQ_CLI_JAVA_ENDORSED_DIRS" = "none" ]; then
debug_msg "Not explicitly setting java.endorsed.dirs"
else
if [ -z "$RHQ_CLI_JAVA_ENDORSED_DIRS" ]; then
- RHQ_CLI_JAVA_ENDORSED_DIRS="${RHQ_CLI_HOME}/lib/endorsed"
+ RHQ_CLI_JAVA_ENDORSED_DIRS="$RHQ_CLI_HOME/lib/endorsed"
fi
# convert the path if on Windows
@@ -167,14 +176,14 @@ else
RHQ_CLI_JAVA_ENDORSED_DIRS=`cygpath --windows --path "$RHQ_CLI_JAVA_ENDORSED_DIRS"`
fi
debug_msg "RHQ_CLI_JAVA_ENDORSED_DIRS: $RHQ_CLI_JAVA_ENDORSED_DIRS"
- _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"${RHQ_CLI_JAVA_ENDORSED_DIRS}\""
+ _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=\"$RHQ_CLI_JAVA_ENDORSED_DIRS\""
fi
if [ "$RHQ_CLI_JAVA_LIBRARY_PATH" = "none" ]; then
debug_msg "Not explicitly setting java.library.path"
else
if [ -z "$RHQ_CLI_JAVA_LIBRARY_PATH" ]; then
- RHQ_CLI_JAVA_LIBRARY_PATH="${RHQ_CLI_HOME}/lib"
+ RHQ_CLI_JAVA_LIBRARY_PATH="$RHQ_CLI_HOME/lib"
fi
# convert the path if on Windows
@@ -182,7 +191,7 @@ else
RHQ_CLI_JAVA_LIBRARY_PATH=`cygpath --windows --path "$RHQ_CLI_JAVA_LIBRARY_PATH"`
fi
debug_msg "RHQ_CLI_JAVA_LIBRARY_PATH: $RHQ_CLI_JAVA_LIBRARY_PATH"
- _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"${RHQ_CLI_JAVA_LIBRARY_PATH}\""
+ _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=\"$RHQ_CLI_JAVA_LIBRARY_PATH\""
fi
debug_msg "RHQ_CLI_ADDITIONAL_JAVA_OPTS: $RHQ_CLI_ADDITIONAL_JAVA_OPTS"
@@ -201,8 +210,8 @@ if [ -n "$RHQ_CLI_DEBUG" ]; then
fi
# create the logs directory
-if [ ! -d "${RHQ_CLI_HOME}/logs" ]; then
- mkdir "${RHQ_CLI_HOME}/logs"
+if [ ! -d "$RHQ_CLI_HOME/logs" ]; then
+ mkdir "$RHQ_CLI_HOME/logs"
fi
# convert some of the paths if we are on Windows
@@ -220,15 +229,15 @@ fi
debug_msg "Executing the CLI with this command line:"
exit_code=0
if [ -z "$RHQ_CLI_CMDLINE_OPTS" ]; then
- debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain $@"
- "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain "$@"
+ debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $@"
+ "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain "$@"
exit_code=$?
else
- debug_msg "${RHQ_CLI_JAVA_EXE_FILE_PATH} ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp ${CLASSPATH} org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}"
- "${RHQ_CLI_JAVA_EXE_FILE_PATH}" ${_JAVA_ENDORSED_DIRS_OPT} ${_JAVA_LIBRARY_PATH_OPT} ${RHQ_CLI_JAVA_OPTS} ${RHQ_CLI_ADDITIONAL_JAVA_OPTS} ${_LOG_CONFIG} -cp "${CLASSPATH}" org.rhq.enterprise.client.ClientMain ${RHQ_CLI_CMDLINE_OPTS}
+ debug_msg "$RHQ_CLI_JAVA_EXE_FILE_PATH $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp $CLASSPATH org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS"
+ "$RHQ_CLI_JAVA_EXE_FILE_PATH" $_JAVA_ENDORSED_DIRS_OPT $_JAVA_LIBRARY_PATH_OPT $RHQ_CLI_JAVA_OPTS $RHQ_CLI_ADDITIONAL_JAVA_OPTS $_LOG_CONFIG -cp "$CLASSPATH" org.rhq.enterprise.client.ClientMain $RHQ_CLI_CMDLINE_OPTS
exit_code=$?
fi
debug_msg "$0 done."
-exit ${exit_code}
+exit $exit_code
commit 38128d7a3a983f5ab2b41a8c8d159c6900f92508
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Fri Dec 13 10:08:04 2013 +0100
No need to print the stack trace here - many systems do not have libvirt installed.
diff --git a/modules/plugins/virt/src/main/java/org/rhq/plugins/virt/VirtualizationHostDiscoveryComponent.java b/modules/plugins/virt/src/main/java/org/rhq/plugins/virt/VirtualizationHostDiscoveryComponent.java
index 749c4c1..98aca93 100644
--- a/modules/plugins/virt/src/main/java/org/rhq/plugins/virt/VirtualizationHostDiscoveryComponent.java
+++ b/modules/plugins/virt/src/main/java/org/rhq/plugins/virt/VirtualizationHostDiscoveryComponent.java
@@ -35,7 +35,7 @@ import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
import org.rhq.plugins.virt.LibVirtConnection.HVInfo;
/**
- * Discovers Host and Guest information using
+ * Discovers Host and Guest information using
*/
public class VirtualizationHostDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
@@ -68,7 +68,12 @@ public class VirtualizationHostDiscoveryComponent implements ResourceDiscoveryCo
res.getPluginConfiguration().put(new PropertySimple("connectionURI", virt.getConnectionURI()));
virt.close();
} catch (Throwable t) {
- log.warn("Can not load libvirt: " + t.getMessage(), t);
+ String message = t.getMessage();
+ if (t.getCause()!=null) {
+ message += ": " + t.getCause().getMessage();
+ }
+ log.warn("Can not load libvirt: " + message);
+
}
return res;
commit 5c69dea30c85ec9ce20510626775143113d6baea
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Dec 12 22:17:25 2013 +0100
Check if the parent can be cast to JMXComponent before doing so.
diff --git a/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheDiscoveryComponent.java b/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheDiscoveryComponent.java
index 4d1eecf..b3fabfa 100644
--- a/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheDiscoveryComponent.java
+++ b/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheDiscoveryComponent.java
@@ -23,6 +23,7 @@
package org.rhq.plugins.jbosscache;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -43,10 +44,10 @@ import org.rhq.plugins.jmx.MBeanResourceDiscoveryComponent;
/**
* Discover JBossCache instances. The only way to detect them are to
* look for "*:cache-interceptor=CacheMgmtInterceptor,*" or "*:treecache-interceptor=CacheMgmtInterceptor,*"
- * This is done in {@link MBeanResourceDiscoveryComponent}. We postprocess the result here to
+ * This is done in {@link MBeanResourceDiscoveryComponent}. We postprocess the result here to
* get the base MBean name (without the property for detection) to be able to use this later and also
* for display purposes, as this is the MBean name the user set up in his MBean.
- *
+ *
* @author Heiko W. Rupp
*/
public class JBossCacheDiscoveryComponent extends MBeanResourceDiscoveryComponent<JMXComponent<?>> {
@@ -60,6 +61,11 @@ public class JBossCacheDiscoveryComponent extends MBeanResourceDiscoveryComponen
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<JMXComponent<?>> context) {
ResourceContext parentCtx = context.getParentResourceContext();
+
+ if (!(parentCtx.getParentResourceComponent() instanceof JMXComponent)) {
+ return Collections.emptySet();
+ }
+
JMXComponent<JBossASServerComponent<?>> gparentComponent = (JMXComponent<JBossASServerComponent<?>>) parentCtx.getParentResourceComponent();
Set<DiscoveredResourceDetails> discovered = super.performDiscovery(context.getDefaultPluginConfiguration(), gparentComponent, context.getResourceType(), false);
commit 0eccf7d3e33e319d08a5dfcf56bbe0d67af16bcd
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 12 22:43:55 2013 +0100
Better testability in the ModulesDirectoryScriptSourceProvider.
Also changed the default module path from "./samples/modules" to
"./modules". This should be safe with the fix for BZ 959603 which
causes the CLI to always pass the system property to load the modules
from. The new default path is less coupled with the FS layout of the CLI
distribution.
diff --git a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProvider.java b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProvider.java
index 0e46abe..6040e8e 100644
--- a/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProvider.java
+++ b/modules/enterprise/remoting/cli/src/main/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProvider.java
@@ -30,13 +30,38 @@ import java.net.URI;
*/
public class ModulesDirectoryScriptSourceProvider extends FileSystemScriptSourceProvider {
- private static final File ROOT_DIR = new File(System.getProperty("rhq.scripting.modules.root-dir", "./samples/modules"));
private static final String SCHEME = "modules";
-
+
+ private File rootDir;
+
+ /**
+ * Creates a new instance of module script source provider that looks for the module sources in a directory
+ * specified by the "rhq.scripting.modules.root-dir" system property. If none such exists, the default value
+ * is assumed to be "./modules".
+ */
public ModulesDirectoryScriptSourceProvider() {
+ this(new File(System.getProperty("rhq.scripting.modules.root-dir", "./modules")));
+ }
+
+ /**
+ * Provided for testing purposes. A script source provider is only instantiated through its no-arg constructor
+ * in the scripting environment.
+ *
+ * @param rootDir the root directory under which to locate module sources
+ */
+ public ModulesDirectoryScriptSourceProvider(File rootDir) {
super(SCHEME);
+ this.rootDir = rootDir;
}
-
+
+ public File getRootDir() {
+ return rootDir;
+ }
+
+ public void setRootDir(File rootDir) {
+ this.rootDir = rootDir;
+ }
+
@Override
protected File getFile(URI location) {
String path = location.getPath();
@@ -44,6 +69,6 @@ public class ModulesDirectoryScriptSourceProvider extends FileSystemScriptSource
//remove the leading /
path = path.substring(1);
- return new File(ROOT_DIR, path);
+ return new File(rootDir, path);
}
}
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
new file mode 100644
index 0000000..4060d07
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
@@ -0,0 +1,67 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+package org.rhq.enterprise.client.script;
+
+import static org.testng.Assert.assertEquals;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.BeforeClass;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Test;
+
+/**
+ * @author Lukas Krejci
+ * @since 4.10.0
+ */
+@Test
+public class ModulesDirectoryScriptSourceProviderTest {
+
+ private File scriptFile;
+
+ @BeforeClass
+ public void createScriptFile() throws IOException {
+ scriptFile = File.createTempFile("modules-test", ".js", new File("."));
+ PrintWriter wrt = new PrintWriter(new FileOutputStream(scriptFile));
+ try {
+ wrt.write("var a = 2;");
+ } finally {
+ wrt.close();
+ }
+ }
+
+ @AfterClass
+ public void deleteScriptFile() {
+ scriptFile.delete();
+ }
+
+ public void testLoad() throws URISyntaxException {
+ ModulesDirectoryScriptSourceProvider provider = new ModulesDirectoryScriptSourceProvider(new File("."));
+
+ File f = provider.getFile(new URI("modules:/" + scriptFile.getName()));
+
+ assertEquals(f, scriptFile, "Unexpected file loaded");
+ }
+}
commit cb4a44d91636ff2fb298b7b9dff8dfed723bf2fc
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 12 23:10:14 2013 +0100
[BZ 959603] - Don't change CWD on CLI startup.
Since the very beginning of CLI, RHQ always changed the CWD to
$RHQ_CLI_HOME when starting up, which is rather strange and
non-standard.
To keep the backwards compatibility, the behavior can be toggled on
or off.
The default behavior can be changed in the build by setting the
rhq.cli.change-dir-on-start-default
property. In RHQ, this defaults to "false", causing RHQ to NOT change
directories when starting up the CLI anymore.
Further, the behavior can be toggled by the user by setting the
RHQ_CLI_CHANGE_DIR_ON_START environment variable to "true" (or any
other value but "false" actually) or "false" explicitly.
Additionally, a new environment variable called "RHQ_CLI_MODULES_DIR"
was added. This was necessary to not break the loading of the
provided CommonJS modules available in the
$RHQ_CLI_HOME/samples/modules directory if the CLI was started from
another directory.
By adding the new RHQ_CLI_MODULES_DIR, which is initialized to the
correct value if not provided externally, we correctly load the
sample modules with the additional benefit of easier change of the
modules location for the user (previously this was only possible
through
RHQ_CLI_ADDITIONAL_JAVA_OPTS="-Drhq.scripting.modules.root-dir=..."
, while now one only needs RHQ_CLI_MODULES_DIR=...).
diff --git a/modules/enterprise/remoting/cli/pom.xml b/modules/enterprise/remoting/cli/pom.xml
index 7515110..296b490 100644
--- a/modules/enterprise/remoting/cli/pom.xml
+++ b/modules/enterprise/remoting/cli/pom.xml
@@ -13,7 +13,11 @@
<name>RHQ Enterprise Remote CLI</name>
<description>RHQ Enterprise Remote Command Line Interface</description>
-
+
+ <properties>
+ <rhq.cli.change-dir-on-start-default>false</rhq.cli.change-dir-on-start-default>
+ </properties>
+
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
@@ -78,7 +82,18 @@
</dependencies>
<build>
- <plugins>
+ <resources>
+ <resource>
+ <directory>src/etc</directory>
+ <filtering>true</filtering>
+ <targetPath>${project.build.outputDirectory}/../etc</targetPath>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ </resource>
+ </resources>
+
+ <plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.bat b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.bat
index d5d1189..dcf1ec8 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.bat
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.bat
@@ -37,7 +37,7 @@ rem CLI's defaults. If you only want to add options
rem to the CLI's defaults, then you will want to
rem use RHQ_CLI_ADDITIONAL_JAVA_OPTS instead.
rem
-rem set RHQ_CLI_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true
+rem set RHQ_CLI_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir="%RHQ_CLI_MODULES_DIR%"
rem RHQ_CLI_JAVA_ENDORSED_DIRS - Java VM command line option to set the
rem endorsed dirs for the CLI's VM. If this
@@ -74,3 +74,33 @@ rem line options and the ones specified here will
rem be passed to the CLI.
rem
rem set RHQ_CLI_CMDLINE_OPTS=
+
+rem RHQ_CLI_CHANGE_DIR_ON_START - By setting this variable to true (or any
+rem other value than "false") you can make RHQ
+rem change the directory to $RHQ_CLI_HOME when
+rem starting the CLI. When this variable is set
+rem to false, the current working directory is
+rem NOT changed when starting the CLI.
+rem
+rem If not set, this variable is understood
+rem to be: ${rhq.cli.change-dir-on-start-default}
+rem set RHQ_CLI_CHANGE_DIR_ON_START=true
+
+rem RHQ_CLI_MODULES_DIR - The default location from which to load CommonJS
+rem modules available through the "modules:/" scheme
+rem is $RHQ_CLI_HOME/samples/modules.
+rem Setting this variable to another value, causes
+rem the modules to be loaded from another location.
+rem
+rem Notice that this can be a relative path, too.
+rem For example, setting:
+rem RHQ_CLI_MODULES_DIR=./modules
+rem would cause the CLI to load modules from the
+rem "modules" subdirectory of whichever current
+rem working directory it is started from.
+rem (See RHQ_CLI_JAVA_OPTS and
+rem RHQ_CLI_CHANGE_DIR_ON_START variables above for
+rem further details of this approach).
+rem
+rem set RHQ_CLI_MODULE_DIR=Z:\company-wide\rhq\cli\modules
+
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
index 2c0affa..112ec66 100755
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli-env.sh
@@ -41,7 +41,7 @@
# to the CLI's defaults, then you will want to
# use RHQ_CLI_ADDITIONAL_JAVA_OPTS instead.
#
-#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true"
+#RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
# RHQ_CLI_JAVA_ENDORSED_DIRS - Java VM command line option to set the
# endorsed dirs for the CLI's VM. If this
@@ -78,3 +78,33 @@
# be passed to the CLI.
#
#RHQ_CLI_CMDLINE_OPTS=""
+
+# RHQ_CLI_CHANGE_DIR_ON_START - By setting this variable to true (or any
+# other value than "false") you can make RHQ
+# change the directory to $RHQ_CLI_HOME when
+# starting the CLI. When this variable is set
+# to false, the current working directory is
+# NOT changed when starting the CLI.
+#
+# If not set, this variable is understood
+# to be: ${rhq.cli.change-dir-on-start-default}
+#RHQ_CLI_CHANGE_DIR_ON_START=true
+
+# RHQ_CLI_MODULES_DIR - The default location from which to load CommonJS
+# modules available through the "modules:/" scheme
+# is $RHQ_CLI_HOME/samples/modules.
+# Setting this variable to another value, causes
+# the modules to be loaded from another location.
+#
+# Notice that this can be a relative path, too.
+# For example, setting:
+# RHQ_CLI_MODULES_DIR=./modules
+# would cause the CLI to load modules from the
+# "modules" subdirectory of whichever current
+# working directory it is started from.
+# (See RHQ_CLI_JAVA_OPTS and
+# RHQ_CLI_CHANGE_DIR_ON_START variables above for
+# further details of this approach).
+#
+#RHQ_CLI_MODULE_DIR=/opt/company-wide/rhq/cli/modules
+
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli.bat b/modules/enterprise/remoting/cli/src/etc/rhq-cli.bat
index 8c96ed7..13dd762 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli.bat
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli.bat
@@ -20,13 +20,6 @@ if "%RHQ_CLI_DEBUG%" == "false" (
set RHQ_CLI_DEBUG=
)
-rem ----------------------------------------------------------------------
-rem Change directory so the current directory is the CLI home.
-rem Here we assume this script is a child directory of the CLI home
-rem We also assume our custom environment script is located in the same
-rem place as this script.
-rem ----------------------------------------------------------------------
-
set RHQ_CLI_BIN_DIR_PATH=%~dp0
if exist "%RHQ_CLI_BIN_DIR_PATH%\rhq-cli-env.bat" (
@@ -36,11 +29,38 @@ if exist "%RHQ_CLI_BIN_DIR_PATH%\rhq-cli-env.bat" (
if defined RHQ_CLI_DEBUG echo No environment script found at: %RHQ_CLI_BIN_DIR_PATH%\rhq-cli-env.bat
)
-rem if debug variable is set, it is assumed to be on, unless its value is false
+rem this variable is set during the build and defines the desired default behavior for directory changing
+rem we do want to change the dir in RHQ, but possibly don't want to do that in JBoss ON for backwards compatibility
+rem reasons.
+set RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT=${rhq.cli.change-dir-on-start-default}
+
+if not defined RHQ_CLI_CHANGE_DIR_ON_START (
+ set RHQ_CLI_CHANGE_DIR_ON_START=%RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT%
+)
+
+rem RHQ_CLI_CHANGE_DIR_ON_START behaves the same as RHQ_CLI_DEBUG - if set, it is assumed on, unless equal to false
+if "%RHQ_CLI_CHANGE_DIR_ON_START%" == "false" (
+ set RHQ_CLI_CHANGE_DIR_ON_START=
+)
+
+rem if debug variable is set, it is assumed to be on, unless its value is false (do this again after we've loaded
+rem the env script)
if "%RHQ_CLI_DEBUG%" == "false" (
set RHQ_CLI_DEBUG=
)
+rem ----------------------------------------------------------------------
+rem Change directory so the current directory is the CLI home.
+rem Here we assume this script is a child directory of the CLI home
+rem We also assume our custom environment script is located in the same
+rem place as this script.
+rem ----------------------------------------------------------------------
+
+rem since RHQ 4.10.0 we don't change the directory to the RHQ_CLI_HOME by default
+if not defined RHQ_CLI_CHANGE_DIR_ON_START (
+ pushd .
+)
+
if not defined RHQ_CLI_HOME (
cd "%RHQ_CLI_BIN_DIR_PATH%\.."
) else (
@@ -52,9 +72,22 @@ if not defined RHQ_CLI_HOME (
set RHQ_CLI_HOME=%CD%
+rem since RHQ 4.10.0 we don't change the directory to the RHQ_CLI_HOME by default
+if not defined RHQ_CLI_CHANGE_DIR_ON_START (
+ popd
+)
+
if defined RHQ_CLI_DEBUG echo RHQ_CLI_HOME: %RHQ_CLI_HOME%
rem ----------------------------------------------------------------------
+rem Prepare the modules:/ script source provider to by default load our
+rem sample modules.
+rem ----------------------------------------------------------------------
+if not defined RHQ_CLI_MODULES_DIR (
+ set RHQ_CLI_MODULES_DIR="%RHQ_CLI_HOME%\samples\modules"
+)
+
+rem ----------------------------------------------------------------------
rem Find the Java executable and verify we have a VM available
rem Note that RHQ_CLI_JAVA_* props are still handled for back compat
rem ----------------------------------------------------------------------
@@ -105,7 +138,7 @@ rem Prepare the VM command line options to be passed in
rem ----------------------------------------------------------------------
if not defined RHQ_CLI_JAVA_OPTS (
- set RHQ_CLI_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true
+ set RHQ_CLI_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir="%RHQ_CLI_MODULES_DIR%"
)
if defined RHQ_CLI_DEBUG echo RHQ_CLI_JAVA_OPTS: %RHQ_CLI_JAVA_OPTS%
diff --git a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
index 9fce451..8399b50 100644
--- a/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
+++ b/modules/enterprise/remoting/cli/src/etc/rhq-cli.sh
@@ -49,20 +49,51 @@ else
debug_msg "No environment script found at: ${RHQ_CLI_BIN_DIR_PATH}/rhq-cli-env.sh"
fi
-if [ -z "$RHQ_CLI_HOME" ]; then
- cd "${RHQ_CLI_BIN_DIR_PATH}/.."
-else
- cd "${RHQ_CLI_HOME}" || {
- echo "Cannot go to the RHQ_CLI_HOME directory: ${RHQ_CLI_HOME}"
- exit 1
- }
+# this variable is set during the build and defines the desired default behavior for directory changing
+# we do want to change the dir in RHQ, but possibly don't want to do that in JBoss ON for backwards compatibility
+# reasons.
+RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT=${rhq.cli.change-dir-on-start-default}
+if [ -z "$RHQ_CLI_CHANGE_DIR_ON_START" ]; then
+ RHQ_CLI_CHANGE_DIR_ON_START="$RHQ_CLI_CHANGE_DIR_ON_START_DEFAULT"
fi
-RHQ_CLI_HOME=`pwd`
+# Only change the directory on start when told so. This is new in RHQ 4.10.0.
+# Previous versions always changed directory.
+if [ -n "$RHQ_CLI_CHANGE_DIR_ON_START" -a "$RHQ_CLI_CHANGE_DIR_ON_START" != "false" ]; then
+ if [ -z "$RHQ_CLI_HOME" ]; then
+ cd "${RHQ_CLI_BIN_DIR_PATH}/.."
+ else
+ cd "${RHQ_CLI_HOME}" || {
+ echo "Cannot go to the RHQ_CLI_HOME directory: ${RHQ_CLI_HOME}"
+ exit 1
+ }
+ fi
+ RHQ_CLI_HOME=`pwd`
+else
+ if [ -z "$RHQ_CLI_HOME" ]; then
+ RHQ_CLI_HOME="${RHQ_CLI_BIN_DIR_PATH}/.."
+ fi
+
+ #get an absolute path
+ RHQ_CLI_HOME=`readlink -f "$RHQ_CLI_HOME"`
+
+ if [ ! -d "$RHQ_CLI_HOME" ]; then
+ echo "RHQ_CLI_HOME detected or defined as [${RHQ_CLI_HOME}] doesn't seem to exist or is not a directory"
+ exit 1
+ fi
+fi
debug_msg "RHQ_CLI_HOME: $RHQ_CLI_HOME"
# ----------------------------------------------------------------------
+# Prepare the modules:/ script source provider to by default load our
+# sample modules.
+# ----------------------------------------------------------------------
+if [ -z "$RHQ_CLI_MODULES_DIR" ]; then
+ RHQ_CLI_MODULES_DIR="${RHQ_CLI_HOME}/samples/modules"
+fi
+
+# ----------------------------------------------------------------------
# If we are on a Mac and JAVA_HOME is not set, then set it to /usr
# as this is the default location.
# ----------------------------------------------------------------------
@@ -113,8 +144,6 @@ for _JAR in $_JAR_FILES ; do
fi
debug_msg "CLASSPATH entry: $_JAR"
done
-_JAR="${RHQ_CLI_HOME}/jbossws-native-dist/deploy/lib/jbossws-client.jar"
-CLASSPATH="${CLASSPATH}:${_JAR}"
debug_msg "CLASSPATH entry: $_JAR"
# ----------------------------------------------------------------------
@@ -122,7 +151,7 @@ debug_msg "CLASSPATH entry: $_JAR"
# ----------------------------------------------------------------------
if [ -z "$RHQ_CLI_JAVA_OPTS" ]; then
- RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true"
+ RHQ_CLI_JAVA_OPTS="-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -Drhq.scripting.modules.root-dir=${RHQ_CLI_MODULES_DIR}"
fi
debug_msg "RHQ_CLI_JAVA_OPTS: $RHQ_CLI_JAVA_OPTS"
diff --git a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
index 1836e9f..a752037 100644
--- a/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
+++ b/modules/enterprise/remoting/cli/src/main/scripts/rhq-client.build.xml
@@ -26,7 +26,7 @@
<target name="prepare-bin-dir">
<echo>*** Populating bin scripts...</echo>
<copy verbose="true" toDir="${bin.home}">
- <fileset dir="${basedir}/src/etc/" includes="rhq-cli*.*" />
+ <fileset dir="${basedir}/target/etc/" includes="rhq-cli*.*" />
</copy>
<chmod dir="${bin.home}" perm="ug+rx" includes="*.sh" />
</target>
commit eac6f82d17db5bba902f57e11851d0bb7a2d9ad0
Author: Lukas Krejci <lkrejci(a)redhat.com>
Date: Thu Dec 12 13:43:56 2013 +0100
Removing old WS client files from CLI codebase
diff --git a/modules/enterprise/remoting/cli/src/etc/Remoting_Setup.txt b/modules/enterprise/remoting/cli/src/etc/Remoting_Setup.txt
deleted file mode 100644
index 10bf3dc..0000000
--- a/modules/enterprise/remoting/cli/src/etc/Remoting_Setup.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-# Steps to build and run Remoting CLI:
-
-i) Checkout RHQ
-
-ii) Enable [rhq_source]/modules/enterprise/client/src/main/java as a java source directory for your ide. For eclipse users, 'use as source folder'
-
-iii) Build RHQ from [rhq_source] and enable the Webservice endpoints by passing in the following additional parameter at build time:
- -Drhq.server.enable.ws
-
-iv) Run rhq as normal and after startup, verify that your webservices are deployed at http://[server]:7080/jbossws/services
-
-v) Until we figure out how to correctly fit this client side portion into the maven build, or even if, the following will need to be done in your IDE/Dev environment to get the client side JAXB generated types to run the following steps successfully. Jay is tasked with Maven-izing it.
- a) Open up a shell and navigate to [rhq_source]/modules/enterprise/client/src/ and run generate-jaxb-client-types.sh/bat script to generate all JAXB types to ./output directory.
- b) Add ./output directory to the top of your classpath setup in your IDE.
- c) Add the XStream 1.2.2 library from your maven repository to the classpath for your project as well.
- d) Download JLine 0.9.94 library and add it to the classpath for your project as well.
-
-vi) Run the test suite (currently incomplete and we'll be enhancing it as we move forward) by:
- a)Log into the RHQ GUI client and create a new WS test user, u:ws-test p:ws-test and the "Super User" Role.
- b)You should be able to startup XMLClientMainTest.java from you IDE and run it as a junit test.
-
-
-Notes:
--i) Take extra care that the classes that you are using for the exposed SLSBs are coming out of the 'org.rhq.enterprise.server.ws' package only. Any components that are using types NOT from these client side JAXB elements is using server side types which we do NOT want to do. We need to continue to operate as a pure JAXB client so that we most closely mimic the operating environment for all WS* clients.
-
-ii) Sometimes it is helpful when debugging the webservice interface to see what the client side or server side type actually looks like as XML. XStream, while inefficient for serializing arbitrary java object trees, is much less picky about serializing these object trees so you can get a quick picture of a given object. JAXB typically will not serialize unless you do some extra coding.
-
-
-iii) After jaxb type generation may need to:
-
- A)
-Navigate to org.rhq.enterprise.server.ws.ContentManagerRemote.getPackageTypes and change
-
-throws ResourceTypeNotFoundException_Exception
- to
-throws ResourceTypeNotFoundException
-
- B) Navigate to and change
-public class ResourceTypeNotFoundException {
- to
-public class ResourceTypeNotFoundException extends Throwable {
-
- C) Delete the ResourceTypeNotFoundException_Exception class.
diff --git a/modules/enterprise/remoting/cli/src/etc/build.xml b/modules/enterprise/remoting/cli/src/etc/build.xml
deleted file mode 100644
index 6aa91c1..0000000
--- a/modules/enterprise/remoting/cli/src/etc/build.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-<project name="RHQ Remoting" basedir="." default="jar">
-
- <!--location that JAXB remote ws types are compiled to -->
- <property name="rhq-jaxb-types-location"
- location="${basedir}/output"/>
-
- <!--location that RHQ client classes are compiled to -->
- <property name="rhq-remote-classes"
- location="${basedir}/bin"/>
-
- <!--location for JLine dependency -->
- <property name="jline-jar"
- location="C:/installers/Jline/jline-0.9.94/jline-0.9.94.jar"/>
-
- <!-- -->
- <property name="repo" location="C:/Documents and Settings/Simeon/.m2/repository"/>
-
- <!--location for Gnu-Getopt dependency -->
- <property name="gnu-jar"
- location="${repo}/gnu-getopt/getopt/1.0.13/getopt-1.0.13.jar"/>
-
- <!--location for Gnu-Getopt dependency -->
- <property name="i18-jar"
- location="${repo}/i18nlog/i18nlog/1.0.10/i18nlog-1.0.10.jar"/>
-
- <property name="runLocation" value="${basedir}/run"/>
-
- <path id="project.class.path">
- <pathelement location="${runLocation}/getopt-1.0.13.jar"/>
- <pathelement location="${runLocation}/remoting.jar"/>
- <pathelement location="${runLocation}/jline-0.9.94.jar"/>
- <pathelement location="${runLocation}/i18nlog-1.0.10.jar"/>
-<!-- <pathelement path="${java.class.path}/"/>
- <pathelement path="${additional.path}"/> -->
- </path>
-
-
- <!--This target bundles up the remoting elements as an
- executable jar
- -->
- <target name="jar" description="Make executable jar">
- <delete file="${runLocation}/remoting.jar"/>
- <jar destfile="${runLocation}/remoting.jar"
- basedir="${rhq-remote-classes}"
- includes="org/rhq/**"
- excludes="**/WsFindGroupsCommand.class"
- />
-
- <!-- Copy all necessary jars to that location too -->
- <copy tofile="${runLocation}/jline-0.9.94.jar" file="${jline-jar}"/>
- <copy tofile="${runLocation}/getopt-1.0.13.jar" file="${gnu-jar}"/>
- <copy tofile="${runLocation}/i18nlog-1.0.10.jar" file="${i18-jar}"/>
- <copy tofile="${runLocation}/toRun.txt" file="${basedir}/toRun.txt"/>
-<!-- <copy todir="${runLocation}">
- <fileset dir="src_dir" excludes="**/*.java"/>
- </copy> -->
-
- </target>
-
- <target name="zip" depends="jar">
- <delete file="${runLocation}/bundle.zip"/>
- <zip destfile="${runLocation}/bundle.zip"
- basedir="${runLocation}"
- />
- </target>
-
- <target name="run">
- <java classname="org.rhq.enterprise.client.ClientMain">
- <!-- <arg value="-h"/> -->
- <classpath refid="project.class.path">
- <!-- <pathelement refid="project.class.path"/>
- <pathelement path="${java.class.path}"/> -->
-
- </classpath>
- </java>
-
-
- </target>
-
-</project>
diff --git a/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.bat b/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.bat
deleted file mode 100644
index 68e8706..0000000
--- a/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.bat
+++ /dev/null
@@ -1,16 +0,0 @@
-rem This script should consume all the wsdls and compile the JAXB types all
-rem into one directory.
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/RoleManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/ContentManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/SubjectManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/OperationManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/RepoManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/ConfigurationManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-call ../../../../dev-container/jbossas/bin/wsconsume.bat -k http://127.0.0.1:7080/rhq-rhq-server/ResourceManagerBean?wsdl -p org.rhq.enterprise.server.ws
\ No newline at end of file
diff --git a/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.sh b/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.sh
deleted file mode 100644
index f83293b..0000000
--- a/modules/enterprise/remoting/cli/src/etc/generate-jaxb-client-types.sh
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/bin/sh
-
-# This script should consume all the wsdls and compile the JAXB types all
-# into one directory.
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/RoleManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/ContentManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/SubjectManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/OperationManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/RepoManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/ConfigurationManagerBean?wsdl -p org.rhq.enterprise.server.ws
-
-../../../../dev-container/jbossas/bin/wsconsume.sh -k http://localhost:7080/rhq-rhq-server/ResourceManagerBean?wsdl -p org.rhq.enterprise.server.ws
\ No newline at end of file
diff --git a/modules/enterprise/remoting/cli/src/etc/toRun.txt b/modules/enterprise/remoting/cli/src/etc/toRun.txt
deleted file mode 100644
index a86a75b..0000000
--- a/modules/enterprise/remoting/cli/src/etc/toRun.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-java -cp getopt-1.0.13.jar:remoting.jar:jline-0.9.94.jar:i18nlog-1.0.9.jar org.rhq.enterprise.client.ClientMain
-
-java -cp getopt-1.0.13.jar;remoting.jar;jline-0.9.94.jar;i18nlog-1.0.9.jar org.rhq.enterprise.client.ClientMain
\ No newline at end of file
commit 40016f023129faf02dc0d591e3e0085d5cc46130
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Thu Dec 12 16:51:31 2013 +0100
[BZ 1040928] - User with default permissions sees his assigned resources as DOWN - using the subjectManager.getOverlord() instead of the actual subject, because the live resource availability should not require the MANAGE_SETTINGS permission.
diff --git a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/ResourceManagerBean.java b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/ResourceManagerBean.java
index 8a78995..2351f08 100644
--- a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/ResourceManagerBean.java
+++ b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/resource/ResourceManagerBean.java
@@ -2452,7 +2452,7 @@ public class ResourceManagerBean implements ResourceManagerLocal, ResourceManage
try {
// first, quickly see if we can even ping the agent, if not, don't bother trying to get the resource avail
- Agent agent = agentManager.getAgentByResourceId(subject, resourceId);
+ Agent agent = agentManager.getAgentByResourceId(subjectManager.getOverlord(), resourceId);
if (agent == null) {
if (log.isDebugEnabled()) {
log.debug("Resource [" + resourceId + "] does not exist or has no agent assigned");
commit a12716ee947628ad29f28cae5fbd34bf5d19984c
Author: John Sanda <jsanda(a)redhat.com>
Date: Thu Dec 12 10:14:12 2013 -0500
calculate bloom filters
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
index 77c22fd..d185f56 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -24,8 +24,113 @@
var time = new Time();
+ function BloomFilter(keys) {
+ function BloomSpecification(k, bucketsPerElement) {
+ this.K = k;
+ this.bucketsPerElement = bucketsPerElement;
+ }
+
+ // This is taken directly from BloomCalculations.java
+ this.probs = [
+ [1.0], // dummy row representing 0 buckets per element
+ [1.0, 1.0], // dummy row representing 1 buckets per element
+ [1.0, 0.393, 0.400],
+ [1.0, 0.283, 0.237, 0.253],
+ [1.0, 0.221, 0.155, 0.147, 0.160],
+ [1.0, 0.181, 0.109, 0.092, 0.092, 0.101], // 5
+ [1.0, 0.154, 0.0804, 0.0609, 0.0561, 0.0578, 0.0638],
+ [1.0, 0.133, 0.0618, 0.0423, 0.0359, 0.0347, 0.0364],
+ [1.0, 0.118, 0.0489, 0.0306, 0.024, 0.0217, 0.0216, 0.0229],
+ [1.0, 0.105, 0.0397, 0.0228, 0.0166, 0.0141, 0.0133, 0.0135, 0.0145],
+ [1.0, 0.0952, 0.0329, 0.0174, 0.0118, 0.00943, 0.00844, 0.00819, 0.00846], // 10
+ [1.0, 0.0869, 0.0276, 0.0136, 0.00864, 0.0065, 0.00552, 0.00513, 0.00509],
+ [1.0, 0.08, 0.0236, 0.0108, 0.00646, 0.00459, 0.00371, 0.00329, 0.00314],
+ [1.0, 0.074, 0.0203, 0.00875, 0.00492, 0.00332, 0.00255, 0.00217, 0.00199, 0.00194],
+ [1.0, 0.0689, 0.0177, 0.00718, 0.00381, 0.00244, 0.00179, 0.00146, 0.00129, 0.00121, 0.0012],
+ [1.0, 0.0645, 0.0156, 0.00596, 0.003, 0.00183, 0.00128, 0.001, 0.000852, 0.000775, 0.000744], // 15
+ [1.0, 0.0606, 0.0138, 0.005, 0.00239, 0.00139, 0.000935, 0.000702, 0.000574, 0.000505, 0.00047, 0.000459],
+ [1.0, 0.0571, 0.0123, 0.00423, 0.00193, 0.00107, 0.000692, 0.000499, 0.000394, 0.000335, 0.000302, 0.000287, 0.000284],
+ [1.0, 0.054, 0.0111, 0.00362, 0.00158, 0.000839, 0.000519, 0.00036, 0.000275, 0.000226, 0.000198, 0.000183, 0.000176],
+ [1.0, 0.0513, 0.00998, 0.00312, 0.0013, 0.000663, 0.000394, 0.000264, 0.000194, 0.000155, 0.000132, 0.000118, 0.000111, 0.000109],
+ [1.0, 0.0488, 0.00906, 0.0027, 0.00108, 0.00053, 0.000303, 0.000196, 0.00014, 0.000108, 8.89e-05, 7.77e-05, 7.12e-05, 6.79e-05, 6.71e-05] // 20
+ ]; // the first column is a dummy column representing K=0.
+
+ var self = this;
+ var excess = 20;
+ var bitset_excess = 20;
+ var minBuckets = 2;
+ var minK = 1;
+ var optKPerBuckets = [];
+ var maxFalsePosProb = 0.01;
+
+
+ for (i = 0; i < this.probs.length; i++) {
+ var min = java.lang.Double.MAX_VALUE;
+ var prob = this.probs[i];
+ for (j = 0; j < prob.length; j++) {
+ if (prob[j] < min) {
+ min = prob[j];
+ optKPerBuckets[i] = Math.max(minK, j);
+ }
+ }
+ }
+
+ var bucketsPerElement = maxBucketsPerElement(keys);
+ var spec = computeBloomSpec(bucketsPerElement, maxFalsePosProb);
+ var numBits = (keys * spec.bucketsPerElement) + bitset_excess;
+ var wordCount = bits2words(numBits);
+
+ if (wordCount > java.lang.Integer.MAX_VALUE) {
+ throw "Bloom filter size is > 16GB, reduce the bloom_filter_fp_chance";
+ }
+
+ var bytes = wordCount * 8;
+ this.size = bytes + 8;
+
+ function maxBucketsPerElement(numElements) {
+ numElements = Math.max(1, numElements);
+ var v = (java.lang.Long.MAX_VALUE - excess) / keys;
+ if (v < 1) {
+ throw "Cannot compute probabilities for " + numElements + " elements.";
+ }
+ return Math.min(self.probs.length - 1, v);
+ }
+
+ // This is taken from BloomCalculations.java
+ function computeBloomSpec(maxBucketsPerElement, maxFalsePosProb) {
+ var maxK = self.probs[maxBucketsPerElement].length - 1;
+
+ // Handle the trivial cases
+ if(maxFalsePosProb >= self.probs[minBuckets][minK]) {
+ return new BloomSpecification(2, optKPerBuckets[2]);
+ }
+ if (maxFalsePosProb < self.probs[maxBucketsPerElement][maxK]) {
+ throw "Unable to satisfy " + maxFalsePosProb + " with " + maxBucketsPerElement + " buckets per element";
+ }
+
+ // First find the minimal required number of buckets:
+ var bucketsPerElement = 2;
+ var K = optKPerBuckets[2];
+ while(self.probs[bucketsPerElement][K] > maxFalsePosProb){
+ bucketsPerElement++;
+ K = optKPerBuckets[bucketsPerElement];
+ }
+ // Now that the number of buckets is sufficient, see if we can relax K
+ // without losing too much precision.
+ while(self.probs[bucketsPerElement][K - 1] <= maxFalsePosProb) {
+ K--;
+ }
+ println('K = ' + K + ', bucketsPerElement = ' + bucketsPerElement);
+ return new BloomSpecification(K, bucketsPerElement);
+ }
+
+ function bits2words(numBits) {
+ return (((numBits-1)>>>6)+1);
+ }
+ }
+
/**
- * Encapsulates sizing data for a set of rows, having the samae number of columns and
+ * Encapsulates sizing data for a set of rows, having the same number of columns and
* each column being the same size.
*/
function RowInfo() {
@@ -124,6 +229,7 @@
schedules) {
var table = new Table(name);
table.data = calculateData(schedules, columnSize, rowKeySize, duration);
+ table.bloomFilter = new BloomFilter(5).size;
util.foreach(table.data.rows, function(interval) {
var rowInfo = table.data.rows[interval];
@@ -216,8 +322,8 @@
var rowKeyValueSize = 4;
var columnNameSize = 8;
- return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize, time.week,
- schedules);
+ return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize,
+ time.week, schedules);
};
/**
@@ -261,4 +367,9 @@
exports.sizeOf24HourMetrics = function(schedules) {
return calculateAggregatesTableSize('twenty_four_hour_metrics', time.day * 365, schedules);
};
+
+ // exposed for testing
+ exports.bloomFilter = function(keys) {
+ return new BloomFilter(keys).size;
+ }
})();
\ No newline at end of file
commit 2fbc974a65c2b0331b52c81143d9821d755863a3
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Dec 11 17:03:35 2013 -0500
adding some usage docs
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
index 77e78b7..77c22fd 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -5,6 +5,11 @@
* installs, some input, in terms of the numbers and types of resources, will have to be provided in order to generate
* the estimates. Right now the module has functionality for generating the sizes of the data and partition index files
* for the metrics tables (which does not include the metrics_index table).
+ *
+ * current usage (from CLI shell):
+ *
+ * $ storage = require('modules:/rhq.storage.sizing.js');
+ * $ results = storage.sizeOfRawMetrics({30000: 5, 60000: 5});
*/
(function() {
var util = require('modules:/util');
commit 3141f63345442240fc2cd3d90a996840dfb1e428
Author: John Sanda <jsanda(a)redhat.com>
Date: Wed Dec 11 16:59:10 2013 -0500
initial commit for storage sizing CLI script
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
new file mode 100644
index 0000000..77e78b7
--- /dev/null
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/rhq.storage.sizing.js
@@ -0,0 +1,259 @@
+/**
+ * This module provides functionality to generate storage cluster sizing estimates. The end goal is for it to support
+ * generating estimates for both existing and new installations. For existing installations, the script will query
+ * the existing RHQ database to get the necessary metric schedule info needed to generate the estimates. For new
+ * installs, some input, in terms of the numbers and types of resources, will have to be provided in order to generate
+ * the estimates. Right now the module has functionality for generating the sizes of the data and partition index files
+ * for the metrics tables (which does not include the metrics_index table).
+ */
+(function() {
+ var util = require('modules:/util');
+
+ function Time() {
+ this.second = 1000;
+ this.minute = this.second * 60;
+ this.hour = this.minute * 60;
+ this.day = this.hour * 24;
+ this.week = this.day * 7;
+ }
+
+ var time = new Time();
+
+ /**
+ * Encapsulates sizing data for a set of rows, having the samae number of columns and
+ * each column being the same size.
+ */
+ function RowInfo() {
+ // The total number of rows described by this RowInfo object
+ this.numRows = 0;
+ // The total number of columns contained in each row
+ this.numColumns = 0;
+ // The size of an individual row
+ this.rowSize = 0;
+ // The total size of all rows described by this RowInfo object
+ this.totalSize = 0;
+ }
+
+ /**
+ * Encapsulates sizing data for an RHQ table, e.g., raw_metrics, one_hour_metrics
+ */
+ function Table(name) {
+ // The name of the table
+ this.name = name;
+ // The size of the partition index component
+ this.partitionIndex = 0;
+ // A Data object that corresponds to the Data component
+ this.data = null;
+ }
+
+ /**
+ * Encapsulates sizing info for the Data component
+ */
+ function Data() {
+ // A map of RowInfo objects. Keys are collection intervals while values are the RowInfo
+ // objects. This mapping allows us to see what footprint on disk a particular collection
+ // interval will have.
+ this.rows = {};
+ // The overall total size of the Data component.
+ this.totalSize = 0;
+ }
+
+ /**
+ * In order to calculate the data component size, we need to know the number of rows and
+ * columns. The number of rows is the number of schedules. The number of columns is
+ * determined by the collection interval. Intermediate (per-row) results are grouped by
+ * collection interval.
+ *
+ * @param schedules
+ * @param columnSize
+ * @param rowKeySize
+ * @param duration
+ * @returns data
+ */
+ var calculateData = function(schedules, columnSize, rowKeySize, duration) {
+ var data = new Data();
+
+ util.foreach(schedules, function(interval, numSchedules) {
+ var rowInfo = new RowInfo();
+ rowInfo.numColumns = duration / interval;
+ rowInfo.rowSize = (rowInfo.numColumns * columnSize) + rowKeySize;
+ rowInfo.numRows = numSchedules;
+ rowInfo.totalSize = rowInfo.rowSize * rowInfo.numRows;
+
+ data.rows[interval] = rowInfo;
+ data.totalSize = data.totalSize + rowInfo.totalSize;
+ });
+
+ return data;
+ };
+
+ var calculatePartitionIndex = function(keySize, rowSize, columnNameSize, numRows) {
+ var keyLength = 2;
+ var position = 8;
+ var promotedSize = 4;
+
+ if (rowSize < 65536) {
+ return (keyLength + keySize + position + promotedSize) * numRows;
+ }
+
+ var localDeletionTime = 4;
+ var markedForDeleteAt = 8;
+ var columnIndexSize =4;
+
+ // compute index entry size
+ var firstNameLen = 2;
+ var firstName = columnNameSize;
+ var lastNameLen = 2;
+ var lastName = columnNameSize;
+ var offset = 8;
+ var width = 8;
+ var columnIndexEntry = firstNameLen + firstName + lastNameLen + lastName + offset + width;
+
+ var numColumnIndexEntries = Math.ceil(rowSize / 65536);
+
+ return (keyLength + keySize + position + promotedSize + localDeletionTime + markedForDeleteAt + columnIndexSize +
+ (numColumnIndexEntries * columnIndexEntry)) * numRows;
+ };
+
+ var calculateMetricsTableSize = function(name, columnSize, rowKeySize, rowKeyValueSize, columnNameSize, duration,
+ schedules) {
+ var table = new Table(name);
+ table.data = calculateData(schedules, columnSize, rowKeySize, duration);
+
+ util.foreach(table.data.rows, function(interval) {
+ var rowInfo = table.data.rows[interval];
+ table.partitionIndex += calculatePartitionIndex(rowKeyValueSize, rowInfo.rowSize, columnNameSize, rowInfo.numRows);
+ });
+
+ return table;
+ };
+
+ var calculateAggregatesTableSize = function(name, duration, schedules) {
+ // Here is the break down the of the column size for aggregate metrics tables
+ //
+ // date component - 11 (see org.apache.cassandra.db.marshal.CompositeType for more details on byte encoding
+ // type component - 7 byte encoding of composite types. In short, there are an extran 3 bytes of overhead
+ // column name length - 2 per component.)
+ // flags - 1
+ // TTL - 4
+ // deletion time - 4
+ // timestamp - 8
+ // column value length - 4
+ // column value - 8
+ var columnSize = 49;
+
+ // See the rowKeySize for rawMetrics. The byte-wise break down is the same.
+ var rowKeySize = 30;
+
+ var columnNameSize = 18;
+ var rowKeyValueSize = 4;
+
+ // We have to multiply the column size by 3 because there are 3 values (i.e., columns)
+ // per aggregate metric.
+ return calculateMetricsTableSize(name, columnSize * 3, rowKeySize, rowKeyValueSize, columnNameSize, duration,
+ schedules);
+ };
+
+ exports.loadSchedules = function() {
+ var criteria = MeasurementScheduleCriteria();
+ criteria.addFilterEnabled(true);
+ criteria.addSortId(PageOrdering.ASC);
+ criteria.setPaging(0, 500);
+
+ var schedulesSummary = {};
+
+ util.foreach(criteria, function(schedule) {
+ var count = schedulesSummary[schedule.interval];
+ if (count == null) {
+ schedulesSummary[schedule.interval] = 1;
+ } else {
+ schedulesSummary[schedule.interval] = count + 1;
+ }
+ });
+
+ return schedulesSummary;
+ };
+
+ exports.time = new Time();
+
+ /**
+ * Calculates SSTable component files sizes for the raw_metrics table for a set of schedules.
+ *
+ * @param schedules A map of collection intervals to the number of schedules for each
+ * interval. It is assumed that counts include only enabled schedules.
+ *
+ * @returns Table
+ */
+ exports.sizeOfRawMetrics = function(schedules) {
+ // Here is a byte-wise break down for the overall column size
+ //
+ // column name length - 2
+ // column name - 8
+ // flags - 1
+ // TTL - 4
+ // local deletion time - 4
+ // timestmap - 8
+ // column value length - 8
+ // column value - 8
+ var columnSize = 39;
+
+ //Here is a byte-wise break down for the row key. Note that this is the total row key
+ // overhead, not just the length of the key itself.
+ //
+ // key length - 2
+ // key value - 4
+ // columns size - 8
+ // local deletion time - 4
+ // marked for delete at - 8
+ // column count - 4
+ var rowKeySize = 30;
+
+ var rowKeyValueSize = 4;
+ var columnNameSize = 8;
+
+ return calculateMetricsTableSize('raw_metrics', columnSize, rowKeySize, rowKeyValueSize, columnNameSize, time.week,
+ schedules);
+ };
+
+ /**
+ * Calculates SSTable component files sizes for the one_hour_metrics table for a set of schedules. Note that this
+ * method is exposed right now primarily for testing. Moreover, it is assumed that the schedules argument is not the
+ * same as the one that would be passed to the sizeOfRawMetrics function.
+ *
+ * @param schedules A map of collection intervals to the number of schedules for each
+ * interval. It is assumed that counts include only enabled schedules.
+ *
+ * @returns Table
+ */
+ exports.sizeOf1HourMetrics = function(schedules) {
+ return calculateAggregatesTableSize('one_hour_metrics', time.day * 7, schedules);
+ };
+
+ /**
+ * Calculates SSTable component files sizes for the six_hour_metrics table for a set of schedules. Note that this
+ * method is exposed right now primarily for testing. Moreover, it is assumed that the schedules argument is not the
+ * same as the one that would be passed to the sizeOfRawMetrics function.
+ *
+ * @param schedules A map of collection intervals to the number of schedules for each
+ * interval. It is assumed that counts include only enabled schedules.
+ *
+ * @returns Table
+ */
+ exports.sizeOf6HourMetrics = function(schedules) {
+ return calculateAggregatesTableSize('six_hour_metrics', time.day * 31, schedules);
+ };
+
+ /**
+ * Calculates SSTable component files sizes for the twenty_four_hour_metrics table for a set of schedules. Note that
+ * this method is exposed right now primarily for testing. Moreover, it is assumed that the schedules argument is not
+ * the same as the one that would be passed to the sizeOfRawMetrics function.
+ *
+ * @param schedules A map of collection intervals to the number of schedules for each
+ * interval. It is assumed that counts include only enabled schedules.
+ *
+ * @returns Table
+ */
+ exports.sizeOf24HourMetrics = function(schedules) {
+ return calculateAggregatesTableSize('twenty_four_hour_metrics', time.day * 365, schedules);
+ };
+})();
\ No newline at end of file
commit f18d8e3d9467bbfb9da8b4afc86789d392cea803
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 10 16:02:27 2013 +0100
Bug 991202 - Excessive warnings of invalid metric calls
Fixed "*-exceeded" metrics dataType
Created new MemoryPoolComponent
Now gathering "usage-threshold-*" and "collection-usage-threshold-*" metrics only if enabled
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/MemoryPoolComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/MemoryPoolComponent.java
new file mode 100644
index 0000000..36939a5
--- /dev/null
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/MemoryPoolComponent.java
@@ -0,0 +1,88 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.modules.plugins.jbossas7;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.rhq.core.domain.measurement.MeasurementDataTrait;
+import org.rhq.core.domain.measurement.MeasurementReport;
+import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
+import org.rhq.core.pluginapi.inventory.ResourceContext;
+
+/**
+ * @author Thomas Segismont
+ */
+public class MemoryPoolComponent extends BaseComponent<BaseComponent<?>> {
+
+ private static final String USAGE_THRESHOLD_PREFIX = "usage-threshold-";
+ private static final String USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE = USAGE_THRESHOLD_PREFIX + "supported";
+ private static final String COLLECTION_USAGE_THRESHOLD_PREFIX = "collection-" + USAGE_THRESHOLD_PREFIX;
+ private static final String COLLECTION_USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE = COLLECTION_USAGE_THRESHOLD_PREFIX
+ + "supported";
+
+ private Boolean usageThresholdSupported;
+ private Boolean collectionUsageThresholdSupported;
+
+ @Override
+ public void start(ResourceContext<BaseComponent<?>> context) throws Exception {
+ super.start(context);
+ usageThresholdSupported = readAttribute(getAddress(), USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE, Boolean.class);
+ collectionUsageThresholdSupported = readAttribute(getAddress(), COLLECTION_USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE,
+ Boolean.class);
+ }
+
+ @Override
+ public void stop() {
+ super.stop();
+ usageThresholdSupported = null;
+ collectionUsageThresholdSupported = null;
+ }
+
+ @Override
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
+ Set<MeasurementScheduleRequest> filteredMetrics = new HashSet<MeasurementScheduleRequest>();
+ for (MeasurementScheduleRequest request : metrics) {
+ String requestName = request.getName();
+ if (USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE.equals(requestName)) {
+ report.addData(new MeasurementDataTrait(request, usageThresholdSupported.toString()));
+ continue;
+ }
+ if (requestName.startsWith(USAGE_THRESHOLD_PREFIX)) {
+ if (usageThresholdSupported) {
+ filteredMetrics.add(request);
+ }
+ continue;
+ }
+ if (COLLECTION_USAGE_THRESHOLD_SUPPORTED_ATTRIBUTE.equals(requestName)) {
+ report.addData(new MeasurementDataTrait(request, collectionUsageThresholdSupported.toString()));
+ continue;
+ }
+ if (requestName.startsWith(COLLECTION_USAGE_THRESHOLD_PREFIX)) {
+ if (collectionUsageThresholdSupported) {
+ filteredMetrics.add(request);
+ }
+ continue;
+ }
+ filteredMetrics.add(request);
+ }
+ super.getValues(report, filteredMetrics);
+ }
+}
diff --git a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
index 2dcf2bd..972e31f 100644
--- a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml
@@ -7280,7 +7280,7 @@
<service name="Memory Pool Resource"
discovery="SubsystemDiscovery"
- class="BaseComponent"
+ class="MemoryPoolComponent"
description="The management interface for a memory pool. A memory pool represents the memory resource managed by the Java virtual machine and is managed by one or more memory managers.">
<plugin-configuration>
@@ -7299,7 +7299,7 @@
<metric property="collection-usage:committed" displayName="Collection Usage - Committed" description="The amount of memory in bytes that is committed for the Java virtual machine to use."/>
<metric property="collection-usage:max" displayName="Collection Usage - Max" description="The maximum amount of memory in bytes that can be used for memory management."/>
<metric property="collection-usage-threshold-count" description="The number of times that the Java virtual machine has detected that the memory usage has reached or exceeded the collection usage threshold. A memory pool may not support a collection usage threshold. If 'collection-usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
- <metric property="collection-usage-threshold-exceeded" description="Whether the memory usage of this memory pool after the most recent collection on which the Java virtual machine has expended effort has reached or exceeded its collection usage threshold. A memory pool may not support a collection usage threshold. If 'collection-usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
+ <metric property="collection-usage-threshold-exceeded" dataType="trait" description="Whether the memory usage of this memory pool after the most recent collection on which the Java virtual machine has expended effort has reached or exceeded its collection usage threshold. A memory pool may not support a collection usage threshold. If 'collection-usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
<metric property="collection-usage-threshold-supported" dataType="trait" description="Whether this memory pool supports a collection usage threshold."/>
<metric property="name" dataType="trait" description="The name representing this memory pool."/>
<metric property="peak-usage:init" displayName="Peak Usage - Init" description="The amount of memory in bytes that the Java virtual machine initially requests from the operating system for memory management."/>
@@ -7312,7 +7312,7 @@
<metric property="usage:committed" displayName="Usage - Committed" description="The amount of memory in bytes that is committed for the Java virtual machine to use."/>
<metric property="usage:max" displayName="Usage - Max" description="The maximum amount of memory in bytes that can be used for memory management."/>
<metric property="usage-threshold-count" description="The number of times that the memory usage has crossed the usage threshold. A memory pool may not support a usage threshold. If 'usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
- <metric property="usage-threshold-exceeded" description="Whether the memory usage of this memory pool reaches or exceeds its usage threshold value. A memory pool may not support a usage threshold. If 'usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
+ <metric property="usage-threshold-exceeded" dataType="trait" description="Whether the memory usage of this memory pool reaches or exceeds its usage threshold value. A memory pool may not support a usage threshold. If 'usage-threshold-supported', is 'false' trying to read this attribute via the 'read-attribute' operation will result in failure, and the value of this attribute in the result of a 'read-resource' operation will be 'undefined'."/>
<metric property="usage-threshold-supported" dataType="trait" description="Whether this memory pool supports usage threshold."/>
<metric property="valid" dataType="trait" description="Whether this memory pool is valid in the Java virtual machine. A memory pool becomes invalid once the Java virtual machine removes it from the memory system."/>
commit f796666a12533b74e3a1218125751a9b617177a2
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 10 15:33:28 2013 +0100
Further updated translations
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_de.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_de.properties
index 50e343f..2670220 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_de.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_de.properties
@@ -528,7 +528,7 @@ view_aboutBox_version=Version
view_adminConfig_agentPlugins=Agent Plugins
view_adminConfig_alertDefTemplates=Vorlagen fÃŒr Alarmdefinitionen
view_adminConfig_downloads=Downloads
-view_adminConfig_driftDefTemplates=Vorlagen fÃŒr Dritf-Definitionen
+view_adminConfig_driftDefTemplates=Vorlagen fÃŒr Drift-Definitionen
view_adminConfig_ignoreResourceTypes=Ignorierte Ressource Typen
view_adminConfig_ignoreResourceTypes_changeTitle=Ãndern?
view_adminConfig_ignoreResourceTypes_confirmIgnore=Sind Sie sicher, dass Sie den Ressourcentyp [{0}] ignorieren wollen? Sie werden nicht mehr in der Lage sein, Ressourcen dieses Typs zu importieren und alle Ressourcen dieses Typs, die bereits im Inventar sind, werden aus dem Inventar entfernt und Sie können diese nicht mehr verwalten.
@@ -554,11 +554,13 @@ view_adminRoles_assignedSubjects=Zugewisene Benutzer
view_adminRoles_failCreateRoleWithExistingName=Konnte die Rolle mit dem existierenden Namen [{0}] nicht anlegen. Bitte wÀhlen Sie einen anderen Namen.
view_adminRoles_failLdap=Konnte nicht ermitteln, ob LDAP konfiguriert ist - gehe von keiner LDAP-Konfiguration aus.
view_adminRoles_failLdapAvailableGroups=Fehlgeschlagen\: Kann Status fÃŒr letzten AvailableGroups() Aufruf nicht abrufen.
+view_adminRoles_failLdapCancelling=Client kann nicht fortfahren. Storniere alle zukÃŒnftigen verfÃŒgbaren GruppenstatusprÃŒfungen fÃŒr diesen Thread. Wahrscheinlich ist ein Fehler bei der 1)LDAP Server Kommunikation oder der 2)ldap AnfrageprÃŒfung.
# #view_adminRoles_failLdapAvailableGroups = Konnte de LDAP-Gruppen nicht laden. Annahme ist, dass es keine gibt.
# #view_adminRoles_failLdapCancelling = Konnte de LDAP-Gruppen nicht laden. Annahme ist, dass es keine gibt.
view_adminRoles_failLdapGroups=Konnte de LDAP-Gruppen nicht laden. Annahme ist, dass es keine gibt.
view_adminRoles_failLdapGroupsRole=Konnte die LDAP-Gruppen fÃŒr die Rolle nicht laden.
view_adminRoles_failLdapGroupsSettings=Konnte Systemeinstellungsinformationen fÃŒr LDAP-Gruppen nicht abrufen.
+view_adminRoles_failLdapRetry=Versuch 3 Mal wiederholt. Storniere zukÃŒnftige verfÃŒgbare Gruppenanfragen.
# #view_adminRoles_failLdapGroupsSettings = Konnte de LDAP-Gruppen nicht laden. Annahme ist, dass es keine gibt.
# #view_adminRoles_failLdapRetry = Konnte de LDAP-Gruppen nicht laden. Annahme ist, dass es keine gibt.
view_adminRoles_failRoles=Konnte die Rollen nicht laden.
@@ -584,6 +586,11 @@ view_adminRoles_ldapWarnTooManyResults=Es werden viele Ergebnisse wiedergegeben.
view_adminRoles_noLdap=Die LDAP-Integration ist nicht konfiguriert. Um LDAP zu konfigurieren, wechseln sie zu <a {0}>{1}</a>.
view_adminRoles_permissions_autoselecting_configureRead_implied=Autodeselected CONFIGURE_WRITE permission, since lack of CONFIGURE_READ implies lack of it...
view_adminRoles_permissions_autoselecting_configureWrite_implied=Autoselected CONFIGURE_READ permission, since CONFIGURE_WRITE implies it...
+view_adminRoles_permissions_autoselecting_manageBundleGroups_implied=Autoselect von View Bundles (Bundles ansehen), das von Manage Bundle Groups (Bundle Gruppen verwalten) gewÀhrt wird...
+view_adminRoles_permissions_autoselecting_manageBundle_implied=Autoselect nicht gewÀhlter Berechtigungen, da Manage Bundle (Bundle verwalten) Berechtigung die Berechtigungen Manage Bundle Groups (Bundle Gruppen verwalten), Create Bundles (Bundles erstellen), Delete Bundles (Bundles löschen), View Bundles (Bundles ansehen) und Deploy_Bundles (Bundles deployen) gewÀhrt...
+view_adminRoles_permissions_autoselecting_manageInventory_implied=Autoselect von nicht gewÀhlten Ressourcen Berechtigungen, da MANAGE_INVENTORY alle Ressourcen Berechtigungen voraussetzt...
+view_adminRoles_permissions_autoselecting_manageSecurity_implied=Autoselect von nicht gewÀhlten Berechtigungen, da MANAGE_SECURITY alle anderen Berechtigungen voraussetzt...
+view_adminRoles_permissions_bundlePermissions=Bundle Berechtigungen
# #view_adminRoles_permissions_autoselecting_configureRead_implied = Autodeselected CONFIGURE_WRITE permission, since lack of CONFIGURE_READ implies lack of it...
# #view_adminRoles_permissions_autoselecting_configureWrite_implied = Autoselected CONFIGURE_READ permission, since CONFIGURE_WRITE implies it...
# #view_adminRoles_permissions_autoselecting_manageBundleGroups_implied = Autoselected View Bundles, which is granted by Manage Bundle Groups...
@@ -592,6 +599,11 @@ view_adminRoles_permissions_autoselecting_configureWrite_implied=Autoselected CO
# #view_adminRoles_permissions_autoselecting_manageSecurity_implied = Autoselected unselected permissions, since MANAGE_SECURITY implies all other permissions...
# #view_adminRoles_permissions_bundlePermissions = Bundle Permissions
view_adminRoles_permissions_globalPermissions=Globale Rechte
+view_adminRoles_permissions_illegalDeselectionDueToCorrespondingWritePermSelection=Auswahl der {0} Leseberechtigung kann nicht aufgehoben werden, wenn nicht die Auswahl der {0} Schreibberechtigung (die die Leseberechtigung voraussetzt), zuerst aufgehoben wird.
+view_adminRoles_permissions_illegalDeselectionDueToManageBundleGroupsSelection=Auswahl der {0} Berechtigung kann nicht aufgehoben werden, wenn nicht die Auswahl von Manage Bundle Groups (Bundle Gruppen verwalten) (das die {0} Berechtigung voraussetzt), zuerst aufgehoben wird.
+view_adminRoles_permissions_illegalDeselectionDueToManageBundleSelection=Auswahl der {0} Berechtigung kann nicht aufgehoben werden, wenn nicht die Auswahl von Manage Bundle (Bundle verwalten) (das alle Bundle Berechtigungen voraussetzt), zuerst aufgehoben wird.
+view_adminRoles_permissions_illegalDeselectionDueToManageInventorySelection=Auswahl der {0} Berechtigung kann nicht aufgehoben werden, wenn nicht die Auswahl von Manage Inventory (Inventar verwalten) (das alle Ressourcen Berechtigungen voraussetzt), zuerst aufgehoben wird.
+view_adminRoles_permissions_illegalDeselectionDueToManageSecuritySelection=Auswahl der {0} Berechtigung kann nicht aufgehoben werden, wenn nicht die Auswahl von Manage Security Permission (Sicherheitsberechtigung verwalten) (die alle anderen Berechtigungen voraussetzt), zuerst aufgehoben wird.
# #view_adminRoles_permissions_illegalDeselectionDueToCorrespondingWritePermSelection = {0} read permission cannot be deselected, unless the {0} write permission, which implies the read permission, is deselected first.
# #view_adminRoles_permissions_illegalDeselectionDueToManageBundleGroupsSelection = {0} permission cannot be deselected, unless Manage Bundle Groups, which implies {0} permission, is deselected first.
# #view_adminRoles_permissions_illegalDeselectionDueToManageBundleSelection = {0} permission cannot be deselected, unless Manage Bundle, which implies all Bundle permissions, is deselected first.
@@ -600,6 +612,15 @@ view_adminRoles_permissions_globalPermissions=Globale Rechte
view_adminRoles_permissions_isAuthorized=Berechtigt?
view_adminRoles_permissions_isRead=Lesen?
view_adminRoles_permissions_isWrite=Schreiben?
+view_adminRoles_permissions_permDesc_assignBundlesToGroup=Kann sichtbare Bundles in die Bundle Gruppe kopieren
+view_adminRoles_permissions_permDesc_createBundles=kann neue Bundle [Version]en erstellen. Kann sichtbare Bundles sichtbaren Gruppen zuweisen
+view_adminRoles_permissions_permDesc_createBundlesInGroup=kann neue Bundle [Version]en fÃŒr die Bundle Gruppe erstellen. Kann sichtbare Bundles in die Bundle Gruppe kopieren.
+view_adminRoles_permissions_permDesc_deleteBundles=kann sichtbare Bundle [Version]en löschen oder Zuweisung aufheben
+view_adminRoles_permissions_permDesc_deleteBundlesFromGroup=kann Bundle [Version]en aus der Bundle Gruppe löschen (diese werden implizit aus anderen zugewiesenen Gruppen gelöscht)
+view_adminRoles_permissions_permDesc_deployBundles=kann beliebige sichtbare Bundle Version an beliebige sichtbare, deploybare, kompatible Ressourcengruppe deployen
+view_adminRoles_permissions_permDesc_deployBundlesToGroup=kann beliebige sichtbare Bundle Version an die sichtbare, deploybare, kompatible Ressourcengruppe deployen
+view_adminRoles_permissions_permDesc_manageBundleGroups=kann Bundle Gruppen erstellen und löschen. Kann Bundles Bundle Gruppen zuweisen. GewÀhrt View Bundles (Bundles ansehen) Berechtigungen
+view_adminRoles_permissions_permDesc_manageBundles=kann beliebige Bundle Aufgabe durchfÌhren. Eine Berechtigung, die Autoselect nicht gewÀhlter Berechtigungen, die Berechtigungen Manage Bundle Groups (Bundle Gruppen verwalten), Create Bundles (Bundles erstellen), Delete Bundles (Bundles löschen), View Bundles (Bundles ansehen) und Deploy_Bundles (Bundles deployen) gewÀhrt...
# #view_adminRoles_permissions_permDesc_assignBundlesToGroup = can copy a viewable bundle to the bundle group
# #view_adminRoles_permissions_permDesc_createBundles = can create new bundle [version]s. can assign viewable bundles to viewable groups
# #view_adminRoles_permissions_permDesc_createBundlesInGroup = can create new bundle [version]s for the bundle group. can copy a viewable bundle to the bundle group.
@@ -610,9 +631,14 @@ view_adminRoles_permissions_isWrite=Schreiben?
# #view_adminRoles_permissions_permDesc_manageBundleGroups = can create and delete bundle groups. can assign bundles to bundle groups. grants View Bundles permissions
# #view_adminRoles_permissions_permDesc_manageBundles = can perform any bundle task. a convenience permission that grants Manage Bundle Groups, Create Bundles, Delete Bundles, Deploy Bundles and View Bundles permissions.
view_adminRoles_permissions_permDesc_manageInventory=Hat alle Rechte auf alle Ressourcen, wie unten beschrieben. Kann Gruppen anlegen, aktualisieren und löschen. Kann Ressourcen in das Inventar aufnehmen.
+view_adminRoles_permissions_permDesc_manageRepositories=kann Repositories jedes Benutzers erstellen, aktualisieren oder löschen (jeder kann seine eigenen Repositories erstellen), kann Content Quellen mit Repositories assoziieren.
# #view_adminRoles_permissions_permDesc_manageRepositories = can create, update, or delete repositories of any user (everyone can create their own repositories), can associate content sources to repositories.
view_adminRoles_permissions_permDesc_manageSecurity=Kann Benutzer und Rollen anlegen, aktualisieren oder löschen (Anschauen ist fÌr alle implizit erlaubt)
view_adminRoles_permissions_permDesc_manageSettings=Kann die Konfiguration des {0}-Servers Àndern und jegliche Server-bezogene FunktionalitÀt ausfÌhren.
+view_adminRoles_permissions_permDesc_unassignBundlesFromGroup=kann die Zuweisung eines Bundles zu einer Bundle Gruppe aufheben (nicht löschen)
+view_adminRoles_permissions_permDesc_viewBundles=kann Bundle Details, Deployments, u.s.w. fÃŒr jedes Bundle ansehen, einschlieÃlich nicht zugewiesener Bundles (die keiner Bundle Gruppe zugewiesen sind)
+view_adminRoles_permissions_permDesc_viewBundlesInGroup=(VORAUSGESETZT) kann Bundle Details, Deployments, u.s.w. fÃŒr jedes Bundle in Bundle Gruppen ansehen, die mit den relevanten Rollen assoziiert sind.
+view_adminRoles_permissions_permDesc_viewUsers=kann andere Benutzer ansehen, mit Ausnahme deren zugewiesener Rollen
# #view_adminRoles_permissions_permDesc_unassignBundlesFromGroup = can unassign (not delete) a bundle from the bundle group
# #view_adminRoles_permissions_permDesc_viewBundles = can view bundle details, deployments, etc for any bundle, including unassigned bundles (those not assigned to any bundle group)
# #view_adminRoles_permissions_permDesc_viewBundlesInGroup = (IMPLIED) can view bundle details, deployments, etc for any bundle in bundle groups associated with the relevant roles.
@@ -641,12 +667,15 @@ view_adminRoles_permissions_perm_assignBundlesToGroup=Bundles zu Gruppen zuweise
view_adminRoles_permissions_perm_configure=Konfiguration
view_adminRoles_permissions_perm_control=Operationen
view_adminRoles_permissions_perm_createBundles=Bundles anlegen
+view_adminRoles_permissions_perm_createBundlesInGroup=Bundles in Gruppe erstellen
# #view_adminRoles_permissions_perm_createBundlesInGroup = Create Bundles In Group
view_adminRoles_permissions_perm_createChildResources=Kind-Ressourcen erzeugen
view_adminRoles_permissions_perm_deleteBundles=Bundles löschen
+view_adminRoles_permissions_perm_deleteBundlesFromGroup=Bundles aus der Gruppe löschen
# #view_adminRoles_permissions_perm_deleteBundlesFromGroup = Delete Bundles From Group
view_adminRoles_permissions_perm_deleteChildResources=Löschen von Kind-Ressourcen
view_adminRoles_permissions_perm_deployBundles=Bundles deployen
+view_adminRoles_permissions_perm_deployBundlesToGroup=Bundles in die Gruppe deployen
# #view_adminRoles_permissions_perm_deployBundlesToGroup = Deploy Bundles To Group
view_adminRoles_permissions_perm_inventory=Inventar
view_adminRoles_permissions_perm_manageAlerts=Alarme verwalten
@@ -676,34 +705,74 @@ view_adminRoles_roleUpdateFailed=Konnte die Rolle [{0}] nicht aktualisieren.
view_adminRoles_roleUpdated=Rolle [{0}] aktualisiert.
view_adminTemplates_definedBy=DDefiniert durch
view_adminTemplates_disabledTemplates=Inaktive Vorlagen
+view_adminTemplates_editAlertTemplate=Alarm Vorlage bearbeiten
view_adminTemplates_editTemplates=Vorlagen bearbeiten
view_adminTemplates_enabledTemplates=Aktive Vorlagen
+view_adminTemplates_platformServices=Plattform Dienste
view_adminTemplates_platforms=Platformen
view_adminTemplates_pluginTemplates=Vorlagen fÃŒr Plugins Plugins
+view_adminTemplates_prompt_disabledAlertTemplates=Anzahl von Alarm Vorlagen an diesem Ressourcentyp, die erstellt aber deaktiviert sind
+view_adminTemplates_prompt_disabledMetricTemplates=Anzahl von metrischen ZeitplÀnen, die standardmÀÃig an diesem Ressourcentyp deaktiviert sind
+view_adminTemplates_prompt_enabledAlertTemplates=Anzahl von Alarm Vorlagen an diesem Ressourcentyp, die aktiviert sind
+view_adminTemplates_prompt_enabledMetricTemplates=Anzahl von metrischen ZeitplÀnen, die standardmÀÃig an diesem Ressourcentyp aktiviert sind
view_adminTemplates_servers=Server
view_adminTemplates_userTemplates=Nutzers-spezifische Vorlagen
view_adminTopology_affinityGroups=AffinitÀtsgruppen
view_adminTopology_affinityGroups_agentCount=Anzahl Agenten
view_adminTopology_affinityGroups_agentsInThisGroup=Agenten in dieser Gruppe
+view_adminTopology_affinityGroups_agentsNotPartOfAnAffinityGroup=Agenten, die nicht Teil einer Affinity Gruppe sind
+view_adminTopology_affinityGroups_agetnMembers=Agent Mitglieder
# #view_adminTopology_affinityGroups_agentsNotPartOfAnAffinityGroup = Agents not Part of an Affinity Group
# #view_adminTopology_affinityGroups_agetnMembers = Agent Members
view_adminTopology_affinityGroups_createNew=Neu anlegen
+view_adminTopology_affinityGroups_details=Affinity Gruppen Details
# #view_adminTopology_affinityGroups_details = Affinity Group Details
view_adminTopology_affinityGroups_removeSelected=AusgewÀhlte entfernen
view_adminTopology_affinityGroups_serverCount=Anzahl Server
+view_adminTopology_affinityGroups_serverMembers=Server Mitglieder
# #view_adminTopology_affinityGroups_serverMembers = Server Members
view_adminTopology_agentDetail_address=Adresse
+view_adminTopology_agentDetail_agentFailoverList=Agent Failover Liste
# #view_adminTopology_agentDetail_agentFailoverList = Agent Failover List
view_adminTopology_agentDetail_currentServer=Aktueller Server
+view_adminTopology_agentDetail_remoteEndpoint=Remote Endpunkt
+view_adminTopology_agentDetail_token=Token
+view_adminTopology_agent_agentBindAddress=Agent Bind Adresse
+view_adminTopology_agent_agentBindPort=Agent Bind Port
# #view_adminTopology_agentDetail_token = Token
# #view_adminTopology_agent_agentBindAddress = Agent Bind Address
# #view_adminTopology_agent_agentBindPort = Agent Bind Port
view_adminTopology_agent_agentName=Name des Agenten
view_adminTopology_agent_connectedServer=Verbundener Server
+view_adminTopology_agent_delete_confirm=Dies hebt die Registrierung der gewÀhlten Agenten auf und entfernt deren korrespondierende Plattformen und alle mit ihnen assoziierten Ressourcen aus dem Inventar. Dieser Vorgang kann nicht rÌckgÀngig gemacht werden. Sind Sie sicher, dass Sie dies tun möchten?
+view_adminTopology_agent_delete_error=Anfrage fÌr das Löschen der ausgewÀhlten Agenten ist fehlgeschlagen
+view_adminTopology_agent_delete_submitted=[{0}] Agenten gelöscht und alle ihre assoziierten Ressourcen dem Inventar entnommen.\n
+view_adminTopology_agent_lastAvailabilityPing=Letzter VerfÃŒgbarkeitsbericht-Ping
+view_adminTopology_agent_lastAvailabilityReport=Letzter VerfÃŒgbarkeitsbericht
# #view_adminTopology_agent_delete_confirm = This will deregister the selected agents and uninventory their corresponding platforms and all other resources associated with them. There is no way to undo this action. Are you sure you want to do this?
# #view_adminTopology_agent_lastAvailabilityPing = Last Availability Ping
# #view_adminTopology_agent_lastAvailabilityReport = Last Availability Report
view_adminTopology_agents=Agenten
+view_adminTopology_message_agentsCount=Es sind {0} Agenten bei diesem Server registriert. Diese Anzahl stimmt nicht mit den derzeit verbundenen Agenten ÃŒberein.
+view_adminTopology_message_agroupAssingAgentsFail=Kann Agenten nicht der Affinity Gruppe mit ID {0} zuweisen.
+view_adminTopology_message_agroupAssingServersFail=Kann Server nicht der Affinity Gruppe mit ID {0} zuweisen.
+view_adminTopology_message_agroupRemovingAgentsFail=Kann Agenten nicht aus der Affinity Gruppe mit ID {0} entfernen.
+view_adminTopology_message_agroupRemovingServersFail=Kann Server nicht aus der Affinity Gruppe mit ID {0} entfernen.
+view_adminTopology_message_agroupRenamed=Affinity Gruppe mit ID {0} und Namen {1} wurde umbenannt in {2}.
+view_adminTopology_message_agroupRenamingFail=Kann Affinity Gruppe mit ID {0} und Namen {1} nicht umbenennen.
+view_adminTopology_message_fetchAgentFail=Kann Agent Details fÃŒr Agent mit ID {0} nicht abrufen.
+view_adminTopology_message_fetchAgents2Fail=Kann Agenten nicht abrufen.
+view_adminTopology_message_fetchAgentsFail=Kann Agent(en) fÃŒr Affinity Gruppe mit ID {0} nicht abrufen.
+view_adminTopology_message_fetchAgroupFail=Kann Affinity Gruppen Details mit ID {0} nicht abrufen.
+view_adminTopology_message_fetchAgroupsFail=Kann Affinity Gruppe(n) nicht abrufen.
+view_adminTopology_message_fetchFailOverLists=Kann Fail Over Listen Details nicht abrufen.
+view_adminTopology_message_fetchPEventDetailsFail=Kann Partitionsereignisdetails fÃŒr Ereignis mit ID {0} nicht abrufen.
+view_adminTopology_message_fetchPEventFail=Kann Partitionsereignisse nicht abrufen.
+view_adminTopology_message_fetchServerFail=Kann Serverdetails fÃŒr Server mit ID {0} nicht abrufen.
+view_adminTopology_message_fetchServers2Fail=Kann Server nicht abrufen.
+view_adminTopology_message_fetchServersFail=Kann Server fÃŒr Affinity Gruppe mit ID {0} nicht abrufen.
+view_adminTopology_message_forceRepartition=Möchten Sie eine Repartitionierung des gesamten Clusters wirklich erzwingen? It will force all the agents to connect to its most preferred server.
+view_adminTopology_message_forceRepartitionFail=Kann keine Repartitionierung ausfÃŒhren.
# #view_adminTopology_message_agentsCount = There are {0} agents registered to this server. This number doesn't correspond to the number of currently connected agents.
# #view_adminTopology_message_agroupAssingAgentsFail = Unable to assign agents to the affinity group with id {0}.
# #view_adminTopology_message_agroupAssingServersFail = Unable to assign servers to the affinity group with id {0}.
@@ -725,6 +794,22 @@ view_adminTopology_agents=Agenten
# #view_adminTopology_message_forceRepartition = Do you really want to force a repartition of whole cluster? It will force all the agents to connect to its most preferred server.
# #view_adminTopology_message_forceRepartitionFail = Unable to run repartition.
view_adminTopology_message_order=Reihenfolge
+view_adminTopology_message_removeAGroupsConfirm=Möchten Sie die folgenden Affinity Gruppen {0} wirklich entfernen?
+view_adminTopology_message_removeAGroupsFail=Kann die folgende(n) Affinity Gruppe(n){0} nicht entfernen.
+view_adminTopology_message_removeAllPEventConfirm=Möchten Sie wirklich alle Partitionsereignisse bereinigen?
+view_adminTopology_message_removePEventConfirm=Möchten Sie die folgenden Partitionsereignisse {0} wirklich entfernen?
+view_adminTopology_message_removePEventFail=Kann {0} Partitionsereignis(se) nicht entfernen.
+view_adminTopology_message_removeServerConfirm=Möchten Sie die Server {0} wirklich entfernen?
+view_adminTopology_message_removeServerFail=Kann {0} Server nicht entfernen.
+view_adminTopology_message_removedAGroups={0} Affinity Gruppe(n) entfernt.
+view_adminTopology_message_removedAllPEvent=Es wurden alle Partitionsereignisse bereinigt.
+view_adminTopology_message_removedAllPEventFail=Kann nicht alle Partitionsereignisse bereinigen.
+view_adminTopology_message_removedPEvent={0} Partitionsereignis(se) entfernt.
+view_adminTopology_message_removedServer={0} Server entfernt.
+view_adminTopology_message_repartitioned=Die Cluster Repartitionierung wurde erfolgreich aufgerufen.
+view_adminTopology_message_serverUpdateFail=Kann Server {0} nicht aktualisieren.
+view_adminTopology_message_serverUpdated=Der Server {0} wurde erfolgreich aktualisiert.
+view_adminTopology_message_setMode={0} Server auf {1} Modus eingestellt.
# #view_adminTopology_message_removeAGroupsConfirm = Do you really want to remove following affinity groups {0}?
# #view_adminTopology_message_removeAGroupsFail = Unable to remove following affinity groups(s) {0}.
# #view_adminTopology_message_removeAllPEventConfirm = Do you really want to purge all partition events?
@@ -744,12 +829,22 @@ view_adminTopology_message_order=Reihenfolge
view_adminTopology_message_setModeConfirm=Möchten Sie wirklich die Sever {0} in den Zustand {1} bringen?
view_adminTopology_message_setModeFail=Konnte die Server {0} nicht in den Zustand {1} bringen.
view_adminTopology_partitionEvents=Partitionierungs-Ereignisse
+view_adminTopology_partitionEventsDetail_agentAssignments=Agent Assignments
+view_adminTopology_partitionEventsDetail_agentAssignments_nothing=Es wurden keine Agenten als Ergebnis dieses Partitionsereignisses erneut zugewiesen
+view_adminTopology_partitionEventsDetail_eventDetails=Ereignis Details
+view_adminTopology_partitionEventsDetail_eventExecutionTime=Ereignis AusfÃŒhrungszeit
+view_adminTopology_partitionEventsDetail_eventType=Ereignistyp
# #view_adminTopology_partitionEventsDetail_agentAssignments = Agent Assignments
# #view_adminTopology_partitionEventsDetail_agentAssignments_nothing = No agents were reassigned as a result of this partition event
# #view_adminTopology_partitionEventsDetail_eventDetails = Event Details
# #view_adminTopology_partitionEventsDetail_eventExecutionTime = Event Execution Time
# #view_adminTopology_partitionEventsDetail_eventType = Event Type
view_adminTopology_partitionEvents_details=Details
+view_adminTopology_partitionEvents_detailsFilter=Filter fÃŒr Details
+view_adminTopology_partitionEvents_execStatusFilter=AusfÃŒhrungsstatus Filter
+view_adminTopology_partitionEvents_execTime=AusfÃŒhrungszeit
+view_adminTopology_partitionEvents_executionStatus=AusfÃŒhrungsstatus
+view_adminTopology_partitionEvents_forceRepartition=Repartitionierung erzwingen
# #view_adminTopology_partitionEvents_detailsFilter = Details Filter
# #view_adminTopology_partitionEvents_execStatusFilter = Execution Status Filter
# #view_adminTopology_partitionEvents_execTime = Execution Time
@@ -758,22 +853,33 @@ view_adminTopology_partitionEvents_details=Details
view_adminTopology_partitionEvents_initiatedBy=Initiiert durch
view_adminTopology_partitionEvents_purgeAll=Alle löschen
view_adminTopology_partitionEvents_type=Typ
+view_adminTopology_partitionEvents_typeFilter=Typ Filter
# #view_adminTopology_partitionEvents_typeFilter = Type Filter
view_adminTopology_remoteAgentInstall=Installation entfernter Agenten
+view_adminTopology_serverDetail_connectedAgents=Verbundene Agenten
+view_adminTopology_serverDetail_installationDate=Installationsdatum
+view_adminTopology_serverDetail_operationMode=Operationsmodus
+view_adminTopology_server_affinityGroup=Affinity Gruppe
# #view_adminTopology_serverDetail_connectedAgents = Connected Agents
# #view_adminTopology_serverDetail_installationDate = Installation Date
# #view_adminTopology_serverDetail_operationMode = Operation Mode
# #view_adminTopology_server_affinityGroup = Affinity Group
view_adminTopology_server_agentCount=Anzahl Agenten
+view_adminTopology_server_endpointAddress=Endpunkt-Adresse
+view_adminTopology_server_lastUpdateTime=Zeitpunkt der letzten Aktualisierung
# #view_adminTopology_server_endpointAddress = Endpoint Address
# #view_adminTopology_server_lastUpdateTime = Last Update Time
view_adminTopology_server_mode=Modus
+view_adminTopology_server_nonSecurePort=Ungesicherter Port
+view_adminTopology_server_removeSelected=Auswahl entfernen
+view_adminTopology_server_securePort=Gesicherter Port
# #view_adminTopology_server_nonSecurePort = Nonsecure Port
# #view_adminTopology_server_removeSelected = Remove Selected
# #view_adminTopology_server_securePort = Secure Port
view_adminTopology_server_setMaintenance=Wartung setzen
view_adminTopology_server_setNormal=Normalbetrieb setzen
view_adminTopology_servers=Server
+view_adminTopology_storageNode_jmxConnectionUrl=JMX Verbindung URL
# #view_adminTopology_storageNode_jmxConnectionUrl = JMX Connection URL
view_adminTopology_storageNodes=Speicherknoten
view_adminUsersDetails_dataTypeName=Benutzer
@@ -783,11 +889,18 @@ view_admin_configuration=Konfiguration
view_admin_content=Inhalte
view_admin_downloads_agentDownload=Agent Download
view_admin_downloads_agent_buildNumber=Build-Nummer des Agents
+view_admin_downloads_agent_help=<p> Dies ist die {0} Agent Update Binary jar Datei. Diese jar Datei soll Ihnen die Installation eines frischen Agenten an einer Maschine ermöglichen, an der noch kein Agent vorhanden ist und Ihnen auÃerdem die Aktualisierung eines bereits an einer Maschine installierten Agenten ermöglichen. FÃŒr weitere Informationen fÃŒhren Sie diese Agent Download jar mit der --help Befehlszeilenoption aus\:<br/> <b>java -jar <agent-download.jar> --help</b> </p> <h3>Agent Install</h3> <p> <b>java -jar <agent-download.jar> --install[\=<new agent directory>]</b><br/> Dieser Befehl installiert einen neuen Agent. Falls Sie das neue Agentverzeichnis nicht festlegen, so lautet der Standard "." </p> <h3>Agent Update</h3> <p> <b>java -jar <agent-download.jar> --update[\=<old agent home>]</b><br/> Dies aktualisiert einen vorhandenen Agent, der bereits installiert war. Falls Sie das Verzeichnis nicht festlegen, in dem der altevorhande
ne Agent installiert war, so wird davon ausgegangen, dass es "rhq-agent" ist. </p>
+view_admin_downloads_agent_link_value=Agent {0} ({1}) herunterladen
+view_admin_downloads_agent_loadError=Erhalt von Agenten Versionsinfo nicht möglich
# #view_admin_downloads_agent_help = <p> This is the {0} Agent Update Binary jar file. The purpose of this jar file is to allow you to install a fresh agent on a machine where an agent does not yet exist and to allow you to update an agent that is already installed on a machine. For more details, run this agent download jar with the --help command line option:<br/> <b>java -jar <agent-download.jar> --help</b> </p> <h3>Agent Install</h3> <p> <b>java -jar <agent-download.jar> --install[=<new agent directory>]</b><br/> This command will install a new agent. If you do not specify the new agent directory, the default will be "." </p> <h3>Agent Update</h3> <p> <b>java -jar <agent-download.jar> --update[=<old agent home>]</b><br/> This will update an existing agent that was already installed. If you do not specify the directory where the old, existing agent was installed, it will assumed to be "rhq-agent". </p>
# #view_admin_downloads_agent_link_value = Download Agent {0} ({1})
# #view_admin_downloads_agent_loadError = Cannot get agent version info
view_admin_downloads_agent_md5=MD5 PrÃŒfsumme des Agents
view_admin_downloads_agent_version=Version des Agents
+view_admin_downloads_bundleDownload=Bundle Deployer Download
+view_admin_downloads_bundle_help=<p> Dies ist das Bundle Deployer Tool. Es dient der Verwendung durch Entwickler und Verpacker von {0} Bundles. Dieses eigenstÀndige Tool das Testen Ihrer Bundles und deren Anleitungen von einer Konsole. </p>
+view_admin_downloads_bundle_link_value=Bundle Deployer {0} herunterladen
+view_admin_downloads_bundle_loadError=Erhalt von Bundle Deployer Info nicht möglich
# #view_admin_downloads_bundleDownload = Bundle Deployer Download
# #view_admin_downloads_bundle_help = <p> This is the Bundle Deployer tool. It is for use by developers and packagers of {0} bundles. This standalone tool allows you to test your bundles and their recipes from a console. </p>
# #view_admin_downloads_bundle_link_value = Download Bundle Deployer {0}
@@ -796,6 +909,11 @@ view_admin_downloads_cliAlertScriptsDownload=Download von CLI-Alarm-Skripten
view_admin_downloads_cliAlertScripts_help=CLI-Alarm-Skripte sind vorefertigete Skripte, die als Vorlagen fÌr die Erstellung von Alert-Skripten genutzt werden können. Die Skripte benötigen Ìblicherweise einiger Anpassungen bevore sie zur Benachrichtigun fÌr eine spezifische Alarm-Definition genutzt werden können.
view_admin_downloads_cliAlertScripts_loadError=Kann keine Informationen ÃŒber CLI-Alarm-Skripte fÃŒr den Download lesen
view_admin_downloads_cliAlertScripts_none=Es liegen keine CLI-Alarm-Skripte fÃŒr den Download vor
+view_admin_downloads_cliDownload=Befehlszeilen Client Download
+view_admin_downloads_cli_buildNumber=CLI Build
+view_admin_downloads_cli_help=<p> Dies ist das Befehlszeilen Client Tool, auch unter dem Namen CLI bekannt. Es handelt sich um ein eigenstÀndiges Tool das von innerhalb einer Konsole lÀuft und ein Befehölszeilen Interface fÌr den {0} Server bereitstellt. Sie können Befehle Ìber das CLI aufrufen und Skripte fÌr die DurchfÌhrung automatisierter Aufgaben ausfÌhren. In der Dokumentation finden Sie weitere Informationen zur Installation und Verwendung des CLI. </p>
+view_admin_downloads_cli_link_value=CLI {0} ({1}) herunterladen
+view_admin_downloads_cli_loadError=Erhalt von CLI Versionsinfo nicht möglich
# #view_admin_downloads_cliDownload = Command Line Client Download
# #view_admin_downloads_cli_buildNumber = CLI Build
# #view_admin_downloads_cli_help = <p> This is the Command Line Client tool, otherwise known as the CLI. It is a standalone tool that runs from within a console and provides a command line interface to the {0} Server. You can invoke commands via the CLI as well as run scripts to perform automated tasks. See the documentation for more information on how to install and use the CLI. </p>
@@ -803,6 +921,14 @@ view_admin_downloads_cliAlertScripts_none=Es liegen keine CLI-Alarm-Skripte fÃŒr
# #view_admin_downloads_cli_loadError = Cannot get CLI version info
view_admin_downloads_cli_md5=MD5 PrÃŒfsumme des CLI
view_admin_downloads_cli_version=Version des CLI
+view_admin_downloads_connectorsDownload=Konnektoren Download
+view_admin_downloads_connectors_help=Bei Konnektoren handelt es sich um Software die erforderlich ist, damit einige Produkte durch {0} verwaltet werden können. Konnektoren werden in einige verwaltete Produkte installiert, damit {0} Agenten mit diesen kommunizieren können. Weitere Informationen finden Sie in der Dokumentation.
+view_admin_downloads_connectors_loadError=Erhalt von Konnektoren Info nicht möglich
+view_admin_downloads_connectors_none=Keine Konnektoren zum Download verfÃŒgbar
+view_admin_downloads_scriptModulesDownload=Skript Module Download
+view_admin_downloads_scriptModules_help=<p> Skript Module sind wiederverwendbare Komponenten, die von der RHQ bereitgestellt werden, um in Ihren Skripten verwendet zu werden (entweder Alarm Skripte oder CLI Skripte). Sie können mittels des "rhq\://downloads/<module-name-without-file-extension>" URI in der Skriptsprache Ihrer Wahl darauf zugreifen (d.h. in javascript wÌrden Sie die "require"-Funktion verwenden).</p>
+view_admin_downloads_scriptModules_loadError=Kann die Liste von verfÃŒgbaren Skriptmodulen nicht laden
+view_admin_downloads_scriptModules_none=Keine Skriptmodule zum Download verfÃŒgbar
# #view_admin_downloads_connectorsDownload = Connectors Download
# #view_admin_downloads_connectors_help = Connectors are software that is needed in order for some products to be manageable by {0}. You install connectors into some managed products so {0} agents can talk to them. See the documentation for more information.
# #view_admin_downloads_connectors_loadError = Cannot get connectors info
@@ -813,6 +939,7 @@ view_admin_downloads_cli_version=Version des CLI
# #view_admin_downloads_scriptModules_none = No script modules are available for download
view_admin_landing=In dieser Sektion können die globalen Einstellungen fÃŒr {0} verwaltet werden. Dies schlieÃt Sicherheitseinstellungen und Plugins ein, sowie die Verwaltung der {0} Server- und Agentplugins.
view_admin_measTemplates_updateExisting_title=Existierende ZeitplÀne aktualisieren
+view_admin_measTemplates_updateExisting_tooltip=Markieren Sie dieses KÀstchen, um die Collection ZeitplÀne fÌr die gewÀhlten Metriken an allen Ressourcen dieses Typs zu aktualisieren. Ist es nicht markiert, so werden die VorlagenzeitplÀne nur an neuen Ressourcen dieses Typs angewendet, die zu einem spÀteren Zeitpunkt dem Inventar hinzugefÌgt werden.
# #view_admin_measTemplates_updateExisting_tooltip = Check this box to update the collection schedules for the selected metrics on all existing resources of this type. If this is not checked, the template schedules will only be applied to new resources of this type that are added to inventory in the future.
view_admin_plugins_agent=Agent
view_admin_plugins_agentDeleteConfirm=<b>Achtung\!</b><br/>\nDie folgenden Agent-Plugins werden gelöscht\:<br/>\n{0}<br/>\nSind Sie sicher, dass sie diese löschen wollen?
@@ -835,6 +962,10 @@ view_admin_plugins_purgedAgentPlugins=Das AufrÀumen der Agent-Plugins {0} wird
view_admin_plugins_purgedAgentPluginsFailure=Konnte die Agent-Plugins nicht entfernen
view_admin_plugins_purgedServerPlugins=Server-Plugins {0} entfernt
view_admin_plugins_purgedServerPluginsFailure=Konnte die Server-Plugins nicht entfernen
+view_admin_plugins_restartMasterPC=Master Plugin Container neu starten
+view_admin_plugins_restartMasterPCComplete=Master Plugin Container wurde neu gestartet.
+view_admin_plugins_restartMasterPCFailure=Neustart von Master Plugin Container fehlgeschlagen
+view_admin_plugins_restartMasterPCStarted=Starte Master Plugin Container neu...
# #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
@@ -847,9 +978,12 @@ view_admin_plugins_serverConfig=Plugin-Konfiguration
view_admin_plugins_serverConfig_badSettings=Bitte geben Sie gÃŒltige Daten ein
view_admin_plugins_serverConfig_saveFailed=Konnte die Einstellungen nicht speichern
view_admin_plugins_serverConfig_settingsSaved=Die Einstellungen wurden gespeichert
+view_admin_plugins_serverControls=Steuerungen
# #view_admin_plugins_serverControls = Controls
view_admin_plugins_serverControls_badParams=Bitte geben Sie gÃŒltige Parameter an
view_admin_plugins_serverControls_clickForError=Klicken, um die Fehlermeldung anzuzeigen
+view_admin_plugins_serverControls_invokeFailure=Aufruf von Steuerung fehlgeschlagen
+view_admin_plugins_serverControls_name=Steuerung
# #view_admin_plugins_serverControls_invokeFailure = Failed to invoke the control
# #view_admin_plugins_serverControls_name = Control
view_admin_plugins_serverControls_parameters=Parameter
@@ -858,27 +992,95 @@ view_admin_plugins_serverDisableConfirm=<b>Achtung\!</b><br/>\nDie folgenden Ser
view_admin_plugins_serverScheduleJobs=Geplante Aufgaben
view_admin_plugins_serverUndeployConfirm=<b>Achtung\!</b><br/>\nDie folgenden Server-Plugins werden gelöscht\:<br/>\n{0}<br/>\nSind Sie sicher, dass diese gelöscht werden sollen?
view_admin_plugins_showDeleted=Gelöschte anzeigen
+view_admin_plugins_showUndeployed=Undeployte anzeigen
+view_admin_plugins_undeploy=Undeployen
+view_admin_plugins_undeployedServerPlugins=Undeployte Server Plugins\: {0}
+view_admin_plugins_undeployedServerPluginsFailure=Konnte die Server-Plugins nicht undeployen.
# #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=Plugin hochladen
view_admin_security=Sicherheit
+view_admin_systemSettings_ActiveDriftServerPlugin_desc=Der Drift Server Plugin der die Persistenz von Drift-bezogenen Entities und Content verwaltet.
# #view_admin_systemSettings_ActiveDriftServerPlugin_desc = The drift server plugin that manages the persistence of drift-related entities and content.
view_admin_systemSettings_ActiveDriftServerPlugin_name=Aktives Drift-Server-Plugin
+view_admin_systemSettings_AgentMaxQuietTimeAllowed_desc=Wenn diese Zeit ohne Reaktion vom Agent verlÀuft, so wird dieser stille Agent als auÃer Betrieb angesehen. Der Wert ist in Minuten festgelegt.
+view_admin_systemSettings_AgentMaxQuietTimeAllowed_name=Agent Max Ruhezeit gestattet
+view_admin_systemSettings_AlertPurge_desc=Wie alt Verlaufselemente sein mÃŒssen, ehe sie aus der Datenbank bereinigt werden. Dies ist in Tagen festgelegt.
view_admin_systemSettings_AlertPurge_name=Bereinigen der Alarme, die Àlter sind als
+view_admin_systemSettings_AvailabilityPurge_desc=Wie alt VerfÃŒgbarkeitsdaten sein mÃŒssen, ehe sie aus der Datenbank bereinigt werden. Dies ist in Tagen festgelegt.
view_admin_systemSettings_AvailabilityPurge_name=Lösche VerfÌgbarkeitsdaten, die Àlter sind als
+view_admin_systemSettings_BaseURL_desc=Eine URL zum Server GUI, die hauptsÀchlich innerhalb von Alarm E-Mail Benachrichtigungen verwendet wird.
+view_admin_systemSettings_BaseURL_name=GUI Konsole URL
+view_admin_systemSettings_BaselineDataSet_desc=Die Menge an vergangenen Messdaten, die zur Bestimmung des Referenzbands verwendet werden. Dies ist in Tagen festgelegt.
+view_admin_systemSettings_BaselineDataSet_name=Referenzband Datensatz
+view_admin_systemSettings_BaselineFrequency_desc=Die HÀufigkeit mit der die auto-calculation (Selbstberechnung) von ReferenzbÀndern durchgefÌhrt wird. Falls 0, so ist die Referenzband auto-calculation deaktiviert. Dies ist in Tagen festgelegt. Maximalwert ist 'Baseline Dataset' (Referenzband Datensatz).
+view_admin_systemSettings_BaselineFrequency_name=Referenzband BerechnungshÀufigkeit
+view_admin_systemSettings_DataMaintenance_desc=Wie oft die Datenbankwartung durchgefÃŒhrt wird (zum Beispiel Vacuuming bei Verwendung von Postgres). Dies wird in Stunden festgelegt.
+view_admin_systemSettings_DataMaintenance_name=Datenbankwartungszeitraum
+view_admin_systemSettings_DataReindex_desc=Wenn aktiviert, so werden bestimmte Datenbanken periodisch neu indiziert.
+view_admin_systemSettings_DataReindex_name=Datentabellen jede Nacht neu indizieren
+view_admin_systemSettings_DriftFilePurge_desc=Wie alt nicht benutzte und verwaiste Drift Dateien sein mÃŒssen, ehe sie aus dem Backend-Speicher bereinigt werden. Dies ist in Tagen festgelegt.
# #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=Bereinige ungenutzte Drift-Dateine, die Àlter sind als
+view_admin_systemSettings_EnableAgentAutoUpdate_desc=Legt fest, ob der Server Agents ein auto-update (Selbstaktualisierung) gestattet. Ist dies deaktiviert, so können Sie keine Agent Distributionen vom Server herunterladen.
+view_admin_systemSettings_EnableAgentAutoUpdate_name=Agenten Auto-Updates aktivieren
+view_admin_systemSettings_EnableDebugMode_desc=Falls aktiviert, so startet der Server den Fehlerbehebungsmodus.
+view_admin_systemSettings_EnableDebugMode_name=Fehlerbehebungsmodus aktivieren
+view_admin_systemSettings_EnableExperimentalFeatures_desc=Falls aktiviert werden im aktuellen Produkt vorhandene experimentelle Features verfÃŒgbar.
+view_admin_systemSettings_EnableExperimentalFeatures_name=Experimentelle Features aktivieren
+view_admin_systemSettings_EventPurge_desc=Wie alt Ereignisdaten sein mÃŒssen, ehe sie aus der Datenbank bereinigt werden. Dies ist in Tagen festgelegt.
+view_admin_systemSettings_EventPurge_name=Ereignisse löschen, die Àlter sind als
+view_admin_systemSettings_JAASProvider_desc=Sollte zur Bestimmung der BenutzeridentitÀt LDAP verwendet werden?
+view_admin_systemSettings_JAASProvider_name=LDAP aktivieren
+view_admin_systemSettings_LDAPBaseDN_desc=Die Basis des Verzeichnisbaums zur Suche nach Benutzernamen und Passwörter werden der Authentifizierung von Benutzern, z.B. ou\=People,dc\=redhat,dc\=com
view_admin_systemSettings_LDAPBaseDN_name=Basis fÃŒr Suche
+view_admin_systemSettings_LDAPBindDN_desc=Der Benutzername zur Verbindung mit dem LDAP Server bei der Abfrage der LDAP Benutzerdatenbank. Dies ist in der Regel der vollstÀndige LDAP distinguierte Name (DN) eines Manager Benutzers, z.B. cn\=Manager,dc\=redhat,dc\=com
+view_admin_systemSettings_LDAPBindDN_name=Benutzername
+view_admin_systemSettings_LDAPBindPW_desc=Die Berechtigungsnachweise des Benutzers, die fÃŒr die Verbindung mit dem LDAP-Server bei der Abfrage der LDAP Benutzerdatenbank verwendet werden.
+view_admin_systemSettings_LDAPBindPW_name=Passwort
+view_admin_systemSettings_LDAPFilter_desc=Etwaige zusÀtzliche Filter, die bei der LDAP Suche angewendet werden sollen. Dies ist nÌtzlich, wenn die Population zur Authentifizierung Ìber eine gegebene LDAP Property identifiziert werden kann, z.B. RHQUser\=true
+view_admin_systemSettings_LDAPFilter_name=Suchfilter
+view_admin_systemSettings_LDAPGroupFilter_desc=LDAP Suchfilter, der alle fÃŒr die Autorisierung verfÃŒgbaren LDAP Gruppen wiedergeben muss. Dies wird fÃŒr LDAP Gruppenautorisierung verwendet.
+view_admin_systemSettings_LDAPGroupFilter_name=Gruppen Suchfilter
+view_admin_systemSettings_LDAPGroupMember_desc=LDAP Suchfilter der in Verbindung mit dem Gruppen Suchfilter zur Bestimmung der Benutzerberechtigung verwendet wird. Dies wird fÃŒr die LDAP Gruppenautorisierung verwendet.
+view_admin_systemSettings_LDAPGroupMember_name=Gruppen Mitgliederfilter
+view_admin_systemSettings_LDAPGroupPageSize_desc=Um RFC 2696 zu unterstÃŒtzen wird dieser LDAP Group Page Size Suchparameter in Verbindung mit anderen Gruppensuchparametern verwendet, um die Anzahl von mit jedem Abfragenergebnis wiedergegebene Anzahl von Gruppenmitgliedern einzuschrÀnken. Dieser Wert sollte so hoch wie möglich eingestellt werden und zum Serverseiten maximalen SeitengröÃenwert um die besten Ergebnisse zu erhalten. Values > (server max page size) hat keine Auswirkung. Verringern Sie diesen Wert wenn LDAP Gruppenanfragen zu lange fÃŒr die Wiedergabe brauchen. Der Standard sind 1000 Ergebnisse.
+view_admin_systemSettings_LDAPGroupPageSize_name=Group Search Page Size (Gruppen Suche Seiten GröÃe)
+view_admin_systemSettings_LDAPGroupUsePaging_desc=Definiert, ob LDAP Gruppenanfragen die "Simple Paged Results" FunktionalitÀt (wie von RFC 2696 definiert) verwenden sollten. Insbesondere bei die Anfrageergebnisse einschrÀnkenden LDAP Servern wird der Client Seite fÃŒr Seite alle Ergebnisse in der Group Page Size (GruppenseitengröÃe) durchgehen, bis er fertig ist. Standardeinstellung ist false.
+view_admin_systemSettings_LDAPGroupUsePaging_name=Verwendung von Group Query Paging
+view_admin_systemSettings_LDAPGroupUsePosixGroup_desc=Definiert, ob PosixGroup Semantik fÃŒr die GruppenmitgliedschaftsprÃŒfung verwendet werden soll. Mit PosixGroups ist der Group Member Filter (Gruppenmitgliedschaftsfilter) in der Regel auf 'memberUid' und der Group Filter (Gruppenfilter) auf 'objectclass\=posixGroup' eingestellt.
+view_admin_systemSettings_LDAPGroupUsePosixGroup_name=Ist PosixGroup
+view_admin_systemSettings_LDAPLoginProperty_desc=Die LDAP Eigenschaft, die den Benutzernamen enthÀlt. Die Standardeinstellung ist "cn". Werden mehrere Ãbereinstimmungen gefunden, so wird der erste Eintrag verwendet.
+view_admin_systemSettings_LDAPLoginProperty_name=Login Eigenschaft
+view_admin_systemSettings_LDAPProtocol_desc=Soll die Kommunikation mit dem LDAP Server ÃŒber SSL erfolgen?
+view_admin_systemSettings_LDAPProtocol_name=SSL
+view_admin_systemSettings_LDAPUrl_desc=URL zum LDAP Server
+view_admin_systemSettings_LDAPUrl_name=LDAP URL
+view_admin_systemSettings_RHQSessionTimeout_desc=Wenn diese Zeit ohne Benutzerinteraktion im Browser verlÀuft, so wird die Session als abgelaufen angesehen und der Benutzer wird um erneute Anmeldung gebeten. Der Wert ist in Stunden festgelegt.
+view_admin_systemSettings_RHQSessionTimeout_name=GUI Session Timeout
+view_admin_systemSettings_RtDataPurge_desc=Wie alt Antwort Zeitdaten sein mÃŒssen, ehe sie aus der Datenbank bereinigt werden. Dies ist in Tagen festgelegt.
+view_admin_systemSettings_RtDataPurge_name=Lösche Antwort Zeitdaten, die Àlter sind als
+view_admin_systemSettings_TraitPurge_desc=Wie alt Mess Merkmaldaten sein mÃŒssen, ehe sie aus der Datenbank bereinigt werden. Dies ist in Tagen festgelegt.
# #view_admin_systemSettings_RHQSessionTimeout_desc = If this amount of time passes without any user interaction in the browser, the session is considered as expired and user is aked to log in again. This value is specified in hours.
# #view_admin_systemSettings_RHQSessionTimeout_name = GUI Session Timeout
view_admin_systemSettings_TraitPurge_name=Bereinigen der Trait-Daten, die Àlter sind als
+view_admin_systemSettings_cannotLoadServerDetails=Kann Server Details nicht laden
+view_admin_systemSettings_cannotLoadSettings=Erhalt aktueller Systemeinstellungen nicht möglich
view_admin_systemSettings_dumpToLogFailed=Schreiben der Systeminformation in die Server-Log-Datei ist fehlgeschlagen
view_admin_systemSettings_dumpedToLog=System informationen wurden erfolgreich in die Server-Log-Datei geschrieben
+view_admin_systemSettings_fixBeforeSaving=Bitte korrigieren Sie ungÃŒltige Werte vor dem Speichern
+view_admin_systemSettings_group_baseline=Automatische Referenzband Konfigurationseigenschaften
+view_admin_systemSettings_group_dataMgr=Datenmanager Konfigurationseigenschaften
+view_admin_systemSettings_group_drift=Drift Server Konfigurationseigenschaften
+view_admin_systemSettings_group_general=Allgemeine Konfigurationseigenschaften
+view_admin_systemSettings_group_ldap=LDAP-Konfigurationseigenschaften
# #view_admin_systemSettings_group_drift = Drift Server Configuration Settings
view_admin_systemSettings_saveFailure=Das Speichern der Systemeinstellungen ist fehlgeschlagen
view_admin_systemSettings_savedSettings=Sie haben die Systemeinstellungen erfolgreich gespeichert
+view_admin_systemSettings_serverDetails=Server Details
+view_admin_systemSettings_serverDetails_buildNumber=Build-Nummer
view_admin_systemSettings_serverDetails_dbDriverName=Name des Datenbanktreibers
view_admin_systemSettings_serverDetails_dbDriverVersion=Version des Datenbanktreibers
view_admin_systemSettings_serverDetails_dbName=Produktname der Datenbank
@@ -892,6 +1094,7 @@ view_admin_systemSettings_serverDetails_tz=Zeitzone des Servers
view_admin_topology=Topologie
view_alert_common_tab_conditions=Bedingungen
view_alert_common_tab_conditions_expression=Alarm auslösen wenn
+view_alert_common_tab_conditions_expression_tooltip=Bestimmt ob ANY (eine) oder ALL (alle) Bedingungen als "true" evaluiert werden mÃŒssen, damit der gesamte Bedingungssatz als "true" angesehen wird.
# #view_alert_common_tab_conditions_expression_tooltip = Determines if ANY or ALL of the conditions must evaluate to true in order for the entire condition set to be considered true.
view_alert_common_tab_conditions_modalEdit_title=Bedingungen Àndern
view_alert_common_tab_conditions_modal_title=Bedingung hinzufÃŒgen
@@ -914,6 +1117,7 @@ view_alert_common_tab_conditions_type_metric_calltime_change_verb=um mindestens
view_alert_common_tab_conditions_type_metric_calltime_delta_grows=WÀchst
view_alert_common_tab_conditions_type_metric_calltime_delta_other=Ãndert sich
view_alert_common_tab_conditions_type_metric_calltime_delta_shrinks=Schrumpft
+view_alert_common_tab_conditions_type_metric_calltime_destination=mit Aufruf DestinationsÃŒbereinstimmung
# #view_alert_common_tab_conditions_type_metric_calltime_destination = with call destination matching
view_alert_common_tab_conditions_type_metric_calltime_threshold=Call-Time ÃŒberschreitet Schwellwert
view_alert_common_tab_conditions_type_metric_change=Wert der Metrik Àndert sich
@@ -923,6 +1127,7 @@ view_alert_common_tab_conditions_type_metric_range_outside_exclusive=Wertbereich
view_alert_common_tab_conditions_type_metric_range_outside_inclusive=Wertbereich der Metrik\: [{0}] ausserhalb [{1}] und [{2}], inklusiv
view_alert_common_tab_conditions_type_metric_threshold=Metrik ÃŒberschreitet Schwellwert
view_alert_common_tab_conditions_type_metric_trait_change=Trait-Ãnderung
+view_alert_common_tab_conditions_type_metric_trait_matching=mit Trait Wert Ãbereinstimmung
# #view_alert_common_tab_conditions_type_metric_trait_matching = with trait value matching
view_alert_common_tab_conditions_type_operation=AusfÃŒhrung der Operation
view_alert_common_tab_conditions_type_operation_status=mit Ergebnis-Status
@@ -947,6 +1152,8 @@ view_alert_common_tab_dampening_partial_evalatuions_label_tooltip=Anzahl wie oft
view_alert_common_tab_dampening_partial_occurrences_label=Anzahl Vorkommen
view_alert_common_tab_dampening_partial_occurrences_label_tooltip=Anzahl wie oft die Bedingungen innerhalb der letzten N Auswertungen wahr sein mÌssen, bevor der Alarm ausgelöst wird.
view_alert_common_tab_general=Allgemeine Eigenschaften
+view_alert_common_tab_invalid_condition_category=UngÃŒltige Bedingungskategorie - bitte berichten Sie diesen Fehler\: {0}
+view_alert_common_tab_invalid_dampening_category=UngÃŒltige Dampening Kategorie - bitte berichten Sie diesen Fehler\: {0}
# #view_alert_common_tab_invalid_condition_category = Invalid condition category - please report this as a bug: {0}
# #view_alert_common_tab_invalid_dampening_category = Invalid dampening category - please report this as a bug: {0}
view_alert_common_tab_invalid_time_units=UngÃŒltige Zeiteinheit - bitte berichten Sie diesen Fehler\: {0}
@@ -956,6 +1163,7 @@ view_alert_common_tab_notifications_sender=Sender
view_alert_common_tab_notifications_status=Status
view_alert_common_tab_recovery=Erholung
view_alert_definition_condition_editor_availabilityDuration=Dauer der VerfÃŒgbarkeit
+view_alert_definition_condition_editor_availabilityDuration_state=VerfÃŒgbarkeitsstatus
# #view_alert_definition_condition_editor_availabilityDuration_state = Availability State
view_alert_definition_condition_editor_availabilityDuration_tooltip=Geben Sie die Ãnderung der VerfÃŒgbarkeit und die LÀnge der Dauer des Zustandes an damit die Bedingung zutrifft. Die Dauer ist in Minuten und sollte lang genug sein (mehrere Minuten), um dem Agent Zeit zugeben eine potentielle Ãnderung des Zustands zu erkennen.
view_alert_definition_condition_editor_availabilityDuration_tooltip_duration=Die Anzahl der Minuten in der die Ressource die gegeben VerfÃŒgbarkeit haben muss, bevor die Bedingung zutrifft.
@@ -969,29 +1177,48 @@ view_alert_definition_condition_editor_common_min=Minimum
view_alert_definition_condition_editor_common_regex=RegulÀrer Ausdruck
view_alert_definition_condition_editor_delete_confirm=Die ausgewÀhlte(n) Alarm-Bedingung(en) löschen?
view_alert_definition_condition_editor_drift_configname_regex=Name der Drift-Definition
+view_alert_definition_condition_editor_drift_configname_regex_tooltip=Falls festgelegt ist dies der Drift Definitionsname, der fÃŒr den aufgespÃŒrten Drift verantwortlich war. Dies kann optional ein regulÀrer Ausdruck sein, falls Sie eine Ãbereinstimmung mit mehreren Drift Definitionsnamen haben möchten.
# #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=RegulÀrer Ausdruck fÌr den Pfadnamen
+view_alert_definition_condition_editor_drift_pathname_regex_tooltip=Falls festgelegt ist dies ein regulÀrer Ausdruck, der mit den Pfadnamen der Dateien mit Drift Ìbereinstimmen muss.
+view_alert_definition_condition_editor_drift_tooltip=Diese Bedingung wird ausgelöst, wenn Drift aufgespÌrt wurde.
# #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_event_regexTooltip=If specified, this is a regular expression that must match a collected event message in order to trigger the condition.
view_alert_definition_condition_editor_event_severity=Schwere des Ereignisses
+view_alert_definition_condition_editor_event_tooltip=Legen Sie eine Ereignisschwere fest, bei der eine Ereignismeldung gemeldet werden muss, um diese Bedingung auszulösen. Falls Sie einen optionalen regulÀren Audruck festlegen, muss die Ereignismeldung auch mit dem regulÀren Ausdruck Ìbereinstimmen, um die Bedingung auszulösen.
+view_alert_definition_condition_editor_metric_baseline_percentage=Referenzband Prozentsatz
+view_alert_definition_condition_editor_metric_baseline_percentage_tooltip=Ein gesammelter metrischer Wert löst diese Bedingung beim Vergleich mit diesem Prozentsatz des gewÀhlten Referenzbandswerts mittels des gewÀhlten Komparators aus.
+view_alert_definition_condition_editor_metric_baseline_tooltip=Legen Sie den Referenzbandwert fest, der verletzt werden muss, um die Bedingung auszulösen. Der von Ihnen festgelegte Wert ist ein Prozentsatz des gegebenen Referenzbandwerts.
# #view_alert_definition_condition_editor_event_tooltip = Specify the event severity that an event message must be reported with in order to trigger this condition. If you specify an optional regular expression, the event message must also match that regular expression in order for the condition to trigger.
# #view_alert_definition_condition_editor_metric_baseline_percentage = Baseline Percentage
# #view_alert_definition_condition_editor_metric_baseline_percentage_tooltip = A collected metric value will trigger this condition when compared to this percentage of the selected baseline value using the selected comparator
# #view_alert_definition_condition_editor_metric_baseline_tooltip = Specify the baseline value that must be violated to trigger the condition. The value you specify is a percentage of the given baseline value.
view_alert_definition_condition_editor_metric_baseline_value=Referenzband
view_alert_definition_condition_editor_metric_calltime_change_percentage=Ãnderung des Prozentsatzes
+view_alert_definition_condition_editor_metric_calltime_change_percentage_tooltip=Ein gesammelter Aufrufzeit Wert löst diese Bedingung aus, wenn er um mindestens diesen Prozentsatz vom gewÀhlten Aufrufzeit Grenzwert differiert.
+view_alert_definition_condition_editor_metric_calltime_change_tooltip=Legen Sie den Aufrufzeit Wert fest der die Bedingung auslöst, wenn er um mindestens eine festgelegte Menge verÀndert wird. Sie mÃŒssen festlegen, welche Aufrufzeit Grenze geprÃŒft werden soll (minimum, maximum oder durchschnittlicher Aufrufzeit Wert) und den Prozentsatz der Ãnderung.
# #view_alert_definition_condition_editor_metric_calltime_change_percentage_tooltip = A collected calltime value will trigger this condition when it differs by at least this percentage of the selected calltime limit value
# #view_alert_definition_condition_editor_metric_calltime_change_tooltip = Specify the calltime value that, when changed at least a specified amount, triggers the condition. You must specify which calltime limit to check (minimum, maximum or average calltime value) and the percentage of change that must occur.
view_alert_definition_condition_editor_metric_calltime_common_comparator=Komparator
view_alert_definition_condition_editor_metric_calltime_common_comparator_changes=Ãndert sich
view_alert_definition_condition_editor_metric_calltime_common_comparator_grows=WÀchst
view_alert_definition_condition_editor_metric_calltime_common_comparator_shrinks=Schrumpft
+view_alert_definition_condition_editor_metric_calltime_common_comparator_tooltip=Wie ein gesammelter Aufrufzeit Wert zur gegebenen Aufrufzeit Grenze verglichen werden soll
+view_alert_definition_condition_editor_metric_calltime_common_limit=Aufrufzeit Grenze
+view_alert_definition_condition_editor_metric_calltime_common_limit_tooltip=Der Aufrufzeit Grenzwert der mit dem gegebenen Wert verglichen werden soll
+view_alert_definition_condition_editor_metric_calltime_common_name=Aufrufzeit Metrik
# #view_alert_definition_condition_editor_metric_calltime_common_comparator_tooltip = How a collected calltime value should be compared to the given calltime limit
# #view_alert_definition_condition_editor_metric_calltime_common_limit = Call Time Limit
# #view_alert_definition_condition_editor_metric_calltime_common_limit_tooltip = The calltime limit value that is to be compared with the given value
# #view_alert_definition_condition_editor_metric_calltime_common_name = Call Time Metric
view_alert_definition_condition_editor_metric_calltime_regexTooltip=If specified, this is a regular expression that must match a call destination in order to trigger the condition.
+view_alert_definition_condition_editor_metric_calltime_threshold_tooltip=Legen Sie den Aufrufzeit-Schwellwert fest, der verletzt werden muss, um die Bedingung auszulösen. Der von Ihnen festgelegte Wert ist ein absoluter Wert, mit einem optionalen Einheiten Spezifikationssymbol. Sie mÌssen auch festlegen, mit welcher Aufrufzeitgrenze der Wert verglichen werden soll (minimum, maximum oder durchschnittlicher Aufrufzeit Wert).
+view_alert_definition_condition_editor_metric_calltime_threshold_value=Aufrufzeit Wert
+view_alert_definition_condition_editor_metric_calltime_threshold_value_tooltip=Der Schwellwert der Metrik, die beim Vergleich mittels des gewÀhlten Komparators die Bedingung auslöst.
+view_alert_definition_condition_editor_metric_change_tooltip=Legen Sie die Metrik fest, deren Wert sich Àndern muss, um die Bedingung auszulösen.
+view_alert_definition_condition_editor_metric_common_definition_not_found=HÀtte metrische Definition finden sollen - etwas stimmt nicht
+view_alert_definition_condition_editor_metric_nometrics=Bei Verwendung der ALL Verbindung können Sie nicht dieselbe Metrik in mehreren Bedingungen verwenden und dieser Alarm verwendet alle verfÌgbaren Metriken in aktuell vorhandenen Bedingungen.
# #view_alert_definition_condition_editor_metric_calltime_threshold_tooltip = Specify the calltime threshold value that, when violated, triggers the condition. The value you specify is an absolute value with an optional units specifier. You also must specify which calltime limit to compare the value with (minimum, maximum or average calltime value).
# #view_alert_definition_condition_editor_metric_calltime_threshold_value = Call Time Value
# #view_alert_definition_condition_editor_metric_calltime_threshold_value_tooltip = The threshold value of the metric that will trigger the condition when compared using the selected comparator.
@@ -1003,26 +1230,43 @@ view_alert_definition_condition_editor_metric_range_comparator_inside_exclusive=
view_alert_definition_condition_editor_metric_range_comparator_inside_inclusive=Innerhalb, inklusiv
view_alert_definition_condition_editor_metric_range_comparator_outside_exclusive=Ausserhalb, exklusiv
view_alert_definition_condition_editor_metric_range_comparator_outside_inclusive=Ausserhalb, inklusiv
+view_alert_definition_condition_editor_metric_range_comparator_tooltip=Legt fest, ob ein metrischer Wert diese Bedingung auslösen soll wenn der Bereich innerhalb oder auÃerhalb verlÀuft. Falls inbegriffen, so wird der Wert als im Bereich angesehen, wenn sein Wert einem Hoch- oder Tiefwert des Schwellwerts gleich ist. Falls exklusiv, so gilt der Wert als nicht im Bereich wenn er dem Hoch- oder Tiefwert gleich ist.
# #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=Oberer Schwellwert
view_alert_definition_condition_editor_metric_range_hivalue_tooltip=Der obere Schwellwert des Bereichs
view_alert_definition_condition_editor_metric_range_lovalue=Unterer Schwellwert
view_alert_definition_condition_editor_metric_range_lovalue_tooltip=Der untere Schwellwert des Bereichs
+view_alert_definition_condition_editor_metric_range_tooltip=Vergleicht einen metrischen Wert zu einem gegebenen Tief-/Hoch Bereichswert.
# #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=Komparator
view_alert_definition_condition_editor_metric_threshold_comparator_equal=Gleich
view_alert_definition_condition_editor_metric_threshold_comparator_greater=GröÃer als
view_alert_definition_condition_editor_metric_threshold_comparator_less=Kleiner als
+view_alert_definition_condition_editor_metric_threshold_comparator_tooltip=Wie ein gesammelter metrischer Wert mit dem gegebenen Schwellwert verglichen werden soll
# #view_alert_definition_condition_editor_metric_threshold_comparator_tooltip = How a collected metric value should be compared to the given threshold value
view_alert_definition_condition_editor_metric_threshold_name=Metrik
+view_alert_definition_condition_editor_metric_threshold_tooltip=Legen Sie den Schwellwert fest, der verletzt werden muss, um die Bedingung auszulösen. Der von Ihnen festgelegte Wert ist ein absoluter Wert, mit einem optionalen Einheiten Spezifikationssymbol.
# #view_alert_definition_condition_editor_metric_threshold_tooltip = Specify the threshold value that, when violated, triggers the condition. The value you specify is an absolute value with an optional units specifier.
view_alert_definition_condition_editor_metric_threshold_value=Wert der Metrik
+view_alert_definition_condition_editor_metric_threshold_value_tooltip=Der Schwellwert der Metrik, die beim Vergleich mittels des gewÀhlten Komparators die Bedingung auslöst.
+view_alert_definition_condition_editor_metric_trait_change_tooltip=Legen Sie das Merkmal fest, dessen Wert sich Àndern muss, um die Bedingung auszulösen.
+view_alert_definition_condition_editor_metric_trait_change_value=Merkmal
+view_alert_definition_condition_editor_metric_trait_regexTooltip=Falls festgelegt ist dies ein regulÀrer Ausdruck, der mit dem neuen Merkmalwert Ìbereinstimmen muss, um die Bedingung auszulösen.
+view_alert_definition_condition_editor_metricswarning=Sie können nicht mehrere Bedingungen haben, die dieselbe Metrik verwenden, wen Sie die ALL VerknÌpfung verwenden. Diese Alarm Definition besitzt mehrere Bedingungen, die die Metrik [{0}] verwenden.
# #view_alert_definition_condition_editor_metric_threshold_value_tooltip = The threshold value of the metric that will trigger the condition when compared using the selected comparator.
# #view_alert_definition_condition_editor_metric_trait_change_tooltip = Specify the trait whose value must change to trigger the condition.
# #view_alert_definition_condition_editor_metric_trait_change_value = Trait
# #view_alert_definition_condition_editor_metric_trait_regexTooltip = If specified, this is a regular expression that must match the new trait value in order to trigger the condition.
# #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_operation_status=Zustand der Operation
+view_alert_definition_condition_editor_operation_tooltip=Legen Sie das Ergebnis fest, das auftreten muss, wenn der gewÀhlte Vorgang ausgefÌhrt wird, damit die Bedingung ausgelöst wird.
+view_alert_definition_condition_editor_operator_availability_durationDown=Bleibt auÃer Betrieb
+view_alert_definition_condition_editor_operator_availability_durationNotUp=Bleibt nicht in Betrieb
+view_alert_definition_condition_editor_operator_availability_goesDisabled=Wird deaktiviert
+view_alert_definition_condition_editor_operator_availability_goesDown=Geht auÃer Betrieb
+view_alert_definition_condition_editor_operator_availability_goesNotUp=Geht nicht in Betrieb
+view_alert_definition_condition_editor_operator_availability_goesUnknown=Geht unbekannt
+view_alert_definition_condition_editor_operator_availability_goesUp=Geht in Betrieb
# #view_alert_definition_condition_editor_operation_tooltip = Specify the result that must occur when the selected operation is executed in order to trigger the condition.
view_alert_definition_condition_editor_option_availability=Ãnderung der VerfÃŒgbarkeit
view_alert_definition_condition_editor_option_drift=Erkennung von Drift
@@ -1030,6 +1274,7 @@ view_alert_definition_condition_editor_option_event=Erkennung von Ereignissen
view_alert_definition_condition_editor_option_label=Typ der Bedingung
view_alert_definition_condition_editor_option_metric_baseline=Schwelle des Referenzbands
view_alert_definition_condition_editor_option_metric_calltime_change=Ãnderung des Call-Time-Werts
+view_alert_definition_condition_editor_option_metric_calltime_threshold=Aufrufzeit Wert Schwellwert
# #view_alert_definition_condition_editor_option_metric_calltime_threshold = Call Time Value Threshold
view_alert_definition_condition_editor_option_metric_change=Wert der Metrik Àndert sich
view_alert_definition_condition_editor_option_metric_range=Wertebereich
@@ -1043,16 +1288,27 @@ view_alert_definition_for_group=Gruppendefinition ansehen
view_alert_definition_for_type=Vorlage ansehen
view_alert_definition_notification_cliScript_editor_anotherUser=Anderer Benutzer
view_alert_definition_notification_cliScript_editor_existingScript=Vorhandenes Skript
+view_alert_definition_notification_cliScript_editor_loadFailed=Konnte CLI Benachrichtigungs-Editor nicht laden.
+view_alert_definition_notification_cliScript_editor_newScriptVersion=Version
+view_alert_definition_notification_cliScript_editor_repository=Repository
view_alert_definition_notification_cliScript_editor_script=Skript
+view_alert_definition_notification_cliScript_editor_selectRepo=WÀhlen Sie das Repository, in dem das Skript gespeichert werden soll
+view_alert_definition_notification_cliScript_editor_selectRepoFirst=WÀhlen Sie zuerst ein Repository-Konfigurationstool
view_alert_definition_notification_cliScript_editor_thisUser=Aktueller Benutzer
+view_alert_definition_notification_cliScript_editor_uploadNewScript=Neues Skript hochladen
+view_alert_definition_notification_cliScript_editor_verifyAuthentication=Verifizieren
+view_alert_definition_notification_cliScript_editor_whichUser=Benutzer, als der das Skript ausgefÃŒhrt werden soll
view_alert_definition_notification_editor_delete_confirm=Sind Sie sicher, dass sie die ausgewÀhlten Alarm-Benachrichtigungen löschen wollen?
view_alert_definition_notification_editor_field_configuration=Konfiguration
view_alert_definition_notification_editor_field_configuration_loadFailed=Konte die Vorschau der Benachrichtigung nicht laden
view_alert_definition_notification_editor_field_configuration_not_loaded=Unbekannt
view_alert_definition_notification_editor_field_sender=Sender
view_alert_definition_notification_editor_loadFailed=Kann die Alarm-Sender nicht laden
+view_alert_definition_notification_editor_loadFailed_single=Erhalt von Alarm Sender Konfigurationsdefinition nicht möglich
# #view_alert_definition_notification_editor_loadFailed_single = Cannot get alert sender configuration definition
view_alert_definition_notification_editor_none_available=Keine Alarm-Sender verfÃŒgbar
+view_alert_definition_notification_editor_saveFailed=Kann die Benachrichtigungskonfiguration nicht speichern
+view_alert_definition_notification_editor_sender=Benachrichtigungssender
# #view_alert_definition_notification_editor_saveFailed = Cannot save the notification configuration
# #view_alert_definition_notification_editor_sender = Notification Sender
view_alert_definition_notification_editor_title_add=Benachrichtigung hinzufÃŒgen
@@ -1061,15 +1317,24 @@ view_alert_definition_notification_operation_editor_common_operation=Operation
view_alert_definition_notification_operation_editor_mode_relative=Relative Ressource
view_alert_definition_notification_operation_editor_mode_specific=Spezifische Ressource
view_alert_definition_notification_operation_editor_mode_this=Diese Ressource
+view_alert_definition_notification_operation_editor_mode_title=Ressourcen Auswahlmodus
# #view_alert_definition_notification_operation_editor_mode_title = Resource Selection Mode
view_alert_definition_notification_operation_editor_mode_unknown=Unbekannte Option - Berichten Sie diesen Fehler
+view_alert_definition_notification_operation_editor_operations_loadFailed=Konnte die Liste der verfÃŒgbaren Operationen nicht laden
+view_alert_definition_notification_operation_editor_operations_no_parameters=Diese Operation nimmt keine Parameter
# #view_alert_definition_notification_operation_editor_operations_loadFailed = Failed to load the list of available operations
# #view_alert_definition_notification_operation_editor_operations_no_parameters = This operation does not take any parameters
view_alert_definition_notification_operation_editor_relative_ancestor=Suche starten bei
+view_alert_definition_notification_operation_editor_relative_ancestor_loadFailed=Erhalt von Typ Vorfahren nicht möglich
+view_alert_definition_notification_operation_editor_relative_ancestor_root=Root Vorfahren Typ
+view_alert_definition_notification_operation_editor_relative_ancestor_tooltip=WÀhlen Sie die Spitze der Typenhierarchie aus, von der aus der Baum der Abkömmlinge fÌr den Filter nach Typ durchsucht werden soll
# #view_alert_definition_notification_operation_editor_relative_ancestor_loadFailed = Cannot get type ancestry
# #view_alert_definition_notification_operation_editor_relative_ancestor_root = Root Ancestor Type
# #view_alert_definition_notification_operation_editor_relative_ancestor_tooltip = Select the top of the type hierarchy from which to search its descedant tree for the Filter By type
view_alert_definition_notification_operation_editor_relative_descendant=dann filtern nach
+view_alert_definition_notification_operation_editor_relative_descendant_filter_tooltip=Ein spezifischer Name zur eindeutigen Identifizierung einer Ressource, wenn mehr als eine Ressource des gewÀhlten Typs vorhanden sein kann. Dies ist optional wenn stets nur eine Ressource des Ressourcentyps in der gewÀhlten Typhierarchie existiert.
+view_alert_definition_notification_operation_editor_relative_descendant_loadFailed=Erhalt von Typ Abkömmlingen nicht möglich
+view_alert_definition_notification_operation_editor_relative_descendant_tooltip=Der Ressourcentyp, nach dem unter dem in der Start Search From Auswahl (Suche starten von) definierten Root Typ gesucht werden soll.
# #view_alert_definition_notification_operation_editor_relative_descendant_filter_tooltip = A specific name to uniquely identify a resource when more than one resource of the selected type might exist. This is optional if there will only ever be one resource of the resource type in the selected type hierarchy.
# #view_alert_definition_notification_operation_editor_relative_descendant_loadFailed = Cannot get type descendants
# #view_alert_definition_notification_operation_editor_relative_descendant_tooltip = The resource type to search for under the root type defined in the Start Search From selection.
@@ -1085,6 +1350,8 @@ view_alert_definition_notification_user_editor_loadFailed=Kann die aktuellen Ben
view_alert_definition_notification_user_editor_restoreFailed=Kann die aktuellen Benutzer nicht verwenden - starte ohne
view_alert_definition_notification_user_editor_saveFailed=Kann die ausgewÀhlten Benutzer nicht sichern
view_alert_definition_recovery_editor_disable_when_fired=Nach dem Auslösen inaktiv schalten
+view_alert_definition_recovery_editor_disable_when_fired_tooltip=Gibt an, ob dieser Alarm nach dem feuern deaktiviert wird. Nach Deaktivierung kann der Alarm manuell wieder aktiviert werden oder ein Wiederherstellungsalarm kann eingestellt werden, damit er automatisch wieder aktiviert wird. Ist dieser Alarm selbst ein Wiederherstellungsalarm, so ist diese Einstellung nicht möglich.
+view_alert_definition_recovery_editor_loadFailed=Kann WiederhesrtsellungsmenÌ nicht erstellen
# #view_alert_definition_recovery_editor_disable_when_fired_tooltip = Indicates if this alert will be disabled after it fires. Once disabled, the alert can be manually re-enabled or a recovery alert can be set up to automatically re-enable it. If this alert is a recovery alert itself, this setting cannot be turned on.
# #view_alert_definition_recovery_editor_loadFailed = Cannot build recovery menu
view_alert_definition_recovery_editor_none_available=Keiner
@@ -1110,6 +1377,7 @@ view_alert_definitions_update_failure=Aktualisieren der Alarm-Definition fehlges
view_alert_definitions_update_success=Alarm-Definition erfolgreich aktualisiert
view_alert_details_field_ack_at=BestÀtigt um
view_alert_details_field_ack_by=BestÀtigt durch
+view_alert_details_field_parent_definition=Parent Definition
view_alert_details_field_recovery_info=Info zur Erholung
view_alert_details_field_resource_ancestry=Vorfahren der Ressource
view_alert_details_field_watched_resource=Beobachtere Ressource
@@ -1161,6 +1429,7 @@ view_autoDiscoveryQ_ignored=Ignoriert
view_autoDiscoveryQ_importFailure=Konnte die Ressourcen nicht importieren
view_autoDiscoveryQ_importInProgress=Die ausgewÀhlten Ressourcen werden in''s Inventar aufgenommen
view_autoDiscoveryQ_importSuccessful=Sie haben die ausgewÀhlten Ressourcen erfolgreich importiert
+view_autoDiscoveryQ_loadFailure=Konnte die Inventar Discovery Warteschlange nicht laden
# #view_autoDiscoveryQ_loadFailure = Failed to load the inventory discovery queue
view_autoDiscoveryQ_newAndIgnored=Neu und Ignoriert
view_autoDiscoveryQ_noperm=(Die erforderlichen "manage inventory" Rechte fehlen. Kontaktieren Sie den Administrator)
@@ -1169,8 +1438,17 @@ view_autoDiscoveryQ_showStatus=Zeige
view_autoDiscoveryQ_title=Autodiscovery-Warteschlange
view_autoDiscoveryQ_unignore=Ignorieren aufheben
view_autoDiscoveryQ_unignoreFailure=Konnte das Ignorieren fÃŒr die Ressourcen nicht aufheben.
+view_autoDiscoveryQ_unignoreInProgress=Hebe die Ignorierung der gewÀhlten Ressourcen auf...
view_autoDiscoveryQ_unignoreSuccessful=Sie haben erfolgreich das Ignorieren der ausgewÀhlten Ressourcen aufgehoben.
view_autoDiscoveryQ_uninventoried=Aus dem Inventory gelöscht
+view_bundleGroup_assignFailPerm=Sie sind nicht berechtigt, dieser Bundle Gruppe Bundles zuzuweisen. Bitte wenden Sie sich an Ihren Administrator.
+view_bundleGroup_deleteConfirm=Sind Sie sicher, dass Sie diese Bundle Gruppe löschen möchten? Bundles fÌr die dies die einzige zugewiesene Bundle Gruppe ist verlieren ihre Zurodnung und deren Ansicht erfordert eine allgemeine View Bundles (Bundles ansehen) Berechtigung.
+view_bundleGroup_deletesFailure=Konnte die Bundle Gruppen nicht löschen
+view_bundleGroup_deletesSuccessful=Sie haben die Bundle Gruppen erfolgreich gelöscht
+view_bundleGroup_unassignFailPerm=Sie sind nicht berechtigt, die Zurodnung von Bundles zu dieser Bundle Gruppe aufzuheben. Bitte wenden Sie sich an Ihren Administrator.
+view_bundleVersion_loadFailure=Konnte aktuellste Bundle Versionsdaten nicht laden
+view_bundle_bundleDeployment=Bundle Deployment
+view_bundle_bundleDeployments=Bundle Deployments
# #view_bundleGroup_assignFailPerm = You are not authorized to assign bundles to this bundle group. Please check with your administrator.
# #view_bundleGroup_deleteConfirm = Are you sure you want to delete this bundle group? Bundles for which this is the only assigned bundle group will become unassigned, and will require global View Bundles permission to view.
# #view_bundleGroup_deletesFailure = Failed to delete the bundle groups
@@ -1184,6 +1462,32 @@ view_bundle_bundleFiles=Bundle-Dateien
view_bundle_bundleType=Bundle-Type
view_bundle_bundleVersion=Bundle-Version
view_bundle_bundleVersions=Bundle-Versionen
+view_bundle_createWizard_bundleDistro=Bundle Distribution
+view_bundle_createWizard_cancelFailure=Konnte die Erstellung von Bundle [{0}], Version \= [{1}] nicht vollstÀndig abbrechen - das Bundle ist möglicherweise noch vorhanden.
+view_bundle_createWizard_cancelFailurePerm=Konnte die Erstellung von Bundle [{0}], Version \= [{1}] nicht vollstÀndig abbrechen da der Benutzer Berechtigung zur Erstellung, nicht aber fÌr das Löschen besitzt. Das Bundle muss wahrscheinlich von einem Administrator entfernt werden mÌssen.
+view_bundle_createWizard_cancelSuccessful=Erstellung von Bundle [{0}], Version \= [{1}] abgebrochen
+view_bundle_createWizard_clickToUploadRecipe=Zum Laden einer Anleitungsdatei klicken
+view_bundle_createWizard_createFailure=Konnte das Bundle nicht erstellen
+view_bundle_createWizard_createSuccessful=Sie haben erfolgreich ein Bundle [{0}] mit einer Version von [{1}] erstellt
+view_bundle_createWizard_enterRecipe=Bitte geben Sie eine gÃŒltige Anleitung ein
+view_bundle_createWizard_enterUrl=Bitte geben Sie eine gÃŒltige URL ein, von wo die Bundle Distribution Datei heruntergeladen werden kann
+view_bundle_createWizard_failedToUploadDistroFile=Konnte die Bundle Distribution Datei nicht hochladen
+view_bundle_createWizard_failedToUploadFile=Konnte die Bundle Datei nicht hochladen
+view_bundle_createWizard_groupsStep_assign=Weisen Sie mindestens eine der berechtigten Bundle Gruppen zu\:
+view_bundle_createWizard_groupsStep_assigned=Die neue Bundle Version ist fÃŒr ein vorhandenes Bundle und erbt seine Bundle Gruppen Zuweisungen\:
+view_bundle_createWizard_groupsStep_failedAssign=Zuordnung erster Bundle Gruppen an ein Bundle namens [{0}] mit einer Version von [{1}] fehlgeschlagen. Bitte beenden Sie den Erstellungs-Wizard und benachrichtigen Sie Ihren Administrator.
+view_bundle_createWizard_groupsStep_failedGetAssignable=Konnte zuweisbare Bundle Gruppen nicht bestimmen. Bitte beenden Sie den Erstellungs-Wizard und benachrichtigen Sie Ihren Administrator.
+view_bundle_createWizard_groupsStep_help=Ein neues Bundle wird erstellt, wenn die erste Version fÃŒr dieses Bundle hochgeladen wird. Das neue Bundle wird dann seinen ersten Bundle Gruppen zugewiesen. Ein Benutzer kann das neue Bundle nur Bundle Gruppen zuweisen, fÃŒr die er eine Create Bundles (Bundles erstellen) Berechtigung besitzt, entweder allgemein oder auf Bundle Gruppen Ebene. Es muss mindestens eine Bundle Gruppe zugewiesen sein, auÃer der Benutzer besitzt allgemeine Berechtigungen zur Erstellung und Ansicht von Bundles, in welchem Falle die Zuweisung ausbleiben kann.
+view_bundle_createWizard_groupsStep_leaveUnassigned=Das neue Bundle ohne Zuweisung lassen.
+view_bundle_createWizard_groupsStep_noAssignable=Konnte erste Bundle Version nicht erstellen, da Benutzer keine Bundle Gruppen besitzt zu denen eine Zuweisung erfolgen kann. Bitte beenden Sie den Erstellungs-Wizard und benachrichtigen Sie Ihren Administrator.
+view_bundle_createWizard_groupsStep_noneAssigned=Die neue Bundle Version muss mindestens einer Bundle Gruppe zugewiesen werden\!
+view_bundle_createWizard_groupsStep_radioTitle=Erste Bundle Gruppen Zuordnung fÃŒr das neue Bundle
+view_bundle_createWizard_groupsStep_successAssign=Sie haben erfolgreich erste Bundle Gruppen einem Bundle namens [{0}] mit einer Version von [{1}] zugeordnet
+view_bundle_createWizard_groupsStep_unassigned=Die neue Bundle Version ist fÃŒr ein vorhandenes Bundle und erbt seine Bundle Gruppen Zuweisungen. Das Bundle ist aktuell keinen Bundle Gruppen zugeordnet.
+view_bundle_createWizard_loadBundleFileFailure=Kann Kann Bundle Datei Informationen nicht vom Server abrufen
+view_bundle_createWizard_noAdditionalFilesNeeded=Es mÃŒssen keine weiteren Dateien fÃŒr dieses Bundle hochgeladen werden
+view_bundle_createWizard_noBundleTypesAvail=Es sind keine Bundle Typen verfÃŒgbar
+view_bundle_createWizard_noBundleTypesSupported=Es werden keine Bundle Typen unterstÃŒtzt - Sie mÃŒssen einen gÃŒltigen Plugin deployen, der Bundle Deployments unterstÃŒtzt
# #view_bundle_createWizard_bundleDistro = Bundle Distribution
# #view_bundle_createWizard_cancelFailure = Failed to fully cancel the creation of bundle [{0}], version=[{1}] - the bundle may still exist.
# #view_bundle_createWizard_cancelFailurePerm = Failed to fully cancel the creation of bundle [{0}], version = [{1}] because the user has create but not delete permissions. The bundle will likley need to be removed by an administrator.
@@ -1211,8 +1515,10 @@ view_bundle_bundleVersions=Bundle-Versionen
# #view_bundle_createWizard_noBundleTypesAvail = No bundle types are available
# #view_bundle_createWizard_noBundleTypesSupported = No bundle types are supported - you must deploy a valid plugin that supports bundle deployments
view_bundle_createWizard_provideBundleDistro=Stellen Sie eine Bundle-Distribution bereit
+view_bundle_createWizard_recipeOption=Anleitung
# #view_bundle_createWizard_recipeOption = Recipe
view_bundle_createWizard_title=Bundle anlegen
+view_bundle_createWizard_unassigned=nicht zugeordnet
# #view_bundle_createWizard_unassigned = unassigned
view_bundle_createWizard_uploadInProgress=Datei wird hochgeladen ... Dies kann fÃŒr groÃe Dateien mehrere Minuten dauern
view_bundle_createWizard_uploadOption=Hochladen
@@ -1223,9 +1529,13 @@ view_bundle_createWizard_urlTooltip=Benutzername und Password können fÌr HTTP
view_bundle_createWizard_urlUserName=Benutzername
view_bundle_createWizard_windowTitle=Assistent zum Anlegen von Bundles
view_bundle_createWizard_youMustChooseOne=Sie mÌssen eine Option auswÀhlen, um ein Bundle anlegen zu können\!
+view_bundle_deleteConfirm=Sind Sie sicher, dass Sie dieses Bundle löschen wollen? Alle Versionen, Destinationen und Deployments fÌr dieses Bundle werden ebenfalls gelöscht. Dies entfernt keine Inhalte von Remote-Maschinen.
# #view_bundle_deleteConfirm = Are you sure you want to delete this bundle? All versions, destinations and deployments for this bundle will also be deleted. However, this will not remove any content from remote machines.
view_bundle_deploy=Deploy
view_bundle_deployDir=Deploy-Verzeichnis
+view_bundle_deployWizard_createGroup_error_1=Die Gruppe wurde nicht erstellt. Gruppe fÃŒr Deployment darf nicht leer sein.
+view_bundle_deployWizard_createGroup_error_2=Die Gruppe wurde nicht erstellt. Die resultierende Gruppe muss kompatibel sein (Mitglieder desselben Typs).
+view_bundle_deployWizard_createGroup_error_3=Die Gruppe wurde nicht erstellt. Der Ressourcentyp der resultierenden Gruppe unterstÃŒtzt keine Deployments.
# #view_bundle_deployWizard_createGroup_error_1 = The group was not created. Group for deployment cannot be empty.
# #view_bundle_deployWizard_createGroup_error_2 = The group was not created. Resulting group must be compatible (members of the same type).
# #view_bundle_deployWizard_createGroup_error_3 = The group was not created. The resource type of the resulting group does not support deployments.
@@ -1233,7 +1543,24 @@ view_bundle_deployWizard_deployStep=Bundle auf die Zielplattformen deployen
view_bundle_deployWizard_deploying=Deploying...
view_bundle_deployWizard_deploymentCreated=Deployment angelegt...
view_bundle_deployWizard_deploymentCreatedDetail=Deployment [{0}] mit Beschreibung [{1}] angelegt
+view_bundle_deployWizard_deploymentCreatedDetail_concise=Sie haben das Deployment [{0}] erstellt
view_bundle_deployWizard_deploymentScheduled=Bundle-Deployment geplant\!
+view_bundle_deployWizard_deploymentScheduledDetail=Sie haben das Bundle Deployment [{0}] zur Destinationsgruppe [{1}] terminiert
+view_bundle_deployWizard_deploymentScheduledDetail_concise=Sie haben das Bundle Deployment terminiert
+view_bundle_deployWizard_destinationCreatedDetail=Sie haben die Destination [{0}] mit der Beschreibung [{1}] erstellt
+view_bundle_deployWizard_destinationCreatedDetail_concise=Sie haben die Destination [{0}] erstellt
+view_bundle_deployWizard_error_1=Löschen des neuen Deployment bei Cancel (Abbrechen) fehlgeschlagen
+view_bundle_deployWizard_error_10=Erstellen der Destination fehlgeschlagen, sie ist möglicherweise bereits vorhanden. (Hinweis\: FÌr eine vorhandene Destination von Destinationsansicht deployen)
+view_bundle_deployWizard_error_11=Auffinden definierter Deployments fehlgeschlagen.
+view_bundle_deployWizard_error_12=Auffinden definierter Bundles fehlgeschlagen.
+view_bundle_deployWizard_error_2=Löschen der neuen Destination bei Cancel (Abbrechen) fehlgeschlagen
+view_bundle_deployWizard_error_3=Terminieren des Deployments fehlgeschlagen\!
+view_bundle_deployWizard_error_4=Terminieren von Deployment fehlgeschlagen\: {0}
+view_bundle_deployWizard_error_5=Erstellen des Deployments fehlgeschlagen\!
+view_bundle_deployWizard_error_6=Erstellen des Deployments fehlgeschlagen\: {0}
+view_bundle_deployWizard_error_7=Abruf des Deployment Namens fehlgeschlagen.
+view_bundle_deployWizard_error_8=Sie mÌssen eine gÌltige Ressourcengruppe aus dem Drop-Down-MenÌ wÀhlen
+view_bundle_deployWizard_error_9=Löschen der neuen Destination in nextPage fehlgeschlagen
view_bundle_deployWizard_error_noBundleConfig=Abruf von Bundle-Zielinformationen fehlgeschlagen. Ist die von Ihnen ausgewÀhlte Gruppe eine gÌltige kompatible Gruppe, die Ziel fÌr Bundle-Deployments sein kann?
view_bundle_deployWizard_getConfigSkip=Keine Konfiguration nötig fÌr diese Bundle-Version.
view_bundle_deployWizard_getConfigStep=Deployment-Konfiguration einstellen
@@ -1294,6 +1621,9 @@ view_bundle_deploy_loadBundleFailure=Konnte das Bundle nicht finden
view_bundle_deploy_loadDeployFailure=Konnte Bundle-Deployments nicht laden
view_bundle_deploy_loadFailure=Konnte Bundle-Deployment nicht laden
view_bundle_deploy_name=Name des Deployments
+view_bundle_deploy_selectARow=Zur Anzeige der Installationsdetails eine Reihe wÀhlen
+view_bundle_deploy_tagUpdateFailure=Aktualisierung der Bundle Deployment Tags fehlgeschlagen
+view_bundle_deploy_tagUpdateSuccessful=Deployment-Tags fÃŒr Bundle erfolgreich aktualisiert
# #view_bundle_deploy_deployedBy = Deployed By
# #view_bundle_deploy_deploymentPlatforms = Deployment Resource
# #view_bundle_deploy_installDetails = Install Details
@@ -1310,8 +1640,22 @@ view_bundle_deployments=Deployments
view_bundle_dest_backToBundle=ZurÃŒck zum Bundle
view_bundle_dest_baseDirName=Basisverzeichnis
view_bundle_dest_created=Angelegt
+view_bundle_dest_deleteConfirm=Sind Sie sicher, dass Sie diese Bundle-Destination löschen wollen? Dies löscht es nur aus der Datenbank. Alle auf Remote Rechnern deployten Bundle-Inhalte an dieser Destination bleiben erhalten.
+view_bundle_dest_deleteFailure=Konnte die Bundle-Destination nicht löschen [{0}]
+view_bundle_dest_deleteSuccessful=Sie haben die Bundle-Destination [{0}] erfolgreich gelöscht
view_bundle_dest_deployDir=Deploy-Verzeichnis
view_bundle_dest_group=Gruppe
+view_bundle_dest_lastDeployedVersion=Zuletzt deployte Version
+view_bundle_dest_lastDeploymentDate=Letztes Deployment Datum
+view_bundle_dest_lastDeploymentStatus=Letzter Deployment Status
+view_bundle_dest_loadFailure=Konnte Bundle-Destination nicht laden
+view_bundle_dest_loadFailureVersionInfo=Konnte Bundle Destination deployte Versionsinformationen nicht laden
+view_bundle_dest_purgeConfirm=Dies bereinigt den Bundle Inhalt von allen Remote Rechnern. Sind Sie sicher, dass Sie dies tun möchten?
+view_bundle_dest_purgeFailure=Bereinigung der Bundle Destination [{0}] von einigen oder allen Remote Rechnern fehlgeschlagen.
+view_bundle_dest_purgeSuccessful=Sie haben die Bundle-Destination [{0}] erfolgreich von allen Remote Rechnern bereinigt.
+view_bundle_dest_revertConfirm=Dies rollt alle Remote Rechner zurÌck zum vorherigen Bundle Deployment. Sind Sie sicher, dass Sie dies tun möchten?
+view_bundle_dest_tagUpdateFailure=Konnte die Tags fÃŒr Bundle-Destination nicht aktualisieren
+view_bundle_dest_tagUpdateSuccessful=Sie haben die Tags fÃŒr Bundle-Destination erfolgreich aktualisiert
# #view_bundle_dest_lastDeployedVersion = Last Deployed Version
# #view_bundle_dest_lastDeploymentDate = Last Deployment Date
# #view_bundle_dest_lastDeploymentStatus = Last Deployment Status
@@ -1320,8 +1664,10 @@ view_bundle_dest_group=Gruppe
# #view_bundle_dest_tagUpdateFailure = Failed to update bundle destination tags
# #view_bundle_dest_tagUpdateSuccessful = You have successfully updated the bundle destination tags
view_bundle_destinations=Ziele
+view_bundle_fail_existingName=Erstellen von [{0}] fehlgeschlagen. Der Name wird bereits verwendet. Bitte versuchen Sie es mit einem anderen Namen.
# #view_bundle_fail_existingName = Failed to create [{0}]. The name is already being used. Please try another name.
view_bundle_fileListView_fileSize=DateigröÃe
+view_bundle_fileListView_loadFailure=Konnte die Bundle-Datei-Daten nicht laden
# #view_bundle_fileListView_loadFailure = Failed to load bundle file data
view_bundle_fileListView_md5=MD5
view_bundle_fileListView_sha256=SHA256
@@ -1330,13 +1676,24 @@ view_bundle_latestVersion=Aktuelle Version
view_bundle_list_backToAll=ZurÃŒck zur Bundle-Ãbersicht
view_bundle_list_deleteConfirm=Sind Sie sicher, dass Sie dieses Bundle löschen wollen?
view_bundle_list_deleteFailure=Löschen des Bundles [{0}] fehlgeschlagen
+view_bundle_list_deleteSuccessful=Sie haben das Bundle [{0}] erfolgreich gelöscht
+view_bundle_list_deletesFailure=Löschen der Bundles fehlgeschlagen
+view_bundle_list_deletesSuccessful=Sie haben die Bundles erfolgreich gelöscht
# #view_bundle_list_deleteSuccessful = You successfully deleted the bundle named [{0}]
# #view_bundle_list_deletesFailure = Failed to delete the bundles
# #view_bundle_list_deletesSuccessful = You successfully deleted the bundles
view_bundle_list_destinationsCount=Anzahl Ziele
+view_bundle_list_error1=Laden von Bundle zum Deployen [{0}] fehlgeschlagen
+view_bundle_list_error2=Erhalt eines einzelnen Bundles zum Deployen [{0}] fehlgeschlagen
# #view_bundle_list_error1 = Failed to load bundle to deploy [{0}]
# #view_bundle_list_error2 = Failed to get a single bundle to deploy [{0}]
view_bundle_list_error3=Konnte das Bundle nicht laden
+view_bundle_list_error4=Keine Bundles in diesem Repository gefunden
+view_bundle_list_loadFailure=Laden von Bundle zum Deployen [{0}] fehlgeschlagen
+view_bundle_list_loadWithLatestFailure=Konnte Bundle mit aktuellsten Versionsdaten nicht laden
+view_bundle_list_singleLoadFailure=Erhalt eines einzelnen Bundles zum Deployen [{0}] fehlgeschlagen
+view_bundle_list_tagUpdateFailure=Aktualisierung von Bundle-Tags fehlgeschlagen
+view_bundle_list_tagUpdateSuccessful=Sie haben die Bundle-Tags erfolgreich aktualisiert
# #view_bundle_list_error4 = No bundles found in this repository
# #view_bundle_list_loadFailure = Failed to load the bundle to be deployed [{0}]
# #view_bundle_list_loadWithLatestFailure = Failed to load bundle with the latest version data
@@ -1345,9 +1702,33 @@ view_bundle_list_error3=Konnte das Bundle nicht laden
# #view_bundle_list_tagUpdateSuccessful = You have successfully updated the bundle tags
view_bundle_list_versionsCount=Anzahl Versionen
view_bundle_purge=Bereinigen
+view_bundle_recipe=Anleitung
+view_bundle_resDeployDS_loadFailure=Konnte Ressourcen-Deployments nicht laden
# #view_bundle_recipe = Recipe
# #view_bundle_resDeployDS_loadFailure = Failed to load bundle resource deployments
view_bundle_revert=ZurÃŒckrollen
+view_bundle_revertWizard_confirmStep_confirmation=Rolle Live Deployment auf vorheriges Deployment zurÃŒck. Klicken Sie auf "Next" (Weiter) um fortzufahren...
+view_bundle_revertWizard_confirmStep_failedToFindLiveDeployment=Konnte Live Deployment nicht finden; kann nicht zurÃŒckrollen
+view_bundle_revertWizard_confirmStep_liveDeployment=Live Deployment
+view_bundle_revertWizard_confirmStep_name=Deployment BestÀtigung zurÌckrollen
+view_bundle_revertWizard_confirmStep_noLiveDeployment=Es wurde kein Live Deployment fÃŒr die Destination [{0}] gefunden
+view_bundle_revertWizard_confirmStep_noLiveDeployment_concise=Es wurde kein Live Deployment fÃŒr die Destination gefunden
+view_bundle_revertWizard_confirmStep_noPriorDeployment=Das Live Deployment [{0}] kann nicht zurÃŒckgerollt werden, weil es kein frÃŒheres Deployment fÃŒr die Destination [{1}] gibt
+view_bundle_revertWizard_confirmStep_noPriorDeployment_concise=Das Live Deployment kann nicht zurÃŒckgerollt werden, weil es kein frÃŒheres Deployment gibt
+view_bundle_revertWizard_confirmStep_prevDeployment=Vorheriges Deployment
+view_bundle_revertWizard_getInfoStep_cleanDeploy=Clean Deployment? (löscht ein altes vorhandenes Deploy-Verzeichnis vor dem Start des ZurÌckroll-Deployments)
+view_bundle_revertWizard_getInfoStep_getNameFailure=Erhalt von ZurÃŒckroll Deployment Namen fehlgeschlagen
+view_bundle_revertWizard_getInfoStep_name=ZurÃŒckroll-Informationen bereitstellen
+view_bundle_revertWizard_getInfoStep_revertDeployDesc=Deploy Beschreibung zurÃŒckrollen
+view_bundle_revertWizard_getInfoStep_revertDeployDescFull=[REVERT From] {0} [REVERT To] {1}
+view_bundle_revertWizard_getInfoStep_revertDeployName=Deploy Namen zurÃŒckrollen
+view_bundle_revertWizard_revertStep_name=Bundle zu Destinationsplattformen deployen
+view_bundle_revertWizard_revertStep_reverting=Rolle zurÃŒck...
+view_bundle_revertWizard_revertStep_scheduled=Sie haben das ZurÃŒckroll-Deployment erfolgreich terminiert\!
+view_bundle_revertWizard_revertStep_scheduledDetails=Sie haben das ZurÃŒckrollen des Bundle Deployment [{0}] von Ressourcen Gruppe [{1}] erfolgreich terminiert
+view_bundle_revertWizard_revertStep_scheduledFailure=Terminieren des ZurÃŒckroll-Deployments fehlgeschlagen\!
+view_bundle_revertWizard_title=Bundle zurÃŒckrollen
+view_bundle_revertWizard_windowTitle=Bundle ZurÃŒckroll Wizard
view_bundle_tree_loadFailure=Konnte die Bundle-Daten nicht laden
view_bundle_tree_unassigned_desc=Dies sind Bundles die noch nicht mit einer Bundle-Gruppe assoziiert sind.
view_bundle_tree_unassigned_name=Nicht zugewiesene Bundles
@@ -1414,6 +1795,7 @@ view_configEdit_invalidListSizeMax=Die Liste sollte höchstens {0} Reihe(n) enth
view_configEdit_invalidListSizeMin=Die Liste sollte mindestens {0} Reihe(n) enthalten
view_configEdit_invalidListSizeMinMax=Die Liste sollte mindestens {0} und höchstens {1} Reihe(n) enthalten
view_configEdit_jumpToSection=Zum Abschnitt springen
+view_configEdit_maxBoundsExceeded=Kann keinen weiteren Eintrag hinzufÃŒgen, da die maximalen GröÃeneinschrÀnkungen erreicht wurden\: {0}
view_configEdit_minBoundsExceeded=Kann diesen Eintrag nicht löschen, da das Minimum eingestellt wurde auf\: {0}
# #view_configEdit_maxBoundsExceeded = Cannot add another entry because the maximum size bounds has been met: {0}
# #view_configEdit_minBoundsExceeded = Cannot delete this entry as the minimum has been set to: {0}
@@ -1439,11 +1821,31 @@ view_configurationDetails_configNotUpdatedDueToNoChange=Konfiguration wurde nich
view_configurationDetails_error_updateFailure=Konnte die Konfiguration nicht aktualisieren
view_configurationDetails_messageConcise=Konfiguration aktualisiert - aktuelle Version is {0}
view_configurationDetails_messageDetailed=Konfiguration der Ressource [{1}] aktualisiert auf Version [{0}].
+view_configurationDetails_noConfigurationFetched=Es wurde keine Konfiguration abgerufen. Dies bedeutet, dass entweder das Laden der Konfiguration durch den Plugin fehlgeschlagen ist oder dass die Konfigurations-Collection lediglich in den Verbindungseinstellungen abgeschaltet ist.
view_configurationDetails_noPermission=Sie sind nicht berechtigt die Konfiguration dieser Ressource zu bearbeiten.
view_configurationDetails_somePropertiesInvalid=Die folgenden Konfigurations-Einstellungen haben ungÃŒtige Werte \: {0}. Die Werte mÃŒssen korrigiert werden, ehe die Konfiguration gespeichert werden kann.
+view_configurationHistoryDetails_error_loadFailure=Konnte den Verlauf der Konfiguration nicht laden.
+view_configurationHistoryList_cannotDeleteCurrent=Eines der gewÀhlten Verlaufselemente reprÀsentiert die aktuelle Konfiguration - Sie können es nicht löschen.
+view_configurationHistoryList_cannotDeleteGroupItems=Eines oder mehrere der gewÀhlten Konfigurationsverlaufselemente ist/sind Teil eines Gruppenkonfigurations-Updates. Sie mÌssen dieses Ìbergeordnete Gruppenverlaufselement bereinigen, ehe Sie dessen einzelne Ressourcen Verlaufselemente löschen können.
+view_configurationHistoryList_delete_failure=Konnte die Konfigurationsverlaufselemente nicht löschen.
+view_configurationHistoryList_delete_success=Sie haben die gewÀhlten Konfigurationsverlaufselemente erfolgreich gelöscht.
+view_configurationHistoryList_rollback=Rollback
+view_configurationHistoryList_rollback_failure=Konnte die Konfiguration nicht zurÃŒcksetzen. Die ursprÃŒngliche Konfiguration ist noch wirksam.
+view_configurationHistoryList_rollback_success=Sie haben die Konfiguration erfolgreich zur gewÀhlten vorherigen Konfiguration zurÌckgesetzt.
+view_configurationHistoryList_table_clickStatusIcon=FÃŒr mehr Details auf das Status Icon klicken
+view_configurationHistoryList_table_statusFailure=Diese Konfigurationsaktualisierung ist fehlgeschlagen
+view_configurationHistoryList_table_statusInprogress=Diese Konfigurationsaktualisierung lÀuft noch
+view_configurationHistoryList_table_statusNochange=Es wurden keine Ãnderungen an dieser Konfiguration vorgenommen
+view_configurationHistoryList_table_statusSuccess=Diese Konfigurationsaktualisierung war erfolgreich
# #view_configurationHistoryDetails_error_loadFailure = Unable to load configuration history.
# #view_configurationHistoryList_itemNamePlural = configuration history items
view_configurationHistoryList_title=KonfigurationsÀnderungen
+view_connectionSettingsDetails_allPropertiesValid=Alle Verbindungseinstellungen haben gÌltige Werte, daher können die Einstellungen jetzt gespeichert werden.
+view_connectionSettingsDetails_error_updateFailure=Verbindungseinstellungen konnten nicht aktualisiert werden.
+view_connectionSettingsDetails_messageConcise_updateSuccess=Aktualisierung fÃŒr die Verbindungseinstellungen initiiert.
+view_connectionSettingsDetails_messageDetailed_updateSuccess=Aktualisierung von Verbindungseinstellungen initiiert fÃŒr Ressource [{0}].
+view_connectionSettingsDetails_noPermission=Sie sind nicht berechtigt die Verbindungseinstellungen dieser Ressource zu bearbeiten.
+view_connectionSettingsDetails_somePropertiesInvalid=Die folgenden Verbindungseinstellungen haben ungÌtige Werte \: {0}. Die Werte mÌssen korrigiert werden, ehe die Einstellungen gespeichert werden können.
# #view_connectionSettingsDetails_allPropertiesValid = All connection settings have valid values, so the settings can now be saved.
# #view_connectionSettingsDetails_error_updateFailure = Failed to update connection settings.
# #view_connectionSettingsDetails_messageConcise_updateSuccess = Connection settings update initiated.
@@ -1465,8 +1867,21 @@ view_dashboard_favorites_error1=Konnte die Ressoucen-Lesezeichen nicht laden
view_dashboardsManager_error1=Konnte das neue Dashboard nicht hinzufÃŒgen
view_dashboardsManager_message_title_details=<h1>Willkommen bei {0}</h1>\n<p>Dieses Dashboard kan durch BetÀtigen des Bearbeitungsmodus-Knopfes verÀndert werden.</p>\n<p>Was möchten Sie tun?</p>\n<p> <a href\="{1}">Neu gefundene Ressourcen importieren.</a></p>\n<p> <a href\="{2}">Ressourcen suchen.</a></p>\n<p> <a href\="{3}">Hilfe und Dokumentation ansehen.</a></p>
view_dashboards_confirm1=Sind Sie sicher, dass Sie löschen möchten
+view_dashboards_portlets_refresh_fail1=Aktualisierung von Auto-refresh Intervall fehlgeschlagen
+view_dashboards_portlets_refresh_fail2=Deaktivierung von erneutem Laden von Auto-refresh Intervall fehlgeschlagen
+view_dashboards_portlets_refresh_multiple_min={0} Minuten
+view_dashboards_portlets_refresh_none=Kein Aktualisieren
+view_dashboards_portlets_refresh_one_min=1 Minute
+view_dashboards_portlets_refresh_success1=Aktualisierter Intervall fÃŒr auto-refresh
+view_dashboards_portlets_refresh_success2=Stoppe erneutes Laden fÃŒr auto-refresh
view_dashboards_title=Dashboard
view_drift_button_detectNow=Jetzt ermitteln
+view_drift_button_pinToDef=An Definition pinnen
+view_drift_button_pinToDef_confirm=Pinning (Festmachen) stellt diesen Schnappschuss als Schnappschuss 0 fÃŒr die Definition ein. Alle anderen vorhandenen SchnappschÃŒsse werden aus der Definition entfernt. Die Definition wird als "pinned" (festgemacht) gekennzeichnet und nachfolgende Drifts werden stets gegen den festgemachten Schnappschuss gemeldet. Soll dieser Schnappschuss an der Definition festgemacht werden?
+view_drift_button_pinToTemplate=An Vorlage festmachen
+view_drift_button_pinToTemplate_confirm=Nach dem Festmachen wird dieser Schnappschuss als erster Schnappschuss fÃŒr alle mittels der Vorlage erstellten Definitionen verwendet. Falls an einer bestehenden Vorlage festgemacht, so werden die bestehenden Definitionen der Vorlage an dem neuen ersten Schnappschuss festgemacht und die bestehenden SchnappschÃŒsse werden entfernt. Mit Vorlagenauswahl fortfahren?
+view_drift_carousel_sizeFilterLabel=Schnappschuss Anzeige Max
+view_drift_carousel_startFilterLabel=Schnappschuss Start
# #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
@@ -1475,8 +1890,27 @@ view_drift_button_detectNow=Jetzt ermitteln
# #view_drift_carousel_startFilterLabel = Snapshot Start
view_drift_category_fileAdded=Datei hinzugefÃŒgt
view_drift_category_fileChanged=Datei geÀndert
+view_drift_category_fileNew=Neu aufgefunden
# #view_drift_category_fileNew = Newly Detected
view_drift_category_fileRemoved=Datei gelöscht
+view_drift_confirm_deleteAllDefs=Alle Drift-Erkennungsdefinitionen löschen?
+view_drift_confirm_deleteDefs=Die ausgewÀhlte(n) Drift-Erkennungsdefinition(en) löschen?
+view_drift_confirm_deleteTemplate=Warnung\! Das Löschen dieser Vorlage löscht auch alle angehÀngten Drift-Definitionen. Diese angehÀngten Definitionen sowie alle deren SchnappschÌsse werden dauerhaft vom System entfernt. Nicht angehÀngte Definitionen werden nicht entfernt. Sind Sie sicher, dass Sie fortfahren möchten?
+view_drift_failure_deleteDefs=Löschen einiger oder alle Drift-Erkennungsdefinitionen fehlgeschlagen.
+view_drift_failure_deleteTemplates=Löschen einiger oder alle Drift Vorlagen fehlgeschlagen
+view_drift_failure_detectNow=Absenden von Anfrage zur AusfÃŒhrung der Drift-Erkennung fehlgeschlagen
+view_drift_failure_load=Abruf von Drift Instanzen fehlgeschlagen
+view_drift_failure_pinToDef=Festmachen von Schnappschuss an Definition fehlgeschlagen
+view_drift_success_defUpdated=Drift Erkennung Definition aktualisiert und wird Auswirkungen auf die nÀchste ErkennungsausfÌhrung haben.
+view_drift_success_delete=Erfolgreiche Löschung von {0} Drift Instanzen
+view_drift_success_deleteDefs=Erfolgreiche Löschung von {0} Drift Erkennung Definitionen
+view_drift_success_deleteTemplate=Erfolgreiche Löschung von {0} Drift Vorlagen
+view_drift_success_detectNow=Absenden von Anfrage zur AusfÃŒhrung der Drift-Erkennung erfolgreich
+view_drift_success_pinToDef=Erfolgreiches Festmachen von Schnappschuss {0} an Drift Definition.
+view_drift_success_templateUpdated=Drift Vorlage aktualisiert und Ãnderungen an angehÀngte Definitionen gepusht.
+view_drift_table_attached=AngehÀngt?
+view_drift_table_baseDir=Basis-Verzeichnis
+view_drift_table_driftHandlingMode=Der Umgang mit Drift
# #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?
@@ -1497,7 +1931,13 @@ view_drift_category_fileRemoved=Datei gelöscht
# #view_drift_table_driftHandlingMode = Drift Handling
view_drift_table_driftHandlingMode_normal=normal
view_drift_table_driftHandlingMode_plannedChanges=geplante Ãnderungen
+view_drift_table_hover_defNotPinned=Die Drift Definition ist nicht festgemacht. Zur Ansicht des ersten Schnappschusses klicken.
+view_drift_table_hover_defPinned=Die Drift Definition ist an ihrem ersten Schnappschuss festgemacht. FÃŒr Ansicht des ersten Schnappschusses klicken.
+view_drift_table_hover_edit=Zur Ansicht oder Bearbeitung der Drift Definition oder Vorlagen Eigenschaften klicken.
+view_drift_table_hover_outOfCompliance_drift=Drift vorhanden
view_drift_table_hover_outOfCompliance_noBaseDir=Das Basisverzeichnis existiert nicht
+view_drift_table_hover_templateNotPinned=Die Drift Vorlage ist nicht an einem Schnappschuss festgemacht.
+view_drift_table_hover_templatePinned=Die Drift Vorlage ist an einem Schnappschuss festgemacht. FÃŒr Ansicht des festgemachten Schnappschusses klicken.
# #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.
@@ -1507,11 +1947,44 @@ view_drift_table_hover_outOfCompliance_noBaseDir=Das Basisverzeichnis existiert
# #view_drift_table_hover_templatePinned = The drift template is pinned to a snapshot. Click to view the pinned snapshot.
view_drift_table_newFile=Neue Datei
view_drift_table_oldFile=Alte Datei
+view_drift_table_pinned=Festgemacht?
+view_drift_table_resourceDef=Ressource Drift Erkennung Definition
+view_drift_table_resourceHistory=Ressource Drift Historie
view_drift_table_snapshot=Schnappschuss
view_drift_table_snapshotTime=Schnappschuss Zeit
view_drift_table_template=Vorlage
+view_drift_table_title_initialSnapshot=Erster Schnappschuss fÃŒr Definition [{0}] \: Pinned \= [{1}]
view_drift_table_title_snapshot=Schnappschuss [{0}] fÃŒr Definition [{1}]
+view_drift_table_title_templateSnapshot=Festgemachter Schnappschuss fÃŒr Vorlage [{0}]
+view_drift_wizard_addDef_failure=HinzufÃŒgen der neuen Drift Erkennung Definition [{0}] fehlgeschlagen
+view_drift_wizard_addDef_infoStepHelp=Jede Drift Erkennungsdefinition beschreibt einen Satz Dateien, fÃŒr die die Drift Ãberwachung durchgefÃŒhrt wird. Die Definition kann aktiviert und deaktiviert sein, definiert den ErkennungsausfÃŒhrungsintervall und legt ein Basisverzeichnis und optionale Dateifilter fest. FÃŒr jeden Ressourcentyp der Drift Erkennung bietet gibt es eine oder mehrere vordefinierte Vorlagen zur Verwendung als Startdefinition, die anschlieÃend bearbeitet werden können.
+view_drift_wizard_addDef_infoStepName=WÀhlen Sie die Vorlage fÌr die neue Drift Erkennungsdefinition
+view_drift_wizard_addDef_success=Neue Drift Erkennungsdefinition [{0}] erfolgreich hinzugefÃŒgt. Agent(en) wird/werden aktualisiert.
+view_drift_wizard_addDef_templatePrompt=Drift Definition Vorlagen
+view_drift_wizard_addDef_title=Drift Erkennung Definition fÃŒr Ressource vom Typ [{0}] hinzufÃŒgen
+view_drift_wizard_addDef_windowTitle=Drift Erkennung Definition Wizard hinzufÃŒgen
+view_drift_wizard_addTemplate_failure=HinzufÃŒgen der neuen Drift Vorlage [{0}] fehlgeschlagen
+view_drift_wizard_addTemplate_infoStepHelp=Jede Drift Vorlage ist von einer bestehenden Vorlage abgeleitet. Dies bietet eine schnelle Weise der Erstellung neuer Vorlagen, die bestehenden Vorlagen Àhneln oder die ihren Ursprung in Plugin-definierten Vorlagen haben. Wie auch eine Drift Definition beschreibt die Vorlage einen Satz von Dateien, fÃŒr die Drift Ãberwachung durchgefÃŒhrt wird. Je nach Situation können einer von einer Vorlage abgeleiteten Definition Ãnderungen am Dateisatz oder anderen Einstellungen gestattet oder nicht gestattet werden. Die Vorlagennamen mÃŒssen innerhalb eines Ressourcentyps eindeutig sein.
+view_drift_wizard_addTemplate_infoStepName=Startvorlage auswÀhlen
+view_drift_wizard_addTemplate_success=HinzufÃŒgen der neuen Drift Vorlage [{0}] erfolgreich.
+view_drift_wizard_addTemplate_title=Drift Definition Vorlage fÃŒr Typ [{0}] hinzufÃŒgen
+view_drift_wizard_addTemplate_windowTitle=Drift Definition Vorlage Wizard hinzufÃŒgen
+view_drift_wizard_pinTemplate_confirmNotPinned=Nach dem Festmachen besitzt jede aktuelle und zukÃŒnftige Drift Definition fÃŒr die Vorlage ihren ersten Schnappschuss, der auf den festgemachten Schnappschuss der Vorlage eingestellt ist. Vorhandene Definitionen fÃŒr diese Vorlage werden auf den neuen ersten Schnappschuss zurÃŒckgesetzt und alle bestehenden SchnappschÃŒsse werden entfernt. Mit dem Festmachen der Vorlage an den Schnappschuss fortfahren?
+view_drift_wizard_pinTemplate_confirmPinned=Warnung\! Diese Vorlage ist bereits festgemacht. Die Vorlage kann erneut an diesem Schnappschuss festgemacht werden. Nach dem erneuten Festmachen ist jede aktuelle und zukÃŒnftige Drift Definition fÃŒr die Vorlage mit ihrem ersten Schnappschuss auf den festgemachten Schnappschuss der Vorlage eingestellt. Bestehende Definitionen fÃŒr diese Vorlage werden auf den ersten Schnappschuss zurÃŒckgesetzt und alle bestehenden SchnappschÃŒsse zurÃŒckgesetzt. Mit dem erneuten Festmachen der Vorlage mit diesem neuen Schnappschuss fortfahren?
view_drift_wizard_pinTemplate_duplicate_name_error=Vorlagen Name muss eindeutig sein
+view_drift_wizard_pinTemplate_failure=Festmachen des Schnappschusses an Drift Vorlage [{0}] fehlgeschlagen
+view_drift_wizard_pinTemplate_infoStepExistingTemplate=An bestehender Vorlage festmachen
+view_drift_wizard_pinTemplate_infoStepHelp=Nach dem Festmachen besitzt jede aktuelle und zukÃŒnftige Drift Definition fÃŒr die Vorlage ihren ersten Schnappschuss, der auf den festgemachten Schnappschuss der Vorlage eingestellt ist. Und die Definition selbst wird als festgemacht markiert. Dies wird zur Erkennung von Drift von einem erwarteten Dateisatz verwendet. Vorhandene Definitionen fÃŒr diese Vorlage werden auf den neuen ersten Schnappschuss zurÃŒckgesetzt und alle bestehenden SchnappschÃŒsse werden entfernt.
+view_drift_wizard_pinTemplate_infoStepName=WÀhlen Sie die festzumachende Vorlage
+view_drift_wizard_pinTemplate_infoStepNewTemplate=An neuer Vorlage festmachen (abgeleitet von der Drift Definition des Schnappschusses)
+view_drift_wizard_pinTemplate_infoStepRadioHelp=Der Schnappschuss kann an einer neuen oder bestehenden Drift Vorlage festgemacht werden. Die "New Template" Option (neue Vorlage) gestattet es dem Benutzer eine vertrauenswÌrdige Definition und einen Schnappschuss auf Ressourcenebene bis zur Typebene zu begÌnstigen. Die neue kann dann an Mitgliedern des Typs angewendet werden. Die neue Vorlage ist anfangs eine Kopie der Drift Definition des Schnappschusses, aber sie kann im nÀchsten Schritt bearbeitet werden. Der Name sollte geÀndert werden und muss ein eindeutiger Drift Vorlagen Name fÌr den Typ sein. Die "Existing Template" Option (vorhandene Option) gestattet dem Benutzer das Festmachen oder das erneute Festmachen mit dem gewÀhlten Schnappschuss. Um gÌltig zu sein muss die vorhandene Vorlage dieselben Verzeichnisse Ìberwachen, die die Definition des Schnappschusses. Das AuswahlkÀstchen zeigt nur gÌltige bestehende Vorlagen an. Falls es keine gÌltigen bestehenden
Vorlagen gibt, so kann diese Option nicht gewÀhlt werden.
+view_drift_wizard_pinTemplate_infoStepRadioTitle=Vorlagenauswahl
+view_drift_wizard_pinTemplate_infoStepSelectBlocked=Es sind keine bestehenden Vorlagen vorhanden, die dieselben Verzeichnisse Ìberwachen wie die Definition des Schnappschusses. WÀhlen Sie die "New Template" Option (neue Vorlage) um fortzufahren.
+view_drift_wizard_pinTemplate_infoStepSelectTitle=Bestehende Vorlagen
+view_drift_wizard_pinTemplate_success=Festmachen der neuen Drift Vorlage [{0}] erfolgreich.
+view_drift_wizard_pinTemplate_title=Schnappschuss [{0}] von Definition [{1}] an Drift Vorlage fÃŒr Typ [{2}] festmachen
+view_drift_wizard_pinTemplate_windowTitle=Drift Definition Vorlage Wizard festmachen
+view_dynagroup_children=DynaGroup Kinder
# #view_drift_table_pinned = Pinned?
# #view_drift_table_resourceDef = Resource Drift Detection Definition
# #view_drift_table_resourceHistory = Resource Drift History
@@ -1574,10 +2047,14 @@ view_dynagroup_exprBuilder_expressionType_resource=Ressource
view_dynagroup_exprBuilder_expressionType_resourceCategory=Ressourcen-Kategorie
view_dynagroup_exprBuilder_expressionType_resourceConfig=Ressourcen-Konfiguration
view_dynagroup_exprBuilder_expressionType_resourceType=Ressourcen-Typ
+view_dynagroup_exprBuilder_expressionType_tooltip=Der Type von Eigenschaft, den dieser Ausdruck einschaltet\:<br/> <b>Ressource</b>\: Eine Ressourceneigenschaft wie deren Name oder Version<br/> <b>Ressourcetyp</b>\: Suche nach Ressourcen eines bestimmten Typs<br/> <b>Ressourcenkategorie</b>\: Suche nach Ressourcen nach Kategorie\: Plattform, Server, Dienst<br/> <b>Merkmal</b>\: Ressourcen, die ausgewÀhlte Werte fÌr ein Ìberwachtes Merkmal besitzen<br/> <b>Plugin Konfiguration</b>\: Suche nach der Plugin Komponente Konfigurationseinstellung der Komponente<br/> <b>Ressourcenkonfiguration</b>\: Suche nach der Konfigurationsenstellung der verwalteten Ressource
view_dynagroup_exprBuilder_expressionType_trait=Trait
+view_dynagroup_exprBuilder_expression_tooltip=Dies ist der vollstÀndige Ausdruck, der von den Auswahlen im Formular unten reprÀsentiert wird. Dieser Text wird dem Text Ihres Gruppendefinitionsausdrucks hinzugefÌgt, wenn Sie auf die SchaltflÀche "Add Expression" (Ausdruck hinzufÌgen) klicken.
view_dynagroup_exprBuilder_groupBy=Gruppieren nach
+view_dynagroup_exprBuilder_groupBy_tooltip=Durch GroupBy schwenkt das System die Werte der eingegebenen AusdrÃŒcke und erstellt eine separate Gruppe fÃŒr jeden Wert. Zum Beispiel GroupBy am Cluster Namen zur Erstellung einer Gruppe fÃŒr jeden Cluster mit allen Cluster Mitgliedern darin.
# #view_dynagroup_exprBuilder_groupBy_tooltip = groupby will cause the system to pivot on the values from the entered expressions creating a separate group for each value. For example, groupby on the cluster name to create a group for each cluster with all cluster members in it.
view_dynagroup_exprBuilder_memberOf=Mitglied von
+view_dynagroup_exprBuilder_memberOf_tooltip=memberof schrÀnkt die dynagroup Mitglieder auf einen Untersatz der festgelegten Ressourcengruppe ein. Die Festlegung mehrerer memberof Bedingungen schrÀnkt die dynagroup Mitglieder auf einen Untersatz der Vereinigung von Mitgliedern der festgelegten Gruppen ein.
# #view_dynagroup_exprBuilder_memberOf_tooltip = memberof will restrict the dynagroup members to be a subset of the specified resource group. Specifying multiple memberof conditions will restrict the dynagroup members to be a subset of the union of members of the specified groups.
view_dynagroup_exprBuilder_noPlugins=--Keine Plugins--
view_dynagroup_exprBuilder_noProperties=--Keine Eigenschaften--
@@ -1585,13 +2062,25 @@ view_dynagroup_exprBuilder_noResourceTypes=--Keine Ressourcen-Typen--
view_dynagroup_exprBuilder_pluginLoadFailure=Kann die Liste der Plugins nicht laden
view_dynagroup_exprBuilder_propLoadFailure=Kann die Liste der Eigenschaften nicht laden
view_dynagroup_exprBuilder_propertyName=Name der Eigenschaft
+view_dynagroup_exprBuilder_propertyName_tooltip=Der Name der abzufragenden Eigenschaft. Dies wird sowohl durch den Ausdruckstyp als auch den Ressourcentyp definiert.
+view_dynagroup_exprBuilder_resTypeLoadFailure=Erhalt der Liste von Ressourcentypen fÌr Plugin [{0}] nicht möglich
+view_dynagroup_exprBuilder_resource=Ressource
+view_dynagroup_exprBuilder_resourceType=Ressourcen-Typ
+view_dynagroup_exprBuilder_resourceType_tooltip=Der Ressourcentyp
+view_dynagroup_exprBuilder_resource_child=Child
+view_dynagroup_exprBuilder_resource_grandparent=Grandparent
view_dynagroup_exprBuilder_resource_greatGrandparent=GreatGrandparent
view_dynagroup_exprBuilder_resource_greatGreatGrandparent=GreatGreatGrandparent
view_dynagroup_exprBuilder_resource_parent=Eltern
view_dynagroup_exprBuilder_resource_resource=Ressource
view_dynagroup_exprBuilder_resource_tooltip=WÀhlen Sie die Ebene der Ressource, die Sie wÀhlen möchten. Wenn Sie zum Beispiel "parent" wÀhlen, so finden Sie Ressourcen, deren Ìbergeordnete Ressource ("Parent" Ressource) mit dem Rest des Ausdrucks Ìbereinstimmt.
view_dynagroup_exprBuilder_savedExpression=Gespeicherter Ausdruck
+view_dynagroup_exprBuilder_title=Expression-Builder
+view_dynagroup_exprBuilder_unset=Ungesetzt
+view_dynagroup_exprBuilder_unset_tooltip=Unset (Ungesetzt) findet alle Werte mit einem Nullwert in der Datenbank. Dies ist nicht mittels des "\=" Operators möglich, wegen der Art undWeise auf die Datenbanken Daten speichern und abfragen.
+view_dynagroup_exprBuilder_value_tooltip=Der String-Wert fÃŒr den abzufragenden Ausdruck
view_dynagroup_expression=Ausdruck
+view_dynagroup_expressionBuilderIconTooltip=Expression Builder...
view_dynagroup_expressionSet=Ausdruck
view_dynagroup_lastCalculationTime=Zeitpunkt letzte Berechnung
view_dynagroup_loadDefinitionFailure=Konnte die Gruppendefinition [{0}] nicht laden
@@ -1616,6 +2105,8 @@ view_dynagroup_template_customExpression=Benutzerdefinierter Ausdruck...
view_dynagroup_template_downedResources=Alle Ressourcen derzeit auÃer Betrieb
view_dynagroup_template_jbossas4_clusters=JBossAS 4 - Cluster
view_dynagroup_template_jbossas4_earClusters=JBossAS 4 - Geclusterte EARs
+view_dynagroup_template_jbossas4_hostingApp=JBossAS 4 - Alle, die eine beliebige Version von "my" App hosten
+view_dynagroup_template_jbossas4_nonsecured=JBossAS 4 - Alle nicht gesicherten
view_dynagroup_template_jbossas4_uniqueVersions=JBossAS 4 - Eindeutige Versionen
view_dynagroup_template_jbossas5_clusters=JBossAS 5/6 - Cluster
view_dynagroup_template_platforms=Plattform Ressourcen im Inventar
@@ -1652,17 +2143,25 @@ view_group_detail_failRecursiveChange=Konnte die Einstellung ''Rekursiv'' fÃŒr d
view_group_detail_implicitAvail=Group availability for all members (includes recursive members).
view_group_detail_recursiveChange=Sie haben erfolgreich die ''Rekursiv''-Einstellung fÌr die Gruppe [{0}] geÀndert.
view_group_inventory_activity_no_recent_metrics=Diese Gruppe hat keine aktuellen Metriken
+view_group_meas_schedules_title=Gruppen Metrik Collection ZeitplÀne
# #view_group_meas_schedules_title = Group Metric Collection Schedules
view_group_membership_failFetch=Konnte die Ressourcen-Gruppe nicht laden
view_group_membership_saveFailure=Aktualisierung der Mitgliedschaft von Gruppe [{0}] fehlgeschlagen
view_group_membership_saveSuccessful=Sie haben die die Mitgliedschaft von Gruppe [{0}] aktualisiert
+view_group_operationScheduleDetails_failedToLoadMembers=Konnte die Gruppen Mitglieder Ressourcen nicht laden.
view_group_operationScheduleDetails_field_execute=AusfÃŒhren
+view_group_operationScheduleDetails_field_haltOnFailure=Bei Fehlschlagen anhalten?
view_group_operationScheduleDetails_memberResource=Mitglied Ressource
+view_group_operationScheduleDetails_value_parallel=parallel
+view_group_operationScheduleDetails_value_sequential=in der unten festgelegten Reihenfolge (Mitglieder Ressourcen zur Ãnderung der Reihenfolge ziehen und ablegen)
view_group_pluginConfig_edit_currentGroupProperties=Aktuelle Gruppeneigenschaften
+view_group_pluginConfig_edit_invalid=Die folgenden Verbindungseinstellungseigenschaften haben ungÌtige Werte und mÌssen korrigiert werden, ehe die Verbindungseinstellungen gespeichert werden können\: [{0}]
view_group_pluginConfig_edit_noperm=Sie sind nicht berechtigt diese Gruppen Verbindungseinstellungen zu bearbeiten
+view_group_pluginConfig_edit_saveFailure=Die Initiierung der Gruppen Verbindungseinstellungsaktualisierungen fÃŒr die [{0}] kompatible Gruppe namens [{1}] ist fehlgeschlagen
view_group_pluginConfig_edit_saveInitiated_concise=Die Gruppen Verbindungseinstellungsaktualisierungen wurden instantiiert
view_group_pluginConfig_edit_saveInitiated_full=Die Gruppen Verbindungseinstellungsaktualisierungen wurden instantiiert fÃŒr die [{0}] kompatible Gruppe namens [{1}]
view_group_pluginConfig_edit_saveTooltip=Die Verbindungseinstellungen aller Gruppenmitglieder aktualisieren
+view_group_pluginConfig_edit_valid=Alle Verbindungseinstellungseigenschaften haben gÌltige Werte, daher können die Verbindungseinstellungen jetzt gespeichert werden.
# #view_group_membership_saveFailure = Failed to update membership of group [{0}]
# #view_group_membership_saveSuccessful = You have updated the membership of group [{0}]
# #view_group_pluginConfig_edit_currentGroupProperties = Current Group Properties
@@ -1674,20 +2173,25 @@ view_group_pluginConfig_edit_saveTooltip=Die Verbindungseinstellungen aller Grup
# #view_group_pluginConfig_edit_saveTooltip = Update the connection settings of all group members
# #view_group_pluginConfig_edit_valid = All connection setting properties have valid values, so the connection settings can now be saved
view_group_pluginConfig_members_fetchFailure=Failed to get plugin config update history for members of group [{0}]
+view_group_pluginConfig_members_fetchFailureConn=Abruf von Mitglieder Verbindungseinstellungen fehlgeschlagen fÃŒr [{0}]
+view_group_pluginConfig_members_fetchFailureConnInProgress=Eine Gruppen Plugin Konfigurationsaktualisierung lÀuft derzeit. Sie mÌssen warten, bis die Aktualisierung abgeschlossen ist, ehe Sie die Gruppeneinstellungen ansehen können.
view_group_pluginConfig_members_statusDetails=Status Details
view_group_pluginConfig_members_statusFailure=Die Konfigurationsaktualisierung ist aus unbekanntem Grund fehlgeschlagen
view_group_pluginConfig_members_statusInprogress=Diese Konfigurationsaktualisierung lÀuft noch
view_group_pluginConfig_members_statusNochange=Es wurden keine Ãnderungen an dieser Konfiguration vorgenommen
view_group_pluginConfig_members_statusSuccess=Diese Konfigurationsaktualisierung war erfolgreich
+view_group_pluginConfig_members_title=Gruppen Verbindungseinstellungen Mitglieder Verlauf
view_group_pluginConfig_table_clickStatusIcon=FÃŒr mehr Details auf das Status Icon klicken
view_group_pluginConfig_table_deleteFailure=Konnte die Gruppen Plugin Config History nicht löschen
view_group_pluginConfig_table_deleteSuccessful=Sie haben [{0}] VerlaufseintrÀge gelöscht
view_group_pluginConfig_table_failFetch=Konnte die Gruppen Plugin Config History nicht abrufen
+view_group_pluginConfig_table_msg1=Mitglieder Historie fÃŒr Status jeder einzelnen Ressource ansehen
view_group_pluginConfig_table_statusDetails=Status Details
view_group_pluginConfig_table_statusFailure=Diese Konfigurationsaktualisierung ist fehlgeschlagen
view_group_pluginConfig_table_statusInprogress=Diese Gruppen Konfigurationsaktualisierung lÀuft noch
view_group_pluginConfig_table_statusNochange=Es wurden keine Ãnderungen an dieser Gruppen Konfiguration vorgenommen
view_group_pluginConfig_table_statusSuccess=Diese Gruppen Konfigurationsaktualisierung ist fehlgeschlagen
+view_group_pluginConfig_table_title=Verlauf der Gruppen Verbindungs-Einstellungen
view_group_pluginConfig_table_viewMemberHistory=Mitglieder Historie ansehen
view_group_pluginConfig_table_viewSettings=Einstellungen ansehen
# #view_group_pluginConfig_members_statusFailure = This configuration update failed for an unknown reason
@@ -1712,21 +2216,31 @@ view_group_pluginConfig_view_noperm=Sie haben nicht die Berechtigung, um die Ver
view_group_resConfig_edit_invalid=Die folgenden Konfigurations-Einstellungen haben ungÌtige Werte und mÌssen korrigiert werden, um die Einstellungen speichen zu können\: [{0}]
view_group_resConfig_edit_loadFail=Abruf von Mitglieder Ressourcen Konfigurationen fehlgeschlagen fÃŒr [{0}]
view_group_resConfig_edit_noperm=Sie sind nicht berechtigt diese Gruppen Konfiguration zu bearbeiten
+view_group_resConfig_edit_saveFailure=Die Initiierung der Gruppen Konfigurationsaktualisierungen fÃŒr die [{0}] kompatible Gruppe namens [{1}] ist fehlgeschlagen
+view_group_resConfig_edit_saveInitiated_concise=Die Gruppen Konfigurationsaktualisierungen wurden initiiert
view_group_resConfig_edit_saveInitiated_full=Die Gruppen Konfigurationsaktualisierungen wurden instantiiert fÃŒr die [{0}] kompatible Gruppe namens [{1}]
view_group_resConfig_edit_saveTooltip=Die Konfigurationen aller Gruppenmitglieder aktualisieren
view_group_resConfig_edit_valid=Alle Konfigurationseigenschaften haben gÃŒltige Werte, daher kann die Konfiguration jetzt gespeichert werden
+view_group_resConfig_members_fetchFailure=Erhalt des Ressourcen config Aktualisierungsverlaufs fÃŒr Mitglieder der Gruppe [{0}] fehlgeschlagen
+view_group_resConfig_members_fetchFailureConfig=Abruf von Mitglieder Ressourcen Konfigurationenseinstellungen fehlgeschlagen fÃŒr [{0}]
+view_group_resConfig_members_fetchFailureConfigInProgress=Eine Gruppen Ressourcen Konfigurationsaktualisierung lÀuft derzeit. Sie mÌssen warten, bis die Aktualisierung abgeschlossen ist, ehe Sie die Gruppeneinstellungen ansehen können.
view_group_resConfig_members_statusDetails=Status Details
view_group_resConfig_members_statusFailure=Die Konfigurationsaktualisierung ist aus unbekanntem Grund fehlgeschlagen
view_group_resConfig_members_statusInprogress=Diese Konfigurationsaktualisierung lÀuft noch
view_group_resConfig_members_statusNochange=Es wurden keine Ãnderungen an dieser Konfiguration vorgenommen
view_group_resConfig_members_statusSuccess=Diese Konfigurationsaktualisierung war erfolgreich
+view_group_resConfig_members_title=Gruppen Ressourcen Konfiguration Mitglieder Verlauf
view_group_resConfig_table_clickStatusIcon=FÃŒr mehr Details auf das Status Icon klicken
+view_group_resConfig_table_deleteFailure=Konnte die Gruppen Ressourcen Config History nicht löschen
view_group_resConfig_table_deleteSuccessful=Sie haben [{0}] VerlaufseintrÀge gelöscht
+view_group_resConfig_table_failFetch=Erhalt der Gruppen Ressourcen Config History fehlgeschlagen
+view_group_resConfig_table_msg1=Mitglieder Historie fÃŒr Status jeder einzelnen Ressource ansehen
view_group_resConfig_table_statusDetails=Status Details
view_group_resConfig_table_statusFailure=Diese Konfigurationsaktualisierung ist fehlgeschlagen
view_group_resConfig_table_statusInprogress=Diese Gruppen Konfigurationsaktualisierung lÀuft noch
view_group_resConfig_table_statusNochange=Es wurden keine Ãnderungen an dieser Gruppen Konfiguration vorgenommen
view_group_resConfig_table_statusSuccess=Diese Gruppen Konfigurationsaktualisierung ist fehlgeschlagen
+view_group_resConfig_table_title=Gruppen Ressourcen Konfigurationsverlauf
view_group_resConfig_table_viewMemberHistory=Mitglieder Historie ansehen
view_group_resConfig_table_viewSettings=Einstellungen ansehen
view_group_resConfig_view_groupProperties=Gruppen-Eigenschaften
@@ -1738,15 +2252,19 @@ view_group_resConfig_view_noperm=Sie haben nicht die Berechtigung, um die Konfig
# #view_group_resConfig_edit_saveTooltip = Update the configurations of all group members
# #view_group_resConfig_edit_valid = All configuration properties have valid values, so the configuration can now be saved
view_group_summary_compatible=Kompatible
+view_group_summary_descUpdateFailure=Konnte Beschreibung der Ressourcen Gruppe mit ID [{0}] nicht Àndern
view_group_summary_descUpdateSuccessful=Sie haben die Beschreibung dieser Ressourcen-Gruppe geÀndert.
# #view_group_summary_descUpdateFailure = Failed to change the description of the resource group with ID [{0}]
# #view_group_summary_descUpdateSuccessful = You have changed the description of this resource group
view_group_summary_dynamic=Dynamisch
+view_group_summary_dynamicNote=Dynamische Gruppen Namen und Beschreibungen werden gemanagt und können daher nicht bearbeitet werden
# #view_group_summary_dynamicNote = Dynamic group names and descriptions are managed, and therefore are not editable
view_group_summary_groupDefinition=Gruppen-Definition
view_group_summary_memberCount=Anzahl Mitglieder
view_group_summary_memberType=Mitglieds-Typ
view_group_summary_mixed=Gemischt
+view_group_summary_nameUpdateFailure=Ãnderung des Namens der Ressourcen Gruppe mit ID [{0}] nicht Àndern - konnte ihn nicht von [{1}] zu [{2}] Àndern
+view_group_summary_nameUpdateSuccessful=Sie haben den Namen der Ressourcen Gruppe mit ID [{0}] von [{1}] zu [{2}] geÀndert
# #view_group_summary_nameUpdateFailure = Failed to change the name of the resource group with ID [{0}] - could not change from [{1}] to [{2}]
# #view_group_summary_nameUpdateSuccessful = You have changed the name of the resource group with ID [{0}] from [{1}] to [{2}]
view_group_summary_recursive=Rekursiv
@@ -1758,11 +2276,18 @@ view_inventory_allGroups=Alle Gruppen
view_inventory_allResources=Alle Ressourcen
view_inventory_collectionInterval=Erfassungs-Intervall
view_inventory_dynagroupDefs=Dynagroup-Definitionen
+view_inventory_eventDetails_loadFailed=Ein Fehler ist beim Laden der Ereignisdetails aufgetreten
+view_inventory_eventHistory_deleteFailed=Konnte die ausgewÀhlten Ereignisse fÌr [{0}] nicht löschen
+view_inventory_eventHistory_deleteSuccessful=Sie haben [{0}] Ereignisse fÌr [{1}] erfolgreich gelöscht
# #view_inventory_eventDetails_loadFailed = An error occurred loading the event details
# #view_inventory_eventHistory_deleteFailed = Failed to deleted selected events for [{0}]
# #view_inventory_eventHistory_deleteSuccessful = You have successfully deleted [{0}] events for [{1}]
view_inventory_eventHistory_details=Details
view_inventory_eventHistory_detailsFilter=Filter fÃŒr Details
+view_inventory_eventHistory_groupEventHistory=Gruppen Ereignisse Verlauf
+view_inventory_eventHistory_purgeFailed=Konnte Ereignisse fÃŒr [{0}] nicht bereinigen
+view_inventory_eventHistory_purgeSuccessful=Sie haben [{0}] Ereignisse fÃŒr [{1}] erfolgreich bereinigt
+view_inventory_eventHistory_resourceEventHistory=Verlauf Ressourcen Ereignisse
# #view_inventory_eventHistory_groupEventHistory = Group Event History
# #view_inventory_eventHistory_purgeFailed = Failed to purge events for [{0}]
# #view_inventory_eventHistory_purgeSuccessful = You have successfully purged [{0}] events for [{1}]
@@ -1770,10 +2295,15 @@ view_inventory_eventHistory_detailsFilter=Filter fÃŒr Details
view_inventory_eventHistory_severity=Schwere
view_inventory_eventHistory_severityFilter=Filter fÃŒr Schwere
view_inventory_eventHistory_sourceFilter=Filter fÃŒr Quelle
+view_inventory_eventHistory_sourceLocation=Speicherort Quelle
# #view_inventory_eventHistory_sourceLocation = Source Location
view_inventory_eventHistory_timestamp=Zeitunkt
view_inventory_groups=Gruppen
view_inventory_groups_children=Kinder
+view_inventory_groups_deleteFailed=Konnte die gewÀhlten Ressourcen-Gruppen nicht löschen
+view_inventory_groups_deleteSuccessful=Sie haben die gewÀhlten Ressourcen Gruppen erfolgreich gelöscht
+view_inventory_groups_descendants=Abkömmlinge
+view_inventory_groups_loadFailed=Konnte die zusammengesetzten Gruppendaten nicht laden
# #view_inventory_groups_deleteFailed = Failed to delete the selected resource groups
# #view_inventory_groups_deleteSuccessful = You have successfully deleted the selected resource groups
# #view_inventory_groups_descendants = Descendants
@@ -1782,18 +2312,38 @@ view_inventory_ignoredResources=Ignorierte Ressourcen
view_inventory_mixed=gemischt
view_inventory_platforms=Platformen
view_inventory_problemGroups=Gruppen mit Problemen
+view_inventory_resource_loadFailed=Ressource mit [{0}] existiert nicht oder ist nicht zugÀnglich
# #view_inventory_resource_loadFailed = Resource with id [{0}] does not exist or is not accessible
view_inventory_resources_deleteConfirm=Sind Sie sicher, dass Sie die ausgewÀhlten Ressourcen löschen wollen?
view_inventory_resources_deleteFailed=Löschen der ausgewÀhlten Ressourcen ist fehlgeschlagen
+view_inventory_resources_deleteFailed2=Löschen der gewÀhlten Ressourcen fehlgeschlagen. Verbindung mit dem Agent nicht möglich. Dies kann darauf hindeuten, dass der Agent nicht in Betrieb ist.
+view_inventory_resources_deleteSuccessful=Eine Anfrage zur DurchfÌhrung einer Ressourcenlöschung wurde erfolgreich bei dem/den Agent(en) eingereicht.
+view_inventory_resources_disableConfirm=Sind Sie sicher, dass Sie die ausgewÀhlten Ressourcen deaktivieren wollen? Vom Agent gemeldete, deaktivierte VerfÃŒgbarkeiten werden ignoriert. Die Deaktivierung kann fÃŒr Ressourcen von Nutzen sein, von denen erwartet wird, dass sie als Teil der normalen Operationen oder Wartung auÃer Betrieb sind.
view_inventory_resources_disableFailed=Deaktivierung der ausgewÀhlten Ressourcen ist fehlgeschlagen
+view_inventory_resources_disableSuccessful=Sie haben die gewÀhlten Ressourcen und deren untergeordnete Ressourcen erfolgreich deaktiviert, [{0}] Ressourcen.
+view_inventory_resources_enableConfirm=Sind Sie sicher, dass Sie die ausgewÀhlten Ressourcen deaktivieren wollen? Wenn aktiviert, so wird die VerfÌgbarkeit auf UNKNOWN (unbekannt) eingestellt, bis die Agenten das nÀchste Mal zur VerfÌgbarkeit der Ressourcen Bericht erstatten. Bei den Agenten wird um die möglichst schnelle Berichterstattung der aktuellen VerfÌgbarkeiten angefragt.
view_inventory_resources_enableFailed=Aktivierung der ausgewÀhlten Ressourcen ist fehlgeschlagen.
+view_inventory_resources_enableSuccessful=Sie haben die gewÀhlten Ressourcen und deren untergeordnete Ressourcen erfolgreich aktiviert, [{0}] Ressourcen.
+view_inventory_resources_ignoreConfirm=Sind Sie sicher, dass die gewÀhlten Ressourcen ignoriert werden sollen? Sie werden dann nicht mehr im Inventar angezeigt.
# #view_inventory_resources_deleteSuccessful = A request to perform the resource deletion has been submitted successfully to the agent(s).
# #view_inventory_resources_disableSuccessful = You have successfully disabled the selected resources and their children, [{0}] resources.
# #view_inventory_resources_ignoreConfirm = Are you sure you want the selected resources to be ignored? They will no longer show up in inventory.
view_inventory_resources_ignoreFailed=Konnte die Ressourcen nicht ignorieren
+view_inventory_resources_ignoreSkipAllPlatforms=Sie können Plattformen nicht ignorieren. Alle Ihre Auswahlen sind Plattformen, daher geschieht nichts. Falls Sie eine Plattform nicht mehr managen wollen, so beenden Sie deren assoziierten Agent und entfernen Sie die Plattform aus dem Inventar.
+view_inventory_resources_ignoreSkipSomePlatforms=Sie können Plattformen nicht ignorieren. Die [{0}] Plattformen, die Sie gewÀhlt haben, werden Ìbersprungen. Falls Sie eine Plattform nicht mehr managen wollen, so beenden Sie deren assoziierten Agent und entfernen Sie die Plattform aus dem Inventar.
# #view_inventory_resources_ignoreSkipAllPlatforms = You cannot ignore platforms. All of your selections are platforms so nothing will be done. If you no longer want to manage a platform, shutdown its associated agent and uninventory the platform.
# #view_inventory_resources_ignoreSkipSomePlatforms = You cannot ignore platforms. The [{0}] platforms you selected will be skipped. If you no longer want to manage a platform, shutdown its associated agent and uninventory the platform.
view_inventory_resources_ignoreSuccessful=Sie haben die ausgewÀhlten Ressourcen erfolgreich ignoriert
+view_inventory_resources_ignoreSuccessfulSkipPlatforms=Sie haben einige der gewÀhlten Ressourcen erfolgreich ignoriert, jedoch wurden die [{0}] Plattformen, die Sie gewÀhlt haben, Ìbersprungen. Falls Sie eine Plattform nicht mehr managen wollen, so beenden Sie deren assoziierten Agent und entfernen Sie die Plattform aus dem Inventar.
+view_inventory_resources_loadFailed=Konnte die zusammengesetzten Ressourcendaten nicht laden
+view_inventory_resources_members=Mitglieder Ressourcen
+view_inventory_resources_unignoreConfirm=Sind Sie sicher, dass das Ignorieren der gewÀhlten Ressourcen aufheben wollen? Sie werden dann wieder im Inventar angezeigt.
+view_inventory_resources_unignoreFailed=Konnte das Ignorieren fÃŒr die Ressourcen nicht aufheben.
+view_inventory_resources_unignoreSuccessful=Sie haben erfolgreich das Ignorieren der ausgewÀhlten Ressourcen aufgehoben.
+view_inventory_resources_uninventoryConfirm=Sind Sie sicher, dass Sie die ausgewÀhlten Ressourcen aus dem Inventar entfernen wollen? Beachten Sie dass - falls eine gewÀhlte Ressource noch vorhanden ist - sie beim nÀchsten Discovery Scan des Agents aufgespÌrt wird.
+view_inventory_resources_uninventoryFailed=Entfernung der ausgewÀhlten Ressourcen aus dem Inventar ist fehlgeschlagen
+view_inventory_resources_uninventoryStorageConfirm=Sie sind im indestenseine Ressource aus dem Inventar zu entfernen, die vom Storage Cluster verwendet wird. Um in Zukunft Fehler zu vermeiden, sollten Sie "undeploy the node" (Undeployment des Knotens) vor diesem Schritt ausfÌhren. Möchten Sie wirklich auf eigenes Risiko fortfahren?
+view_inventory_resources_uninventorySuccessful=Sie haben die ausgewÀhlten Ressourcen erfolgreich aus dem Inventar entfernt
# #view_inventory_resources_ignoreSuccessfulSkipPlatforms = You have successfully ignored some of the selected resources, however, the [{0}] platforms you selected were skipped. Platforms cannot be ignored. If you no longer want to manage a platform, shutdown its associated agent and uninventory the platform.
# #view_inventory_resources_loadFailed = Failed to load resource composite data
# #view_inventory_resources_members = Member Resources
@@ -1873,6 +2423,11 @@ view_operationHistoryDetails_status=Status
view_operationHistoryList_button_forceDelete=Löschen erzwingen
view_operationHistoryList_button_runOperation=Operation ausfÃŒhren
view_operationHistoryList_cancelConfirm=Sind Sie sicher, dass Sie die gewÀhlten Operationen abbrechen möchten? HINWEIS\: Nur diejenigen der gewÀhlten Operationen, die den Status "in progress" ("im Gange") besitzen, werden abgebrochen.
+view_operationHistoryList_cancelFailure=Die Anfrage zum Abbruch wurde fÃŒr die Operation mit der Historien ID [{0}] ist fehlgeschlagen.
+view_operationHistoryList_cancelSubmitted=Anfragen zum Abbruch von [{0}] "in progress" Operationen (im Gange) wurden eingereicht.
+view_operationHistoryList_cancelSuccess=Die Anfrage zum Abbruch wurde fÃŒr die Operation mit der Historien ID [{0}] erfolgreich eingereicht.
+view_operationHistoryList_deleteFailure=Konnte Operations Historie [{0}] nicht löschen.
+view_operationHistoryList_deletePartialSuccess=Es wurden [{0}] Operations Historienelemente gelöscht, aber Löschen fehlgeschlagen fÌr Elemente mit den folgenden IDs\: {1}
# #view_operationHistoryList_cancelConfirm = Are you sure you want to cancel the selected operations? NOTE: Only those selected operations that are currently "in progress" will be attempted to be canceled.
# #view_operationHistoryList_cancelFailure = The cancel request failed for the operation with the history ID of [{0}].
# #view_operationHistoryList_cancelSubmitted = Requests to cancel [{0}] "in progress" operations have been submitted.
@@ -1906,7 +2461,10 @@ view_portlet_defaultName_group_config_updates=Gruppen\: Aktualisierungen der Kon
view_portlet_defaultName_group_events=Gruppen\: Events
view_portlet_defaultName_group_metrics=Gruppen\: Metriken
view_portlet_defaultName_group_oobs=Gruppen\: OOB
+view_portlet_defaultName_group_operations=Gruppe\: Operationen
+view_portlet_defaultName_group_pkg_hisory=Gruppe\: Paketverlauf
view_portlet_defaultName_inventorySummary=InventarÃŒbersicht
+view_portlet_defaultName_mashup=Mashup
# #view_portlet_defaultName_mashup = Mashup
view_portlet_defaultName_message=Nachricht
view_portlet_defaultName_operations=KÃŒrzlich ausgefÃŒhrte Operationen
@@ -1914,6 +2472,7 @@ view_portlet_defaultName_platformSummary=Platformauslastung
view_portlet_defaultName_problemResources=Nicht verfÃŒgbare Resourcen oder mit Alarmen
view_portlet_defaultName_recentAlerts=KÌrzlich ausgelöste Alarme
view_portlet_defaultName_recentlyAddedResources=Zuletzt hinzugefÃŒgte Ressourcen
+view_portlet_defaultName_resourceMetric=Charts fÃŒr Ressourcen-Metriken
view_portlet_defaultName_resource_alerts=Ressource\: Alarme
view_portlet_defaultName_resource_bundles=Ressource\: Bundle Deployments
view_portlet_defaultName_resource_config_updates=Ressource\: Konfigurationsaktualisierungen
@@ -1925,6 +2484,10 @@ view_portlet_defaultName_resource_pkg_hisory=Ressource\: Paketverlauf
view_portlet_factory_invalidPortlet=Dies ist ein obsoletes Portlet, das nicht mehr gÌltig ist. Bitte löschen Sie es.
view_portlet_graph_configure_metricDefinition_graph=Die ID der Metrik, die dargestellt werden soll
view_portlet_graph_configure_resource_graph=Die Ressource deren Metrik dargestellt werden soll
+view_portlet_help_autodiscovery=Dieses Portlet gestattet das Importieren oder Ignorieren von neu aufgefundenen Ressourcen. Importierte Ressourcen werden dem Inventar zur Beobachtung und Verwaltung hinzugefÃŒgt. Ignorierte Ressourcen werden nicht importiert und bleiben der Ansicht verborgen falls das Ignorieren nicht explizit aufgehoben wird.
+view_portlet_help_bundle_deps=Dieses Portlet zeigt die relevante Bundle Deployments an, basierend auf den konfigurierten Anzeigekriterien.
+view_portlet_help_config_updates=Dieses Portlet zeigt aktuelle KonfigurationsÀnderungen, die konsistent mit Konfigurationseinstellungen sind.
+view_portlet_help_eventcounts=Dieses Portlet zeigt EreigniszÀhlungen an, die konsistent mit den konfigurierten Anzeigekriterien sind.
# #view_portlet_graph_configure_title = Graph Config
# #view_portlet_graph_configure_title_desc = Configuration of the graph portlet
# #view_portlet_graph_help_msg = This Portlet supports the graphing of a resource metric.
@@ -1932,12 +2495,23 @@ view_portlet_graph_configure_resource_graph=Die Ressource deren Metrik dargestel
# #view_portlet_graph_help_unconfigured = This graph is unconfigured, click the settings button to configure.
# #view_portlet_graph_title = Resource Graph
view_portlet_help_favoriteResources=Dieses Portlet zeigt die Lesezeichen des Benutzers fÃŒr Ressourcen
+view_portlet_help_graph=Dieses Portlet zeigt das Ressourcen Metrik Diagramm an.
view_portlet_help_inventorySummary=Dieses Portlet zeigt eine Ãbersicht ÃŒber das fÃŒr den Benutzer sichtbare Inventar
view_portlet_help_mashup=Dieses Portlet zeigt den Inhalt eines HTTP-Requests in einem IFrame dar.
view_portlet_help_message=Dieses Portlet zeigt eine statische HTML-Seite an. Die <i>Nachricht</i> kann konfiguriert werden.
+view_portlet_help_metrics=Dieses Portlet liefert eine grafische Darstellung relevanter metrischer Daten basierend auf den konfigurierten Anzeigekriterien.
view_portlet_help_none=FÃŒr dieses Portlet ist keine Hilfe verfÃŒgbar
view_portlet_help_oobs=Dieses Portlet zeigt Metriken, die aus dem Baseband gelaufen sind
+view_portlet_help_operations=Dieses Portlet zeigt die zuletzt ausgefÃŒhrten Operationen fÃŒr das Inventar des aktuellen Benutzers an.
+view_portlet_help_operations_criteria=Dieses Portlet zeigt Operationen an, die konsistent mit den konfigurierten Anzeigekriterien sind.
+view_portlet_help_pkg_history=Dieses Portlet zeigt die relevante Paket Historie an, basierend auf den konfigurierten Anzeigekriterien.
+view_portlet_help_platformSummary=Dieses Portlet zeigt die Auslastungsdaten (wie etwa aktuelle CPU- und Speicherauslastung) fÃŒr Plattform-Ressourcen an, auf die der aktuelle Benutzer zugreifen kann.
view_portlet_help_problemResources=Dieses Portlet zeigt die "alerted" oder "unavailable" Ressourcen des aktuellen Benutzers an.
+view_portlet_help_recentAlerts=Dieses Portlet zeigt Alarm Meldungen an, die aktuell am sichtbaren Inventar des Benutzers herausgegeben werden.
+view_portlet_help_recentDrifts=Dieses Portlet zeigt den aktuellen Datei Drift am sichtbaren Inventar des Benutzers an.
+view_portlet_help_recentlyAdded=Dieses Portlet zeigt Ressourcen an, die aktuell in das Inventar importiert wurden.
+view_portlet_help_scheduledOperations=Dieses Portlet zeigt die als nÀchstes terminierten Operationen fÌr das Inventar des aktuellen Benutzers an.
+view_portlet_help_tagCloud=Dieses Portlet zeigt die relative Tag ZÀhlungen fÌr das Inventar des aktuellen Benutzers an.
# #view_portlet_help_recentAlerts = This portlet displays alerts recently fired on the current user''s viewable inventory.
# #view_portlet_help_recentDrifts = This portlet displays recent file drift on the current user''s viewable inventory.
view_portlet_inventory_error1=Konnte die InventarÃŒbersicht nicht laden
@@ -1946,7 +2520,9 @@ view_portlet_inventory_tooltip_expand=Klicken, um mehr Details fÃŒr diese Ressou
view_portlet_message_title=Nachricht
# #view_portlet_message_unconfigured = Message not yet configured, click the settings button to setup this portlet.
view_portlet_operations_config_completed=Fertiggestellte Operationen
+view_portlet_operations_config_completed_enable=Ob die Gruppierung fertiggestellter Operationsergebnisse fÃŒr das Dashboard aktiviert werden soll.
view_portlet_operations_config_completed_maximum=Maximale Anzahl anzuzeigender abgeschlossener Operationen.
+view_portlet_operations_config_scheduled_enable=Ob die Gruppierung terminierter Operationsergebnisse fÃŒr das Dashboard aktiviert werden soll.
view_portlet_operations_config_scheduled_maximum=Maximale Anzahl anzuzeigender geplanter Operationen.
# #view_portlet_operations_config_completed_enable = Whether to enable completed operations results grouping for dashboard.
# #view_portlet_operations_config_completed_maximum = Maximum number of Completed operations to display.
@@ -1958,11 +2534,16 @@ view_portlet_operations_disabled=(Ergebnisse derzeit deaktiviert. Ãndern Sie di
view_portlet_platform_platform_error_1=Laden der Plattform Metriken fehlgeschlagen
view_portlet_platform_type_error_1=Konnte Typdaten nicht laden
view_portlet_problemResources_config_display_maximum=Maximale Anzahl anzuzeigender Problemressourcen.
+view_portlet_problemResources_config_display_range=Problem Ressourcen um so viele Stunden rÃŒckwirkend anzeigen.
view_portlet_problemResources_config_display_range2=Von {0} bis {1}
view_portlet_problemResources_maxDisplaySetting=Maximaler Ressourcen.
+view_portlet_recentAlerts_config_members=Mitglieder auswÀhlen
+view_portlet_recentAlerts_config_priority_label=PrioritÀt Alarme
# #view_portlet_recentAlerts_config_members = Select Members
# #view_portlet_recentAlerts_config_priority_label = priority Alerts,
view_portlet_recentAlerts_config_when=innerhalb der letzten
+view_portlet_recentAlerts_fail_msg=Laden von fÃŒr Alarm Filterung zugewiesenen Ressourcen fehlgeschlagen.
+view_portlet_recentlyAdded_error1=Laden zuletzt hinzugefÃŒgter Ressourcen fehlgeschlagen
view_portlet_recentlyAdded_setting_addedPlatforms=zuletzt hinzugefÃŒgte Plattformen
# #view_portlet_recentlyAdded_approved_platforms = recently approved platforms on dashboard.
# #view_portlet_recentlyAdded_error1 = Failed to load recently added resources
@@ -1974,6 +2555,9 @@ view_remoteAgentInstall_agentStatusDefault=-Klicken Sie auf SchaltflÀche Status
# #view_remoteAgentInstall_agentStatusDefault = -Click Update Status Button-
view_remoteAgentInstall_buttonFindAgent=Agent suchen
view_remoteAgentInstall_connInfo=Verbindungsinformationen
+view_remoteAgentInstall_error_1=Fehler bei der Suche nach dem Pfad der Agent-Installation
+view_remoteAgentInstall_error_2=Bei der Suche in Speicherorten konnte kein installierter Agent gefunden werden
+view_remoteAgentInstall_error_3=Konnte keinen an oder unter [{0}] installierten Agent finden
# #view_remoteAgentInstall_connInfo = Connection Information
# #view_remoteAgentInstall_error_1 = Error occurred while trying to find agent install path
# #view_remoteAgentInstall_error_2 = Could not find an agent installed when looking in common locations
@@ -2003,6 +2587,7 @@ view_reportsTop_description=Dieser Abschnitt bietet Zugang zu applikationsweiten
view_reportsTop_title=Berichte
view_reports_alertDefinitions=Alarmierungskriterien
view_reports_alertDefinitions_parentHover=Klicken, um zur ÃŒbergeordneten Alarm-Definition zu gelangen
+view_reports_alertDefinitions_resTypeLoadError=Erhalt von Ressourcentyp Vorlage nicht möglich - Ansicht von Alarm Vorlage nicht möglich.
# #view_reports_alertDefinitions_resTypeLoadError = Cannot get the template resource type - unable to view the alert template.
view_reports_driftCompliance=Drift-Ãbereinstimmung
view_reports_inventorySummary_failFetch=Konnte die Inventar-Zusammenfassung nicht laden
@@ -2012,11 +2597,15 @@ view_resourceResourceGroupList_error_fetchFailure=Abruf der Gruppen der Ressourc
view_resourceResourceGroupList_error_updateFailure=Fehler beim Aktualisieren der zugewiesenen Ressourcen-Gruppen.
view_resourceResourceGroupList_message_updateSuccess=Gruppenmitgliedschaft aktualisiert fÃŒr [{0}].
view_resource_inventory_activity_changed_by=GeÀndert von
+view_resource_inventory_activity_criteria_no_recent_events=Keine EreigniszÀhlungen basieren auf Anzeigekriterien.
view_resource_inventory_activity_no_recent_alerts=Es liegen keine aktuellen Alarme vor
view_resource_inventory_activity_no_recent_bundle_deploy=Es liegen keine aktuelle BÃŒndel-Deployments vor
+view_resource_inventory_activity_no_recent_config_history=Keine KonfigurationsÀnderungen Historie
view_resource_inventory_activity_no_recent_events=Keine Ereignisse in den letzten 24 Stunden
view_resource_inventory_activity_no_recent_metrics=Diese Ressource hat keine aktuellen Metriken
view_resource_inventory_activity_no_recent_oob=Keine OOB Bedingungen gefunden
+view_resource_inventory_activity_no_recent_operations=Keine aktuelle Operations-Historie
+view_resource_inventory_activity_no_recent_pkg_history=Keine aktuelle Paket Historie
view_resource_inventory_childhistory_createdChild=Kind erstellt
view_resource_inventory_childhistory_deletedChild=Kind gelöscht
# #view_resourceResourceGroupList_error_fetchFailure = Failed to fetch Resource''s groups.
@@ -2080,8 +2669,11 @@ view_resource_monitor_table_last=Live Wert
# #view_resource_monitor_table_last = Live Value
view_resource_monitor_table_max=Maximum
view_resource_monitor_table_min=Minimum
+view_resource_title_component_errors_cleanup=Nach Behebung dieses Problems, werden Sie die Nachricht unten löschen mÌssen, um das 'managed component error' (Fehler bei gemanagter Komponente) Symbol vom vorherigen Bildschirm zu löschen.
view_resource_title_component_errors_tooltip=Zeigt Fehler der gemanagten Ressource. Klicken fÃŒr Details
view_resource_title_tagUpdateFailed=Fehler beim Aktualisieren der Ressourcen-Tags
+view_searchBar_buttonTooltip=Zum Anzeigen/Verbergen der Suchvorschlagsliste klicken. Verbergen der Liste erfolgt auch durch DrÃŒcken auf Escape wenn der Fokus in der Suchmuster Textbox ist.
+view_searchBar_savedSearch_buttonTooltip=Klicken Sie, um in den Saved Search (Gespeicherte Suche) Modus umzuschalten. Wenn aktiv, speichern Sie das aktuelle Muster, indem Sie einen Namen eingeben und auf die Eingabetaste klicken. Bearbeiten Sie eine bestehende Suche, indem Sie sie in der Liste auswÀhlen, das Muster oder den Namen aktualisieren und im NamenstextkÀstchen auf Eingabe drÌcken. Das Löschen erfolgt durch Doppelklicken des Listeneintrags.
# #view_searchBar_buttonTooltip = Click to hide/show the search suggestion list. Also hide the list by hitting Escape when focus is in the search pattern text box.
# #view_searchBar_savedSearch_buttonTooltip = Click to toggle Saved Search mode. When active, save the current pattern by entering a name and hitting return. Edit an existing search by selecting it in the list, updating the pattern or name, and hitting return in the name text box. Delete by double-clicking the list entry.
view_searchBar_savedSearch_confirmDelete=Die gespeicherte Suche mit dem Namen [{0}] löschen?
@@ -2101,6 +2693,7 @@ view_searchGUI_loginStatus=Kann Login-Status nicht bestimmen, ÃŒberprÃŒfen Sie d
view_selector_assigned=Zugewiesen {0}
view_selector_available=VerfÃŒgbar {0}
view_subTab_error_disabled=Kann den deaktivierten Unter-Reiter [{0}] nicht anwÀhlen.
+view_summaryDashboard_resetConfirm=Auf standardmÀÃiges Ãbersichts-Dashboard zurÃŒcksetzen (lokale Ãnderungen gehen verloren)?
view_summaryOverviewForm_error_descriptionChangeFailure=Konnte die Beschreibung der Ressource mit der id {0} nicht von [{1}] auf [{2}] Àndern.
view_summaryOverviewForm_error_locationChangeFailure=Konnte den Ort der Ressource mit der id {0} nicht von [{1}] auf [{2}] Àndern.
view_summaryOverviewForm_error_nameChangeFailure=Konnte den Namen der Ressource mit der id {0} nicht von [{1}] auf [{2}] Àndern.
@@ -2204,6 +2797,7 @@ view_tree_common_contextMenu_type_name_label=Typ\: {0}
view_tree_common_createFailed_autoCluster=Erstellung oder Aktualisierung von autocluster Backing Gruppe fehlgeschlagen
view_tree_common_loadFailed_children=Konnte die Kinder fÃŒr den Knoten nicht laden
view_tree_common_loadFailed_create=Konnte die Ansicht fÃŒr diesen Knoten nicht erzeugen
+view_tree_common_loadFailed_descendants=Konnte Abkömmlinge fÌr Baumansicht nicht laden
# #view_tree_common_loadFailed_descendants = Failed to load descendants for tree
view_tree_common_loadFailed_generic=Konnte die Daten fÃŒr den Bau nicht laden
view_tree_common_loadFailed_group=Konnte die Gruppe mit ID [{0}] nicht laden
@@ -2305,6 +2899,7 @@ widget_resourceFactoryWizard_execute1=Konnte keine neue Ressource anlegen - es w
widget_resourceFactoryWizard_execute2=Erstellung einer neuen Ressource fehlgeschlagen. Verbindung mit dem Agent nicht möglich. Dies kann darauf hindeuten, dass der Agent nicht in Betrieb ist.
widget_resourceFactoryWizard_execute3=Erstellung einer neuen Ressource fehlgeschlagen.
widget_resourceFactoryWizard_failedToDeleteVersion=Löschen der Paketversion fehlgeschlagen wÀhrend Abbruch einer Ressourcenerstellung
+widget_resourceFactoryWizard_failedToGetType=Erhalt des Pakettyps fÃŒr neue Ressource fehlgeschlagen
# widget_resourceFactoryWizard_execute2 = Failed to create a new resource. Cannot connect to the agent. This may indicate that the agent is down.
# #widget_resourceFactoryWizard_failedToDeleteVersion = Failed to delete package version while canceling a resource create
# #widget_resourceFactoryWizard_failedToGetType = Failed to get backing package type for new resource
@@ -2316,12 +2911,16 @@ widget_resourceFactoryWizard_importWizardTitle=Importieren von Ressourcen des Ty
widget_resourceFactoryWizard_importWizardWindowTitle=Wizard zum Import von Ressourcen
widget_resourceFactoryWizard_infoStepName=Information ÃŒber die Ressource
widget_resourceFactoryWizard_infoStep_loadFail=Konnte die verfÃŒgbaren Architekturen nicht ermitteln
+widget_resourceFactoryWizard_nameComment=Nicht alle Management Plug-ins oder deren gemanagte Ressourcen gestatten dem Agent die Einstellung des Namens fÌr eine neue Ressource. Dieser Wert wird nur von Agent Plug-ins verwendet, die diese Möglichkeit unterstÌtzen. FÌr Plug-ins, die dies nicht unterstÌtzen, kann die Ressource bei deren Auffinden einen generischen oder anderen Namen erhalten.
widget_resourceFactoryWizard_namePrompt=Name der neuen Ressource
# widget_resourceFactoryWizard_nameComment = Not all management plug-ins or their managed resources allow the agent to set the name for a new resource. This value will only be used by agent plug-ins that support the capability. For plug-ins that do not support the capability, the resource may receive a generic or different name when it is discovered.
widget_resourceFactoryWizard_templatePrompt=Vorlage fÃŒr die Verbindungseinstellungen
widget_resourceFactoryWizard_timeoutFailure=Timeout
+widget_resourceFactoryWizard_timeoutHelp=Eine Timeout Dauer, die - falls festgelegt - den standardmÀÃigen Timeout fÃŒr die Erstellung der untergeordneten Ressource auÃer Kraft setzt (am {0} Agent). Der standardmÀÃige Timeout ist auf 60 Sekunden eingestellt. Ein höherer Wert kann von bei besonders langen ErstellungsvorgÀngen von Nutzen sein, wie etwa bei dem Deployment einer groÃen Applikation. Wird in der Regel verwendet, wenn es beim vorherigen Versuch zu einem Fehlschlagen aufgrund eines Timeout kam. Beachten Sie, dass es bei einem Fehlschlagen aufgrund eines Timeout trotzdem möglich ist, dass das Ressourcen Deployment erfolgreich verlief. Im Falle eines Timeout empfiehlt sich ein Discovery Scan vor dem erneuten Versuch des Deployments der Ressource.
# #widget_resourceFactoryWizard_timeoutHelp = A timeout duration that if specified will override the default timeout for child resource creation (on the {0} Agent). The default timeout is set to 60 seconds. A higher value may be useful for particularly long create actions, like deployment of a large application. Usually used if a previous attempt suffered a timeout failure. Note that if there is a timeout failure, it is still possible that the resource deployment succeeded. In the event of a timeout you may want to execute a discovery scan before attempting to redeploy the resource.
widget_resourceFactoryWizard_uploadFailure=Konnte die Datei nicht hochladen
+widget_resourceFactoryWizard_uploadFileStepName=Laden Sie die Ressourcen Content Datei hoch
+widget_resourceFactoryWizard_uploadInProgress=Datei wird hochgeladen ... Dies kann fÃŒr groÃe Distributionsdateien mehrere Minuten dauern.
# ##widget_resourceFactoryWizard_timeoutFailure = Timed out
# #widget_resourceFactoryWizard_uploadFileStepName = Upload Resource Content File
# #widget_resourceFactoryWizard_uploadInProgress = The upload is in progress... This can take several minutes to complete for large distribution files.
diff --git a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_ja.properties b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_ja.properties
index 328383c..aa366a0 100644
--- a/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_ja.properties
+++ b/modules/enterprise/gui/coregui/src/main/resources/org/rhq/coregui/client/Messages_ja.properties
@@ -1358,36 +1358,36 @@ view_alert_definition_notification_operation_editor_specific_resource=ãªãœãŒ
view_alert_definition_notification_role_editor_loadFailed=çŸåšã®ããŒã«ã決å®ã§ããŸãã - 空ã§éå§ããŸã
view_alert_definition_notification_role_editor_restoreFailed=çŸåšã®ããŒã«ãå©çšã§ããŸãã - 空ããéå§ããŸã
view_alert_definition_notification_role_editor_saveFailed=éžæãããããŒã«ãä¿åã§ããŸãã
-view_alert_definition_notification_user_editor_loadFailed=çŸåšã®ãŠãŒã¶ãŒã決å®ã§ããŸãã - 空ããéå§ããŸã
-view_alert_definition_notification_user_editor_restoreFailed=çŸåšã®ãŠãŒã¶ãŒã䜿çšã§ããŸãã - 空ããéå§ããŸã
+view_alert_definition_notification_user_editor_loadFailed=çŸåšã®ãŠãŒã¶ãŒã決å®ã§ããŸãã - 空ã§éå§ããŸã
+view_alert_definition_notification_user_editor_restoreFailed=çŸåšã®ãŠãŒã¶ãŒã䜿çšã§ããŸãã - 空ã§éå§ããŸã
view_alert_definition_notification_user_editor_saveFailed=éžæãããŠãŒã¶ãŒãä¿åã§ããŸãã
-view_alert_definition_recovery_editor_disable_when_fired=çºç«æéãç¡å¹ã«ãã
-view_alert_definition_recovery_editor_disable_when_fired_tooltip=ãã®ã¢ã©ãŒããçºç«ããåŸã«ç¡å¹ã«ãããã¹ããæå®ããŸããäžåºŠç¡å¹ã«ããããšããã®ã¢ã©ãŒãã¯æåã§å床æå¹ã«ãããããŸãã¯èªåçã«å床æå¹ã«ããããã®ãªã«ããªã¢ã©ãŒããèšå®ã§ããŸãã
+view_alert_definition_recovery_editor_disable_when_fired=çºçæã«ç¡å¹
+view_alert_definition_recovery_editor_disable_when_fired_tooltip=ãã®ã¢ã©ãŒããçºçããåŸã«ç¡å¹ã«ãªããã©ããã瀺ããŸããç¡å¹ã«ãªã£ãã¢ã©ãŒãã¯ãæäœæ¥ã§å床æå¹ã«ã§ãããªã«ããªã¢ã©ãŒããèšå®ããŠèªåçã«å床æå¹ã«ããããšãå¯èœã§ãã
view_alert_definition_recovery_editor_loadFailed=ãªã«ããªã¡ãã¥ãŒãæ§ç¯ã§ããŸãã
view_alert_definition_recovery_editor_none_available=ãªã
view_alert_definition_recovery_editor_recovery_alert=ã¢ã©ãŒãã®ãªã«ããª
-view_alert_definition_recovery_editor_recovery_alert_tooltip=ã¢ã©ãŒããåŒãèµ·ããããåŸã«ãªã«ããªããã(ã€ãŸããå床æå¹å)ã¿ãŒã²ããã¢ã©ãŒãããªã«ããªã¢ã©ãŒããå®çŸ©ããã®ã§ã¯ãªãã®ãªãããã§ã¢ã©ãŒããéžæããªãã§ãã ããã
+view_alert_definition_recovery_editor_recovery_alert_tooltip=ã¢ã©ãŒããåŒãèµ·ããããåŸã«ãªã«ããªããã (åæå¹åãªã©) ã¿ãŒã²ããã¢ã©ãŒãããªã«ããªã¢ã©ãŒããå®çŸ©ããå Žåã®ã¿ã¢ã©ãŒããéžæããŠãã ããã
view_alert_definitions_create_failure=ã¢ã©ãŒãå®çŸ©ã®äœæã«å€±æããŸãã
view_alert_definitions_create_success=ã¢ã©ãŒãå®çŸ©ã®äœæã«æåããŸãã
view_alert_definitions_delete_confirm=éžæãããã¢ã©ãŒãå®çŸ©ãåé€ããŸãã?
view_alert_definitions_delete_failure=éžæãããã¢ã©ãŒãå®çŸ©ãåé€ããã®ã«å€±æããŸãã
-view_alert_definitions_delete_success={0} ã¢ã©ãŒãå®çŸ©ãåé€ããã®ã«æåããŸãã
+view_alert_definitions_delete_success={0} ã®ã¢ã©ãŒãå®çŸ©ãæ£åžžã«åé€ããŸãã
view_alert_definitions_disable_confirm=éžæãããã¢ã©ãŒãå®çŸ©ãç¡å¹ã«ããŸãã?
view_alert_definitions_disable_failure=éžæãããã¢ã©ãŒãå®çŸ©ãç¡å¹ã«ããã®ã«å€±æããŸãã
-view_alert_definitions_disable_success={0} ã¢ã©ãŒãå®çŸ©ãç¡å¹ã«ããã®ã«æåããŸãã
-view_alert_definitions_enable_confirm=éžæãããã¢ã©ãŒãå®çŸ©ãã«ããŸãã?
+view_alert_definitions_disable_success={0} ã®ã¢ã©ãŒãå®çŸ©ãç¡å¹ã«ããŸãã
+view_alert_definitions_enable_confirm=éžæãããã¢ã©ãŒãå®çŸ©ãæå¹ã«ããŸãã?
view_alert_definitions_enable_failure=éžæãããã¢ã©ãŒãå®çŸ©ãæå¹ã«ããã®ã«å€±æããŸãã
-view_alert_definitions_enable_success={0} ã¢ã©ãŒãå®çŸ©ãæå¹ã«ããã®ã«æåããŸãã
+view_alert_definitions_enable_success={0} ã®ã¢ã©ãŒãå®çŸ©ãæå¹ã«ããŸãã
view_alert_definitions_leaveUnsaved=ç·šéããã¢ã©ãŒãå®çŸ©ãä¿åããŸãã?
# #view_alert_definitions_leaveUnsaved = Do you want to save the modified alert definition?
-view_alert_definitions_loadFailed=ã¢ã©ãŒãå®çŸ©ããŒã¿ãåãåºãã®ã«å€±æããŸãã
-view_alert_definitions_loadFailed_single=ID {0} ã®ä»ããã¢ã©ãŒãå®çŸ©ã®ããŒã¿ãåãåºãã®ã«å€±æããŸãã
+view_alert_definitions_loadFailed=ã¢ã©ãŒãå®çŸ©ããŒã¿ã®ååŸã«å€±æããŸãã
+view_alert_definitions_loadFailed_single=ID {0} ã®ã¢ã©ãŒãå®çŸ©ã®ããŒã¿ãååŸã§ããŸããã§ãã
view_alert_definitions_table_title_group=ã°ã«ãŒãã¢ã©ãŒãå®çŸ©
view_alert_definitions_table_title_resource=ãªãœãŒã¹ã¢ã©ãŒãå®çŸ©
view_alert_definitions_update_failure=ã¢ã©ãŒãå®çŸ©ã®æŽæ°ã«å€±æããŸãã
view_alert_definitions_update_success=ã¢ã©ãŒãå®çŸ©ã®æŽæ°ã«æåããŸãã
-view_alert_details_field_ack_at=次ã®å Žæã§ç¢ºèªæžã¿ã§ã
-view_alert_details_field_ack_by=次ã®äººã«ãã£ãŠç¢ºèªæžã¿ã§ã
+view_alert_details_field_ack_at=確èªå Žæ\:
+view_alert_details_field_ack_by=確èªè
\:
view_alert_details_field_parent_definition=芪å®çŸ©
# #view_alert_details_field_parent_definition = Parent definition
view_alert_details_field_recovery_info=ãªã«ããªæ
å ±
@@ -1395,12 +1395,12 @@ view_alert_details_field_resource_ancestry=ãªãœãŒã¹ã®ç¥å
view_alert_details_field_watched_resource=ãŠã©ããããããªãœãŒã¹
# #view_alert_details_field_resource_ancestry = Resource Ancestry
# #view_alert_details_field_watched_resource = Watched Resource
-view_alert_details_loadFailed=ã¢ã©ãŒãã®è©³çŽ°ãåãåºãã®ã«å€±æããŸãã
+view_alert_details_loadFailed=ã¢ã©ãŒã詳现ã®ååŸã«å€±æããŸãã
view_alerts_ack_confirm=éžæãããã¢ã©ãŒãã確èªããŸãã?
view_alerts_ack_confirm_all=ãã®ãœãŒã¹ãããã¹ãŠã®ã¢ã©ãŒãã確èªããŸãã?
-view_alerts_ack_failure=ID {0} ã®ä»ããã¢ã©ãŒãã確èªããã®ã«å€±æããŸãã
-view_alerts_ack_failure_all=ãã®ãœãŒã¹ãããã¹ãŠã®ã¢ã©ãŒãã確èªããã®ã«å€±æããŸãã
-view_alerts_ack_success={0} ã¢ã©ãŒãã®ç¢ºèªã«æåããŸãã
+view_alerts_ack_failure=ID {0} ã®ã¢ã©ãŒãã確èªã§ããŸããã§ãã
+view_alerts_ack_failure_all=ãã®ãœãŒã¹ãããã¹ãŠã®ã¢ã©ãŒãã確èªã§ããŸããã§ãã
+view_alerts_ack_success={0} ã¢ã©ãŒãã確èªããŸãã
view_alerts_delete_confirm=éžæãããã¢ã©ãŒããåé€ããŸãã?
view_alerts_delete_confirm_all=ãã®ãœãŒã¹ãããã¹ãŠã®ã¢ã©ãŒããåé€ããŸãã?
view_alerts_delete_failure=ID {0} ã®ä»ããã¢ã©ãŒããåé€ããã®ã«å€±æããŸãã
@@ -1408,8 +1408,8 @@ view_alerts_delete_failure_all=ãã®ãœãŒã¹ãããã¹ãŠã®ã¢ã©ãŒãã
view_alerts_delete_success={0} ã¢ã©ãŒããåé€ããã®ã«æåããŸãã
view_alerts_field_ack_status=ã¹ããŒã¿ã¹
view_alerts_field_ack_status_ack=ç¢ºèª ({0})
-view_alerts_field_ack_status_ackHover={1} ã«ãã㊠{0} ã«ãã£ãŠç¢ºèªãããŸãã
-view_alerts_field_ack_status_noAck=æªç¢ºèª
+view_alerts_field_ack_status_ackHover={1} ã«ãã㊠{0} ã確èª
+view_alerts_field_ack_status_noAck=確èªãªã
view_alerts_field_ack_status_noAckHover=ãŸã 確èªãããŠããŸãã
view_alerts_field_ack_subject=確èªãµããžã§ã¯ã
view_alerts_field_ack_time=確èªæå»
@@ -1422,8 +1422,8 @@ view_alerts_field_modified_time=ä¿®æ£æå»
view_alerts_field_parent=芪
view_alerts_field_priority=åªå
床
view_alerts_field_protected=ä¿è·ãããŠãã
-view_alerts_field_protected_tooltip=ããtrueãªãããã®å®çŸ©ã¯èŠªå®çŸ©ã«ããå€æŽããä¿è·ãããŠããŸããä»ã®èšèã§èšãã°ããã®èŠªå®çŸ©èšå®ã¯ãã®å®çŸ©ãäžæžãããŸããã
-view_alerts_loadFailed=ã¢ã©ãŒãããŒã¿ãåãåºãã®ã«ããŸãã
+view_alerts_field_protected_tooltip=true ã®å Žåããã®å®çŸ©ã¯ä¿è·ããã芪å®çŸ©ãå€æŽã§ããŸããããã£ãŠã芪å®çŸ©ã®èšå®ã¯ãã®å®çŸ©ãäžæžãã§ããŸããã
+view_alerts_loadFailed=ã¢ã©ãŒãããŒã¿ã®ååŸã«å€±æããŸãã
view_alerts_table_filter_priority=åªå
床ãã£ã«ã¿ãŒ
view_alerts_table_title_group=ã°ã«ãŒãã¢ã©ãŒãå±¥æŽ
view_alerts_table_title_resource=ãªãœãŒã¹ã¢ã©ãŒãå±¥æŽ
@@ -1455,7 +1455,7 @@ view_autoDiscoveryQ_unignoreInProgress=éžæããããªãœãŒã¹ã®ç¡èŠã
view_autoDiscoveryQ_unignoreSuccessful=éžæãããªãœãŒã¹ã®ç¡èŠè§£é€ã«æåããŸãã
view_autoDiscoveryQ_uninventoried=ã€ã³ãã³ããªç»é²ãããªã
view_bundleGroup_assignFailPerm=ãã³ãã«ããã®ãã³ãã«ã°ã«ãŒããžå²ãåœãŠãæš©å©ããããŸããã管çè
ãžåãåãããŠãã ããã
-view_bundleGroup_deleteConfirm=ãã®ãã³ãã«ã°ã«ãŒããåé€ããŠãããããã§ãã? å¯äžå²ãåœãŠããããã³ãã«ã°ã«ãŒãã®ãã³ãã«ãå²ãåœãŠè§£é€ãšãªããé²èŠ§ã«ã°ããŒãã« View Bundles ããŒããã·ã§ã³ãå¿
èŠãšãªããŸãã
+view_bundleGroup_deleteConfirm=ãã®ãã³ãã«ã°ã«ãŒããåé€ããŠãããããã§ãã? å¯äžå²ãåœãŠããããã³ãã«ã°ã«ãŒãã®ãã³ãã«ãå²ãåœãŠè§£é€ãšãªããé²èŠ§ã«ã°ããŒãã«ã®ããã³ãã«ã®è¡šç€ºãããŒããã·ã§ã³ãå¿
èŠãšãªããŸãã
view_bundleGroup_deletesFailure=ãã³ãã«ã°ã«ãŒããåé€ã§ããŸããã§ãã
view_bundleGroup_deletesSuccessful=ãã³ãã«ã°ã«ãŒããæ£åžžã«åé€ããŸãã
view_bundleGroup_unassignFailPerm=ãã®ãã³ãã«ã°ã«ãŒããããã³ãã«ãå²ãåœãŠè§£é€ããæš©å©ããããŸããã管çè
ã«åãåãããŠãã ããã
@@ -1473,22 +1473,22 @@ view_bundle_bundleType=ãã³ãã«ã¿ã€ã
view_bundle_bundleVersion=ãã³ãã«ããŒãžã§ã³
view_bundle_bundleVersions=ãã³ãã«ããŒãžã§ã³
view_bundle_createWizard_bundleDistro=ãã³ãã«é
åž
-view_bundle_createWizard_cancelFailure=ãã³ãã« [{0}], ããŒãžã§ã³ \= [{1}] ã®äœæã®å®å
šãªãã£ã³ã»ã«ã«å€±æããŸãã - ãã³ãã«ã¯ããŒã¿ããŒã¹ã«ãŸã æ®ã£ãŠãããããããŸãã
+view_bundle_createWizard_cancelFailure=ãã³ãã« [{0}]ãããŒãžã§ã³ \= [{1}] ã®äœæãå®å
šã«ãã£ã³ã»ã«ã§ããŸããã§ãã - ãã³ãã«ããŸã ååšããå¯èœæ§ããããŸãã
view_bundle_createWizard_cancelFailurePerm=ãŠãŒã¶ãŒãããŒããã·ã§ã³ãäœæããåé€ããªãã£ãããããã³ãã« [{0}]ãããŒãžã§ã³ \= [{1}] ã®äœæãå®å
šã«ãã£ã³ã»ã«ã§ããŸããã§ããã管çè
ã«ãããã³ãã«ã®åé€ãå¿
èŠã«ãªããšäºæ³ãããŸãã
# #view_bundle_createWizard_cancelFailurePerm = Failed to fully cancel the creation of bundle [{0}], version = [{1}] because the user has create but not delete permissions. The bundle will likley need to be removed by an administrator.
-view_bundle_createWizard_cancelSuccessful=ãã³ãã« [{0}], ããŒãžã§ã³ \= [{1}] ã®äœæããã£ã³ã»ã«ããŸãã
-view_bundle_createWizard_clickToUploadRecipe=ã¬ã·ããã¡ã€ã«ã®ããŒãããããã«ã¯ãªãã¯ããŠãã ãã
-view_bundle_createWizard_createFailure=ãã³ãã«ãäœæããã®ã«å€±æããŸãã
+view_bundle_createWizard_cancelSuccessful=ãã³ãã« [{0}]ãããŒãžã§ã³ \= [{1}] ã®äœæããã£ã³ã»ã«ããŸãã
+view_bundle_createWizard_clickToUploadRecipe=ã¯ãªãã¯ãããšã¬ã·ããã¡ã€ã«ãããŒãããŸã
+view_bundle_createWizard_createFailure=ãã³ãã«ãäœæã«å€±æããŸãã
view_bundle_createWizard_createSuccessful=ããŒãžã§ã³ [{1}] ã® [{0}] ãšããååã®ãã³ãã«ã®äœæã«æåããŸãã
view_bundle_createWizard_enterRecipe=æ£ããã¬ã·ããæäŸããŠãã ãã
-view_bundle_createWizard_enterUrl=ãã³ãã«é
åžãã¡ã€ã«ãããŠã³ããŒãã§ããæ£ããURLãå
¥åããŠãã ãã
+view_bundle_createWizard_enterUrl=ãã³ãã«é
åžãã¡ã€ã«ãããŠã³ããŒãã§ããæ£ãã URL ãå
¥åããŠãã ãã
view_bundle_createWizard_failedToUploadDistroFile=ãã³ãã«é
åžãã¡ã€ã«ã®ã¢ããããŒãã«å€±æããŸãã
view_bundle_createWizard_failedToUploadFile=ãã³ãã«ãã¡ã€ã«ã®ã¢ããããŒãã«å€±æããŸãã
view_bundle_createWizard_groupsStep_assign=æå¹ãªãã³ãã«ã°ã«ãŒãã 1 ã€ä»¥äžå²ãåœãŠãŸã\:
view_bundle_createWizard_groupsStep_assigned=æ°ãããã³ãã«ããŒãžã§ã³ã¯æ¢åã®ãã³ãã«çšã§ããã³ãã«ã°ã«ãŒãã®å²ãåœãŠãç¶æ¿ããŸã\:
view_bundle_createWizard_groupsStep_failedAssign=æåã®ãã³ãã«ã°ã«ãŒãããããŒãžã§ã³ [{1}] ã® [{0}] ãšããååã®ãã³ãã«ãžå²ãåœãŠã§ããŸããã§ãããäœæãŠã£ã¶ãŒãããã£ã³ã»ã«ãã管çè
ã«å ±åããŠãã ããã
view_bundle_createWizard_groupsStep_failedGetAssignable=å²ãåœãŠå¯èœãªãã³ãã«ã°ã«ãŒãã決å®ã§ããŸããã§ãããäœæãŠã£ã¶ãŒãããã£ã³ã»ã«ãã管çè
ã«å ±åããŠãã ããã
-view_bundle_createWizard_groupsStep_help=æ°ãããã³ãã«ã¯ããã®ãã³ãã«ã®æåã®ããŒãžã§ã³ãã¢ããããŒãããæã«äœæãããŸãããã®åŸãæ°ãããã³ãã«ã¯æåã®ãã³ãã«ã°ã«ãŒããžå²ãåœãŠãããŸããæ°ãããã³ãã«ã¯ãã°ããŒãã«ãŸãã¯ãã³ãã«ã°ã«ãŒãã¬ãã«ã«ãŠãCreate Bundles ããŒããã·ã§ã³ãæã€ãã³ãã«ã°ã«ãŒããžã®ã¿å²ãåœãŠã§ããŸãããŠãŒã¶ãŒãã°ããŒãã«ã® Create ãŸã㯠View Bundles ããŒããã·ã§ã³ãæã£ãŠãããšããã³ãã«ã°ã«ãŒãã¯å²ãåœãŠãããŸããããã®å Žå以å€ã¯ãæäœã§ã 1 ã€ã®ãã³ãã«ã°ã«ãŒããå²ãåœãŠãå¿
èŠããããŸãã
+view_bundle_createWizard_groupsStep_help=æ°ãããã³ãã«ã¯ããã®ãã³ãã«ã®æåã®ããŒãžã§ã³ãã¢ããããŒãããæã«äœæãããŸãããã®åŸãæ°ãããã³ãã«ã¯æåã®ãã³ãã«ã°ã«ãŒããžå²ãåœãŠãããŸããæ°ãããã³ãã«ã¯ãã°ããŒãã«ãŸãã¯ãã³ãã«ã°ã«ãŒãã¬ãã«ã«ãŠãããã³ãã«ã®äœæãããŒããã·ã§ã³ãæã€ãã³ãã«ã°ã«ãŒããžã®ã¿å²ãåœãŠã§ããŸãããŠãŒã¶ãŒãã°ããŒãã«ã®ãäœæãããã³ããã³ãã«ã®è¡šç€ºã ããŒããã·ã§ã³ãæã£ãŠãããšããã³ãã«ã°ã«ãŒãã¯å²ãåœãŠãããŸããããã®å Žå以å€ã¯ãæäœã§ã 1 ã€ã®ãã³ãã«ã°ã«ãŒããå²ãåœãŠãå¿
èŠããããŸãã
view_bundle_createWizard_groupsStep_leaveUnassigned=æ°ãããã³ãã«ã¯å²ãåœãŠãªãã§ãã ããã
view_bundle_createWizard_groupsStep_noAssignable=å²ãåœãŠã§ãããã³ãã«ã°ã«ãŒãããŠãŒã¶ãŒãæã£ãŠããªããããæåã®ãã³ãã«ããŒãžã§ã³ãäœæã§ããŸãããäœæãŠã£ã¶ãŒãããã£ã³ã»ã«ãã管çè
ã«å ±åããŠãã ããã
view_bundle_createWizard_groupsStep_noneAssigned=æ°ãããã³ãã«ããŒãžã§ã³ãæäœã§ã 1 ã€ã®ãã³ãã«ã°ã«ãŒããžå²ãåœãŠãå¿
èŠããããŸãã
@@ -1509,7 +1509,7 @@ view_bundle_createWizard_groupsStep_unassigned=æ°ãããã³ãã«ããŒãžã§
view_bundle_createWizard_loadBundleFileFailure=ãµãŒããŒãããã³ãã«ãã¡ã€ã«æ
å ±ã®ååŸãã§ããŸãã
view_bundle_createWizard_noAdditionalFilesNeeded=ãã®ãã³ãã«ã®ããã«ã¢ããããŒããããå¿
èŠãããè¿œå ãã¡ã€ã«ã¯ãããŸãã
view_bundle_createWizard_noBundleTypesAvail=ãã³ãã«ã¿ã€ããå©çšã§ããŸãã
-view_bundle_createWizard_noBundleTypesSupported=ãã³ãã«ã¿ã€ãã¯äžã€ããµããŒããããŠããŸãã - ãã³ãã«ãããã€ã¡ã³ãããµããŒãããæ£ãããã©ã°ã€ã³ããããã€ããªããã°ãªããŸãã
+view_bundle_createWizard_noBundleTypesSupported=ãµããŒãããããã³ãã«ã¿ã€ãã¯ãããŸãã - ãã³ãã«ãããã€ã¡ã³ãããµããŒãããæ£ãããã©ã°ã€ã³ããããã€ããå¿
èŠããããŸã
view_bundle_createWizard_provideBundleDistro=ãã³ãã«é
åžã®æäŸ
view_bundle_createWizard_recipeOption=ã¬ã·ã
view_bundle_createWizard_title=ãã³ãã«äœæ
@@ -1527,7 +1527,7 @@ view_bundle_createWizard_urlUserName=ãŠãŒã¶ãŒå
# #view_bundle_createWizard_urlUserName = User name
view_bundle_createWizard_windowTitle=ãã³ãã«äœæãŠã£ã¶ãŒã
view_bundle_createWizard_youMustChooseOne=ãã³ãã«ãäœæããããã®ãªãã·ã§ã³ãéžæããªããã°ãªããŸãã
-view_bundle_deleteConfirm=ãã®ãã³ãã«ãåé€ããŠãããããã§ãã? ãã®ãã³ãã«ã®ãã¹ãŠã®ããŒãžã§ã³ãå®å
ããããã€ãåé€ãããŸãã
+view_bundle_deleteConfirm=ãã®ãã³ãã«ãåé€ããŠãããããã§ãã? ãã®ãã³ãã«ã®ãã¹ãŠã®ããŒãžã§ã³ãå®å
ããããã€ãåé€ãããŸããããªã¢ãŒããã·ã³ã®å
容ã¯åé€ãããŸããã
view_bundle_deploy=ãããã€
view_bundle_deployDir=ãããã€ãã£ã¬ã¯ããª
view_bundle_deployWizard_createGroup_error_1=ã°ã«ãŒããäœæãããŸããã§ããããããã€ã¡ã³ãã®ã°ã«ãŒãã¯ç©ºã«ã¯ã§ããŸããã
@@ -1547,23 +1547,23 @@ view_bundle_deployWizard_deploymentScheduledDetail_concise=ãã³ãã«ããã
view_bundle_deployWizard_destinationCreatedDetail=ãã£ã¹ã¯ãªãã·ã§ã³ [{1}] ã®ä»ãããããã€ã¡ã³ã [{0}] ãäœæããŸãã
view_bundle_deployWizard_destinationCreatedDetail_concise=å®å
[{0}] ãäœæããŸãã
view_bundle_deployWizard_error_1=ãã£ã³ã»ã«ã«ã€ããŠã®æ°ãããããã€ã¡ã³ãã®åé€ã«å€±æããŸãã
-view_bundle_deployWizard_error_10=å®å
ã®äœæã«å€±æããŸãããããã¯ãã§ã«ååšãããããããŸããã (泚æïŒå®å
ãã¥ãŒããæ¢åã®å®å
ãããã€ã®ãã)
+view_bundle_deployWizard_error_10=å®å
ã®äœæã«å€±æããŸãããå®å
ã¯ãã§ã«ååšããå¯èœæ§ããããŸã (å®å
ãã¥ãŒãããããã€ãããæ¢åã®å®å
)ã
view_bundle_deployWizard_error_11=å®çŸ©æžã¿ãããã€ã¡ã³ãã®æ€çŽ¢ã«å€±æããŸãã
view_bundle_deployWizard_error_12=å®çŸ©æžã¿ãã³ãã«ã®æ€çŽ¢ã«å€±æããŸãã
-view_bundle_deployWizard_error_2=ãã£ã³ã»ã«ã«ã€ããŠã®æ°ããå®å
ã®åé€ã«å€±æããŸãã
+view_bundle_deployWizard_error_2=ãã£ã³ã»ã«ã§æ°ããå®å
ã®åé€ã«å€±æããŸãã
view_bundle_deployWizard_error_3=ãããã€ã¡ã³ãã®ã¹ã±ãžã¥ãŒã«ã«å€±æããŸãã\!
view_bundle_deployWizard_error_4=ãããã€ã¡ã³ãã®ã¹ã±ãžã¥ãŒã«ã«å€±æããŸãã\: {0}
view_bundle_deployWizard_error_5=ãããã€ã¡ã³ãã®äœæã«å€±æããŸãã\!
view_bundle_deployWizard_error_6=ãããã€ã¡ã³ãã®äœæã«å€±æããŸãã\: {0}
view_bundle_deployWizard_error_7=ãããã€ã¡ã³ãåã®ååŸã«å€±æããŸãã
-view_bundle_deployWizard_error_8=ããããããŠã³ããæ£ãããªãœãŒã¹ã°ã«ãŒããéžæããªããã°ãªããŸãã
+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_deployDir=ãããã€ã¡ã³ããã£ã¬ã¯ããª
+view_bundle_deployWizard_getDest_deployDir_help=ãã®ãã³ãã«ããããã€ããããã£ã¬ã¯ããªããã®ãã£ã¬ã¯ããªã¯ãã¹ãŠã®ãªãœãŒã¹ã®ãã¹ãŠã®ãããã€ã¡ã³ãã§åããã®ã䜿ãããŸãããå®å
ã®ããŒã¹ãã£ã¬ã¯ããªã®å Žæã«çžå¯ŸããŸãããã£ãŠãã¿ãŒã²ãããªãœãŒã¹äžã§ããŒã¹ãšãªãå Žæã«å¿ããŠã絶察ãã£ã¬ã¯ããªã¯ã¿ãŒã²ãããªãœãŒã¹ã«ãã£ãŠç°ãªããã¹ãæã€å¯èœæ§ããããŸãã
view_bundle_deployWizard_getDest_desc=å®å
ã®èª¬æ
view_bundle_deployWizard_getDest_destBaseDirName=åºæ¬ã®é
眮
view_bundle_deployWizard_getDest_group_help=ã¡ã³ããŒããã¹ãŠã®ãã³ãã«ãããã€ã®ããã®å®å
ã¿ãŒã²ãããšãªããããªã°ã«ãŒãããã³ãã«ãããã€ããµããŒããããªãœãŒã¹ãå«ãäºæã°ã«ãŒãã®ã¿ãéžæå¯èœã§ãã
@@ -1578,10 +1578,10 @@ view_bundle_deployWizard_getOptions_deployLater=åŸã§ãããã€ãã
view_bundle_deployWizard_getOptions_deployNow=ä»ãããããã€ãã
view_bundle_deployWizard_getOptions_deployTime=ãããã€æå»
view_bundle_deployWizard_selectBundleStep=ãããã€ãã³ãã«ã®éžæ
-view_bundle_deployWizard_selectBundle_single=ãããã€ã®ããã«åäžãã³ãã«ã ããéžæããŠãã ãã
+view_bundle_deployWizard_selectBundle_single=ãããã€ã«åäžã®ãã³ãã«ã®ã¿ãéžæããŠãã ãã
view_bundle_deployWizard_selectVersionStep=ãã³ãã«ããŒãžã§ã³ãéžæããŠãã ãã
view_bundle_deployWizard_selectVersion_latest=ææ°ããŒãžã§ã³ [{0}]
-view_bundle_deployWizard_selectVersion_live=åäœããŒãžã§ã³ [{0}]
+view_bundle_deployWizard_selectVersion_live=ã©ã€ãããŒãžã§ã³ [{0}]
view_bundle_deployWizard_selectVersion_select=ãªã¹ãããããŒãžã§ã³ãéžæããŠãã ãã\:
view_bundle_deployWizard_title=ãã³ãã«ãããã€ã®ãŠã£ã¶ãŒã
view_bundle_deploy_action=ã¢ã¯ã·ã§ã³
@@ -1607,20 +1607,20 @@ view_bundle_deployments=ãããã€
view_bundle_dest_backToBundle=ãã³ãã«ãžæ»ã
view_bundle_dest_baseDirName=åºæ¬ã®é
眮
view_bundle_dest_created=äœæããã
-view_bundle_dest_deleteConfirm=ãã®ãã³ãã«å®å
ãåé€ããŠãããããã§ãã? ããã¯ããŒã¿ããŒã¹ããåé€ããã ãã§ããã€ãŸãããªã¢ãŒããã·ã³ã®ãã®å®å
ã«ãããã€ããããã¹ãŠã®ãã³ãã«ã®å
容ã¯æ®ãç¶ããŸã
+view_bundle_dest_deleteConfirm=ãã®ãã³ãã«å®å
ãåé€ããŠãããããã§ãã? ããã¯ããŒã¿ããŒã¹ããã®ã¿åé€ãããŸããã€ãŸãããªã¢ãŒããã·ã³ã®ãã®å®å
ã«ãããã€ããããã¹ãŠã®ãã³ãã«ã®å
容ã¯åé€ãããŸããã
view_bundle_dest_deleteFailure=ãã³ãã«å®å
[{0}] ã®åé€ã«å€±æããŸãã
view_bundle_dest_deleteSuccessful=ãã³ãã«å®å
[{0}] ã®åé€ã«æåããŸãã
view_bundle_dest_deployDir=ãããã€ãã£ã¬ã¯ããª
view_bundle_dest_group=ã°ã«ãŒã
view_bundle_dest_lastDeployedVersion=æåŸã«ãããã€ãããããŒãžã§ã³
-view_bundle_dest_lastDeploymentDate=æåŸã®ãããã€ããããæ¥ä»
+view_bundle_dest_lastDeploymentDate=æåŸã®ãããã€ã¡ã³ãã®æ¥ä»
view_bundle_dest_lastDeploymentStatus=æåŸã®ãããã€ã¡ã³ãã¹ããŒã¿ã¹
view_bundle_dest_loadFailure=ãã³ãã«ã®å®å
ãããŒãããã®ã«å€±æããŸãã
view_bundle_dest_loadFailureVersionInfo=ãããã€ãããããŒãžã§ã³æ
å ±ã®ããŒãã«å€±æããŸãã
-view_bundle_dest_purgeConfirm=ãã®æäœã¯ãã¹ãŠã®ãªã¢ãŒããã·ã³ãããã³ãã«ã³ã³ãã³ããé€å»ããŸããããããã§ãã?
-view_bundle_dest_purgeFailure=ãªã¢ãŒããã·ã³ã®äžéšãããã¯ãã¹ãŠããã®ãã³ãã«å®å
[{0}] ã®é€å»ã«å€±æããŸãã
-view_bundle_dest_purgeSuccessful=ãªã¢ãŒããã·ã³ã®äžéšãããã¯ãã¹ãŠããã®ãã³ãã«å®å
[{0}] ã®é€å»ã«æåããŸãã
-view_bundle_dest_revertConfirm=ããã¯ãã¹ãŠã®ãªã¢ãŒããã·ã³ã以åã®ãã³ãã«ãããã€ã¡ã³ããžæ»ããŸãããããããŠãããããã§ãã?
+view_bundle_dest_purgeConfirm=ãã®æäœã¯ãã¹ãŠã®ãªã¢ãŒããã·ã³ãããã³ãã«ã³ã³ãã³ããããŒãžããŸããããããã§ãã?
+view_bundle_dest_purgeFailure=ãªã¢ãŒããã·ã³ã®äžéšãããã¯ãã¹ãŠãããã³ãã«å®å
[{0}] ã®ããŒãžã«å€±æããŸãã
+view_bundle_dest_purgeSuccessful=ãªã¢ãŒããã·ã³ã®äžéšãããã¯ãã¹ãŠãããã³ãã«å®å
[{0}] ãæ£åžžã«ããŒãžããŸãã
+view_bundle_dest_revertConfirm=ããã¯ãã¹ãŠã®ãªã¢ãŒããã·ã³ã以åã®ãã³ãã«ãããã€ã¡ã³ããžæ»ããŸããå®è¡ããŠãããããã§ãã?
view_bundle_dest_tagUpdateFailure=ãã³ãã«å®å
ã¿ã°ã®æŽæ°ã«å€±æããŸãã
view_bundle_dest_tagUpdateSuccessful=ãã³ãã«å®å
ã¿ã°ã®æŽæ°ã«æåããŸãã
view_bundle_destinations=å®å
@@ -1650,28 +1650,28 @@ view_bundle_list_singleLoadFailure=ãããã€ãããåäžãã³ãã« [{0}]
view_bundle_list_tagUpdateFailure=ãã³ãã«ã¿ã°ã®æŽæ°ã«å€±æããŸãã
view_bundle_list_tagUpdateSuccessful=ãã³ãã«ã¿ã°ã®æŽæ°ã«æåããŸãã
view_bundle_list_versionsCount=ããŒãžã§ã³æ°
-view_bundle_purge=é€å»
-view_bundle_recipe=ãªã·ã
-view_bundle_resDeployDS_loadFailure=ãã³ãã«ãªãœãŒã¹ãããã€ã®ããŒãã«å€±æããŸãã
+view_bundle_purge=ããŒãž
+view_bundle_recipe=ã¬ã·ã
+view_bundle_resDeployDS_loadFailure=ãã³ãã«ãªãœãŒã¹ãããã€ã¡ã³ãã®ããŒãã«å€±æããŸãã
view_bundle_revert=åãæ¶ã
view_bundle_revertWizard_confirmStep_confirmation=åäœäžã®ãããã€ã以åã®ãããã€ãžåãæ¶ãäžã§ããç¶ç¶ããã«ã¯"次ãž"ãã¯ãªãã¯ããŠãã ããã
view_bundle_revertWizard_confirmStep_failedToFindLiveDeployment=åäœäžã®ãããã€ãèŠã€ãããŸãããåãæ¶ããã§ããŸããã
view_bundle_revertWizard_confirmStep_liveDeployment=åäœäžã®ãããã€
view_bundle_revertWizard_confirmStep_name=åãæ¶ããããã€ç¢ºèª
-view_bundle_revertWizard_confirmStep_noLiveDeployment=ãã®å®å
[{0}] çšã®åäœäžã®ãããã€ã¯èŠã€ãããŸããã§ãã
-view_bundle_revertWizard_confirmStep_noLiveDeployment_concise=ãã®å®å
çšã®åäœäžã®ãããã€ã¯èŠã€ãããŸããã§ãã
-view_bundle_revertWizard_confirmStep_noPriorDeployment=以åã®ããã〠[{1}] ãååšããªãã®ã§åäœäžã®ããã〠[{0}] ã¯åãæ¶ãããšãã§ããŸããã§ãã
-view_bundle_revertWizard_confirmStep_noPriorDeployment_concise=以åã®ãããã€ãååšããªãã®ã§åäœäžã®ãããã€ã¯åãæ¶ãããšãã§ããŸããã§ãã
-view_bundle_revertWizard_confirmStep_prevDeployment=以åã®ãããã€
-view_bundle_revertWizard_getInfoStep_cleanDeploy=ãããã€ãåé€ããŸãã? (ããã¯å€ããæ¢åã®ãããã€ãã£ã¬ã¯ããªãåãæ¶ããããã€ãéå§ããåã«åé€ããŸã)
-view_bundle_revertWizard_getInfoStep_getNameFailure=åãæ¶ããããã€åã®ååŸã«å€±æããŸãã
+view_bundle_revertWizard_confirmStep_noLiveDeployment=å®å
[{0}] çšã®åäœäžã®ãããã€ã¡ã³ãã¯èŠã€ãããŸããã§ãã
+view_bundle_revertWizard_confirmStep_noLiveDeployment_concise=å®å
çšã®åäœäžã®ãããã€ã¡ã³ãã¯èŠã€ãããŸããã§ãã
+view_bundle_revertWizard_confirmStep_noPriorDeployment=å®å
[{1}] ã®ä»¥åã®ãããã€ã¡ã³ããååšããªããããåäœäžã®ãããã€ã¡ã³ã [{0}] ã¯åãæ¶ãããšãã§ããŸããã§ãã
+view_bundle_revertWizard_confirmStep_noPriorDeployment_concise=以åã®ãããã€ãååšããªãããåäœäžã®ãããã€ã¯åãæ¶ãããšãã§ããŸããã§ãã
+view_bundle_revertWizard_confirmStep_prevDeployment=以åã®ãããã€ã¡ã³ã
+view_bundle_revertWizard_getInfoStep_cleanDeploy=ãããã€ã¡ã³ããåé€ããŸãã? (ããã«ããããããã€ã¡ã³ãã®åãæ¶ããéå§ããåã«ãæ¢åã®å€ããããã€ãã£ã¬ã¯ããªãåé€ãããŸã)
+view_bundle_revertWizard_getInfoStep_getNameFailure=åãæ¶ããããã€ã¡ã³ãåã®ååŸã«å€±æããŸãã
view_bundle_revertWizard_getInfoStep_name=åãæ¶ãæ
å ±ã®æäŸ
view_bundle_revertWizard_getInfoStep_revertDeployDesc=ãããã€èšè¿°ã®åãæ¶ã
view_bundle_revertWizard_getInfoStep_revertDeployDescFull=[åãæ¶ã å] {0} [åãæ¶ã åŸ] {1}
view_bundle_revertWizard_getInfoStep_revertDeployName=ãããã€åã®åãæ¶ã
view_bundle_revertWizard_revertStep_name=å®å
ãã©ãããã©ãŒã ãžãã³ãã«ããããã€
view_bundle_revertWizard_revertStep_reverting=åãæ¶ãäž...
-view_bundle_revertWizard_revertStep_scheduled=ãããã€ã®åãæ¶ãã®ã¹ã±ãžã¥ãŒã«ã«æåããŸããã
+view_bundle_revertWizard_revertStep_scheduled=ãããã€ã¡ã³ãã®åãæ¶ãã®ã¹ã±ãžã¥ãŒã«ã«æåããŸããã
view_bundle_revertWizard_revertStep_scheduledDetails=ãªãœãŒã¹ã°ã«ãŒã [{1}] ãããã³ãã«ããã〠[{0}] ã®åãæ¶ãã®ã¹ã±ãžã¥ãŒã«ã«æåããŸãã
view_bundle_revertWizard_revertStep_scheduledFailure=ãããã€ã®åãæ¶ãã®ã¹ã±ãžã¥ãŒã«ã«å€±æããŸãã
view_bundle_revertWizard_title=ãã³ãã«åãæ¶ã
@@ -1684,7 +1684,7 @@ view_bundle_tree_unassigned_name=å²ãåœãŠãããŠããªããã³ãã«
view_bundle_version_backToBundle=ãã³ãã«ãžæ»ã
view_bundle_version_bundleVersionTagUpdateFailure=ãã³ãã«ããŒãžã§ã³ã¿ã°ã®æŽæ°ã«å€±æããŸãã
view_bundle_version_bundleVersionTagUpdateSuccessful=ãã³ãã«ããŒãžã§ã³ã¿ã°ã®æŽæ°ã«æåããŸãã
-view_bundle_version_deleteConfirm=ãã®ãã³ãã«ããŒãžã§ã³ãåé€ããŠãããããã§ãã?
+view_bundle_version_deleteConfirm=ãã®ãã³ãã«ããŒãžã§ã³ãåé€ããŠãããããã§ãã? ãªã¢ãŒããã·ã³ã®ã³ã³ãã³ãã¯åé€ãããŸããã
view_bundle_version_deleteFailure=ãã®ãã³ãã«ããŒãžã§ã³ [{0}] ãåé€ããã®ã«å€±æããŸãã
view_bundle_version_deleteSuccessful=ãã³ãã«ããŒãžã§ã³ [{0}] ã®åé€ã«æåããŸãã
view_bundle_version_loadFailure=ãã³ãã«ããŒãžã§ã³ã®ããŒãã«å€±æããŸãã
@@ -1722,7 +1722,7 @@ view_configEdit_msg_4=ãªã¹ãã«è¿œå ãããã¢ã€ãã
view_configEdit_properties=ããããã£
view_configEdit_property=ããããã£ãŒ
# #view_configEdit_property = Property
-view_configEdit_tooltip_1=ãªã¹ãããéžæãããã¢ã€ãã ãåé€ããŸã
+view_configEdit_tooltip_1=ãªã¹ãããéžæãããã¢ã€ãã ãåé€ããŸã
view_configEdit_tooltip_2=ãªã¹ãã«ã¢ã€ãã ãè¿œå ããŸã
view_configEdit_unset=æªèšå®ã«ããŸãã?
# #view_configEdit_unset = Unset?
@@ -1749,12 +1749,12 @@ view_configurationHistoryList_table_statusInprogress=ãã®èšå®ã®æŽæ°ã¯ãŸ
view_configurationHistoryList_table_statusNochange=ãã®èšå®ã¯å€æŽãããŸããã§ãã
view_configurationHistoryList_table_statusSuccess=ãã®èšå®ã®æŽæ°ã¯æåããŸãã
view_configurationHistoryList_title=èšå®å±¥æŽ
-view_connectionSettingsDetails_allPropertiesValid=ãã¹ãŠã®ã³ãã¯ã·ã§ã³èšå®ã¯æ£ããå€ã§ãã®ã§ããã®èšå®ã¯ä¿åå¯èœã§ã
-view_connectionSettingsDetails_error_updateFailure=ã³ãã¯ã·ã§ã³èšå®ã®æŽæ°ã«å€±æããŸãã
-view_connectionSettingsDetails_messageConcise_updateSuccess=ã³ãã¯ã·ã§ã³èšå®ãæŽæ°ãããŸãã
-view_connectionSettingsDetails_messageDetailed_updateSuccess=ãªãœãŒã¹ [{0} ã®ããã®ã³ãã¯ã·ã§ã³èšå®ãæŽæ°ãããŸãã
-view_connectionSettingsDetails_noPermission=ãã®ãªãœãŒã¹ã®ã³ãã¯ã·ã§ã³èšå®ãç·šéããæš©éããããŸãã
-view_connectionSettingsDetails_somePropertiesInvalid=次ã®ã³ãã¯ã·ã§ã³èšå®ã¯äžæ£ãªå€\: {0} ã§ããèšå®ãä¿åããåã«èšæ£ããªããã°ãªããŸããã
+view_connectionSettingsDetails_allPropertiesValid=ãã¹ãŠã®æ¥ç¶èšå®ã®å€ãæå¹ã§ãããããèšå®ãä¿åã§ããŸãã
+view_connectionSettingsDetails_error_updateFailure=æ¥ç¶èšå®ã®æŽæ°ã«å€±æããŸãã
+view_connectionSettingsDetails_messageConcise_updateSuccess=æ¥ç¶èšå®ã®æŽæ°ãéå§ãããŸããã
+view_connectionSettingsDetails_messageDetailed_updateSuccess=ãªãœãŒã¹ [{0} ã®æ¥ç¶èšå®ã®æŽæ°ãéå§ãããŸããã
+view_connectionSettingsDetails_noPermission=ãã®ãªãœãŒã¹ã®æ¥ç¶èšå®ãç·šéããæš©éããããŸãã
+view_connectionSettingsDetails_somePropertiesInvalid=次ã®æ¥ç¶èšå®ã®å€ã¯ç¡å¹ã§ã\: {0}ãèšå®ãä¿åããã«ã¯ãå€ãä¿®æ£ããå¿
èŠããããŸãã
view_core_loggedOut=ãã°ã¢ãŠã
view_core_noRecentAlerts=ã¬ããŒããã¹ãæè¿ã®ã¢ã©ãŒãã¯ååšããŸãã
view_core_recentAlerts=[{0}] åã®æè¿ã®ã¢ã©ãŒãããããŸãã - æè¿ã®ã¢ã©ãŒãã¬ããŒããžã¯ãªãã¯ããŠãã ãã
@@ -1797,8 +1797,8 @@ view_drift_category_fileNew=æ°èŠã«æ€åºããããã¡ã€ã«
view_drift_category_fileRemoved=åé€ãã¡ã€ã«
view_drift_confirm_deleteAllDefs=ããªããæ€åºå®çŸ©ããã¹ãŠåé€ããŸãã?
view_drift_confirm_deleteDefs=éžæããããªããæ€åºå®çŸ©ãåé€ããŸãã?
-view_drift_confirm_deleteTemplate=èŠå\! ãã®ãã³ãã¬ãŒããåé€ãããšãã¢ã¿ãããããããªããå®çŸ©ããã¹ãŠåé€ãããŸããã¢ã¿ãããããå®çŸ©ã¯ãããã®ã¹ãããã·ã§ãããšãšãã«ã·ã¹ãã ããæ°žä¹
çã«åé€ãããŸããã§ã¿ãããããå®çŸ©ã¯åé€ãããŸãããæ¬åœã«ç¶ç¶ããŠãããããã§ãã?
-view_drift_failure_deleteDefs=ããã€ãããããã¯ãã¹ãŠã®ããªããæ€åºå®çŸ©ã®åé€ã«å€±æããŸãã
+view_drift_confirm_deleteTemplate=èŠå\! ãã®ãã³ãã¬ãŒããåé€ãããšãã¢ã¿ãããããããªããå®çŸ©ããã¹ãŠåé€ãããŸããã¢ã¿ãããããå®çŸ©ã¯ãããã®ã¹ãããã·ã§ãããšãšãã«ã·ã¹ãã ããæ°žä¹
çã«åé€ãããŸãããã¿ãããããå®çŸ©ã¯åé€ãããŸãããæ¬åœã«ç¶ç¶ããŠãããããã§ãã?
+view_drift_failure_deleteDefs=äžéšãããã¯ãã¹ãŠã®ããªããæ€åºå®çŸ©ã®åé€ã«å€±æããŸãã
view_drift_failure_deleteTemplates=ããªãããã³ãã¬ãŒãã®äžéšãŸãã¯ãã¹ãŠãåé€ã§ããŸããã§ããã
view_drift_failure_detectNow=ããªããæ€åºå®è¡ãªã¯ãšã¹ãã®éä¿¡ã«å€±æããŸãã
view_drift_failure_load=ããªããã€ã³ã¹ã¿ã³ã¹ã®åãåºãã«å€±æããŸãã
@@ -1834,35 +1834,35 @@ view_drift_table_title_initialSnapshot=å®çŸ© [{0}] ã®ããã®åæã¹ãã
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_infoStepHelp=åããªããæ€åºå®çŸ©ã¯ãããªããã®ç£èŠãå®è¡ããããã¡ã€ã«ãèšè¿°ããŸããå®çŸ©ã¯æå¹ãŸãã¯ç¡å¹ã«ã§ããæ€åºå®è¡ã®ééãå®çŸ©ããããŒã¹ãã£ã¬ã¯ããªãšãªãã·ã§ã³ã®ãã¡ã€ã«ãã£ã«ã¿ãŒãæå®ããŸããããªããæ€åºãæäŸãããªãœãŒã¹ã¿ã€ãããšã«ãæåã®å®çŸ©ãšããŠäœ¿çšã§ãã 1 ã€ä»¥äžã®äºåå®çŸ©ãã³ãã¬ãŒãããããŸãããã®ãã³ãã¬ãŒãã¯ç·šéå¯èœã§ãã
+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_title=ã¿ã€ã [{0}] ã®ãªãœãŒã¹ã®ããªããæ€åºå®çŸ©ãè¿œå
view_drift_wizard_addDef_windowTitle=ããªããæ€åºå®çŸ©ãŠã£ã¶ãŒãã®è¿œå
view_drift_wizard_addTemplate_failure=æ°èŠããªããæ€åºãã³ãã¬ãŒã [{0}] ã®è¿œå ã«å€±æããŸãã
-view_drift_wizard_addTemplate_infoStepHelp=åããªãããã³ãã¬ãŒãã¯æ¢åã®ãã³ãã¬ãŒããã掟çããŠäœæã§ããŸããããã¯æ¢åãã³ãã¬ãŒãã«é¡äŒŒãããã®ãããã©ã°ã€ã³ãå®çŸ©ãããã³ãã¬ãŒãããæ°èŠãã³ãã¬ãŒããæ§ç¯ããçŽ æ©ãæ¹æ³ãæäŸããŸããããªããå®çŸ©ã®ããã«ããã®ãã³ãã¬ãŒãã¯ããªãããç£èŠãå®è¡ããäžåŒã®ãã¡ã€ã«ãèšè¿°ããŸããç¶æ³ã«ãã£ãŠã¯ããã®ãã³ãã¬ãŒãããå°åºãããå®çŸ©ã¯ãã®ãã¡ã€ã«ã»ããã®å€æŽããä»ã®èšå®ã®å€æŽãèš±ããããèš±ããªãã£ããããŸãããã³ãã¬ãŒãåã¯äžã€ã®ãªãœãŒã¹ã¿ã€ãå
ã§ãŠããŒã¯ã§ãªããã°ãªããŸããã
+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_confirmNotPinned=ãã³ãã¬ãŒãã®çŸåšããã³ä»åŸã®ããªããå®çŸ©ããã³çãããããšããã³ãã¬ãŒãã®ãã³çããããã¹ãããã·ã§ããã«åæã¹ãããã·ã§ãããèšå®ãããŸãããã®ãã³ãã¬ãŒãã®æ¢åã®å®çŸ©ã¯æ°ããåæã¹ãããã·ã§ãããžãªã»ãããããæ¢åã®ã¹ãããã·ã§ããã¯ãã¹ãŠåé€ãããŸãããã³ãã¬ãŒãã®ã¹ãããã·ã§ãããžã®ãã³çããç¶è¡ããŸãã?
+view_drift_wizard_pinTemplate_confirmPinned=èŠå\! ãã®ãã³ãã¬ãŒãã¯ãã§ã«ãã³çããããŠããŸãããã³ãã¬ãŒãããã®æ°èŠã¹ãããã·ã§ããã«å床ãã³çãããããšãå¯èœã§ããå床ãã³çããããšããã³ãã¬ãŒãã®çŸåšããã³ä»åŸã®ããªããå®çŸ©ãæã€åæã¹ãããã·ã§ããããã³ãã¬ãŒãã®ãã³çããããã¹ãããã·ã§ãããžèšå®ãããŸãããã®ãã³ãã¬ãŒãã®æ¢åã®å®çŸ©ã¯æ°ããåæã¹ãããã·ã§ãããžãªã»ãããããæ¢åã®ã¹ãããã·ã§ããã¯ãã¹ãŠåé€ãããŸãããã®æ°èŠã¹ãããã·ã§ãããæã€ãã³ãã¬ãŒããå床ãã³çãããŸãã?
view_drift_wizard_pinTemplate_duplicate_name_error=ãã³ãã¬ãŒãåã¯äžæã§ãªããã°ãªããŸãã
view_drift_wizard_pinTemplate_failure=ããªãããã³ãã¬ãŒã [{0}] ãžã®ã¹ãããã·ã§ããããã³çãããã®ã«å€±æããŸãã
view_drift_wizard_pinTemplate_infoStepExistingTemplate=çŸåšã®ãã³ãã¬ãŒããžã®ãã³çã
-view_drift_wizard_pinTemplate_infoStepHelp=ãã³çãããããã³ãã¬ãŒããéžæããŠãã ããããã®ãã³ãã¬ãŒãã®ããã®çŸåšãšå°æ¥ã®åããªããå®çŸ©ããã£ãããã³çãããããšããã®ãã³ãã¬ãŒãã¯åæã¹ãããã·ã§ããããã®ãã³ãã¬ãŒããæã€ãã³çããããã¹ãããã·ã§ãããšããŠèšå®ãããæã€ããã«ãªããŸããããã«ããŸãããããŠãã®å®çŸ©èªèº«ã¯ãã³çãããããã®ãšããŠããŒã¯ãããŸããããã¯æåŸ
ããããã¡ã€ã«ã»ããããããªãããæ€åºããã®ã«äœ¿ãããŸãããã®ãã³ãã¬ãŒãã®ããã®æ¢åå®çŸ©ã¯æ°èŠã®åæã¹ãããã·ã§ããã«ãªã»ããããããã¹ãŠã®æ¢åã¹ãããã·ã§ããã¯åé€ãããããšã«æ³šæããŠãã ããã
+view_drift_wizard_pinTemplate_infoStepHelp=ãã³ãã¬ãŒãã®çŸåšããã³ä»åŸã®ããªããå®çŸ©ããã³çãããããšããã³ãã¬ãŒãã®ãã³çããããã¹ãããã·ã§ããã«åæã¹ãããã·ã§ãããèšå®ãããŸãããŸããå®çŸ©èªäœããã³çãããããšã¿ãªãããŸããããã¯ãæ³å®ããããã¡ã€ã«ã»ããããããªãããæ€åºããããã«äœ¿çšãããŸãããã®ãã³ãã¬ãŒãã®æ¢åã®å®çŸ©ã¯æ°ããåæã¹ãããã·ã§ãããžãªã»ãããããæ¢åã®ã¹ãããã·ã§ããã¯ãã¹ãŠåé€ãããããšã«æ³šæããŠãã ããã
view_drift_wizard_pinTemplate_infoStepName=ãã³çãããããã³ãã¬ãŒãã®éžæ
-view_drift_wizard_pinTemplate_infoStepNewTemplate=æ°èŠãã³ãã¬ãŒããžã®ãã³çã (ã¹ãããã·ã§ããã®ããªããå®çŸ©ããå°åºããããã®)
-view_drift_wizard_pinTemplate_infoStepRadioHelp=ãã®ã¹ãããã·ã§ããã¯æ°èŠãŸãã¯æ¢åã®ããªãããã³ãã¬ãŒãã«ãã³çãããããšãå¯èœã§ãããã®''æ°èŠãã³ãã¬ãŒã''ãªãã·ã§ã³ã¯ããŠãŒã¶ãŒã®æã«ãã£ãŠä¿¡é Œããå®çŸ©ãã¹ãããã·ã§ãããããªãœãŒã¹ã¬ãã«ãã¿ã€ãã¬ãã«ã§ããã³ãã¬ãŒãåã§ããããã«ããŸãããã®æ°èŠãã³ãã¬ãŒãã¯ã次ã«ããã®ã¿ã€ãã®ã¡ã³ããŒã«å¯ŸããŠé©çšã§ããŸãããã®æ°èŠãã³ãã¬ãŒãã¯åææã¯ã¹ãããã·ã§ããã®ããªããå®çŸ©ã®ã³ããŒã§ããã次ã®ã¹ãããã§ç·šéããããšãã§ããŸãããã®ååã¯å€æŽãã¹ãã§ããã®ã¿ã€ãã®ãŠããŒã¯ãªããªãããã³ãã¬ãŒãåã§ãªããã°ãªããŸããã''æ¢åãã³ãã¬ãŒã''ãªãã·ã§ã³ã¯ããŠãŒã¶ãŒãéžæããã¹ãããã·ã§ãããæã€æ¢åãã³ãã¬ãŒãã«å¯ŸããŠããã³çã
ãŸãã¯å床ãã³çããã§ããããã«ããŸããæ£ããèšå®ããã«ã¯ããã®æ¢åãã³ãã¬ãŒãã¯åããã£ã¬ã¯ããªããã®ã¹ãããã·ã§ããå®çŸ©ãšããŠç£èŠã§ããªããã°ãªããŸãããã»ã¬ã¯ã·ã§ã³ããã¯ã¹ã¯æ£åœãªæ¢åã®ãã³ãã¬ãŒãã ãã衚瀺ããŸããããæ£åœã§ãªãæ¢åãã³ãã¬ãŒããååšãããšããŠããã®ãªãã·ã§ã³ã§ã¯éžæãããŸããã
+view_drift_wizard_pinTemplate_infoStepNewTemplate=æ°èŠãã³ãã¬ãŒããžã®ãã³çã (ã¹ãããã·ã§ããã®ããªããå®çŸ©ãã掟ç)
+view_drift_wizard_pinTemplate_infoStepRadioHelp=ã¹ãããã·ã§ãããæ°èŠãŸãã¯æ¢åã®ããªãããã³ãã¬ãŒãã«ãã³çãã§ããŸãããæ°èŠãã³ãã¬ãŒãããªãã·ã§ã³ã䜿çšãããšããŠãŒã¶ãŒã¯ãªãœãŒã¹ã¬ãã«ã®ä¿¡é Œã§ããå®çŸ©ããã³ã¹ãããã·ã§ããããã¿ã€ãã¬ãã«ãžææ Œã§ããŸãããã®åŸãæ°èŠãã³ãã¬ãŒããã¿ã€ãã®ã¡ã³ããŒãžé©çšã§ããŸããæ°èŠãã³ãã¬ãŒãã¯ãæåã¯ã¹ãããã·ã§ããã®ããªããå®çŸ©ã®ã³ããŒã§ããã次ã®ã¹ãããã§ç·šéå¯èœã§ããã¿ã€ãã«ãŠäžæã®ããªãããã³ãã¬ãŒãåãšãªãããã«ãååãå€æŽããå¿
èŠããããŸãããæ¢åãã³ãã¬ãŒãããªãã·ã§ã³ã䜿çšãããšããŠãŒã¶ãŒã¯éžæãããã¹ãããã·ã§ãããæã€æ¢åã®ãã³ãã¬ãŒãããã³çããŸãã¯å床ãã³çãã§ããŸããæ¢åã®ãã³ãã¬ãŒãã¯ãã¹ãããã·ã
§ããã®å®çŸ©ãšåããã£ã¬ã¯ããªãç£èŠããå¿
èŠããããŸããéžæããã¯ã¹ã¯æå¹ãªæ¢åãã³ãã¬ãŒãã®ã¿ã衚瀺ããŸããæå¹ãªæ¢åãã³ãã¬ãŒãããªãå Žåããã®ãªãã·ã§ã³ãéžæã§ããŸããã
view_drift_wizard_pinTemplate_infoStepRadioTitle=ãã³ãã¬ãŒãã®éžæ
-view_drift_wizard_pinTemplate_infoStepSelectBlocked=æ¢åãã³ãã¬ãŒãã§ã¹ãããã·ã§ããã®å®çŸ©ãšåããã£ã¬ã¯ããªãç£èŠãããã®ã¯ååšããŸãããç¶ç¶ããã«''æ°èŠãã³ãã¬ãŒã''ãªãã·ã§ã³ãéžæããŠãã ããã
+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=äºæ
+view_dynagroup_children=DynaGroup ã®å
+view_dynagroup_compatible=äºææ§
view_dynagroup_definitionAlreadyExists=ãã§ã«ãã®ååã®ã°ã«ãŒãå®çŸ©ãååšããŸã
view_dynagroup_definitionCreated=ã°ã«ãŒãå®çŸ©å [{0}] ã®äœæã«æåããŸãã
view_dynagroup_definitionLoadFailure=ã°ã«ãŒãå®çŸ©ã®ããŒãã«å€±æããŸãã
@@ -1872,13 +1872,13 @@ view_dynagroup_deleteSuccessfulSelection=[{0}] ã°ã«ãŒãå®çŸ©ã®åé€ã«æ
view_dynagroup_editing=[{0}] ãç·šéäž
view_dynagroup_exprBuilder_addExpression=åŒã®è¿œå
view_dynagroup_exprBuilder_comparisonType=æ¯èŒã¿ã€ã
-view_dynagroup_exprBuilder_comparisonType_contains=ãå«ã
-view_dynagroup_exprBuilder_comparisonType_endsWith=ã§çµãã
+view_dynagroup_exprBuilder_comparisonType_contains=次ãå«ã\:
+view_dynagroup_exprBuilder_comparisonType_endsWith=次ã§çµãã\:
view_dynagroup_exprBuilder_comparisonType_equals=çãã
-view_dynagroup_exprBuilder_comparisonType_startsWith=ã§éå§ãã
+view_dynagroup_exprBuilder_comparisonType_startsWith=次ãšéå§ãã\:
view_dynagroup_exprBuilder_comparisonType_tooltip=æ¯èŒã¿ã€ã
view_dynagroup_exprBuilder_definingPlugin=å®çŸ©ãã©ã°ã€ã³
-view_dynagroup_exprBuilder_definingPlugin_tooltip=æ€çŽ¢ã®ããã®ãã©ã°ã€ã³
+view_dynagroup_exprBuilder_definingPlugin_tooltip=æ€çŽ¢ãããã©ã°ã€ã³
view_dynagroup_exprBuilder_expression=åŒ
view_dynagroup_exprBuilder_expressionType=åŒã®ã¿ã€ã
view_dynagroup_exprBuilder_expressionType_pluginConfig=ãã©ã°ã€ã³èšå®
@@ -1902,7 +1902,7 @@ view_dynagroup_exprBuilder_noResourceTypes=--ãªãœãŒã¹ã¿ã€ããªã--
view_dynagroup_exprBuilder_pluginLoadFailure=ãã©ã°ã€ã³ã®ãªã¹ãã®ååŸãã§ããŸãã
view_dynagroup_exprBuilder_propLoadFailure=ããããã£ã®ãªã¹ãã®ååŸãã§ããŸãã
view_dynagroup_exprBuilder_propertyName=ããããã£å
-view_dynagroup_exprBuilder_propertyName_tooltip=åãåããçšã®ããããã£åãããã¯ãªãœãŒã¹ã¿ã€ããšåæ§ã«åŒã¿ã€ãã«ãã£ãŠå®çŸ©ãããŸãã
+view_dynagroup_exprBuilder_propertyName_tooltip=ã¯ãšãªããããããã£ãŒã®ååãåŒã¿ã€ãããã³ãªãœãŒã¹ã¿ã€ãã«ãã£ãŠå®çŸ©ãããŸãã
view_dynagroup_exprBuilder_resTypeLoadFailure=ãã©ã°ã€ã³ [{0}] ã®ãªãœãŒã¹ã¿ã€ãã®ãªã¹ãã®ååŸãã§ããŸãã
view_dynagroup_exprBuilder_resource=ãªãœãŒã¹
view_dynagroup_exprBuilder_resourceType=ãªãœãŒã¹ã¿ã€ã
@@ -1913,12 +1913,12 @@ view_dynagroup_exprBuilder_resource_greatGrandparent=æŸç¥ç¶æ¯
view_dynagroup_exprBuilder_resource_greatGreatGrandparent=æŸç¥ç¶æ¯ã®èŠª
view_dynagroup_exprBuilder_resource_parent=芪
view_dynagroup_exprBuilder_resource_resource=ãªãœãŒã¹
-view_dynagroup_exprBuilder_resource_tooltip=ãªãœãŒã¹ã®ã¬ãã«ãæã¿ã®ãã®ã«éžæããŠãã ãããäŸãã°ã"芪"ãéžæãããšã芪ãªãœãŒã¹ãåŒã®æ®ãã®éšåã«äžèŽãããããªãªãœãŒã¹ãçºèŠã§ããŸãã
+view_dynagroup_exprBuilder_resource_tooltip=åžæã®ãªãœãŒã¹ã¬ãã«ãéžæããŸããããšãã°ãã芪ããéžæãããšã芪ãªãœãŒã¹ãä»ã®åŒãšäžèŽãããªãœãŒã¹ãèŠã€ããŸãã
view_dynagroup_exprBuilder_savedExpression=ä¿åãããåŒ
view_dynagroup_exprBuilder_title=åŒãã«ããŒ
view_dynagroup_exprBuilder_unset=èšå®è§£é€
-view_dynagroup_exprBuilder_unset_tooltip=èšå®è§£é€ã¯ããŒã¿ããŒã¹å
ã®nullå€ãæã€å€ãã®ãã¹ãŠãçºèŠããŸããããã¯ããŒã¿ããŒã¹ã¹ãã¢ãšåãåããããŒã¿ã®çç±ã«ãã"\="ãªãã¬ãŒã¿ã䜿ãããšã¯å¯èœã§ã¯ãããŸããã
-view_dynagroup_exprBuilder_value_tooltip=åãåããåŒã®ããã®æååå€
+view_dynagroup_exprBuilder_unset_tooltip=èšå®è§£é€ãæå®ãããšãnull ã§ããå€ããã¹ãŠèŠã€ããŸããããŒã¿ããŒã¹ãããŒã¿ãä¿åããã³ã¯ãšãªããæ¹æ³ã«ãããã\=ãæŒç®åã¯äœ¿çšã§ããŸããã
+view_dynagroup_exprBuilder_value_tooltip=ã¯ãšãªããåŒã®æååå€
view_dynagroup_expression=åŒ
view_dynagroup_expressionBuilderIconTooltip=åŒãã«ããŒ...
view_dynagroup_expressionSet=åŒã»ãã
@@ -1938,34 +1938,34 @@ view_dynagroup_recalculationInterval=åèšç®ã®éé (å)
view_dynagroup_recalculationInterval_error=å€ã¯æŽæ°ã§ãªããã°ãªããŸãã
# view_dynagroup_recalculationInterval = \u518D\u8A08\u7B97\u9593\u9694 (min)
view_dynagroup_recursive=ååž°
-view_dynagroup_saveAndRecalculate=ä¿å & åèšç®
+view_dynagroup_saveAndRecalculate=ä¿åãšåèšç®
view_dynagroup_saveFailure=ã°ã«ãŒãå®çŸ©å [{0}] ã®ä¿åã«å€±æããŸãã
view_dynagroup_saveSuccessful=ã°ã«ãŒãå®çŸ©å [{0}] ã®ä¿åã«æåããŸãã
-view_dynagroup_singleSaveFailure=ãšã©ãŒãçºçããŸãã - äžã€äœæãããã¹ãã§ãããã代ããã« [{0}] åäœãããŸãã
+view_dynagroup_singleSaveFailure=ãšã©ãŒãçºçããŸãã - 1 ã€äœæãããã¯ãã [{0}] åäœæãããŸãã
view_dynagroup_template_customExpression=ã«ã¹ã¿ã åŒ...
view_dynagroup_template_downedResources=ãã¹ãŠã®ãªãœãŒã¹ãçŸåšããŠã³ããŠããŸã
view_dynagroup_template_jbossas4_clusters=JBossAS 4 - ã¯ã©ã¹ã¿
-view_dynagroup_template_jbossas4_earClusters=JBossAS 4 - ã¯ã©ã¹ã¿å EARs
+view_dynagroup_template_jbossas4_earClusters=JBossAS 4 - ã¯ã©ã¹ã¿å EAR
view_dynagroup_template_jbossas4_hostingApp=JBossAS 4 - ãã¹ãã£ã³ã°ããŠãã"èªåã®"ã¢ããªã±ãŒã·ã§ã³ã®ãã¹ãŠ
view_dynagroup_template_jbossas4_nonsecured=JBossAS 4 - ã»ãã¥ã¢ã§ãªããã¹ãŠ
-view_dynagroup_template_jbossas4_uniqueVersions=JBossAS 4 - ãŠããŒã¯ããŒãžã§ã³
+view_dynagroup_template_jbossas4_uniqueVersions=JBossAS 4 - äžæã®ããŒãžã§ã³
view_dynagroup_template_jbossas5_clusters=JBossAS 5/6 - ã¯ã©ã¹ã¿
view_dynagroup_template_platforms=ã€ã³ãã³ããªå
ã®ãã©ãããã©ãŒã ãªãœãŒã¹
-view_dynagroup_template_uniqueResourceTypes=ã€ã³ãã³ããªå
ã®ãŠããŒã¯ãªãœãŒã¹ã¿ã€ã
+view_dynagroup_template_uniqueResourceTypes=ã€ã³ãã³ããªå
ã®äžæã®ãªãœãŒã¹ã¿ã€ã
view_groupConfigEdit_member=ã¡ã³ããŒ
view_groupConfigEdit_noListProps=ã°ã«ãŒãèšå®ã§çŸåšãµããŒããããŠããªãããããã£ã®ãªã¹ã
-view_groupConfigEdit_saveReminder=èšå®ãå€æŽãããŠããŸã - ãããã®å€æŽãä¿åããã®ãå¿ããªãã§ãã ããããããªããšå€±ãããŸãã
-view_groupConfigEdit_setAll=ãã¹ãŠã®å€ãèšå®\:
-view_groupConfigEdit_tooltip_1=ã¡ã³ããŒå€ã®éã - ç·šéããããã«ã¢ã€ã³ã³ãã¯ãªãã¯ããŸã
+view_groupConfigEdit_saveReminder=èšå®ã®äžéšãå€æŽãããŸãã - å€æŽãä¿åããªããšå€æŽå
容ã倱ãããŸãã\n
+view_groupConfigEdit_setAll=ãã¹ãŠã®å€ã次ã«èšå®\:
+view_groupConfigEdit_tooltip_1=ã¡ã³ããŒå€ãç°ãªããŸã - ã¢ã€ã³ã³ãã¯ãªãã¯ããŠç·šéããŠãã ããã
view_groupConfigEdit_unset=èšå®è§£é€
view_groupConfigEdit_valsDiff=ã¡ã³ããŒå€ã®éã
-view_groupConfigEdit_valsDiffForProp=ãããã㣠[{0}] ã®ããã®ã¡ã³ããŒå€
+view_groupConfigEdit_valsDiffForProp=ãããã㣠[{0}] ã®ã¡ã³ããŒå€
view_groupCreateWizard_createFailure=ãªãœãŒã¹ã°ã«ãŒã [{0}] ã®äœæã«å€±æããŸãã \: {1}
view_groupCreateWizard_createStepName=ã°ã«ãŒãèšå®
view_groupCreateWizard_createStep_group_exists=[{0}] ãšããååã®ã°ã«ãŒãã¯ãã§ã«ååšããŸã
view_groupCreateWizard_createStep_recursive=ååž°
view_groupCreateWizard_createSuccessful_concise=[{0}] ãšããååã®æ°èŠã®ãªãœãŒã¹ã°ã«ãŒããäœæããŸããã
-view_groupCreateWizard_createSuccessful_full=[{2}] ã¡ã³ããŒãªãœãŒã¹ãå«ã åå [{1}] ã®æ°èŠ [{0}] ãªãœãŒã¹ã°ã«ãŒããäœæããŸããã
+view_groupCreateWizard_createSuccessful_full=[{2}] ã¡ã³ããŒãªãœãŒã¹ãå«ãŸããåå [{1}] ãæã€æ°ãã [{0}] ãªãœãŒã¹ã°ã«ãŒããäœæãããŸããã
view_groupCreateWizard_membersStepName=ã¡ã³ããŒã®éžæ
view_groupCreateWizard_title=ã°ã«ãŒãäœæ
view_groupCreateWizard_windowTitle=ã°ã«ãŒãäœæ
@@ -1993,22 +1993,22 @@ view_group_operationScheduleDetails_memberResource=ã¡ã³ããŒãªãœãŒã¹
view_group_operationScheduleDetails_value_parallel=䞊è¡å®è¡
view_group_operationScheduleDetails_value_sequential=以äžã«ç€ºããé (ã¡ã³ããŒãªãœãŒã¹ããã©ãã°ã¢ã³ãããããããŠé çªãå€æŽ)
view_group_pluginConfig_edit_currentGroupProperties=çŸåšã®ã°ã«ãŒãããããã£
-view_group_pluginConfig_edit_invalid=次ã®ã³ãã¯ã·ã§ã³èšå®ããããã£ã«ã¯äžæ£ãªå€ãèšå®ãããŠããŸãã®ã§ãã³ãã¯ã·ã§ã³èšå®ãä¿åããããŸãã«ä¿®æ£ãããå¿
èŠããããŸã\: [{0}]
-view_group_pluginConfig_edit_noperm=ãã®ã°ã«ãŒãã³ãã¯ã·ã§ã³èšå®ãç·šéããæš©éããããŸãã
+view_group_pluginConfig_edit_invalid=次ã®æ¥ç¶èšå®ããããã£ãŒã«ã¯ç¡å¹ãªå€ãå«ãŸããŠããŸããç¡å¹ãªå€ãä¿®æ£ããªããšãèšå®ãä¿åã§ããŸãã\: [{0}]
+view_group_pluginConfig_edit_noperm=ãã®ã°ã«ãŒãæ¥ç¶èšå®ãç·šéããæš©éããããŸãã
view_group_pluginConfig_edit_saveFailure=ã°ã«ãŒãå [{1}] ãšäºææ§ã®ãã [{0}] ã®ããã®ã°ã«ãŒãèšå®æŽæ°ã®éå§ã«å€±æããŸãã
-view_group_pluginConfig_edit_saveInitiated_concise=ã°ã«ãŒãã³ãã¯ã·ã§ã³èšå®ã®æŽæ°ãéå§ãããŸãã
-view_group_pluginConfig_edit_saveInitiated_full=ã°ã«ãŒãã³ãã¯ã·ã§ã³èšå®ã®æŽæ°ã¯ã°ã«ãŒãå [{1}] ãšäºææ§ã®ãã [{0}] ã®ããã«éå§ãããŸãã
-view_group_pluginConfig_edit_saveTooltip=ãã¹ãŠã®ã°ã«ãŒãã¡ã³ããŒã®ã³ãã¯ã·ã§ã³èšå®ãæŽæ°ããŸã
-view_group_pluginConfig_edit_valid=ãã¹ãŠã®ã³ãã¯ã·ã§ã³èšå®ããããã£ã«ã¯æ£ããå€ãèšå®ãããŠããŸãã®ã§ãã³ãã¯ã·ã§ã³èšå®ã¯ä¿åã§ããŸã
+view_group_pluginConfig_edit_saveInitiated_concise=ã°ã«ãŒãæ¥ç¶èšå®ã®æŽæ°ãéå§ãããŸãã
+view_group_pluginConfig_edit_saveInitiated_full=ã°ã«ãŒãæ¥ç¶èšå®ã®æŽæ°ãã[{1}] ãšããååã® [{0}] ãšäºææ§ã®ããã°ã«ãŒãã«å¯ŸããŠéå§ãããŸãã
+view_group_pluginConfig_edit_saveTooltip=ãã¹ãŠã®ã°ã«ãŒãã¡ã³ããŒã®æ¥ç¶èšå®ãæŽæ°ããŸã
+view_group_pluginConfig_edit_valid=ãã¹ãŠã®æ¥ç¶èšå®ããããã£ãŒã®å€ãæå¹ã§ãããããèšå®ãä¿åã§ããŸã
view_group_pluginConfig_members_fetchFailure=ã°ã«ãŒã [{0}] ã®ã¡ã³ããŒã®ãã©ã°ã€ã³èšå®æŽæ°å±¥æŽã®ååŸã«å€±æããŸãã
-view_group_pluginConfig_members_fetchFailureConn=[{0}] ã®ããã®ã¡ã³ããŒã³ãã¯ã·ã§ã³èšå®ã®åãåºãã«å€±æããŸãã
+view_group_pluginConfig_members_fetchFailureConn=[{0}] ã®ã¡ã³ããŒæ¥ç¶èšå®ã®èªã¿åºãã«å€±æããŸãã
view_group_pluginConfig_members_fetchFailureConnInProgress=ã°ã«ãŒããã©ã°ã€ã³èšå®ã®æŽæ°ã¯çŸåšé²è¡äžã§ããã°ã«ãŒãèšå®ãé²èŠ§ããã«ã¯ãæŽæ°ãçµäºãããŸã§åŸ
æ©ããå¿
èŠããããŸãã
view_group_pluginConfig_members_statusDetails=ã¹ããŒã¿ã¹ã®è©³çŽ°
view_group_pluginConfig_members_statusFailure=ãã®èšå®ã®æŽæ°ã¯æªç¥ã®çç±ã«ãã倱æããŸãã
view_group_pluginConfig_members_statusInprogress=ãã®èšå®ã®æŽæ°ã¯ãŸã é²è¡äžã§ã
view_group_pluginConfig_members_statusNochange=ãã®èšå®ã¯å€æŽãããŸããã§ãã
view_group_pluginConfig_members_statusSuccess=ãã®èšå®ã®æŽæ°ã¯æåããŸãã
-view_group_pluginConfig_members_title=ã°ã«ãŒãã³ãã¯ã·ã§ã³èšå®ã®ã¡ã³ããŒå±¥æŽ
+view_group_pluginConfig_members_title=ã°ã«ãŒãæ¥ç¶èšå®ã®ã¡ã³ããŒå±¥æŽ
view_group_pluginConfig_table_clickStatusIcon=詳现ã¯ã¹ããŒã¿ã¹ã¢ã€ã³ã³ãã¯ãªãã¯ããŠãã ãã
view_group_pluginConfig_table_deleteFailure=ã°ã«ãŒããã©ã°ã€ã³èšå®å±¥æŽã®åé€ã«å€±æããŸãã
view_group_pluginConfig_table_deleteSuccessful=[{0}] ã®å±¥æŽãåé€ããŸãã
@@ -2019,11 +2019,11 @@ view_group_pluginConfig_table_statusFailure=ãã®ã°ã«ãŒãèšå®ã®æŽæ°ã¯
view_group_pluginConfig_table_statusInprogress=ãã®ã°ã«ãŒãèšå®ã®æŽæ°ã¯é²è¡äžã§ã
view_group_pluginConfig_table_statusNochange=ãã®ã°ã«ãŒãèšå®ã¯å€æŽãããŸããã§ãã
view_group_pluginConfig_table_statusSuccess=ãã®ã°ã«ãŒãèšå®ã®æŽæ°ã¯æåããŸãã
-view_group_pluginConfig_table_title=ã°ã«ãŒãã³ãã¯ã·ã§ã³èšå®å±¥æŽ
+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=ãã®ã°ã«ãŒãèšå®ãç·šéããæš©éããããŸãã
@@ -2635,7 +2635,7 @@ widget_resourceFactoryWizard_infoStep_loadFail=ã¢ãŒããã¯ãã£ãŒãå©çš
widget_resourceFactoryWizard_nameComment=管çãã©ã°ã€ã³ãŸãã¯ãããã®ç®¡ç察象ãªãœãŒã¹ã®äžéšã¯ããšãŒãžã§ã³ãã«ããæ°ãããªãœãŒã¹ã®ååèšå®ãèš±å¯ããŸããããã®å€ã¯ããã®æ©èœããµããŒããããšãŒãžã§ã³ããã©ã°ã€ã³ã®ã¿ã䜿çšããŸãããã®æ©èœããµããŒãããªããã©ã°ã€ã³ã®å ŽåããªãœãŒã¹ãçºèŠããããšãã«ãªãœãŒã¹ã«æ±çšãŸãã¯ç°ãªãååãä»ããããå¯èœæ§ããããŸãã
widget_resourceFactoryWizard_namePrompt=æ°èŠãªãœãŒã¹å
# widget_resourceFactoryWizard_nameComment = Not all management plug-ins or their managed resources allow the agent to set the name for a new resource. This value will only be used by agent plug-ins that support the capability. For plug-ins that do not support the capability, the resource may receive a generic or different name when it is discovered.
-widget_resourceFactoryWizard_templatePrompt=ã³ãã¯ã·ã§ã³èšå®ãã³ãã¬ãŒã
+widget_resourceFactoryWizard_templatePrompt=æ¥ç¶èšå®ãã³ãã¬ãŒã
widget_resourceFactoryWizard_timeoutFailure=ã¿ã€ã ã¢ãŠãããŸãã
widget_resourceFactoryWizard_timeoutHelp=æå®ãããå ŽåãåãªãœãŒã¹äœæ ({0} ãšãŒãžã§ã³ãäž) ã®ããã©ã«ãã®ã¿ã€ã ã¢ãŠãå€ãäžæžãããã¿ã€ã ã¢ãŠãã®æéã§ããããã©ã«ãã®ã¿ã€ã ã¢ãŠãå€ã¯ 60 ç§ã§ãã倧åã¢ããªã±ãŒã·ã§ã³ã®ãããã€ã¡ã³ããªã©ãç¹ã«é·ãäœæäœæ¥ã§ã¯å€ã倧ãããããšããã§ããããéåžžã以åã®äœæãã¿ã€ã ã¢ãŠãã«ãã£ãŠå€±æã«çµãã£ãå Žåã«äœ¿çšãããŸããã¿ã€ã ã¢ãŠãã«ãã£ãŠå€±æããå Žåã§ãããªãœãŒã¹ã®ãããã€ã¡ã³ãã¯æåããå¯èœæ§ããããã泚æããŠãã ãããã¿ã€ã ã¢ãŠããçºçããå ŽåããªãœãŒã¹ãåãããã€ããåã«ãã£ã¹ã«ããªã¹ãã£ã³ãå®è¡ãããšããã§ãããã
# ##widget_resourceFactoryWizard_timeoutFailure = Timed out
commit 0d73a65bd3f55aadf877ee5bcdfa530b8fde2dd1
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 10 13:42:16 2013 +0100
AS7 plugin minor changes: share base component logger and ASConnection verbose mode
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/BaseComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/BaseComponent.java
index a6043c4..2451743 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/BaseComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/BaseComponent.java
@@ -18,6 +18,8 @@
*/
package org.rhq.modules.plugins.jbossas7;
+import static org.rhq.modules.plugins.jbossas7.ASConnection.verbose;
+
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
@@ -82,6 +84,8 @@ import org.rhq.modules.plugins.jbossas7.json.Result;
public class BaseComponent<T extends ResourceComponent<?>> implements AS7Component<T>, MeasurementFacet,
ConfigurationFacet, DeleteResourceFacet, CreateChildResourceFacet, OperationFacet {
+ private static final Log LOG = LogFactory.getLog(BaseComponent.class);
+
static final String INTERNAL = "_internal:";
static final int INTERNAL_SIZE = INTERNAL.length();
static final String EXPRESSION = "_expr:";
@@ -90,7 +94,11 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
public static final String MANAGED_SERVER = "Managed Server";
- final Log log = LogFactory.getLog(this.getClass());
+ /**
+ * @deprecated as of 4.10. Use your own logger or {@link #getLog()} method.
+ */
+ @Deprecated
+ final Log log = LOG;
ResourceContext<T> context;
Configuration pluginConfiguration;
@@ -101,7 +109,6 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
String key;
boolean includeRuntime;
- private boolean verbose = ASConnection.verbose;
private BaseServerComponent serverComponent;
protected ASConnection testConnection;
@@ -192,7 +199,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
Result res = getASConnection().execute(op);
if (!res.isSuccess()) {
- log.warn("Getting metric [" + req.getName() + "] at [ " + address + "] failed: "
+ getLog().warn("Getting metric [" + req.getName() + "] at [ " + address + "] failed: "
+ res.getFailureDescription());
continue;
}
@@ -217,7 +224,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
addMetric2Report(report, req, val, resolveExpression);
}
} catch (NumberFormatException e) {
- log.warn("Non numeric input for [" + req.getName() + "] : [" + val + "]");
+ getLog().warn("Non numeric input for [" + req.getName() + "] : [" + val + "]");
}
} else if (req.getDataType() == DataType.TRAIT) {
@@ -226,8 +233,8 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
ResolveExpression resolveExpressionOperation = new ResolveExpression(expression);
Result result = getASConnection().execute(resolveExpressionOperation);
if (!result.isSuccess()) {
- if (log.isWarnEnabled()) {
- log.warn("Skipping trait [" + req.getName()
+ if (getLog().isWarnEnabled()) {
+ getLog().warn("Skipping trait [" + req.getName()
+ "] in measurement report. Could not resolve expression [" + expression
+ "], failureDescription:" + result.getFailureDescription());
continue;
@@ -250,8 +257,8 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
ResolveExpression resolveExpressionOperation = new ResolveExpression(expression);
Result result = getASConnection().execute(resolveExpressionOperation);
if (!result.isSuccess()) {
- if (log.isWarnEnabled()) {
- log.warn("Skipping metric [" + req.getName()
+ if (getLog().isWarnEnabled()) {
+ getLog().warn("Skipping metric [" + req.getName()
+ "] in measurement report. Could not resolve expression [" + expression
+ "], failureDescription:" + result.getFailureDescription());
return;
@@ -354,7 +361,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
@Override
public void deleteResource() throws Exception {
- log.info("Removing AS7 resource [" + path + "]...");
+ getLog().info("Removing AS7 resource [" + path + "]...");
if (context.getResourceType().getName().equals(MANAGED_SERVER)) {
// We need to do two steps because of AS7-4032
@@ -461,7 +468,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
JsonNode uploadResult = uploadConnection.finishUpload();
if (verbose) {
- log.info(uploadResult);
+ getLog().info(uploadResult);
}
if (ASUploadConnection.isErrorReply(uploadResult)) {
@@ -496,7 +503,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
String deploymentName, String hash) {
boolean toServerGroup = context.getResourceKey().contains("server-group=");
- log.info("Deploying [" + runtimeName + "] (toDomainOnly=" + !toServerGroup + ")...");
+ getLog().info("Deploying [" + runtimeName + "] (toDomainOnly=" + !toServerGroup + ")...");
ASConnection connection = getASConnection();
@@ -546,7 +553,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
resourceKey = serverGroupAddress.getPath();
if (verbose) {
- log.info("Deploy operation: " + cop);
+ getLog().info("Deploy operation: " + cop);
}
result = connection.execute(cop, 300);
@@ -556,14 +563,14 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
String failureDescription = result.getFailureDescription();
report.setErrorMessage(failureDescription);
report.setStatus(CreateResourceStatus.FAILURE);
- log.warn("Deploy of [" + runtimeName + "] failed: " + failureDescription);
+ getLog().warn("Deploy of [" + runtimeName + "] failed: " + failureDescription);
} else {
report.setStatus(CreateResourceStatus.SUCCESS);
report.setResourceName(runtimeName);
report.setResourceKey(resourceKey);
report.getPackageDetails().setSHA256(hash);
report.getPackageDetails().setInstallationTimestamp(System.currentTimeMillis());
- log.info("Deploy of [" + runtimeName + "] succeeded - Resource key is [" + resourceKey + "].");
+ getLog().info("Deploy of [" + runtimeName + "] succeeded - Resource key is [" + resourceKey + "].");
}
return report;
@@ -656,7 +663,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
}
operation.addAdditionalProperty(pl.getName(), items);
} else {
- log.error("PropertyMap for " + prop.getName() + " not yet supported");
+ getLog().error("PropertyMap for " + prop.getName() + " not yet supported");
}
}
}
@@ -698,7 +705,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
PropertyDefinitionSimple pds = (PropertyDefinitionSimple) pd;
return getObjectForProperty(prop, pds);
} else {
- log.warn("Property [" + prop.getName() + "] is not understood yet");
+ getLog().warn("Property [" + prop.getName() + "] is not understood yet");
return null;
}
}
@@ -866,7 +873,7 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
if (defaultValue != null) {
multicastHost = defaultValue;
} else {
- log.error("Failed to resolve expression value [" + expressionValue
+ getLog().error("Failed to resolve expression value [" + expressionValue
+ "] of 'multicast-address' attribute.");
}
}
@@ -912,4 +919,12 @@ public class BaseComponent<T extends ResourceComponent<?>> implements AS7Compone
}
}
+ /**
+ * Get the logger for AS7 plugin resource components.
+ *
+ * @return
+ */
+ public static Log getLog() {
+ return LOG;
+ }
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DeploymentComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DeploymentComponent.java
index 9a15a85..94c101f 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DeploymentComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DeploymentComponent.java
@@ -1,5 +1,26 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.modules.plugins.jbossas7;
+import static org.rhq.modules.plugins.jbossas7.ASConnection.verbose;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -29,7 +50,6 @@ import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.content.ContentFacet;
import org.rhq.core.pluginapi.content.ContentServices;
import org.rhq.core.pluginapi.content.FileContentDelegate;
-import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
@@ -52,8 +72,8 @@ import org.rhq.modules.plugins.jbossas7.json.Result;
*/
public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> implements OperationFacet, ContentFacet {
+
private static final String DOMAIN_DATA_CONTENT_SUBDIR = "/data/content";
- private boolean verbose = ASConnection.verbose;
private File deploymentFile;
@Override
@@ -123,13 +143,13 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
@Override
public DeployPackagesResponse deployPackages(Set<ResourcePackageDetails> packages, ContentServices contentServices) {
- log.debug("Starting deployment..");
+ getLog().debug("Starting deployment..");
DeployPackagesResponse response = new DeployPackagesResponse();
if (packages.size() != 1) {
response.setOverallRequestResult(ContentResponseResult.FAILURE);
response.setOverallRequestErrorMessage("Can only deploy one package at a time");
- log.warn("deployPackages can only deploy one package at a time");
+ getLog().warn("deployPackages can only deploy one package at a time");
}
ResourcePackageDetails detail = packages.iterator().next();
@@ -145,7 +165,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
}
ResourceType resourceType = context.getResourceType();
- log.info("Deploying " + resourceType.getName() + " Resource with key [" + detail.getKey() + "]...");
+ getLog().info("Deploying " + resourceType.getName() + " Resource with key [" + detail.getKey() + "]...");
try {
contentServices.downloadPackageBits(context.getContentContext(), detail.getKey(), out, true);
@@ -158,7 +178,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
JsonNode uploadResult = uploadConnection.finishUpload();
if (verbose) {
- log.info(uploadResult);
+ getLog().info(uploadResult);
}
if (ASUploadConnection.isErrorReply(uploadResult)) {
@@ -183,7 +203,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
response.setOverallRequestResult(ContentResponseResult.FAILURE);
}
- log.info("Result of deployment of " + resourceType.getName() + " Resource with key [" + detail.getKey() + "]: "
+ getLog().info("Result of deployment of " + resourceType.getName() + " Resource with key [" + detail.getKey() + "]: "
+ response);
return response;
@@ -288,7 +308,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
return deploymentFile;
}
} else {
- log.warn("Could not determine the location of the deployment - the content descriptor wasn't found for deployment"
+ getLog().warn("Could not determine the location of the deployment - the content descriptor wasn't found for deployment"
+ getAddress() + ".");
return null;
}
@@ -296,7 +316,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
Boolean archive = (Boolean) content.get(0).get("archive");
if (archive != null && !archive) {
- log.debug("Exploded deployments not supported for retrieving the content.");
+ getLog().debug("Exploded deployments not supported for retrieving the content.");
return null;
}
@@ -338,7 +358,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
}
deploymentFile = getDeploymentFileFromHash(hash, contentPath);
} else {
- log.warn("Failed to determine the deployment file of " + getAddress()
+ getLog().warn("Failed to determine the deployment file of " + getAddress()
+ " deployment. Neither path nor hash attributes were available.");
}
@@ -362,7 +382,7 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
return new File(relativeTo, path);
} else {
- log.warn("Unsupported property used as a base for deployment path specification: " + relativeTo);
+ getLog().warn("Unsupported property used as a base for deployment path specification: " + relativeTo);
return null;
}
}
@@ -394,8 +414,8 @@ public class DeploymentComponent extends BaseComponent<ResourceComponent<?>> imp
FileContentDelegate fileContentDelegate = new FileContentDelegate();
sha256 = fileContentDelegate.retrieveDeploymentSHA(file, context.getResourceDataDirectory());
} catch (Exception iex) {
- if (log.isDebugEnabled()) {
- log.debug("Problem calculating digest of package [" + file.getPath() + "]." + iex.getMessage());
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Problem calculating digest of package [" + file.getPath() + "]." + iex.getMessage());
}
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DomainDeploymentComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DomainDeploymentComponent.java
index 54cffbf..532d403 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DomainDeploymentComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/DomainDeploymentComponent.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.modules.plugins.jbossas7;
import java.util.ArrayList;
@@ -49,7 +68,7 @@ public class DomainDeploymentComponent extends DeploymentComponent implements Op
String resourceKey = context.getResourceKey();
resourceKey = resourceKey.substring(resourceKey.indexOf("=") + 1);
- log.info("Promoting [" + resourceKey + "] to server group(s) [" + serverGroups + "]...");
+ getLog().info("Promoting [" + resourceKey + "] to server group(s) [" + serverGroups + "]...");
PropertySimple simple = parameters.getSimple("enabled");
Boolean enabled = false;
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java
index 7f7bf33..4f8d88a 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2013 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* 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.
+ * 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.plugins.jbossas7;
@@ -163,12 +163,13 @@ public class Ejb3BeanRuntimeComponent extends BaseComponent<BaseComponent<?>> {
} else {
OSGiVersion currentAsVersion = getASVersion();
if (currentAsVersion == null) {
- log.warn("Could not determine the AS version while reporting unexpected result of method" +
- " stats. Request: " + request);
+ getLog().warn(
+ "Could not determine the AS version while reporting unexpected result of method"
+ + " stats. Request: " + request);
} else if (FIRST_VERSION_SUPPORTING_METHOD_STATS.compareTo(currentAsVersion) <= 0) {
- log.error("Unexpected type of results when querying method stats for measurement request " +
- request + ". Expected map but got " + (value == null ? "null" :
- value.getClass().getName()));
+ getLog().error(
+ "Unexpected type of results when querying method stats for measurement request " + request
+ + ". Expected map but got " + (value == null ? "null" : value.getClass().getName()));
}
}
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ManagedASComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ManagedASComponent.java
index 5c02170..c70fd3d 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ManagedASComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ManagedASComponent.java
@@ -57,10 +57,8 @@ public class ManagedASComponent extends BaseComponent<HostControllerComponent<?>
public void start(ResourceContext<HostControllerComponent<?>> hostControllerComponentResourceContext)
throws InvalidPluginConfigurationException, Exception {
super.start(hostControllerComponentResourceContext);
-
logFileEventDelegate = new LogFileEventResourceComponentHelper(context);
logFileEventDelegate.startLogFileEventPollers();
-
}
@Override
@@ -87,7 +85,7 @@ public class ManagedASComponent extends BaseComponent<HostControllerComponent<?>
try {
result = getASConnection().execute(getStatus);
} catch (Exception e) {
- log.warn(e.getMessage());
+ getLog().warn(e.getMessage());
return AvailabilityType.DOWN;
}
if (!result.isSuccess())
@@ -149,18 +147,21 @@ public class ManagedASComponent extends BaseComponent<HostControllerComponent<?>
}
if ("null".equals(val)) {
- if (realName.equals("product-name"))
+ if (realName.equals("product-name")) {
val = "JBoss AS";
- else if (realName.equals("product-version"))
+ }
+ else if (realName.equals("product-version")) {
val = getStringValue(props.get("release-version"));
- else
- log.debug("Value for " + realName + " was 'null' and no replacement found");
+ }
+ else if (getLog().isDebugEnabled()) {
+ getLog().debug("Value for " + realName + " was 'null' and no replacement found");
+ }
}
MeasurementDataTrait data = new MeasurementDataTrait(request, val);
report.addData(data);
}
- } else {
- log.debug("getSKMRequests failed: " + res.getFailureDescription());
+ } else if (getLog().isDebugEnabled()) {
+ getLog().debug("getSKMRequests failed: " + res.getFailureDescription());
}
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterConfigurationDiscovery.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterConfigurationDiscovery.java
index d8f3ea1..b58dfd4 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterConfigurationDiscovery.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterConfigurationDiscovery.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,14 @@
* 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.
+ * 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.plugins.jbossas7;
+import static org.rhq.modules.plugins.jbossas7.ASConnection.verbose;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -109,7 +112,7 @@ public class ModClusterConfigurationDiscovery extends SubsystemDiscovery {
}
}
- if (Boolean.getBoolean("as7plugin.verbose"))
+ if (verbose)
log.info("total path: [" + path + "]");
if (lookForChildren) {
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterContextComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterContextComponent.java
index 836b6a7..d22aa32 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterContextComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/ModClusterContextComponent.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.modules.plugins.jbossas7;
import org.rhq.core.domain.configuration.Configuration;
@@ -27,7 +46,7 @@ public class ModClusterContextComponent extends ModClusterComponent implements A
int indexOfSeparator = this.context.getResourceKey().indexOf(":");
context = ProxyInfo.Context.fromString(this.context.getResourceKey().substring(indexOfSeparator + 1));
} catch (Exception e) {
- log.warn("Invalid resourcekey is being used for modcluster component: " + e.getMessage());
+ getLog().warn("Invalid resourcekey is being used for modcluster component: " + e.getMessage());
return AvailabilityType.DOWN;
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/SubsystemDiscovery.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/SubsystemDiscovery.java
index e8f8301..8b68f46 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/SubsystemDiscovery.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/SubsystemDiscovery.java
@@ -19,6 +19,8 @@
package org.rhq.modules.plugins.jbossas7;
+import static org.rhq.modules.plugins.jbossas7.ASConnection.verbose;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -112,7 +114,7 @@ public class SubsystemDiscovery implements ResourceDiscoveryComponent<BaseCompon
}
}
- if (Boolean.getBoolean("as7plugin.verbose")) {
+ if (verbose) {
log.info("total path: [" + path + "]");
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/VHostComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/VHostComponent.java
index 342bcba..4f67159 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/VHostComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/VHostComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* 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.
+ * 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.plugins.jbossas7;
@@ -56,7 +56,7 @@ public class VHostComponent extends BaseComponent<VHostComponent> implements Mea
}
report.addData(data);
} else
- log.warn("Could not get aliases for " + getAddress() + ": " + res.getFailureDescription());
+ getLog().warn("Could not get aliases for " + getAddress() + ": " + res.getFailureDescription());
} else {
leftovers.add(request);
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/WebRuntimeComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/WebRuntimeComponent.java
index 072f4d7..9573cfb 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/WebRuntimeComponent.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/WebRuntimeComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * 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.plugins.jbossas7;
import java.io.File;
@@ -56,12 +57,12 @@ public class WebRuntimeComponent extends BaseComponent<BaseComponent<?>> {
}
if (logFile != null) {
- if (log.isDebugEnabled()) {
+ if (getLog().isDebugEnabled()) {
if (logFile.isFile()) {
- log.debug("[" + resourceContext.getResourceKey() + "] is using the response time log file ["
+ getLog().debug("[" + resourceContext.getResourceKey() + "] is using the response time log file ["
+ logFile + "]");
} else {
- log.debug("The response time log file [" + logFile + "] for ["
+ getLog().debug("The response time log file [" + logFile + "] for ["
+ resourceContext.getResourceKey() + "] does not exist yet.");
}
}
@@ -70,8 +71,8 @@ public class WebRuntimeComponent extends BaseComponent<BaseComponent<?>> {
this.responseTimeLogParser.setExcludes(responseTimeConfig.getExcludes());
this.responseTimeLogParser.setTransforms(responseTimeConfig.getTransforms());
} else {
- if (log.isDebugEnabled()) {
- log.debug("Cannot monitor response time for [" + resourceContext.getResourceKey()
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Cannot monitor response time for [" + resourceContext.getResourceKey()
+ "] - unknown log file location");
}
}
@@ -96,12 +97,12 @@ public class WebRuntimeComponent extends BaseComponent<BaseComponent<?>> {
this.responseTimeLogParser.parseLog(callTimeData);
report.addData(callTimeData);
} catch (Exception e) {
- log.error("Failed to retrieve call-time metric '" + RESPONSE_TIME_METRIC + "' for "
+ getLog().error("Failed to retrieve call-time metric '" + RESPONSE_TIME_METRIC + "' for "
+ context.getResourceType() + " Resource with key [" + context.getResourceKey() + "].",
e);
}
} else {
- log.error("The '" + RESPONSE_TIME_METRIC + "' metric is enabled for " + context.getResourceType()
+ getLog().error("The '" + RESPONSE_TIME_METRIC + "' metric is enabled for " + context.getResourceType()
+ " Resource with key [" + context.getResourceKey() + "], but no value is defined for the '"
+ ResponseTimeConfiguration.RESPONSE_TIME_LOG_FILE_CONFIG_PROP + "' connection property.");
// TODO: Communicate this error back to the server for display in the GUI.
@@ -147,13 +148,13 @@ public class WebRuntimeComponent extends BaseComponent<BaseComponent<?>> {
String logFileName = String.format("rt/%s%s_rt.log", virtualHost, contextRoot);
logFile = new File(logDir, logFileName);
} else {
- if (log.isDebugEnabled()) {
- log.debug("Unknown context root for: " + getAddress());
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Unknown context root for: " + getAddress());
}
}
} else {
- if (log.isDebugEnabled()) {
- log.debug("Unknown virtual host for: " + getAddress());
+ if (getLog().isDebugEnabled()) {
+ getLog().debug("Unknown virtual host for: " + getAddress());
}
}
} catch (Exception e) {
commit acc51182c7b2aedb4112a2257ea4acede50dbddd
Author: John Sanda <jsanda(a)redhat.com>
Date: Mon Dec 9 22:02:45 2013 -0500
fixing bugs in foreach with criteria query
diff --git a/modules/enterprise/remoting/cli/src/main/samples/modules/util.js b/modules/enterprise/remoting/cli/src/main/samples/modules/util.js
index 18c34b3..7de3db0 100644
--- a/modules/enterprise/remoting/cli/src/main/samples/modules/util.js
+++ b/modules/enterprise/remoting/cli/src/main/samples/modules/util.js
@@ -57,7 +57,7 @@ exports.foreach = function (obj, fn) {
MeasurementDefinition: function(criteria) { return MeasurementDefinitionManager.findMeasurementDefinitionsByCriteria(criteria); },
JPADriftChangeSet: function(criteria) { return DriftManager.findDriftChangeSetsByCriteria(criteria); },
JPADrift: function(criteria) { return DriftManager.findDriftsByCriteria(criteria); },
- MeasurementSchedule: function(criteria) { return MeasurementScheduleManager,findSchedulesByCriteria(criteria); },
+ MeasurementSchedule: function(criteria) { return MeasurementScheduleManager.findSchedulesByCriteria(criteria); },
OperationDefinition: function(criteria) { return OperationManager.findOperationDefinitionsByCriteria(criteria); },
ResourceOperationHistory: function(criteria) { return OperationManager.findResourceOperationHistoriesByCriteria(criteria); },
ResourceType: function(criteria) { return ResourceTypeManager.findResourceTypesByCriteria(criteria); },
@@ -79,24 +79,24 @@ exports.foreach = function (obj, fn) {
fn(iterator.next());
}
} else if (obj instanceof java.util.Map) {
- var iterator = obj.entrySet().iterator()
+ var iterator = obj.entrySet().iterator();
while (iterator.hasNext()) {
var entry = iterator.next();
fn(entry.key, entry.value);
}
} else if (obj instanceof Criteria) {
var criteria = obj;
- var executeQuery = criteriaExecutors[criteria.persistentClass];
+ var executeQuery = criteriaExecutors[criteria.persistentClass.simpleName];
if (executeQuery == null) {
throw "No criteria executor found for " + criteria.getClass().name + ". A new executor may need to be added to " +
"this script.";
}
- var currentPage = executeQuery();
+ var currentPage = executeQuery(criteria);
while (!currentPage.isEmpty()) {
- util.foreach(currentPage, fn);
+ this.foreach(currentPage, fn);
if (currentPage.pageControl == null && currentPage.pageControlOverrides == null) {
reachedEnd = true;
} else {
@@ -107,7 +107,7 @@ exports.foreach = function (obj, fn) {
}
currentPage.clear();
- currentPage = executeQuery();
+ currentPage = executeQuery(criteria);
}
}
} else { // assume we have a generic object
@@ -349,22 +349,4 @@ exports.asHash = function(configuration) {
})(configuration);
return ret;
-}
-
-exports.walkTree = function(root, visitorFn, nodesProperty, filterFn) {
- var nodes = root[nodesProperty];
- var stack = [];
- var pushChildNodesOntoStack = function(node) {
- exports.foreach(node[nodesProperty], function(childNode) {
- stack.push(childNode);
- });
- }
-
- pushChildNodesOntoStack(root);
-
- while (stack.length > 0) {
- var node = stack.pop();
- pushChildNodesOntoStack(node);
-
- }
}
\ No newline at end of file
commit ec2ad0ca872b6b377a8742bf1a9fabc4732598d8
Author: Simeon Pinder <spinder(a)fulliautomatix.conchfritter.com>
Date: Mon Dec 9 17:17:43 2013 -0500
BZ 1039664 - one more udpate to logged warning to refer to documentation.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 8f6d0b0..24e4995 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -140,7 +140,8 @@ public class RHQControl {
// but there are no other rpms other than the agent, so if we see this jboss-on-* location, we know the agent is here.
log.warn("An agent RPM installation was found in ["
+ jonDir
- + "]!!! You will not be able to successfully run this older agent anymore. You should manually remove it.");
+ + "]!!! You will not be able to successfully run this older agent anymore. You should consult the "
+ +"install documentation about manually removing and/or merging the old and new agent.");
return true;
} else {
return false;
commit b5acf065cbc30514263f30c29c35f8eb696e8407
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Mon Dec 9 15:37:43 2013 -0500
BZ 1037855 - related to the fix for BZ 971556 - the last commit for this BZ caused the api check to fail.
putting back the obsolete, but public, constants and marking them as deprecated.
diff --git a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/ApplicationServerPluginConfigurationProperties.java b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/ApplicationServerPluginConfigurationProperties.java
index 7c30135..c2a1ba1 100644
--- a/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/ApplicationServerPluginConfigurationProperties.java
+++ b/modules/plugins/jboss-as-5/src/main/java/org/rhq/plugins/jbossas5/ApplicationServerPluginConfigurationProperties.java
@@ -45,6 +45,8 @@ public class ApplicationServerPluginConfigurationProperties {
public static final String SHUTDOWN_MBEAN_OPERATION_CONFIG_PROP = "shutdownMBeanOperation";
public static final String SHUTDOWN_METHOD_CONFIG_PROP = "shutdownMethod";
public static final String SCRIPT_PREFIX_CONFIG_PROP = "scriptPrefix";
+ @Deprecated
+ public static final String AVAIL_CHECK_PERIOD_CONFIG_PROP = "availabilityCheckPeriod";
private ApplicationServerPluginConfigurationProperties() {
}
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/helper/ServerPluginConfiguration.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/helper/ServerPluginConfiguration.java
index 4695e61..d4c7e48 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/helper/ServerPluginConfiguration.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/helper/ServerPluginConfiguration.java
@@ -44,6 +44,8 @@ public class ServerPluginConfiguration {
public static final String LOG_DIR = "logDir";
public static final String PRODUCT_TYPE = "productType";
public static final String HOST_CONFIG_FILE = "hostConfigFile";
+ @Deprecated
+ public static final String AVAIL_CHECK_PERIOD_CONFIG_PROP = "availabilityCheckPeriod";
}
private Configuration pluginConfig;
@@ -144,4 +146,13 @@ public class ServerPluginConfiguration {
hostConfigFile.toString() : null);
}
+ @Deprecated
+ public Integer getAvailabilityCheckPeriod() {
+ return 0;
+ }
+
+ @Deprecated
+ public void setAvailabilityCheckPeriod(Integer availabilityCheckPeriod) {
+ }
+
}
diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java
index 873caaa..01c795f 100644
--- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java
+++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java
@@ -139,6 +139,8 @@ public class JBossASServerComponent<T extends ResourceComponent<?>> implements M
public static final String SHUTDOWN_MBEAN_OPERATION_CONFIG_PROP = "shutdownMbeanOperation";
public static final String SHUTDOWN_METHOD_CONFIG_PROP = "shutdownMethod";
public static final String JAVA_HOME_PATH_CONFIG_PROP = "javaHomePath";
+ @Deprecated
+ public static final String AVAIL_CHECK_PERIOD_CONFIG_PROP = "availabilityCheckPeriod";
public static final String BINDING_ADDRESS_CONFIG_PROP = "bindingAddress";
10 years, 4 months
[rhq] 4 commits - modules/enterprise
by mazz
modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java | 2
modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh | 5
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java | 39 +--
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java | 33 ++
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java | 112 +++++++---
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java | 33 +-
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java | 27 +-
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java | 55 +++-
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java | 6
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java | 55 +++-
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java | 35 +--
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java | 55 +++-
modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java | 67 +++--
13 files changed, 359 insertions(+), 165 deletions(-)
New commits:
commit c913286801818771276081035e99aa3cae36c96b
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 11:12:18 2014 -0500
i'll assume this was a mistake to import a JUnit annotation, and it should have been testng
diff --git a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
index 4060d07..8227e38 100644
--- a/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
+++ b/modules/enterprise/remoting/cli/src/test/java/org/rhq/enterprise/client/script/ModulesDirectoryScriptSourceProviderTest.java
@@ -28,8 +28,8 @@ import java.io.PrintWriter;
import java.net.URI;
import java.net.URISyntaxException;
-import org.junit.BeforeClass;
import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
commit 9128c69cfe78f1de182c46bbfeebaadb18001327
Author: John Mazzitelli <mazz(a)redhat.com>
Date: Thu Jan 2 10:50:02 2014 -0500
BZ 994250 - finish the merging/peer review for patches submitted so that rhqctl returns proper exit codes. Note that this completes the merge of the two submitted patches - see prior two commits to this one. This third commit fixes some problems with the original patches: 1. Some scripts/files are not found in bin/internal of the distro, but rather are in bin/ - so we need to avoid using getBinDir() in those cases 2. Fix the code to conform to code conventions - DEATH TO TABS! 3. Remove a constant that got resurrected (ControlCommand.RHQ_STORAGE_BASEDIR_PROP is no longer needed) 4. A couple other minor things
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index 27f8e8f..a13a598 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,7 +60,6 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
- public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
@@ -300,7 +299,7 @@ public abstract class ControlCommand {
}
protected String getStoragePid() throws IOException {
- File pidFile = getStoragePidFile();
+ File pidFile = getStoragePidFile();
if (pidFile.exists()) {
return StreamUtil.slurp(new FileReader(pidFile));
@@ -494,18 +493,18 @@ public abstract class ControlCommand {
protected boolean isStorageRunning() throws IOException {
String pid = getStoragePid();
- if(pid == null) {
- return false;
- } else if(pid != null && !isUnixPidRunning(pid)) {
- // There is a phantom pidfile
- File pidFile = getStoragePidFile();
- if(!pidFile.delete()) {
- throw new RHQControlException("Could not delete storage pidfile " + pidFile.getAbsolutePath());
- }
- return false;
- } else {
- return true;
- }
+ if (pid == null) {
+ return false;
+ } else if (pid != null && !isUnixPidRunning(pid)) {
+ // There is a phantom pidfile
+ File pidFile = getStoragePidFile();
+ if (!pidFile.delete()) {
+ throw new RHQControlException("Could not delete storage pidfile " + pidFile.getAbsolutePath());
+ }
+ return false;
+ } else {
+ return true;
+ }
}
private class NullOutputStream extends OutputStream {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 28a37de..88b085f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -135,7 +135,7 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
- rValue = EXIT_CODE_OPERATION_FAILED;
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index 1b3d47b..ed2b2f8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -88,14 +88,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue = RHQControl.EXIT_CODE_OK;
+ int rValue = RHQControl.EXIT_CODE_OK;
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- rValue = Math.max(rValue, executor.execute(commandLine));
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
@@ -277,7 +277,7 @@ public abstract class AbstractInstall extends ControlCommand {
return RHQControl.EXIT_CODE_OK;
}
- int rValue = 0;
+ int rValue = 0;
try {
File agentBinDir = new File(agentBasedir, "bin");
@@ -317,7 +317,7 @@ public abstract class AbstractInstall extends ControlCommand {
throw e;
}
- return rValue;
+ return rValue;
}
protected int startAgent(final File agentBasedir) throws Exception {
@@ -366,7 +366,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ org.apache.commons.exec.CommandLine commandLine;
int rValue = 0;
@@ -635,7 +635,7 @@ public abstract class AbstractInstall extends ControlCommand {
clearAgentPreferences();
int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
- return rValue;
+ return rValue;
}
private int installAgent(final File agentBasedir) throws IOException {
@@ -667,7 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
- return exitValue;
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 5d540a1..e2ef3a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -138,7 +138,7 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
- int rValue;
+ int rValue;
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
@@ -167,7 +167,7 @@ public class Start extends ControlCommand {
// For now we are duplicating logic in the status command. This code will be
// replaced when we implement a rhq-storage.sh script.
if (isStorageRunning()) {
- String pid = getStoragePid();
+ String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
rValue = RHQControl.EXIT_CODE_OK;
} else {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index a3920e0..7ef66b4 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -144,7 +144,7 @@ public class Stop extends AbstractInstall {
rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if(isStorageRunning()) {
+ if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index cc3d8c2..e80edd9 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -186,7 +186,7 @@ public class Upgrade extends AbstractInstall {
return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -229,7 +229,7 @@ public class Upgrade extends AbstractInstall {
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
- rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
@@ -262,7 +262,7 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(getBinDir());
+ executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
@@ -546,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
commit 72625626a668034023dcfaa39f283e9f28ea5749
Author: burmanm <yak(a)iki.fi>
Date: Sat Dec 28 23:56:16 2013 +0200
Fix the return codes for rhq-server.sh status and clean commands.
diff --git a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
index 9856190..1b974ae 100755
--- a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
+++ b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
@@ -610,7 +610,7 @@ case "$1" in
if [ "$_SERVER_RUNNING" = "1" ]; then
echo "$_SERVER_STATUS"
echo "Please shutdown the server before cleaning."
- exit 0
+ exit 1
fi
echo "Cleaning data, tmp and log directories..."
@@ -622,6 +622,9 @@ case "$1" in
'status')
echo "$_SERVER_STATUS"
echo "$_JVM_STATUS"
+ if [ "$_SERVER_RUNNING" = "0" ] || [ "$_JVM_RUNNING" = 0 ]; then
+ exit 3
+ fi
exit 0
;;
commit e626a35f95f2ad0c360438fdf87e7308b85f668a
Author: burmanm <yak(a)iki.fi>
Date: Tue Aug 6 17:52:38 2013 +0200
Fix return codes of the rhqctl command, rebased to the 4.10 master.
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
index db6a0ca..27f8e8f 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/ControlCommand.java
@@ -60,6 +60,7 @@ public abstract class ControlCommand {
public static final String SERVER_OPTION = "server";
public static final String STORAGE_OPTION = "storage";
public static final String AGENT_OPTION = "agent";
+ public static final String RHQ_STORAGE_BASEDIR_PROP = "rhq.storage.basedir";
public static final String RHQ_AGENT_BASEDIR_PROP = "rhq.agent.basedir";
protected static final String STORAGE_BASEDIR_NAME = "rhq-storage";
@@ -133,6 +134,7 @@ public abstract class ControlCommand {
throw new RHQControlException("Failed to load configuration", e);
}
}
+
defaultStorageBasedir = new File(getBaseDir(), STORAGE_BASEDIR_NAME);
defaultAgentBasedir = new File(getBaseDir().getParent(), AGENT_BASEDIR_NAME);
}
@@ -143,22 +145,26 @@ public abstract class ControlCommand {
public abstract Options getOptions();
- protected abstract void exec(CommandLine commandLine);
+ protected abstract int exec(CommandLine commandLine);
- public void exec(String[] args) {
+ public int exec(String[] args) {
Options options = getOptions();
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
CommandLineParser parser = new PosixParser();
CommandLine cmdLine = parser.parse(options, args);
- exec(cmdLine);
+ rValue = exec(cmdLine);
if (rhqctlConfig != null) {
rhqctlConfig.save();
}
} catch (ParseException e) {
printUsage();
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
} catch (ConfigurationException e) {
throw new RHQControlException("Failed to update " + getRhqCtlProperties(), e);
+ } finally {
+ return rValue;
}
}
@@ -386,7 +392,6 @@ public abstract class ControlCommand {
result = new org.apache.commons.exec.CommandLine("cmd.exe");
result.addArgument("/C");
result.addArgument(scriptName.replace('/', '\\') + ".bat");
-
} else {
result = new org.apache.commons.exec.CommandLine("./" + (addShExt ? scriptName + ".sh" : scriptName));
}
@@ -456,6 +461,7 @@ public abstract class ControlCommand {
PumpStreamHandler streamHandler = new PumpStreamHandler(new NullOutputStream(), new NullOutputStream());
executor.setStreamHandler(streamHandler);
org.apache.commons.exec.CommandLine commandLine;
+
commandLine = new org.apache.commons.exec.CommandLine("kill").addArgument(pid);
executor.execute(commandLine);
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
index 24e4995..28a37de 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/RHQControl.java
@@ -22,6 +22,7 @@
* * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
+
package org.rhq.server.control;
import java.io.Console;
@@ -50,6 +51,19 @@ public class RHQControl {
private final Log log = LogFactory.getLog(RHQControl.class);
+ public static final int EXIT_CODE_OK = 0;
+
+ // These try to follow the LSB specification - status command
+ public static final int EXIT_CODE_STATUS_NOT_RUNNING = 3;
+ public static final int EXIT_CODE_STATUS_UNKNOWN = 4;
+
+ // These try to follow the LSB specification - other commands
+ public static final int EXIT_CODE_OPERATION_FAILED = 1;
+ public static final int EXIT_CODE_INVALID_ARGUMENT = 2;
+ public static final int EXIT_CODE_NOT_INSTALLED = 5;
+// public static final int EXIT_CODE_OPERATION_NOT_RUNNING = 7;
+
+
private Commands commands = new Commands();
public void printUsage() {
@@ -63,7 +77,8 @@ public class RHQControl {
helpFormatter.printHelp(syntax, header, commands.getOptions(), footer);
}
- public void exec(String[] args) {
+ public int exec(String[] args) {
+ int rValue = EXIT_CODE_OK;
ControlCommand command = null;
boolean undo = false;
AbortHook abortHook = new AbortHook();
@@ -71,6 +86,7 @@ public class RHQControl {
try {
if (args.length == 0) {
printUsage();
+ rValue = EXIT_CODE_INVALID_ARGUMENT;
} else {
String commandName = findCommand(commands, args);
command = commands.get(commandName);
@@ -84,10 +100,11 @@ public class RHQControl {
Runtime.getRuntime().addShutdownHook(abortHook);
// run the command
- command.exec(getCommandLine(commandName, args));
+ rValue = command.exec(getCommandLine(commandName, args));
}
} catch (UsageException e) {
printUsage();
+ rValue = EXIT_CODE_INVALID_ARGUMENT;
} catch (RHQControlException e) {
undo = true;
@@ -98,9 +115,11 @@ public class RHQControl {
} else {
log.error(rootCause.getMessage());
}
+ rValue = EXIT_CODE_OPERATION_FAILED;
} catch (Throwable t) {
undo = true;
log.error(t);
+ rValue = EXIT_CODE_OPERATION_FAILED;
} finally {
abortHook.setCommand(null);
Runtime.getRuntime().removeShutdownHook(abortHook);
@@ -116,10 +135,11 @@ public class RHQControl {
} catch (Throwable t) {
log.warn("Failed to clean up after the failed installation attempt. "
+ "You may have to clean up some things before attempting to install again", t);
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
}
- return;
+ return rValue;
}
private void logWarningIfAgentRPMIsInstalled(ControlCommand command) {
@@ -303,9 +323,9 @@ public class RHQControl {
public static void main(String[] args) throws Exception {
RHQControl control = new RHQControl();
+ int rValue;
try {
- control.exec(args);
- System.exit(0);
+ rValue = control.exec(args);
} catch (RHQControlException e) {
Throwable rootCause = ThrowableUtil.getRootCause(e);
// Only show the messy stack trace if we're in debug mode. Otherwise keep it cleaner for the user...
@@ -314,8 +334,9 @@ public class RHQControl {
} else {
control.log.error("There was an unexpected error: " + rootCause.getMessage());
}
- System.exit(1);
+ rValue = EXIT_CODE_OPERATION_FAILED;
}
+ System.exit(rValue);
}
private class AbortHook extends Thread {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
index eb6cd15..1b3d47b 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/AbstractInstall.java
@@ -43,6 +43,7 @@ import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.PosixParser;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
+import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
@@ -55,6 +56,7 @@ import org.rhq.core.util.file.FileReverter;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -79,28 +81,31 @@ public abstract class AbstractInstall extends ControlCommand {
private static final String PREF_RHQ_AGENT_SERVER_BINDPORT = "rhq.agent.server.bind-port";
private static final String PREF_RHQ_AGENT_SERVER_TRANSPORTPARAMS = "rhq.agent.server.transport-params";
- protected void installWindowsService(File workingDir, String batFile, boolean replaceExistingService, boolean start)
+ protected int installWindowsService(File workingDir, String batFile, boolean replaceExistingService, boolean start)
throws Exception {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(workingDir);
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (replaceExistingService) {
- commandLine = getCommandLine(batFile, "stop");
- executor.execute(commandLine);
+ commandLine = getCommandLine(batFile, "stop");
+ rValue = Math.max(rValue, executor.execute(commandLine));
- commandLine = getCommandLine(batFile, "remove");
- executor.execute(commandLine);
+ commandLine = getCommandLine(batFile, "remove");
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
commandLine = getCommandLine(batFile, "install");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
if (start) {
commandLine = getCommandLine(batFile, "start");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
}
+ return rValue;
}
protected void validateCustomStorageDataDirectories(CommandLine commandLine, List<String> errors) {
@@ -155,6 +160,31 @@ public abstract class AbstractInstall extends ControlCommand {
}
+ protected boolean isUnixPidRunning(String pid) {
+
+ Executor executor = new DefaultExecutor();
+ executor.setWorkingDirectory(getBinDir());
+ executor.setStreamHandler(new PumpStreamHandler());
+ org.apache.commons.exec.CommandLine commandLine;
+
+ commandLine = new org.apache.commons.exec.CommandLine("/bin/kill").addArgument("-0").addArgument(pid);
+
+ try {
+ int code = executor.execute(commandLine);
+ if (code != 0) {
+ return false;
+ }
+ } catch (ExecuteException ee) {
+ if (ee.getExitValue() == 1) {
+ // return code 1 means process does not exist
+ return false;
+ }
+ } catch (IOException e) {
+ log.error("Checking for running process failed: " + e.getMessage());
+ }
+ return true;
+ }
+
protected void waitForRHQServerToInitialize() throws Exception {
try {
final long messageInterval = 30000L;
@@ -169,13 +199,13 @@ public abstract class AbstractInstall extends ControlCommand {
long totalWait = (now - timerStart);
if (totalWait < problemMessageInterval) {
- log.info("Still waiting for server to initialize...");
+ log.info("Still waiting for server to start...");
} else {
long minutes = totalWait / 60000;
log.info("It has been over ["
+ minutes
- + "] minutes - you may want to ensure your server initialization is proceeding as expected. You can check the log at ["
+ + "] minutes - you may want to ensure your server startup is proceeding as expected. You can check the log at ["
+ new File(getBaseDir(), "logs/server.log").getPath() + "].");
timerStart = now;
@@ -196,6 +226,7 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
+ @SuppressWarnings("resource")
protected boolean isRHQServerInitialized() throws IOException {
BufferedReader reader = null;
@@ -241,11 +272,13 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void updateWindowsAgentService(final File agentBasedir) throws Exception {
+ protected int updateWindowsAgentService(final File agentBasedir) throws Exception {
if (!isWindows()) {
- return;
+ return RHQControl.EXIT_CODE_OK;
}
+ int rValue = 0;
+
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -262,7 +295,7 @@ public abstract class AbstractInstall extends ControlCommand {
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
@@ -270,22 +303,26 @@ public abstract class AbstractInstall extends ControlCommand {
commandLine = getCommandLine("rhq-agent-wrapper", "remove");
try {
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
} catch (Exception e) {
// Ignore, service may not exist, script returns 1
log.debug("Failed to uninstall agent service", e);
}
commandLine = getCommandLine("rhq-agent-wrapper", "install");
- executor.execute(commandLine);
+ rValue = Math.max(rValue, executor.execute(commandLine));
} catch (IOException e) {
log.error("An error occurred while updating the agent service: " + e.getMessage());
throw e;
}
+
+ return rValue;
}
- protected void startAgent(final File agentBasedir) throws Exception {
+ protected int startAgent(final File agentBasedir) throws Exception {
+ int rValue;
+
try {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -300,7 +337,7 @@ public abstract class AbstractInstall extends ControlCommand {
// For *nix, just start the server in the background, for Win, now that the service is installed, start it
commandLine = getCommandLine("rhq-agent-wrapper", "start");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
// if any errors occur after now, we need to stop the agent
addUndoTask(new ControlCommand.UndoTask("Stopping agent") {
@@ -314,9 +351,10 @@ public abstract class AbstractInstall extends ControlCommand {
log.error("An error occurred while starting the agent: " + e.getMessage());
throw e;
}
+ return rValue;
}
- protected void killAgent(File agentBasedir) throws Exception {
+ protected int killAgent(File agentBasedir) throws Exception {
File agentBinDir = new File(agentBasedir, "bin");
if (!agentBinDir.exists()) {
@@ -328,25 +366,32 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
+ org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+
+ int rValue = 0;
if (isWindows()) {
try {
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
- executor.execute(commandLine);
+ commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
- org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "kill");
- executor.execute(commandLine);
+ commandLine = getCommandLine("rhq-agent-wrapper", "kill");
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- protected void stopServer() throws Exception {
+ protected int stopServer() throws Exception {
File serverBinDir = getBinDir();
if (!serverBinDir.exists()) {
@@ -360,16 +405,20 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+ return rValue;
}
protected void startRHQServerForInstallation() throws IOException {
@@ -446,7 +495,7 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void runRHQServerInstaller() throws IOException {
+ protected int runRHQServerInstaller() throws IOException {
try {
log.info("Installing RHQ server");
@@ -467,10 +516,14 @@ public abstract class AbstractInstall extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine, new DefaultExecuteResultHandler());
+ DefaultExecuteResultHandler executeHandler = new DefaultExecuteResultHandler();
+
+ executor.execute(commandLine, executeHandler);
log.info("The server installer is running");
- } catch (Exception e) {
+ return executeHandler.getExitValue();
+ } catch (IOException e) {
log.error("An error occurred while starting the server installer: " + e.getMessage());
+ return RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
@@ -560,6 +613,7 @@ public abstract class AbstractInstall extends ControlCommand {
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
+
int exitCode = executor.execute(commandLine);
log.info("The storage node installer has finished with an exit value of " + exitCode);
@@ -577,13 +631,14 @@ public abstract class AbstractInstall extends ControlCommand {
}
}
- protected void installAgent(final File agentBasedir, final CommandLine commandLine) throws Exception {
+ protected int installAgent(final File agentBasedir, final CommandLine commandLine) throws Exception {
clearAgentPreferences();
- installAgent(agentBasedir);
+ int rValue = installAgent(agentBasedir);
configureAgent(agentBasedir, commandLine);
+ return rValue;
}
- private void installAgent(final File agentBasedir) throws IOException {
+ private int installAgent(final File agentBasedir) throws IOException {
try {
log.info("Installing RHQ agent");
@@ -612,6 +667,7 @@ public abstract class AbstractInstall extends ControlCommand {
int exitValue = executor.execute(commandLine);
log.info("The agent installer finished running with exit value " + exitValue);
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while running the agent installer: " + e.getMessage());
throw e;
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
index 4139969..6039e9d 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Console.java
@@ -32,6 +32,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -70,32 +71,38 @@ public class Console extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (commandLine.getOptions().length != 1) {
printUsage();
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
} else {
String option = commandLine.getOptions()[0].getLongOpt();
try {
if (option.equals(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- startStorageInForeground();
+ rValue = Math.max(rValue, startStorageInForeground());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else if (option.equals(SERVER_OPTION)) {
if (isServerInstalled()) {
- startServerInForeground();
+ rValue = Math.max(rValue, startServerInForeground());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else if (option.equals(AGENT_OPTION)) {
if (isAgentInstalled()) {
- startAgentInForeground();
+ rValue = Math.max(rValue, startAgentInForeground());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
} else {
throw new IllegalArgumentException(option + " is not a supported option");
@@ -104,32 +111,33 @@ public class Console extends ControlCommand {
throw new RHQControlException("Failed to execute console command", e);
}
}
+ return rValue;
}
- private void startStorageInForeground() throws Exception {
+ private int startStorageInForeground() throws Exception {
log.debug("Starting RHQ storage node in foreground");
File storageBinDir = new File(getStorageBasedir(), "bin");
org.apache.commons.exec.CommandLine commandLine = new org.apache.commons.exec.CommandLine(getCommandLine(false,
"cassandra", "-f"));
- Executor exeuctor = new DefaultExecutor();
- exeuctor.setWorkingDirectory(storageBinDir);
- exeuctor.setStreamHandler(new PumpStreamHandler());
- exeuctor.execute(commandLine);
+ Executor executor = new DefaultExecutor();
+ executor.setWorkingDirectory(storageBinDir);
+ executor.setStreamHandler(new PumpStreamHandler());
+ return executor.execute(commandLine);
}
- private void startServerInForeground() throws Exception {
+ private int startServerInForeground() throws Exception {
log.debug("Starting RHQ server in foreground");
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "console");
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine);
+ return executor.execute(commandLine);
}
- private void startAgentInForeground() throws Exception {
+ private int startAgentInForeground() throws Exception {
log.info("Starting RHQ agent in foreground");
File agentHomeDir = getAgentBasedir();
@@ -165,6 +173,7 @@ public class Console extends ControlCommand {
// agentThread.start();
// doneSignal.await();
// agentThread.join();
+ return RHQControl.EXIT_CODE_OK;
}
private class AgentInputStreamPipe extends Thread {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
index be76e59..3ab3f00 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Install.java
@@ -33,6 +33,7 @@ import org.apache.commons.cli.Options;
import org.rhq.core.util.file.FileReverter;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -95,10 +96,11 @@ public class Install extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
boolean start = commandLine.hasOption(START_OPTION);
boolean startedStorage = false;
boolean startedServer = false;
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
List<String> errors = validateOptions(commandLine);
@@ -107,7 +109,7 @@ public class Install extends AbstractInstall {
log.error(error);
}
log.error("Exiting due to the previous errors");
- return;
+ return RHQControl.EXIT_CODE_NOT_INSTALLED;
}
// If any failures occur, we know we need to reset rhq-server.properties.
@@ -137,10 +139,10 @@ public class Install extends AbstractInstall {
if (isWindows()) {
log.info("Ensuring the RHQ Storage Windows service exists. Ignore any CreateService failure.");
- installWindowsService(getBinDir(), "rhq-storage", false, false);
+ rValue = Math.max(rValue, installWindowsService(getBinDir(), "rhq-storage", false, false));
}
} else {
- installStorageNode(getStorageBasedir(), commandLine, false);
+ rValue = Math.max(rValue, installStorageNode(getStorageBasedir(), commandLine, false));
startStorage = start;
}
}
@@ -148,7 +150,7 @@ public class Install extends AbstractInstall {
if (startStorage || installServer) {
startedStorage = true;
Start startCommand = new Start();
- startCommand.exec(new String[] { "start", "--storage" });
+ rValue = Math.max(rValue, startCommand.exec(new String[] { "start", "--storage" }));
}
if (installServer) {
@@ -157,12 +159,12 @@ public class Install extends AbstractInstall {
if (isWindows()) {
log.info("Ensuring the RHQ Server Windows service exists. Ignore any CreateService failure.");
- installWindowsService(getBinDir(), "rhq-server", false, false);
+ rValue = Math.max(rValue, installWindowsService(getBinDir(), "rhq-server", false, false));
}
} else {
startedServer = true;
startRHQServerForInstallation();
- runRHQServerInstaller();
+ rValue = Math.max(rValue, runRHQServerInstaller());
waitForRHQServerToInitialize();
}
}
@@ -175,7 +177,7 @@ public class Install extends AbstractInstall {
if (isWindows()) {
try {
log.info("Ensuring the RHQ Agent Windows service exists. Ignore any CreateService failure.");
- installWindowsService(new File(getAgentBasedir(), "bin"), "rhq-agent-wrapper", false, false);
+ rValue = Math.max(rValue, installWindowsService(new File(getAgentBasedir(), "bin"), "rhq-agent-wrapper", false, false));
} catch (Exception e) {
// Ignore, service may already exist or be running, wrapper script returns 1
log.debug("Failed to stop agent service", e);
@@ -185,10 +187,10 @@ public class Install extends AbstractInstall {
File agentBasedir = getAgentBasedir();
installAgent(agentBasedir, commandLine);
- updateWindowsAgentService(agentBasedir);
+ rValue = Math.max(rValue, updateWindowsAgentService(agentBasedir));
if (start) {
- startAgent(agentBasedir);
+ rValue = Math.max(rValue, startAgent(agentBasedir));
}
}
}
@@ -200,13 +202,14 @@ public class Install extends AbstractInstall {
if (!start && (startedStorage || startedServer)) {
Stop stopCommand = new Stop();
if (startedServer) {
- stopCommand.exec(new String[] { "stop", "--server" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--server" }));
}
if (startedStorage) {
- stopCommand.exec(new String[] { "stop", "--storage" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--storage" }));
}
}
}
+ return rValue;
}
private List<String> validateOptions(CommandLine commandLine) {
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
index cb8f43b..1de4250 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Remove.java
@@ -34,6 +34,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -74,52 +75,56 @@ public class Remove extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then stop whatever is installed
if (commandLine.getOptions().length == 0) {
if (isAgentInstalled()) {
- removeAgentService();
+ rValue = Math.max(rValue, removeAgentService());
}
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to remove the service. This
// may help clean up a failed install.
- removeServerService();
+ rValue = Math.max(rValue, removeServerService());
if (isStorageInstalled()) {
- removeStorageService();
+ rValue = Math.max(rValue, removeStorageService());
}
} else {
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- removeAgentService();
+ rValue = Math.max(rValue, removeAgentService());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
if (commandLine.hasOption(SERVER_OPTION)) {
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to remove the service. This
// may help clean up a failed install.
- removeServerService();
+ rValue = Math.max(rValue, removeServerService());
}
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- removeStorageService();
+ rValue = Math.max(rValue, removeStorageService());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to stop services", e);
}
+ return rValue;
}
- private void removeStorageService() throws Exception {
+ private int removeStorageService() throws Exception {
log.debug("Stopping RHQ storage node");
Executor executor = new DefaultExecutor();
@@ -127,12 +132,15 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
commandLine = getCommandLine("rhq-storage", "remove");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
log.debug("Failed to remove storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getStoragePid();
@@ -141,15 +149,18 @@ public class Remove extends ControlCommand {
System.out.println("RHQ storage node (pid=" + pid + ") is stopping...");
commandLine = new org.apache.commons.exec.CommandLine("kill").addArgument(pid);
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
System.out.println("RHQ storage node has stopped");
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void removeServerService() throws Exception {
+ private int removeServerService() throws Exception {
log.debug("Stopping RHQ server");
Executor executor = new DefaultExecutor();
@@ -157,25 +168,31 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
try {
commandLine = getCommandLine("rhq-server", "remove");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to remove server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getServerPid();
if (pid != null) {
commandLine = getCommandLine("rhq-server", "stop");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void removeAgentService() throws Exception {
+ private int removeAgentService() throws Exception {
log.debug("Stopping RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -184,21 +201,27 @@ public class Remove extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
try {
commandLine = getCommandLine("rhq-agent-wrapper", "remove");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist, script returns 1
log.debug("Failed to remove agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
commandLine = getCommandLine("rhq-agent-wrapper", "stop");
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
index fffb769..40d61c6 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Restart.java
@@ -64,11 +64,13 @@ public class Restart extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
Stop stop = new Stop();
stop.exec(commandLine);
+ // If the server isn't stopped.. restart had some LSB rules.. check 'em.
+
Start start = new Start();
- start.exec(commandLine);
+ return start.exec(commandLine);
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
index 7d621cb..5d540a1 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Start.java
@@ -36,6 +36,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -72,7 +73,8 @@ public class Start extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then start whatever is installed
if (commandLine.getOptions().length == 0) {
@@ -82,55 +84,62 @@ public class Start extends ControlCommand {
if (!(storageInstalled || serverInstalled || agentInstalled)) {
log.warn("Nothing to start. No RHQ services are installed.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
} else {
if (storageInstalled) {
- startStorage();
+ rValue = Math.max(rValue, startStorage());
}
if (serverInstalled) {
- startRHQServer();
+ rValue = Math.max(rValue, startRHQServer());
}
if (agentInstalled) {
- startAgent();
+ rValue = Math.max(rValue, startAgent());
}
}
} else {
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- startStorage();
+ rValue = Math.max(rValue, startStorage());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
if (commandLine.hasOption(SERVER_OPTION)) {
if (isServerInstalled()) {
- startRHQServer();
+ rValue = Math.max(rValue, startRHQServer());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- startAgent();
+ rValue = Math.max(rValue, startAgent());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_NOT_INSTALLED;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to start services", e);
}
+ return rValue;
}
- private void startStorage() throws Exception {
+ private int startStorage() throws Exception {
log.debug("Starting RHQ storage node");
Executor executor = new DefaultExecutor();
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
// Cassandra looks for JAVA_HOME or then defaults to PATH. We want it to use the Java
// defined for RHQ, so make sure JAVA_HOME is set, and set to the RHQ Java for the executor
// environment.
@@ -144,11 +153,12 @@ public class Start extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
commandLine = getCommandLine("rhq-storage", "start");
try {
- executor.execute(commandLine, env);
+ rValue = executor.execute(commandLine, env);
} catch (Exception e) {
// Ignore, service may not exist or may already be running, script returns 1
log.debug("Failed to start storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
File storageBinDir = new File(getStorageBasedir(), "bin");
@@ -159,16 +169,18 @@ public class Start extends ControlCommand {
if (isStorageRunning()) {
String pid = getStoragePid();
System.out.println("RHQ storage node (pid " + pid + ") is running");
+ rValue = RHQControl.EXIT_CODE_OK;
} else {
commandLine = getCommandLine(false, "cassandra", "-p", pidFile.getAbsolutePath());
executor.setWorkingDirectory(storageBinDir);
- executor.execute(commandLine, env);
+ rValue = executor.execute(commandLine, env);
}
}
+ return rValue;
}
- private void startRHQServer() throws Exception {
+ private int startRHQServer() throws Exception {
log.debug("Starting RHQ server");
Executor executor = new DefaultExecutor();
@@ -176,19 +188,23 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "start");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to start server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+ return rValue;
}
- private void startAgent() throws Exception {
+ private int startAgent() throws Exception {
log.debug("Starting RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -197,16 +213,21 @@ public class Start extends ControlCommand {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "start");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to start agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
}
+
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
index 9ba9f60..e52f0a8 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Status.java
@@ -35,6 +35,7 @@ import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -71,23 +72,24 @@ public class Status extends ControlCommand {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
try {
// if no options specified, then check the status of whatever is installed
if (commandLine.getOptions().length == 0) {
if (isStorageInstalled()) {
- checkStorageStatus();
+ rValue = Math.max(rValue, checkStorageStatus());
}
if (isServerInstalled()) {
- checkServerStatus();
+ rValue = Math.max(rValue, checkServerStatus());
}
if (isAgentInstalled()) {
- checkAgentStatus();
+ rValue = Math.max(rValue, checkAgentStatus());
}
} else {
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- checkStorageStatus();
+ rValue = Math.max(rValue, checkStorageStatus());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
@@ -95,7 +97,7 @@ public class Status extends ControlCommand {
}
if (commandLine.hasOption(SERVER_OPTION)) {
if (isServerInstalled()) {
- checkServerStatus();
+ rValue = Math.max(rValue, checkServerStatus());
} else {
log.warn("It appears that the server is not installed. The --" + SERVER_OPTION
+ " option will be ignored.");
@@ -103,7 +105,7 @@ public class Status extends ControlCommand {
}
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- checkAgentStatus();
+ rValue = Math.max(rValue, checkAgentStatus());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
@@ -113,11 +115,14 @@ public class Status extends ControlCommand {
} catch (Exception e) {
throw new RHQControlException("Failed to check statuses", e);
}
+ return rValue;
}
- private void checkStorageStatus() throws Exception {
+ private int checkStorageStatus() throws Exception {
log.debug("Checking RHQ storage node status");
+ int rValue = RHQControl.EXIT_CODE_OK;
+
if (isWindows()) {
Executor executor = new DefaultExecutor();
executor.setStreamHandler(new PumpStreamHandler());
@@ -125,31 +130,34 @@ public class Status extends ControlCommand {
executor.setWorkingDirectory(getBinDir());
commandLine = getCommandLine("rhq-storage", "status");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
log.debug("Failed to check storage service status", e);
+ rValue = RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
} else {
if(isStorageRunning()) {
System.out.println(String.format("%-30s", "RHQ Storage Node") + " (pid " + String.format("%-7s", getStoragePid()) + ") IS running");
} else {
System.out.println(String.format("%-30s", "RHQ Storage Node") + " (no pid file) IS NOT running");
+ rValue = RHQControl.EXIT_CODE_STATUS_NOT_RUNNING;
}
}
+ return rValue;
}
- private void checkServerStatus() throws Exception {
+ private int checkServerStatus() throws Exception {
log.debug("Checking RHQ server status");
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "status");
Executor executor = new DefaultExecutor();
executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
- executor.execute(commandLine);
+ return executor.execute(commandLine);
}
- private void checkAgentStatus() throws Exception {
+ private int checkAgentStatus() throws Exception {
log.debug("Checking RHQ agent status");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -159,7 +167,7 @@ public class Status extends ControlCommand {
executor.setWorkingDirectory(agentBinDir);
executor.setStreamHandler(new PumpStreamHandler());
try {
- executor.execute(commandLine);
+ return executor.execute(commandLine);
} catch (ExecuteException e) {
// For windows the JSW exit code for a status check is expected to be a mask value and the agent wrapper
// .bat will return it explicitly. We can ignore it and assume that the logged output is sufficient.
@@ -167,6 +175,7 @@ public class Status extends ControlCommand {
if (!isWindows()) {
throw e;
}
+ return RHQControl.EXIT_CODE_STATUS_UNKNOWN;
}
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
index 9b51e28..a3920e0 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Stop.java
@@ -33,6 +33,7 @@ import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -68,29 +69,33 @@ public class Stop extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+
+ int rValue = RHQControl.EXIT_CODE_OK;
+
try {
// if no options specified, then stop whatever is installed
if (commandLine.getOptions().length == 0) {
if (isAgentInstalled()) {
- stopAgent();
+ rValue = Math.max(rValue, stopAgent());
}
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to stop the service. This
// may help clean up a failed install.
- stopRHQServer();
+ rValue = Math.max(rValue, stopRHQServer());
if (isStorageInstalled()) {
- stopStorage();
+ rValue = Math.max(rValue, stopStorage());
}
} else {
if (commandLine.hasOption(AGENT_OPTION)) {
if (isAgentInstalled()) {
- stopAgent();
+ rValue = Math.max(rValue, stopAgent());
} else {
log.warn("It appears that the agent is not installed. The --" + AGENT_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
@@ -98,24 +103,26 @@ public class Stop extends AbstractInstall {
// the server service may be installed even if the full server install fails. The files to execute
// the remove are there after the initial unzip, so just go ahead and try to stop the service. This
// may help clean up a failed install.
- stopRHQServer();
+ rValue = Math.max(rValue, stopRHQServer());
}
if (commandLine.hasOption(STORAGE_OPTION)) {
if (isStorageInstalled()) {
- stopStorage();
+ rValue = Math.max(rValue, stopStorage());
} else {
log.warn("It appears that the storage node is not installed. The --" + STORAGE_OPTION
+ " option will be ignored.");
+ rValue = RHQControl.EXIT_CODE_INVALID_ARGUMENT;
}
}
}
} catch (Exception e) {
throw new RHQControlException("Failed to stop services", e);
}
+ return rValue;
}
- private void stopStorage() throws Exception {
+ private int stopStorage() throws Exception {
log.debug("Stopping RHQ storage node");
Executor executor = new DefaultExecutor();
@@ -123,17 +130,21 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine;
+ int rValue;
+
if (isWindows()) {
commandLine = getCommandLine("rhq-storage", "stop");
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ System.out.println("RHQ storage node has stopped");
} catch (Exception e) {
// Ignore, service may not exist or be running, script returns 1
log.debug("Failed to stop storage service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
- if (isStorageRunning()) {
+ if(isStorageRunning()) {
String pid = getStoragePid();
System.out.println("Stopping RHQ storage node...");
@@ -145,11 +156,13 @@ public class Stop extends AbstractInstall {
System.out.println("RHQ storage node has stopped");
}
+ rValue = RHQControl.EXIT_CODE_OK; // If process isn't running, stopping it is considered OK.
}
+ return rValue;
}
- private void stopRHQServer() throws Exception {
+ private int stopRHQServer() throws Exception {
log.debug("Stopping RHQ server");
Executor executor = new DefaultExecutor();
@@ -157,23 +170,29 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-server", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop server service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getServerPid();
if (pid != null) {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
- private void stopAgent() throws Exception {
+ private int stopAgent() throws Exception {
log.debug("Stopping RHQ agent");
File agentBinDir = new File(getAgentBasedir(), "bin");
@@ -182,19 +201,25 @@ public class Stop extends AbstractInstall {
executor.setStreamHandler(new PumpStreamHandler());
org.apache.commons.exec.CommandLine commandLine = getCommandLine("rhq-agent-wrapper", "stop");
+ int rValue;
+
if (isWindows()) {
try {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
} catch (Exception e) {
// Ignore, service may not exist or be running, , script returns 1
log.debug("Failed to stop agent service", e);
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
} else {
String pid = getAgentPid();
if (pid != null) {
- executor.execute(commandLine);
+ rValue = executor.execute(commandLine);
+ } else {
+ rValue = RHQControl.EXIT_CODE_OK;
}
}
+ return rValue;
}
}
diff --git a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
index 5a762cd..cc3d8c2 100644
--- a/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
+++ b/modules/enterprise/server/server-control/src/main/java/org/rhq/server/control/command/Upgrade.java
@@ -48,6 +48,7 @@ import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.file.FileVisitor;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.server.control.ControlCommand;
+import org.rhq.server.control.RHQControl;
import org.rhq.server.control.RHQControlException;
/**
@@ -125,7 +126,8 @@ public class Upgrade extends AbstractInstall {
}
@Override
- protected void exec(CommandLine commandLine) {
+ protected int exec(CommandLine commandLine) {
+ int rValue = RHQControl.EXIT_CODE_OK;
boolean start = commandLine.hasOption(START_OPTION);
try {
@@ -135,9 +137,13 @@ public class Upgrade extends AbstractInstall {
log.error(error);
}
log.error("Exiting due to the previous errors");
- return;
+ return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
+ // Attempt to shutdown any running components. A failure to shutdown a component is not a failure as it
+ // really shouldn't be running anyway. This is just an attempt to avoid upgrade problems.
+ log.info("Stopping any running RHQ components...");
+
// If using non-default agent location then save it so it will be applied to all subsequent rhqctl commands.
boolean hasFromAgentOption = commandLine.hasOption(FROM_AGENT_DIR_OPTION);
if (hasFromAgentOption) {
@@ -149,7 +155,7 @@ public class Upgrade extends AbstractInstall {
// if the agent already exists in the default location, it may be there from a prior install.
if (isStorageInstalled() || isServerInstalled()) {
log.warn("RHQ is already installed so upgrade can not be performed.");
- return;
+ return RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// Attempt to shutdown any running components. A failure to shutdown a component is not a failure as it
@@ -158,7 +164,7 @@ public class Upgrade extends AbstractInstall {
// Stop the agent, if running.
if (hasFromAgentOption) {
- killAgent(getFromAgentDir(commandLine)); // this validates the path as well
+ rValue = Math.max(rValue, killAgent(getFromAgentDir(commandLine))); // this validates the path as well
}
// If rhqctl exists in the old version, use it to stop old components, otherwise, just try and stop the
@@ -177,10 +183,10 @@ public class Upgrade extends AbstractInstall {
} else {
log.error("The old installation components failed to be stopped. Please stop them manually before continuing. exit code="
+ exitValue);
- return;
+ return exitValue;
}
- // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
+ // If any failures occur during upgrade, we know we need to reset rhq-server.properties.
final FileReverter serverPropFileReverter = new FileReverter(getServerPropertiesFile());
addUndoTask(new ControlCommand.UndoTask("Reverting server properties file") {
public void performUndoWork() throws Exception {
@@ -193,9 +199,9 @@ public class Upgrade extends AbstractInstall {
});
// now upgrade everything
- upgradeStorage(commandLine);
- upgradeServer(commandLine);
- upgradeAgent(commandLine);
+ rValue = Math.max(rValue, upgradeStorage(commandLine));
+ rValue = Math.max(rValue, upgradeServer(commandLine));
+ rValue = Math.max(rValue, upgradeAgent(commandLine));
File agentDir;
@@ -208,7 +214,7 @@ public class Upgrade extends AbstractInstall {
updateWindowsAgentService(agentDir);
if (start) {
- startAgent(agentDir);
+ rValue = Math.max(rValue, startAgent(agentDir));
}
} catch (Exception e) {
throw new RHQControlException("An error occurred while executing the upgrade command", e);
@@ -218,30 +224,33 @@ public class Upgrade extends AbstractInstall {
Stop stopCommand = new Stop();
stopCommand.exec(new String[] { "stop", "--server" });
if (!commandLine.hasOption(RUN_DATA_MIGRATION)) {
- stopCommand.exec(new String[] { "stop", "--storage" });
+ rValue = Math.max(rValue, stopCommand.exec(new String[] { "stop", "--storage" }));
}
}
} catch (Throwable t) {
log.warn("Unable to stop services: " + t.getMessage());
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
}
if (!isRhq48OrLater(commandLine) && commandLine.hasOption(RUN_DATA_MIGRATION)) {
- runDataMigration(commandLine);
+ rValue = Math.max(rValue, runDataMigration(commandLine));
}
-
+ return rValue;
}
- private void runDataMigration(CommandLine rhqctlCommandLine) {
+ private int runDataMigration(CommandLine rhqctlCommandLine) {
String migrationOption = rhqctlCommandLine.getOptionValue(RUN_DATA_MIGRATION);
+ int rValue;
+
if (migrationOption.equals("none")) {
log.info("No data migration will run");
if (!isRhq48OrLater(rhqctlCommandLine)) {
printDataMigrationNotice();
}
- return;
+ return RHQControl.EXIT_CODE_OK;
}
// We deduct the database parameters from the server properties
@@ -253,23 +262,28 @@ public class Upgrade extends AbstractInstall {
}
Executor executor = new DefaultExecutor();
- executor.setWorkingDirectory(new File(getBaseDir(), "bin")); // data migrator script is not in bin/internal
+ executor.setWorkingDirectory(getBinDir());
executor.setStreamHandler(new PumpStreamHandler());
int exitValue = executor.execute(commandLine);
log.info("The data migrator finished with exit value " + exitValue);
+ rValue = exitValue;
} catch (Exception e) {
log.error("Running the data migrator failed - please try to run it from the command line: "
+ e.getMessage());
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
+ return rValue;
}
- private void upgradeStorage(CommandLine rhqctlCommandLine) throws Exception {
+ private int upgradeStorage(CommandLine rhqctlCommandLine) throws Exception {
if (rhqctlCommandLine.hasOption(USE_REMOTE_STORAGE_NODE)) {
log.info("Ignoring storage node upgrade, a remote storage node is configured.");
- return;
+ return RHQControl.EXIT_CODE_OK;
}
+ int rValue;
+
// If upgrading from a pre-cassandra then just install an initial storage node. Otherwise, upgrade
if (isRhq48OrLater(rhqctlCommandLine)) {
try {
@@ -293,28 +307,30 @@ public class Upgrade extends AbstractInstall {
int exitCode = executor.execute(commandLine);
log.info("The storage node upgrade has finished with an exit value of " + exitCode);
-
+ rValue = exitCode;
} catch (IOException e) {
log.error("An error occurred while running the storage node upgrade: " + e.getMessage());
throw e;
}
} else {
- installStorageNode(getStorageBasedir(), rhqctlCommandLine, true);
+ rValue = installStorageNode(getStorageBasedir(), rhqctlCommandLine, true);
}
+ return rValue;
}
- private void upgradeServer(CommandLine commandLine) throws Exception {
+ private int upgradeServer(CommandLine commandLine) throws Exception {
// don't upgrade the server if this is a storage node only install
File oldServerDir = getFromServerDir(commandLine);
if (!(!isRhq48OrLater(commandLine) || isServerInstalled(oldServerDir))) {
log.info("Ignoring server upgrade, this is a storage node only installation.");
- return;
+ return RHQControl.EXIT_CODE_OK;
}
// copy all the old settings into the new rhq-server.properties file
upgradeServerPropertiesFile(commandLine);
+ int rValue = RHQControl.EXIT_CODE_OK;
// make sure we retain the oracle driver if one exists
try {
copyOracleDriver(oldServerDir);
@@ -323,6 +339,7 @@ public class Upgrade extends AbstractInstall {
+ "The upgrade will continue but your server may not work if connecting to an Oracle database, "
+ "in which case you will need to manually install an Oracle driver to your server. " + "Cause: "
+ ThrowableUtil.getAllMessages(e));
+ rValue = RHQControl.EXIT_CODE_OPERATION_FAILED;
}
// copy over any wrapper.inc that may have been added
@@ -334,10 +351,10 @@ public class Upgrade extends AbstractInstall {
// start the server, the invoke the installer and wait for the server to be completely installed
startRHQServerForInstallation();
- runRHQServerInstaller();
+ rValue = Math.max(rValue, runRHQServerInstaller());
waitForRHQServerToInitialize();
- return;
+ return rValue;
}
public void copyOracleDriver(File oldServerDir) throws IOException {
@@ -529,7 +546,7 @@ public class Upgrade extends AbstractInstall {
}
// now merge the old settings in with the default properties from the new server install
- String newServerPropsFilePath = new File(getBaseDir(), "bin/rhq-server.properties").getAbsolutePath();
+ String newServerPropsFilePath = new File(getBinDir(), "rhq-server.properties").getAbsolutePath();
PropertiesFileUpdate newServerPropsFile = new PropertiesFileUpdate(newServerPropsFilePath);
newServerPropsFile.update(oldServerProps);
@@ -606,7 +623,7 @@ public class Upgrade extends AbstractInstall {
return (null != path) ? path.replace('\\', '/') : null;
}
- private void upgradeAgent(CommandLine rhqctlCommandLine) throws Exception {
+ private int upgradeAgent(CommandLine rhqctlCommandLine) throws Exception {
try {
File oldAgentDir;
if (rhqctlCommandLine.hasOption(FROM_AGENT_DIR_OPTION)) {
@@ -628,8 +645,7 @@ public class Upgrade extends AbstractInstall {
+ " option specified and no agent found in the default location ["
+ oldAgentDir.getAbsolutePath()
+ "]. Installing agent in the default location as part of the upgrade.");
- installAgent(getAgentBasedir(), rhqctlCommandLine);
- return;
+ return installAgent(getAgentBasedir(), rhqctlCommandLine);
}
}
@@ -687,6 +703,7 @@ public class Upgrade extends AbstractInstall {
});
log.info("The agent has been upgraded and placed in: " + agentBasedir);
+ return exitValue;
} catch (IOException e) {
log.error("An error occurred while upgrading the agent: " + e.getMessage());
10 years, 4 months
[rhq] .classpath
by Thomas Segismont
.classpath | 1 +
1 file changed, 1 insertion(+)
New commits:
commit d415e017e410ab3cf15b0ca90c67e9482d214387
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Thu Jan 2 16:59:20 2014 +0100
Add BoneCP to Eclipse classpath file
diff --git a/.classpath b/.classpath
index f8048fc..f371634 100644
--- a/.classpath
+++ b/.classpath
@@ -399,5 +399,6 @@
<classpathentry kind="var" path="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.1.1/jackson-core-2.1.1.jar"/>
<classpathentry kind="var" path="M2_REPO/com/codahale/metrics/metrics-core/3.0.1/metrics-core-3.0.1.jar"/>
<classpathentry kind="var" path="M2_REPO/com/datastax/cassandra/cassandra-driver-core/1.0.2/cassandra-driver-core-1.0.2.jar"/>
+ <classpathentry kind="var" path="M2_REPO/com/jolbox/bonecp/0.8.0.RELEASE/bonecp-0.8.0.RELEASE.jar"/>
<classpathentry kind="output" path="eclipse-classes"/>
</classpath>
10 years, 4 months
[rhq] modules/plugins
by Thomas Segismont
modules/plugins/database/pom.xml | 64 +
modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java | 36
modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java | 226 +++++
modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java | 52 +
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java | 65 +
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java | 84 +-
modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java | 84 +-
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java | 14
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java | 7
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java | 379 ++++++++++
modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java | 16
modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java | 103 ++
modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java | 39 +
modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java | 19
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java | 82 +-
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java | 20
modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java | 59 +
modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java | 17
modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java | 88 +-
modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml | 94 +-
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java | 131 ++-
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java | 92 --
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java | 125 ---
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java | 182 ++--
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java | 79 --
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java | 117 +--
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java | 36
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java | 61 +
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java | 126 +--
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java | 72 -
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java | 103 +-
modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java | 62 -
modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java | 6
modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java | 9
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java | 147 +--
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java | 65 -
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java | 35
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java | 40 -
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java | 19
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java | 71 +
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java | 96 +-
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java | 40 -
modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java | 40 -
modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml | 8
modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java | 19
modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java | 28
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java | 312 ++++----
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java | 18
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java | 95 +-
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java | 7
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java | 62 +
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java | 258 +++---
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java | 98 +-
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java | 61 +
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java | 100 +-
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java | 28
modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java | 15
modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java | 10
58 files changed, 2971 insertions(+), 1450 deletions(-)
New commits:
commit 2b810f1c9fa247a9d8ddf08d2b9ba93c9e1cf2a6
Author: Thomas Segismont <tsegismo(a)redhat.com>
Date: Tue Dec 17 21:35:47 2013 +0100
Bug 968361 - Improve database plugin design to support connection pooling
This changeset introduces a new API for database plugins and deprecates the previous one. Compatibility with the previous API will be maintained until next major version of RHQ.
The 'rhq-database-plugin' was based on org.rhq.plugins.database.DatabaseComponent interface which encouraged plugin authors to share a single JDBC connection across database components. This was wrong for various reasons (connection leaks, concurrent JDBC calls... etc).
The new API introduces three important classes:
* org.rhq.plugins.database.PooledConnectionProvider
* org.rhq.plugins.database.BasePooledConnectionProvider
* org.rhq.plugins.database.ConnectionPoolingSupport
BasePooledConnectionProvider is a base implementation of a PooledConnectionProvider. Plugin authors should create a concrete implementation of BasePooledConnectionProvider which overrides the #getDriverClass() method. This is important if a database plugin embeds a JDBC driver: the database-specific driver class must be loaded by the child plugin classloader.
ConnectionPoolingSupport helps to manage the compatibility with the old API. It's a contract that all new database resource components should obey to. It declares the following methods:
* #supportsConnectionPooling()
* #getPooledConnectionProvider()
Results of calls to #supportsConnectionPooling() #getPooledConnectionProvider() must be consistent. In practice, a top level server database component should be able to create a PooledConnectionProvider instance, and child servers and services should indicate they support connection pooling only if their parent component does.
The 'rhq-database-plugin' embeds the BoneCP library (JDBC connection pooling) and its dependencies (Google's Guava). Child plugins will have all the classes accessible as soon as they have this node in their plugin descriptor:
===
<depends plugin="Database" useClasses="true"/>
===
This changeset includes the necessary changes to support connection pooling in the Oracle, Postgres and MySQL plugins.
Thanks to Elias Ross for contributing the original patch from which this changeset is derived.
diff --git a/modules/plugins/database/pom.xml b/modules/plugins/database/pom.xml
index 7ede2af..952e919 100644
--- a/modules/plugins/database/pom.xml
+++ b/modules/plugins/database/pom.xml
@@ -17,9 +17,33 @@
<properties>
<rhq.internal>false</rhq.internal>
+ <slf4j.version>1.7.2</slf4j.version>
</properties>
<dependencies>
+
+ <!-- Bone CP and its dependencies -->
+ <dependency>
+ <groupId>com.jolbox</groupId>
+ <artifactId>bonecp</artifactId>
+ <version>0.8.0.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>15.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+ <!-- Test dependencies -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
@@ -28,7 +52,45 @@
</dependency>
</dependencies>
- <profiles>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-dependency-jars</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>com.jolbox</groupId>
+ <artifactId>bonecp</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.build.outputDirectory}/lib</outputDirectory>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
<profile>
<id>dev</id>
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
index 5976695..a0fdf07 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/AbstractDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,31 +13,49 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
/**
- * Base class for database components.
+ * Base class for nested database components.
* @author Greg Hinkle
*/
-public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>> implements DatabaseComponent {
+public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>> implements DatabaseComponent,
+ ConnectionPoolingSupport {
+
+ private PooledConnectionProvider pooledConnectionProvider;
protected ResourceContext<T> resourceContext;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
+ if (hasConnectionPoolingSupport(resourceContext.getParentResourceComponent())) {
+ pooledConnectionProvider = ((ConnectionPoolingSupport) resourceContext.getParentResourceComponent())
+ .getPooledConnectionProvider();
+ }
}
- /*public void start(ResourceContext<T> resourceContext) throws InvalidPluginConfigurationException, Exception
- * { this.resourceContext = resourceContext;}*/
-
public void stop() {
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return pooledConnectionProvider != null;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public Connection getConnection() {
@@ -52,4 +70,4 @@ public abstract class AbstractDatabaseComponent<T extends DatabaseComponent<?>>
public String toString() {
return getClass().getName() + " key=" + resourceContext.getResourceKey();
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java
new file mode 100644
index 0000000..c9b3a59
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/BasePooledConnectionProvider.java
@@ -0,0 +1,226 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import javax.sql.DataSource;
+
+import com.jolbox.bonecp.BoneCP;
+import com.jolbox.bonecp.BoneCPConfig;
+import com.jolbox.bonecp.hooks.AbstractConnectionHook;
+import com.jolbox.bonecp.hooks.AcquireFailConfig;
+import com.jolbox.bonecp.hooks.ConnectionHook;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.configuration.Configuration;
+
+/**
+ * Base implementation of a {@link PooledConnectionProvider}. Plugin authors <em>should</em>:
+ * <ol>
+ * <li>create a concrete implementation which overrides the {@link #getDriverClass()} method</li>
+ * <li>adopt the configuration properties described here or override the corresponding #get method</li>
+ * </ol>
+ *
+ * The first point is important if a concrete database plugin embeds the JDBC driver: the database-specific driver class
+ * <strong>must</strong> be loaded by the child plugin classloader.
+ *
+ * @author Elias Ross
+ */
+public abstract class BasePooledConnectionProvider implements PooledConnectionProvider {
+ private static final Log LOG = LogFactory.getLog(BasePooledConnectionProvider.class);
+
+ /**
+ * Driver class key.
+ */
+ public static final String DRIVER_CLASS = "driverClass";
+
+ /**
+ * JDBC URL config key.
+ */
+ public static final String URL = "url";
+
+ /**
+ * JDBC username config key.
+ */
+ public static final String USERNAME = "username";
+
+ /**
+ * JDBC password config key.
+ */
+ public static final String PASSWORD = "password";
+
+ /**
+ * If true, track connections and statements.
+ */
+ public static final String TRACK = "track";
+
+ /**
+ * Connection timeout setting.
+ */
+ public static final String TIMEOUT = "connectionTimeout";
+
+ protected final Configuration pluginConfig;
+
+ /**
+ * Connection pool.
+ * The connection pool implementation details should not be exposed as
+ * the implementation may change.
+ */
+ private final BoneCP connectionPool;
+
+ protected BasePooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ this.pluginConfig = pluginConfig;
+ BoneCPConfig bconfig = new BoneCPConfig(getConnectionProperties());
+ Class<Driver> driverClass;
+ try {
+ driverClass = getDriverClass();
+ } catch (ClassNotFoundException e) {
+ LOG.warn("Could not load driver class from: " + Thread.currentThread().getContextClassLoader());
+ throw e;
+ }
+ if (driverClass != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using driver class " + driverClass);
+ }
+ Driver driver = driverClass.newInstance();
+ DataSource datasourceBean = new DriverDataSource(driver, getJdbcUrl(), getConnectionProperties());
+ bconfig.setDatasourceBean(datasourceBean);
+ } else {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Using driver manager for " + getJdbcUrl());
+ }
+ bconfig.setJdbcUrl(getJdbcUrl());
+ }
+ bconfig.setAcquireIncrement(1);
+ bconfig.setAcquireRetryAttempts(0);
+ bconfig.setAcquireRetryDelayInMs(0);
+ bconfig.setPartitionCount(2);
+ bconfig.setMaxConnectionsPerPartition(5);
+ bconfig.setPassword(getPassword());
+ bconfig.setUsername(getUsername());
+ bconfig.setConnectionTimeoutInMs(getConnectionTimeout());
+ if (isTrack()) {
+ bconfig.setCloseConnectionWatch(true);
+ bconfig.setCloseConnectionWatchTimeout(10, TimeUnit.MINUTES);
+ }
+ bconfig.setLazyInit(false);
+ bconfig.setDisableJMX(true);
+ // Do not manage retry
+ ConnectionHook hook = new AbstractConnectionHook() {
+ public boolean onAcquireFail(Throwable t, AcquireFailConfig acquireConfig) {
+ LOG.error("Failed to obtain connection", t);
+ return false;
+ }
+ };
+ bconfig.setConnectionHook(hook);
+ connectionPool = new BoneCP(bconfig);
+ }
+
+ private boolean isTrack() {
+ return Boolean.valueOf(pluginConfig.getSimpleValue(TRACK, null));
+ }
+
+ @Override
+ public Connection getPooledConnection() throws SQLException {
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("get connection for " + getJdbcUrl() + " free " + connectionPool.getTotalFree() + "/"
+ + connectionPool.getTotalCreatedConnections());
+ }
+ return connectionPool.getConnection();
+ }
+
+ /**
+ * Return additional database connection pool properties.
+ * By default, returns an empty properties object.
+ */
+ protected Properties getConnectionProperties() {
+ return new Properties();
+ }
+
+ /**
+ * Returns the driver class, by default the name from {@link #getDriverClassName()}.
+ * If not configured, this assumes the plugin will load the appropriate driver.
+ * @throws ClassNotFoundException if the classloader could not load this class
+ */
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ String cname = getDriverClassName();
+ if (cname != null) {
+ return (Class<Driver>) Thread.currentThread().getContextClassLoader().loadClass(cname);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the driver class, by default configuration item {@link #DRIVER_CLASS}.
+ */
+ protected String getDriverClassName() throws ClassNotFoundException {
+ return pluginConfig.getSimpleValue(DRIVER_CLASS, null);
+ }
+
+ /**
+ * Implemented by subclasses to return the JDBC connection URL.
+ * By default, returns the configuration item {@link #URL}.
+ */
+ protected String getJdbcUrl() {
+ return pluginConfig.getSimpleValue(URL);
+ }
+
+ /**
+ * Return the JDBC username.
+ * By default, returns the configuration item {@link #USERNAME}.
+ */
+ protected String getUsername() {
+ return pluginConfig.getSimpleValue(USERNAME);
+ }
+
+ /**
+ * Return the JDBC password.
+ * By default, returns the configuration item {@link #PASSWORD}.
+ */
+ protected String getPassword() {
+ return pluginConfig.getSimpleValue(PASSWORD);
+ }
+
+ /**
+ * Return the connection timeout setting, in milliseconds.
+ * By default, returns the configuration item {@link #TIMEOUT}.
+ */
+ protected long getConnectionTimeout() {
+ String s = pluginConfig.getSimpleValue(TIMEOUT);
+ if (s == null) {
+ return 1000 * 10;
+ }
+ return Long.parseLong(s);
+ }
+
+ /**
+ * Shutdown the connection pool.
+ */
+ public void close() {
+ connectionPool.shutdown();
+ }
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java
new file mode 100644
index 0000000..a899e44
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/ConnectionPoolingSupport.java
@@ -0,0 +1,52 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+/**
+ * <p>
+ * A contract that all database related resource components should obey to.
+ * </p>
+ * <p>
+ * Results of calls to {@link #supportsConnectionPooling()} and {@link #getPooledConnectionProvider()}
+ * <strong>MUST</strong> be consistent.
+ * </p>
+ * <p>
+ * In practice, a top level server database component should be able to create a {@link PooledConnectionProvider}
+ * instance, and child servers and services should indicate they support connection pooling only if their parent
+ * component does.
+ * </p>
+ *
+ * @author Thomas Segismont
+ */
+public interface ConnectionPoolingSupport {
+
+ /**
+ * @return true if this component can give a reference to a {@link PooledConnectionProvider}, false otherwise.
+ */
+ boolean supportsConnectionPooling();
+
+ /**
+ * @return a reference to a {@link PooledConnectionProvider} or null if this component does not support connection
+ * pooling.
+ * @see #supportsConnectionPooling()
+ */
+ PooledConnectionProvider getPooledConnectionProvider();
+
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
index f2e8fa7..90f3f2f 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,12 +13,19 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValueMap;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
@@ -36,24 +43,40 @@ import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* The component for the {@link CustomTableDiscoveryComponent}.
*
* @author Greg Hinkle
*/
-public class CustomTableComponent implements DatabaseComponent<DatabaseComponent<?>>, MeasurementFacet {
- private ResourceContext<DatabaseComponent<?>> context;
+public class CustomTableComponent implements DatabaseComponent<DatabaseComponent<?>>, ConnectionPoolingSupport,
+ MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(CustomTableComponent.class);
- private static final Log log = LogFactory.getLog(CustomTableComponent.class);
+ private ResourceContext<DatabaseComponent<?>> context;
+ private PooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext<DatabaseComponent<?>> resourceContext)
throws InvalidPluginConfigurationException, Exception {
this.context = resourceContext;
+ if (hasConnectionPoolingSupport(context.getParentResourceComponent())) {
+ pooledConnectionProvider = ((ConnectionPoolingSupport) context.getParentResourceComponent())
+ .getPooledConnectionProvider();
+ }
}
public void stop() {
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return pooledConnectionProvider != null;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public String getTable() {
@@ -66,16 +89,22 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
return AvailabilityType.UP;
}
Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
- statement = getConnection().createStatement();
+ connection = getConnectionFromComponent(this);
+ statement = connection.createStatement();
statement.setMaxRows(1);
statement.setFetchSize(1);
- statement.executeQuery("SELECT * FROM " + getTable()).close();
+ resultSet = statement.executeQuery("SELECT * FROM " + getTable());
return AvailabilityType.UP;
} catch (SQLException e) {
return AvailabilityType.DOWN;
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
}
@@ -85,10 +114,10 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
String query = config.getSimpleValue("metricQuery", null);
if (query == null) {
- if (log.isTraceEnabled()) {
+ if (LOG.isTraceEnabled()) {
ResourceType type = this.context.getResourceType();
String resourceKey = this.context.getResourceKey();
- log.trace("Resource "
+ LOG.trace("Resource "
+ resourceKey
+ " ("
+ type.getName()
@@ -104,13 +133,13 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
Map<String, Double> values;
if (Boolean.parseBoolean(column)) {
// data in column format
- values = DatabaseQueryUtility.getNumericQueryValues(this, query);
+ values = getNumericQueryValues(this, query);
} else {
// data in row format
- values = DatabaseQueryUtility.getNumericQueryValueMap(this, query);
+ values = getNumericQueryValueMap(this, query);
}
- if (log.isDebugEnabled())
- log.debug("returned values " + values);
+ if (LOG.isDebugEnabled())
+ LOG.debug("returned values " + values);
// this is a for loop because the name of each column can be the name of the metric
for (MeasurementScheduleRequest request : metrics) {
@@ -129,7 +158,9 @@ public class CustomTableComponent implements DatabaseComponent<DatabaseComponent
if (value != null) {
report.addData(new MeasurementDataNumeric(request, value));
} else {
- log.debug("Missing column in query results - metric not collected: " + columnName);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Missing column in query results - metric not collected: " + columnName);
+ }
}
}
}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
index aeab429..b3aca8d 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,12 +13,18 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.canProvideConnection;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
+import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
@@ -26,14 +32,15 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ManualAddFacet;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Discovery for a generic component that can read data out of a table for
@@ -46,62 +53,85 @@ import org.rhq.core.util.jdbc.JDBCUtil;
*
* @author Greg Hinkle
*/
-public class CustomTableDiscoveryComponent implements ManualAddFacet<DatabaseComponent<?>>,
- ResourceDiscoveryComponent<DatabaseComponent<?>> {
+public class CustomTableDiscoveryComponent implements ManualAddFacet<ResourceComponent<?>>,
+ ResourceDiscoveryComponent<ResourceComponent<?>> {
protected Log log = LogFactory.getLog(getClass());
+ @Override
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
- throws InvalidPluginConfigurationException, Exception {
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException,
+ Exception {
- Configuration config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ ResourceComponent<?> parentComponent = discoveryContext.getParentResourceComponent();
+
+ Configuration config = discoveryContext.getDefaultPluginConfiguration();
String table = config.getSimpleValue("table", "");
- ResourceType rt = resourceDiscoveryContext.getResourceType();
- String resourceName = config.getSimpleValue("name", rt.getName());
- String resourceDescription = config.getSimpleValue("description", rt.getDescription());
+ ResourceType resourceType = discoveryContext.getResourceType();
+ String resourceName = config.getSimpleValue("name", resourceType.getName());
+ String resourceDescription = config.getSimpleValue("description", resourceType.getDescription());
+
+ if (!canProvideConnection(parentComponent)) {
+ if (log.isDebugEnabled()) {
+ log.debug("Parent component does not provide JDBC connections, cannot discover" + resourceName);
+ }
+ return Collections.emptySet();
+ }
if (table.isEmpty()) {
- log.debug("'table' value not set, cannot discover " + resourceName);
+ if (log.isDebugEnabled()) {
+ log.debug("'table' value not set, cannot discover " + resourceName);
+ }
return Collections.emptySet();
}
Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
- if (conn == null)
+ connection = getConnectionFromComponent(parentComponent);
+ if (connection == null) {
throw new InvalidPluginConfigurationException("cannot obtain connection from parent");
-
- statement = conn.createStatement();
+ }
+ statement = connection.createStatement();
statement.setMaxRows(1);
statement.setFetchSize(1);
// This is more efficient than 'count(*)'
// unless the JDBC driver fails to support setMaxRows or doesn't stream results
- statement.executeQuery("SELECT * FROM " + table).close();
- DiscoveredResourceDetails details = new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(), table + resourceName, resourceName, null,
- resourceDescription, config, null);
+ resultSet = statement.executeQuery("SELECT * FROM " + table);
- log.debug("discovered " + details);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(), table
+ + resourceName, resourceName, null, resourceDescription, config, null);
+
+ if (log.isDebugEnabled()) {
+ log.debug("discovered " + details);
+ }
return Collections.singleton(details);
+
} catch (SQLException e) {
- log.debug("discovery failed " + e + " for " + table);
+ if (log.isDebugEnabled()) {
+ log.debug("discovery failed " + e + " for " + table);
+ }
// table not found, don't inventory
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
return Collections.emptySet();
}
+ @Override
public DiscoveredResourceDetails discoverResource(Configuration pluginConfiguration,
- ResourceDiscoveryContext<DatabaseComponent<?>> discoveryContext) throws InvalidPluginConfigurationException {
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException {
Configuration config = pluginConfiguration;
String table = config.getSimpleValue("table", null);
String resourceName = config.getSimpleValue("name", table);
- String resourceDescription = config.getSimpleValue("description",
- discoveryContext.getResourceType().getDescription());
+ String resourceDescription = config.getSimpleValue("description", discoveryContext.getResourceType()
+ .getDescription());
String resourceVersion = config.getSimpleValue("version", null);
DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(), table
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
index 50da33b..bde426a 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/CustomTableRowDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.canProvideConnection;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -29,13 +34,15 @@ import java.util.regex.Matcher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Discovery for a generic component that can read data out of a table for
@@ -47,57 +54,70 @@ import org.rhq.core.util.jdbc.JDBCUtil;
*
* @author Greg Hinkle
*/
-public class CustomTableRowDiscoveryComponent implements ResourceDiscoveryComponent<DatabaseComponent<?>> {
-
- private final Log log = LogFactory.getLog(getClass());
+public class CustomTableRowDiscoveryComponent implements ResourceDiscoveryComponent<ResourceComponent<?>> {
+ private static final Log LOG = LogFactory.getLog(CustomTableRowDiscoveryComponent.class);
+ @Override
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
- throws InvalidPluginConfigurationException, Exception {
- Statement statement = null;
- ResultSet resultSet = null;
+ ResourceDiscoveryContext<ResourceComponent<?>> discoveryContext) throws InvalidPluginConfigurationException,
+ Exception {
+
+ ResourceComponent<?> parentComponent = discoveryContext.getParentResourceComponent();
- Configuration config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ Configuration config = discoveryContext.getDefaultPluginConfiguration();
String table = config.getSimpleValue("table", null);
String keyColumn = config.getSimpleValue("keyColumn", null);
- try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
+ ResourceType resourceType = discoveryContext.getResourceType();
+ String resourceName = config.getSimpleValue("name", resourceType.getName());
+ String resourceDescription = config.getSimpleValue("description", "");
- String resourceName = config.getSimpleValue("name", null);
- String resourceDescription = config.getSimpleValue("description", "");
+ if (!canProvideConnection(parentComponent)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Parent component does not provide JDBC connections, cannot discover" + resourceName);
+ }
+ return Collections.emptySet();
+ }
- if (resourceName == null) {
- throw new InvalidPluginConfigurationException("The 'name' connection property has to be specified.");
+ if (resourceName == null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("'name' property not set, cannot discover" + resourceName);
}
+ return Collections.emptySet();
+ }
- statement = conn.createStatement();
+ Statement statement = null;
+ Connection connection = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnectionFromComponent(parentComponent);
+ if (connection == null) {
+ throw new InvalidPluginConfigurationException("cannot obtain connection from parent");
+ }
+ statement = connection.createStatement();
resultSet = statement.executeQuery("SELECT * FROM " + table);
Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>();
while (resultSet.next()) {
- config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ config = discoveryContext.getDefaultPluginConfiguration();
String key = resultSet.getString(keyColumn);
config.put(new PropertySimple("key", key));
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(),
- key,
- formatMessage(resourceName, key),
- null,
- formatMessage(resourceDescription,
- key), config, null);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(discoveryContext.getResourceType(),
+ key, formatMessage(resourceName, key), null, formatMessage(resourceDescription, key), config, null);
found.add(details);
}
return found;
+
} catch (SQLException e) {
- log.debug("table " + table + " column " + keyColumn, e);
+ LOG.debug("table " + table + " column " + keyColumn, e);
} finally {
- JDBCUtil.safeClose(resultSet);
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
+ }
}
- return Collections.emptySet();
+ return Collections.emptySet();
}
/**
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
index 5b8ad5f..3878c9d 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Connection;
@@ -24,9 +25,14 @@ import org.rhq.core.pluginapi.inventory.ResourceComponent;
/**
* @author Greg Hinkle
+ * @deprecated as of RHQ 4.10. Use {@link ConnectionPoolingSupport} instead.
*/
+@Deprecated
public interface DatabaseComponent<T extends ResourceComponent<?>> extends ResourceComponent<T> {
+
+ @Deprecated
Connection getConnection();
+ @Deprecated
void removeConnection();
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
index 38cd949..0a1fdb8 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Driver;
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java
new file mode 100644
index 0000000..f17c9b4
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabasePluginUtil.java
@@ -0,0 +1,379 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+import org.rhq.core.util.exception.ThrowableUtil;
+
+/**
+ * @author Thomas Segismont
+ */
+public class DatabasePluginUtil {
+ private static final Log LOG = LogFactory.getLog(DatabasePluginUtil.class);
+
+ public static final class ComponentCannotProvideConnectionException extends IllegalArgumentException {
+ private final ResourceComponent component;
+
+ public ComponentCannotProvideConnectionException(ResourceComponent component) {
+ super(component + " cannot provide a JDBC Connection");
+ this.component = component;
+ }
+
+ public ResourceComponent getComponent() {
+ return component;
+ }
+ }
+
+ /**
+ * @return false unless <code>component</code> is not null and supports connection pooling or is an instance of
+ * {@link DatabaseComponent}.
+ */
+ public static boolean canProvideConnection(ResourceComponent component) {
+ return hasConnectionPoolingSupport(component) || component instanceof DatabaseComponent;
+ }
+
+ /**
+ * Determines if a resource component supports connection pooling.
+ *
+ * @param component the resource component
+ * @return false unless <code>component</code> is not null, implements
+ * {@link org.rhq.plugins.database.ConnectionPoolingSupport} and a call to
+ * {@link ConnectionPoolingSupport#supportsConnectionPooling()} returns true.
+ */
+ public static boolean hasConnectionPoolingSupport(ResourceComponent component) {
+ if (component instanceof ConnectionPoolingSupport) {
+ return ((ConnectionPoolingSupport) component).supportsConnectionPooling();
+ }
+ return false;
+ }
+
+ /**
+ * Gets a {@link Connection} from the <code>component</code>.
+ *
+ * @param component a resource component that must be able to provide a {@link Connection}.
+ * @throws SQLException if <code>component</code> supports connection pooling and a pooled connection could not be
+ * retrieved.
+ * @throws IllegalArgumentException if resource component is null or cannot provide a {@link Connection} (does not
+ * support connection pooling and does not implement {@link DatabaseComponent}).
+ */
+ public static Connection getConnectionFromComponent(ResourceComponent component) throws SQLException {
+ if (hasConnectionPoolingSupport(component)) {
+ return getConnectionFromPool((ConnectionPoolingSupport) component);
+ }
+ if (component instanceof DatabaseComponent) {
+ return getConnectionFromDatabaseComponent((DatabaseComponent) component);
+ }
+ throw new ComponentCannotProvideConnectionException(component);
+ }
+
+ /**
+ * Executes a query, returning the results as a map where the keys are the column names and values are the value of
+ * that column. Note that depending on the database, the column names may be uppercase (Oracle) or lowercase.
+ *
+ * @param component
+ * @param query SQL query string
+ * @param parameters optional bind parameters
+ *
+ * @return a map of query results
+ */
+ public static Map<String, Double> getNumericQueryValues(ResourceComponent component, String query,
+ Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ Map<String, Double> row = new HashMap<String, Double>();
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ String[] names = getColumns(md);
+
+ if (resultSet.next()) {
+ for (String name : names) {
+ try {
+ row.put(name, resultSet.getDouble(name));
+ } catch (SQLException e) {
+ // Ignore columns that can't be read as doubles
+ }
+ }
+ }
+
+ return row;
+ } catch (SQLException e) {
+ LOG.debug("Unable to read value", e);
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns a mapping of rows as key-value pairs where the key is the first column (a string) and the second column
+ * is a value numeric.
+ *
+ * @param component the component to execute on
+ * @param query the sql query to run
+ * @param parameters any parameters to bind first
+ *
+ * @return a Map<String,Double> of the keys against the value
+ */
+ public static Map<String, Double> getNumericQueryValueMap(ResourceComponent component, String query,
+ Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ Map<String, Double> map = new HashMap<String, Double>();
+
+ while (resultSet.next()) {
+ try {
+ map.put(resultSet.getString(1), resultSet.getDouble(2));
+ } catch (SQLException e) {
+ // Ignore columns that can't be read as doubles
+ if (LOG.isTraceEnabled()) {
+ LOG.trace("A query column value is not a double, ignoring:" + ThrowableUtil.getAllMessages(e));
+ }
+ }
+ }
+
+ return map;
+ } catch (SQLException e) {
+ LOG.info("Unable to read value", e);
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Collections.emptyMap();
+ }
+
+ /**
+ * Returns the result of a query as a single Double value.
+ * Returns {@link Double#NaN} if the query fails.
+ */
+ public static Double getSingleNumericQueryValue(ResourceComponent component, String query, Object... parameters) {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+ resultSet = statement.executeQuery();
+
+ if (resultSet.next()) {
+ return resultSet.getDouble(1);
+ }
+ } catch (SQLException e) {
+ if (!componentHasConnectionPoolingSupport) {
+ ((DatabaseComponent) component).removeConnection();
+ }
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+
+ return Double.NaN;
+ }
+
+ /**
+ * Returns a list of values, one per row, containing a map of column names to values of that row.
+ * Note depending on the database, the column names may be uppercase (Oracle) or lowercase.
+ * @param component database to query
+ * @param query SQL query
+ * @param parameters parameters to bind to the query
+ *
+ * @throws SQLException if query fails
+ */
+ public static List<Map<String, Object>> getGridValues(ResourceComponent component, String query,
+ Object... parameters) throws SQLException {
+
+ boolean componentHasConnectionPoolingSupport = hasConnectionPoolingSupport(component);
+ checkComponent(component, componentHasConnectionPoolingSupport);
+
+ Connection connection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ List<Map<String, Object>> l = new ArrayList<Map<String, Object>>();
+ try {
+ connection = getConnection0(component, componentHasConnectionPoolingSupport);
+ statement = connection.prepareStatement(query);
+ bindParameters(statement, parameters);
+
+ resultSet = statement.executeQuery();
+
+ while (resultSet.next()) {
+ Map<String, Object> row = new HashMap<String, Object>();
+ l.add(row);
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ String[] names = getColumns(md);
+
+ for (String name : names) {
+ Object o = resultSet.getObject(name);
+ row.put(name, o);
+ }
+ }
+
+ } finally {
+ safeClose(null, statement, resultSet);
+ if (componentHasConnectionPoolingSupport) {
+ safeClose(connection);
+ }
+ }
+ return l;
+
+ }
+
+ public static void safeClose(Connection connection) {
+ if (connection != null) {
+ try {
+ connection.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ public static void safeClose(Connection connection, Statement statement) {
+ safeClose(statement);
+ safeClose(connection);
+ }
+
+ public static void safeClose(Connection connection, Statement statement, ResultSet resultSet) {
+ safeClose(resultSet);
+ safeClose(statement);
+ safeClose(connection);
+ }
+
+ public static void safeClose(Statement statement) {
+ if (statement != null) {
+ try {
+ statement.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ public static void safeClose(ResultSet resultSet) {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (Exception ignore) {
+ }
+ }
+ }
+
+ private static void checkComponent(ResourceComponent component, boolean componentHasConnectionPoolingSupport) {
+ if (!componentHasConnectionPoolingSupport && !(component instanceof DatabaseComponent)) {
+ throw new ComponentCannotProvideConnectionException(component);
+ }
+ }
+
+ private static Connection getConnection0(ResourceComponent component, boolean componentHasConnectionPoolingSupport)
+ throws SQLException {
+ return componentHasConnectionPoolingSupport ? getConnectionFromPool((ConnectionPoolingSupport) component)
+ : getConnectionFromDatabaseComponent((DatabaseComponent) component);
+ }
+
+ private static Connection getConnectionFromPool(ConnectionPoolingSupport component) throws SQLException {
+ return component.getPooledConnectionProvider().getPooledConnection();
+ }
+
+ private static Connection getConnectionFromDatabaseComponent(DatabaseComponent component) {
+ return component.getConnection();
+ }
+
+ private static void bindParameters(PreparedStatement statement, Object... parameters) throws SQLException {
+ int i = 1;
+ for (Object p : parameters) {
+ if (p instanceof String) {
+ statement.setString(i++, (String) p);
+ } else if (p instanceof Number) {
+ statement.setDouble(i++, ((Number) p).doubleValue());
+ } else {
+ statement.setObject(i++, p);
+ }
+ }
+ }
+
+ private static String[] getColumns(ResultSetMetaData rsmd) throws SQLException {
+ String[] names = new String[rsmd.getColumnCount()];
+ for (int i = 0; i < rsmd.getColumnCount(); i++) {
+ names[i] = rsmd.getColumnName(i + 1);
+ }
+ return names;
+ }
+
+ private DatabasePluginUtil() {
+ // Utility class
+ }
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
index d6d83f2..70024c0 100644
--- a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DatabaseQueryUtility.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.database;
import java.sql.Connection;
@@ -39,7 +40,9 @@ import org.rhq.core.util.exception.ThrowableUtil;
* Various database (JDBC) query functions.
*
* @author Greg Hinkle
+ * @deprecated as of RHQ 4.10, use {@link DatabasePluginUtil} instead.
*/
+@Deprecated
public class DatabaseQueryUtility {
private static final Log LOG = LogFactory.getLog(DatabaseQueryUtility.class);
@@ -59,6 +62,7 @@ public class DatabaseQueryUtility {
* @return
* @throws SQLException
*/
+ @Deprecated
public static int executeUpdate(DatabaseComponent databaseComponent, String query, Object... parameters)
throws SQLException {
PreparedStatement statement = null;
@@ -79,6 +83,7 @@ public class DatabaseQueryUtility {
* Returns the result of a query as a single Double value.
* Returns {@link Double#NaN} if the query fails.
*/
+ @Deprecated
public static Double getSingleNumericQueryValue(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -111,6 +116,7 @@ public class DatabaseQueryUtility {
*
* @return a map of query results
*/
+ @Deprecated
public static Map<String, Double> getNumericQueryValues(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -156,6 +162,7 @@ public class DatabaseQueryUtility {
*
* @throws SQLException if query fails
*/
+ @Deprecated
public static List<Map<String, Object>> getGridValues(DatabaseComponent databaseComponent, String query,
Object... parameters) throws SQLException {
PreparedStatement statement = null;
@@ -197,6 +204,7 @@ public class DatabaseQueryUtility {
*
* @return a Map<String,Double> of the keys against the value
*/
+ @Deprecated
public static Map<String, Double> getNumericQueryValueMap(DatabaseComponent databaseComponent, String query,
Object... parameters) {
PreparedStatement statement = null;
@@ -251,6 +259,7 @@ public class DatabaseQueryUtility {
/**
* Returns an array of strings as upper-case column names.
*/
+ @Deprecated
public static String[] getColumns(ResultSetMetaData rsmd) throws SQLException {
String[] names = new String[rsmd.getColumnCount()];
for (int i = 0; i < rsmd.getColumnCount(); i++) {
@@ -263,6 +272,7 @@ public class DatabaseQueryUtility {
/**
* Closes statements and result sets.
*/
+ @Deprecated
public static void close(Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java
new file mode 100644
index 0000000..4fce379
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/DriverDataSource.java
@@ -0,0 +1,103 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.io.PrintWriter;
+import java.sql.Connection;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+import javax.sql.DataSource;
+
+/**
+ * Works with BoneCP to provide connections, because DriverManager won't load the class correctly through BoneCP
+ * (the actual driver class will be loaded by concrete database plugins).
+ */
+class DriverDataSource implements DataSource {
+
+ private final Properties properties;
+ private final Driver driver;
+ private final String url;
+ private PrintWriter out = new PrintWriter(System.out);
+
+ DriverDataSource(Driver driver, String url, Properties properties) {
+ this.driver = driver;
+ this.url = url;
+ if (properties == null) {
+ properties = new Properties();
+ }
+ this.properties = properties;
+ }
+
+ @Override
+ public PrintWriter getLogWriter() throws SQLException {
+ return out;
+ }
+
+ @Override
+ public void setLogWriter(PrintWriter out) throws SQLException {
+ this.out = out;
+ }
+
+ @Override
+ public void setLoginTimeout(int seconds) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public int getLoginTimeout() throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public <T> T unwrap(Class<T> iface) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public boolean isWrapperFor(Class<?> iface) throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+ @Override
+ public Connection getConnection() throws SQLException {
+ return driver.connect(url, properties);
+ }
+
+ @Override
+ public Connection getConnection(String username, String password) throws SQLException {
+ Properties p = new Properties(properties);
+ if (username != null) {
+ p.put("user", username);
+ }
+ if (password != null) {
+ p.put("password", password);
+ }
+ return driver.connect(url, p);
+ }
+
+ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException();
+ }
+
+}
diff --git a/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java b/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java
new file mode 100644
index 0000000..8089152
--- /dev/null
+++ b/modules/plugins/database/src/main/java/org/rhq/plugins/database/PooledConnectionProvider.java
@@ -0,0 +1,39 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Contract for resource components providing pooled {@link Connection} objects.
+ *
+ * @author Thomas Segismont
+ */
+public interface PooledConnectionProvider {
+ /**
+ * Get a pooled connection. It's the responsibility of the caller to return the connection to the pool by calling
+ * {@link java.sql.Connection#close()} on the returned {@link Connection} object.
+ *
+ * @return a pooled {@link Connection}
+ * @throws SQLException if a pooled connection could not be retrieved
+ */
+ Connection getPooledConnection() throws SQLException;
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
index 6caba36..8e869e4 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/ComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.database;
import static org.testng.AssertJUnit.assertEquals;
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
index a81d998..2d388b6 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2Database.java
@@ -1,66 +1,97 @@
package org.rhq.plugins.database;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
/**
* Tests using the H2Database.
*/
-public class H2Database implements DatabaseComponent<ResourceComponent<?>> {
+public class H2Database implements DatabaseComponent<ResourceComponent<?>>, ConnectionPoolingSupport {
+ private static final Log LOG = LogFactory.getLog(H2Database.class);
+
+ static final String DRIVER_CLASS_PROPERTY = "driverClass";
+ static final String URL_PROPERTY = "url";
+ static final String USERNAME_PROPERTY = "username";
+ static final String PASSWORD_PROPERTY = "password";
- private Log log = LogFactory.getLog(this.getClass());
protected ResourceContext resourceContext;
+ @Deprecated
private Connection connection;
- private Configuration configuration;
+ private H2PooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- this.configuration = resourceContext.getPluginConfiguration();
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new H2PooledConnectionProvider(resourceContext.getPluginConfiguration());
}
public void stop() {
- removeConnection();
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(connection);
+ connection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
public AvailabilityType getAvailability() {
- Connection conn = getConnection();
- AvailabilityType result = AvailabilityType.DOWN;
- if (conn != null) {
- result = AvailabilityType.UP;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
- return result;
-
}
public Connection getConnection() {
+ buildSharedConnectionIfNeeded();
+ return connection;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
try {
- if (this.connection == null || connection.isClosed()) {
- this.connection = buildConnection();
+ if ((connection == null) || connection.isClosed()) {
+ connection = buildConnection(resourceContext.getPluginConfiguration());
}
} catch (SQLException e) {
- log.info("Unable to create connection", e);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
- return this.connection;
}
- @Override
public void removeConnection() {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
- private Connection buildConnection() throws SQLException {
- String driverClass = configuration.getSimpleValue("driverClass", "org.h2.Driver");
+ static Connection buildConnection(Configuration pluginConfig) throws SQLException {
+ String driverClass = pluginConfig.getSimpleValue(DRIVER_CLASS_PROPERTY, "org.h2.Driver");
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
@@ -68,12 +99,13 @@ public class H2Database implements DatabaseComponent<ResourceComponent<?>> {
+ ") not found.");
}
- String url = configuration.getSimpleValue("url", "jdbc:h2:test");
- String username = configuration.getSimpleValue("username", "sa");
- String password = configuration.getSimpleValue("password", "");
- log.debug("Attempting JDBC connection to [" + url + "]");
+ String url = pluginConfig.getSimpleValue(URL_PROPERTY, "jdbc:h2:test");
+ String username = pluginConfig.getSimpleValue(USERNAME_PROPERTY, "sa");
+ String password = pluginConfig.getSimpleValue(PASSWORD_PROPERTY, "");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempting JDBC connection to [" + url + "]");
+ }
return DriverManager.getConnection(url, username, password);
}
-
}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
index dbce116..97e9d41 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2DatabaseDiscovery.java
@@ -1,9 +1,29 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.database;
import java.util.Collections;
import java.util.Set;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java
new file mode 100644
index 0000000..9d414c3
--- /dev/null
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/H2PooledConnectionProvider.java
@@ -0,0 +1,59 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.database;
+
+import static org.rhq.plugins.database.H2Database.DRIVER_CLASS_PROPERTY;
+import static org.rhq.plugins.database.H2Database.PASSWORD_PROPERTY;
+import static org.rhq.plugins.database.H2Database.URL_PROPERTY;
+import static org.rhq.plugins.database.H2Database.USERNAME_PROPERTY;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+
+/**
+ * @author Thomas Segismont
+ */
+class H2PooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public H2PooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimpleValue(DRIVER_CLASS_PROPERTY, "org.h2.Driver"));
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return pluginConfig.getSimpleValue(URL_PROPERTY, "jdbc:h2:test");
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(USERNAME_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(PASSWORD_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java
new file mode 100644
index 0000000..496e654
--- /dev/null
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/NonPoolingCustomTableComponent.java
@@ -0,0 +1,17 @@
+package org.rhq.plugins.database;
+
+/**
+ * @author Thomas Segismont
+ */
+public class NonPoolingCustomTableComponent extends CustomTableComponent {
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return false;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return null;
+ }
+}
diff --git a/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java b/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
index 4ce8107..45bac41 100644
--- a/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
+++ b/modules/plugins/database/src/test/java/org/rhq/plugins/database/PluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,40 +13,94 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.database;
+import static org.rhq.plugins.database.DatabasePluginUtil.getGridValues;
+import static org.testng.Assert.assertEquals;
+
import java.sql.Connection;
+import java.sql.SQLException;
import java.util.List;
import java.util.Map;
+import org.testng.annotations.Test;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.resource.ResourceType;
-import org.testng.annotations.Test;
-import org.testng.AssertJUnit;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
@Test
public class PluginTest extends ComponentTest {
public void test() throws Exception {
- H2Database db = (H2Database)manuallyAdd("H2 Database");
+
+ H2Database db = (H2Database) manuallyAdd("H2 Database");
assertUp(db);
- Connection connection = db.getConnection();
- connection.prepareStatement("create table sometable(a int, b int)").execute();
- connection.prepareStatement("insert into sometable values(42, 54)").execute();
- ResourceType rt = resourceTypes.get("Generic Query");
+
+ setupData(db);
+
+ // Pooling < Non Pooling < Pooling hierarchy
+
+ ResourceComponent level1 = add("Generic Query", db);
+ assertUp(level1);
+ checkAllMetrics("Generic Query", level1);
+
+ ResourceComponent level2 = add("Generic Query Non Pooling", level1);
+ assertUp(level2);
+ checkAllMetrics("Generic Query Non Pooling", level2);
+
+ ResourceComponent level3 = add("Nested Generic Query", level2);
+ assertUp(level3);
+ checkAllMetrics("Generic Query Non Pooling", level3);
+
+ checkData(db);
+ }
+
+ private ResourceComponent add(String resourceType, ResourceComponent component) throws Exception {
+ ResourceType rt = resourceTypes.get(resourceType);
Configuration configuration = getConfiguration(rt);
- CustomTableComponent ctc = (CustomTableComponent) manuallyAdd(rt, configuration, db);
- MeasurementReport report = getMeasurementReport(ctc);
- assertAll(report, getResourceDescriptor("Generic Query"));
- List<Map<String, Object>> grid = DatabaseQueryUtility.getGridValues(db, "select a, b from sometable");
- assert grid.size() == 1;
- Map<String, Object> map = grid.get(0);
- AssertJUnit.assertEquals(42, map.get("A"));
+ return manuallyAdd(rt, configuration, component);
+ }
+
+ private void checkAllMetrics(String resourceType, ResourceComponent component) throws Exception {
+ MeasurementReport report = getMeasurementReport(component);
+ assertAll(report, getResourceDescriptor(resourceType));
+ }
+
+ private void setupData(H2Database db) throws SQLException {
+ Connection connection = null;
+ try {
+ connection = db.getPooledConnectionProvider().getPooledConnection();
+ connection.prepareStatement("create table table_a(a int, b int)").execute();
+ connection.prepareStatement("insert into table_a values(1, 2)").execute();
+ connection.prepareStatement("create table table_b(a int, b int)").execute();
+ connection.prepareStatement("insert into table_b values(3, 4)").execute();
+ connection.prepareStatement("create table table_c(a int, b int)").execute();
+ connection.prepareStatement("insert into table_c values(5, 6)").execute();
+ } finally {
+ DatabasePluginUtil.safeClose(connection);
+ }
+ }
+
+ private void checkData(H2Database db) throws SQLException {
+ List<Map<String, Object>> grid = getGridValues(db, "select a, b from table_a");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 1);
+ assertEquals(grid.get(0).get("B"), 2);
+ grid = getGridValues(db, "select a, b from table_b");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 3);
+ assertEquals(grid.get(0).get("B"), 4);
+ grid = getGridValues(db, "select a, b from table_c");
+ assertEquals(grid.size(), 1);
+ assertEquals(grid.get(0).get("A"), 5);
+ assertEquals(grid.get(0).get("B"), 6);
+ System.out.println("grid = " + grid);
}
}
diff --git a/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml b/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
index 1e8f7d1..79f6413 100644
--- a/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/database/src/test/resources/META-INF/rhq-plugin.xml
@@ -5,43 +5,89 @@
description="Plugin supporting H2 database"
package="org.rhq.plugins.database"
pluginLifecycleListener="DatabasePluginLifecycleListener"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="urn:xmlns:rhq-configuration"
xmlns="urn:xmlns:rhq-plugin">
- <depends plugin="Database" useClasses="true"/>
+ <depends plugin="Database" useClasses="true"/>
- <server name="H2 Database" class="org.rhq.plugins.database.H2Database" discovery="org.rhq.plugins.database.H2DatabaseDiscovery">
- <plugin-configuration>
- <c:simple-property name="url" type="string" default="jdbc:h2:mem:"/>
- <c:simple-property name="username" type="string" default="sa"/>
- <c:simple-property name="password" type="string" default=""/>
- </plugin-configuration>
+ <server name="H2 Database" class="org.rhq.plugins.database.H2Database"
+ discovery="org.rhq.plugins.database.H2DatabaseDiscovery">
+ <plugin-configuration>
+ <c:simple-property name="url" type="string" default="jdbc:h2:mem:testdb"/>
+ <c:simple-property name="username" type="string" default="sa"/>
+ <c:simple-property name="password" type="string" default=""/>
+ </plugin-configuration>
- </server>
+ </server>
- <service name="Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
- discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
- description="Query the database for various results"
- supportsManualAdd="true" singleton="false"
- createDeletePolicy="both">
+ <service name="Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
- <runs-inside>
- <parent-resource-type name="H2 Database" plugin="H2"/>
- </runs-inside>
+ <runs-inside>
+ <parent-resource-type name="H2 Database" plugin="H2"/>
+ </runs-inside>
- <plugin-configuration>
- <c:simple-property name="metricQuery" type="string" default="select a, b from sometable"/>
- <c:simple-property name="column" type="boolean" default="true"/>
- </plugin-configuration>
- <metric property="A" displayName="A results" displayType="summary"
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_a"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_a"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
description="Number appearing in A column"
units="none" dataType="measurement"/>
- <metric property="B" displayName="B results" displayType="summary"
+ <metric property="B" displayName="B results" displayType="summary"
description="Number appearing in B column"
units="none" dataType="measurement"/>
- </service>
+ </service>
+ <service name="Generic Query Non Pooling" class="org.rhq.plugins.database.NonPoolingCustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
+
+ <runs-inside>
+ <parent-resource-type name="Generic Query" plugin="H2"/>
+ </runs-inside>
+
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_b"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_b"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
+ description="Number appearing in A column"
+ units="none" dataType="measurement"/>
+ <metric property="B" displayName="B results" displayType="summary"
+ description="Number appearing in B column"
+ units="none" dataType="measurement"/>
+ </service>
+
+ <service name="Nested Generic Query" class="org.rhq.plugins.database.CustomTableComponent"
+ discovery="org.rhq.plugins.database.CustomTableDiscoveryComponent"
+ description="Query the database for various results"
+ supportsManualAdd="true" singleton="false"
+ createDeletePolicy="both">
+
+ <runs-inside>
+ <parent-resource-type name="Generic Query Non Pooling" plugin="H2"/>
+ </runs-inside>
+
+ <plugin-configuration>
+ <c:simple-property name="table" type="string" default="table_c"/>
+ <c:simple-property name="metricQuery" type="string" default="select a, b from table_c"/>
+ <c:simple-property name="column" type="boolean" default="true"/>
+ </plugin-configuration>
+ <metric property="A" displayName="A results" displayType="summary"
+ description="Number appearing in A column"
+ units="none" dataType="measurement"/>
+ <metric property="B" displayName="B results" displayType="summary"
+ description="Number appearing in B column"
+ units="none" dataType="measurement"/>
+ </service>
</plugin>
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
index 9d7169e..3cd2401 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.io.File;
import java.io.FileReader;
import java.sql.Connection;
@@ -43,78 +47,104 @@ import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.system.AggregateProcessInfo;
import org.rhq.core.system.ProcessInfo;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
* @author Steve Millidge
*/
-public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
+public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>, ConnectionPoolingSupport,
ResourceComponent<ResourceComponent<?>>, MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(MySqlComponent.class);
+
private ResourceContext resourceContext;
private AggregateProcessInfo aggregateProcessInfo;
- private MySqlConnectionInfo info;
- private Log log = LogFactory.getLog(this.getClass());
private Map<String, String> globalStatusValues = new HashMap<String, String>();
private Map<String, String> globalVariables = new HashMap<String, String>();
+ private MySqlPooledConnectionProvider pooledConnectionProvider;
+ @Deprecated
+ private Connection sharedConnection;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- info = MySqlDiscoveryComponent.buildConnectionInfo(resourceContext.getPluginConfiguration());
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new MySqlPooledConnectionProvider(resourceContext.getPluginConfiguration());
ProcessInfo processInfo = resourceContext.getNativeProcess();
if (processInfo != null) {
aggregateProcessInfo = processInfo.getAggregateProcessTree();
} else {
- //findProcessInfo();
- //log.debug("Unable to locate native process information. Process level statistics will be unavailable.");
+ findProcessInfo();
+ }
+ }
+
+ private void buildSharedConnectionIfNeeded() {
+ try {
+ if ((sharedConnection == null) || sharedConnection.isClosed()) {
+ sharedConnection = MySqlDiscoveryComponent.buildConnection(this.resourceContext
+ .getPluginConfiguration());
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
}
public void stop() {
- MySqlConnectionManager.getConnectionManager().closeConnection(info);
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(sharedConnection);
+ sharedConnection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ aggregateProcessInfo = null;
}
- public AvailabilityType getAvailability() {
- if (log.isDebugEnabled()) {
- log.debug("Doing an availability check on " + info.buildURL());
- }
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
- Connection conn = getConnection();
- AvailabilityType result = AvailabilityType.DOWN;
- if (conn != null) {
- // the connection must be OK as the validity check will have worked
- result = AvailabilityType.UP;
- }
- if (log.isDebugEnabled()) {
- log.debug("Availability check on " + info.buildURL() + " gives " + result);
- }
- return result;
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
+ }
+ public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Connection conn = getConnection();
- if (conn != null) {
- ResultSet rs = null;
- Statement stmt = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("SHOW GLOBAL STATUS");
- while (rs.next()) {
- globalStatusValues.put(rs.getString(1), rs.getString(2));
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW GLOBAL STATUS");
+ while (resultSet.next()) {
+ globalStatusValues.put(resultSet.getString(1), resultSet.getString(2));
+ }
- rs.close();
- rs = stmt.executeQuery("select * from information_schema.global_variables");
- while (rs.next()) {
- globalVariables.put(rs.getString(1), rs.getString(2));
- }
- } catch (SQLException sqle) {
- } finally {
- DatabaseQueryUtility.close(stmt, rs);
+ resultSet.close();
+ resultSet = statement.executeQuery("select * from information_schema.global_variables");
+ while (resultSet.next()) {
+ globalVariables.put(resultSet.getString(1), resultSet.getString(2));
}
+ } catch (SQLException ignore) {
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
// get process information
@@ -154,7 +184,7 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
String strVal = globalStatusValues.get(request.getName());
double val = Double.parseDouble(strVal);
report.addData(new MeasurementDataNumeric(request, val));
- } catch (Exception e) {
+ } catch (Exception ignore) {
}
}
}
@@ -162,17 +192,14 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
}
public Connection getConnection() {
- try {
- return MySqlConnectionManager.getConnectionManager().getConnection(info);
- } catch (SQLException ex) {
- log.warn("Unable to obtain database connection ", ex);
- return null;
- }
+ buildSharedConnectionIfNeeded();
+ return sharedConnection;
}
@Override
public void removeConnection() {
- MySqlConnectionManager.getConnectionManager().closeConnection(info);
+ DatabasePluginUtil.safeClose(this.sharedConnection);
+ this.sharedConnection = null;
}
private AggregateProcessInfo findProcessInfo() {
@@ -212,7 +239,7 @@ public class MySqlComponent implements DatabaseComponent<ResourceComponent<?>>,
pidFileReader.close();
}
} catch (Exception ex) {
- log.warn("Unable to read MySQL pid file " + pidFile);
+ LOG.warn("Unable to read MySQL pid file " + pidFile);
}
}
return result;
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java
deleted file mode 100644
index 4fa0a4f..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionInfo.java
+++ /dev/null
@@ -1,92 +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.plugins.mysql;
-
-/**
- * A class to act as a key to a specific MySQL connection
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionInfo {
-
- private String host;
- private String port;
- private String db;
- private String user;
- private String password;
- private int hashCode;
-
- MySqlConnectionInfo(String host, String port, String db, String user, String password ) {
- this.host = host;
- this.port = port;
- this.db = db;
- this.user = user;
- this.password = password;
- this.hashCode = new StringBuilder().append(host).
- append(port).
- append(db).
- append(user).
- append(password).toString().hashCode();
-
- }
-
- public String getDb() {
- return db;
- }
-
- @Override
- public int hashCode() {
- return hashCode;
- }
-
- public String getHost() {
- return host;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getPort() {
- return port;
- }
-
- public String getUser() {
- return user;
- }
-
- public String buildURL() {
- return new StringBuilder().append("jdbc:mysql://")
- .append(host)
- .append(":")
- .append(port)
- .append("/")
- .append(db).toString();
- }
-
- @Override
- public boolean equals(Object other) {
- boolean result = false;
- if ((other instanceof MySqlConnectionInfo) && (other.hashCode() == this.hashCode())) {
- result = true;
- }
- return result;
- }
-
-}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
deleted file mode 100644
index 6c81332..0000000
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlConnectionManager.java
+++ /dev/null
@@ -1,125 +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.plugins.mysql;
-
-import java.sql.Connection;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
-import java.util.HashMap;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * A class to manage the connections to MySQL
- * This class keeps a cache of connections to MySQL and reuses them on demand
- * We assume single threaded access to the Connection in the agent
- * this will need to be reworked if that assumption is not correct
- * @author Steve Millidge (C2B2 Consulting Limited)
- */
-class MySqlConnectionManager {
-
- private HashMap<MySqlConnectionInfo, Connection> connections;
- private static MySqlConnectionManager singleton;
- private Log logger = LogFactory.getLog(MySqlConnectionManager.class);
-
- private MySqlConnectionManager() {
- connections = new HashMap<MySqlConnectionInfo,Connection>();
- }
-
- static MySqlConnectionManager getConnectionManager() {
- if (singleton == null) {
- singleton = new MySqlConnectionManager();
- }
- return singleton;
- }
-
- public void shutdown() {
- Driver driver = null;
- for (Connection conn : connections.values()) {
- try {
- if (driver == null) {
- String driverName = conn.getMetaData().getDriverName();
- driver = DriverManager.getDriver(driverName);
- }
- conn.close();
- }catch(SQLException e) { logger.info("Problem closing connection on Shutdown ignoring...");}
- }
- // deregister driver as well
- if (driver != null) {
- try {
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- logger.warn("Unable to deregister MySQL Driver on shutdown");
- }
- }
- }
-
- void closeConnection(MySqlConnectionInfo info) {
- Connection conn = connections.get(info);
- if (conn != null) {
- try {
- if (logger.isDebugEnabled()) {
- logger.debug("Closing Connection to " + info.buildURL());
- }
- conn.close();
- } catch (SQLException e) {
- logger.warn("Problem closing connection to " + info.buildURL() + " on close");
- }
- }
- connections.remove(info);
- }
-
- Connection getConnection (MySqlConnectionInfo info) throws SQLException {
- try {
- Class.forName("com.mysql.jdbc.Driver");
- } catch (Exception ex) {
- logger.error("Unable to find com.mysql.jdbc.Driver");
- }
-
- Connection conn = connections.get(info);
- String url = info.buildURL();
- if (conn == null) {
- if (logger.isInfoEnabled()) {
- logger.info("Attemping connection to " + url);
- }
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- if (logger.isInfoEnabled()) {
- logger.info("Successfully connected to " + url);
- }
- connections.put(info, conn);
- } else {
- if (logger.isDebugEnabled()) {
- logger.debug("Reusing existing connection to " + url);
- }
- }
-
- // check the validity of the connection
- if (!conn.isValid(0)) {
- // attempt a single reconnect here and now
- conn.close();
- conn = DriverManager.getConnection(url,info.getUser(), info.getPassword());
- connections.put(info, conn);
- logger.info("Refreshed a connection to " + url);
- }
- return conn;
- }
-
-}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
index 43747bc..be436af 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,40 +13,45 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-package org.rhq.plugins.mysql;
+package org.rhq.plugins.mysql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.pluginapi.availability.AvailabilityFacet;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
-import org.rhq.core.domain.configuration.PropertySimple;
-import org.rhq.core.domain.measurement.AvailabilityType;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlDatabaseComponent implements DatabaseComponent, AvailabilityFacet, OperationFacet {
+public class MySqlDatabaseComponent implements DatabaseComponent, ConnectionPoolingSupport, AvailabilityFacet,
+ OperationFacet {
+
+ private static final Log LOG = LogFactory.getLog(MySqlDatabaseComponent.class);
private ResourceContext resourceContext;
private MySqlComponent parent;
private String databaseName;
- private static Log log = LogFactory.getLog(MySqlDatabaseComponent.class);
@Override
public Connection getConnection() {
@@ -59,109 +64,120 @@ public class MySqlDatabaseComponent implements DatabaseComponent, AvailabilityFa
}
@Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
+ }
+
+ @Override
public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
resourceContext = rc;
databaseName = rc.getResourceKey();
- parent = (MySqlComponent)resourceContext.getParentResourceComponent();
+ parent = (MySqlComponent) resourceContext.getParentResourceComponent();
}
- public String getName() { return databaseName; }
+ public String getName() {
+ return databaseName;
+ }
@Override
public void stop() {
+ resourceContext = null;
+ databaseName = null;
+ parent = null;
}
@Override
public AvailabilityType getAvailability() {
AvailabilityType result = AvailabilityType.DOWN;
- if (log.isDebugEnabled()) {
- log.debug("Availability check for " + databaseName);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Availability check for " + databaseName);
}
- Connection conn = getConnection();
- if (conn != null) {
- Statement statement = null;
- ResultSet resultSet = null;
- try {
- statement = conn.createStatement();
- resultSet = statement.executeQuery("SHOW DATABASES LIKE '" + databaseName + "'");
- if (resultSet.next()) {
- if (resultSet.getString(1).equalsIgnoreCase(databaseName)) {
- result = AvailabilityType.UP;
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW DATABASES LIKE '" + databaseName + "'");
+ if (resultSet.next()) {
+ if (resultSet.getString(1).equalsIgnoreCase(databaseName)) {
+ result = AvailabilityType.UP;
}
- }catch(SQLException e) {
- if (log.isDebugEnabled()) {
- log.debug("Got Exception when determining database availability",e);
- }
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
}
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Got Exception when determining database availability", e);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return result;
}
@Override
- public OperationResult invokeOperation(String name, Configuration parameters)
- throws InterruptedException, Exception {
-
+ public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
+ Exception {
if ("invokeSql".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- String sql = parameters.getSimple("sql").getStringValue();
- OperationResult result = new OperationResult();
-
- if (parameters.getSimple("type").getStringValue().equals("update")) {
- int updateCount = stmt.executeUpdate(sql);
- result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
-
- } else {
- rs = stmt.executeQuery(parameters.getSimple("sql").getStringValue());
-
- ResultSetMetaData md = rs.getMetaData();
- StringBuilder buf = new StringBuilder();
- int rowCount = 0;
-
- buf.append("<table>");
- buf.append("<th>");
+ return invokeSql(parameters);
+ } else {
+ throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ }
+ }
+
+ private OperationResult invokeSql(Configuration parameters) throws SQLException {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = getConnection().createStatement();
+ String sql = parameters.getSimple("sql").getStringValue();
+ OperationResult result = new OperationResult();
+
+ if (parameters.getSimple("type").getStringValue().equals("update")) {
+ int updateCount = statement.executeUpdate(sql);
+ result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+
+ } else {
+ resultSet = statement.executeQuery(parameters.getSimple("sql").getStringValue());
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ StringBuilder buf = new StringBuilder();
+ int rowCount = 0;
+
+ buf.append("<table>");
+ buf.append("<th>");
+ for (int i = 1; i <= md.getColumnCount(); i++) {
+ buf.append("<td>");
+ buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append("</td>");
+ }
+ buf.append("</th>");
+
+ while (resultSet.next()) {
+ rowCount++;
+ buf.append("<tr>");
for (int i = 1; i <= md.getColumnCount(); i++) {
buf.append("<td>");
- buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append(resultSet.getString(i));
buf.append("</td>");
}
- buf.append("</th>");
-
-
- while (rs.next()) {
- rowCount++;
- buf.append("<tr>");
- for (int i = 1; i <= md.getColumnCount(); i++) {
- buf.append("<td>");
- buf.append(rs.getString(i));
- buf.append("</td>");
- }
- buf.append("</tr>");
- }
-
- buf.append("</table>");
- result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
- result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
- }
- return result;
- } finally {
- if (rs != null) {
- rs.close();
+ buf.append("</tr>");
}
- if (stmt != null) {
- stmt.close();
- }
+ buf.append("</table>");
+ result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
+ result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
}
- } else {
- throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
}
-
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
index 100e536..8356d56 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDatabaseDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,78 +13,63 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
-package org.rhq.plugins.mysql;
-import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
-import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
-import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+package org.rhq.plugins.mysql;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
+import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
+import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
* @author Steve Millidge
*/
public class MySqlDatabaseDiscoveryComponent implements ResourceDiscoveryComponent<MySqlComponent> {
-
- private Log logger = LogFactory.getLog(this.getClass());
+ private static final Log LOG = LogFactory.getLog(MySqlDatabaseDiscoveryComponent.class);
@Override
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<MySqlComponent> context) {
-
- if (logger.isDebugEnabled()) {
- logger.debug("Database discovery started");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Database discovery started");
}
- Set<DiscoveredResourceDetails> databases = new LinkedHashSet<DiscoveredResourceDetails>();
- Connection connection = context.getParentResourceComponent().getConnection();
-
-
+ Connection jdbcConnection = null;
Statement statement = null;
ResultSet resultSet = null;
- if (connection != null) {
- try {
- statement = connection.createStatement();
- resultSet = statement.executeQuery("SHOW DATABASES");
-
- while (resultSet.next()) {
- String databaseName = resultSet.getString(1);
- Configuration config = context.getDefaultPluginConfiguration();
- config.put(new PropertySimple("databaseName",databaseName));
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- context.getResourceType(),
- databaseName,
- databaseName + " Database",
- null,
- "A MySql Database",
- config,
- null);
- databases.add(details);
- }
-
- } catch (SQLException e) {
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
- }
- } else {
- if (logger.isInfoEnabled()) {
- logger.info("No connection to MySQL obtained from connection manager");
+ try {
+ jdbcConnection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SHOW DATABASES");
+ Set<DiscoveredResourceDetails> databases = new LinkedHashSet<DiscoveredResourceDetails>();
+ while (resultSet.next()) {
+ String databaseName = resultSet.getString(1);
+ Configuration config = context.getDefaultPluginConfiguration();
+ config.put(new PropertySimple("databaseName", databaseName));
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(context.getResourceType(),
+ databaseName, databaseName + " Database", null, "A MySql Database", config, null);
+ databases.add(details);
}
+ return databases;
+ } catch (SQLException ignore) {
+ return Collections.emptySet();
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
-
- return databases;
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
index c4ada90..30ccef4 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,28 +13,31 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
@@ -42,7 +45,7 @@ import java.util.Set;
* @author Steve Millidge
*/
public class MySqlDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
- private static final Log log = LogFactory.getLog(MySqlDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(MySqlDiscoveryComponent.class);
public static final String HOST_CONFIGURATION_PROPERTY = "host";
public static final String PORT_CONFIGURATION_PROPERTY = "port";
@@ -52,81 +55,83 @@ public class MySqlDiscoveryComponent implements ResourceDiscoveryComponent, Manu
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext context) {
- if (log.isDebugEnabled()) {
- log.debug("Resource Discovery Started");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Resource Discovery Started");
}
Set<DiscoveredResourceDetails> servers = new LinkedHashSet<DiscoveredResourceDetails>();
// Process any auto-discovered resources.
List<ProcessScanResult> autoDiscoveryResults = context.getAutoDiscoveredProcesses();
for (ProcessScanResult result : autoDiscoveryResults) {
- log.info("Discovered a mysql process: " + result);
+ LOG.info("Discovered a mysql process: " + result);
ProcessInfo procInfo = result.getProcessInfo();
- servers.add(createResourceDetails(context,context.getDefaultPluginConfiguration(),procInfo));
+ servers.add(createResourceDetails(context, context.getDefaultPluginConfiguration(), procInfo));
}
return servers;
}
public DiscoveredResourceDetails discoverResource(Configuration pluginConfiguration,
- ResourceDiscoveryContext resourceDiscoveryContext)
- throws InvalidPluginConfigurationException {
+ ResourceDiscoveryContext resourceDiscoveryContext) throws InvalidPluginConfigurationException {
ProcessInfo processInfo = null;
- DiscoveredResourceDetails resourceDetails = createResourceDetails(resourceDiscoveryContext, pluginConfiguration,
- processInfo);
+ DiscoveredResourceDetails resourceDetails = createResourceDetails(resourceDiscoveryContext,
+ pluginConfiguration, processInfo);
return resourceDetails;
}
protected static DiscoveredResourceDetails createResourceDetails(ResourceDiscoveryContext discoveryContext,
- Configuration pluginConfiguration,
- ProcessInfo processInfo) throws InvalidPluginConfigurationException {
+ Configuration pluginConfig, ProcessInfo processInfo) throws InvalidPluginConfigurationException {
- MySqlConnectionInfo ci = buildConnectionInfo(pluginConfiguration);
- Connection conn;
+ Connection conn = null;
String version = "";
try {
- conn = MySqlConnectionManager.getConnectionManager().getConnection(ci);
+ conn = buildConnection(pluginConfig);
version = conn.getMetaData().getDatabaseProductVersion();
} catch (SQLException ex) {
// ignore so we can still add to the inventory even though we can't currently connect
+ } finally {
+ DatabasePluginUtil.safeClose(conn);
}
- String key = new StringBuilder().append("MySql:")
- .append(ci.getDb())
- .append(":")
- .append(ci.getHost())
- .append(":")
- .append(ci.getPort())
- .append("-")
- .append(ci.getUser()).toString();
+ String key = new StringBuilder().append("MySql:")
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue()).append("-")
+ .append(pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue()).toString();
String name = new StringBuilder().append("MySql [")
- .append(ci.getDb())
- .append("]").toString();
-
- DiscoveredResourceDetails result = new DiscoveredResourceDetails(
- discoveryContext.getResourceType(),
- key,
- name,
- version,
- "MySql Server",
- pluginConfiguration,
- processInfo);
-
- if (log.isDebugEnabled()) {
- log.debug("Discovered Database Server for MySQL Database " + ci.buildURL());
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).append("]").toString();
+
+ DiscoveredResourceDetails result = new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name,
+ version, "MySql Server", pluginConfig, processInfo);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovered Database Server for MySQL Database " + buildConnectionURL(pluginConfig));
}
return result;
}
- static MySqlConnectionInfo buildConnectionInfo(Configuration configuration) {
- // build the Discovered Resource from the configuration
- String host = configuration.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue();
- String port = configuration.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue();
- String user = configuration.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
- String pass = configuration.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
- String db = configuration.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue();
- return new MySqlConnectionInfo(host, port, db, user, pass);
- }
+ static String buildConnectionURL(Configuration pluginConfig) {
+ return new StringBuilder().append("jdbc:mysql://")
+ .append(pluginConfig.getSimple(HOST_CONFIGURATION_PROPERTY).getStringValue()).append(":")
+ .append(pluginConfig.getSimple(PORT_CONFIGURATION_PROPERTY).getStringValue()).append("/")
+ .append(pluginConfig.getSimple(DB_CONFIGURATION_PROPERTY).getStringValue()).toString();
+ }
+
+ static Connection buildConnection(Configuration pluginConfig) throws SQLException {
+ String driverClass = "com.mysql.jdbc.Driver";
+ try {
+ Class.forName(driverClass);
+ } catch (ClassNotFoundException e) {
+ throw new InvalidPluginConfigurationException("Specified JDBC driver class (" + driverClass
+ + ") not found.");
+ }
+
+ String url = buildConnectionURL(pluginConfig);
+ String principal = pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ String credentials = pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+
+ return DriverManager.getConnection(url, principal, credentials);
+ }
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
index a731aed..db4580e 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,34 +13,50 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import java.sql.Driver;
+import java.sql.DriverManager;
+import java.util.Enumeration;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.pluginapi.plugin.PluginContext;
import org.rhq.core.pluginapi.plugin.PluginLifecycleListener;
+import org.rhq.core.util.exception.ThrowableUtil;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlPluginLifecycleListener implements PluginLifecycleListener {
- private final Log log = LogFactory.getLog(MySqlPluginLifecycleListener.class);
- private String pluginName;
+ private static final Log LOG = LogFactory.getLog(MySqlPluginLifecycleListener.class);
public void initialize(PluginContext context) throws Exception {
- pluginName = context.getPluginName();
}
public void shutdown() {
- if (log.isDebugEnabled()) {
- log.debug(new StringBuilder().append(pluginName).append(" Plugin Shutdown").toString());
+ // so we do not cause our classloader to leak perm gen, we need to de-register
+ // any and all JDBC drivers this plugin registered
+ Enumeration<Driver> drivers = DriverManager.getDrivers();
+ while (drivers.hasMoreElements()) {
+ try {
+ Driver driver = drivers.nextElement();
+ DriverManager.deregisterDriver(driver);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Deregistered JDBC driver: " + driver.getClass());
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
+ }
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this.getClass().getSimpleName() + " completed shutdown.");
}
- MySqlConnectionManager.getConnectionManager().shutdown();
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java
new file mode 100644
index 0000000..56f5782
--- /dev/null
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlPooledConnectionProvider.java
@@ -0,0 +1,61 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.mysql;
+
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.CREDENTIALS_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.PRINCIPAL_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.mysql.MySqlDiscoveryComponent.buildConnectionURL;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * A MySql plugin adapted {@link org.rhq.plugins.database.PooledConnectionProvider}.
+ *
+ * @author Thomas Segismont
+ */
+public class MySqlPooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public MySqlPooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName("com.mysql.jdbc.Driver");
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return buildConnectionURL(pluginConfig);
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
index 73354f9..ecbde73 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,18 +13,24 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementDataTrait;
@@ -33,19 +39,20 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlTableComponent implements DatabaseComponent, MeasurementFacet {
+public class MySqlTableComponent implements DatabaseComponent, ConnectionPoolingSupport, MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(MySqlTableComponent.class);
private String tableName;
private MySqlDatabaseComponent parent;
private String databaseName;
- private Log log = LogFactory.getLog(this.getClass());
@Override
public Connection getConnection() {
@@ -58,72 +65,85 @@ public class MySqlTableComponent implements DatabaseComponent, MeasurementFacet
}
@Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
+ }
+
+ @Override
public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
tableName = rc.getResourceKey();
- parent = (MySqlDatabaseComponent)rc.getParentResourceComponent();
+ parent = (MySqlDatabaseComponent) rc.getParentResourceComponent();
databaseName = parent.getName();
}
@Override
public void stop() {
+ tableName = null;
+ parent = null;
+ databaseName = null;
}
@Override
public AvailabilityType getAvailability() {
- AvailabilityType result = AvailabilityType.DOWN;
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show tables from " + databaseName + " like '" + tableName + "'");
- if (rs.first()) {
- result = AvailabilityType.UP;
- }
- }catch (SQLException se) {
- // ignore as unablailable if we can't execute the query
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show tables from " + databaseName + " like '" + tableName + "'");
+ if (resultSet.first()) {
+ return UP;
}
+ } catch (SQLException se) {
+ // Will return down
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- return result;
+ return DOWN;
}
@Override
public void getValues(MeasurementReport mr, Set<MeasurementScheduleRequest> set) throws Exception {
- Connection conn = parent.getConnection();
- if (conn != null ) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show table status from " + databaseName+ " like '" + tableName + "'");
- if (rs.next()) {
- for (MeasurementScheduleRequest request : set) {
- String value = rs.getString(request.getName());
- if (value == null) {value = "0";}
- switch (request.getDataType()) {
- case MEASUREMENT: {
- mr.addData(new MeasurementDataNumeric(request, Double.valueOf(value)));
- break;
- } case TRAIT: {
- mr.addData(new MeasurementDataTrait(request, value));
- break;
- } default: {
- break;
- }
- }
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show table status from " + databaseName + " like '" + tableName + "'");
+ if (resultSet.next()) {
+ for (MeasurementScheduleRequest request : set) {
+ String value = resultSet.getString(request.getName());
+ if (value == null) {
+ value = "0";
+ }
+ switch (request.getDataType()) {
+ case MEASUREMENT: {
+ mr.addData(new MeasurementDataNumeric(request, Double.valueOf(value)));
+ break;
+ }
+ case TRAIT: {
+ mr.addData(new MeasurementDataTrait(request, value));
+ break;
+ }
+ default: {
+ break;
+ }
}
}
- } catch(Exception se) {
- if (log.isInfoEnabled()) {
- log.info("Unable to measure table statistics", se);
- }
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
}
+ } catch (Exception se) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to measure table statistics", se);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
index 4d7b354..83535e2 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
import java.sql.Connection;
@@ -24,71 +25,66 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers MySQL tables.
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlTableDiscoveryComponent implements ResourceDiscoveryComponent {
+ private static final Log LOG = LogFactory.getLog(MySqlTableDiscoveryComponent.class);
private static final String TABLE_DISCOVERY = "tableDiscovery";
- private Log log = LogFactory.getLog(this.getClass());
@Override
public Set discoverResources(ResourceDiscoveryContext rdc) throws InvalidPluginConfigurationException, Exception {
-
- HashSet<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
- MySqlDatabaseComponent parent = (MySqlDatabaseComponent)rdc.getParentResourceComponent();
+ Set<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
+ MySqlDatabaseComponent parent = (MySqlDatabaseComponent) rdc.getParentResourceComponent();
Configuration pconfig = rdc.getParentResourceContext().getPluginConfiguration();
// If the user has disabled table discovery on the parent, we don't autodiscover
// them, as we may hit temporary ones that go away any time soon again
// See BZ-797356
if (!Boolean.parseBoolean(pconfig.getSimpleValue(TABLE_DISCOVERY, "true"))) {
- log.debug("table discovery disabled");
+ LOG.debug("table discovery disabled");
return set;
}
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("show tables from " + parent.getName());
- while (rs.next()) {
- String tableName = rs.getString(1);
- if (log.isDebugEnabled()) {
- log.debug("Discovered Table "+ tableName);
- }
- Configuration config = new Configuration();
- config.put(new PropertySimple("tableName",tableName));
- DiscoveredResourceDetails details = new DiscoveredResourceDetails(
- rdc.getResourceType(),
- tableName,
- tableName + " Table",
- null,
- tableName + " MySql Table", config, null);
- set.add(details);
- }
- } catch(SQLException se) {
- if (log.isDebugEnabled()) {
- log.debug("Unable to Discover Tables",se);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = parent.getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("show tables from " + parent.getName());
+ while (resultSet.next()) {
+ String tableName = resultSet.getString(1);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovered Table " + tableName);
}
-
- }finally {
- DatabaseQueryUtility.close(stmt, rs);
+ Configuration config = new Configuration();
+ config.put(new PropertySimple("tableName", tableName));
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(rdc.getResourceType(), tableName,
+ tableName + " Table", null, tableName + " MySql Table", config, null);
+ set.add(details);
+ }
+ } catch (SQLException se) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to Discover Tables", se);
}
+
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return set;
}
-
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
index 32525c2..d0ffb26 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,20 +13,24 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.sql.Connection;
-import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
@@ -34,20 +38,21 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
-public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent {
+public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent, ConnectionPoolingSupport {
+ private static final Log LOG = LogFactory.getLog(MySqlUserComponent.class);
private String userName;
private String host;
private MySqlComponent parent;
- private Log log = LogFactory.getLog(this.getClass());
- private ResourceContext context;
+ private ResourceContext resourceContext;
@Override
public Connection getConnection() {
@@ -60,69 +65,85 @@ public class MySqlUserComponent implements MeasurementFacet, DatabaseComponent {
}
@Override
- public void start(ResourceContext rc) throws InvalidPluginConfigurationException, Exception {
- parent = (MySqlComponent)rc.getParentResourceComponent();
- context = rc;
- userName = context.getPluginConfiguration().getSimple("userName").getStringValue();
- host = context.getPluginConfiguration().getSimple("host").getStringValue();
+ public boolean supportsConnectionPooling() {
+ return true;
}
@Override
- public void stop() {
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return parent.getPooledConnectionProvider();
}
+ @Override
+ public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
+ parent = (MySqlComponent) resourceContext.getParentResourceComponent();
+ this.resourceContext = resourceContext;
+ userName = this.resourceContext.getPluginConfiguration().getSimple("userName").getStringValue();
+ host = this.resourceContext.getPluginConfiguration().getSimple("host").getStringValue();
+ }
+
+ @Override
+ public void stop() {
+ parent = null;
+ resourceContext = null;
+ userName = null;
+ host = null;
+ }
public void getValues(MeasurementReport mr, Set<MeasurementScheduleRequest> requests) throws Exception {
- Connection conn = getConnection();
- ResultSet rs = null;
- Statement stmt = null;
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
int activeConnections = 0;
int totalConnections = 0;
try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select User,Host,State from information_schema.processlist where User='"+userName+"'");
- while(rs.next()) {
- String hostVal = rs.getString(2);
- String state = rs.getString(3);
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User,Host,State from information_schema.processlist where User='" + userName
+ + "'");
+ while (resultSet.next()) {
+ String hostVal = resultSet.getString(2);
+ String state = resultSet.getString(3);
if (hostVal.startsWith(host)) {
if (state.length() > 1) {
- activeConnections ++;
+ activeConnections++;
}
totalConnections++;
}
}
- }catch(SQLException sqle) {
-
+ } catch (SQLException ignore) {
} finally {
- DatabaseQueryUtility.close(stmt, rs);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
for (MeasurementScheduleRequest request : requests) {
if (request.getName().equals("TotalConnections")) {
- mr.addData(new MeasurementDataNumeric(request, new Double((double)totalConnections)));
+ mr.addData(new MeasurementDataNumeric(request, new Double((double) totalConnections)));
} else if (request.getName().equals("ActiveConnections")) {
- mr.addData(new MeasurementDataNumeric(request, new Double((double)activeConnections)));
+ mr.addData(new MeasurementDataNumeric(request, new Double((double) activeConnections)));
}
}
}
public AvailabilityType getAvailability() {
- AvailabilityType result = AvailabilityType.DOWN;
- Connection conn = getConnection();
- ResultSet rs = null;
- Statement stmt = null;
+ Connection jdbcConnection = null;
+ ResultSet resultSet = null;
+ Statement statement = null;
try {
- stmt = conn.createStatement();
- rs = stmt.executeQuery("select User from mysql.user where User='"+userName+"' and Host='" + host +"'");
- if (rs.first()) {
- result = AvailabilityType.UP;
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User from mysql.user where User='" + userName + "' and Host='"
+ + host + "'");
+ if (resultSet.first()) {
+ return UP;
}
- }catch(SQLException sqle) {
-
+ } catch (SQLException sqle) {
+ // Will return DOWN
+ System.out.println("sqle = " + sqle);
} finally {
- DatabaseQueryUtility.close(stmt, rs);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- return result;
+ return DOWN;
}
}
diff --git a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
index d05682a..9075b1c 100644
--- a/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
+++ b/modules/plugins/mysql/src/main/java/org/rhq/plugins/mysql/MySqlUserDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.mysql;
import java.sql.Connection;
@@ -23,53 +24,44 @@ import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
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.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
- *
* @author Steve Millidge (C2B2 Consulting Limited)
*/
public class MySqlUserDiscoveryComponent implements ResourceDiscoveryComponent {
public Set discoverResources(ResourceDiscoveryContext rdc) throws InvalidPluginConfigurationException, Exception {
- HashSet<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
+ Set<DiscoveredResourceDetails> set = new HashSet<DiscoveredResourceDetails>();
MySqlComponent parent = (MySqlComponent) rdc.getParentResourceComponent();
- Connection conn = parent.getConnection();
- if (conn != null) {
- Statement statement = null;
- ResultSet resultSet = null;
- try {
- statement = conn.createStatement();
- resultSet = statement.executeQuery("select User,Host from mysql.user");
- while (resultSet.next()) {
- String user = resultSet.getString(1);
- String host = resultSet.getString(2);
- String userName = user + "@" + host;
- Configuration config = new Configuration();
- config.put(new PropertySimple("userName",user));
- config.put(new PropertySimple("host",host));
- DiscoveredResourceDetails discoveredUser =
- new DiscoveredResourceDetails(
- rdc.getResourceType(),
- userName,
- userName,
- null,
- "A MySql User",
- config,
- null);
- set.add(discoveredUser);
- }
- } catch (Exception e) {
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = parent.getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select User,Host from mysql.user");
+ while (resultSet.next()) {
+ String user = resultSet.getString(1);
+ String host = resultSet.getString(2);
+ String userName = user + "@" + host;
+ Configuration config = new Configuration();
+ config.put(new PropertySimple("userName", user));
+ config.put(new PropertySimple("host", host));
+ DiscoveredResourceDetails discoveredUser = new DiscoveredResourceDetails(rdc.getResourceType(),
+ userName, userName, null, "A MySql User", config, null);
+ set.add(discoveredUser);
}
+ } catch (Exception e) {
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return set;
}
diff --git a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
index 3b431d6..fdcce4a 100644
--- a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
+++ b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/ComponentTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,8 +13,8 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
diff --git a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
index 498fe22..90703ca 100644
--- a/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
+++ b/modules/plugins/mysql/src/test/java/org/rhq/plugins/mysql/PluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,16 +13,17 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package org.rhq.plugins.mysql;
+import org.testng.annotations.Test;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.pluginapi.inventory.ResourceComponent;
-import org.testng.annotations.Test;
/**
* Tests MySql Server.
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
index 09e1a80..129b971 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -26,6 +30,7 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
@@ -33,9 +38,8 @@ import org.rhq.core.domain.measurement.MeasurementDataTrait;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle ASM Disk Group Component.
@@ -43,75 +47,72 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
@SuppressWarnings("rawtypes")
-public class OracleAsmDiskGroupComponent extends AbstractDatabaseComponent
- implements MeasurementFacet {
-
- private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$asm_diskgroup WHERE group_number = ? and STATE <> 'BROKEN'";
-
- private static final String SQL_VALUES = "SELECT GROUP_NUMBER, " + "NAME, "
- + "SECTOR_SIZE sectorSize, " + "BLOCK_SIZE blockSize, "
- + "ALLOCATION_UNIT_SIZE allocationUnitSize, " + "STATE state, "
- + "TYPE type, " + "TOTAL_MB totalMb, " + "FREE_MB freeMb, "
- + "((TOTAL_MB-FREE_MB)/TOTAL_MB) usedPercent, "
- + "REQUIRED_MIRROR_FREE_MB requiredMirrorFreeMb, "
- + "USABLE_FILE_MB usableFileMb, " + "OFFLINE_DISKS offlineDisks, "
- + "COMPATIBILITY compatibility, "
- + "DATABASE_COMPATIBILITY databaseCompatibility "
- + "FROM v$asm_diskgroup WHERE group_number = ?";
+public class OracleAsmDiskGroupComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleAsmDiskGroupComponent.class);
- private static Log log = LogFactory
- .getLog(OracleAsmDiskGroupComponent.class);
+ private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$asm_diskgroup WHERE group_number = ? and STATE <> 'BROKEN'";
+ private static final String SQL_VALUES = "SELECT GROUP_NUMBER, " + "NAME, " + "SECTOR_SIZE sectorSize, "
+ + "BLOCK_SIZE blockSize, " + "ALLOCATION_UNIT_SIZE allocationUnitSize, " + "STATE state, " + "TYPE type, "
+ + "TOTAL_MB totalMb, " + "FREE_MB freeMb, " + "((TOTAL_MB-FREE_MB)/TOTAL_MB) usedPercent, "
+ + "REQUIRED_MIRROR_FREE_MB requiredMirrorFreeMb, " + "USABLE_FILE_MB usableFileMb, "
+ + "OFFLINE_DISKS offlineDisks, " + "COMPATIBILITY compatibility, "
+ + "DATABASE_COMPATIBILITY databaseCompatibility " + "FROM v$asm_diskgroup WHERE group_number = ?";
- public AvailabilityType getAvailability() {
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
- statement.setString(1, this.resourceContext.getResourceKey());
- resultSet = statement.executeQuery();
- if (resultSet.next() && (resultSet.getInt(1) == 1)) {
- return AvailabilityType.UP;
- }
- } catch (SQLException e) {
- log.debug("unable to query", e);
- } finally {
- JDBCUtil.safeClose(statement, resultSet);
- }
+ public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
+ statement.setString(1, this.resourceContext.getResourceKey());
+ resultSet = statement.executeQuery();
+ if (resultSet.next() && (resultSet.getInt(1) == 1)) {
+ return AvailabilityType.UP;
+ }
+ } catch (SQLException e) {
+ LOG.debug("unable to query", e);
+ } finally {
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
- return AvailabilityType.DOWN;
- }
+ return AvailabilityType.DOWN;
+ }
- public void getValues(MeasurementReport report,
- Set<MeasurementScheduleRequest> metrics) throws Exception {
- PreparedStatement statement = null;
- ResultSet resultSet = null;
- try {
- statement = this.getConnection().prepareStatement(SQL_VALUES);
- statement.setString(1, this.resourceContext.getResourceKey());
- resultSet = statement.executeQuery();
- if (resultSet.next()) {
- for (MeasurementScheduleRequest request : metrics) {
- String name = request.getName().toUpperCase(Locale.US);
- if (request.getDataType().equals(DataType.TRAIT)) {
- report.addData(new MeasurementDataTrait(request,
- resultSet.getString(name)));
- } else {
- try {
- report.addData(new MeasurementDataNumeric(request,
- resultSet.getDouble(name)));
- } catch (SQLException e) {
- // Ignoring metrics that cannot be read as a double
- log.warn("Ignoring metric " + name
- + " as it cannot be read as a double");
- }
- }
- }
- }
- } catch (SQLException e) {
- log.debug("Unable to read value", e);
- removeConnection();
- } finally {
- DatabaseQueryUtility.close(statement, resultSet);
- }
- }
-}
\ No newline at end of file
+ public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
+ Connection jdbcConnection = null;
+ PreparedStatement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_VALUES);
+ statement.setString(1, this.resourceContext.getResourceKey());
+ resultSet = statement.executeQuery();
+ if (resultSet.next()) {
+ for (MeasurementScheduleRequest request : metrics) {
+ String name = request.getName().toUpperCase(Locale.US);
+ if (request.getDataType().equals(DataType.TRAIT)) {
+ report.addData(new MeasurementDataTrait(request, resultSet.getString(name)));
+ } else {
+ try {
+ report.addData(new MeasurementDataNumeric(request, resultSet.getDouble(name)));
+ } catch (SQLException e) {
+ // Ignoring metrics that cannot be read as a double
+ LOG.warn("Ignoring metric " + name + " as it cannot be read as a double");
+ }
+ }
+ }
+ }
+ } catch (SQLException e) {
+ LOG.debug("Unable to read value", e);
+ } finally {
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
+ }
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
index c023b1b..f4b49ef 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleAsmDiskGroupDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.hasConnectionPoolingSupport;
+
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -28,65 +32,66 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
-import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovery Oracle ASM Disk Groups.
*
* @author Richard Hensman
*/
-public class OracleAsmDiskGroupDiscoveryComponent implements ResourceDiscoveryComponent<DatabaseComponent<?>> {
-
- private final Log log = LogFactory.getLog(getClass());
+public class OracleAsmDiskGroupDiscoveryComponent implements ResourceDiscoveryComponent<ResourceComponent<?>> {
+ private static final Log LOG = LogFactory.getLog(OracleAsmDiskGroupDiscoveryComponent.class);
public Set<DiscoveredResourceDetails> discoverResources(
- ResourceDiscoveryContext<DatabaseComponent<?>> resourceDiscoveryContext)
+ ResourceDiscoveryContext<ResourceComponent<?>> resourceDiscoveryContext)
throws InvalidPluginConfigurationException, Exception {
- Statement statement = null;
- ResultSet resultSet = null;
-
+
String table = "V$ASM_DISKGROUP";
String keyColumn = "GROUP_NUMBER";
String nameColumn = "NAME";
String description = "Oracle ASM Disk Groups";
-
- try {
- Connection conn = resourceDiscoveryContext.getParentResourceComponent().getConnection();
- statement = conn.createStatement();
+ ResourceComponent<?> parentComponent = resourceDiscoveryContext.getParentResourceComponent();
+
+ Connection connection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ connection = getConnectionFromComponent(parentComponent);
+ statement = connection.createStatement();
resultSet = statement.executeQuery("SELECT * FROM " + table);
Configuration config = null;
Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>();
while (resultSet.next()) {
- config = resourceDiscoveryContext.getDefaultPluginConfiguration();
+ config = resourceDiscoveryContext.getDefaultPluginConfiguration();
String key = resultSet.getString(keyColumn);
String name = resultSet.getString(nameColumn);
- DiscoveredResourceDetails details =
- new DiscoveredResourceDetails(
- resourceDiscoveryContext.getResourceType(),
- key,
- name,
- null,
- description, config, null);
+ DiscoveredResourceDetails details = new DiscoveredResourceDetails(
+ resourceDiscoveryContext.getResourceType(), key, name, null, description, config, null);
found.add(details);
}
return found;
} catch (SQLException e) {
- log.debug("table " + table + " column " + keyColumn, e);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("table " + table + " column " + keyColumn, e);
+ }
} finally {
- JDBCUtil.safeClose(resultSet);
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (hasConnectionPoolingSupport(parentComponent)) {
+ DatabasePluginUtil.safeClose(connection);
+ }
}
- return Collections.emptySet();
- }
+ return Collections.emptySet();
+ }
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
index 79d67fd..f3ab7b6 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
import java.sql.Connection;
@@ -23,26 +24,27 @@ import java.sql.DatabaseMetaData;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
+import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.pluginapi.inventory.ProcessScanResult;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.pluginapi.inventory.ManualAddFacet;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
-
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
*/
public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
- private static Log log = LogFactory.getLog(OracleDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(OracleDiscoveryComponent.class);
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext resourceDiscoveryContext)
throws InvalidPluginConfigurationException, Exception {
@@ -51,7 +53,7 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
for (ProcessScanResult process : autoDiscoveryResults) {
String sid = process.getProcessInfo().getEnvironmentVariable("ORACLE_SID");
if ((sid == null) || (sid.length() == 0)) {
- log.info("Unable to discover Oracle instance SID. Use manual inventory to complete setup.");
+ LOG.info("Unable to discover Oracle instance SID. Use manual inventory to complete setup.");
continue;
}
@@ -74,22 +76,21 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
}
public DiscoveredResourceDetails discoverResource(Configuration pluginConfig,
- ResourceDiscoveryContext resourceDiscoveryContext)
- throws InvalidPluginConfigurationException {
-
+ ResourceDiscoveryContext resourceDiscoveryContext) throws InvalidPluginConfigurationException {
+
Connection connection = null;
try {
connection = OracleServerComponent.buildConnection(pluginConfig);
DatabaseMetaData dbmd = connection.getMetaData();
String version = dbmd.getDatabaseMajorVersion() + "." + dbmd.getDatabaseMinorVersion();
- DiscoveredResourceDetails details = createResourceDetails(resourceDiscoveryContext, pluginConfig,
- version, null);
+ DiscoveredResourceDetails details = createResourceDetails(resourceDiscoveryContext, pluginConfig, version,
+ null);
return details;
} catch (Exception e) {
- log.warn("Could not connect to oracle with supplied configuration", e);
- throw new InvalidPluginConfigurationException("Unable to connect to Oracle",e);
+ LOG.warn("Could not connect to oracle with supplied configuration", e);
+ throw new InvalidPluginConfigurationException("Unable to connect to Oracle", e);
} finally {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(connection);
}
}
@@ -102,4 +103,4 @@ public class OracleDiscoveryComponent implements ResourceDiscoveryComponent, Man
return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version, description,
pluginConfig, processInfo);
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
index 0446d7f..c1ef458 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleFlashRecoveryAreaComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,14 +32,14 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle Flash Recovery Area Component.
@@ -42,37 +47,38 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
public class OracleFlashRecoveryAreaComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleFlashRecoveryAreaComponent.class);
private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM v$recovery_file_dest WHERE name = ?";
- private static final String SQL_VALUES =
- "SELECT space_limit spaceLimit, space_used spaceUsed, space_reclaimable spaceReclaimable, number_of_files numberOfFiles, (space_used/space_limit) usedPercent " +
- "FROM v$recovery_file_dest WHERE name = ?";
-
-
- private static Log log = LogFactory.getLog(OracleFlashRecoveryAreaComponent.class);
+ private static final String SQL_VALUES = "SELECT space_limit spaceLimit, space_used spaceUsed, space_reclaimable spaceReclaimable, number_of_files numberOfFiles, (space_used/space_limit) usedPercent "
+ + "FROM v$recovery_file_dest WHERE name = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SQL_VALUES,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SQL_VALUES, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -80,4 +86,4 @@ public class OracleFlashRecoveryAreaComponent extends AbstractDatabaseComponent
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
index 488404f..a6e390b 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
import java.sql.Driver;
@@ -30,7 +31,7 @@ import org.rhq.core.pluginapi.plugin.PluginLifecycleListener;
import org.rhq.core.util.exception.ThrowableUtil;
public class OraclePluginLifecycleListener implements PluginLifecycleListener {
- private final Log log = LogFactory.getLog(OraclePluginLifecycleListener.class);
+ private static final Log LOG = LogFactory.getLog(OraclePluginLifecycleListener.class);
public void initialize(PluginContext context) throws Exception {
// no-op
@@ -44,13 +45,17 @@ public class OraclePluginLifecycleListener implements PluginLifecycleListener {
try {
Driver driver = drivers.nextElement();
DriverManager.deregisterDriver(driver);
- log.debug("Deregistered JDBC driver: " + driver.getClass());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Deregistered JDBC driver: " + driver.getClass());
+ }
} catch (Exception e) {
- log.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
+ LOG.warn("Failed to deregister JDBC drivers - memory might leak" + ThrowableUtil.getAllMessages(e));
}
}
- log.debug(this.getClass().getSimpleName() + " completed shutdown.");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(this.getClass().getSimpleName() + " completed shutdown.");
+ }
return;
}
}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java
new file mode 100644
index 0000000..b057a40
--- /dev/null
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OraclePooledConnectionProvider.java
@@ -0,0 +1,71 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.oracle;
+
+import java.sql.Driver;
+import java.util.Properties;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * An Oracle plugin adapted {@link org.rhq.plugins.database.PooledConnectionProvider}.
+ *
+ * @author Thomas Segismont
+ */
+public class OraclePooledConnectionProvider extends BasePooledConnectionProvider {
+
+ static final String DRIVER_CLASS_PROPERTY = "driverClass";
+ static final String PRINCIPAL_PROPERTY = "principal";
+ static final String CREDENTIALS_PROPERTY = "credentials";
+
+ public OraclePooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimple(DRIVER_CLASS_PROPERTY).getStringValue());
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return OracleServerComponent.buildUrl(pluginConfig);
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected Properties getConnectionProperties() {
+ Properties connectionProperties = super.getConnectionProperties();
+ if (getUsername().equalsIgnoreCase("SYS")) {
+ connectionProperties.put("internal_logon", "sysdba");
+ }
+ return connectionProperties;
+ }
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
index 3849657..6e6b6b9 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleServerComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,20 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValueMap;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.CREDENTIALS_PROPERTY;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.DRIVER_CLASS_PROPERTY;
+import static org.rhq.plugins.oracle.OraclePooledConnectionProvider.PRINCIPAL_PROPERTY;
+
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
@@ -36,48 +45,69 @@ import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
*/
-public class OracleServerComponent implements DatabaseComponent, MeasurementFacet {
+public class OracleServerComponent implements DatabaseComponent, ConnectionPoolingSupport, MeasurementFacet {
private static final Log LOG = LogFactory.getLog(OracleServerComponent.class);
- private Connection connection;
-
private ResourceContext resourceContext;
-
- private boolean started;
+ @Deprecated
+ private Connection connection;
+ private OraclePooledConnectionProvider pooledConnectionProvider;
public void start(ResourceContext resourceContext) throws InvalidPluginConfigurationException, Exception {
this.resourceContext = resourceContext;
- this.connection = buildConnection(resourceContext.getPluginConfiguration());
- this.started = true;
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new OraclePooledConnectionProvider(resourceContext.getPluginConfiguration());
}
public void stop() {
removeConnection();
- this.started = false;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
+ try {
+ if (this.connection == null || connection.isClosed()) {
+ this.connection = buildConnection(this.resourceContext.getPluginConfiguration());
+ }
+ } catch (SQLException e) {
+ LOG.debug("Unable to create oracle connection", e);
+ }
}
public AvailabilityType getAvailability() {
- if (started && getConnection() != null) {
- return AvailabilityType.UP;
- } else {
- return AvailabilityType.DOWN;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValueMap(this,
- "SELECT name, value FROM V$SYSSTAT");
+ Map<String, Double> values = getNumericQueryValueMap(this, "SELECT name, value FROM V$SYSSTAT");
for (MeasurementScheduleRequest request : metrics) {
if (request.getName().equals("totalSize")) {
- Double val = DatabaseQueryUtility.getSingleNumericQueryValue(this,
- "SELECT SUM(bytes) FROM SYS.DBA_DATA_FILES");
+ Double val = getSingleNumericQueryValue(this, "SELECT SUM(bytes) FROM SYS.DBA_DATA_FILES");
report.addData(new MeasurementDataNumeric(request, val));
} else {
Double value = values.get(request.getName());
@@ -89,23 +119,17 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
}
public Connection getConnection() {
- try {
- if (this.connection == null || connection.isClosed()) {
- this.connection = buildConnection(this.resourceContext.getPluginConfiguration());
- }
- } catch (SQLException e) {
- LOG.info("Unable to create oracle connection", e);
- }
+ buildSharedConnectionIfNeeded();
return this.connection;
}
public void removeConnection() {
- JDBCUtil.safeClose(connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
public static Connection buildConnection(Configuration configuration) throws SQLException {
- String driverClass = configuration.getSimple("driverClass").getStringValue();
+ String driverClass = configuration.getSimple(DRIVER_CLASS_PROPERTY).getStringValue();
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
@@ -114,10 +138,12 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
}
String url = buildUrl(configuration);
- LOG.debug("Attempting JDBC connection to [" + url + "]");
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Attempting JDBC connection to [" + url + "]");
+ }
- String principal = configuration.getSimple("principal").getStringValue();
- String credentials = configuration.getSimple("credentials").getStringValue();
+ String principal = configuration.getSimple(PRINCIPAL_PROPERTY).getStringValue();
+ String credentials = configuration.getSimple(CREDENTIALS_PROPERTY).getStringValue();
Properties props = new Properties();
props.put("user", principal);
@@ -129,7 +155,7 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
return DriverManager.getConnection(url, props);
}
- private static String buildUrl(Configuration configuration) {
+ static String buildUrl(Configuration configuration) {
String connMethod = configuration.getSimpleValue("connectionMethod", "SID");
if (connMethod.equalsIgnoreCase("SID")) {
return "jdbc:oracle:thin:@" + configuration.getSimpleValue("host", "localhost") + ":"
@@ -139,4 +165,4 @@ public class OracleServerComponent implements DatabaseComponent, MeasurementFace
+ configuration.getSimpleValue("port", "1521") + "/" + configuration.getSimpleValue("sid", "XE");
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
index e99787b..e7e457e 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleTablespaceComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,14 +32,14 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Oracle Tablespace Component.
@@ -42,37 +47,38 @@ import org.rhq.plugins.database.DatabaseQueryUtility;
* @author Richard Hensman
*/
public class OracleTablespaceComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleTablespaceComponent.class);
private static final String SQL_AVAILABLE = "SELECT COUNT(*) FROM dba_tablespaces WHERE tablespace_name = ?";
- private static final String SQL_VALUES =
- "SELECT USED_SPACE usedSpace, TABLESPACE_SIZE totalSize, (USED_PERCENT/100) usedPercent " +
- "FROM dba_tablespace_usage_metrics where tablespace_name = ?";
-
-
- private static Log log = LogFactory.getLog(OracleTablespaceComponent.class);
+ private static final String SQL_VALUES = "SELECT USED_SPACE usedSpace, TABLESPACE_SIZE totalSize, (USED_PERCENT/100) usedPercent "
+ + "FROM dba_tablespace_usage_metrics where tablespace_name = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_AVAILABLE);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_AVAILABLE);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SQL_VALUES,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SQL_VALUES, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -80,4 +86,4 @@ public class OracleTablespaceComponent extends AbstractDatabaseComponent impleme
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
index 49999fd..63b9985 100644
--- a/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
+++ b/modules/plugins/oracle/src/main/java/org/rhq/plugins/oracle/OracleUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,16 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.oracle;
+import static org.rhq.plugins.database.DatabasePluginUtil.getConnectionFromComponent;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+
+import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@@ -27,50 +32,51 @@ import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
import org.rhq.plugins.database.AbstractDatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
*/
public class OracleUserComponent extends AbstractDatabaseComponent implements MeasurementFacet {
+ private static final Log LOG = LogFactory.getLog(OracleUserComponent.class);
private static final String SQL_USER = "SELECT COUNT(*) FROM DBA_USERS WHERE username = ?";
- private static final String SESSIONS =
- "SELECT SUM(DECODE(Status, 'ACTIVE', 1, 0)) active, COUNT(1) connections " +
- "FROM V$SESSION where username = ?";
-
-
- private static Log log = LogFactory.getLog(OracleUserComponent.class);
+ private static final String SESSIONS = "SELECT SUM(DECODE(Status, 'ACTIVE', 1, 0)) active, COUNT(1) connections "
+ + "FROM V$SESSION where username = ?";
public AvailabilityType getAvailability() {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement(SQL_USER);
+ jdbcConnection = getConnectionFromComponent(this);
+ statement = jdbcConnection.prepareStatement(SQL_USER);
statement.setString(1, this.resourceContext.getResourceKey());
resultSet = statement.executeQuery();
if (resultSet.next() && (resultSet.getInt(1) == 1)) {
return AvailabilityType.UP;
}
} catch (SQLException e) {
- log.debug("unable to query", e);
+ LOG.debug("unable to query", e);
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
+ if (supportsConnectionPooling()) {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
}
return AvailabilityType.DOWN;
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this, SESSIONS,
- this.resourceContext.getResourceKey());
+ Map<String, Double> values = getNumericQueryValues(this, SESSIONS, this.resourceContext.getResourceKey());
for (MeasurementScheduleRequest request : metrics) {
Double d = values.get(request.getName().toUpperCase(Locale.US));
if (d != null) {
@@ -78,4 +84,4 @@ public class OracleUserComponent extends AbstractDatabaseComponent implements Me
}
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
index b8ac5cb..4820785 100644
--- a/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
+++ b/modules/plugins/oracle/src/main/resources/META-INF/rhq-plugin.xml
@@ -641,14 +641,6 @@
discovery="org.rhq.plugins.oracle.OracleAsmDiskGroupDiscoveryComponent"
class="org.rhq.plugins.oracle.OracleAsmDiskGroupComponent">
- <!--plugin-configuration>
- <c:simple-property name="table" default="V$ASM_DISKGROUP"/>
- <c:simple-property name="metricQuery" default="SELECT {key} FROM V$ASM_DISKGROUP"/>
- <c:simple-property name="keyColumn" default="GROUP_NUMBER"/>
- <c:simple-property name="name" default="NAME"/>
- <c:simple-property name="description" default="Oracle ASM Disk Groups"/>
- </plugin-configuration -->
-
<metric property="sectorSize" displayName="Sector Size" description="Physical block size (in bytes)" units="bytes" dataType="trait"/>
<metric property="blockSize" displayName="Block Size" description="Automatic Storage Management metadata block size (in bytes)" units="bytes" dataType="trait"/>
<metric property="allocationUnitSize" displayName="Allocation Unit Size" description="Size of the allocation unit (in bytes)" units="bytes" dataType="trait"/>
diff --git a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
index e98c2e9..346565b 100644
--- a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
+++ b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/ComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.oracle;
import static org.testng.AssertJUnit.assertEquals;
diff --git a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
index d9d2fb0..dfb16ac 100644
--- a/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
+++ b/modules/plugins/oracle/src/test/java/org/rhq/plugins/oracle/OracleServerComponentTest.java
@@ -1,3 +1,22 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
package org.rhq.plugins.oracle;
import static org.testng.AssertJUnit.assertNotNull;
@@ -6,15 +25,16 @@ import static org.testng.AssertJUnit.assertTrue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.rhq.core.domain.configuration.Configuration;
-import org.rhq.core.domain.measurement.MeasurementReport;
-import org.rhq.core.domain.resource.ResourceType;
-import org.rhq.core.pluginapi.inventory.ResourceComponent;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.measurement.MeasurementReport;
+import org.rhq.core.domain.resource.ResourceType;
+import org.rhq.core.pluginapi.inventory.ResourceComponent;
+
public class OracleServerComponentTest extends ComponentTest {
private static final String ORACLE_SERVER = "Oracle Server";
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
index f235f93..ec58833 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,20 +13,29 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+import static org.rhq.core.domain.resource.CreateResourceStatus.FAILURE;
+import static org.rhq.core.domain.resource.CreateResourceStatus.SUCCESS;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.buildConnection;
+
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
-import java.sql.ResultSetMetaData;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.configuration.PropertyList;
@@ -36,43 +45,71 @@ import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementScheduleRequest;
-import org.rhq.core.domain.resource.CreateResourceStatus;
import org.rhq.core.pluginapi.inventory.CreateChildResourceFacet;
import org.rhq.core.pluginapi.inventory.CreateResourceReport;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
-public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServerComponent<?>>, MeasurementFacet,
- CreateChildResourceFacet, OperationFacet {
- private Log log = LogFactory.getLog(PostgresDatabaseComponent.class);
+public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServerComponent<?>>,
+ ConnectionPoolingSupport, MeasurementFacet, CreateChildResourceFacet, OperationFacet {
- private ResourceContext<PostgresServerComponent<?>> resourceContext;
+ private static final Log LOG = LogFactory.getLog(PostgresDatabaseComponent.class);
- private Connection databaseConnection;
+ private static final String QUERY_DATABASE_SIZE = "SELECT *, pg_database_size(datname) AS size FROM pg_stat_database where datname = ?";
+ private ResourceContext<PostgresServerComponent<?>> resourceContext;
private String databaseName;
+ private PostgresServerComponent<?> postgresServerComponent;
+ private boolean useOwnJdbcConnections;
+ @Deprecated
+ private Connection databaseConnection;
+ private PostgresPooledConnectionProvider pooledConnectionProvider;
+
+ public void start(ResourceContext<PostgresServerComponent<?>> context) throws Exception {
+ this.resourceContext = context;
+ databaseName = resourceContext.getPluginConfiguration().getSimple("databaseName").getStringValue();
+ postgresServerComponent = resourceContext.getParentResourceComponent();
+ useOwnJdbcConnections = !databaseName.equals(postgresServerComponent.getResourceContext()
+ .getPluginConfiguration().getSimple("db").getStringValue());
+ if (useOwnJdbcConnections) {
+ buildDatabaseConnectionIfNeeded();
+ pooledConnectionProvider = new PostgresPooledConnectionProvider(createDatabaseSpecificConfig());
+ }
+ }
+
+ public void stop() {
+ this.resourceContext = null;
+ databaseName = null;
+ postgresServerComponent = null;
+ if (useOwnJdbcConnections) {
+ DatabasePluginUtil.safeClose(databaseConnection);
+ databaseConnection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ }
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return useOwnJdbcConnections ? pooledConnectionProvider : postgresServerComponent.getPooledConnectionProvider();
+ }
public Connection getConnection() {
- this.databaseName = resourceContext.getPluginConfiguration().getSimple("databaseName").getStringValue();
- if (this.databaseName.equals(resourceContext.getParentResourceComponent().getResourceContext()
- .getPluginConfiguration().getSimple("db").getStringValue())) {
- return resourceContext.getParentResourceComponent().getConnection();
+ if (useOwnJdbcConnections) {
+ return postgresServerComponent.getConnection();
} else {
- // ??? Need to use a different connection to talk to a different db?
- if (this.databaseConnection == null) {
- Configuration config = resourceContext.getParentResourceComponent().getResourceContext()
- .getPluginConfiguration();
- config = config.deepCopy();
- config.put(new PropertySimple("db", databaseName));
- log.debug("Getting db specific connection to postgres for [" + databaseName + "] database");
- this.databaseConnection = PostgresDiscoveryComponent.buildConnection(config, true);
- }
-
- // TODO GH: Attempt to load other db connections? or only monitor this dbs stuff? Weird situation.
+ buildDatabaseConnectionIfNeeded();
return this.databaseConnection;
}
}
@@ -82,23 +119,47 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
if ((this.databaseConnection != null) && !this.databaseConnection.isClosed()) {
this.databaseConnection.close();
}
- } catch (SQLException se) {
- log.debug("Closing and removing postgres connection");
+ } catch (SQLException e) {
+ LOG.debug("Could not remove connection", e);
}
-
this.databaseConnection = null;
}
- public void start(ResourceContext<PostgresServerComponent<?>> context) {
- this.resourceContext = context;
+ private void buildDatabaseConnectionIfNeeded() {
+ try {
+ if (this.databaseConnection == null || this.databaseConnection.isClosed()) {
+ this.databaseConnection = buildConnection(createDatabaseSpecificConfig(), true);
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
+ }
}
- public void stop() {
- this.resourceContext = null;
+ private Configuration createDatabaseSpecificConfig() {
+ Configuration config = postgresServerComponent.getResourceContext().getPluginConfiguration();
+ config = config.deepCopy();
+ config.put(new PropertySimple("db", databaseName));
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Getting db specific connection to postgres for [" + databaseName + "] database");
+ }
+ return config;
}
public AvailabilityType getAvailability() {
- return resourceContext.getParentResourceComponent().getAvailability();
+ if (useOwnJdbcConnections) {
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
+ }
+ }
+ return postgresServerComponent.getAvailability();
}
public String getDatabaseName() {
@@ -106,35 +167,27 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) {
+ Connection jdbcConnection = null;
PreparedStatement statement = null;
+ ResultSet resultSet = null;
try {
- statement = this.resourceContext.getParentResourceComponent().getConnection().prepareStatement(
- "SELECT *, pg_database_size(datname) AS size FROM pg_stat_database where datname = ?");
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.prepareStatement(QUERY_DATABASE_SIZE);
statement.setString(1, this.resourceContext.getPluginConfiguration().getSimple("databaseName")
.getStringValue());
- ResultSet results = statement.executeQuery();
- try {
- if (!results.next()) {
- throw new RuntimeException("Couldn't get the data"); // TODO Error handling system
- }
-
- for (MeasurementScheduleRequest request : metrics) {
- // Only size expected
- double val = results.getDouble(request.getName());
- report.addData(new MeasurementDataNumeric(request, val));
- }
- } finally {
- results.close();
+ resultSet = statement.executeQuery();
+ if (!resultSet.next()) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Result set is empty: " + QUERY_DATABASE_SIZE);
+ }
+ }
+ for (MeasurementScheduleRequest request : metrics) {
+ report.addData(new MeasurementDataNumeric(request, resultSet.getDouble(request.getName())));
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
- try {
- if (statement != null) {
- statement.close();
- }
- } catch (SQLException e) {
- }
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
@@ -188,7 +241,8 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
buf.append("\n)");
- log.info("Creating table with: " + buf.toString());
+ String createTableSql = buf.toString();
+ LOG.info("Creating table with: " + createTableSql);
PropertyList constraintList = configuration.getList("constraints");
if (constraintList != null) {
for (Property c : constraintList.getList()) {
@@ -197,99 +251,105 @@ public class PostgresDatabaseComponent implements DatabaseComponent<PostgresServ
}
}
+ Connection jdbcConnection = null;
Statement statement = null;
try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ statement.executeUpdate(createTableSql);
+ report.setStatus(SUCCESS);
report.setResourceKey(tableName);
- statement = getConnection().createStatement();
- statement.executeUpdate(buf.toString());
- report.setStatus(CreateResourceStatus.SUCCESS);
report.setResourceName(tableName);
} catch (SQLException e) {
report.setException(e);
- report.setStatus(CreateResourceStatus.FAILURE);
+ report.setStatus(FAILURE);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, null);
}
return report;
}
- public OperationResult invokeOperation(String name, Configuration parameters)
- throws InterruptedException, Exception {
-
+ public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
+ Exception {
if ("resetStatistics".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- rs = stmt.executeQuery("select * from pg_stat_reset()");
-
- } finally {
- if (rs != null) {
- rs.close();
- }
-
- if (stmt != null) {
- stmt.close();
- }
- }
- return null;
+ return resetStatistics();
} else if ("invokeSql".equals(name)) {
- Statement stmt = null;
- ResultSet rs = null;
- try {
- stmt = getConnection().createStatement();
- String sql = parameters.getSimple("sql").getStringValue();
- OperationResult result = new OperationResult();
-
- if (parameters.getSimple("type").getStringValue().equals("update")) {
- int updateCount = stmt.executeUpdate(sql);
- result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+ return invokeSql(parameters);
+ } else {
+ throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ }
+ }
- } else {
- rs = stmt.executeQuery(parameters.getSimple("sql").getStringValue());
+ private OperationResult resetStatistics() {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select * from pg_stat_reset()");
+ return null; // does not return results
+ } catch (SQLException e) {
+ OperationResult result = new OperationResult("Failed to reset statistics");
+ result.setErrorMessage(e.getMessage());
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
+ }
+ }
- ResultSetMetaData md = rs.getMetaData();
- StringBuilder buf = new StringBuilder();
- int rowCount = 0;
+ private OperationResult invokeSql(Configuration parameters) throws SQLException {
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ String sql = parameters.getSimple("sql").getStringValue();
+
+ OperationResult result = new OperationResult();
+ if (parameters.getSimple("type").getStringValue().equals("update")) {
+ int updateCount = statement.executeUpdate(sql);
+ result.getComplexResults().put(new PropertySimple("result", "Query updated " + updateCount + " rows"));
+ } else {
+ resultSet = statement.executeQuery(sql);
+
+ ResultSetMetaData md = resultSet.getMetaData();
+ StringBuilder buf = new StringBuilder();
+ int rowCount = 0;
+
+ buf.append("<table>");
+ buf.append("<th>");
+ for (int i = 1; i <= md.getColumnCount(); i++) {
+ buf.append("<td>");
+ buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append("</td>");
+ }
+ buf.append("</th>");
- buf.append("<table>");
- buf.append("<th>");
+ while (resultSet.next()) {
+ rowCount++;
+ buf.append("<tr>");
for (int i = 1; i <= md.getColumnCount(); i++) {
buf.append("<td>");
- buf.append(md.getColumnName(i) + " (" + md.getColumnTypeName(i) + ")");
+ buf.append(resultSet.getString(i));
buf.append("</td>");
}
- buf.append("</th>");
-
-
- while (rs.next()) {
- rowCount++;
- buf.append("<tr>");
- for (int i = 1; i <= md.getColumnCount(); i++) {
- buf.append("<td>");
- buf.append(rs.getString(i));
- buf.append("</td>");
- }
- buf.append("</tr>");
- }
-
- buf.append("</table>");
- result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
- result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
- }
- return result;
- } finally {
- if (rs != null) {
- rs.close();
+ buf.append("</tr>");
}
- if (stmt != null) {
- stmt.close();
- }
+ buf.append("</table>");
+ result.getComplexResults().put(new PropertySimple("result", "Query returned " + rowCount + " rows"));
+ result.getComplexResults().put(new PropertySimple("contents", buf.toString()));
}
- } else {
- throw new UnsupportedOperationException("Operation [" + name + "] is not supported yet.");
+ return result;
+ } catch (SQLException e) {
+ OperationResult result = new OperationResult("Failed to invoke SQL");
+ result.setErrorMessage(e.getMessage());
+ return result;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
index cfc60b2..d4c89dc 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDatabaseDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,13 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashSet;
@@ -27,7 +29,7 @@ import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* @author Greg Hinkle
@@ -37,10 +39,12 @@ public class PostgresDatabaseDiscoveryComponent implements ResourceDiscoveryComp
throws Exception {
Set<DiscoveredResourceDetails> databases = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
- statement = context.getParentResourceComponent().getConnection().createStatement();
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = connection.createStatement();
resultSet = statement
.executeQuery("SELECT *, pg_database_size(datname) FROM pg_database where datistemplate = false");
while (resultSet.next()) {
@@ -51,9 +55,9 @@ public class PostgresDatabaseDiscoveryComponent implements ResourceDiscoveryComp
databases.add(database);
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
return databases;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
index b62c029..59150c4 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.io.File;
@@ -39,6 +40,7 @@ import org.apache.commons.logging.LogFactory;
import org.hyperic.sigar.ProcExe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
@@ -51,7 +53,7 @@ import org.rhq.core.system.ProcessExecution;
import org.rhq.core.system.ProcessExecutionResults;
import org.rhq.core.system.ProcessInfo;
import org.rhq.core.system.SystemInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
import org.rhq.plugins.postgres.util.PostgresqlConfFile;
/**
@@ -59,8 +61,7 @@ import org.rhq.plugins.postgres.util.PostgresqlConfFile;
* @author Ian Springer
*/
public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet {
-
- private static final Log log = LogFactory.getLog(PostgresDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresDiscoveryComponent.class);
public static final String PGDATA_DIR_CONFIGURATION_PROPERTY = "pgdataDir";
public static final String CONFIG_FILE_CONFIGURATION_PROPERTY = "configFile";
@@ -82,15 +83,15 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
// Process any auto-discovered resources.
List<ProcessScanResult> autoDiscoveryResults = context.getAutoDiscoveredProcesses();
for (ProcessScanResult result : autoDiscoveryResults) {
- log.info("Discovered a postgres process: " + result);
+ LOG.info("Discovered a postgres process: " + result);
ProcessInfo procInfo = result.getProcessInfo();
String pgDataPath = getDataDirPath(procInfo);
if (pgDataPath == null) {
- log.error("Unable to obtain data directory for postgres process with pid " + procInfo.getPid()
- + " (tried checking both -D command line argument, as well as " + PGDATA_ENV_VAR
- + " environment variable).");
+ LOG.error("Unable to obtain data directory for postgres process with pid " + procInfo.getPid()
+ + " (tried checking both -D command line argument, as well as " + PGDATA_ENV_VAR
+ + " environment variable).");
continue;
}
@@ -99,26 +100,28 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
PostgresqlConfFile confFile = null;
if (!pgData.exists()) {
- log.warn("PostgreSQL data directory (" + pgData + ") does not exist or is not readable. "
+ LOG.warn("PostgreSQL data directory ("
+ + pgData
+ + ") does not exist or is not readable. "
+ "Make sure the user the RHQ Agent is running as has read permissions on the directory and its parent directory.");
} else {
- log.debug("PostgreSQL data directory: " + pgData);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("PostgreSQL data directory: " + pgData);
+ }
File postgresConfFile = (configFilePath != null) ? new File(configFilePath) : new File(pgData,
PostgresServerComponent.DEFAULT_CONFIG_FILE_NAME);
- log.debug("PostgreSQL configuration file: " + postgresConfFile);
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("PostgreSQL configuration file: " + postgresConfFile);
+ }
if (!postgresConfFile.exists()) {
- log.warn("PostgreSQL configuration file (" + postgresConfFile + ") does not exist.");
+ LOG.warn("PostgreSQL configuration file (" + postgresConfFile + ") does not exist.");
} else {
try {
confFile = new PostgresqlConfFile(postgresConfFile);
} catch (IOException e) {
- if (log.isDebugEnabled()) {
- log.debug("Could not load PostgreSQL configuration file [" + postgresConfFile + "].", e);
- } else {
- log.warn("Could not load PostgreSQL configuration file [" + postgresConfFile + "]: " + e);
- }
+ LOG.warn("Could not load PostgreSQL configuration file [" + postgresConfFile + "]: " + e);
}
}
}
@@ -167,14 +170,19 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
protected static DiscoveredResourceDetails createResourceDetails(ResourceDiscoveryContext discoveryContext,
- Configuration pluginConfiguration, @Nullable ProcessInfo processInfo, boolean logConnectionFailure) {
+ Configuration pluginConfiguration, @Nullable
+ ProcessInfo processInfo, boolean logConnectionFailure) {
String key = buildUrl(pluginConfiguration);
- Connection conn = buildConnection(pluginConfiguration, logConnectionFailure);
- String name = getServerResourceName(pluginConfiguration, conn);
- String version = getVersion(processInfo, discoveryContext.getSystemInformation(), conn);
- JDBCUtil.safeClose(conn);
- return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version,
- DEFAULT_RESOURCE_DESCRIPTION, pluginConfiguration, processInfo);
+ Connection conn = null;
+ try {
+ conn = buildConnection(pluginConfiguration, logConnectionFailure);
+ String name = getServerResourceName(pluginConfiguration, conn);
+ String version = getVersion(processInfo, discoveryContext.getSystemInformation(), conn);
+ return new DiscoveredResourceDetails(discoveryContext.getResourceType(), key, name, version,
+ DEFAULT_RESOURCE_DESCRIPTION, pluginConfiguration, processInfo);
+ } finally {
+ DatabasePluginUtil.safeClose(conn);
+ }
}
protected static String buildUrl(Configuration config) {
@@ -193,7 +201,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
} catch (SQLException e) {
// TODO GH: How to put this back to the server while inventorying this resource in an unconfigured state
- log.info("Exception detecting postgres instance version.", e);
+ LOG.info("Exception detecting postgres instance version.", e);
}
//now try to extract the version information by asking the server executable itself
@@ -211,11 +219,11 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
if (m.find()) {
version = versionInfo.substring(m.start(), m.end());
} else {
- log.debug("Can't get the process executable - does the agent have the right permissions?");
+ LOG.debug("Can't get the process executable - does the agent have the right permissions?");
}
}
} catch (Exception e) {
- log.info("Failed to obtain Postgres version information from the executable file.", e);
+ LOG.info("Failed to obtain Postgres version information from the executable file.", e);
}
}
@@ -240,16 +248,19 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
return DriverManager.getConnection(url, principal, credentials);
} catch (SQLException e) {
if (logFailure) {
- log.info("Failed to connect to the database: " + e.getMessage());
+ LOG.info("Failed to connect to the database: " + e.getMessage());
} else {
- log.debug("Failed to connect to the database: " + e.getMessage());
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Failed to connect to the database: " + e.getMessage());
+ }
}
return null;
}
}
@Nullable
- protected static String getDataDirPath(@NotNull ProcessInfo procInfo) {
+ protected static String getDataDirPath(@NotNull
+ ProcessInfo procInfo) {
String dataDirPath = null;
String[] cmdLine = procInfo.getCommandLine();
for (int i = 0; i < cmdLine.length; i++) {
@@ -258,7 +269,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
dataDirPath = cmdLine[i + 1];
break;
} else {
- log.error("-D option was last option on postgres command line: " + Arrays.asList(cmdLine));
+ LOG.error("-D option was last option on postgres command line: " + Arrays.asList(cmdLine));
}
}
}
@@ -274,7 +285,8 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
@Nullable
- private static String getConfigFilePath(@NotNull ProcessInfo procInfo) {
+ private static String getConfigFilePath(@NotNull
+ ProcessInfo procInfo) {
String configFilePath = null;
String[] cmdLine = procInfo.getCommandLine();
for (int i = 0; i < cmdLine.length; i++) {
@@ -283,8 +295,8 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
String paramString = cmdLine[i + 1];
int equalsIndex = paramString.indexOf('=');
if (equalsIndex == -1) {
- log.error("Invalid value '" + paramString + "' for -c option on postgres command line: "
- + Arrays.asList(cmdLine));
+ LOG.error("Invalid value '" + paramString + "' for -c option on postgres command line: "
+ + Arrays.asList(cmdLine));
continue;
}
@@ -294,7 +306,7 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
break;
}
} else {
- log.error("-c option was last option on postgres command line: " + Arrays.asList(cmdLine));
+ LOG.error("-c option was last option on postgres command line: " + Arrays.asList(cmdLine));
}
}
}
@@ -303,13 +315,12 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
}
private static List<String> getDatabaseNames(Connection conn) {
- Statement statement = null;
- ResultSet resultSet = null;
-
if (conn == null) {
return Collections.emptyList();
}
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
List<String> ret = new ArrayList<String>();
@@ -323,10 +334,10 @@ public class PostgresDiscoveryComponent implements ResourceDiscoveryComponent, M
return ret;
} catch (SQLException e) {
- log.info("Failed to obtain the list of databases in a postgres instance", e);
+ LOG.error("Failed to obtain the list of databases in a postgres instance", e);
return Collections.emptyList();
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(null, statement, resultSet);
}
}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
index 526caeb..d651506 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPluginLifecycleListener.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Driver;
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java
new file mode 100644
index 0000000..c505bae
--- /dev/null
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresPooledConnectionProvider.java
@@ -0,0 +1,62 @@
+/*
+ * RHQ Management Platform
+ * Copyright (C) 2005-2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+package org.rhq.plugins.postgres;
+
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.CREDENTIALS_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.DRIVER_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.PRINCIPAL_CONFIGURATION_PROPERTY;
+import static org.rhq.plugins.postgres.PostgresDiscoveryComponent.buildUrl;
+
+import java.sql.Driver;
+
+import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.plugins.database.BasePooledConnectionProvider;
+
+/**
+ * A Postgres plugin adapated {@link org.rhq.plugins.database.PooledConnectionProvider}
+ *
+ * @author Thomas Segismont
+ */
+public class PostgresPooledConnectionProvider extends BasePooledConnectionProvider {
+
+ public PostgresPooledConnectionProvider(Configuration pluginConfig) throws Exception {
+ super(pluginConfig);
+ }
+
+ @Override
+ protected Class<Driver> getDriverClass() throws ClassNotFoundException {
+ return (Class<Driver>) Class.forName(pluginConfig.getSimple(DRIVER_CONFIGURATION_PROPERTY).getStringValue());
+ }
+
+ @Override
+ protected String getJdbcUrl() {
+ return buildUrl(pluginConfig);
+ }
+
+ @Override
+ protected String getPassword() {
+ return pluginConfig.getSimple(CREDENTIALS_CONFIGURATION_PROPERTY).getStringValue();
+ }
+
+ @Override
+ protected String getUsername() {
+ return pluginConfig.getSimple(PRINCIPAL_CONFIGURATION_PROPERTY).getStringValue();
+ }
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
index d132598..3fb7354 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresServerComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.core.domain.measurement.AvailabilityType.DOWN;
+import static org.rhq.core.domain.measurement.AvailabilityType.UP;
+
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
@@ -61,8 +65,10 @@ import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
import org.rhq.core.system.AggregateProcessInfo;
import org.rhq.core.system.ProcessInfo;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
import org.rhq.plugins.postgres.util.PostgresqlConfFile;
/**
@@ -70,43 +76,48 @@ import org.rhq.plugins.postgres.util.PostgresqlConfFile;
*
* @author Greg Hinkle
*/
-public class PostgresServerComponent<T extends ResourceComponent<?>> implements DatabaseComponent<T>, ConfigurationFacet, MeasurementFacet,
- OperationFacet, CreateChildResourceFacet {
+public class PostgresServerComponent<T extends ResourceComponent<?>> implements DatabaseComponent<T>,
+ ConnectionPoolingSupport, ConfigurationFacet, MeasurementFacet, OperationFacet, CreateChildResourceFacet {
- private static Log log = LogFactory.getLog(PostgresServerComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresServerComponent.class);
- private Connection connection;
+ static final String DEFAULT_CONFIG_FILE_NAME = "postgresql.conf";
private AggregateProcessInfo aggregateProcessInfo;
-
+ @Deprecated
+ private Connection connection;
private ResourceContext resourceContext;
+ private PostgresPooledConnectionProvider pooledConnectionProvider;
- static final String DEFAULT_CONFIG_FILE_NAME = "postgresql.conf";
-
- /*
- * TODO: Other things to support active sessions: select * from pg_stat_activity
- */
-
- public void start(ResourceContext context) throws SQLException {
+ public void start(ResourceContext context) throws Exception {
this.resourceContext = context;
- Configuration config = context.getPluginConfiguration();
-
- JDBCUtil.safeClose(this.connection); // just to be sure we don't leak a connection
- this.connection = PostgresDiscoveryComponent.buildConnection(config, true);
-
+ buildSharedConnectionIfNeeded();
+ pooledConnectionProvider = new PostgresPooledConnectionProvider(resourceContext.getPluginConfiguration());
ProcessInfo processInfo = resourceContext.getNativeProcess();
if (processInfo != null) {
aggregateProcessInfo = processInfo.getAggregateProcessTree();
} else {
findProcessInfo();
- //log.debug("Unable to locate native process information. Process level statistics will be unavailable.");
}
}
public void stop() {
- this.resourceContext = null;
- JDBCUtil.safeClose(this.connection);
- this.connection = null;
+ resourceContext = null;
+ DatabasePluginUtil.safeClose(connection);
+ connection = null;
+ pooledConnectionProvider.close();
+ pooledConnectionProvider = null;
+ aggregateProcessInfo = null;
+ }
+
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return pooledConnectionProvider;
}
protected String getJDBCUrl() {
@@ -114,15 +125,15 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
public AvailabilityType getAvailability() {
- AvailabilityType type;
- getConnection(); // This retries the connection if its null
- if (connection == null) {
- type = AvailabilityType.DOWN;
- } else {
- type = AvailabilityType.UP;
+ Connection jdbcConnection = null;
+ try {
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ return jdbcConnection.isValid(1) ? UP : DOWN;
+ } catch (SQLException e) {
+ return DOWN;
+ } finally {
+ DatabasePluginUtil.safeClose(jdbcConnection);
}
-
- return type;
}
ResourceContext getResourceContext() {
@@ -130,20 +141,25 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
public Connection getConnection() {
- // TODO: This method should probably be synchronized to prevent connection leaks. (ips, 10/4/07)
+ buildSharedConnectionIfNeeded();
+ return connection;
+ }
+
+ private void buildSharedConnectionIfNeeded() {
try {
if ((connection == null) || connection.isClosed()) {
- connection = PostgresDiscoveryComponent.buildConnection(this.resourceContext.getPluginConfiguration(), true);
+ connection = PostgresDiscoveryComponent.buildConnection(this.resourceContext.getPluginConfiguration(),
+ true);
}
} catch (SQLException e) {
- // TODO Should we throw this?
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Could not build shared connection", e);
+ }
}
-
- return connection;
}
public void removeConnection() {
- JDBCUtil.safeClose(this.connection);
+ DatabasePluginUtil.safeClose(this.connection);
this.connection = null;
}
@@ -177,12 +193,12 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
// Runtime settings (session params) - obtained via SQL.
- // TODO (ips, 05/16/12): We should move these to a separate Resource, since they are loaded via a completely
- // separate mechanism than the static config above.
+ Connection jdbcConnection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
- statement = connection.createStatement();
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
resultSet = statement.executeQuery("show all");
PropertyMap runtimeSettings = new PropertyMap("runtimeSettings");
@@ -201,7 +217,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
return config;
@@ -230,69 +246,80 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
PostgresqlConfFile confFile = getConfigurationFile();
confFile.setProperties(parameters);
} catch (IOException e) {
- log.error("Unable to update postgres configuration file", e);
+ LOG.error("Unable to update postgres configuration file", e);
}
report.setStatus(ConfigurationUpdateStatus.SUCCESS);
}
- /**
- * Get data about the database server. Currently we have two categories:
- * <ul>
- * <li>Database.* are metrics that are obtained from the database server itself</li>
- * <li>Process.* are metrics obtained from the native system.</li>
- * </ul>
- *
- * @param report the report where all collected measurement data will be added
- * @param metrics the schedule of what needs to be collected when
- */
+ /**
+ * Get data about the database server. Currently we have two categories:
+ * <ul>
+ * <li>Database.* are metrics that are obtained from the database server itself</li>
+ * <li>Process.* are metrics obtained from the native system.</li>
+ * </ul>
+ *
+ * @param report the report where all collected measurement data will be added
+ * @param metrics the schedule of what needs to be collected when
+ */
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) {
- for (MeasurementScheduleRequest request : metrics) {
- String property = request.getName();
- if (property.startsWith("Process.")) {
- if (aggregateProcessInfo != null) {
- aggregateProcessInfo.refresh();
+ for (MeasurementScheduleRequest request : metrics) {
+ String property = request.getName();
+ if (property.startsWith("Process.")) {
+ if (aggregateProcessInfo != null) {
+ aggregateProcessInfo.refresh();
- //report.addData(new MeasurementDataNumeric(request, getProcessProperty(request.getName())));
+ //report.addData(new MeasurementDataNumeric(request, getProcessProperty(request.getName())));
- Object val = lookupAttributeProperty(aggregateProcessInfo, property.substring("Process.".length()));
- if (val != null && val instanceof Number) {
-// aggregateProcessInfo.getAggregateMemory().Cpu().getTotal()
- report.addData(new MeasurementDataNumeric(request, ((Number) val).doubleValue()));
+ Object val = lookupAttributeProperty(aggregateProcessInfo, property.substring("Process.".length()));
+ if (val != null && val instanceof Number) {
+ // aggregateProcessInfo.getAggregateMemory().Cpu().getTotal()
+ report.addData(new MeasurementDataNumeric(request, ((Number) val).doubleValue()));
+ }
}
- }
- } else if (property.startsWith("Database")) {
- try {
+ } else if (property.startsWith("Database")) {
if (property.endsWith("startTime")) {
- // db start time
- ResultSet rs = getConnection().createStatement().executeQuery("SELECT pg_postmaster_start_time()");
+ // db start time
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- if (rs.next()) {
- report.addData(new MeasurementDataTrait(request, rs.getTimestamp(1).toString()));
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SELECT pg_postmaster_start_time()");
+ if (resultSet.next()) {
+ report.addData(new MeasurementDataTrait(request, resultSet.getTimestamp(1).toString()));
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Can not collect property: " + property + ": " + e.getLocalizedMessage());
}
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
- }
- else if (property.endsWith("backends")) {
+ } else if (property.endsWith("backends")) {
// number of connected backends
- ResultSet rs = getConnection().createStatement().executeQuery("select count(*) from pg_stat_activity");
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- if (rs.next()) {
- report.addData(new MeasurementDataNumeric(request, (double) rs.getLong(1)));
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("select count(*) from pg_stat_activity");
+ if (resultSet.next()) {
+ report.addData(new MeasurementDataNumeric(request, (double) resultSet.getLong(1)));
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Can not collect property: " + property + ": " + e.getLocalizedMessage());
}
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
-
- }
- catch (SQLException e) {
- log.warn("Can not collect property: " + property + ": " + e.getLocalizedMessage());
- }
- }
- }
+ }
+ }
}
private Double getProcessProperty(String property) {
@@ -320,8 +347,10 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} catch (Exception e) {
- log.debug("Unable to read property from measurement attribute [" + searchProperty + "] not found on ["
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Unable to read property from measurement attribute [" + searchProperty + "] not found on ["
+ this.resourceContext.getResourceKey() + "]");
+ }
}
if (ps.length > 1) {
@@ -340,34 +369,31 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
}
}
} catch (Exception e) {
- log.error("Error occurred while retrieving property '" + name + "' from object [" + object + "]", e);
+ LOG.error("Error occurred while retrieving property '" + name + "' from object [" + object + "]", e);
}
return Double.NaN;
}
- /*private ProcessInfo getProcess(String pgdata)
- * { List<ProcessScanResult> matches = this.resourceContext.getNativeProcessesForType(); for (ProcessScanResult
- * process : matches) { if (pgdata.equals(process.getProcessInfo().getEnvironmentProperty("PGDATA"))) return
- * process.getProcessInfo(); } return null;}*/
-
public OperationResult invokeOperation(String name, Configuration parameters) throws InterruptedException,
Exception {
if (name.equals("listProcessStatistics")) {
- Statement stmt = null;
- ResultSet rs = null;
+ Connection jdbcConnection = null;
+ Statement statement = null;
+ ResultSet resultSet = null;
try {
- stmt = getConnection().createStatement();
- rs = stmt.executeQuery("SELECT * FROM pg_stat_activity ORDER BY current_query desc");
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
+ resultSet = statement.executeQuery("SELECT * FROM pg_stat_activity ORDER BY current_query desc");
PropertyList procList = new PropertyList("processList");
- while (rs.next()) {
+ while (resultSet.next()) {
PropertyMap pm = new PropertyMap("process");
- pm.put(new PropertySimple("pid", rs.getInt("procpid")));
- pm.put(new PropertySimple("userName", rs.getString("usename")));
- pm.put(new PropertySimple("query", rs.getString("current_query")));
- pm.put(new PropertySimple("address", rs.getString("client_addr")));
- pm.put(new PropertySimple("port", rs.getInt("client_port")));
+ pm.put(new PropertySimple("pid", resultSet.getInt("procpid")));
+ pm.put(new PropertySimple("userName", resultSet.getString("usename")));
+ pm.put(new PropertySimple("query", resultSet.getString("current_query")));
+ pm.put(new PropertySimple("address", resultSet.getString("client_addr")));
+ pm.put(new PropertySimple("port", resultSet.getInt("client_port")));
procList.add(pm);
}
@@ -376,13 +402,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
result.getComplexResults().put(procList);
return result;
} finally {
- if (rs != null) {
- rs.close();
- }
-
- if (stmt != null) {
- stmt.close();
- }
+ DatabasePluginUtil.safeClose(jdbcConnection, statement, resultSet);
}
}
@@ -393,11 +413,12 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
Configuration userConfig = report.getResourceConfiguration();
String user = userConfig.getSimpleValue("user", null);
+ Connection jdbcConnection = null;
Statement statement = null;
String sql = PostgresUserComponent.getUserSQL(userConfig, PostgresUserComponent.UpdateType.CREATE);
try {
- statement = getConnection().createStatement();
-
+ jdbcConnection = getPooledConnectionProvider().getPooledConnection();
+ statement = jdbcConnection.createStatement();
// NOTE: Postgres doesn't seem to indicate the expect count of 1 row updated but this work
// Postgres returns 0 for DDL that does not return rows
statement.executeUpdate(sql);
@@ -406,7 +427,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
} catch (SQLException e) {
report.setException(e);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(jdbcConnection, statement);
}
return report;
@@ -436,7 +457,7 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
jonValue = Boolean.FALSE.toString();
} else {
jonValue = (propDef.isRequired()) ? Boolean.FALSE.toString() : null;
- log.warn("Boolean PostgreSQL configuration parameter '" + propDef.getName()
+ LOG.warn("Boolean PostgreSQL configuration parameter '" + propDef.getName()
+ "' has an invalid value: '" + value + "' - defaulting value to '" + jonValue + "'");
}
} else {
@@ -446,16 +467,15 @@ public class PostgresServerComponent<T extends ResourceComponent<?>> implements
return new PropertySimple(propDef.getName(), jonValue);
}
-
public void findProcessInfo() {
- List<ProcessInfo> processes =
- this.resourceContext.getSystemInformation().getProcesses(
+ List<ProcessInfo> processes = this.resourceContext
+ .getSystemInformation()
+ .getProcesses(
"process|basename|match=^(?i)(postgres|postmaster)\\.exe$,process|basename|nomatch|parent=^(?i)(postgres|postmaster)\\.exe$");
- processes.addAll(
- this.resourceContext.getSystemInformation().getProcesses(
- "process|basename|match=^(postgres|postmaster)$,process|basename|nomatch|parent=^(postgres|postmaster)$"));
+ processes.addAll(this.resourceContext.getSystemInformation().getProcesses(
+ "process|basename|match=^(postgres|postmaster)$,process|basename|nomatch|parent=^(postgres|postmaster)$"));
for (ProcessInfo processInfo : processes) {
String pgDataPath = PostgresDiscoveryComponent.getDataDirPath(processInfo);
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
index 9dc07bd..c65a97d 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,13 +13,18 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+
import java.sql.Connection;
import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
@@ -27,6 +32,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.Property;
@@ -44,16 +50,20 @@ import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
import org.rhq.core.pluginapi.operation.OperationFacet;
import org.rhq.core.pluginapi.operation.OperationResult;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
+import org.rhq.plugins.database.DatabasePluginUtil;
import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* Represents a postgres table
*
* @author Greg Hinkle
*/
-public class PostgresTableComponent implements DatabaseComponent<PostgresDatabaseComponent>, MeasurementFacet,
- ConfigurationFacet, DeleteResourceFacet, OperationFacet {
+public class PostgresTableComponent implements DatabaseComponent<PostgresDatabaseComponent>, ConnectionPoolingSupport,
+ MeasurementFacet, ConfigurationFacet, DeleteResourceFacet, OperationFacet {
+
private static final List<String> PG_STAT_USER_TABLE_STATS = Arrays.asList("seq_scan", "seq_tup_read", "idx_scan",
"idx_tup_fetch", "n_tup_ins", "n_tup_upd", "n_tup_del", "table_size", "total_size");
@@ -77,6 +87,16 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
this.resourceContext = null;
}
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return resourceContext.getParentResourceComponent().getPooledConnectionProvider();
+ }
+
public String getTableName() {
return this.resourceContext.getPluginConfiguration().getSimple("tableName").getStringValue();
}
@@ -88,28 +108,35 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> requests) {
this.resourceContext.getParentResourceComponent().getConnection();
- Map<String, Double> results = DatabaseQueryUtility.getNumericQueryValues(this, PG_STAT_USER_TABLES_QUERY,
- getTableName());
+ Map<String, Double> results = getNumericQueryValues(this, PG_STAT_USER_TABLES_QUERY, getTableName());
for (MeasurementScheduleRequest request : requests) {
String metricName = request.getName();
Double value;
if (metricName.equals("rows")) {
- value = DatabaseQueryUtility.getSingleNumericQueryValue(this, PG_COUNT_ROWS + getTableName());
+ value = getSingleNumericQueryValue(this, PG_COUNT_ROWS + getTableName());
} else if (metricName.equals("rows_approx")) {
- value = DatabaseQueryUtility.getSingleNumericQueryValue(this, PG_COUNT_ROWS_APPROX, getTableName());
+ value = getSingleNumericQueryValue(this, PG_COUNT_ROWS_APPROX, getTableName());
} else {
value = results.get(metricName);
}
- if (value!=null) {
+ if (value != null) {
MeasurementDataNumeric mdn = new MeasurementDataNumeric(request, value);
report.addData(mdn);
}
}
}
- public void deleteResource() throws SQLException {
- DatabaseQueryUtility.executeUpdate(this, "DROP TABLE " + getTableName(), new Object[] {});
+ public void deleteResource() throws Exception {
+ Connection connection = null;
+ PreparedStatement statement = null;
+ try {
+ connection = getPooledConnectionProvider().getPooledConnection();
+ statement = connection.prepareStatement("DROP TABLE " + getTableName());
+ statement.executeUpdate();
+ } finally {
+ DatabasePluginUtil.safeClose(connection, statement);
+ }
}
public Configuration loadResourceConfiguration() throws Exception {
@@ -117,29 +144,31 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
config.put(new PropertySimple("tableName", resourceContext.getPluginConfiguration().getSimple("tableName")
.getStringValue()));
- Connection connection = this.resourceContext.getParentResourceComponent().getConnection();
-
- DatabaseMetaData dmd = connection.getMetaData();
- ResultSet rs = dmd.getColumns("", "", getTableName(), "");
+ Connection connection = null;
+ ResultSet columns = null;
try {
+ connection = this.resourceContext.getParentResourceComponent().getConnection();
+ DatabaseMetaData databaseMetaData = connection.getMetaData();
+ columns = databaseMetaData.getColumns("", "", getTableName(), "");
+
PropertyList columnList = new PropertyList("columns");
- while (rs.next()) {
+ while (columns.next()) {
PropertyMap col = new PropertyMap("columnDefinition");
- col.put(new PropertySimple("columnName", rs.getString("COLUMN_NAME")));
- col.put(new PropertySimple("columnType", rs.getString("TYPE_NAME")));
- col.put(new PropertySimple("columnLength", rs.getInt("COLUMN_SIZE")));
- col.put(new PropertySimple("columnPrecision", rs.getInt("DECIMAL_DIGITS")));
- col.put(new PropertySimple("columnDefault", rs.getString("COLUMN_DEF")));
- col.put(new PropertySimple("columnNullable", rs.getBoolean("IS_NULLABLE")));
+ col.put(new PropertySimple("columnName", columns.getString("COLUMN_NAME")));
+ col.put(new PropertySimple("columnType", columns.getString("TYPE_NAME")));
+ col.put(new PropertySimple("columnLength", columns.getInt("COLUMN_SIZE")));
+ col.put(new PropertySimple("columnPrecision", columns.getInt("DECIMAL_DIGITS")));
+ col.put(new PropertySimple("columnDefault", columns.getString("COLUMN_DEF")));
+ col.put(new PropertySimple("columnNullable", columns.getBoolean("IS_NULLABLE")));
columnList.add(col);
}
config.put(columnList);
} finally {
- rs.close();
+ DatabasePluginUtil.safeClose(connection);
}
return config;
@@ -177,13 +206,12 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
}
} else {
existingDefs.remove(existingDef.columnName);
- boolean columnLengthChanged = ((existingDef.columnLength != null && !existingDef.columnLength.equals(newDef.columnLength)) ||
- (existingDef.columnLength == null && existingDef.columnLength != null));
- boolean columnPrecisionChanged = ((existingDef.columnPrecision != null && !existingDef.columnPrecision.equals(newDef.columnPrecision)) ||
- (existingDef.columnPrecision == null && existingDef.columnPrecision != null));
- if (!existingDef.columnType.equals(newDef.columnType) ||
- columnLengthChanged ||
- columnPrecisionChanged) {
+ boolean columnLengthChanged = ((existingDef.columnLength != null && !existingDef.columnLength
+ .equals(newDef.columnLength)) || (existingDef.columnLength == null && existingDef.columnLength != null));
+ boolean columnPrecisionChanged = ((existingDef.columnPrecision != null && !existingDef.columnPrecision
+ .equals(newDef.columnPrecision)) || (existingDef.columnPrecision == null && existingDef.columnPrecision != null));
+ if (!existingDef.columnType.equals(newDef.columnType) || columnLengthChanged
+ || columnPrecisionChanged) {
String sql = "ALTER TABLE " + getTableName() + " ALTER COLUMN " + newDef.columnName + " TYPE "
+ newDef.columnType;
if (newDef.columnLength != null) {
@@ -200,8 +228,8 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
}
// Set default separately.
- boolean columnDefaultChanged = ((existingDef.columnDefault != null && !existingDef.columnDefault.equals(newDef.columnDefault)) ||
- (existingDef.columnDefault == null && newDef.columnDefault != null));
+ boolean columnDefaultChanged = ((existingDef.columnDefault != null && !existingDef.columnDefault
+ .equals(newDef.columnDefault)) || (existingDef.columnDefault == null && newDef.columnDefault != null));
if (columnDefaultChanged) {
String sql = "ALTER TABLE " + getTableName() + " ALTER COLUMN " + newDef.columnName;
if (newDef.columnDefault == null) {
@@ -242,7 +270,7 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
Exception {
if ("vacuum".equals(name)) {
- DatabaseQueryUtility.executeUpdate(this,"vacuum " + getTableName());
+ DatabaseQueryUtility.executeUpdate(this, "vacuum " + getTableName());
}
return null;
}
@@ -301,4 +329,4 @@ public class PostgresTableComponent implements DatabaseComponent<PostgresDatabas
return buf.toString();
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
index 3114a60..9d5864a 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresTableDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,23 +13,27 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Connection;
import java.sql.ResultSet;
+import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashSet;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers postgres tables
@@ -37,34 +41,45 @@ import org.rhq.core.util.jdbc.JDBCUtil;
* @author Greg Hinkle
*/
public class PostgresTableDiscoveryComponent implements ResourceDiscoveryComponent<PostgresDatabaseComponent> {
- private static final Log log = LogFactory.getLog(PostgresTableDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresTableDiscoveryComponent.class);
public static final String TABLE_NAMES_QUERY = "select relname from pg_stat_user_tables";
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<PostgresDatabaseComponent> context)
throws Exception {
- log.debug("Discovering postgres tables for " + context.getParentResourceComponent().getDatabaseName() + "...");
- Set<DiscoveredResourceDetails> discoveredTables = new HashSet<DiscoveredResourceDetails>();
-
- Connection connection = context.getParentResourceComponent().getConnection();
- if (connection == null) // For databases we don't have access to don't find the tables
- {
- return discoveredTables;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovering postgres tables for " + context.getParentResourceComponent().getDatabaseName()
+ + "...");
}
+ Set<DiscoveredResourceDetails> discoveredTables = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
+ Statement statement = null;
ResultSet resultSet = null;
- Statement statement = connection.createStatement();
- resultSet = statement.executeQuery(TABLE_NAMES_QUERY);
- while (resultSet.next()) {
- String tableName = resultSet.getString(1);
- DiscoveredResourceDetails service = new DiscoveredResourceDetails(context.getResourceType(), tableName,
- tableName, null, null, null, null);
- service.getPluginConfiguration().put(new PropertySimple("tableName", tableName));
- discoveredTables.add(service);
+ try {
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
+ statement = connection.createStatement();
+ resultSet = statement.executeQuery(TABLE_NAMES_QUERY);
+ while (resultSet.next()) {
+ String tableName = resultSet.getString(1);
+ DiscoveredResourceDetails service = new DiscoveredResourceDetails(context.getResourceType(), tableName,
+ tableName, null, null, null, null);
+ service.getPluginConfiguration().put(new PropertySimple("tableName", tableName));
+ discoveredTables.add(service);
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Found " + discoveredTables.size() + " tables in database "
+ + context.getParentResourceComponent().getDatabaseName());
+ }
+ } catch (SQLException e) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(
+ "Could not find tables in database " + context.getParentResourceComponent().getDatabaseName(), e);
+ }
+ } finally {
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
- JDBCUtil.safeClose(statement, resultSet);
-
return discoveredTables;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
index 92a5203..064c3ec 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,11 +13,15 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
+import static org.rhq.plugins.database.DatabasePluginUtil.getNumericQueryValues;
+import static org.rhq.plugins.database.DatabasePluginUtil.getSingleNumericQueryValue;
+
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -25,6 +29,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Set;
+
import org.rhq.core.domain.configuration.Configuration;
import org.rhq.core.domain.configuration.ConfigurationUpdateStatus;
import org.rhq.core.domain.configuration.PropertySimple;
@@ -37,15 +42,17 @@ import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport;
import org.rhq.core.pluginapi.inventory.DeleteResourceFacet;
import org.rhq.core.pluginapi.inventory.ResourceContext;
import org.rhq.core.pluginapi.measurement.MeasurementFacet;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.ConnectionPoolingSupport;
import org.rhq.plugins.database.DatabaseComponent;
-import org.rhq.plugins.database.DatabaseQueryUtility;
+import org.rhq.plugins.database.DatabasePluginUtil;
+import org.rhq.plugins.database.PooledConnectionProvider;
/**
* @author Greg Hinkle
*/
-public class PostgresUserComponent implements DatabaseComponent<PostgresServerComponent<?>>, MeasurementFacet,
- ConfigurationFacet, DeleteResourceFacet {
+public class PostgresUserComponent implements DatabaseComponent<PostgresServerComponent<?>>, ConnectionPoolingSupport,
+ MeasurementFacet, ConfigurationFacet, DeleteResourceFacet {
+
private ResourceContext<PostgresServerComponent<?>> resourceContext;
public void start(ResourceContext<PostgresServerComponent<?>> resourceContext) {
@@ -56,13 +63,22 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
this.resourceContext = null;
}
+ @Override
+ public boolean supportsConnectionPooling() {
+ return true;
+ }
+
+ @Override
+ public PooledConnectionProvider getPooledConnectionProvider() {
+ return resourceContext.getParentResourceComponent().getPooledConnectionProvider();
+ }
+
public String getUserName() {
return this.resourceContext.getPluginConfiguration().getSimpleValue("userName", null);
}
public AvailabilityType getAvailability() {
- if (DatabaseQueryUtility.getSingleNumericQueryValue(this, "SELECT COUNT(*) FROM PG_ROLES WHERE rolname = ?",
- getUserName()) == 1) {
+ if (getSingleNumericQueryValue(this, "SELECT COUNT(*) FROM PG_ROLES WHERE rolname = ?", getUserName()) == 1) {
return AvailabilityType.UP;
} else {
return AvailabilityType.DOWN;
@@ -78,7 +94,7 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
}
public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception {
- Map<String, Double> values = DatabaseQueryUtility.getNumericQueryValues(this,
+ Map<String, Double> values = getNumericQueryValues(this,
"SELECT (SELECT COUNT(*) FROM pg_stat_activity where usename = ? AND current_query != '<IDLE>') AS active,\n"
+ " (SELECT COUNT(*) FROM pg_stat_activity WHERE usename = ?) AS total", getUserName(), getUserName());
@@ -88,49 +104,49 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
}
public Configuration loadResourceConfiguration() throws Exception {
+ Connection connection = null;
PreparedStatement statement = null;
- ResultSet rs = null;
+ ResultSet resultSet = null;
try {
- statement = getConnection().prepareStatement("SELECT * FROM PG_ROLES WHERE rolname = ?");
+ connection = getPooledConnectionProvider().getPooledConnection();
+ statement = connection.prepareStatement("SELECT * FROM PG_ROLES WHERE rolname = ?");
statement.setString(1, getUserName());
- rs = statement.executeQuery();
- rs.next();
+ resultSet = statement.executeQuery();
+ resultSet.next();
Configuration config = new Configuration();
- config.put(new PropertySimple("user", rs.getString("rolname")));
- config.put(new PropertySimple("canLogin", rs.getBoolean("rolcanlogin")));
- config.put(new PropertySimple("inheritRights", rs.getBoolean("rolinherit")));
- config.put(new PropertySimple("superuser", rs.getBoolean("rolsuper")));
- config.put(new PropertySimple("canCreateDatabaseObjects", rs.getBoolean("rolcreatedb")));
- config.put(new PropertySimple("canCreateRoles", rs.getBoolean("rolcreaterole")));
- config.put(new PropertySimple("canModifyCatalogDirectly", rs.getBoolean("rolcatupdate")));
- config.put(new PropertySimple("connectionLimit", rs.getInt("rolconnlimit")));
+ config.put(new PropertySimple("user", resultSet.getString("rolname")));
+ config.put(new PropertySimple("canLogin", resultSet.getBoolean("rolcanlogin")));
+ config.put(new PropertySimple("inheritRights", resultSet.getBoolean("rolinherit")));
+ config.put(new PropertySimple("superuser", resultSet.getBoolean("rolsuper")));
+ config.put(new PropertySimple("canCreateDatabaseObjects", resultSet.getBoolean("rolcreatedb")));
+ config.put(new PropertySimple("canCreateRoles", resultSet.getBoolean("rolcreaterole")));
+ config.put(new PropertySimple("canModifyCatalogDirectly", resultSet.getBoolean("rolcatupdate")));
+ config.put(new PropertySimple("connectionLimit", resultSet.getInt("rolconnlimit")));
return config;
} catch (Exception e) {
e.printStackTrace();
throw e;
} finally {
- JDBCUtil.safeClose(statement, rs);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
}
public void updateResourceConfiguration(ConfigurationUpdateReport report) {
Configuration config = report.getConfiguration();
+ String sql = getUserSQL(config, UpdateType.ALTER);
+ Connection connection = null;
Statement statement = null;
- String sql = getUserSQL(config, UpdateType.ALTER);
try {
+ connection = getPooledConnectionProvider().getPooledConnection();
statement = getConnection().createStatement();
- int updates = statement.executeUpdate(sql);
- if (updates != 1) {
- report.setErrorMessage("Failed to update user " + config.getSimpleValue("user", null));
- } else {
- report.setStatus(ConfigurationUpdateStatus.SUCCESS);
- }
+ statement.executeUpdate(sql);
+ report.setStatus(ConfigurationUpdateStatus.SUCCESS);
} catch (SQLException e) {
report.setErrorMessageFromThrowable(e);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(connection, statement);
}
}
@@ -145,36 +161,32 @@ public class PostgresUserComponent implements DatabaseComponent<PostgresServerCo
connectionLimit = connLimit.getIntegerValue();
}
- String sql = type.name()
- + " USER "
- + config.getSimpleValue("user", null)
- + " ";
+ String sql = type.name() + " USER " + config.getSimpleValue("user", null) + " ";
if (type != UpdateType.DROP) {
- String password = config.getSimpleValue("password",null);
+ String password = config.getSimpleValue("password", null);
if (password != null && password.length() != 0) {
- sql += " WITH PASSWORD '" + config.getSimpleValue("password",null) + "' ";
+ sql += " WITH PASSWORD '" + config.getSimpleValue("password", null) + "' ";
}
sql += (config.getSimple("canCreateDatabaseObjects").getBooleanValue() ? "CREATEDB " : "NOCREATEDB ");
sql += (config.getSimple("canCreateRoles").getBooleanValue() ? "CREATEUSER " : "NOCREATEUSER ");
- sql += (connectionLimit > -1) ? ("CONNECTION LIMIT " + connectionLimit): "";
+ sql += (connectionLimit > -1) ? ("CONNECTION LIMIT " + connectionLimit) : "";
}
return sql;
}
public void deleteResource() throws Exception {
- Statement statement = null;
String sql = "DROP USER " + this.resourceContext.getResourceKey();
+ Connection connection = null;
+ Statement statement = null;
try {
+ connection = getPooledConnectionProvider().getPooledConnection();
statement = getConnection().createStatement();
-
- // Note: Postgres doesn't seem to return the expected update count of 1 here... but this does work
- // Note: executeUpdate() returns 0 for statements that don't return rows like e.g. drop xxx
statement.executeUpdate(sql);
} finally {
- JDBCUtil.safeClose(statement);
+ DatabasePluginUtil.safeClose(connection, statement);
}
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
index 9c7ae80..6301cdb 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/PostgresUserDiscoveryComponent.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres;
import java.sql.Connection;
@@ -31,7 +32,7 @@ import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryComponent;
import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext;
-import org.rhq.core.util.jdbc.JDBCUtil;
+import org.rhq.plugins.database.DatabasePluginUtil;
/**
* Discovers Postgres Users though shouldn't need super user access
@@ -39,25 +40,22 @@ import org.rhq.core.util.jdbc.JDBCUtil;
* @author Greg Hinkle
*/
public class PostgresUserDiscoveryComponent implements ResourceDiscoveryComponent<PostgresServerComponent<?>> {
- private static final Log log = LogFactory.getLog(PostgresUserDiscoveryComponent.class);
+ private static final Log LOG = LogFactory.getLog(PostgresUserDiscoveryComponent.class);
public static final String USERS_QUERY = "select * from pg_roles";
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<PostgresServerComponent<?>> context)
throws Exception {
- log.debug("Discovering postgres users for " + context.getParentResourceComponent().getJDBCUrl() + "...");
- Set<DiscoveredResourceDetails> discoveredUsers = new HashSet<DiscoveredResourceDetails>();
-
- Connection connection = context.getParentResourceComponent().getConnection();
- if (connection == null) // For databases we don't have access to don't find the tables
- {
- return discoveredUsers;
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Discovering postgres users for " + context.getParentResourceComponent().getJDBCUrl() + "...");
}
+ Set<DiscoveredResourceDetails> discoveredUsers = new HashSet<DiscoveredResourceDetails>();
+ Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
-
try {
+ connection = context.getParentResourceComponent().getPooledConnectionProvider().getPooledConnection();
statement = connection.createStatement();
resultSet = statement.executeQuery(USERS_QUERY);
while (resultSet.next()) {
@@ -68,9 +66,9 @@ public class PostgresUserDiscoveryComponent implements ResourceDiscoveryComponen
discoveredUsers.add(service);
}
} finally {
- JDBCUtil.safeClose(statement, resultSet);
+ DatabasePluginUtil.safeClose(connection, statement, resultSet);
}
return discoveredUsers;
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
index a80652e..3c5765a 100644
--- a/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
+++ b/modules/plugins/postgres/src/main/java/org/rhq/plugins/postgres/util/PostgresqlConfFile.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2012 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres.util;
import java.io.BufferedOutputStream;
@@ -33,11 +34,13 @@ import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.rhq.core.util.jdbc.JDBCUtil;
+
+import org.rhq.core.util.stream.StreamUtil;
/**
* Represents a PostgreSQL configuration file (i.e. postgresql.conf) - provides methods for reading and updating
@@ -99,7 +102,7 @@ public class PostgresqlConfFile {
}
}
} finally {
- JDBCUtil.safeClose(r);
+ StreamUtil.safeClose(r);
}
}
@@ -261,4 +264,4 @@ public class PostgresqlConfFile {
return this.configurationFile.toString();
}
-}
\ No newline at end of file
+}
diff --git a/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java b/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
index bc0d247..c1e9e60 100644
--- a/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
+++ b/modules/plugins/postgres/src/test/java/org/rhq/plugins/postgres/test/PostgresPluginTest.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2008 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -13,9 +13,10 @@
* 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.
+ * along with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
+
package org.rhq.plugins.postgres.test;
import java.io.File;
@@ -25,6 +26,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.annotations.AfterSuite;
@@ -171,4 +173,4 @@ public class PostgresPluginTest {
}
}
}
-}
\ No newline at end of file
+}
10 years, 4 months
[rhq] Branch 'heiko/agentFootprint' - 2 commits - modules/core
by Heiko W. Rupp
modules/core/domain/src/main/java/org/rhq/core/domain/configuration/Configuration.java | 6 +
modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinitionSimple.java | 36 ++++++++--
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java | 24 +++++-
3 files changed, 58 insertions(+), 8 deletions(-)
New commits:
commit f31f3b78faa0af69a0b01088d8db9c2997bafa02
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jan 2 14:17:25 2014 +0100
Compact properties and collections of properties. Detach resource config on receive
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/Configuration.java b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/Configuration.java
index 7113b35..e4b08c4 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/Configuration.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/Configuration.java
@@ -851,6 +851,12 @@ public class Configuration implements Serializable, Cloneable, AbstractPropertyM
}
}
+ public void resize() {
+ Map<String,Property> tmp =new LinkedHashMap<String, Property>(this.properties.size());
+ tmp.putAll(this.properties);
+ this.properties=tmp;
+ }
+
public String getNotes() {
return notes;
}
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
index 746deea..f7fb1a3 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/InventoryManager.java
@@ -65,6 +65,7 @@ import org.rhq.core.clientapi.server.discovery.InvalidInventoryReportException;
import org.rhq.core.clientapi.server.discovery.InventoryReport;
import org.rhq.core.clientapi.server.discovery.StaleTypeException;
import org.rhq.core.domain.configuration.Configuration;
+import org.rhq.core.domain.configuration.Property;
import org.rhq.core.domain.discovery.AvailabilityReport;
import org.rhq.core.domain.discovery.MergeInventoryReportResults;
import org.rhq.core.domain.discovery.MergeInventoryReportResults.ResourceTypeFlyweight;
@@ -90,6 +91,7 @@ import org.rhq.core.pc.agent.AgentRegistrar;
import org.rhq.core.pc.agent.AgentService;
import org.rhq.core.pc.availability.AvailabilityContextImpl;
import org.rhq.core.pc.component.ComponentInvocationContextImpl;
+import org.rhq.core.pc.configuration.ConfigurationCheckExecutor;
import org.rhq.core.pc.content.ContentContextImpl;
import org.rhq.core.pc.drift.sync.DriftSyncManager;
import org.rhq.core.pc.event.EventContextImpl;
@@ -2992,12 +2994,13 @@ public class InventoryManager extends AgentService implements ContainerService,
if (resource.getChildResources().isEmpty()) {
resource.setChildResources(Collections.EMPTY_SET);
}
-/* TODO comment this in again once we understand why this makes the tests fail
+/* TODO the next will make the tests fail
TODO I have not seen issues inside a real running agent - hrupp
-
+*/
Configuration pluginConfiguration = resource.getPluginConfiguration();
if (pluginConfiguration !=null ) {
pluginConfiguration.cleanoutRawConfiguration();
+ compactConfiguration(pluginConfiguration);
}
Configuration resourceConfiguration = resource.getResourceConfiguration();
@@ -3011,8 +3014,23 @@ public class InventoryManager extends AgentService implements ContainerService,
}
}
-*/
+
+ }
+
+ private void compactConfiguration(Configuration config) {
+ if (config==null) {
+ return;
+ }
+ if (config.getProperties()==null) {
+ return;
+ }
+ for (Property prop : config.getProperties()) {
+ if (prop.getName()!=null) {
+ prop.setName(prop.getName().intern());
+ }
+ }
+ config.resize();
}
private void mergeModifiedResources(Set<Integer> modifiedResourceIds) {
commit 4ed365c83b1290b8af5e94903adbd09e36b7269e
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jan 2 14:14:55 2014 +0100
Do some lazy allocations of collections that are seldom used.
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinitionSimple.java b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinitionSimple.java
index ea4080c..01994db 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinitionSimple.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinitionSimple.java
@@ -1,6 +1,6 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
+ * Copyright (C) 2005-2014 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
package org.rhq.core.domain.configuration.definition;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -69,7 +70,7 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
@Cascade( { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
@OneToMany(mappedBy = "propertyDefinitionSimple", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
- private Set<Constraint> constraints = new HashSet<Constraint>();
+ private Set<Constraint> constraints = null;
/**
* The <options> within <property-options> for a <simple-property>
@@ -77,7 +78,7 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
@Cascade( { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
@IndexColumn(name = "order_index")
@OneToMany(mappedBy = "propertyDefinitionSimple", fetch = FetchType.EAGER)
- private List<PropertyDefinitionEnumeration> enumeratedValues = new ArrayList<PropertyDefinitionEnumeration>();
+ private List<PropertyDefinitionEnumeration> enumeratedValues = null;
/**
* This property's default value. This field should have a non-null value for properties whose
@@ -99,7 +100,7 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
@Cascade( { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
@OneToMany(mappedBy = "propertyDefinition", fetch = FetchType.EAGER)
- List<PropertyOptionsSource> optionsSource = new ArrayList<PropertyOptionsSource>();
+ List<PropertyOptionsSource> optionsSource = null;
public PropertyDefinitionSimple(@NotNull String name, String description, boolean required,
@NotNull PropertySimpleType type) {
@@ -126,10 +127,20 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
*/
@NotNull
public Set<Constraint> getConstraints() {
+ if (this.constraints==null) {
+ return Collections.EMPTY_SET;
+ }
return this.constraints;
}
public void setConstraints(Set<Constraint> constraints) {
+ if (constraints==null || constraints.isEmpty()) {
+ this.constraints=null;
+ return;
+ }
+ if (this.constraints==null) {
+ this.constraints = new HashSet<Constraint>(constraints.size());
+ }
for (Constraint constraint : constraints) {
getConstraints().add(constraint);
constraint.setPropertyDefinitionSimple(this);
@@ -137,6 +148,9 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
}
public void addConstraints(Constraint... constraintsToAdd) {
+ if (this.constraints==null) {
+ this.constraints = new HashSet<Constraint>(constraintsToAdd.length);
+ }
for (Constraint constraint : constraintsToAdd) {
getConstraints().add(constraint);
constraint.setPropertyDefinitionSimple(this);
@@ -149,6 +163,9 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
*/
@NotNull
public List<PropertyDefinitionEnumeration> getEnumeratedValues() {
+ if (this.enumeratedValues==null) {
+ return new ArrayList<PropertyDefinitionEnumeration>(1);
+ }
return this.enumeratedValues;
}
@@ -159,6 +176,9 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
}
public void addEnumeratedValues(PropertyDefinitionEnumeration... enumerations) {
+ if (this.enumeratedValues==null) {
+ this.enumeratedValues = new ArrayList<PropertyDefinitionEnumeration>(1);
+ }
for (PropertyDefinitionEnumeration enumeration : enumerations) {
enumeration.setPropertyDefinitionSimple(this);
getEnumeratedValues().add(enumeration);
@@ -170,6 +190,9 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
for (PropertyDefinitionEnumeration enumeration : enumerations) {
getEnumeratedValues().remove(enumeration);
}
+ if (this.enumeratedValues.isEmpty()) {
+ this.enumeratedValues=null;
+ }
ensureOrdering();
}
@@ -243,12 +266,15 @@ public class PropertyDefinitionSimple extends PropertyDefinition {
}
public PropertyOptionsSource getOptionsSource() {
- if (optionsSource.isEmpty())
+ if (optionsSource==null || optionsSource.isEmpty())
return null;
return optionsSource.get(0);
}
public void setOptionsSource(PropertyOptionsSource source) {
+ if (this.optionsSource==null) {
+ optionsSource = new ArrayList<PropertyOptionsSource>(1);
+ }
this.optionsSource.clear();
if (source==null)
return;
10 years, 4 months
[rhq] Branch 'heiko/agentFootprint' - 7 commits - modules/core modules/enterprise modules/plugins
by Heiko W. Rupp
modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinition.java | 10 ++-
modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceContext.java | 6 +-
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AvailabilityExecutor.java | 4 -
modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java | 30 ++++++----
modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh | 6 +-
modules/enterprise/server/appserver/src/main/bin-resources/bin/wrapper/rhq-server-wrapper.conf | 3 +
modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/json/PROPERTY_VALUE.java | 24 +++++---
7 files changed, 56 insertions(+), 27 deletions(-)
New commits:
commit ac34372eebdb57ff9ef9fafcf9842f9a81f20048
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jan 2 11:42:36 2014 +0100
Use a long for scheduleTime and inline the SlimAvailability into fields. Saves another 32bytes per resource
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AvailabilityExecutor.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AvailabilityExecutor.java
index d21505e..a6a87fe 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AvailabilityExecutor.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/AvailabilityExecutor.java
@@ -240,12 +240,12 @@ public class AvailabilityExecutor implements Runnable, Callable<AvailabilityRepo
// See if this resource is scheduled for an avail check
boolean checkAvail = false;
boolean deferToParent = false;
- Long availabilityScheduleTime = resourceContainer.getAvailabilityScheduleTime();
+ long availabilityScheduleTime = resourceContainer.getAvailabilityScheduleTime();
MeasurementScheduleRequest availScheduleRequest = resourceContainer.getAvailabilitySchedule();
// if no avail check is scheduled or we're forcing the check, schedule the next check. Note that a forcedCheck
// is "off-schedule" so we need to push out the next check.
- if ((null == availabilityScheduleTime) || isForced) {
+ if ((0 == availabilityScheduleTime) || isForced) {
// if there is no availability schedule (platform) then just perform the avail check
// (note, platforms always return UP anyway).
if (null == availScheduleRequest) {
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
index 79610b6..aa6fc47 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
@@ -121,9 +121,10 @@ public class ResourceContainer implements Serializable {
private transient TIntObjectMap<Object> proxyCache = new TIntObjectHashMap<Object>(5);
private transient ClassLoader resourceClassLoader;
// the currently known availability
- private transient SlimAvailability availability = new SlimAvailability();
- // the time at which this resource is up for an avail check. null indicates unscheduled.
- private transient Long availabilityScheduleTime;
+ private transient AvailabilityType currentAvailType = AvailabilityType.UNKNOWN;
+ private transient long currentAvailStart;
+ // the time at which this resource is up for an avail check. 0 indicates unscheduled.
+ private transient long availabilityScheduleTime;
private transient AvailabilityProxy availabilityProxy;
/**
@@ -158,9 +159,12 @@ public class ResourceContainer implements Serializable {
public Availability updateAvailability(AvailabilityType availabilityType) {
synchronized (this) {
- this.availability = new SlimAvailability(availabilityType);
+ this.currentAvailType = availabilityType;
+ this.currentAvailStart = System.currentTimeMillis();
- return this.availability.toAvailability(this.resource);
+ Availability tmp = new Availability(this.resource,availabilityType);
+ tmp.setStartTime(this.currentAvailStart);
+ return tmp;
}
}
@@ -177,7 +181,9 @@ public class ResourceContainer implements Serializable {
@Nullable
public Availability getAvailability() {
synchronized (this) {
- return this.availability.toAvailability(this.resource);
+ Availability tmp = new Availability(this.resource,this.currentAvailType);
+ tmp.setStartTime(this.currentAvailStart);
+ return tmp;
}
}
@@ -302,13 +308,13 @@ public class ResourceContainer implements Serializable {
public void setAvailabilitySchedule(MeasurementScheduleRequest availabilitySchedule) {
synchronized (this) {
this.availabilitySchedule = availabilitySchedule;
- // when the schedule is (re)set just null out the schedule time and it will get rescheduled on the
+ // when the schedule is (re)set just 0 out the schedule time and it will get rescheduled on the
// next avail execution.
- this.availabilityScheduleTime = null;
+ this.availabilityScheduleTime = 0;
}
}
- public Long getAvailabilityScheduleTime() {
+ public long getAvailabilityScheduleTime() {
return availabilityScheduleTime;
}
@@ -452,7 +458,7 @@ public class ResourceContainer implements Serializable {
@Override
public String toString() {
- AvailabilityType avail = (this.availability != null) ? this.availability.type : null;
+ AvailabilityType avail = (this.currentAvailType != null) ? this.currentAvailType : null;
return this.getClass().getSimpleName() + "[resource=" + this.resource + ", syncState="
+ this.synchronizationState + ", componentState=" + this.resourceComponentState + ", avail=" + avail + "]";
}
@@ -784,30 +790,4 @@ public class ResourceContainer implements Serializable {
return sb.toString();
}
}
-
- private static class SlimAvailability {
-
- private long startTime;
- private AvailabilityType type = AvailabilityType.UNKNOWN;
-
- private SlimAvailability() {
- type = AvailabilityType.UNKNOWN;
- }
-
- private SlimAvailability(AvailabilityType type) {
- this.type = type;
- this.startTime = System.currentTimeMillis();
- }
-
- private SlimAvailability(long startTime, AvailabilityType type) {
- this.startTime = startTime;
- this.type = type;
- }
-
- Availability toAvailability(Resource r) {
- Availability tmp = new Availability(r,type);
- tmp.setStartTime(startTime);
- return tmp;
- }
- }
}
commit 75a8e23f5b67830336b1bafccf15590688c3787c
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jan 2 09:26:25 2014 +0100
Prevent a possible NPE in tests
diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceContext.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceContext.java
index ca0a048..7d1e3ff 100644
--- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceContext.java
+++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceContext.java
@@ -186,7 +186,11 @@ public class ResourceContext<T extends ResourceComponent<?>> {
this.systemInformation = systemInfo;
this.pluginConfiguration = resource.getPluginConfiguration();
this.baseDataDirectory = baseDataDirectory;
- this.pluginContainerName = pluginContainerName.intern();
+ if (pluginContainerName!=null) {
+ this.pluginContainerName = pluginContainerName.intern();
+ } else {
+ this.pluginContainerName = null;
+ }
this.pluginContainerDeployment = pluginContainerDeployment;
this.temporaryDirectory = temporaryDirectory;
commit bc73492047cbaf6125e9fb2ee2d9c9cd8d1a7dbb
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Thu Jan 2 09:25:59 2014 +0100
Keep a slim version of Availability - saves 32bytes per resource.
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
index 61ebafd..79610b6 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
@@ -121,7 +121,7 @@ public class ResourceContainer implements Serializable {
private transient TIntObjectMap<Object> proxyCache = new TIntObjectHashMap<Object>(5);
private transient ClassLoader resourceClassLoader;
// the currently known availability
- private transient Availability availability;
+ private transient SlimAvailability availability = new SlimAvailability();
// the time at which this resource is up for an avail check. null indicates unscheduled.
private transient Long availabilityScheduleTime;
private transient AvailabilityProxy availabilityProxy;
@@ -158,8 +158,9 @@ public class ResourceContainer implements Serializable {
public Availability updateAvailability(AvailabilityType availabilityType) {
synchronized (this) {
- this.availability = new Availability(this.resource, availabilityType);
- return this.availability;
+ this.availability = new SlimAvailability(availabilityType);
+
+ return this.availability.toAvailability(this.resource);
}
}
@@ -176,7 +177,7 @@ public class ResourceContainer implements Serializable {
@Nullable
public Availability getAvailability() {
synchronized (this) {
- return this.availability;
+ return this.availability.toAvailability(this.resource);
}
}
@@ -451,7 +452,7 @@ public class ResourceContainer implements Serializable {
@Override
public String toString() {
- AvailabilityType avail = (this.availability != null) ? this.availability.getAvailabilityType() : null;
+ AvailabilityType avail = (this.availability != null) ? this.availability.type : null;
return this.getClass().getSimpleName() + "[resource=" + this.resource + ", syncState="
+ this.synchronizationState + ", componentState=" + this.resourceComponentState + ", avail=" + avail + "]";
}
@@ -783,4 +784,30 @@ public class ResourceContainer implements Serializable {
return sb.toString();
}
}
+
+ private static class SlimAvailability {
+
+ private long startTime;
+ private AvailabilityType type = AvailabilityType.UNKNOWN;
+
+ private SlimAvailability() {
+ type = AvailabilityType.UNKNOWN;
+ }
+
+ private SlimAvailability(AvailabilityType type) {
+ this.type = type;
+ this.startTime = System.currentTimeMillis();
+ }
+
+ private SlimAvailability(long startTime, AvailabilityType type) {
+ this.startTime = startTime;
+ this.type = type;
+ }
+
+ Availability toAvailability(Resource r) {
+ Availability tmp = new Availability(r,type);
+ tmp.setStartTime(startTime);
+ return tmp;
+ }
+ }
}
commit 4254af520b01bf0b3520a463a9ee578c1d99edd4
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 31 14:16:02 2013 +0100
Re-package schedules in a Trove HashSet; saves ~200bytes per resource (depending on number of schedules)
diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
index 39e6f1a..61ebafd 100644
--- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
+++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/inventory/ResourceContainer.java
@@ -42,6 +42,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
+import gnu.trove.set.hash.THashSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -253,7 +254,7 @@ public class ResourceContainer implements Serializable {
public void setMeasurementSchedule(Set<MeasurementScheduleRequest> measurementSchedule) {
synchronized (this) {
- this.measurementSchedule = measurementSchedule;
+ this.measurementSchedule = new THashSet<MeasurementScheduleRequest>(measurementSchedule);
// this should not happen but if it does, protect against it because it will sink the agent
if (null != this.measurementSchedule) {
commit d6af564d06ce769d1fccd66dc3b75f2fcc251982
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 31 13:59:59 2013 +0100
Increase the interning map size on the server as well.
diff --git a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
index 9856190..d30bef2 100755
--- a/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
+++ b/modules/enterprise/server/appserver/src/main/bin-resources/bin/internal/rhq-server.sh
@@ -86,8 +86,8 @@
# killed if it is still running after the
# RHQ_SERVER_STOP_DELAY. If this is not
# defined or set to "false" the script
-# will exit with error code 127.
-#
+# will exit with error code 127.
+#
#
#
# If the embedded JRE is to be used but is not available, the fallback
@@ -392,7 +392,7 @@ fi
# Add the JVM opts that we always want to specify, whether or not the user set RHQ_SERVER_JAVA_OPTS.
# Note that the double equals for the policy file specification IS INTENTIONAL
_HTTP_COMPRESSION="-Dorg.apache.coyote.http11.Http11Protocol.COMPRESSION=on -Dorg.apache.coyote.http11.Http11Protocol.COMPRESSION_MIME_TYPES=text/javascript,text/css,text/html"
-RHQ_SERVER_JAVA_OPTS="-Dapp.name=rhq-server ${RHQ_SERVER_JAVA_OPTS} -Drhq.server.home=${RHQ_SERVER_HOME} -Djboss.server.log.dir=${_LOG_DIR_PATH} -Djava.awt.headless=true -Dsun.lang.ClassLoader.allowArraySyntax=true -Djboss.server.default.config=standalone-full.xml -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.security.manager -Djava.security.policy==${RHQ_SERVER_HOME}/bin/internal/rhq-server.security-policy ${_HTTP_COMPRESSION} ${_JBOSS_DEBUG_LOGGING}"
+RHQ_SERVER_JAVA_OPTS="-Dapp.name=rhq-server ${RHQ_SERVER_JAVA_OPTS} -Drhq.server.home=${RHQ_SERVER_HOME} -Djboss.server.log.dir=${_LOG_DIR_PATH} -Djava.awt.headless=true -Dsun.lang.ClassLoader.allowArraySyntax=true -Djboss.server.default.config=standalone-full.xml -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.security.manager -Djava.security.policy==${RHQ_SERVER_HOME}/bin/internal/rhq-server.security-policy ${_HTTP_COMPRESSION} ${_JBOSS_DEBUG_LOGGING} -XX:StringTableSize=1000003"
debug_msg "RHQ_SERVER_JAVA_OPTS: $RHQ_SERVER_JAVA_OPTS"
debug_msg "RHQ_SERVER_ADDITIONAL_JAVA_OPTS: $RHQ_SERVER_ADDITIONAL_JAVA_OPTS"
diff --git a/modules/enterprise/server/appserver/src/main/bin-resources/bin/wrapper/rhq-server-wrapper.conf b/modules/enterprise/server/appserver/src/main/bin-resources/bin/wrapper/rhq-server-wrapper.conf
index 8546c02..9bfecee 100644
--- a/modules/enterprise/server/appserver/src/main/bin-resources/bin/wrapper/rhq-server-wrapper.conf
+++ b/modules/enterprise/server/appserver/src/main/bin-resources/bin/wrapper/rhq-server-wrapper.conf
@@ -98,6 +98,9 @@ wrapper.java.additional.23="-Djava.io.tmpdir=%RHQ_SERVER_HOME%/temp"
# Don't need these now, but this is commented out in case we need to add an endorsed dir in the future
#wrapper.java.additional.26="-Djava.endorsed.dirs=%RHQ_SERVER_HOME%/jbossas/lib/endorsed"
+# For string interning
+wrapper.java.additional.27=-XX:StringTableSize=1000003
+
# We want to make sure the Server starts in the JBossAS bin directory
wrapper.working.dir=%RHQ_SERVER_HOME%/jbossas/bin
commit 09182720b47c6c08d33573f6792ea6fe8f6cb2fe
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 31 13:59:24 2013 +0100
Display names are very often the same, so we can intern them to save space.
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinition.java b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinition.java
index db22c1d..0d3b144 100644
--- a/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinition.java
+++ b/modules/core/domain/src/main/java/org/rhq/core/domain/configuration/definition/PropertyDefinition.java
@@ -120,7 +120,9 @@ public class PropertyDefinition implements Serializable {
}
protected PropertyDefinition(@NotNull String name, String description, boolean required) {
- this.name = name;
+ if (name!=null) {
+ this.name = name.intern();
+ }
this.description = description;
this.required = required;
}
@@ -164,7 +166,11 @@ public class PropertyDefinition implements Serializable {
}
public void setDisplayName(String displayName) {
- this.displayName = displayName;
+ if (displayName!=null) {
+ this.displayName = displayName.intern();
+ } else {
+ this.displayName = displayName;
+ }
}
public String getDescription() {
commit 42cf83c6f37788287aafca53a513c4967d9dda74
Author: Heiko W. Rupp <hwr(a)redhat.com>
Date: Tue Dec 31 10:10:59 2013 +0100
Keys are very often the same, so we can intern them to save space.
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/json/PROPERTY_VALUE.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/json/PROPERTY_VALUE.java
index c9c9ac9..887171b 100644
--- a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/json/PROPERTY_VALUE.java
+++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/json/PROPERTY_VALUE.java
@@ -1,20 +1,24 @@
/*
* RHQ Management Platform
- * Copyright (C) 2005-2011 Red Hat, Inc.
+ * Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation version 2 of the License.
+ * 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 for more details.
+ * 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
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * 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.plugins.jbossas7.json;
@@ -37,7 +41,9 @@ public class PROPERTY_VALUE implements Serializable{
private String value;
public PROPERTY_VALUE(String key, String value) {
- this.key = key;
+ if (key!=null) {
+ this.key = key.intern();
+ }
this.value = value;
}
@@ -50,7 +56,9 @@ public class PROPERTY_VALUE implements Serializable{
}
public void setKey(String key) {
- this.key = key;
+ if (key!=null) {
+ this.key = key.intern();
+ }
}
public void setValue(String value) {
10 years, 4 months