mirror of
https://github.com/elastic/logstash.git
synced 2025-04-24 06:37:19 -04:00
PERFORMANCE: Bring Javafier design in line with Rubyfier and Valuefier
Fixes #8158
This commit is contained in:
parent
0467f0dbda
commit
029be86c6d
2 changed files with 60 additions and 64 deletions
|
@ -1,5 +1,7 @@
|
|||
package org.logstash;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.jruby.RubyBoolean;
|
||||
import org.jruby.RubyFixnum;
|
||||
import org.jruby.RubyFloat;
|
||||
|
@ -8,63 +10,67 @@ import org.logstash.bivalues.BiValue;
|
|||
import org.logstash.bivalues.BiValues;
|
||||
import org.logstash.ext.JrubyTimestampExtLibrary;
|
||||
|
||||
public class Javafier {
|
||||
private static final String ERR_TEMPLATE = "Missing Ruby class handling for full class name=%s, simple name=%s";
|
||||
/*
|
||||
Javafier.deep() is called by getField.
|
||||
When any value is added to the Event it should pass through Valuefier.convert.
|
||||
deep(Object o) is the mechanism to pluck the Java value from a BiValue or convert a
|
||||
ConvertedList and ConvertedMap back to ArrayList or HashMap.
|
||||
*/
|
||||
private Javafier(){}
|
||||
public final class Javafier {
|
||||
|
||||
public static Object deep(Object o) {
|
||||
if (o instanceof RubyString) {
|
||||
return o.toString();
|
||||
}
|
||||
if (o instanceof String || o instanceof Float || o instanceof Double ||
|
||||
o instanceof Long || o instanceof Integer || o instanceof Boolean ||
|
||||
o instanceof Timestamp) {
|
||||
return o;
|
||||
}
|
||||
if (o instanceof RubyFloat) {
|
||||
return ((RubyFloat) o).getDoubleValue();
|
||||
}
|
||||
if (o instanceof JrubyTimestampExtLibrary.RubyTimestamp) {
|
||||
return ((JrubyTimestampExtLibrary.RubyTimestamp) o).getTimestamp();
|
||||
}
|
||||
if (o instanceof RubyBoolean) {
|
||||
return ((RubyBoolean) o).isTrue();
|
||||
}
|
||||
if (o instanceof RubyFixnum) {
|
||||
return ((RubyFixnum) o).getLongValue();
|
||||
}
|
||||
if (o instanceof BiValue) {
|
||||
return ((BiValue)o).javaValue();
|
||||
} else if(o instanceof ConvertedMap) {
|
||||
return ((ConvertedMap) o).unconvert();
|
||||
} else if(o instanceof ConvertedList) {
|
||||
return ((ConvertedList) o).unconvert();
|
||||
} else {
|
||||
return fallback(o);
|
||||
}
|
||||
}
|
||||
private static final Map<Class<?>, Valuefier.Converter> CONVERTER_MAP = initConverters();
|
||||
|
||||
private static final Valuefier.Converter BIVALUES_CONVERTER =
|
||||
value -> BiValues.newBiValue(value).javaValue();
|
||||
|
||||
/**
|
||||
* Cold path of {@link Javafier#deep(Object)}.
|
||||
* We assume that we never get an input that is neither {@link ConvertedMap}, {@link ConvertedList}
|
||||
* nor {@link BiValue}, but fallback to attempting to create a {@link BiValue} from the input
|
||||
* before converting to a Java type.
|
||||
* @param o Know to not be an expected type in {@link Javafier#deep(Object)}.
|
||||
* @return Input converted to Java type
|
||||
* Javafier.deep() is called by getField.
|
||||
* When any value is added to the Event it should pass through Valuefier.convert.
|
||||
* deep(Object o) is the mechanism to pluck the Java value from a BiValue or convert a
|
||||
* ConvertedList and ConvertedMap back to ArrayList or HashMap.
|
||||
*/
|
||||
private static Object fallback(final Object o) {
|
||||
try {
|
||||
return BiValues.newBiValue(o).javaValue();
|
||||
} catch (IllegalArgumentException e) {
|
||||
final Class<?> cls = o.getClass();
|
||||
throw new IllegalArgumentException(String.format(ERR_TEMPLATE, cls.getName(), cls.getSimpleName()));
|
||||
private Javafier() {
|
||||
}
|
||||
|
||||
public static Object deep(Object o) {
|
||||
final Class<?> cls = o.getClass();
|
||||
final Valuefier.Converter converter = CONVERTER_MAP.get(cls);
|
||||
if (converter != null) {
|
||||
return converter.convert(o);
|
||||
}
|
||||
return fallbackConvert(o, cls);
|
||||
}
|
||||
|
||||
private static Object fallbackConvert(final Object o, final Class<?> cls) {
|
||||
for (final Map.Entry<Class<?>, Valuefier.Converter> entry : CONVERTER_MAP.entrySet()) {
|
||||
if (entry.getKey().isAssignableFrom(cls)) {
|
||||
final Valuefier.Converter found = entry.getValue();
|
||||
CONVERTER_MAP.put(cls, found);
|
||||
return found.convert(o);
|
||||
}
|
||||
}
|
||||
CONVERTER_MAP.put(cls, BIVALUES_CONVERTER);
|
||||
return BIVALUES_CONVERTER.convert(o);
|
||||
}
|
||||
|
||||
private static Map<Class<?>, Valuefier.Converter> initConverters() {
|
||||
final Map<Class<?>, Valuefier.Converter> converters =
|
||||
new ConcurrentHashMap<>(50, 0.2F, 1);
|
||||
converters.put(String.class, Valuefier.IDENTITY);
|
||||
converters.put(Float.class, Valuefier.IDENTITY);
|
||||
converters.put(Double.class, Valuefier.IDENTITY);
|
||||
converters.put(Long.class, Valuefier.IDENTITY);
|
||||
converters.put(Integer.class, Valuefier.IDENTITY);
|
||||
converters.put(Boolean.class, Valuefier.IDENTITY);
|
||||
converters.put(Timestamp.class, Valuefier.IDENTITY);
|
||||
// Explicitly casting to RubyString when we know it's a RubyString for sure is faster
|
||||
// than having the JVM look up the type.
|
||||
converters.put(RubyString.class, value -> ((RubyString) value).toString());
|
||||
converters.put(RubyBoolean.class, value -> ((RubyBoolean) value).isTrue());
|
||||
converters.put(BiValue.class, value -> ((BiValue<?, ?>) value).javaValue());
|
||||
converters.put(RubyFixnum.class, value -> ((RubyFixnum) value).getLongValue());
|
||||
converters.put(RubyFloat.class, value -> ((RubyFloat) value).getDoubleValue());
|
||||
converters.put(ConvertedMap.class, value -> ((ConvertedMap) value).unconvert());
|
||||
converters.put(ConvertedList.class, value -> ((ConvertedList) value).unconvert());
|
||||
converters.put(
|
||||
JrubyTimestampExtLibrary.RubyTimestamp.class,
|
||||
value -> ((JrubyTimestampExtLibrary.RubyTimestamp) value).getTimestamp()
|
||||
);
|
||||
return converters;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.logstash.ext.JrubyTimestampExtLibrary;
|
|||
|
||||
public final class Valuefier {
|
||||
|
||||
private static final Valuefier.Converter IDENTITY = input -> input;
|
||||
public static final Valuefier.Converter IDENTITY = input -> input;
|
||||
|
||||
private static final Valuefier.Converter FLOAT_CONVERTER =
|
||||
input -> RubyUtil.RUBY.newFloat(((Number) input).doubleValue());
|
||||
|
@ -145,18 +145,8 @@ public final class Valuefier {
|
|||
return converters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converter from either a Java or a Ruby type to a type that both {@link Javafier} and
|
||||
* {@link Rubyfier} are able to convert back their respective types efficiently.
|
||||
*/
|
||||
private interface Converter {
|
||||
public interface Converter {
|
||||
|
||||
/**
|
||||
* Converts a Java or a Ruby typed object to an object that can be efficiently converted
|
||||
* back to Java as well as Ruby.
|
||||
* @param input Either a Java or Ruby type object
|
||||
* @return Object that can be converted back to Java as well as Ruby efficiently
|
||||
*/
|
||||
Object convert(Object input);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue