modules/core/domain/src/main/java/org/rhq/core/domain/resource/Resource.java | 23 modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceType.java | 2 modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceDiscoveryComponent.java | 5 modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java | 17 modules/enterprise/agent/src/etc/rhq-agent.bat | 446 +++---- modules/enterprise/agent/src/etc/rhq-agent.sh | 9 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/AbstractMessagingDiscoveryComponent.java | 2 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASServerComponent.java | 2 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASTomcatConnectorDiscoveryComponent.java | 2 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/WarComponent.java | 8 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/DeploymentUtility.java | 4 modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/WarDiscoveryHelper.java | 3 modules/plugins/jboss-cache-v3/src/main/java/org/rhq/plugins/jbosscache3/JBossCacheDiscoveryComponent.java | 2 modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheComponent.java | 2 modules/plugins/jmx/pom.xml | 39 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/EmbeddedJMXServerDiscoveryComponent.java | 1 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/InternalJMXServerDiscoveryComponent.java | 30 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXDiscoveryComponent.java | 599 ++++++---- modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXServerComponent.java | 181 --- modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/MBeanResourceDiscoveryComponent.java | 6 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ObjectNameQueryUtility.java | 218 --- modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ParentDefinedJMXServerNamingUtility.java | 57 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ConnectionProviderFactory.java | 218 +++ modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmResourceKey.java | 179 ++ modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmUtility.java | 118 + modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ObjectNameQueryUtility.java | 218 +++ modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ParentDefinedJMXServerNamingUtility.java | 57 modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/Socket.java | 120 ++ modules/plugins/jmx/src/main/resources/META-INF/rhq-plugin.xml | 8 modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/JMXPluginTest.java | 211 ++- modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/ObjectNameQueryUtilityTest.java | 2 modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java | 2 modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorDiscoveryComponent.java | 2 modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java | 2 34 files changed, 1835 insertions(+), 960 deletions(-)
New commits: commit 4673ea4fd280f1073bd92d2aed26ea34c8c23474 Author: Ian Springer ian.springer@redhat.com Date: Mon Jan 23 14:20:29 2012 -0500
[BZ 703557] when possible, auto-discover JVM's not exposing JMX Remoting; as part of this, include the JDK tools.jar in the Agent's classpath when it's available from the Agent's JDK (https://bugzilla.redhat.com/show_bug.cgi?id=703557)
diff --git a/modules/enterprise/agent/src/etc/rhq-agent.bat b/modules/enterprise/agent/src/etc/rhq-agent.bat index 59b7676..fc8bdf1 100644 --- a/modules/enterprise/agent/src/etc/rhq-agent.bat +++ b/modules/enterprise/agent/src/etc/rhq-agent.bat @@ -1,219 +1,227 @@ -@echo off - -rem =========================================================================== -rem RHQ Agent Windows Startup Script -rem -rem This file is used to execute the RHQ Agent on a Windows platform. -rem Run this script with the --help option for the runtime options. -rem -rem Note that this script can also be used to simply set environment variables -rem that define the agent's environment but it will not actually run the agent. -rem Pass in _SETENV_ONLY as the first argument to this script if you want this -rem behavior. -rem -rem This script is customizable by setting certain environment variables, which -rem are described in comments in rhq-agent-env.bat. The variables can also be -rem set via rhq-agent-env.bat, which is sourced by this script. -rem -rem If the embedded JRE is to be used but is not available, the fallback -rem JRE to be used will be determined by the JAVA_HOME environment variable. -rem =========================================================================== - -setlocal - -if "%1"=="_SETENV_ONLY" ( - set _SETENV_ONLY=true -) else ( - title RHQ Agent -) - -rem if debug variable is set, it is assumed to be on, unless its value is false -if "%RHQ_AGENT_DEBUG%" == "false" ( - set RHQ_AGENT_DEBUG= -) - -rem ---------------------------------------------------------------------- -rem Change directory so the current directory is the Agent home. -rem Here we assume this script is a child directory of the Agent home -rem We also assume our custom environment script is located in the same -rem place as this script. -rem ---------------------------------------------------------------------- - -set RHQ_AGENT_BIN_DIR_PATH=%~dp0 - -if exist "%RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat" ( - if defined RHQ_AGENT_DEBUG echo Loading environment script: %RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat - call "%RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat" %* -) else ( - if defined RHQ_AGENT_DEBUG echo No environment script found at: %RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat -) - -rem if debug variable is set, it is assumed to be on, unless its value is false -if "%RHQ_AGENT_DEBUG%" == "false" ( - set RHQ_AGENT_DEBUG= -) - -rem if sigar debug variable is set, it is assumed to be on, unless its value is false -if "%RHQ_AGENT_SIGAR_DEBUG%" == "false" ( - set RHQ_AGENT_SIGAR_DEBUG= -) - -if not defined RHQ_AGENT_HOME ( - cd "%RHQ_AGENT_BIN_DIR_PATH%.." -) else ( - cd "%RHQ_AGENT_HOME%" || ( - echo Cannot go to the RHQ_AGENT_HOME directory: %RHQ_AGENT_HOME% - exit /B 1 - ) -) - -set RHQ_AGENT_HOME=%CD% - -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_HOME: %RHQ_AGENT_HOME% - -rem ---------------------------------------------------------------------- -rem Find the Java executable and verify we have a VM available -rem ---------------------------------------------------------------------- - -if not defined RHQ_AGENT_JAVA_EXE_FILE_PATH ( - if not defined RHQ_AGENT_JAVA_HOME call :prepare_embedded_jre -) - -if not defined RHQ_AGENT_JAVA_EXE_FILE_PATH ( - set RHQ_AGENT_JAVA_EXE_FILE_PATH=%RHQ_AGENT_JAVA_HOME%\bin\java.exe -) - -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_HOME: %RHQ_AGENT_JAVA_HOME% -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_EXE_FILE_PATH: %RHQ_AGENT_JAVA_EXE_FILE_PATH% - -if not exist "%RHQ_AGENT_JAVA_EXE_FILE_PATH%" ( - echo There is no JVM available. - echo Please set RHQ_AGENT_JAVA_HOME or RHQ_AGENT_JAVA_EXE_FILE_PATH appropriately. - exit /B 1 -) - -rem ---------------------------------------------------------------------- -rem Prepare the classpath -rem ---------------------------------------------------------------------- - -set CLASSPATH= -call :append_classpath "%RHQ_AGENT_HOME%\conf" -for /R "%RHQ_AGENT_HOME%\lib" %%G in ("*.jar") do ( - call :append_classpath "%%G" - if defined RHQ_AGENT_DEBUG echo CLASSPATH entry: %%G -) - -rem ---------------------------------------------------------------------- -rem Prepare the VM command line options to be passed in -rem ---------------------------------------------------------------------- - -if not defined RHQ_AGENT_JAVA_OPTS ( - set RHQ_AGENT_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true -) -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_OPTS: %RHQ_AGENT_JAVA_OPTS% - -if "%RHQ_AGENT_JAVA_ENDORSED_DIRS%" == "none" ( - if defined RHQ_AGENT_DEBUG echo Not explicitly setting java.endorsed.dirs - goto :skip_java_endorsed_dirs -) -if not defined RHQ_AGENT_JAVA_ENDORSED_DIRS set RHQ_AGENT_JAVA_ENDORSED_DIRS=%RHQ_AGENT_HOME%\lib\endorsed -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_ENDORSED_DIRS: %RHQ_AGENT_JAVA_ENDORSED_DIRS% -set _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=%RHQ_AGENT_JAVA_ENDORSED_DIRS%" -:skip_java_endorsed_dirs - -if "%RHQ_AGENT_JAVA_LIBRARY_PATH%" == "none" ( - if defined RHQ_AGENT_DEBUG echo Not explicitly setting java.library.path - goto :skip_java_library_path -) -if not defined RHQ_AGENT_JAVA_LIBRARY_PATH set RHQ_AGENT_JAVA_LIBRARY_PATH=%RHQ_AGENT_HOME%\lib -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_LIBRARY_PATH: %RHQ_AGENT_JAVA_LIBRARY_PATH% -set _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=%RHQ_AGENT_JAVA_LIBRARY_PATH%" -:skip_java_library_path - -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_ADDITIONAL_JAVA_OPTS: %RHQ_AGENT_ADDITIONAL_JAVA_OPTS% - -rem ---------------------------------------------------------------------- -rem Prepare the command line arguments passed to the RHQ Agent -rem ---------------------------------------------------------------------- - -if not defined RHQ_AGENT_CMDLINE_OPTS ( - set RHQ_AGENT_CMDLINE_OPTS=%* -) - -if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_CMDLINE_OPTS: %RHQ_AGENT_CMDLINE_OPTS% - -rem ---------------------------------------------------------------------- -rem Execute the VM which starts the agent -rem ---------------------------------------------------------------------- - -if defined RHQ_AGENT_DEBUG ( - set _LOG_CONFIG=-Dlog4j.configuration=log4j-debug.xml -Di18nlog.dump-stack-traces=true -) else ( - set _LOG_CONFIG=-Dlog4j.configuration=log4j.xml -) - -rem if sigar debug is enabled, the log configuration is different - sigar debugging is noisy, so its got its own debug var -if defined RHQ_AGENT_SIGAR_DEBUG ( - set _LOG_CONFIG=%_LOG_CONFIG% -Dsigar.nativeLogging=true -) - -rem to support other agents/plugin containers, allow the caller to override the main classname -if not defined RHQ_AGENT_MAINCLASS ( - set RHQ_AGENT_MAINCLASS=org.rhq.enterprise.agent.AgentMain -) - -set CMD="%RHQ_AGENT_JAVA_EXE_FILE_PATH%" %_JAVA_ENDORSED_DIRS_OPT% %_JAVA_LIBRARY_PATH_OPT% %RHQ_AGENT_JAVA_OPTS% %RHQ_AGENT_ADDITIONAL_JAVA_OPTS% %_LOG_CONFIG% -cp "%CLASSPATH%" %RHQ_AGENT_MAINCLASS% %RHQ_AGENT_CMDLINE_OPTS% - -if not defined _SETENV_ONLY ( - rem log4j 1.2.8 does not create the directory for us (later versions do) - if not exist "%RHQ_AGENT_HOME%\logs" ( - mkdir "%RHQ_AGENT_HOME%\logs" - ) - if defined RHQ_AGENT_DEBUG ( - echo Executing the agent with this command line: - echo %CMD% - ) - cmd.exe /S /C "%CMD%" -) - -goto :done - -rem ---------------------------------------------------------------------- -rem CALL subroutine that appends the first argument to CLASSPATH -rem ---------------------------------------------------------------------- - -:append_classpath -set _entry=%1 -if not defined CLASSPATH ( - set CLASSPATH=%_entry:"=% -) else ( - set CLASSPATH=%CLASSPATH%;%_entry:"=% -) -goto :eof - -rem ---------------------------------------------------------------------- -rem CALL subroutine that prepares to use the embedded JRE -rem ---------------------------------------------------------------------- - -:prepare_embedded_jre -set RHQ_AGENT_JAVA_HOME=%RHQ_AGENT_HOME%\jre -if defined RHQ_AGENT_DEBUG echo Using the embedded JRE -if not exist "%RHQ_AGENT_JAVA_HOME%" ( - if defined RHQ_AGENT_DEBUG echo No embedded JRE found - will try to use JAVA_HOME: %JAVA_HOME% - set RHQ_AGENT_JAVA_HOME=%JAVA_HOME% -) -goto :eof - -rem ---------------------------------------------------------------------- -rem CALL subroutine that exits this script normally -rem ---------------------------------------------------------------------- - -:done -if defined RHQ_AGENT_DEBUG echo %0 done. -if defined _SETENV_ONLY ( -endlocal & SET "RHQ_AGENT_HOME=%RHQ_AGENT_HOME%" & SET "RHQ_AGENT_JAVA_EXE_FILE_PATH=%RHQ_AGENT_JAVA_EXE_FILE_PATH%" & SET "RHQ_AGENT_JAVA_OPTS=%RHQ_AGENT_JAVA_OPTS%" & SET "RHQ_AGENT_ADDITIONAL_JAVA_OPTS=%RHQ_AGENT_ADDITIONAL_JAVA_OPTS%" & SET "RHQ_AGENT_BIN_DIR_PATH=%RHQ_AGENT_BIN_DIR_PATH%" -) else ( -endlocal -) - -exit /B 0 +@echo off + +rem =========================================================================== +rem RHQ Agent Windows Startup Script +rem +rem This file is used to execute the RHQ Agent on a Windows platform. +rem Run this script with the --help option for the runtime options. +rem +rem Note that this script can also be used to simply set environment variables +rem that define the agent's environment but it will not actually run the agent. +rem Pass in _SETENV_ONLY as the first argument to this script if you want this +rem behavior. +rem +rem This script is customizable by setting certain environment variables, which +rem are described in comments in rhq-agent-env.bat. The variables can also be +rem set via rhq-agent-env.bat, which is sourced by this script. +rem +rem If the embedded JRE is to be used but is not available, the fallback +rem JRE to be used will be determined by the JAVA_HOME environment variable. +rem =========================================================================== + +setlocal + +if "%1"=="_SETENV_ONLY" ( + set _SETENV_ONLY=true +) else ( + title RHQ Agent +) + +rem if debug variable is set, it is assumed to be on, unless its value is false +if "%RHQ_AGENT_DEBUG%" == "false" ( + set RHQ_AGENT_DEBUG= +) + +rem ---------------------------------------------------------------------- +rem Change directory so the current directory is the Agent home. +rem Here we assume this script is a child directory of the Agent home +rem We also assume our custom environment script is located in the same +rem place as this script. +rem ---------------------------------------------------------------------- + +set RHQ_AGENT_BIN_DIR_PATH=%~dp0 + +if exist "%RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat" ( + if defined RHQ_AGENT_DEBUG echo Loading environment script: %RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat + call "%RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat" %* +) else ( + if defined RHQ_AGENT_DEBUG echo No environment script found at: %RHQ_AGENT_BIN_DIR_PATH%\rhq-agent-env.bat +) + +rem if debug variable is set, it is assumed to be on, unless its value is false +if "%RHQ_AGENT_DEBUG%" == "false" ( + set RHQ_AGENT_DEBUG= +) + +rem if sigar debug variable is set, it is assumed to be on, unless its value is false +if "%RHQ_AGENT_SIGAR_DEBUG%" == "false" ( + set RHQ_AGENT_SIGAR_DEBUG= +) + +if not defined RHQ_AGENT_HOME ( + cd "%RHQ_AGENT_BIN_DIR_PATH%.." +) else ( + cd "%RHQ_AGENT_HOME%" || ( + echo Cannot go to the RHQ_AGENT_HOME directory: %RHQ_AGENT_HOME% + exit /B 1 + ) +) + +set RHQ_AGENT_HOME=%CD% + +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_HOME: %RHQ_AGENT_HOME% + +rem ---------------------------------------------------------------------- +rem Find the Java executable and verify we have a VM available +rem ---------------------------------------------------------------------- + +if not defined RHQ_AGENT_JAVA_EXE_FILE_PATH ( + if not defined RHQ_AGENT_JAVA_HOME call :prepare_embedded_jre +) + +if not defined RHQ_AGENT_JAVA_EXE_FILE_PATH ( + set RHQ_AGENT_JAVA_EXE_FILE_PATH=%RHQ_AGENT_JAVA_HOME%\bin\java.exe +) + +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_HOME: %RHQ_AGENT_JAVA_HOME% +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_EXE_FILE_PATH: %RHQ_AGENT_JAVA_EXE_FILE_PATH% + +if not exist "%RHQ_AGENT_JAVA_EXE_FILE_PATH%" ( + echo There is no JVM available. + echo Please set RHQ_AGENT_JAVA_HOME or RHQ_AGENT_JAVA_EXE_FILE_PATH appropriately. + exit /B 1 +) + +rem ---------------------------------------------------------------------- +rem Prepare the classpath +rem ---------------------------------------------------------------------- + +set CLASSPATH= +call :append_classpath "%RHQ_AGENT_HOME%\conf" +for /R "%RHQ_AGENT_HOME%\lib" %%G in ("*.jar") do ( + call :append_classpath "%%G" + if defined RHQ_AGENT_DEBUG echo CLASSPATH entry: %%G +) +for %%G in ("%RHQ_AGENT_JAVA_HOME%\lib\tools.jar" "%RHQ_AGENT_JAVA_HOME%..\lib\tools.jar") do ( + if exist "%%G" ( + call :append_classpath "%%G" + if defined RHQ_AGENT_DEBUG echo CLASSPATH entry: %%G + goto end_classpath + ) +) +:end_classpath + +rem ---------------------------------------------------------------------- +rem Prepare the VM command line options to be passed in +rem ---------------------------------------------------------------------- + +if not defined RHQ_AGENT_JAVA_OPTS ( + set RHQ_AGENT_JAVA_OPTS=-Xms64m -Xmx128m -Djava.net.preferIPv4Stack=true +) +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_OPTS: %RHQ_AGENT_JAVA_OPTS% + +if "%RHQ_AGENT_JAVA_ENDORSED_DIRS%" == "none" ( + if defined RHQ_AGENT_DEBUG echo Not explicitly setting java.endorsed.dirs + goto :skip_java_endorsed_dirs +) +if not defined RHQ_AGENT_JAVA_ENDORSED_DIRS set RHQ_AGENT_JAVA_ENDORSED_DIRS=%RHQ_AGENT_HOME%\lib\endorsed +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_ENDORSED_DIRS: %RHQ_AGENT_JAVA_ENDORSED_DIRS% +set _JAVA_ENDORSED_DIRS_OPT="-Djava.endorsed.dirs=%RHQ_AGENT_JAVA_ENDORSED_DIRS%" +:skip_java_endorsed_dirs + +if "%RHQ_AGENT_JAVA_LIBRARY_PATH%" == "none" ( + if defined RHQ_AGENT_DEBUG echo Not explicitly setting java.library.path + goto :skip_java_library_path +) +if not defined RHQ_AGENT_JAVA_LIBRARY_PATH set RHQ_AGENT_JAVA_LIBRARY_PATH=%RHQ_AGENT_HOME%\lib +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_JAVA_LIBRARY_PATH: %RHQ_AGENT_JAVA_LIBRARY_PATH% +set _JAVA_LIBRARY_PATH_OPT="-Djava.library.path=%RHQ_AGENT_JAVA_LIBRARY_PATH%" +:skip_java_library_path + +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_ADDITIONAL_JAVA_OPTS: %RHQ_AGENT_ADDITIONAL_JAVA_OPTS% + +rem ---------------------------------------------------------------------- +rem Prepare the command line arguments passed to the RHQ Agent +rem ---------------------------------------------------------------------- + +if not defined RHQ_AGENT_CMDLINE_OPTS ( + set RHQ_AGENT_CMDLINE_OPTS=%* +) + +if defined RHQ_AGENT_DEBUG echo RHQ_AGENT_CMDLINE_OPTS: %RHQ_AGENT_CMDLINE_OPTS% + +rem ---------------------------------------------------------------------- +rem Execute the VM which starts the agent +rem ---------------------------------------------------------------------- + +if defined RHQ_AGENT_DEBUG ( + set _LOG_CONFIG=-Dlog4j.configuration=log4j-debug.xml -Di18nlog.dump-stack-traces=true +) else ( + set _LOG_CONFIG=-Dlog4j.configuration=log4j.xml +) + +rem if sigar debug is enabled, the log configuration is different - sigar debugging is noisy, so it has its own debug var +if defined RHQ_AGENT_SIGAR_DEBUG ( + set _LOG_CONFIG=%_LOG_CONFIG% -Dsigar.nativeLogging=true +) + +rem to support other agents/plugin containers, allow the caller to override the main classname +if not defined RHQ_AGENT_MAINCLASS ( + set RHQ_AGENT_MAINCLASS=org.rhq.enterprise.agent.AgentMain +) + +set CMD="%RHQ_AGENT_JAVA_EXE_FILE_PATH%" %_JAVA_ENDORSED_DIRS_OPT% %_JAVA_LIBRARY_PATH_OPT% %RHQ_AGENT_JAVA_OPTS% %RHQ_AGENT_ADDITIONAL_JAVA_OPTS% %_LOG_CONFIG% -cp "%CLASSPATH%" %RHQ_AGENT_MAINCLASS% %RHQ_AGENT_CMDLINE_OPTS% + +if not defined _SETENV_ONLY ( + rem log4j 1.2.8 does not create the directory for us (later versions do) + if not exist "%RHQ_AGENT_HOME%\logs" ( + mkdir "%RHQ_AGENT_HOME%\logs" + ) + if defined RHQ_AGENT_DEBUG ( + echo Executing the agent with this command line: + echo %CMD% + ) + cmd.exe /S /C "%CMD%" +) + +goto :done + +rem ---------------------------------------------------------------------- +rem CALL subroutine that appends the first argument to CLASSPATH +rem ---------------------------------------------------------------------- + +:append_classpath +set _entry=%1 +if not defined CLASSPATH ( + set CLASSPATH=%_entry:"=% +) else ( + set CLASSPATH=%CLASSPATH%;%_entry:"=% +) +goto :eof + +rem ---------------------------------------------------------------------- +rem CALL subroutine that prepares to use the embedded JRE +rem ---------------------------------------------------------------------- + +:prepare_embedded_jre +set RHQ_AGENT_JAVA_HOME=%RHQ_AGENT_HOME%\jre +if defined RHQ_AGENT_DEBUG echo Using the embedded JRE +if not exist "%RHQ_AGENT_JAVA_HOME%" ( + if defined RHQ_AGENT_DEBUG echo No embedded JRE found - will try to use JAVA_HOME: %JAVA_HOME% + set RHQ_AGENT_JAVA_HOME=%JAVA_HOME% +) +goto :eof + +rem ---------------------------------------------------------------------- +rem CALL subroutine that exits this script normally +rem ---------------------------------------------------------------------- + +:done +if defined RHQ_AGENT_DEBUG echo %0 done. +if defined _SETENV_ONLY ( +endlocal & SET "RHQ_AGENT_HOME=%RHQ_AGENT_HOME%" & SET "RHQ_AGENT_JAVA_EXE_FILE_PATH=%RHQ_AGENT_JAVA_EXE_FILE_PATH%" & SET "RHQ_AGENT_JAVA_OPTS=%RHQ_AGENT_JAVA_OPTS%" & SET "RHQ_AGENT_ADDITIONAL_JAVA_OPTS=%RHQ_AGENT_ADDITIONAL_JAVA_OPTS%" & SET "RHQ_AGENT_BIN_DIR_PATH=%RHQ_AGENT_BIN_DIR_PATH%" +) else ( +endlocal +) + +exit /B 0 diff --git a/modules/enterprise/agent/src/etc/rhq-agent.sh b/modules/enterprise/agent/src/etc/rhq-agent.sh index e0122bd..45b99a2 100755 --- a/modules/enterprise/agent/src/etc/rhq-agent.sh +++ b/modules/enterprise/agent/src/etc/rhq-agent.sh @@ -151,6 +151,13 @@ for _JAR in $_JAR_FILES ; do fi debug_msg "CLASSPATH entry: $_JAR" done +for _TOOLS_JAR in "${RHQ_AGENT_JAVA_HOME}/lib/tools.jar" "${RHQ_AGENT_JAVA_HOME}/../lib/tools.jar"; do + if [ -f "${_TOOLS_JAR}" ]; then + debug_msg "CLASSPATH entry: ${_TOOLS_JAR}" + CLASSPATH="${CLASSPATH}:${_TOOLS_JAR}" + break + fi +done
# ---------------------------------------------------------------------- # Prepare the VM command line options to be passed in @@ -236,7 +243,7 @@ if [ "x$RHQ_AGENT_DEBUG" != "x" ]; then fi fi
-# if sigar debug is enabled, the log configuration is different - sigar debugging is noisy, so its got its own debug var +# if sigar debug is enabled, the log configuration is different - sigar debugging is noisy, so it has its own debug var if [ "x$RHQ_AGENT_SIGAR_DEBUG" != "x" ]; then if [ "$RHQ_AGENT_SIGAR_DEBUG" != "false" ]; then _LOG_CONFIG="$_LOG_CONFIG -Dsigar.nativeLogging=true" diff --git a/modules/plugins/jmx/pom.xml b/modules/plugins/jmx/pom.xml index e92eb21..8780b32 100644 --- a/modules/plugins/jmx/pom.xml +++ b/modules/plugins/jmx/pom.xml @@ -16,11 +16,23 @@ <description>a plugin for managing a generic JMX-enabled application</description>
<dependencies> + <dependency> <groupId>mc4j</groupId> <artifactId>org-mc4j-ems</artifactId> <!-- NOTE: The version is defined in the root POM's dependencyManagement section. --> </dependency> + + <!-- needed for the JVM Attach API. NOTE: we do not include tools.jar in the plugin jar's lib dir, because if it + is available at runtime, rhq-agent.sh/rhq-agent.bat will add it to the Agent's classpath. --> + <dependency> + <groupId>com.sun</groupId> + <artifactId>tools</artifactId> + <version>1.6.0</version> + <scope>system</scope> + <systemPath>${java.home}/../lib/tools.jar</systemPath> + </dependency> + </dependencies>
<build> @@ -48,8 +60,8 @@ </execution> </executions> </plugin> -</plugins> - </build> + </plugins> + </build>
<profiles> <profile> @@ -146,28 +158,7 @@
</plugins> </build> -</profile> - <!-- This includes the tools.jar in the system classpath to avoid the unsatisfied link error on - tools.jar dependencies that EMS was trying to load multiple times. --> - <profile> - <id>default-tools.jar</id> - <activation> - <property> - <name>java.vendor</name> - <value>Sun Microsystems Inc.</value> - </property> - </activation> - - <dependencies> - <dependency> - <groupId>com.sun</groupId> - <artifactId>tools</artifactId> - <version>1.4.2</version> - <scope>system</scope> - <systemPath>${java.home}/../lib/tools.jar</systemPath> - </dependency> - </dependencies> - </profile> + </profile>
<profile> <id>dev</id> diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXDiscoveryComponent.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXDiscoveryComponent.java index ce8631a..a61208d 100644 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXDiscoveryComponent.java +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXDiscoveryComponent.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2010 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -23,37 +23,84 @@ package org.rhq.plugins.jmx;
import java.io.File; -import java.io.FilenameFilter; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set;
+import javax.management.MBeanServerConnection; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.hyperic.sigar.NetConnection; +import org.hyperic.sigar.NetFlags; +import org.hyperic.sigar.Sigar; +import org.hyperic.sigar.SigarException; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; +import org.rhq.core.domain.resource.ResourceUpgradeReport; 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.ResourceComponent; 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.pluginapi.inventory.InvalidPluginConfigurationException; +import org.rhq.core.pluginapi.upgrade.ResourceUpgradeContext; +import org.rhq.core.pluginapi.upgrade.ResourceUpgradeFacet; import org.rhq.core.system.ProcessInfo; +import org.rhq.plugins.jmx.util.ConnectionProviderFactory; +import org.rhq.plugins.jmx.util.JvmResourceKey; +import org.rhq.plugins.jmx.util.JvmUtility; +import org.rhq.plugins.jmx.util.Socket; + +import org.mc4j.ems.connection.EmsConnection; +import org.mc4j.ems.connection.bean.EmsBean; +import org.mc4j.ems.connection.bean.attribute.EmsAttribute; +import org.mc4j.ems.connection.support.ConnectionProvider; +import org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor;
/** - * This component will discover JDK 5 agents running locally that have active JSR-160 connectors defined via system - * properties. + * This component will discover JVM processes that appear to be long-running (i.e. "servers"). Specifically, it will + * discover java processes that: + * <ul> + * <li>have enabled JMX Remoting (JSR-160) via com.sun.management.jmxremote* system properties on their command lines, or</li> + * <li>are Sun/Oracle-compatible java processes accessible via the com.sun.tools.attach API AND specify the + * org.rhq.resourceKey system property on their command lines (e.g. -Dorg.rhq.resourceKey=FOO); the attach API uses IPC + * under the covers, so for a process to be accessible, it either must be running as the same user as the RHQ Agent, + * or the Agent must be running as root<li> + * </ul> + * Some other java processes that do not meet these criteria can be manually added if they expose JMX remotely in + * another supported form (WebLogic, WebSphere, etc.). * * @author Greg Hinkle + * @author Ian Springer */ -public class JMXDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet { //, ClassLoaderFacet { +public class JMXDiscoveryComponent implements ResourceDiscoveryComponent, ManualAddFacet, ResourceUpgradeFacet { //, ClassLoaderFacet { + private static final Log log = LogFactory.getLog(JMXDiscoveryComponent.class);
- public static final String VMID_CONFIG_PROPERTY = "vmid"; + // our own private SIGAR + // TODO (ips, 01/05/12): enhance native-system API, then use that instead of using SIGAR directly + private static Sigar SIGAR; + static { + try { + SIGAR = new Sigar(); + } catch (RuntimeException re) { + SIGAR = null; + } + }
public static final String COMMAND_LINE_CONFIG_PROPERTY = "commandLine";
@@ -67,120 +114,162 @@ public class JMXDiscoveryComponent implements ResourceDiscoveryComponent, Manual
public static final String ADDITIONAL_CLASSPATH_ENTRIES = "additionalClassPathEntries";
- /* Ignore certain processes that are managed by their own plugin. For example, the Tomcat plugin will + private static final String SYSPROP_JMXREMOTE_PORT = "com.sun.management.jmxremote.port"; + private static final String SYSPROP_RHQ_JMXPLUGIN_PROCESS_FILTERS = "rhq.jmxplugin.process-filters"; + public static final String SYSPROP_RHQ_RESOURCE_KEY = "org.rhq.resourceKey"; + private static final String SYSPROP_JAVA_VERSION = "java.version"; + + /* + * Ignore certain processes that are managed by their own plugin. For example, the Tomcat plugin will * handle Tomcat processes configured for JMX management. */ - private static final String[] PROCESS_FILTERS; - - static { - String[] processFilters = new String[] { "catalina.startup.Bootstrap", "org.jboss.Main" }; - try { - String env = System.getProperty("rhq.jmxplugin.process-filters"); - if (env != null) { - processFilters = env.split(","); - } - } catch (Throwable t) { - log.error("Can't determine process filters; using default... Cause: " + t); - } finally { - PROCESS_FILTERS = processFilters; - } - } + private static final String[] DEFAULT_PROCESS_EXCLUDES = new String[]{"org.jboss.Main", "catalina.startup.Bootstrap"};
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext context) { + Set<DiscoveredResourceDetails> discoveredResources = new LinkedHashSet<DiscoveredResourceDetails>(); + Map<String, List<DiscoveredResourceDetails>> duplicatesByKey = new LinkedHashMap<String, List<DiscoveredResourceDetails>>();
- Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>(); - - // This model of discovery is of questionable usefulness since if you restart your process you'll get a new resource - // Works only on JDK6 and maybe some 64 bit JDK5 See JBNADM-3332. - // - // Map<Integer, LocalVirtualMachine> vms; - // - // try { - // vms = LocalVMFinder.getManageableVirtualMachines(); - // } catch (Exception e) { - // log.info("JMX Platform Autodiscovery only supported on JDK6 and above"); - // return null; - // } - // - // if (vms != null) { - // for (LocalVirtualMachine vm : vms.values()) { - // // TODO: Might want to limit to vms already managed as the other kind are temporary connector addresses - // String resourceKey = (vm.getCommandLine() != null) ? vm.getCommandLine() : vm.getConnectorAddress(); - // DiscoveredResourceDetails s = new DiscoveredResourceDetails(context.getResourceType(), resourceKey, - // "Java VM [" + vm.getVmid() + "]", System.getProperty("java.version"), // TODO Get the vm's version - // vm.getCommandLine(), null, null); - // - // Configuration configuration = s.getPluginConfiguration(); - // configuration.setNotes("Auto-discovered"); - // configuration.put(new PropertySimple(VMID_CONFIG_PROPERTY, String.valueOf(vm.getVmid()))); - // configuration.put(new PropertySimple(CONNECTOR_ADDRESS_CONFIG_PROPERTY, String.valueOf(vm - // .getConnectorAddress()))); - // configuration - // .put(new PropertySimple(COMMAND_LINE_CONFIG_PROPERTY, String.valueOf(vm.getCommandLine()))); - // configuration.put(new PropertySimple(CONNECTION_TYPE, LocalVMTypeDescriptor.class.getName())); - // - // found.add(s); - // } - // - // /* GH: Disabling discovery of the internal VM... Other plugins should probably embed via the internal - // * component above - // * DiscoveredResourceDetails localVM = new DiscoveredResourceDetails(context.getResourceType(), - // * "InternalVM", - // * "Internal Java VM", - // * System.getProperty("java.version"), "VM of - // * plugin container", null, null); Configuration configuration = localVM.getPluginConfiguration(); - // * configuration.put(new PropertySimple(CONNECTOR_ADDRESS_CONFIG_PROPERTY, "Local Connection")); - // * configuration.put(new PropertySimple(CONNECTION_TYPE, InternalVMTypeDescriptor.class.getName())); - // * - // *found.add(localVM);*/ - // } - - try { - List<ProcessScanResult> processes = context.getAutoDiscoveredProcesses(); + // Filter out JBoss, Tomcat, etc. processes, which will be represented by more specific types of Resources + // discovered by other plugins. + List<ProcessScanResult> nonExcludedProcesses = getNonExcludedJavaProcesses(context);
- for (ProcessScanResult process : processes) { + for (ProcessScanResult process : nonExcludedProcesses) { + try { ProcessInfo processInfo = process.getProcessInfo(); - DiscoveredResourceDetails details = discoverProcess(context, processInfo); + DiscoveredResourceDetails details = discoverResourceDetails(context, processInfo); if (details != null) { - boolean isFiltered = false; - for (String filter : PROCESS_FILTERS) { - if (details.getResourceName().contains(filter)) { - isFiltered = true; - break; + if (discoveredResources.contains(details)) { + List<DiscoveredResourceDetails> duplicates = duplicatesByKey.get(details.getResourceKey()); + if (duplicates == null) { + duplicates = new ArrayList<DiscoveredResourceDetails>(); + duplicatesByKey.put(details.getResourceKey(), duplicates); } - } - if (!isFiltered) { - found.add(details); - } + duplicates.add(details); + } + discoveredResources.add(details); + } + } catch (RuntimeException re) { + // Don't let a runtime exception for a particular ProcessInfo cause the entire discovery scan to fail. + if (log.isDebugEnabled()) { + log.debug("Error when trying to discover JVM process [" + process + "].", re); + } else { + log.warn("Error when trying to discover JVM process [" + process + "] (enable DEBUG for stack trace): " + re); } } - } catch (Exception e) { - if (log.isDebugEnabled()) { - log.debug("Unable to complete base JMX server discovery.", e); + } + + for (String duplicateKey : duplicatesByKey.keySet()) { + List<DiscoveredResourceDetails> duplicates = duplicatesByKey.get(duplicateKey); + log.error("Multiple Resources with the same key (" + duplicateKey + + ") were discovered - none will be reported to the plugin container! This most likely means that there are multiple java processes running with the same value for the " + + SYSPROP_RHQ_RESOURCE_KEY + " system property specified on their command lines. Here is the list of Resources: " + + duplicates); + discoveredResources.remove(duplicates.get(0)); + } + + return discoveredResources; + } + + private List<ProcessScanResult> getNonExcludedJavaProcesses(ResourceDiscoveryContext context) { + // This is the list of all currently running java processes. + List<ProcessScanResult> javaProcesses = context.getAutoDiscoveredProcesses(); + + List<ProcessScanResult> nonExcludedJavaProcesses = new ArrayList<ProcessScanResult>(); + Set<String> processExcludes = getProcessExcludes(); + for (ProcessScanResult javaProcess : javaProcesses) { + if (javaProcess.getProcessInfo().equals(context.getSystemInformation().getThisProcess())) { + // If the process is our own process (i.e. the RHQ Agent JVM), then skip it, since the rhq-agent + // plugin will handle discovering that. + continue; + } + String[] args = javaProcess.getProcessInfo().getCommandLine(); + StringBuilder buffer = new StringBuilder(); + for (String arg : args) { + buffer.append(arg).append(" "); + } + String commandLine = buffer.toString(); + if (!isExcluded(commandLine, processExcludes)) { + nonExcludedJavaProcesses.add(javaProcess); } else { - log.warn("Unable to complete base JMX server discovery (enable DEBUG for stack): " + e); + if (log.isDebugEnabled()) { + log.debug("Process [" + javaProcess.getProcessInfo() + + "] excluded since its command line contains one of the following: " + processExcludes); + } + } + } + return nonExcludedJavaProcesses; + } + + private boolean isExcluded(String commandLine, Set<String> processExcludes) { + for (String processExclude : processExcludes) { + if (commandLine.contains(processExclude)) { + return true; } } + return false; + }
- return found; + protected Set<String> getProcessExcludes() { + Set<String> processExcludes; + String overrideProcessExcludes = System.getProperty(SYSPROP_RHQ_JMXPLUGIN_PROCESS_FILTERS); + if (overrideProcessExcludes != null) { + processExcludes = new HashSet<String>(Arrays.asList(overrideProcessExcludes.split(","))); + } else { + processExcludes = new HashSet<String>(Arrays.asList(DEFAULT_PROCESS_EXCLUDES)); + } + return processExcludes; }
+ // MANUAL ADD public DiscoveredResourceDetails discoverResource(Configuration pluginConfig, ResourceDiscoveryContext discoveryContext) throws InvalidPluginConfigurationException { - // TODO: Connect to the remote JVM to verify the user-specified conn props are valid, and if connecting - // fails, throw an exception. - String resourceKey = pluginConfig.getSimpleValue(CONNECTOR_ADDRESS_CONFIG_PROPERTY, null); - String connectionType = pluginConfig.getSimpleValue(CONNECTION_TYPE, null); + + String connectorAddress = pluginConfig.getSimpleValue(CONNECTOR_ADDRESS_CONFIG_PROPERTY, null); + if (connectorAddress == null) { + throw new InvalidPluginConfigurationException("A connector address must be specified when manually adding a JMX Server."); + } + + ConnectionProvider connectionProvider; + EmsConnection connection; + try { + connectionProvider = ConnectionProviderFactory.createConnectionProvider(pluginConfig, null, + discoveryContext.getParentResourceContext().getTemporaryDirectory()); + connection = connectionProvider.connect(); + connection.loadSynchronous(false); + } catch (Exception e) { + throw new RuntimeException("Failed to connection to connector address [" + connectorAddress + "].", e); + } + + String key = connectorAddress; + String name = connectorAddress;
- // TODO (ips, 09/04/09): We should connect to the remote JVM in order to obtain its version. - String version = null; + String version = getJavaVersion(connection); + if (version == null) { + log.warn("Unable to determine version of JVM with connector address [" + connectorAddress + "]."); + } + + String connectionType = pluginConfig.getSimpleValue(CONNECTION_TYPE, null); + String description = connectionType + " JVM (" + connectorAddress + ")";
DiscoveredResourceDetails resourceDetails = new DiscoveredResourceDetails(discoveryContext.getResourceType(), - resourceKey, "Java VM", version, connectionType + " [" + resourceKey + "]", pluginConfig, null); + key, name, version, description, pluginConfig, null); return resourceDetails; }
+ private String getJavaVersion(EmsConnection connection) { + String version = null; + EmsBean runtimeMXBean = connection.getBean(ManagementFactory.RUNTIME_MXBEAN_NAME); + if (runtimeMXBean != null) { + EmsAttribute systemPropertiesAttribute = runtimeMXBean.getAttribute("systemProperties"); + if (systemPropertiesAttribute != null) { + Map<String, String> systemProperties = (Map<String, String>) systemPropertiesAttribute.getValue(); + version = systemProperties.get("java.version"); + } + } + return version; + } + // For now, this method is not used. This method is the ClassLoaderFacet method, but I commented // out the fact that this class implements that interface. As of today, 7/20/2009, I'm not sure we really // need to implement the classloader facet since EMS's ability to use additionalClasspathEntries in its @@ -191,11 +280,10 @@ public class JMXDiscoveryComponent implements ResourceDiscoveryComponent, Manual // be changed other than to add "implements ClassLoaderFacet" to the class definition). // For now, since we don't want the additional overhead of calling into this method when we currently have // no need for it, we do not implement the ClassLoaderFacet. - public List<URL> getAdditionalClasspathUrls(ResourceDiscoveryContext context, DiscoveredResourceDetails details) - throws Exception { - - List<File> jars = getAdditionalJarsFromConfig(details.getPluginConfiguration()); - if (jars == null || jars.size() == 0) { + public List<URL> getAdditionalClasspathUrls(ResourceDiscoveryContext<ResourceComponent<?>> context, + DiscoveredResourceDetails details) throws Exception { + List<File> jars = ConnectionProviderFactory.getAdditionalJarsFromConfig(details.getPluginConfiguration()); + if (jars == null || jars.isEmpty()) { return null; }
@@ -207,113 +295,256 @@ public class JMXDiscoveryComponent implements ResourceDiscoveryComponent, Manual return urls; }
- protected DiscoveredResourceDetails discoverProcess(ResourceDiscoveryContext context, ProcessInfo process) { - String portProp = "com.sun.management.jmxremote.port"; + @Override + public ResourceUpgradeReport upgrade(ResourceUpgradeContext inventoriedResource) { + JvmResourceKey oldKey = JvmResourceKey.valueOf(inventoriedResource.getResourceKey()); + JvmResourceKey.Type oldKeyType = oldKey.getType(); + if (oldKeyType == JvmResourceKey.Type.Legacy || oldKeyType == JvmResourceKey.Type.JmxRemotingPort) { + if (!inventoriedResource.getSystemInformation().isNative()) { + log.warn("Cannot attempt to upgrade Resource key [" + inventoriedResource.getResourceKey() + + "] of JVM Resource, because this Agent is not running with native system info support (i.e. SIGAR)."); + return null; + }
- String port = null; - for (String argument : process.getCommandLine()) { - String cmdLineArg = "-D" + portProp + "="; - if (argument.startsWith(cmdLineArg)) { - port = argument.substring(cmdLineArg.length()); - break; + Configuration pluginConfig = inventoriedResource.getPluginConfiguration(); + String connectorAddress = pluginConfig.getSimpleValue(CONNECTOR_ADDRESS_CONFIG_PROPERTY, null); + JMXServiceURL jmxServiceURL; + try { + jmxServiceURL = new JMXServiceURL(connectorAddress); + } catch (MalformedURLException e) { + throw new RuntimeException("Failed to parse connector address: " + connectorAddress, e); + } + + Long pid; + try { + JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL); + MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection(); + RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy(mbeanServerConnection, + ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class); + pid = getJvmPid(runtimeMXBean); + if (pid == null) { + throw new RuntimeException("Failed to determine JVM pid by parsing JVM name."); + } + } catch (Exception e) { + throw new RuntimeException("Failed to determine JVM pid.", e); } - }
- if (port == null) { - port = process.getEnvironmentVariable(portProp); + List<ProcessInfo> processes = inventoriedResource.getSystemInformation().getProcesses( + "process|pid|match=" + pid); + if (processes.size() != 1) { + throw new IllegalStateException("Failed to find process with PID [" + pid + "]."); + } + ProcessInfo process = processes.get(0); + String mainClassName = getJavaMainClassName(process); + String explicitKeyValue = getSystemPropertyValue(process, SYSPROP_RHQ_RESOURCE_KEY); + if (oldKeyType == JvmResourceKey.Type.Legacy || explicitKeyValue != null) { + // We need to upgrade the key. + JvmResourceKey newKey; + if (explicitKeyValue != null) { + newKey = JvmResourceKey.fromExplicitValue(mainClassName, explicitKeyValue); + } else { + newKey = JvmResourceKey.fromJmxRemotingPort(mainClassName, oldKey.getJmxRemotingPort()); + } + + ResourceUpgradeReport resourceUpgradeReport = new ResourceUpgradeReport(); + resourceUpgradeReport.setNewResourceKey(newKey.toString()); + return resourceUpgradeReport; + } }
- DiscoveredResourceDetails details = null; - if (port != null) { + return null; + }
- String name = "JMX Server"; - for (int i = 1; i < process.getCommandLine().length; i++) { - String arg = process.getCommandLine()[i]; + private static Long getJvmPid(RuntimeMXBean runtimeMXBean) { + Long pid; + String jvmName = runtimeMXBean.getName(); + int atIndex = jvmName.indexOf('@'); + pid = (atIndex != -1) ? Long.valueOf(jvmName.substring(0, atIndex)) : null; + return pid; + }
- if (!arg.startsWith("-")) { - if (arg.length() <= 200) { - name = arg; - } else { - // Truncate it if it's really long for a more palatable Resource name. - name = arg.substring(arg.length() - 200); - } - break; - } else if (arg.equals("-cp") || arg.equals("-classpath")) { - // Skip the next arg - it's the classpath, and we don't want that as the name. - i++; + protected DiscoveredResourceDetails discoverResourceDetails(ResourceDiscoveryContext context, ProcessInfo process) { + Integer jmxRemotingPort = getJmxRemotingPort(process); + JMXServiceURL jmxServiceURL; + Set<Socket> serverSockets; + if (jmxRemotingPort != null) { + // Use JMX Remoting when possible, since it doesn't require the RHQ Agent to have OS-level permissions to + // communicate with the remote JVM via IPC. + try { + jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://127.0.0.1:" + jmxRemotingPort + "/jmxrmi"); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } else { + // If JMX Remoting is not enabled, it's required that a Resource key is explicitly specified via the org.rhq.resourceKey sysprop. + String keyString = getSystemPropertyValue(process, SYSPROP_RHQ_RESOURCE_KEY); + if (keyString == null || keyString.equals("")) { + serverSockets = getServerSockets(process); + if (!serverSockets.isEmpty()) { + log.info("Server JVM process [" + process.getPid() + "] with command line [" + + Arrays.asList(process.getCommandLine()) + + "] cannot be discovered, because it does not specify either of the following system properties: -D" + + SYSPROP_JMXREMOTE_PORT + "=JMX_REMOTING_PORT, -D" + SYSPROP_RHQ_RESOURCE_KEY + "=UNIQUE_KEY"); + return null; } }
- name += " (" + port + ")"; + // Start up a JMX agent within the JVM via the Sun Attach API, and return a URL that can be used to connect + // to that agent. + // Note, this will only work if the remote JVM is Java 6 or later, and maybe some 64 bit Java 5 - see + // JBNADM-3332. Also, the RHQ Agent will have to be running on a JDK, not a JRE, so that we can access + // the JDK's tools.jar, which contains the Sun JVM Attach API classes. + jmxServiceURL = JvmUtility.extractJMXServiceURL(process.getPid()); + }
- Configuration config = context.getDefaultPluginConfiguration(); - config.put(new PropertySimple(CONNECTION_TYPE, - "org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor")); - config.put(new PropertySimple(CONNECTOR_ADDRESS_CONFIG_PROPERTY, "service:jmx:rmi:///jndi/rmi://localhost:" - + port + "/jmxrmi")); - // config.put(new PropertySimple(INSTALL_URI, process.getCurrentWorkingDirectory())); + if (jmxServiceURL != null) { + log.debug("JMX service URL for process [" + process + "] is [" + jmxServiceURL + "]."); + return buildResourceDetails(context, process, jmxServiceURL, jmxRemotingPort); + } else { + return null; + } + }
- // TODO (ips, 09/04/09): I think we should connect to the remote JVM in order to obtain its version. - String version = null; + protected DiscoveredResourceDetails buildResourceDetails(ResourceDiscoveryContext context, ProcessInfo process, + JMXServiceURL jmxServiceURL, Integer jmxRemotingPort) { + JvmResourceKey key; + String mainClassName = getJavaMainClassName(process); + String value = getSystemPropertyValue(process, SYSPROP_RHQ_RESOURCE_KEY); + if (value != null && !value.equals("")) { + log.debug("Using explicitly specified Resource key: [" + value + "]..."); + key = JvmResourceKey.fromExplicitValue(mainClassName, value); + } else { + if (jmxRemotingPort != null) { + log.debug("Using JMX remoting port [" + jmxRemotingPort + "] as Resource key..."); + key = JvmResourceKey.fromJmxRemotingPort(mainClassName, jmxRemotingPort); + } else { + log.info("Process [" + process.getPid() + "] with command line [" + Arrays.asList(process.getCommandLine()) + "] cannot be discovered, because it does not specify either of the following system properties: -Dcom.sun.management.jmxremote, -D" + SYSPROP_RHQ_RESOURCE_KEY + "=UNIQUE_KEY"); + return null; + } + } + + String name = buildResourceName(key);
- details = new DiscoveredResourceDetails(context.getResourceType(), port, name, version, - "Standalone JVM Process", config, null); + String version; + try { + JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceURL); + MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection(); + RuntimeMXBean runtimeMXBean = ManagementFactory.newPlatformMXBeanProxy(mbeanServerConnection, + ManagementFactory.RUNTIME_MXBEAN_NAME, RuntimeMXBean.class); + version = runtimeMXBean.getSystemProperties().get(SYSPROP_JAVA_VERSION); + if (version == null) { + throw new IllegalStateException("System property " + SYSPROP_JAVA_VERSION + " is not defined."); + } + } catch (Exception e) { + log.error("Failed to determine JVM version.", e); + version = null; }
- return details; + String description = "JVM, monitored via " + ((jmxRemotingPort != null) ? "JMX Remoting" : "Sun JVM Attach API"); + + Configuration pluginConfig = context.getDefaultPluginConfiguration(); + pluginConfig.put(new PropertySimple(CONNECTION_TYPE, J2SE5ConnectionTypeDescriptor.class.getName())); + if (jmxRemotingPort != null) { + pluginConfig.put(new PropertySimple(CONNECTOR_ADDRESS_CONFIG_PROPERTY, jmxServiceURL)); + } + + return new DiscoveredResourceDetails(context.getResourceType(), key.toString(), name, version, description, + pluginConfig, process); }
- /** - * Examines the plugin configuration and if it defines additional classpath entries, this - * will return a list of files that point to all the jars that need to be added to a classloader - * to support the managed JMX resource. - * - * Note: this is package static scoped so the resource component can use this method. - * - * @param pluginConfiguration - * - * @return list of files pointing to additional jars; will be empty if no additional jars are to be added - */ - static List<File> getAdditionalJarsFromConfig(Configuration pluginConfiguration) { - List<File> jarFiles = new ArrayList<File>(); - - // get the plugin config setting that contains comma-separated list of files/dirs to additional jars - // if no additional classpath entries are specified, we'll return an empty list - PropertySimple prop = pluginConfiguration.getSimple(JMXDiscoveryComponent.ADDITIONAL_CLASSPATH_ENTRIES); - if (prop == null || prop.getStringValue() == null || prop.getStringValue().trim().length() == 0) { - return jarFiles; - } - String[] paths = prop.getStringValue().trim().split(","); - if (paths == null || paths.length == 0) { - return jarFiles; + private String buildResourceName(JvmResourceKey key) { + StringBuilder name = new StringBuilder(); + String mainClassName = key.getMainClassName(); + if (mainClassName != null) { + if (mainClassName.length() <= 200) { + name.append(mainClassName); + } else { + // Truncate it if it's really long for a more palatable Resource name. + name.append(mainClassName.substring(mainClassName.length() - 200)); + } }
- // Get all additional classpath entries which can be listed as jar filenames or directories. - // If a directory has "/*.jar" at the end, all jar files found in that directory will be added - // as class path entries. - final class JarFilenameFilter implements FilenameFilter { - public boolean accept(File dir, String name) { - return name.endsWith(".jar"); - } + switch (key.getType()) { + case JmxRemotingPort: + name.append(':').append(key.getJmxRemotingPort()); break; + case Explicit: + name.append(' ').append(key.getExplicitValue()); break; + default: + throw new IllegalStateException("Unsupported key type: " + key.getType()); }
- for (String path : paths) { - path = path.trim(); - if (path.length() > 0) { - if (path.endsWith("*.jar")) { - path = path.substring(0, path.length() - 5); - File dir = new File(path); - File[] jars = dir.listFiles(new JarFilenameFilter()); - if (jars != null && jars.length > 0) { - jarFiles.addAll(Arrays.asList(jars)); + return name.toString(); + } + + private Set<Socket> getServerSockets(ProcessInfo process) { + Set<Socket> serverSockets = new HashSet<Socket>(); + if (SIGAR != null) { + // Only request the type of connections we are interested in - TCP and UDP listen sockets. + int netFlags = (NetFlags.CONN_TCP | NetFlags.CONN_UDP | NetFlags.CONN_SERVER); + NetConnection[] connections; + try { + connections = SIGAR.getNetConnectionList(netFlags); + } catch (SigarException e) { + log.debug("Failed to get net connections.", e); + connections = new NetConnection[0]; + } + for (NetConnection connection : connections) { + if (connection.getState() == NetFlags.TCP_LISTEN) { + try { + long pid = SIGAR.getProcPort(connection.getType(), connection.getLocalPort()); + if (pid == process.getPid()) { + Socket.Protocol protocol = (connection.getType() == NetFlags.CONN_UDP) ? + Socket.Protocol.UDP : Socket.Protocol.TCP; + serverSockets.add(new Socket(protocol, connection.getLocalAddress(), + connection.getLocalPort())); + } + } catch (SigarException e) { + log.debug("Failed to get pid for connection [" + connection + "].", e); } - } else { - File pathFile = new File(path); - jarFiles.add(pathFile); } } } + return serverSockets; + }
- return jarFiles; + protected String getJavaMainClassName(ProcessInfo process) { + String className = null; + for (int i = 1; i < process.getCommandLine().length; i++) { + String arg = process.getCommandLine()[i]; + + if (!arg.startsWith("-")) { + className = arg; + break; + } else if (arg.equals("-cp") || arg.equals("-classpath")) { + // The next arg is the classpath - skip it. + i++; + } + } + return className; } + + protected Integer getJmxRemotingPort(ProcessInfo process) { + String value = getSystemPropertyValue(process, SYSPROP_JMXREMOTE_PORT); + if (value != null) { + try { + return Integer.valueOf(value); + } catch (NumberFormatException e) { + return null; + } + } else { + return null; + } + } + + protected String getSystemPropertyValue(ProcessInfo process, String systemPropertyName) { + for (String argument : process.getCommandLine()) { + String prefix = "-D" + systemPropertyName + "="; + if (argument.startsWith(prefix)) { + return argument.substring(prefix.length()); + } + } + return null; + } + + } \ No newline at end of file diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXServerComponent.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXServerComponent.java index 1f77f48..a5a7a13 100644 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXServerComponent.java +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/JMXServerComponent.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -22,30 +22,17 @@ */ package org.rhq.plugins.jmx;
-import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.mc4j.ems.connection.ConnectionFactory; + import org.mc4j.ems.connection.EmsConnection; -import org.mc4j.ems.connection.local.LocalVMFinder; -import org.mc4j.ems.connection.local.LocalVirtualMachine; -import org.mc4j.ems.connection.settings.ConnectionSettings; import org.mc4j.ems.connection.support.ConnectionProvider; -import org.mc4j.ems.connection.support.metadata.ConnectionTypeDescriptor; -import org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor; -import org.mc4j.ems.connection.support.metadata.LocalVMTypeDescriptor;
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.domain.resource.Resource; -import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.pluginapi.inventory.ResourceComponent; import org.rhq.core.pluginapi.inventory.ResourceContext; +import org.rhq.plugins.jmx.util.ConnectionProviderFactory;
/** * The generic JMX server component used to create and cache a connection to a local or @@ -61,124 +48,50 @@ import org.rhq.core.pluginapi.inventory.ResourceContext; * @author John Mazzitelli */ public class JMXServerComponent<T extends ResourceComponent<?>> implements JMXComponent<T> { + private static Log log = LogFactory.getLog(JMXServerComponent.class);
private EmsConnection connection; private ConnectionProvider connectionProvider;
/** - * Access to this is deprecated, use #getResourceContext() instead. + * The context of a component that is started. Note, other classes should use #getResourceContext(), rather than + * this field. */ ResourceContext context;
public void start(ResourceContext context) throws Exception { this.context = context; - log.info("Starting connection to JMX Server " + context.getResourceKey()); + log.debug("Starting connection to " + context.getResourceType() + "[" + context.getResourceKey() + "]...");
+ // If connecting to the EMS fails, log a warning but still succeed in starting. getAvailablity() will keep + // trying to connect each time it is called. try { internalStart(); } catch (Exception e) { - log.warn("JMX Plugin connection failure", e); - // The new model is to always succeed in starting, but warn about the errors (we only do this the first request) - /*throw new Exception("Unable to connect to Java VM [" - + configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY).getStringValue() - + "]", e);*/ + log.warn("Failed to connect to " + context.getResourceType() + "[" + context.getResourceKey() + "].", e); + }
if (connection == null) { - log.warn("Unable to connect to JMX Server " + context.getResourceKey()); + log.warn("Unable to connect to " + context.getResourceType() + "[" + context.getResourceKey() + "]."); } }
protected void internalStart() throws Exception { - Configuration configuration = context.getPluginConfiguration(); - - String connectionTypeDescriptorClass = configuration.getSimple(JMXDiscoveryComponent.CONNECTION_TYPE) + Configuration pluginConfig = context.getPluginConfiguration(); + String connectionTypeDescriptorClassName = pluginConfig.getSimple(JMXDiscoveryComponent.CONNECTION_TYPE) .getStringValue(); - - if (LocalVMTypeDescriptor.class.getName().equals(connectionTypeDescriptorClass)) { - String commandLine = configuration.getSimple(JMXDiscoveryComponent.COMMAND_LINE_CONFIG_PROPERTY) - .getStringValue(); - - Map<Integer, LocalVirtualMachine> vms = LocalVMFinder.getManageableVirtualMachines(); - if (vms != null) { - for (LocalVirtualMachine vm : vms.values()) { - if (vm.getCommandLine().equals(commandLine)) { - connectLocal(vm.getVmid()); - } - } - } - } else if (JMXDiscoveryComponent.PARENT_TYPE.equals(connectionTypeDescriptorClass)) { - // We're embedded in another jmx server component without jmxremoting set so just use the parent's connection + if (JMXDiscoveryComponent.PARENT_TYPE.equals(connectionTypeDescriptorClassName)) { + // Our parent is itself a JMX component, so just reuse its connection. this.connection = ((JMXComponent) context.getParentResourceComponent()).getEmsConnection(); this.connectionProvider = this.connection.getConnectionProvider(); - } else if (J2SE5ConnectionTypeDescriptor.class.getName().equals(connectionTypeDescriptorClass)) { - // We're embedded in a J2SE VM with jmxremote defined (e.g. for jconsole usage) - String principal = null; - String credentials = null; - PropertySimple o = configuration.getSimple(JMXComponent.PRINCIPAL_CONFIG_PROP); - if (o != null) { - principal = o.getStringValue(); - } - o = configuration.getSimple(JMXComponent.CREDENTIALS_CONFIG_PROP); - if (o != null) { - credentials = o.getStringValue(); - } - - ConnectionSettings settings = new ConnectionSettings(); - J2SE5ConnectionTypeDescriptor desc = new J2SE5ConnectionTypeDescriptor(); - settings.setConnectionType(desc); - settings.setServerUrl(configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY) - .getStringValue()); - if (principal != null) { - settings.setPrincipal(principal); - } - if (credentials != null) { - settings.setCredentials(credentials); - } - - prepareConnection(settings); - } else { - // This can handle internal connections (within the same vm as the plugin container) as well as - // any remote connections - ConnectionSettings settings = new ConnectionSettings(); - - String principal = null; - String credentials = null; - PropertySimple o = configuration.getSimple(JMXComponent.PRINCIPAL_CONFIG_PROP); - if (o != null) { - principal = o.getStringValue(); - } - o = configuration.getSimple(JMXComponent.CREDENTIALS_CONFIG_PROP); - if (o != null) { - credentials = o.getStringValue(); - } - - settings.initializeConnectionType((ConnectionTypeDescriptor) Class.forName(connectionTypeDescriptorClass) - .newInstance()); - - settings.setConnectionType((ConnectionTypeDescriptor) Class.forName(connectionTypeDescriptorClass) - .newInstance()); - settings.setServerUrl(configuration.getSimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY) - .getStringValue()); - - String installPath = configuration.getSimpleValue(JMXDiscoveryComponent.INSTALL_URI, null); - if (installPath != null) { - settings.setLibraryURI(configuration.getSimple(JMXDiscoveryComponent.INSTALL_URI).getStringValue()); - } - - if (principal != null) { - settings.setPrincipal(principal); - } - if (credentials != null) { - settings.setCredentials(credentials); - } - prepareConnection(settings); + this.connectionProvider = ConnectionProviderFactory.createConnectionProvider(pluginConfig, + this.context.getNativeProcess(), this.context.getTemporaryDirectory()); + this.connection = this.connectionProvider.connect(); + this.connection.loadSynchronous(false); } - - this.connection.loadSynchronous(false); - }
public void stop() { @@ -192,28 +105,6 @@ public class JMXServerComponent<T extends ResourceComponent<?>> implements JMXCo } }
- protected void connectLocal(int vmid) { - // TODO GH: Refactor ems to also accept the vm itself - ConnectionSettings settings = new ConnectionSettings(); - settings.setConnectionType(new LocalVMTypeDescriptor()); - settings.setServerUrl(String.valueOf(vmid)); - prepareConnection(settings); - } - - protected void prepareConnection(ConnectionSettings settings) { - settings.getControlProperties().setProperty(ConnectionFactory.COPY_JARS_TO_TEMP, String.valueOf(Boolean.TRUE)); - settings.getControlProperties().setProperty(ConnectionFactory.JAR_TEMP_DIR, - this.context.getTemporaryDirectory().getAbsolutePath()); - - addAdditionalJarsToConnectionSettings(settings); - - ConnectionFactory cf = new ConnectionFactory(); - cf.discoverServerClasses(settings); - - this.connectionProvider = cf.getConnectionProvider(settings); - this.connection = this.connectionProvider.connect(); - } - public EmsConnection getEmsConnection() { return this.connection; } @@ -223,8 +114,8 @@ public class JMXServerComponent<T extends ResourceComponent<?>> implements JMXCo try { internalStart(); } catch (Exception e) { - log.debug("Still unable to reconnect resource: " + context.getResourceKey() + " due to error: " - + e.getMessage()); + log.debug("Still unable to reconnect to " + context.getResourceType() + "[" + context.getResourceKey() + + "] due to error: " + e); } }
@@ -236,32 +127,4 @@ public class JMXServerComponent<T extends ResourceComponent<?>> implements JMXCo return this.context; }
- public List<Resource> discoverServices(ResourceType type, Configuration defaultPluginConfiguration) { - defaultPluginConfiguration.getSimple("objectName").getStringValue(); - - return null; - } - - private void addAdditionalJarsToConnectionSettings(ConnectionSettings settings) { - // get the additional jars from the config - Configuration pluginConfiguration = getResourceContext().getPluginConfiguration(); - List<File> additionalEntries = JMXDiscoveryComponent.getAdditionalJarsFromConfig(pluginConfiguration); - if (additionalEntries == null || additionalEntries.size() == 0) { - return; // nothing to do, there are no additional entries to add - } - - // get the setting's current list of classpath entries - we are going to add to these - List<File> settingsEntries = settings.getClassPathEntries(); - if (settingsEntries == null) { - settingsEntries = new ArrayList<File>(); - } - - // append the additional entries to the end of the setting's current entries - settingsEntries.addAll(additionalEntries); - - // now that we've appended our additional jars, tell the connection settings about the new list - settings.setClassPathEntries(settingsEntries); - - return; - } } \ No newline at end of file diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ConnectionProviderFactory.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ConnectionProviderFactory.java new file mode 100644 index 0000000..5c29673 --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ConnectionProviderFactory.java @@ -0,0 +1,218 @@ +package org.rhq.plugins.jmx.util; + +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.management.remote.JMXServiceURL; + +import org.rhq.core.domain.configuration.Configuration; +import org.rhq.core.domain.configuration.PropertySimple; +import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException; +import org.rhq.core.system.ProcessInfo; +import org.rhq.plugins.jmx.JMXComponent; +import org.rhq.plugins.jmx.JMXDiscoveryComponent; + +import org.mc4j.ems.connection.ConnectionFactory; +import org.mc4j.ems.connection.local.LocalVMFinder; +import org.mc4j.ems.connection.local.LocalVirtualMachine; +import org.mc4j.ems.connection.settings.ConnectionSettings; +import org.mc4j.ems.connection.support.ConnectionProvider; +import org.mc4j.ems.connection.support.metadata.ConnectionTypeDescriptor; +import org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor; +import org.mc4j.ems.connection.support.metadata.LocalVMTypeDescriptor; + +/** + * A factory that can construct an EMS {@link ConnectionProvider} for a JMX Server Resource from that Resource's plugin + * configuration. + * + * @author Ian Springer + */ +public class ConnectionProviderFactory { + + public static ConnectionProvider createConnectionProvider(Configuration pluginConfig, ProcessInfo process, + File tempDir) throws Exception { + String connectionTypeDescriptorClassName = pluginConfig.getSimple(JMXDiscoveryComponent.CONNECTION_TYPE) + .getStringValue(); + + Class<?> connectionTypeDescriptorClass; + try { + connectionTypeDescriptorClass = Class.forName(connectionTypeDescriptorClassName); + } catch (ClassNotFoundException e) { + throw new InvalidPluginConfigurationException("Invalid connection type - class [" + connectionTypeDescriptorClassName + + "] not found."); + } + if (!(ConnectionTypeDescriptor.class.isAssignableFrom(connectionTypeDescriptorClass))) { + throw new InvalidPluginConfigurationException("Invalid connection type - class [" + connectionTypeDescriptorClassName + + "] does not implement the " + ConnectionTypeDescriptor.class.getName() + " interface."); + } + ConnectionTypeDescriptor connectionTypeDescriptor; + try { + connectionTypeDescriptor = (ConnectionTypeDescriptor) connectionTypeDescriptorClass.newInstance(); + } catch (Exception e) { + throw new RuntimeException("Failed to instantiate connection type descriptor of type [" + connectionTypeDescriptorClassName + "].", e); + } + + ConnectionSettings settings = new ConnectionSettings(); + settings.initializeConnectionType(connectionTypeDescriptor); + + // Set principal and credentials. + String principal = pluginConfig.getSimpleValue(JMXComponent.PRINCIPAL_CONFIG_PROP, null); + settings.setPrincipal(principal); + String credentials = pluginConfig.getSimpleValue(JMXComponent.CREDENTIALS_CONFIG_PROP, null); + settings.setCredentials(credentials); + + if (connectionTypeDescriptor instanceof LocalVMTypeDescriptor) { + // NOTE (ips, 01/19/12): This is not very reliable for long-term management of a JVM, since it uses the + // command line from the time the JVM was originally discovered, which may have changed. + String commandLine = pluginConfig.getSimpleValue(JMXDiscoveryComponent.COMMAND_LINE_CONFIG_PROPERTY, null); + if (commandLine == null) { + throw new InvalidPluginConfigurationException("A command line is required for the " + + connectionTypeDescriptorClassName + " connection type."); + } + + Map<Integer, LocalVirtualMachine> vms = LocalVMFinder.getManageableVirtualMachines(); + LocalVirtualMachine targetVm = null; + if (vms != null) { + for (LocalVirtualMachine vm : vms.values()) { + if (vm.getCommandLine().equals(commandLine)) { + targetVm = vm; + break; + } + } + } + if (targetVm == null) { + // This could just be because the JVM is not currently running. + throw new Exception("JVM with command line [" + commandLine + "] not found."); + } + String vmId = String.valueOf(targetVm.getVmid()); + settings.setServerUrl(vmId); + } else if (connectionTypeDescriptor instanceof J2SE5ConnectionTypeDescriptor) { + // Connect via JMX Remoting, using the JVM Attach API to start up a JMX Remoting Agent if necessary. + String jmxConnectorAddress = getJmxConnectorAddress(pluginConfig, process); + settings.setServerUrl(jmxConnectorAddress); + } else { + // Handle internal connections (InternalVMTypeDescriptor) (i.e. the RHQ plugin container's own JVM), as + // well as miscellaneous types of remote connections - WebSphere, WebLogic, etc. + String connectorAddress = pluginConfig.getSimpleValue(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY, + null); + if (connectorAddress == null) { + throw new InvalidPluginConfigurationException("A connector address is required for the " + + connectionTypeDescriptorClassName + " connection type."); + } + settings.setServerUrl(connectorAddress); + String installURI = pluginConfig.getSimpleValue(JMXDiscoveryComponent.INSTALL_URI, null); + settings.setLibraryURI(installURI); + } + + addAdditionalJarsToConnectionSettings(settings, pluginConfig); + + return createConnectionProvider(settings, tempDir); + } + + private static ConnectionProvider createConnectionProvider(ConnectionSettings settings, File tempDir) { + settings.getControlProperties().setProperty(ConnectionFactory.COPY_JARS_TO_TEMP, String.valueOf(Boolean.TRUE)); + settings.getControlProperties().setProperty(ConnectionFactory.JAR_TEMP_DIR, + tempDir.getAbsolutePath()); + + ConnectionFactory connectionFactory = new ConnectionFactory(); + connectionFactory.discoverServerClasses(settings); + + return connectionFactory.getConnectionProvider(settings); + } + + private static void addAdditionalJarsToConnectionSettings(ConnectionSettings settings, Configuration pluginConfig) { + + List<File> additionalEntries = getAdditionalJarsFromConfig(pluginConfig); + if (additionalEntries == null || additionalEntries.size() == 0) { + return; // nothing to do, there are no additional entries to add + } + + // get the setting's current list of classpath entries - we are going to add to these + List<File> settingsEntries = settings.getClassPathEntries(); + if (settingsEntries == null) { + settingsEntries = new ArrayList<File>(); + } + + // append the additional entries to the end of the setting's current entries + settingsEntries.addAll(additionalEntries); + + // now that we've appended our additional jars, tell the connection settings about the new list + settings.setClassPathEntries(settingsEntries); + } + + private static String getJmxConnectorAddress(Configuration pluginConfig, ProcessInfo process) throws Exception { + String connectorAddress = pluginConfig.getSimpleValue(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY, + null); + if (connectorAddress == null) { + // No JMX connector address defined - try to connect via Attach API. + if (process == null) { + throw new Exception("Could not find java process for JVM."); + } + JMXServiceURL jmxServiceURL = JvmUtility.extractJMXServiceURL(process.getPid()); + if (jmxServiceURL == null) { + throw new Exception("Could not obtain JMX service URL via Attach API for JVM [" + process + "]."); + } + connectorAddress = jmxServiceURL.toString(); + } + return connectorAddress; + } + + /** + * Examines the plugin configuration and if it defines additional classpath entries, this + * will return a list of files that point to all the jars that need to be added to a classloader + * to support the managed JMX resource. + * + * Note: this is package static scoped so the resource component can use this method. + * + * @param pluginConfiguration + * + * @return list of files pointing to additional jars; will be empty if no additional jars are to be added + */ + public static List<File> getAdditionalJarsFromConfig(Configuration pluginConfiguration) { + List<File> jarFiles = new ArrayList<File>(); + + // get the plugin config setting that contains comma-separated list of files/dirs to additional jars + // if no additional classpath entries are specified, we'll return an empty list + PropertySimple prop = pluginConfiguration.getSimple(JMXDiscoveryComponent.ADDITIONAL_CLASSPATH_ENTRIES); + if (prop == null || prop.getStringValue() == null || prop.getStringValue().trim().length() == 0) { + return jarFiles; + } + String[] paths = prop.getStringValue().trim().split(","); + if (paths == null || paths.length == 0) { + return jarFiles; + } + + // Get all additional classpath entries which can be listed as jar file names or directories. + // If a directory has ".jar" at the end, all jar files found in that directory will be added + // as class path entries. + final class JarFilenameFilter implements FilenameFilter { + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + } + + for (String path : paths) { + path = path.trim(); + if (path.length() > 0) { + if (path.endsWith("*.jar")) { + path = path.substring(0, path.length() - 5); + File dir = new File(path); + File[] jars = dir.listFiles(new JarFilenameFilter()); + if (jars != null && jars.length > 0) { + jarFiles.addAll(Arrays.asList(jars)); + } + } else { + File pathFile = new File(path); + jarFiles.add(pathFile); + } + } + } + + return jarFiles; + } + +} diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmResourceKey.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmResourceKey.java new file mode 100644 index 0000000..70053cb --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmResourceKey.java @@ -0,0 +1,179 @@ +/* + * RHQ Management Platform + * Copyright (C) 2012 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.plugins.jmx.util; + +/** + * @author Ian Springer + */ +public class JvmResourceKey { + + private String mainClassName; + private String explicitValue; + private Integer jmxRemotingPort; + private String connectorAddress; + private transient Type type; + + public static JvmResourceKey fromExplicitValue(String mainClassName, String explicitValue) { + JvmResourceKey instance = new JvmResourceKey(mainClassName); + instance.explicitValue = explicitValue; + instance.type = Type.Explicit; + return instance; + } + + public static JvmResourceKey fromJmxRemotingPort(String mainClassName, int jmxRemotingPort) { + JvmResourceKey instance = new JvmResourceKey(mainClassName); + instance.jmxRemotingPort = jmxRemotingPort; + instance.type = (mainClassName != null) ? Type.JmxRemotingPort : Type.Legacy; + return instance; + } + + public static JvmResourceKey fromConnectorAddress(String connectorAddress) { + JvmResourceKey instance = new JvmResourceKey(null); + instance.connectorAddress = connectorAddress; + instance.type = Type.ConnectorAddress; + return instance; + } + + public static JvmResourceKey valueOf(String string) { + JvmResourceKey instance; + if (string.contains("{") && string.endsWith("}")) { + String mainClassName = string.substring(0, string.indexOf('{')); + String explicitValue = string.substring(string.indexOf('{') + 1, string.length() - 1); + instance = JvmResourceKey.fromExplicitValue(mainClassName, explicitValue); + } else if (string.contains("(") && string.endsWith(")")) { + String mainClassName = string.substring(0, string.indexOf('(')); + String value = string.substring(string.indexOf('(') + 1, string.length() - 1); + int jmxRemotingPort = Integer.parseInt(value); + instance = JvmResourceKey.fromJmxRemotingPort(mainClassName, jmxRemotingPort); + } else { + + try { + int jmxRemotingPort = Integer.parseInt(string); + // It's a legacy key, e.g. "9999". + instance = JvmResourceKey.fromJmxRemotingPort(null, jmxRemotingPort); + } catch (NumberFormatException e) { + // At this point, assume it's a connector address, e.g. + // "service:jmx:iiop://127.0.0.1:7001/jndi/weblogic.management.mbeanservers.runtime". + instance = JvmResourceKey.fromConnectorAddress(string); + } + } + return instance; + } + + public String getMainClassName() { + return mainClassName; + } + + public String getExplicitValue() { + return explicitValue; + } + + public Integer getJmxRemotingPort() { + return jmxRemotingPort; + } + + public String getConnectorAddress() { + return connectorAddress; + } + + public Type getType() { + return type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + JvmResourceKey that = (JvmResourceKey) o; + + if (connectorAddress != null ? !connectorAddress.equals(that.connectorAddress) : that.connectorAddress != null) + return false; + if (explicitValue != null ? !explicitValue.equals(that.explicitValue) : that.explicitValue != null) + return false; + if (jmxRemotingPort != null ? !jmxRemotingPort.equals(that.jmxRemotingPort) : that.jmxRemotingPort != null) + return false; + if (mainClassName != null ? !mainClassName.equals(that.mainClassName) : that.mainClassName != null) + return false; + + return true; + } + + @Override + public int hashCode() { + int result = mainClassName != null ? mainClassName.hashCode() : 0; + result = 31 * result + (explicitValue != null ? explicitValue.hashCode() : 0); + result = 31 * result + (jmxRemotingPort != null ? jmxRemotingPort.hashCode() : 0); + result = 31 * result + (connectorAddress != null ? connectorAddress.hashCode() : 0); + return result; + } + + @Override + public String toString() { + String string; + switch (this.type) { + case Legacy: + string = this.jmxRemotingPort.toString(); + break; + case ConnectorAddress: + string = this.connectorAddress; + break; + case JmxRemotingPort: + string = this.mainClassName + "(" + this.jmxRemotingPort + ")"; + break; + case Explicit: + string = this.mainClassName + "{" + this.explicitValue + "}"; + break; + default: + throw new IllegalStateException("Unsupported key type: " + this.type); + } + return string; + } + + public enum Type { + /** + * The legacy format is a simple integer representing the JVM's JMX remoting port, e.g. "9999" + */ + Legacy, + /** + * Manually added JVM's use the JMX connector address as the key, e.g. + * "service:jmx:iiop://127.0.0.1:7001/jndi/weblogic.management.mbeanservers.runtime" + */ + ConnectorAddress, + /** + * The successor of the legacy format; includes the main class name in addition to the JMX remoting port, + * e.g. "org.example.Main(9999)" + */ + JmxRemotingPort, + /** + * This format is used when a key is explicitly specified on the JVM's command line via the org.rhq.resourceKey + * sysprop; the main class name is also included, e.g. "org.example.Main{foo}" + */ + Explicit + } + + private JvmResourceKey(String mainClassName) { + this.mainClassName = mainClassName; + } + +} diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmUtility.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmUtility.java new file mode 100644 index 0000000..c4c93b3 --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/JvmUtility.java @@ -0,0 +1,118 @@ +/* + * RHQ Management Platform + * Copyright (C) 2011-2012 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.plugins.jmx.util; + +import java.io.File; +import java.io.IOException; + +import javax.management.remote.JMXServiceURL; + +import com.sun.tools.attach.VirtualMachine; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.rhq.plugins.jmx.MBeanResourceComponent; + +/** + * @author Ian Springer + */ +public class JvmUtility { + + private static final Log LOG = LogFactory.getLog(MBeanResourceComponent.class); + + private static final String AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS = + "com.sun.management.jmxremote.localConnectorAddress"; + + private static boolean attachApiAvailable; + + static { + try { + Class.forName("com.sun.tools.attach.VirtualMachine"); + attachApiAvailable = true; + } catch (ClassNotFoundException e) { + LOG.warn("JDK tools.jar not found on system classpath - cannot discover JVMs using Sun JVM Attach API; " + + "to fix this, run the RHQ Agent on a JDK, rather than a JRE."); + } + } + + /** + * TODO + * + * @param pid the process ID of a JVM (java*) process + * + * @return + * + * @throws Exception + */ + public static JMXServiceURL extractJMXServiceURL(long pid) { + if (!attachApiAvailable) { + LOG.debug("Returning null since the Attach API is not available..."); + return null; + } + LOG.debug("Attaching to JVM for java process with PID [" + pid + "]..."); + JMXServiceURL url; + try { + VirtualMachine vm = VirtualMachine.attach(String.valueOf(pid)); + LOG.debug("Attached to JVM [" + vm + "]."); + + String jmxConnectorAddress = vm.getAgentProperties().getProperty(AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS); + LOG.debug("Connector address for JVM [" + vm + "] is [" + jmxConnectorAddress + "]."); + if (jmxConnectorAddress == null) { + // java.home always points to the jre dir (e.g. /usr/java/default/jre). + String jreDir = vm.getSystemProperties().getProperty("java.home"); + // management-agent.jar is included with the v6 JRE, so we can rely on it always being there. + File jmxAgentJarFile = new File(jreDir, "lib/management-agent.jar"); + String jmxAgentJar = jmxAgentJarFile.getCanonicalPath(); + try { + vm.loadAgent(jmxAgentJar); + } catch (Exception e) { + throw new RuntimeException("Failed to load JVM agent from [" + jmxAgentJar + "].", e); + } + + // JMX agent is started - get the connector address. + LOG.debug("JMX agent started - getting the connector address..."); + jmxConnectorAddress = vm.getAgentProperties().getProperty(AGENT_PROP_JMXREMOTE_LOCAL_CONNECTOR_ADDRESS); + if (jmxConnectorAddress == null) { + throw new RuntimeException("Failed to determine JMX connector address."); + } + } + + try { + vm.detach(); + } catch (Exception e) { + // We already succeeded in obtaining the connector address, so just log this, rather than throwing an exception. + LOG.error("Failed to detach from JVM [" + vm + "].", e); + } + + url = new JMXServiceURL(jmxConnectorAddress); + } catch (Exception e) { + throw new RuntimeException("Failed to extract JMX service URL for process with PID [" + pid + "].", e); + } + + LOG.debug("JMX service URL for java process with PID [" + pid + "]: " + url); + return url; + } + + private JvmUtility() { + } + +} diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/Socket.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/Socket.java new file mode 100644 index 0000000..142cf1c --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/Socket.java @@ -0,0 +1,120 @@ +/* + * RHQ Management Platform + * Copyright (C) 2011-2012 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.rhq.plugins.jmx.util; + +/** + * @author Ian Springer + */ +public class Socket implements Comparable<Socket> { + + private static final String STRING_DELIMITER = "/"; + + private Protocol protocol; + private String host; // either an IP address or a host name + private long port; + + public static Socket valueOf(String string) { + String[] values = string.split(STRING_DELIMITER); + Protocol protocol = Protocol.valueOf(values[0]); + String host = values[1]; + long port = Long.valueOf(values[2]); + + return new Socket(protocol, host, port); + } + + public Socket(Protocol protocol, String host, long port) { + if (protocol == null) { + throw new IllegalArgumentException("protocol must be non-null."); + } + if (host == null) { + throw new IllegalArgumentException("host must be non-null."); + } + if (port < 0 || port > 65535) { + throw new IllegalArgumentException("invalid port: " + port); + } + + this.protocol = protocol; + this.host = host; + this.port = port; + } + + public Protocol getProtocol() { + return protocol; + } + + public String getHost() { + return host; + } + + public long getPort() { + return port; + } + + public enum Protocol { TCP, UDP } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Socket that = (Socket) o; + + if (port != that.port) return false; + if (!host.equals(that.host)) return false; + if (protocol != that.protocol) return false; + + return true; + } + + @Override + public int hashCode() { + int result = protocol.hashCode(); + result = 31 * result + host.hashCode(); + result = 31 * result + (int) (port ^ (port >>> 32)); + return result; + } + + @Override + public String toString() { + return this.protocol + STRING_DELIMITER + this.host + STRING_DELIMITER + this.port; + } + + @Override + public int compareTo(Socket that) { + if (this == that) { + return 0; + } + + int result = this.protocol.compareTo(that.protocol); + if (result == 0) { + result = this.host.compareTo(that.host); + if (result == 0) { + result = Long.valueOf(this.port).compareTo(that.port); + + } + } + return result; + } + +} diff --git a/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/JMXPluginTest.java b/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/JMXPluginTest.java index 791a578..ee172ba 100644 --- a/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/JMXPluginTest.java +++ b/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/JMXPluginTest.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2011 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -28,13 +28,22 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.net.ServerSocket; +import java.net.Socket; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set;
import org.rhq.core.domain.configuration.Configuration; +import org.rhq.core.domain.resource.ResourceCategory; +import org.rhq.core.domain.resource.ResourceType; +import org.rhq.plugins.jmx.JMXDiscoveryComponent; +import org.rhq.plugins.jmx.util.JvmResourceKey; import org.testng.annotations.AfterSuite; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; @@ -61,38 +70,38 @@ import org.rhq.core.pluginapi.operation.OperationFacet; * Integration test for the JMX plugin. * * @author Greg Hinkle + * @author Ian Springer */ public class JMXPluginTest { - static final String PROGRAM_CLASS = "org.rhq.plugins.jmx.test.JMXPluginTest$TestProgram"; - static final String MONITORABLE = "-Dcom.sun.management.jmxremote.port=9921 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"; - static final String MBEAN_SERVER_ADDRESS = "service:jmx:rmi:///jndi/rmi://localhost:9921/jmxrmi";
- static final String PROJECT_ARTIFACT_ID_SYSPROP = "project.artifactId"; - static final String PROJECT_VERSION_SYSPROP = "project.version"; + private static final int JMX_REMOTING_PORT1 = 9921; + private static final int JMX_REMOTING_PORT2 = 9922;
- private Process testProgram; - private InventoryManager inventoryManager; private static final String PLUGIN_NAME = "JMX"; + private static final String SERVER_TYPE_NAME = "JMX Server"; + private static final String EXPLICIT_RESOURCE_KEY1 = "foo1"; + private static final String EXPLICIT_RESOURCE_KEY2 = "foo2";
+ private List<Process> testServerJvms = new ArrayList<Process>(); + + private InventoryManager inventoryManager; + @BeforeSuite public void start() { try { - String javaHome = System.getProperty("java.home"); - String javaCmd = javaHome + "/bin/java"; + // Start the test server JVMs. + this.testServerJvms.add(startTestServerJvm("-Dcom.sun.management.jmxremote.port=" + JMX_REMOTING_PORT1, + "-Dcom.sun.management.jmxremote.ssl=false", "-Dcom.sun.management.jmxremote.authenticate=false"));
- ProcessBuilder processBuilder = new ProcessBuilder(javaCmd, "-cp", "target/test-classes", - "-Dcom.sun.management.jmxremote.port=9921", "-Dcom.sun.management.jmxremote.ssl=false", - "-Dcom.sun.management.jmxremote.authenticate=false", PROGRAM_CLASS); - processBuilder.redirectErrorStream(true); - testProgram = processBuilder.start(); + this.testServerJvms.add(startTestServerJvm("-D" + JMXDiscoveryComponent.SYSPROP_RHQ_RESOURCE_KEY + "=" + + EXPLICIT_RESOURCE_KEY1));
- OutputReader or = new OutputReader(testProgram.getInputStream()); - Thread ort = new Thread(or); - ort.setDaemon(true); - ort.start(); + this.testServerJvms.add(startTestServerJvm("-Dcom.sun.management.jmxremote.port=" + JMX_REMOTING_PORT2, + "-Dcom.sun.management.jmxremote.ssl=false", "-Dcom.sun.management.jmxremote.authenticate=false", + "-D" + JMXDiscoveryComponent.SYSPROP_RHQ_RESOURCE_KEY + "=" + EXPLICIT_RESOURCE_KEY2));
- // Give it time to start - Thread.sleep(2000); + // Give them time to fully start. + Thread.sleep(3000);
File pluginDir = new File("target/itest/plugins"); PluginContainerConfiguration pcConfig = new PluginContainerConfiguration(); @@ -102,10 +111,9 @@ public class JMXPluginTest {
PluginContainer.getInstance().setConfiguration(pcConfig); PluginContainer.getInstance().initialize(); - System.out.println("PC Started"); - for (String plugin : PluginContainer.getInstance().getPluginManager().getMetadataManager().getPluginNames()) { - System.out.println("PLUGIN: " + plugin); - } + + Set<String> pluginNames = PluginContainer.getInstance().getPluginManager().getMetadataManager().getPluginNames(); + System.out.println("PC started with plugins " + pluginNames + ".");
this.inventoryManager = PluginContainer.getInstance().getInventoryManager(); } catch (Throwable t) { @@ -116,17 +124,43 @@ public class JMXPluginTest { } }
+ private Process startTestServerJvm(String... jvmArgs) throws IOException { + String javaHome = System.getProperty("java.home"); + String javaCmd = javaHome + "/bin/java"; + + List<String> args = new ArrayList<String>(); + args.add(javaCmd); + args.add("-cp"); + args.add("target/test-classes"); + args.addAll(Arrays.asList(jvmArgs)); + args.add(TestProgram.class.getName()); + + ProcessBuilder processBuilder = new ProcessBuilder(args); + processBuilder.redirectErrorStream(true); + Process process = processBuilder.start(); + + OutputReader outputReader = new OutputReader(process.getInputStream()); + Thread outputReaderThread = new Thread(outputReader); + outputReaderThread.setDaemon(true); + outputReaderThread.start(); + + return process; + } + @AfterSuite - public void stop() { + public void stop() { PluginContainer.getInstance().shutdown(); - testProgram.destroy(); + + for (Process process : this.testServerJvms) { + process.destroy(); + } }
@Test public void testPluginLoad() { PluginManager pluginManager = PluginContainer.getInstance().getPluginManager(); PluginEnvironment pluginEnvironment = pluginManager.getPlugin(PLUGIN_NAME); - assert pluginEnvironment != null : "Null environment, plugin not loaded"; + assert (pluginEnvironment != null) : "Null environment, plugin not loaded"; assert (pluginEnvironment.getPluginName().equals(PLUGIN_NAME)); }
@@ -135,9 +169,43 @@ public class JMXPluginTest { InventoryReport report = PluginContainer.getInstance().getInventoryManager().executeServerScanImmediately(); assert report != null; System.out.println("Discovery took: " + (report.getEndTime() - report.getStartTime()) + "ms"); + Resource platform = PluginContainer.getInstance().getInventoryManager().getPlatform(); - Set<Resource> servers = platform.getChildResources(); - System.out.println("Found " + servers.size() + " servers"); + + Set<Resource> jmxServers = getChildResourcesOfType(platform, new ResourceType(SERVER_TYPE_NAME, PLUGIN_NAME, + ResourceCategory.SERVER, null)); + System.out.println("Found " + jmxServers.size() + " JMX Servers:"); + + boolean foundJmxRemotingServer = false; + boolean foundExplicitKey1Server = false; + boolean foundExplicitKey2Server = false; + for (Resource jmxServer : jmxServers) { + System.out.println(" * " + jmxServer); + JvmResourceKey key = JvmResourceKey.valueOf(jmxServer.getResourceKey()); + switch (key.getType()) { + case Explicit: + if (key.getExplicitValue().equals(EXPLICIT_RESOURCE_KEY1)) { + assert key.getMainClassName().equals(TestProgram.class.getName()); + foundExplicitKey1Server = true; + } else if (key.getExplicitValue().equals(EXPLICIT_RESOURCE_KEY2)) { + assert key.getMainClassName().equals(TestProgram.class.getName()); + foundExplicitKey2Server = true; + } + break; + case JmxRemotingPort: + if (key.getMainClassName().equals(TestProgram.class.getName()) && + key.getJmxRemotingPort().equals(JMX_REMOTING_PORT1)) { + assert key.getMainClassName().equals(TestProgram.class.getName()); + foundJmxRemotingServer = true; + } + break; + default: + throw new IllegalStateException("Unsupported key type: " + key.getType()); + } + } + assert foundJmxRemotingServer : "JMX Remoting server not found."; + assert foundExplicitKey1Server : "Explicit key server not found."; + assert foundExplicitKey2Server : "JMX Remoting + explicit key server not found."; }
@Test(dependsOnMethods = "testServerDiscovery") @@ -147,27 +215,20 @@ public class JMXPluginTest { Resource platform = PluginContainer.getInstance().getInventoryManager().getPlatform();
assert platform != null; - Set<Resource> childResources = platform.getChildResources(); - assert childResources != null; - - /*System.out.println("RUNTIME SERVERS: " + childResources.size()); - * for (Resource server : platform.getChildResources()) { System.out.println("Server: " + server.toString()); - * System.out.println("Found with " + server.getChildResources().size() + " child services");}*/ - InventoryPrinter.outputInventory(new PrintWriter(System.out), false); - }
- @Test(dependsOnMethods = "testServerDiscovery") - public void testNumberOfServers() throws Exception { - InventoryReport report = PluginContainer.getInstance().getInventoryManager().executeServiceScanImmediately(); - assert report != null; - Resource platform = PluginContainer.getInstance().getInventoryManager().getPlatform(); + Set<Resource> jmxServers = getChildResourcesOfType(platform, new ResourceType(SERVER_TYPE_NAME, PLUGIN_NAME, + ResourceCategory.SERVER, null));
- assert platform != null; - Set<Resource> childResources = platform.getChildResources(); - assert childResources != null; + for (Resource jmxServer : jmxServers) { + Set<Resource> childResources = jmxServer.getChildResources(); + // Each JMX Server should have exactly six singleton child Resources with the following types: + // Operating System, Threading, VM Class Loading System, VM Compilation System, VM Memory System, and + // java.util.logging. + assert childResources.size() == 6 : jmxServer + " does not have 6 child Resources - child Resources: " + + childResources; + }
- // TODO GH: ccrouch... why would this be two now? - //assert childResources.size() == 2 : "Not all Server instances were found."; + InventoryPrinter.outputInventory(new PrintWriter(System.out), false); }
@Test(dependsOnMethods = "testServiceDiscovery") @@ -198,8 +259,6 @@ public class JMXPluginTest {
@Test(dependsOnMethods = "testServiceDiscovery") public void testOperation() throws Exception { - // TODO GH: Test only runs when tested on JDK 6 as the JVM services aren't detected - // currently for the test platform Resource platform = PluginContainer.getInstance().getInventoryManager().getPlatform(); for (Resource server : platform.getChildResources()) { List<Resource> services = new ArrayList<Resource>(server.getChildResources()); @@ -253,6 +312,19 @@ public class JMXPluginTest { * } } */
+ private static Set<Resource> getChildResourcesOfType(Resource platform, ResourceType resourceType) { + Set<Resource> childResources = platform.getChildResources(); + Set<Resource> results = new HashSet<Resource>(); + for (Resource resource : childResources) { + ResourceType childResourceType = resource.getResourceType(); + if (childResourceType.getPlugin().equals(resourceType.getPlugin()) && + childResourceType.getName().equals(resourceType.getName())) { + results.add(resource); + } + } + return results; + } + public static class OutputReader implements Runnable { InputStream inputStream;
@@ -262,10 +334,10 @@ public class JMXPluginTest {
public void run() { try { - BufferedReader r = new BufferedReader(new InputStreamReader(inputStream)); + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line; - while ((line = r.readLine()) != null) { + while ((line = reader.readLine()) != null) { System.out.println("__" + line); } } catch (IOException e) { @@ -277,8 +349,35 @@ public class JMXPluginTest { public static class TestProgram implements Runnable { long started = System.currentTimeMillis();
- public static void main(String[] args) { - System.out.println("Test program running..."); + public static void main(String[] args) { + final ServerSocket serverSocket; + try { + serverSocket = new ServerSocket(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + + RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); + String jvmName = runtimeMXBean.getName(); + int atIndex = jvmName.indexOf('@'); + String pid = (atIndex != -1) ? jvmName.substring(0, atIndex) : "?"; + + System.out.println("Test server JVM with pid [" + pid + "] listening on port [" + + serverSocket.getLocalPort() + "]..."); + Runnable runnable = new Runnable() { + public void run() { + Socket socket; + try { + while ((socket = serverSocket.accept()) != null) { + socket.close(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }; + runnable.run(); + TestProgram tp = new TestProgram(); tp.run(); } @@ -289,8 +388,10 @@ public class JMXPluginTest { try { Thread.sleep(5000); } catch (InterruptedException e) { + // ignore } } } } -} \ No newline at end of file + +}
commit f83f9b96e135f7f6e4dbc505c24218103847835c Author: Ian Springer ian.springer@redhat.com Date: Mon Jan 23 14:12:11 2012 -0500
move ObjectNameQueryUtility and ParentDefinedJMXServerNamingUtility classes to util subpackage (missed files from earlier commit); minor code cleanup in InternalJMXServerDiscoveryComponent
diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/InternalJMXServerDiscoveryComponent.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/InternalJMXServerDiscoveryComponent.java index fcd16fc..ca5e5e5 100644 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/InternalJMXServerDiscoveryComponent.java +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/InternalJMXServerDiscoveryComponent.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -25,8 +25,6 @@ package org.rhq.plugins.jmx; import java.util.HashSet; import java.util.Set;
-import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.support.metadata.InternalVMTypeDescriptor;
import org.rhq.core.domain.configuration.Configuration; @@ -34,24 +32,32 @@ 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.jmx.util.ParentDefinedJMXServerNamingUtility;
+/** + * Discovers a singleton Resource representing the local JVM (i.e. the JVM in which the RHQ Plugin Container is running). + */ public class InternalJMXServerDiscoveryComponent implements ResourceDiscoveryComponent { - private static final Log log = LogFactory.getLog(InternalJMXServerDiscoveryComponent.class); + + private static final String RESOURCE_KEY = "InternalVM"; + private static final String DEFAULT_RESOURCE_DESCRIPTION = "JVM of RHQ Plugin Container";
public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext context) { - Set<DiscoveredResourceDetails> found = new HashSet<DiscoveredResourceDetails>(); + Set<DiscoveredResourceDetails> discoveredResources = new HashSet<DiscoveredResourceDetails>(1);
- DiscoveredResourceDetails localVM = new DiscoveredResourceDetails(context.getResourceType(), "InternalVM", - ParentDefinedJMXServerNamingUtility.getJVMName(context), System.getProperty("java.version"), - "VM of plugin container", null, null); + String name = ParentDefinedJMXServerNamingUtility.getJVMName(context); + String version = System.getProperty("java.version"); + DiscoveredResourceDetails localVM = new DiscoveredResourceDetails(context.getResourceType(), RESOURCE_KEY, + name, version, DEFAULT_RESOURCE_DESCRIPTION, context.getDefaultPluginConfiguration(), + context.getSystemInformation().getThisProcess()); Configuration configuration = localVM.getPluginConfiguration(); configuration.put(new PropertySimple(JMXDiscoveryComponent.CONNECTOR_ADDRESS_CONFIG_PROPERTY, "Local Connection")); configuration.put(new PropertySimple(JMXDiscoveryComponent.CONNECTION_TYPE, InternalVMTypeDescriptor.class .getName())); + discoveredResources.add(localVM);
- found.add(localVM); - - return found; + return discoveredResources; } -} \ No newline at end of file + +} diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/MBeanResourceDiscoveryComponent.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/MBeanResourceDiscoveryComponent.java index 471dcbe..8cf89de 100644 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/MBeanResourceDiscoveryComponent.java +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/MBeanResourceDiscoveryComponent.java @@ -38,6 +38,7 @@ import org.rhq.core.domain.resource.ResourceType; 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.jmx.util.ObjectNameQueryUtility;
/** * This is meant to be a generic discovery component for MBeans. In order to use it you configure your resource @@ -87,8 +88,9 @@ public class MBeanResourceDiscoveryComponent<T extends JMXComponent<?>> implemen // Public --------------------------------------------
/** - * Same as {@link discoverResources(ResourceDiscoveryContext<T>)} with additional param. - * @param skipUnknownProps Should we skip over MBeans that have unknown properties in their ObjectName + * Same as {@link #discoverResources(ResourceDiscoveryContext<T>)} with additional param. + * + * @param skipUnknownProps if true, skip over MBeans that have unknown properties in their ObjectName */ public Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<T> context, boolean skipUnknownProps) {
commit 01eb02937bb322f4efb53224bbd7889498f9df2d Author: Ian Springer ian.springer@redhat.com Date: Mon Jan 23 12:12:21 2012 -0500
add 'commandLine' prop to plugin config def for JMX Server type (this prop was already being used in the plugin's code)
diff --git a/modules/plugins/jmx/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jmx/src/main/resources/META-INF/rhq-plugin.xml index 5572bf0..97a4025 100644 --- a/modules/plugins/jmx/src/main/resources/META-INF/rhq-plugin.xml +++ b/modules/plugins/jmx/src/main/resources/META-INF/rhq-plugin.xml @@ -6,7 +6,6 @@ description="Supports management of JMX MBean Servers via various remoting systems." pluginLifecycleListener="JMXPluginLifecycleListener" ampsVersion="2.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:xmlns:rhq-plugin" xmlns:c="urn:xmlns:rhq-configuration">
@@ -40,11 +39,12 @@ </c:property-options> </c:simple-property>
- <c:simple-property name="connectorAddress" required="true" type="string" description="The connection url in the form of a JMXServiceURL"/> - <c:simple-property name="installURI" required="false" type="string" description="The installation path for the selected server type which will be used to find client libraries (if appropriate)."/> + <c:simple-property name="connectorAddress" required="false" type="string" description="The connection url in the form of a JMXServiceURL - this should only be set if the JVM has JMX Remoting enabled"/> + <c:simple-property name="installURI" required="false" type="string" description="The installation path for the selected server type which will be used to find client libraries (if appropriate)"/> <c:simple-property name="principal" required="false" description="The login principal/username"/> <c:simple-property name="credentials" required="false" type="password" description="The login credentials/password"/> <c:simple-property name="additionalClassPathEntries" required="false" type="string" description="Comma-separated list of directories and filenames that contain resources and classes needed to communicate with the JMX Server and its MBeans. If you specify 'some/directory/*.jar', all jars found in the given directory will be added."/> + <c:simple-property name="commandLine" required="false" type="string" description="the command line of the JVM at the time it was discovered - only used by JVMs with type Local; if the command line of the JVM changes, this property's value will need to be updated accordingly in order for RHQ to connect to the JVM"/>
<c:template name="JDK 5" description="Connect to JDK 5"> <c:simple-property name="type" default="org.mc4j.ems.connection.support.metadata.J2SE5ConnectionTypeDescriptor"/> @@ -79,7 +79,7 @@ In order to do this, you must pass in some system properties when starting your application's Java virtual machine.</p>
- <p> To run a JVM with JMX remoting enabled without authentication, you must pass in the following system + <p>To run a JVM with JMX remoting enabled without authentication, you must pass in the following system properties:</p>
<ul>
commit c56e22e9227dd87f5411ef7dbd17c03833dbea8c Author: Ian Springer ian.springer@redhat.com Date: Mon Jan 23 12:10:57 2012 -0500
move ObjectNameQueryUtility and ParentDefinedJMXServerNamingUtility classes to util subpackage (missed file from earlier commit)
diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/EmbeddedJMXServerDiscoveryComponent.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/EmbeddedJMXServerDiscoveryComponent.java index 4b48067..58f7698 100644 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/EmbeddedJMXServerDiscoveryComponent.java +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/EmbeddedJMXServerDiscoveryComponent.java @@ -37,6 +37,7 @@ 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.system.ProcessInfo; +import org.rhq.plugins.jmx.util.ParentDefinedJMXServerNamingUtility;
/** * This discovery component can be used to include JVM information under a parent Process oriented server that supports
commit ebe55c758809eb79b4fd6bbbf01413ae76aef646 Author: Ian Springer ian.springer@redhat.com Date: Fri Jan 20 17:48:30 2012 -0500
cosmetic: minor tweak to comment
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceType.java b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceType.java index 219d828..04600b7 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceType.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/ResourceType.java @@ -844,7 +844,7 @@ public class ResourceType implements Serializable, Comparable<ResourceType> {
// NOTE: It's vital that compareTo() is consistent with equals(), otherwise TreeSets containing ResourceTypes, or // TreeMaps with ResourceTypes as keys, will not work reliably. See the Javadoc for Comparable for a precise - // definition of consistent with equals(). + // definition of "consistent with equals()". @Override public int compareTo(ResourceType that) { if (this.name == null) {
commit e80d575b40ee51b6bf81e4525d853b728f0ced71 Author: Ian Springer ian.springer@redhat.com Date: Fri Jan 20 17:47:49 2012 -0500
make Resource's compareTo impl consistent with its equals() impl, so Resources can be reliably added to TreeSets and TreeMaps; update "inventory" Agent prompt command to sort sibling Resources by type and then by name, rather than just by name - this makes the output much more readable
diff --git a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Resource.java b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Resource.java index c90ed4e..a403cd4 100644 --- a/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Resource.java +++ b/modules/core/domain/src/main/java/org/rhq/core/domain/resource/Resource.java @@ -1789,8 +1789,29 @@ public class Resource implements Comparable<Resource>, Serializable { driftDefinition.setResource(this); }
+ // NOTE: It's vital that compareTo() is consistent with equals(), otherwise TreeSets containing Resources, or + // TreeMaps with Resources as keys, will not work reliably. See the Javadoc for Comparable for a precise + // definition of "consistent with equals()". + @Override public int compareTo(Resource that) { - return this.name.compareTo(that.getName()); + if (this == that) { + return 0; + } + int result; + if (this.name != null) { + result = (that.name != null) ? this.name.compareTo(that.name) : -1; + } else { + result = (that.name == null) ? 0 : 1; + } + if (result == 0) { + // The names are equal - compare the UUIDs to break the tie. + if (this.uuid != null) { + result = (that.uuid != null) ? this.uuid.compareTo(that.uuid) : -1; + } else { + result = (that.uuid == null) ? 0 : 1; + } + } + return result; }
@Override diff --git a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java index e02cd34..4b1fe14 100644 --- a/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java +++ b/modules/core/plugin-container/src/main/java/org/rhq/core/pc/util/InventoryPrinter.java @@ -1,6 +1,6 @@ /* * RHQ Management Platform - * Copyright (C) 2005-2008 Red Hat, Inc. + * Copyright (C) 2005-2012 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -24,11 +24,12 @@ package org.rhq.core.pc.util;
import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Comparator; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet;
import org.rhq.core.domain.measurement.Availability; import org.rhq.core.domain.measurement.AvailabilityType; @@ -282,7 +283,17 @@ public class InventoryPrinter { }
if (recurseChildren) { - Set<Resource> children = new HashSet(resource.getChildResources()); // wrap in new HashSet to avoid CCMEs + Set<Resource> children = new TreeSet<Resource>(new Comparator<Resource>() { + public int compare(Resource o1, Resource o2) { + int result = o1.getResourceType().compareTo(o2.getResourceType()); + if (result == 0) { + // The types are the same - let the Resource.compareTo() break the tie. + result = o1.compareTo(o2); + } + return result; + } + }); // wrap in new TreeSet to avoid CCMEs and to sort by type + children.addAll(resource.getChildResources()); for (Resource child : children) { ResourceContainer childContainer;
commit 4e2594029b4d3368b671b6eef722ede8f7e62efa Author: Ian Springer ian.springer@redhat.com Date: Fri Jan 20 17:44:53 2012 -0500
move ObjectNameQueryUtility and ParentDefinedJMXServerNamingUtility classes to util subpackage (missed this file in my last commit)
diff --git a/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/ObjectNameQueryUtilityTest.java b/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/ObjectNameQueryUtilityTest.java index e9849b7..a7e9f77 100644 --- a/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/ObjectNameQueryUtilityTest.java +++ b/modules/plugins/jmx/src/test/java/org/rhq/plugins/jmx/test/ObjectNameQueryUtilityTest.java @@ -27,11 +27,11 @@ import java.util.Collections; import javax.management.MalformedObjectNameException; import javax.management.ObjectName;
+import org.rhq.plugins.jmx.util.ObjectNameQueryUtility; import org.testng.annotations.Test;
import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; -import org.rhq.plugins.jmx.ObjectNameQueryUtility;
public class ObjectNameQueryUtilityTest { @Test
commit d06bde3ab789c06f603836525d7281e7a96bad85 Author: Ian Springer ian.springer@redhat.com Date: Fri Jan 20 17:43:25 2012 -0500
move ObjectNameQueryUtility and ParentDefinedJMXServerNamingUtility classes to util subpackage
diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/AbstractMessagingDiscoveryComponent.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/AbstractMessagingDiscoveryComponent.java index 4d1d19d..3de8057 100644 --- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/AbstractMessagingDiscoveryComponent.java +++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/AbstractMessagingDiscoveryComponent.java @@ -38,7 +38,7 @@ import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails; import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; import org.rhq.plugins.jmx.JMXComponent; import org.rhq.plugins.jmx.MBeanResourceDiscoveryComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * Abstract base class to discover JBossMessaging and JBossMQ related stuff 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 9cc19bc..3169376 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 @@ -114,7 +114,7 @@ import org.rhq.plugins.jbossas.util.JarContentDelegate; import org.rhq.plugins.jbossas.util.XMLConfigurationEditor; import org.rhq.plugins.jmx.JMXComponent; import org.rhq.plugins.jmx.JMXDiscoveryComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * Resource component for managing JBoss AS 3.2.3 through 4.2.x, and JBoss EAP and SOA-P 4.x. diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASTomcatConnectorDiscoveryComponent.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASTomcatConnectorDiscoveryComponent.java index 30068e8..0adc4db 100644 --- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASTomcatConnectorDiscoveryComponent.java +++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/JBossASTomcatConnectorDiscoveryComponent.java @@ -40,7 +40,7 @@ import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails; import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; import org.rhq.plugins.jmx.JMXComponent; import org.rhq.plugins.jmx.MBeanResourceDiscoveryComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * Plugin discovery component for JBoss Web (embedded Tomcat) connectors. The bulk of the discovery is performed by the diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/WarComponent.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/WarComponent.java index b3d218c..1993e7b 100644 --- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/WarComponent.java +++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/WarComponent.java @@ -24,13 +24,9 @@ package org.rhq.plugins.jbossas;
import java.io.File; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Set;
import org.apache.commons.logging.Log; @@ -57,9 +53,9 @@ import org.rhq.core.pluginapi.util.ResponseTimeLogParser; import org.rhq.plugins.jbossas.util.DeploymentUtility; import org.rhq.plugins.jbossas.util.WarDeploymentInformation; import org.rhq.plugins.jbossas.util.WarDiscoveryHelper; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
-/** + /** * A resource component for managing a web application (WAR) deployed to a JBossAS server. * * @author Ian Springer diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/DeploymentUtility.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/DeploymentUtility.java index d915ff0..0a8e8c8 100644 --- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/DeploymentUtility.java +++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/DeploymentUtility.java @@ -48,9 +48,9 @@ import org.mc4j.ems.connection.bean.EmsBean; import org.mc4j.ems.connection.bean.attribute.EmsAttribute; import org.mc4j.ems.connection.bean.operation.EmsOperation;
-import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
-/** + /** * Accesses the MainDeployer mbean to find the deployment files behind services. * * @author Greg Hinkle diff --git a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/WarDiscoveryHelper.java b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/WarDiscoveryHelper.java index 964223f..48d4c46 100644 --- a/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/WarDiscoveryHelper.java +++ b/modules/plugins/jboss-as/src/main/java/org/rhq/plugins/jbossas/util/WarDiscoveryHelper.java @@ -42,6 +42,8 @@ import org.apache.commons.logging.LogFactory; import org.mc4j.ems.connection.EmsConnection; import org.mc4j.ems.connection.bean.EmsBean; import org.mc4j.ems.connection.bean.attribute.EmsAttribute; + +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility; import org.xml.sax.InputSource;
import org.rhq.core.domain.configuration.Configuration; @@ -53,7 +55,6 @@ import org.rhq.plugins.jbossas.JBossASServerComponent; import org.rhq.plugins.jbossas.WarComponent; import org.rhq.plugins.jbossas.WarDiscoveryComponent; import org.rhq.plugins.jmx.MBeanResourceComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility;
/** * Provides helper methods that are used by both {@link WarDiscoveryComponent} and {@link EmbeddedWarDiscoveryComponent} diff --git a/modules/plugins/jboss-cache-v3/src/main/java/org/rhq/plugins/jbosscache3/JBossCacheDiscoveryComponent.java b/modules/plugins/jboss-cache-v3/src/main/java/org/rhq/plugins/jbosscache3/JBossCacheDiscoveryComponent.java index 86bd02f..bfc3fc4 100644 --- a/modules/plugins/jboss-cache-v3/src/main/java/org/rhq/plugins/jbosscache3/JBossCacheDiscoveryComponent.java +++ b/modules/plugins/jboss-cache-v3/src/main/java/org/rhq/plugins/jbosscache3/JBossCacheDiscoveryComponent.java @@ -40,7 +40,7 @@ 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.jbossas5.ProfileServiceComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * diff --git a/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheComponent.java b/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheComponent.java index 3ec0276..68050e0 100644 --- a/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheComponent.java +++ b/modules/plugins/jboss-cache/src/main/java/org/rhq/plugins/jbosscache/JBossCacheComponent.java @@ -63,7 +63,7 @@ import org.rhq.core.pluginapi.operation.OperationFacet; import org.rhq.core.pluginapi.operation.OperationResult; import org.rhq.plugins.jbossas.util.DeploymentUtility; import org.rhq.plugins.jmx.JMXComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * Get statistic for JBossCache instances diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ObjectNameQueryUtility.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ObjectNameQueryUtility.java deleted file mode 100644 index a3f46ed..0000000 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ObjectNameQueryUtility.java +++ /dev/null @@ -1,218 +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, 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.plugins.jmx; - -import org.rhq.core.domain.configuration.Configuration; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.HashSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * A utility class to help in querying object names and utilizing parts of the object name for setting configuration - * values and changing string messages. A template that you'd build this utility with could look like - * "foo:name=%myName%,type=myType". This will be "translated" into a valid JMX objectName Query of the form - * "foo:type=myType,*". It will also detect that we've got a variable defined as "myName" that we'd like to later match - * to. - * - * <p/>We can then find beans with the query and apply their objectName properties to the object and use those found - * values to rewrite strings or setup configuration properities. For example, we've got a detected object name for the - * above template "foo:name=bar,type=myType". We set its detected keys against this utility and we now have a variable - * defined as the key "myName" and the value of "bar". Then I can call formatMessage with "A foo called {myName}" which - * will be translated into "A foo called bar". This can be useful for naming resources and descriptions using parts of a - * mapped ObjectName. - * - * @author Greg Hinkle - */ -public class ObjectNameQueryUtility { - private String queryTemplate; - - private Map<String, String> variableProperties = new HashMap<String, String>(); - - private Map<String, String> variableValues = new HashMap<String, String>(); - - private Set<String> nonVariableProperties = new HashSet<String>(); - - private String translatedQuery; - - /** - * Builds a mapped query utility object and finds the variables in the supplied object name query template. - * - * @param objectNameQueryTemplate string of form "a:b=%c%,d=e,f=%g%" - */ - public ObjectNameQueryUtility(String objectNameQueryTemplate) { - this.queryTemplate = objectNameQueryTemplate; - buildMatchMap(queryTemplate); - } - - /** - * Builds a mapped query utility object and finds the variables in the supplied object name query template. - * This version first translates the objectName template for defined values in provided configuration. This - * is explicitly built for hierarchical objectName models to find the children of parents. - * - * @param objectNameQueryTemplate string of form "a:b=%c%,d=e,f=%g%,h={myParentsH}" - * @param parentConfiguration the config holding the matched values for the object name key property variables - */ - public ObjectNameQueryUtility(String objectNameQueryTemplate, Configuration parentConfiguration) { - - Pattern p = Pattern.compile("\{([^\{\}]*)\}"); - Matcher m = p.matcher(objectNameQueryTemplate); - while (m.find()) { - String objectNameKeyPropVariableName = m.group(1); - String value = parentConfiguration.getSimple(objectNameKeyPropVariableName).getStringValue(); - objectNameQueryTemplate = objectNameQueryTemplate.replaceAll("\{" + objectNameKeyPropVariableName + "\}", value); - } - - this.queryTemplate = objectNameQueryTemplate; - buildMatchMap(this.queryTemplate); - } - - /** - * Set values for properties from an objectName. These are first translated into the "real" keys. e.g. foo:bar=%baz% - * In this case, the property of the bar objectName property will be set into the properties keyed against "baz" - * which is the real key. - * - * @param keyProperties properties from the found objectName to apply - * - * @return true if the objectName properties contained all variable properties or false if some where missing (e.g. - * foo:A=%a%,B=%b% is the queryTemplate but objectName found is foo:A=alpha) - */ - public boolean setMatchedKeyValues(Map<String, String> keyProperties) { - for (String key : keyProperties.keySet()) { - if (this.variableProperties.containsKey(key)) { - String realKey = this.variableProperties.get(key); - String value = keyProperties.get(key); - - this.variableValues.put(realKey, value); - } - } - - // Return true if there are key properties for every variable in the template and false otherwise - return (keyProperties.keySet().containsAll(this.variableProperties.keySet())); - } - - /** - * Format a message with {<key>} formatted replacement keys. - * - * @param message the message to format - * - * @return the formatted text with variables replaced - */ - public String formatMessage(String message) { - for (String key : variableValues.keySet()) { - message = message.replaceAll("\{" + key + "\}", this.variableValues.get(key)); - } - - return message; - } - - /** - * Clears out variables so that a new found bean can be used against the same utility object again. - */ - public void resetVariables() { - this.variableValues.clear(); - } - - public String getQueryTemplate() { - return queryTemplate; - } - - public Map<String, String> getVariableProperties() { - return variableProperties; - } - - public Map<String, String> getVariableValues() { - return variableValues; - } - - public String getTranslatedQuery() { - return translatedQuery; - } - - /** - * Detects the mapped variable object name properties and the resulting object name query that can find matching - * beans. - * - * @param objectNameQueryTemplate a template of the form foo:bar=%baz% - */ - private void buildMatchMap(String objectNameQueryTemplate) { - StringBuilder queryBuilder = new StringBuilder(); - - Pattern p = Pattern.compile("^([^:]*\:)(.*)$"); - Matcher m = p.matcher(objectNameQueryTemplate); - if (!m.find()) { - assert false : "ObjectName did not match expected regular expression: " + objectNameQueryTemplate; - } - - queryBuilder.append(m.group(1)); - String keyProps = m.group(2); - String[] keys = keyProps.split(","); - - boolean firstVar = true; - boolean onlyVar = true; - for (String key : keys) { - Pattern p2 = Pattern.compile("^([^=]*)=\%(.*)\%$"); - Matcher m2 = p2.matcher(key); - if (m2.find()) { - variableProperties.put(m2.group(1), m2.group(2)); - } else { - Pattern p3 = Pattern.compile("^([^=]*)=(.*)$"); - Matcher m3 = p3.matcher(key); - if (m3.find()) { - nonVariableProperties.add(m3.group(1)); - } - - onlyVar = false; - if (firstVar) { - firstVar = false; - } else { - queryBuilder.append(","); - } - - queryBuilder.append(key); - } - } - - if (variableProperties.size() > 0) { - if (!onlyVar) { - queryBuilder.append(","); - } - - queryBuilder.append("*"); - } - - this.translatedQuery = queryBuilder.toString(); - } - - public boolean isContainsExtraKeyProperties(Set<String> strings) { - for (String key : strings) { - if (!nonVariableProperties.contains(key) && !variableProperties.containsKey(key)) { - return true; - } - } - return false; - } -} \ No newline at end of file diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ParentDefinedJMXServerNamingUtility.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ParentDefinedJMXServerNamingUtility.java deleted file mode 100644 index 8817fa7..0000000 --- a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/ParentDefinedJMXServerNamingUtility.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * RHQ Management Platform - * Copyright (C) 2005-2010 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.plugins.jmx; - -import org.rhq.core.domain.configuration.Configuration; -import org.rhq.core.domain.configuration.PropertySimple; -import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; - -/** - * A simple utility class to extract the name of a JMX server from the parent resource's plugin configuration. - * - * @author Lukas Krejci - */ -public class ParentDefinedJMXServerNamingUtility { - public static final String PROPERTY_CHILD_JMX_SERVER_NAME = "childJmxServerName"; - - /** - * Checks if the parent resource's plugin configuration contains a property called {@link #PROPERTY_CHILD_JMX_SERVER_NAME}. - * If such property exists and its value is non-empty, its value is returned. Otherwise the name of the provided - * resource type is returned. - * - * @param context the discovery context to get the parent plugin configuration and current resource type from. - * @return the name that can be used for the JVM - */ - public static String getJVMName(ResourceDiscoveryContext<?> context) { - Configuration parentPluginConfiguration = context.getParentResourceContext().getPluginConfiguration(); - PropertySimple nameProperty = parentPluginConfiguration.getSimple(PROPERTY_CHILD_JMX_SERVER_NAME); - if (nameProperty == null || nameProperty.getStringValue() == null - || nameProperty.getStringValue().trim().length() == 0) { - - return context.getResourceType().getName(); - } else { - return nameProperty.getStringValue(); - } - } -} diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ObjectNameQueryUtility.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ObjectNameQueryUtility.java new file mode 100644 index 0000000..b8454d7 --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ObjectNameQueryUtility.java @@ -0,0 +1,218 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation, and/or the GNU Lesser + * General Public License, version 2.1, also as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.rhq.plugins.jmx.util; + +import org.rhq.core.domain.configuration.Configuration; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A utility class to help in querying object names and utilizing parts of the object name for setting configuration + * values and changing string messages. A template that you'd build this utility with could look like + * "foo:name=%myName%,type=myType". This will be "translated" into a valid JMX objectName Query of the form + * "foo:type=myType,*". It will also detect that we've got a variable defined as "myName" that we'd like to later match + * to. + * + * <p/>We can then find beans with the query and apply their objectName properties to the object and use those found + * values to rewrite strings or setup configuration properties. For example, we've got a detected object name for the + * above template "foo:name=bar,type=myType". We set its detected keys against this utility and we now have a variable + * defined as the key "myName" and the value of "bar". Then I can call formatMessage with "A foo called {myName}" which + * will be translated into "A foo called bar". This can be useful for naming resources and descriptions using parts of a + * mapped ObjectName. + * + * @author Greg Hinkle + */ +public class ObjectNameQueryUtility { + private String queryTemplate; + + private Map<String, String> variableProperties = new HashMap<String, String>(); + + private Map<String, String> variableValues = new HashMap<String, String>(); + + private Set<String> nonVariableProperties = new HashSet<String>(); + + private String translatedQuery; + + /** + * Builds a mapped query utility object and finds the variables in the supplied object name query template. + * + * @param objectNameQueryTemplate string of form "a:b=%c%,d=e,f=%g%" + */ + public ObjectNameQueryUtility(String objectNameQueryTemplate) { + this.queryTemplate = objectNameQueryTemplate; + buildMatchMap(queryTemplate); + } + + /** + * Builds a mapped query utility object and finds the variables in the supplied object name query template. + * This version first translates the objectName template for defined values in provided configuration. This + * is explicitly built for hierarchical objectName models to find the children of parents. + * + * @param objectNameQueryTemplate string of form "a:b=%c%,d=e,f=%g%,h={myParentsH}" + * @param parentConfiguration the config holding the matched values for the object name key property variables + */ + public ObjectNameQueryUtility(String objectNameQueryTemplate, Configuration parentConfiguration) { + + Pattern p = Pattern.compile("\\{([^\\{\\}]*)\\}"); + Matcher m = p.matcher(objectNameQueryTemplate); + while (m.find()) { + String objectNameKeyPropVariableName = m.group(1); + String value = parentConfiguration.getSimple(objectNameKeyPropVariableName).getStringValue(); + objectNameQueryTemplate = objectNameQueryTemplate.replaceAll("\\{" + objectNameKeyPropVariableName + "\\}", value); + } + + this.queryTemplate = objectNameQueryTemplate; + buildMatchMap(this.queryTemplate); + } + + /** + * Set values for properties from an objectName. These are first translated into the "real" keys. e.g. foo:bar=%baz% + * In this case, the property of the bar objectName property will be set into the properties keyed against "baz" + * which is the real key. + * + * @param keyProperties properties from the found objectName to apply + * + * @return true if the objectName properties contained all variable properties or false if some where missing (e.g. + * foo:A=%a%,B=%b% is the queryTemplate but objectName found is foo:A=alpha) + */ + public boolean setMatchedKeyValues(Map<String, String> keyProperties) { + for (String key : keyProperties.keySet()) { + if (this.variableProperties.containsKey(key)) { + String realKey = this.variableProperties.get(key); + String value = keyProperties.get(key); + + this.variableValues.put(realKey, value); + } + } + + // Return true if there are key properties for every variable in the template and false otherwise + return (keyProperties.keySet().containsAll(this.variableProperties.keySet())); + } + + /** + * Format a message with {<key>} formatted replacement keys. + * + * @param message the message to format + * + * @return the formatted text with variables replaced + */ + public String formatMessage(String message) { + for (String key : variableValues.keySet()) { + message = message.replaceAll("\\{" + key + "\\}", this.variableValues.get(key)); + } + + return message; + } + + /** + * Clears out variables so that a new found bean can be used against the same utility object again. + */ + public void resetVariables() { + this.variableValues.clear(); + } + + public String getQueryTemplate() { + return queryTemplate; + } + + public Map<String, String> getVariableProperties() { + return variableProperties; + } + + public Map<String, String> getVariableValues() { + return variableValues; + } + + public String getTranslatedQuery() { + return translatedQuery; + } + + /** + * Detects the mapped variable object name properties and the resulting object name query that can find matching + * beans. + * + * @param objectNameQueryTemplate a template of the form foo:bar=%baz% + */ + private void buildMatchMap(String objectNameQueryTemplate) { + StringBuilder queryBuilder = new StringBuilder(); + + Pattern p = Pattern.compile("^([^:]*\\:)(.*)$"); + Matcher m = p.matcher(objectNameQueryTemplate); + if (!m.find()) { + assert false : "ObjectName did not match expected regular expression: " + objectNameQueryTemplate; + } + + queryBuilder.append(m.group(1)); + String keyProps = m.group(2); + String[] keys = keyProps.split(","); + + boolean firstVar = true; + boolean onlyVar = true; + for (String key : keys) { + Pattern p2 = Pattern.compile("^([^=]*)=\\%(.*)\\%$"); + Matcher m2 = p2.matcher(key); + if (m2.find()) { + variableProperties.put(m2.group(1), m2.group(2)); + } else { + Pattern p3 = Pattern.compile("^([^=]*)=(.*)$"); + Matcher m3 = p3.matcher(key); + if (m3.find()) { + nonVariableProperties.add(m3.group(1)); + } + + onlyVar = false; + if (firstVar) { + firstVar = false; + } else { + queryBuilder.append(","); + } + + queryBuilder.append(key); + } + } + + if (variableProperties.size() > 0) { + if (!onlyVar) { + queryBuilder.append(","); + } + + queryBuilder.append("*"); + } + + this.translatedQuery = queryBuilder.toString(); + } + + public boolean isContainsExtraKeyProperties(Set<String> strings) { + for (String key : strings) { + if (!nonVariableProperties.contains(key) && !variableProperties.containsKey(key)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ParentDefinedJMXServerNamingUtility.java b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ParentDefinedJMXServerNamingUtility.java new file mode 100644 index 0000000..118debc --- /dev/null +++ b/modules/plugins/jmx/src/main/java/org/rhq/plugins/jmx/util/ParentDefinedJMXServerNamingUtility.java @@ -0,0 +1,57 @@ +/* + * RHQ Management Platform + * Copyright (C) 2005-2010 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.plugins.jmx.util; + +import org.rhq.core.domain.configuration.Configuration; +import org.rhq.core.domain.configuration.PropertySimple; +import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; + +/** + * A simple utility class to extract the name of a JMX server from the parent resource's plugin configuration. + * + * @author Lukas Krejci + */ +public class ParentDefinedJMXServerNamingUtility { + public static final String PROPERTY_CHILD_JMX_SERVER_NAME = "childJmxServerName"; + + /** + * Checks if the parent resource's plugin configuration contains a property called {@link #PROPERTY_CHILD_JMX_SERVER_NAME}. + * If such property exists and its value is non-empty, its value is returned. Otherwise the name of the provided + * resource type is returned. + * + * @param context the discovery context to get the parent plugin configuration and current resource type from. + * @return the name that can be used for the JVM + */ + public static String getJVMName(ResourceDiscoveryContext<?> context) { + Configuration parentPluginConfiguration = context.getParentResourceContext().getPluginConfiguration(); + PropertySimple nameProperty = parentPluginConfiguration.getSimple(PROPERTY_CHILD_JMX_SERVER_NAME); + if (nameProperty == null || nameProperty.getStringValue() == null + || nameProperty.getStringValue().trim().length() == 0) { + + return context.getResourceType().getName(); + } else { + return nameProperty.getStringValue(); + } + } +} diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java index 4f334a4..ab3e829 100644 --- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java +++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorComponent.java @@ -45,7 +45,7 @@ import org.rhq.core.pluginapi.configuration.ConfigurationUpdateReport; import org.rhq.core.pluginapi.inventory.InvalidPluginConfigurationException; import org.rhq.core.pluginapi.inventory.ResourceContext; import org.rhq.plugins.jmx.MBeanResourceComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * Plugin component for representing Tomcat connectors. Much of the functionality is left to the super class, diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorDiscoveryComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorDiscoveryComponent.java index 0940910..e6e95ee 100644 --- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorDiscoveryComponent.java +++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatConnectorDiscoveryComponent.java @@ -39,7 +39,7 @@ import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.pluginapi.inventory.DiscoveredResourceDetails; import org.rhq.core.pluginapi.inventory.ResourceDiscoveryContext; import org.rhq.plugins.jmx.MBeanResourceDiscoveryComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * JON plugin discovery component for Tomcat connectors. The bulk of the discovery is performed by the super class. This diff --git a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java index d1c0f59..75f8e92 100644 --- a/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java +++ b/modules/plugins/tomcat/src/main/java/org/jboss/on/plugins/tomcat/TomcatWarComponent.java @@ -74,7 +74,7 @@ import org.rhq.core.util.ZipUtil; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.core.util.file.JarContentFileInfo; import org.rhq.plugins.jmx.MBeanResourceComponent; -import org.rhq.plugins.jmx.ObjectNameQueryUtility; +import org.rhq.plugins.jmx.util.ObjectNameQueryUtility;
/** * A resource component for managing a web application (WAR) deployed to a Tomcat server.
commit 55d2403c5e1c0dceab624242e849cdda9b65f9dc Author: Ian Springer ian.springer@redhat.com Date: Fri Jan 20 17:39:10 2012 -0500
improve javadoc
diff --git a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceDiscoveryComponent.java b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceDiscoveryComponent.java index b695290..3f6da9a 100644 --- a/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceDiscoveryComponent.java +++ b/modules/core/plugin-api/src/main/java/org/rhq/core/pluginapi/inventory/ResourceDiscoveryComponent.java @@ -76,8 +76,9 @@ public interface ResourceDiscoveryComponent<T extends ResourceComponent<?>> { * * @return a set of discovered resource details that were discovered and can be imported/merged into inventory * - * @throws InvalidPluginConfigurationException if a plugin configuration found in the context was somehow invalid - * and thus caused a failure to connect to a resource + * @throws InvalidPluginConfigurationException (used only be the deprecated manual add API) if the plugin + * configuration found in the context was somehow invalid and thus + * caused a failure to connect to a managed resource * @throws Exception if a generic error occurred that caused the discovery to abort */ Set<DiscoveredResourceDetails> discoverResources(ResourceDiscoveryContext<T> context)
rhq-commits@lists.fedorahosted.org