modules/enterprise/server/ear/src/main/application/META-INF/jboss-deployment-structure.xml | 3 modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java | 206 ++++++++++ modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml | 37 + 3 files changed, 240 insertions(+), 6 deletions(-)
New commits: commit 728e7969ba2afcec80d3e0196528d8bcaf3a24e7 Author: Lukas Krejci lkrejci@redhat.com Date: Wed Sep 18 12:53:50 2013 +0200
[BZ 1009042] - make sure rss4j can access Xerces classes in the server.
diff --git a/modules/enterprise/server/ear/src/main/application/META-INF/jboss-deployment-structure.xml b/modules/enterprise/server/ear/src/main/application/META-INF/jboss-deployment-structure.xml index 80a648f..a6f5d90 100644 --- a/modules/enterprise/server/ear/src/main/application/META-INF/jboss-deployment-structure.xml +++ b/modules/enterprise/server/ear/src/main/application/META-INF/jboss-deployment-structure.xml @@ -50,6 +50,9 @@ <module name="org.codehaus.jackson.jackson-core-asl" export="true"/> <module name="org.codehaus.jackson.jackson-jaxrs" export="true"/> <module name="org.codehaus.jackson.jackson-mapper-asl" export="true"/> + + <!-- rss4j (which we include in /lib and is used in some server plugins) directly uses Xerces, so we have to make it visible --> + <module name="org.apache.xerces" export="true"/> </dependencies> </sub-deployment>
commit 7fa1c742509e1aa365abd62f272a17e9edef7cbf Author: Lukas Krejci lkrejci@redhat.com Date: Thu Sep 12 13:06:14 2013 +0200
[BZ 998645] - Collect method invocation stats on AS7 EJBs.
diff --git a/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java new file mode 100644 index 0000000..c1fdf42 --- /dev/null +++ b/modules/plugins/jboss-as-7/src/main/java/org/rhq/modules/plugins/jbossas7/Ejb3BeanRuntimeComponent.java @@ -0,0 +1,206 @@ +/* + * RHQ Management Platform + * Copyright (C) 2013 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package org.rhq.modules.plugins.jbossas7; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.rhq.core.domain.measurement.DataType; +import org.rhq.core.domain.measurement.MeasurementReport; +import org.rhq.core.domain.measurement.MeasurementScheduleRequest; +import org.rhq.core.domain.measurement.calltime.CallTimeData; +import org.rhq.core.domain.measurement.calltime.CallTimeDataValue; +import org.rhq.core.pluginapi.inventory.ResourceComponent; +import org.rhq.core.util.stream.StreamUtil; +import org.rhq.modules.plugins.jbossas7.json.ReadAttribute; +import org.rhq.modules.plugins.jbossas7.json.Result; + +/** + * A specialization of the AS component for EJB3 runtime beans. This kind of beans can collect method invocation + * statistics which is what this class handles. + * + * @author Lukas Krejci + * @since 4.9 + */ +public class Ejb3BeanRuntimeComponent extends BaseComponent<ResourceComponent<?>> { + + private static final String METHODS_ATTRIBUTE = "methods"; + private static final int CALLTIME_METRIC_NAME_PREFIX_LENGTH = "__calltime:".length(); + + private static class StatsRecord implements Serializable { + private static final long serialVersionUID = 1L; + + long invocations; + long total; + } + + private static class Stats implements Serializable { + private static final long serialVersionUID = 1L; + + long collectionTime; + Map<String, StatsRecord> data; + + static Stats fromMap(Map<String, Map<String, Number>> map, String collectedMetric, long collectionTime) { + Stats ret = new Stats(); + ret.collectionTime = collectionTime; + ret.data = new HashMap<String, StatsRecord>(map.size()); + + for(Map.Entry<String, Map<String, Number>> entry : map.entrySet()) { + StatsRecord rec = new StatsRecord(); + String methodName = entry.getKey(); + + rec.invocations = entry.getValue().get("invocations").longValue(); + rec.total = entry.getValue().get(collectedMetric).longValue(); + + ret.data.put(methodName, rec); + } + + return ret; + } + } + + @Override + public void getValues(MeasurementReport report, Set<MeasurementScheduleRequest> metrics) throws Exception { + //we'll handling the rest of the metrics using the super method, but we may leave out some of the requests + //if we handle them here. Right now, just use the obtained set. We only create a copy of the (unmodifiable) set + //of requests if necessary. + Set<MeasurementScheduleRequest> metricsToPassDown = metrics; + + for(MeasurementScheduleRequest request : metrics) { + if (request.getDataType() == DataType.CALLTIME) { + //make a copy to pass down to super class if necessary + if (metricsToPassDown == metrics) { + metricsToPassDown = new HashSet<MeasurementScheduleRequest>(metrics); + } + + metricsToPassDown.remove(request); + + //handle this ourselves + //the name of the metric is actually the name of the stat collected for each method. we then provide + //the calltime data for each method. + + Result result = getASConnection().execute(new ReadAttribute(address, METHODS_ATTRIBUTE)); + Object value = result.getResult(); + if (value instanceof Map) { + String requestedMetric = request.getName().substring(CALLTIME_METRIC_NAME_PREFIX_LENGTH); + + @SuppressWarnings("unchecked") + Map<String, Map<String, Number>> allMethodStats = (Map<String, Map<String, Number>>) value; + + Stats lastCollection = getLastCallTimeCollection(requestedMetric, allMethodStats); + Stats thisCollection = Stats.fromMap(allMethodStats, requestedMetric, System.currentTimeMillis()); + + CallTimeData callTime = new CallTimeData(request); + + fillCallTimeData(callTime, requestedMetric, thisCollection, lastCollection); + + saveCallTimeCollection(requestedMetric, thisCollection); + + report.addData(callTime); + } else { + log.error("Unexpected type of results when querying method stats"); + } + } + } + + super.getValues(report, metricsToPassDown); + } + + private Stats getLastCallTimeCollection(String requestName, Map<String, Map<String, Number>> fallbackValues) throws IOException { + File dataFile = new File(context.getResourceDataDirectory(), requestName); + if (!dataFile.exists()) { + return Stats.fromMap(fallbackValues, requestName, System.currentTimeMillis()); + } else { + ObjectInputStream in = null; + try { + in = new ObjectInputStream(new FileInputStream(dataFile)); + + return (Stats) in.readObject(); + } catch (IOException e) { + throw new IOException("Couldn't read the stored calltime data from file " + dataFile + ".", e); + } catch (ClassNotFoundException e) { + throw new IllegalStateException("Couldn't find plugin API classes. This is serious!", e); + } finally { + StreamUtil.safeClose(in); + } + } + } + + private void saveCallTimeCollection(String requestName, Stats stats) throws IOException { + File dataFile = new File(context.getResourceDataDirectory(), requestName); + + ObjectOutputStream out = null; + try { + out = new ObjectOutputStream(new FileOutputStream(dataFile)); + out.writeObject(stats); + } catch (IOException e) { + throw new IOException("Couldn't write the last collected calltime data to file " + dataFile + ".", e); + } finally { + StreamUtil.safeClose(out); + } + } + + /** + * Given the current and previous stats collected from the AS, this fills in the provided calltime data record with + * the differential values from the last time to this time. + * + * @param callTimeData the calltime record to fill in + * @param requestName the name of the metric being collected + * @param stats the current stats collected from the AS + * @param previousStats the previously collected stats + */ + private void fillCallTimeData(CallTimeData callTimeData, String requestName, Stats stats, Stats previousStats) { + Date startDate = new Date(previousStats.collectionTime); + Date endDate = new Date(stats.collectionTime); + + for(Map.Entry<String, StatsRecord> entry : stats.data.entrySet()) { + String methodName = entry.getKey(); + StatsRecord thisStatsRecord = entry.getValue(); + + StatsRecord previousStatsRecord = previousStats.data.get(methodName); + + long invocations = thisStatsRecord.invocations - previousStatsRecord.invocations; + if (invocations == 0) { + continue; + } + + long total = thisStatsRecord.total - previousStatsRecord.total; + + //AS doesn't really give us this info... + double min = (double) total / invocations; + double max = (double) total / invocations; + + callTimeData.addAggregatedCallData(methodName, startDate, endDate, min, max, total, + invocations); + } + + } +} diff --git a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml index 3960407..af8474b 100644 --- a/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml +++ b/modules/plugins/jboss-as-7/src/main/resources/META-INF/rhq-plugin.xml @@ -903,6 +903,22 @@ </resource-configuration> '>
+ <!ENTITY ejb3BeanStats ' + <metric property="peak-concurrent-invocations" category="performance" dataType="measurement" displayType="summary" + measurementType="dynamic" description="Peak concurrent invocations." + displayName="Peak concurrent invocations"/> + <metric property="invocations" category="performance" dataType="measurement" displayType="summary" + measurementType="trendsup" description="Number of invocations processed." displayName="Invocations"/> + <metric property="wait-time" category="performance" dataType="measurement" displayType="summary" + measurementType="dynamic" description="Time spent waiting to obtain an instance." displayName="Wait Time" + units="milliseconds"/> + <metric property="execution-time" category="performance" dataType="measurement" displayType="summary" + measurementType="trendsup" description="Time spent within a bean method" displayName="Execution Time" + units="milliseconds"/> + <metric property="__calltime:execution-time" category="performance" dataType="calltime" displayType="summary" + description="The execution times of individual methods on a bean" displayName="Method Execution Time" + units="milliseconds" destinationType="Method Name"/> +'> ]> <plugin name="&pluginName;" displayName="JBoss Application Server 7.x" @@ -972,7 +988,6 @@ </results> </operation>
- <metric property="_internal:mgmtRequests" category="performance" dataType="measurement" defaultInterval="120000" displayType="summary" measurementType="trendsup" description="Number of requests sent to the controller" displayName="Number of management requests"/> @@ -2969,6 +2984,7 @@ <c:simple-property name="default-slsb-instance-pool" required="false" type="string" readOnly="true" description="Name of the default stateless bean instance pool, which will be applicable to all stateless EJBs, unless overridden at the deployment or bean level"/> <c:simple-property name="default-stateful-bean-access-timeout:expr" displayName="Default Stateful Bean Access Timeout" required="false" type="string" readOnly="true" defaultValue="5000" description="The default access timeout for stateful beans. The default value is 5000."/> <c:simple-property name="in-vm-remote-interface-invocation-pass-by-value" required="false" type="boolean" readOnly="true" defaultValue="true" description="If set to false, the parameters to invocations on remote interface of an EJB, will be passed by reference. Else, the parameters will be passed by value. The default value is true."/> + <c:simple-property name="enable-statistics" required="false" type="boolean" readOnly="true" defaultValue="false" description="If set to true, enable the collection of invocation statistics."/> </resource-configuration>
<service name="EJB3 Thread Pool (Managed Server)" @@ -5803,6 +5819,7 @@ <c:simple-property name="default-slsb-instance-pool" required="false" type="string" readOnly="false" description="Name of the default stateless bean instance pool, which will be applicable to all stateless EJBs, unless overridden at the deployment or bean level"/> <c:simple-property name="default-stateful-bean-access-timeout:expr" displayName="Default Stateful Bean Access Timeout" required="false" type="string" readOnly="false" defaultValue="5000" description="The default access timeout for stateful beans. The default value is 5000."/> <c:simple-property name="in-vm-remote-interface-invocation-pass-by-value" required="false" type="boolean" readOnly="false" defaultValue="true" description="If set to false, the parameters to invocations on remote interface of an EJB, will be passed by reference. Else, the parameters will be passed by value. The default value is true."/> + <c:simple-property name="enable-statistics" required="false" type="boolean" readOnly="false" defaultValue="false" description="If set to true, enable the collection of invocation statistics."/> </resource-configuration>
<service name="EJB3 Thread Pool (Profile)" @@ -9798,7 +9815,7 @@ </plugin-configuration>
<service name="Message Driven Bean Runtime" - class="BaseComponent" + class="Ejb3BeanRuntimeComponent" discovery="SubsystemDiscovery" description="Bean component included in the deployment.">
@@ -9811,6 +9828,7 @@ <metric property="pool-current-size" description="The current size of the pool."/> <metric property="pool-max-size" description="The maximum size of the pool."/> <metric property="pool-remove-count" description="The number of bean instances that have been removed."/> + &ejb3BeanStats;
<resource-configuration> <c:simple-property name="component-class-name" required="false" type="string" readOnly="true" description="The component's class name."/> @@ -9843,7 +9861,7 @@ </service>
<service name="Singleton Bean Runtime" - class="BaseComponent" + class="Ejb3BeanRuntimeComponent" discovery="SubsystemDiscovery" description="Singleton bean component included in the deployment.">
@@ -9851,6 +9869,8 @@ <c:simple-property name="path" default="singleton-bean" readOnly="true"/> </plugin-configuration>
+ &ejb3BeanStats; + <resource-configuration> <c:simple-property name="component-class-name" required="false" type="string" readOnly="true" description="The component's class name."/> <c:list-property name="declared-roles" description="The roles declared (via @DeclareRoles) on this EJB component."> @@ -9882,7 +9902,7 @@ </service>
<service name="Stateless Session Bean Runtime" - class="BaseComponent" + class="Ejb3BeanRuntimeComponent" discovery="SubsystemDiscovery" description="Stateless session bean component included in the deployment.">
@@ -9895,6 +9915,7 @@ <metric property="pool-current-size" description="The current size of the pool."/> <metric property="pool-max-size" description="The maximum size of the pool."/> <metric property="pool-remove-count" description="The number of bean instances that have been removed."/> + &ejb3BeanStats;
<resource-configuration> <c:simple-property name="component-class-name" required="false" type="string" readOnly="true" description="The component's class name."/> @@ -9927,7 +9948,7 @@ </service>
<service name="Entity Bean Runtime" - class="BaseComponent" + class="Ejb3BeanRuntimeComponent" discovery="SubsystemDiscovery" description="Entity bean component included in the deployment.">
@@ -9940,6 +9961,7 @@ <metric property="pool-current-size" description="The current size of the pool."/> <metric property="pool-max-size" description="The maximum size of the pool."/> <metric property="pool-remove-count" description="The number of bean instances that have been removed."/> + &ejb3BeanStats;
<resource-configuration> <c:simple-property name="component-class-name" required="false" type="string" readOnly="true" description="The component's class name."/> @@ -9952,7 +9974,7 @@ </service>
<service name="Stateful Session Bean Runtime" - class="BaseComponent" + class="Ejb3BeanRuntimeComponent" discovery="SubsystemDiscovery" description="Stateful session bean component included in the deployment.">
@@ -9960,6 +9982,8 @@ <c:simple-property name="path" default="stateful-session-bean" readOnly="true"/> </plugin-configuration>
+ &ejb3BeanStats; + <resource-configuration> <c:simple-property name="component-class-name" required="false" type="string" readOnly="true" description="The component's class name."/> <c:list-property name="declared-roles" description="The roles declared (via @DeclareRoles) on this EJB component."> @@ -11005,6 +11029,7 @@ <c:simple-property name="default-slsb-instance-pool" required="false" type="string" readOnly="false" description="Name of the default stateless bean instance pool, which will be applicable to all stateless EJBs, unless overridden at the deployment or bean level"/> <c:simple-property name="default-stateful-bean-access-timeout:expr" displayName="Default Stateful Bean Access Timeout" required="false" type="string" readOnly="false" defaultValue="5000" description="The default access timeout for stateful beans. The default value is 5000."/> <c:simple-property name="in-vm-remote-interface-invocation-pass-by-value" required="false" type="boolean" readOnly="false" defaultValue="true" description="If set to false, the parameters to invocations on remote interface of an EJB, will be passed by reference. Else, the parameters will be passed by value. The default value is true."/> + <c:simple-property name="enable-statistics" required="false" type="boolean" readOnly="false" defaultValue="false" description="If set to true, enable the collection of invocation statistics."/> </resource-configuration>
<service name="EJB3 Thread Pool"
rhq-commits@lists.fedorahosted.org