blob: ce799f5f2b65c2437181607eb5fbdcb913db0635 [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.hadoop.mapreduce.util;
import java.text.ParseException;
import java.util.List;
import com.google.common.collect.Lists;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.counters.AbstractCounters;
import org.apache.hadoop.mapreduce.Counter;
import org.apache.hadoop.mapreduce.counters.CounterGroupBase;
import org.apache.hadoop.util.StringInterner;
import org.apache.hadoop.util.StringUtils;
/**
* String conversion utilities for counters.
* Candidate for deprecation since we start to use JSON in 0.21+
*/
@InterfaceAudience.Private
public class CountersStrings {
private static final char GROUP_OPEN = '{';
private static final char GROUP_CLOSE = '}';
private static final char COUNTER_OPEN = '[';
private static final char COUNTER_CLOSE = ']';
private static final char UNIT_OPEN = '(';
private static final char UNIT_CLOSE = ')';
private static char[] charsToEscape = {GROUP_OPEN, GROUP_CLOSE,
COUNTER_OPEN, COUNTER_CLOSE,
UNIT_OPEN, UNIT_CLOSE};
/**
* Make the pre 0.21 counter string (for e.g. old job history files)
* [(actual-name)(display-name)(value)]
* @param counter to stringify
* @return the stringified result
*/
public static String toEscapedCompactString(Counter counter) {
// First up, obtain the strings that need escaping. This will help us
// determine the buffer length apriori.
String escapedName, escapedDispName;
long currentValue;
synchronized(counter) {
escapedName = escape(counter.getName());
escapedDispName = escape(counter.getDisplayName());
currentValue = counter.getValue();
}
int length = escapedName.length() + escapedDispName.length() + 4;
length += 8; // For the following delimiting characters
StringBuilder builder = new StringBuilder(length);
builder.append(COUNTER_OPEN);
// Add the counter name
builder.append(UNIT_OPEN);
builder.append(escapedName);
builder.append(UNIT_CLOSE);
// Add the display name
builder.append(UNIT_OPEN);
builder.append(escapedDispName);
builder.append(UNIT_CLOSE);
// Add the value
builder.append(UNIT_OPEN);
builder.append(currentValue);
builder.append(UNIT_CLOSE);
builder.append(COUNTER_CLOSE);
return builder.toString();
}
/**
* Make the 0.21 counter group string.
* format: {(actual-name)(display-name)(value)[][][]}
* where [] are compact strings for the counters within.
* @param <G> type of the group
* @param group to stringify
* @return the stringified result
*/
public static <G extends CounterGroupBase<?>>
String toEscapedCompactString(G group) {
List<String> escapedStrs = Lists.newArrayList();
int length;
String escapedName, escapedDispName;
synchronized(group) {
// First up, obtain the strings that need escaping. This will help us
// determine the buffer length apriori.
escapedName = escape(group.getName());
escapedDispName = escape(group.getDisplayName());
int i = 0;
length = escapedName.length() + escapedDispName.length();
for (Counter counter : group) {
String escapedStr = toEscapedCompactString(counter);
escapedStrs.add(escapedStr);
length += escapedStr.length();
}
}
length += 6; // for all the delimiting characters below
StringBuilder builder = new StringBuilder(length);
builder.append(GROUP_OPEN); // group start
// Add the group name
builder.append(UNIT_OPEN);
builder.append(escapedName);
builder.append(UNIT_CLOSE);
// Add the display name
builder.append(UNIT_OPEN);
builder.append(escapedDispName);
builder.append(UNIT_CLOSE);
// write the value
for(String escaped : escapedStrs) {
builder.append(escaped);
}
builder.append(GROUP_CLOSE); // group end
return builder.toString();
}
/**
* Make the pre 0.21 counters string
* @param <C> type of the counter
* @param <G> type of the counter group
* @param <T> type of the counters object
* @param counters the object to stringify
* @return the string in the following format
* {(groupName)(group-displayName)[(counterName)(displayName)(value)]*}*
*/
public static <C extends Counter, G extends CounterGroupBase<C>,
T extends AbstractCounters<C, G>>
String toEscapedCompactString(T counters) {
String[] groupsArray;
int length = 0;
synchronized(counters) {
groupsArray = new String[counters.countCounters()];
int i = 0;
// First up, obtain the escaped string for each group so that we can
// determine the buffer length apriori.
for (G group : counters) {
String escapedString = toEscapedCompactString(group);
groupsArray[i++] = escapedString;
length += escapedString.length();
}
}
// Now construct the buffer
StringBuilder builder = new StringBuilder(length);
for (String group : groupsArray) {
builder.append(group);
}
return builder.toString();
}
// Escapes all the delimiters for counters i.e {,[,(,),],}
private static String escape(String string) {
return StringUtils.escapeString(string, StringUtils.ESCAPE_CHAR,
charsToEscape);
}
// Unescapes all the delimiters for counters i.e {,[,(,),],}
private static String unescape(String string) {
return StringUtils.unEscapeString(string, StringUtils.ESCAPE_CHAR,
charsToEscape);
}
// Extracts a block (data enclosed within delimeters) ignoring escape
// sequences. Throws ParseException if an incomplete block is found else
// returns null.
private static String getBlock(String str, char open, char close,
IntWritable index) throws ParseException {
StringBuilder split = new StringBuilder();
int next = StringUtils.findNext(str, open, StringUtils.ESCAPE_CHAR,
index.get(), split);
split.setLength(0); // clear the buffer
if (next >= 0) {
++next; // move over '('
next = StringUtils.findNext(str, close, StringUtils.ESCAPE_CHAR,
next, split);
if (next >= 0) {
++next; // move over ')'
index.set(next);
return split.toString(); // found a block
} else {
throw new ParseException("Unexpected end of block", next);
}
}
return null; // found nothing
}
/**
* Parse a pre 0.21 counters string into a counter object.
* @param <C> type of the counter
* @param <G> type of the counter group
* @param <T> type of the counters object
* @param compactString to parse
* @param counters an empty counters object to hold the result
* @return the counters object holding the result
* @throws ParseException
*/
@SuppressWarnings("deprecation")
public static <C extends Counter, G extends CounterGroupBase<C>,
T extends AbstractCounters<C, G>>
T parseEscapedCompactString(String compactString, T counters)
throws ParseException {
IntWritable index = new IntWritable(0);
// Get the group to work on
String groupString =
getBlock(compactString, GROUP_OPEN, GROUP_CLOSE, index);
while (groupString != null) {
IntWritable groupIndex = new IntWritable(0);
// Get the actual name
String groupName =
StringInterner.weakIntern(getBlock(groupString, UNIT_OPEN, UNIT_CLOSE, groupIndex));
groupName = StringInterner.weakIntern(unescape(groupName));
// Get the display name
String groupDisplayName =
StringInterner.weakIntern(getBlock(groupString, UNIT_OPEN, UNIT_CLOSE, groupIndex));
groupDisplayName = StringInterner.weakIntern(unescape(groupDisplayName));
// Get the counters
G group = counters.getGroup(groupName);
group.setDisplayName(groupDisplayName);
String counterString =
getBlock(groupString, COUNTER_OPEN, COUNTER_CLOSE, groupIndex);
while (counterString != null) {
IntWritable counterIndex = new IntWritable(0);
// Get the actual name
String counterName =
StringInterner.weakIntern(getBlock(counterString, UNIT_OPEN, UNIT_CLOSE, counterIndex));
counterName = StringInterner.weakIntern(unescape(counterName));
// Get the display name
String counterDisplayName =
StringInterner.weakIntern(getBlock(counterString, UNIT_OPEN, UNIT_CLOSE, counterIndex));
counterDisplayName = StringInterner.weakIntern(unescape(counterDisplayName));
// Get the value
long value =
Long.parseLong(getBlock(counterString, UNIT_OPEN, UNIT_CLOSE,
counterIndex));
// Add the counter
Counter counter = group.findCounter(counterName);
counter.setDisplayName(counterDisplayName);
counter.increment(value);
// Get the next counter
counterString =
getBlock(groupString, COUNTER_OPEN, COUNTER_CLOSE, groupIndex);
}
groupString = getBlock(compactString, GROUP_OPEN, GROUP_CLOSE, index);
}
return counters;
}
}