31
31
import java .time .Duration ;
32
32
import java .util .ArrayList ;
33
33
import java .util .Arrays ;
34
+ import java .util .Collection ;
34
35
import java .util .List ;
35
36
import java .util .UUID ;
36
37
import java .util .regex .Matcher ;
37
38
import java .util .regex .Pattern ;
39
+ import java .util .stream .Stream ;
38
40
39
41
import com .google .common .annotations .VisibleForTesting ;
40
42
import org .apache .commons .codec .binary .Hex ;
43
45
import org .jruby .Ruby ;
44
46
import org .jruby .RubyArray ;
45
47
import org .jruby .RubyBasicObject ;
48
+ import org .jruby .RubyBoolean ;
46
49
import org .jruby .RubyClass ;
47
50
import org .jruby .RubyString ;
48
51
import org .jruby .RubySymbol ;
58
61
import org .logstash .ackedqueue .ext .JRubyWrappedAckedQueueExt ;
59
62
import org .logstash .common .DeadLetterQueueFactory ;
60
63
import org .logstash .common .EnvironmentVariableProvider ;
64
+ import org .logstash .common .IncompleteSourceWithMetadataException ;
61
65
import org .logstash .common .SourceWithMetadata ;
62
66
import org .logstash .common .io .DeadLetterQueueWriter ;
63
67
import org .logstash .common .io .QueueStorageType ;
68
+ import org .logstash .config .ir .CompiledPipeline ;
64
69
import org .logstash .config .ir .ConfigCompiler ;
65
70
import org .logstash .config .ir .InvalidIRException ;
66
71
import org .logstash .config .ir .PipelineConfig ;
67
72
import org .logstash .config .ir .PipelineIR ;
73
+ import org .logstash .execution .queue .QueueWriter ;
68
74
import org .logstash .ext .JRubyAbstractQueueWriteClientExt ;
69
75
import org .logstash .ext .JRubyWrappedWriteClientExt ;
70
76
import org .logstash .instrument .metrics .AbstractMetricExt ;
75
81
import org .logstash .instrument .metrics .UptimeMetric ;
76
82
import org .logstash .instrument .metrics .counter .LongCounter ;
77
83
import org .logstash .plugins .ConfigVariableExpander ;
84
+ import org .logstash .plugins .factory .ExecutionContextFactoryExt ;
85
+ import org .logstash .plugins .factory .PluginFactoryExt ;
86
+ import org .logstash .plugins .factory .PluginMetricsFactoryExt ;
78
87
import org .logstash .secret .store .SecretStore ;
79
88
import org .logstash .secret .store .SecretStoreExt ;
80
89
83
92
import static org .logstash .instrument .metrics .UptimeMetric .ScaleUnits .SECONDS ;
84
93
85
94
/**
86
- * JRuby extension to provide ancestor class for Ruby's Pipeline and JavaPipeline classes.
95
+ * JRuby extension to provide ancestor class for the ruby-defined {@code LogStash::JavaPipeline} class.
96
+ *
97
+ * <p>NOTE: Although this class' name implies that it is "abstract", we instantiated it directly
98
+ * as a lightweight temporary-scoped pipeline in the ruby-defined {@code LogStash::PipelineAction::Reload}
87
99
* */
88
100
@ JRubyClass (name = "AbstractPipeline" )
89
101
public class AbstractPipelineExt extends RubyBasicObject {
@@ -104,6 +116,7 @@ public class AbstractPipelineExt extends RubyBasicObject {
104
116
105
117
@ SuppressWarnings ("serial" )
106
118
protected PipelineIR lir ;
119
+ private transient CompiledPipeline lirExecution ;
107
120
108
121
private final RubyString ephemeralId = RubyUtil .RUBY .newString (UUID .randomUUID ().toString ());
109
122
@@ -135,13 +148,46 @@ public class AbstractPipelineExt extends RubyBasicObject {
135
148
private QueueReadClientBase filterQueueClient ;
136
149
137
150
private ArrayList <FlowMetric > flowMetrics = new ArrayList <>();
151
+ private @ SuppressWarnings ("rawtypes" ) RubyArray inputs ;
152
+ private @ SuppressWarnings ("rawtypes" ) RubyArray filters ;
153
+ private @ SuppressWarnings ("rawtypes" ) RubyArray outputs ;
138
154
139
155
public AbstractPipelineExt (final Ruby runtime , final RubyClass metaClass ) {
140
156
super (runtime , metaClass );
141
157
}
142
158
159
+ @ JRubyMethod (required = 4 )
160
+ public AbstractPipelineExt initialize (final ThreadContext context , final IRubyObject [] args )
161
+ throws IncompleteSourceWithMetadataException , NoSuchAlgorithmException {
162
+ initialize (context , args [0 ], args [1 ], args [2 ]);
163
+ lirExecution = new CompiledPipeline (
164
+ lir ,
165
+ new PluginFactoryExt (context .runtime , RubyUtil .PLUGIN_FACTORY_CLASS ).init (
166
+ lir ,
167
+ new PluginMetricsFactoryExt (
168
+ context .runtime , RubyUtil .PLUGIN_METRICS_FACTORY_CLASS
169
+ ).initialize (context , pipelineId (), metric ()),
170
+ new ExecutionContextFactoryExt (
171
+ context .runtime , RubyUtil .EXECUTION_CONTEXT_FACTORY_CLASS
172
+ ).initialize (context , args [3 ], this , dlqWriter (context )),
173
+ RubyUtil .FILTER_DELEGATOR_CLASS
174
+ ),
175
+ getSecretStore (context )
176
+ );
177
+ inputs = RubyArray .newArray (context .runtime , lirExecution .inputs ());
178
+ filters = RubyArray .newArray (context .runtime , lirExecution .filters ());
179
+ outputs = RubyArray .newArray (context .runtime , lirExecution .outputs ());
180
+ if (getSetting (context , "config.debug" ).isTrue () && LOGGER .isDebugEnabled ()) {
181
+ LOGGER .debug (
182
+ "Compiled pipeline code for pipeline {} : {}" , pipelineId (),
183
+ lir .getGraph ().toString ()
184
+ );
185
+ }
186
+ return this ;
187
+ }
188
+
143
189
@ JRubyMethod
144
- public final AbstractPipelineExt initialize (final ThreadContext context ,
190
+ private AbstractPipelineExt initialize (final ThreadContext context ,
145
191
final IRubyObject pipelineConfig , final IRubyObject namespacedMetric ,
146
192
final IRubyObject rubyLogger )
147
193
throws NoSuchAlgorithmException {
@@ -269,6 +315,11 @@ public final IRubyObject lir(final ThreadContext context) {
269
315
return JavaUtil .convertJavaToUsableRubyObject (context .runtime , lir );
270
316
}
271
317
318
+ @ JRubyMethod (name = "lir_execution" )
319
+ public IRubyObject lirExecution (final ThreadContext context ) {
320
+ return JavaUtil .convertJavaToUsableRubyObject (context .runtime , lirExecution );
321
+ }
322
+
272
323
@ JRubyMethod (name = "dlq_writer" )
273
324
public final IRubyObject dlqWriter (final ThreadContext context ) {
274
325
if (dlqWriter == null ) {
@@ -375,6 +426,29 @@ public final IRubyObject isConfiguredReloadable(final ThreadContext context) {
375
426
return getSetting (context , "pipeline.reloadable" );
376
427
}
377
428
429
+ @ JRubyMethod (name = "reloadable?" )
430
+ public RubyBoolean isReloadable (final ThreadContext context ) {
431
+ return isConfiguredReloadable (context ).isTrue () && reloadablePlugins (context ).isTrue ()
432
+ ? context .tru : context .fals ;
433
+ }
434
+
435
+ @ JRubyMethod (name = "reloadable_plugins?" )
436
+ public RubyBoolean reloadablePlugins (final ThreadContext context ) {
437
+ return nonReloadablePlugins (context ).isEmpty () ? context .tru : context .fals ;
438
+ }
439
+
440
+ @ SuppressWarnings ({"unchecked" , "rawtypes" })
441
+ @ JRubyMethod (name = "non_reloadable_plugins" )
442
+ public RubyArray nonReloadablePlugins (final ThreadContext context ) {
443
+ final RubyArray result = RubyArray .newArray (context .runtime );
444
+ Stream .of (inputs , outputs , filters ).flatMap (
445
+ plugins -> ((Collection <IRubyObject >) plugins ).stream ()
446
+ ).filter (
447
+ plugin -> !plugin .callMethod (context , "reloadable?" ).isTrue ()
448
+ ).forEach (result ::add );
449
+ return result ;
450
+ }
451
+
378
452
@ JRubyMethod (name = "collect_stats" )
379
453
public final IRubyObject collectStats (final ThreadContext context ) throws IOException {
380
454
final AbstractNamespacedMetricExt pipelineMetric =
@@ -536,6 +610,17 @@ public final JRubyWrappedWriteClientExt wrappedWriteClient(final ThreadContext c
536
610
.initialize (inputQueueClient , pipelineId .asJavaString (), metric , pluginId );
537
611
}
538
612
613
+ public QueueWriter getQueueWriter (final String inputName ) {
614
+ return new JRubyWrappedWriteClientExt (RubyUtil .RUBY , RubyUtil .WRAPPED_WRITE_CLIENT_CLASS )
615
+ .initialize (
616
+ RubyUtil .RUBY .getCurrentContext (),
617
+ new IRubyObject []{
618
+ inputQueueClient (), pipelineId ().convertToString ().intern (),
619
+ metric (), RubyUtil .RUBY .newSymbol (inputName )
620
+ }
621
+ );
622
+ }
623
+
539
624
@ JRubyMethod (name = "pipeline_source_details" , visibility = Visibility .PROTECTED )
540
625
@ SuppressWarnings ("rawtypes" )
541
626
public RubyArray getPipelineSourceDetails (final ThreadContext context ) {
@@ -589,4 +674,28 @@ private AbstractNamespacedMetricExt getDlqMetric(final ThreadContext context) {
589
674
}
590
675
return dlqMetric ;
591
676
}
677
+
678
+ @ JRubyMethod
679
+ @ SuppressWarnings ("rawtypes" )
680
+ public RubyArray inputs () {
681
+ return inputs ;
682
+ }
683
+
684
+ @ JRubyMethod
685
+ @ SuppressWarnings ("rawtypes" )
686
+ public RubyArray filters () {
687
+ return filters ;
688
+ }
689
+
690
+ @ JRubyMethod
691
+ @ SuppressWarnings ("rawtypes" )
692
+ public RubyArray outputs () {
693
+ return outputs ;
694
+ }
695
+
696
+ @ JRubyMethod (name = "shutdown_requested?" )
697
+ public IRubyObject isShutdownRequested (final ThreadContext context ) {
698
+ // shutdown_requested? MUST be implemented in the concrete implementation of this class.
699
+ throw new IllegalStateException ("Pipeline implementation does not provide `shutdown_requested?`, which is a Logstash internal error." );
700
+ }
592
701
}
0 commit comments