#8154 break cyclic dependency between Ruby and Java Event classes

Fixes #8529
This commit is contained in:
Armin 2017-10-25 15:34:59 +02:00 committed by Armin Braun
parent aca01544a4
commit c0a355f6ab
6 changed files with 79 additions and 59 deletions

View file

@ -1,6 +1,10 @@
# encoding: utf-8 # encoding: utf-8
# Force loading the RubyUtil to ensure that the custom Exception types it sets up are ready at the
# same time as those that are set by this script.
java_import org.logstash.RubyUtil
module LogStash module LogStash
class Error < ::StandardError; end
class EnvironmentError < Error; end class EnvironmentError < Error; end
class ConfigurationError < Error; end class ConfigurationError < Error; end
class PluginLoadingError < Error; end class PluginLoadingError < Error; end

View file

@ -2,10 +2,13 @@
require "logstash/namespace" require "logstash/namespace"
require "logstash/json" require "logstash/json"
require "jruby_event_ext"
require "jruby_timestamp_ext" require "jruby_timestamp_ext"
require "logstash/timestamp" require "logstash/timestamp"
# Force loading the RubyUtil to ensure its loaded before the Event class is set up in Ruby since
# Event depends on Ruby classes that are dynamically set up by Java code.
java_import org.logstash.RubyUtil
# transient pipeline events for normal in-flow signaling as opposed to # transient pipeline events for normal in-flow signaling as opposed to
# flow altering exceptions. for now having base classes is adequate and # flow altering exceptions. for now having base classes is adequate and
# in the future it might be necessary to refactor using like a BaseEvent # in the future it might be necessary to refactor using like a BaseEvent

View file

@ -1,14 +1,10 @@
# encoding: utf-8 # encoding: utf-8
require "logstash/environment" require "logstash/environment"
require "logstash/errors"
require "jrjackson" require "jrjackson"
require "logstash/java_integration" require "logstash/java_integration"
module LogStash module LogStash
module Json module Json
class ParserError < LogStash::Error; end
class GeneratorError < LogStash::Error; end
extend self extend self
def jruby_load(data, options = {}) def jruby_load(data, options = {})

View file

@ -1,11 +0,0 @@
import org.jruby.Ruby;
import org.jruby.runtime.load.BasicLibraryService;
import org.logstash.ext.JrubyEventExtLibrary;
public final class JrubyEventExtService implements BasicLibraryService {
@Override
public boolean basicLoad(final Ruby runtime) {
new JrubyEventExtLibrary().load(runtime, false);
return true;
}
}

View file

