blob: b43bcbb0270c22988ae109205ba8e2158f0d146b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.analysis_engine.impl;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.uima.UimaContextAdmin;
import org.apache.uima.analysis_engine.AnalysisEngineManagement;
/**
* Implements Monitoring/Management interface to an AnalysisEngine.
*
*/
public class AnalysisEngineManagementImpl
implements AnalysisEngineManagementImplMBean, AnalysisEngineManagement {
private static final long serialVersionUID = 1988620286191379887L;
private static final Pattern RESERVED_CHAR_PATTERN = Pattern.compile("[\",=:*?]");
static final DecimalFormat format = new DecimalFormat("0.##");
/**
* This static set is needed to keep track of what names we've already used for "root" MBeans
* (those representing top-level AEs and CPEs).
*/
private static Set<String> usedRootNames = new HashSet<String>();
private String name;
private long numProcessed;
private long markedAnalysisTime;
private long markedBatchProcessCompleteTime;
private long markedCollectionProcessCompleteTime;
private long markedServiceCallTime;
private long analysisTime;
private long batchProcessCompleteTime;
private long collectionProcessCompleteTime;
private long serviceCallTime;
private Map<String, AnalysisEngineManagement> components = new HashMap<String, AnalysisEngineManagement>();
private String uniqueMBeanName;
public void reportAnalysisTime(long time) {
analysisTime += time;
}
public void reportBatchProcessCompleteTime(long time) {
batchProcessCompleteTime += time;
}
public void reportCollectionProcessCompleteTime(long time) {
collectionProcessCompleteTime += time;
}
public void reportServiceCallTime(long time) {
serviceCallTime += time;
}
public void incrementCASesProcessed() {
numProcessed++;
}
public long getBatchProcessCompleteTime() {
return batchProcessCompleteTime;
}
public long getCollectionProcessCompleteTime() {
return collectionProcessCompleteTime;
}
public long getAnalysisTime() {
return analysisTime + serviceCallTime;
}
public long getServiceCallTime() {
return serviceCallTime;
}
/**
* Internal use only. Used to implement backwards compatibility with the ProcessTrace interface.
*/
public void mark() {
markedAnalysisTime = analysisTime;
markedBatchProcessCompleteTime = batchProcessCompleteTime;
markedCollectionProcessCompleteTime = collectionProcessCompleteTime;
markedServiceCallTime = serviceCallTime;
// mark components also
Iterator<AnalysisEngineManagement> iter = components.values().iterator();
while (iter.hasNext()) {
AnalysisEngineManagementImpl component = (AnalysisEngineManagementImpl) iter.next();
component.mark();
}
}
/**
* Internal use only. Used to implement backwards compatibility with the ProcessTrace interface.
*/
public long getBatchProcessCompleteTimeSinceMark() {
return batchProcessCompleteTime - markedBatchProcessCompleteTime;
}
/**
* Internal use only. Used to implement backwards compatibility with the ProcessTrace interface.
*/
public long getCollectionProcessCompleteTimeSinceMark() {
return collectionProcessCompleteTime - markedCollectionProcessCompleteTime;
}
/**
* Internal use only. Used to implement backwards compatibility with the ProcessTrace interface.
*/
public long getAnalysisTimeSinceMark() {
return analysisTime - markedAnalysisTime;
}
/**
* Internal use only. Used to implement backwards compatibility with the ProcessTrace interface.
*/
public long getServiceCallTimeSinceMark() {
return serviceCallTime - markedServiceCallTime;
}
public long getNumberOfCASesProcessed() {
return numProcessed;
}
public String getCASesPerSecond() {
if (getAnalysisTime() == 0)
return "0";
float docsPerSecond = (float) numProcessed / getAnalysisTime() * 1000;
return format.format(docsPerSecond);
}
public Map<String, AnalysisEngineManagement> getComponents() {
return Collections.unmodifiableMap(components);
}
public void addComponent(String key, AnalysisEngineManagementImpl component) {
components.put(key, component);
}
public String getName() {
return name;
}
public String getUniqueMBeanName() {
return uniqueMBeanName;
}
public void resetStats() {
numProcessed = 0;
analysisTime = 0;
batchProcessCompleteTime = 0;
collectionProcessCompleteTime = 0;
serviceCallTime = 0;
markedAnalysisTime = 0;
markedBatchProcessCompleteTime = 0;
markedCollectionProcessCompleteTime = 0;
markedServiceCallTime = 0;
// reset components also
Iterator<AnalysisEngineManagement> iter = components.values().iterator();
while (iter.hasNext()) {
AnalysisEngineManagementImpl component = (AnalysisEngineManagementImpl) iter.next();
component.resetStats();
}
}
/**
* Sets the name of this AnalyaisEngineManagement object, and also computes the unique MBean name
* that can later be used to register this object with an MBeanServer.
*
* @param aName
* the simple name of this AnalysisEngine (generally this is the <code>name</code>
* property from the AnalysisEngineMetaData)
* @param aContext
* the UimaContext for this AnalysisEngine. Needed to compute the unique name, which is
* hierarchical
* @param aCustomPrefix an optional prefix provided by the Application, which will be
* prepended to the name generated by UIMA. If null, the prefix
* "org.apache.uima:" will be used.
*/
public void setName(String aName, UimaContextAdmin aContext, String aCustomPrefix) {
// set the simple name
name = aName;
//determine the MBean name prefix we should use
String prefix;
if (aCustomPrefix == null) {
prefix = "org.apache.uima:";
} else {
prefix = aCustomPrefix;
if (!prefix.endsWith(":") && !prefix.endsWith(",")) {
prefix += ",";
}
}
// compute the unique name
// (first get the rootMBean and assign it a unique name if it doesn't already have one)
AnalysisEngineManagementImpl rootMBean = (AnalysisEngineManagementImpl) aContext
.getRootContext().getManagementInterface();
if (rootMBean.getUniqueMBeanName() == null) {
// try to find a unique name for the root MBean
String baseRootName = rootMBean.getName();
if (baseRootName == null) {
baseRootName = "CPE"; // CPE's don't currently have names
}
String rootName = baseRootName;
int i = 2;
while (usedRootNames.contains(rootName)) {
rootName = baseRootName + " " + i++;
}
usedRootNames.add(rootName);
// create a propertly-formatted MBean name, using the specified prefix
rootMBean.uniqueMBeanName = prefix + "name=" + escapeValue(rootName);
}
if (rootMBean != this) {
// form the MBean name hierarchically starting from the root name
String rootName = rootMBean.getUniqueMBeanName();
// strip off the MBean name prefix to get just the simple name
rootName = rootName.substring(prefix.length() + "name=".length());
// form the hierarchical MBean name
prefix += "p0=";
//we add "Components" to the end of the rootName, but be aware of quoting issues
if (rootName.endsWith("\"")) {
prefix += rootName.substring(0,rootName.length() - 1) + " Components\",";
}
else {
prefix += rootName + " Components,";
}
uniqueMBeanName = makeMBeanName(prefix, aContext.getQualifiedContextName().substring(1), 1);
}
}
/**
* Recursive utility method for generating a hierarchical mbean name
*/
private static String makeMBeanName(String prefix, String contextName, int depth) {
int firstSlash = contextName.indexOf('/');
if (firstSlash == contextName.length() - 1) {
return prefix + "name=" + escapeValue(contextName.substring(0, contextName.length() - 1));
} else {
String newPrefix = prefix + "p" + depth + "=" + escapeValue(contextName.substring(0, firstSlash)
+ " Components") + ",";
return makeMBeanName(newPrefix, contextName.substring(firstSlash + 1), depth + 1);
}
}
/** Escapes the "value" part of a JMX name if necessary. If the value
* includes reserved characters (" , = : * ?) the value will be enclosed
* in quotes and some characters (" ? * \) will be escaped with backslashes.
*/
private static String escapeValue(String value) {
if (RESERVED_CHAR_PATTERN.matcher(value).find()) {
//must quote the value
StringBuffer buf = new StringBuffer();
buf.append('\"');
//must escape special characters inside the quoted value
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '\"' || c =='\\' || c == '?' || c =='*') {
buf.append('\\');
}
buf.append(c);
}
buf.append('\"');
return buf.toString();
} else {
return value; //no escaping needed
}
}
}