@ -2,8 +2,12 @@ package org.logstash;
import org.jruby.NativeException; import org.jruby.NativeException;
import org.jruby.Ruby; import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyException;
import org.jruby.RubyModule; import org.jruby.RubyModule;
import org.jruby.anno.JRubyClass;
import org.jruby.exceptions.RaiseException; import org.jruby.exceptions.RaiseException;
import org.logstash.ext.JrubyEventExtLibrary;
/** /**
* Utilities around interaction with the {@link Ruby} runtime. * Utilities around interaction with the {@link Ruby} runtime.
@ -20,9 +24,45 @@ public final class RubyUtil {
*/ */
public static final RubyModule LOGSTASH_MODULE; public static final RubyModule LOGSTASH_MODULE;
public static final RubyClass RUBY_EVENT_CLASS;
public static final RubyClass PARSER_ERROR;
public static final RubyClass GENERATOR_ERROR;
public static final RubyClass LOGSTASH_ERROR;
static { static {
RUBY = Ruby.getGlobalRuntime(); RUBY = Ruby.getGlobalRuntime();
LOGSTASH_MODULE = RUBY.getOrCreateModule("LogStash"); LOGSTASH_MODULE = RUBY.getOrCreateModule("LogStash");
RUBY_EVENT_CLASS = RUBY.defineClassUnder(
"Event", RUBY.getObject(), JrubyEventExtLibrary.RubyEvent::new, LOGSTASH_MODULE
);
final RubyModule json = LOGSTASH_MODULE.defineOrGetModuleUnder("Json");
LOGSTASH_ERROR = LOGSTASH_MODULE.defineClassUnder(
"Error", RUBY.getStandardError(), RubyUtil.LogstashRubyError::new
);
PARSER_ERROR = json.defineClassUnder(
"ParserError", LOGSTASH_ERROR, RubyUtil.LogstashRubyParserError::new
);
GENERATOR_ERROR = json.defineClassUnder("GeneratorError", LOGSTASH_ERROR,
RubyUtil.LogstashRubyGeneratorError::new
);
RUBY_EVENT_CLASS.setConstant("METADATA", RUBY.newString(Event.METADATA));
RUBY_EVENT_CLASS.setConstant(
"METADATA_BRACKETS", RUBY.newString(Event.METADATA_BRACKETS)
);
RUBY_EVENT_CLASS.setConstant("TIMESTAMP", RUBY.newString(Event.TIMESTAMP));
RUBY_EVENT_CLASS.setConstant(
"TIMESTAMP_FAILURE_TAG", RUBY.newString(Event.TIMESTAMP_FAILURE_TAG)
);
RUBY_EVENT_CLASS.setConstant(
"TIMESTAMP_FAILURE_FIELD", RUBY.newString(Event.TIMESTAMP_FAILURE_FIELD)
);
RUBY_EVENT_CLASS.setConstant("VERSION", RUBY.newString(Event.VERSION));
RUBY_EVENT_CLASS.setConstant("VERSION_ONE", RUBY.newString(Event.VERSION_ONE));
RUBY_EVENT_CLASS.defineAnnotatedMethods(JrubyEventExtLibrary.RubyEvent.class);
RUBY_EVENT_CLASS.defineAnnotatedConstants(JrubyEventExtLibrary.RubyEvent.class);
} }
private RubyUtil() { private RubyUtil() {
@ -39,4 +79,28 @@ public final class RubyUtil {
// will preserve Java stacktrace & bubble up as a Ruby IOError // will preserve Java stacktrace & bubble up as a Ruby IOError
return new RaiseException(e, new NativeException(runtime, runtime.getIOError(), e)); return new RaiseException(e, new NativeException(runtime, runtime.getIOError(), e));
} }
@JRubyClass(name = "Error")
public static final class LogstashRubyError extends RubyException {
public LogstashRubyError(final Ruby runtime, final RubyClass metaClass) {
super(runtime, metaClass);
}
}
@JRubyClass(name = "ParserError")
public static final class LogstashRubyParserError extends RubyException {
public LogstashRubyParserError(final Ruby runtime, final RubyClass metaClass) {
super(runtime, metaClass);
}
}
@JRubyClass(name = "GeneratorError")
public static final class LogstashRubyGeneratorError extends RubyException {
public LogstashRubyGeneratorError(final Ruby runtime, final RubyClass metaClass) {
super(runtime, metaClass);
}
}
} }

View file

@ -17,7 +17,6 @@ import org.jruby.java.proxies.MapJavaProxy;
import org.jruby.javasupport.JavaUtil; import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.ThreadContext; import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.Library;
import org.logstash.ConvertedMap; import org.logstash.ConvertedMap;
import org.logstash.Event; import org.logstash.Event;
import org.logstash.FieldReference; import org.logstash.FieldReference;
@ -25,42 +24,7 @@ import org.logstash.RubyUtil;
import org.logstash.Rubyfier; import org.logstash.Rubyfier;
import org.logstash.Valuefier; import org.logstash.Valuefier;
public final class JrubyEventExtLibrary implements Library { public final class JrubyEventExtLibrary {
private static RubyClass PARSER_ERROR = null;
private static RubyClass GENERATOR_ERROR = null;
private static RubyClass LOGSTASH_ERROR = null;
@Override
public void load(Ruby runtime, boolean wrap) {
RubyClass clazz = runtime.defineClassUnder(
"Event", runtime.getObject(), RubyEvent::new, RubyUtil.LOGSTASH_MODULE
);
clazz.setConstant("METADATA", runtime.newString(Event.METADATA));
clazz.setConstant("METADATA_BRACKETS", runtime.newString(Event.METADATA_BRACKETS));
clazz.setConstant("TIMESTAMP", runtime.newString(Event.TIMESTAMP));
clazz.setConstant("TIMESTAMP_FAILURE_TAG", runtime.newString(Event.TIMESTAMP_FAILURE_TAG));
clazz.setConstant("TIMESTAMP_FAILURE_FIELD", runtime.newString(Event.TIMESTAMP_FAILURE_FIELD));
clazz.setConstant("VERSION", runtime.newString(Event.VERSION));
clazz.setConstant("VERSION_ONE", runtime.newString(Event.VERSION_ONE));
clazz.defineAnnotatedMethods(RubyEvent.class);
clazz.defineAnnotatedConstants(RubyEvent.class);
PARSER_ERROR = RubyUtil.LOGSTASH_MODULE.defineOrGetModuleUnder("Json").getClass("ParserError");
if (PARSER_ERROR == null) {
throw new RaiseException(runtime, runtime.getClass("StandardError"), "Could not find LogStash::Json::ParserError class", true);
}
GENERATOR_ERROR = RubyUtil.LOGSTASH_MODULE.defineOrGetModuleUnder("Json").getClass("GeneratorError");
if (GENERATOR_ERROR == null) {
throw new RaiseException(runtime, runtime.getClass("StandardError"), "Could not find LogStash::Json::GeneratorError class", true);
}
LOGSTASH_ERROR = RubyUtil.LOGSTASH_MODULE.getClass("Error");
if (LOGSTASH_ERROR == null) {
throw new RaiseException(runtime, runtime.getClass("StandardError"), "Could not find LogStash::Error class", true);
}
}
@JRubyClass(name = "Event") @JRubyClass(name = "Event")
public static final class RubyEvent extends RubyObject { public static final class RubyEvent extends RubyObject {
@ -80,13 +44,13 @@ public final class JrubyEventExtLibrary implements Library {
private Event event; private Event event;
private RubyEvent(final Ruby runtime, final RubyClass klass) { public RubyEvent(final Ruby runtime, final RubyClass klass) {
super(runtime, klass); super(runtime, klass);
} }
public static RubyEvent newRubyEvent(Ruby runtime, Event event) { public static RubyEvent newRubyEvent(Ruby runtime, Event event) {
final RubyEvent ruby = final RubyEvent ruby =
new RubyEvent(runtime, RubyUtil.LOGSTASH_MODULE.getClass("Event")); new RubyEvent(runtime, RubyUtil.RUBY_EVENT_CLASS);
ruby.setEvent(event); ruby.setEvent(event);
return ruby; return ruby;
} }
@ -201,7 +165,7 @@ public final class JrubyEventExtLibrary implements Library {
try { try {
return RubyString.newString(context.runtime, event.sprintf(format.toString())); return RubyString.newString(context.runtime, event.sprintf(format.toString()));
} catch (IOException e) { } catch (IOException e) {
throw new RaiseException(getRuntime(), LOGSTASH_ERROR, "timestamp field is missing", true); throw new RaiseException(getRuntime(), RubyUtil.LOGSTASH_ERROR, "timestamp field is missing", true);
} }
} }
@ -239,7 +203,7 @@ public final class JrubyEventExtLibrary implements Library {
try { try {
return RubyString.newString(context.runtime, event.toJson()); return RubyString.newString(context.runtime, event.toJson());
} catch (Exception e) { } catch (Exception e) {
throw new RaiseException(context.runtime, GENERATOR_ERROR, e.getMessage(), true); throw new RaiseException(context.runtime, RubyUtil.GENERATOR_ERROR, e.getMessage(), true);
} }
} }
@ -253,7 +217,7 @@ public final class JrubyEventExtLibrary implements Library {
try { try {
events = Event.fromJson(value.asJavaString()); events = Event.fromJson(value.asJavaString());
} catch (Exception e) { } catch (Exception e) {
throw new RaiseException(context.runtime, PARSER_ERROR, e.getMessage(), true); throw new RaiseException(context.runtime, RubyUtil.PARSER_ERROR, e.getMessage(), true);
} }
RubyArray result = RubyArray.newArray(context.runtime, events.length); RubyArray result = RubyArray.newArray(context.runtime, events.length);