Skip to content

Commit 07a3c8e

Browse files
authored
Reimplement LogStash::Numeric setting in Java (elastic#17127)
Reimplements `LogStash::Setting::Numeric` Ruby setting class into the `org.logstash.settings.NumericSetting` and exposes it through `java_import` as `LogStash::Setting::NumericSetting`. Updates the rspec tests: - verifies `java.lang.IllegalArgumentException` instead of `ArgumentError` is thrown because the kind of exception thrown by Java code, during verification.
1 parent a736178 commit 07a3c8e

File tree

6 files changed

+136
-34
lines changed

6 files changed

+136
-34
lines changed

logstash-core/lib/logstash/environment.rb

+7-7
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ module Environment
4848
Setting::Boolean.new("pipeline.system", false),
4949
Setting::PositiveInteger.new("pipeline.workers", LogStash::Config::CpuCoreStrategy.maximum),
5050
Setting::PositiveInteger.new("pipeline.batch.size", 125),
51-
Setting::Numeric.new("pipeline.batch.delay", 50), # in milliseconds
51+
Setting::SettingNumeric.new("pipeline.batch.delay", 50), # in milliseconds
5252
Setting::Boolean.new("pipeline.unsafe_shutdown", false),
5353
Setting::Boolean.new("pipeline.reloadable", true),
5454
Setting::Boolean.new("pipeline.plugin_classloaders", false),
@@ -72,7 +72,7 @@ module Environment
7272
Setting::SettingString.new("api.auth.basic.username", nil, false).nullable,
7373
Setting::Password.new("api.auth.basic.password", nil, false).nullable,
7474
Setting::SettingString.new("api.auth.basic.password_policy.mode", "WARN", true, %w[WARN ERROR]),
75-
Setting::Numeric.new("api.auth.basic.password_policy.length.minimum", 8),
75+
Setting::SettingNumeric.new("api.auth.basic.password_policy.length.minimum", 8),
7676
Setting::SettingString.new("api.auth.basic.password_policy.include.upper", "REQUIRED", true, %w[REQUIRED OPTIONAL]),
7777
Setting::SettingString.new("api.auth.basic.password_policy.include.lower", "REQUIRED", true, %w[REQUIRED OPTIONAL]),
7878
Setting::SettingString.new("api.auth.basic.password_policy.include.digit", "REQUIRED", true, %w[REQUIRED OPTIONAL]),
@@ -85,14 +85,14 @@ module Environment
8585
Setting::Boolean.new("queue.drain", false),
8686
Setting::Bytes.new("queue.page_capacity", "64mb"),
8787
Setting::Bytes.new("queue.max_bytes", "1024mb"),
88-
Setting::Numeric.new("queue.max_events", 0), # 0 is unlimited
89-
Setting::Numeric.new("queue.checkpoint.acks", 1024), # 0 is unlimited
90-
Setting::Numeric.new("queue.checkpoint.writes", 1024), # 0 is unlimited
91-
Setting::Numeric.new("queue.checkpoint.interval", 1000), # 0 is no time-based checkpointing
88+
Setting::SettingNumeric.new("queue.max_events", 0), # 0 is unlimited
89+
Setting::SettingNumeric.new("queue.checkpoint.acks", 1024), # 0 is unlimited
90+
Setting::SettingNumeric.new("queue.checkpoint.writes", 1024), # 0 is unlimited
91+
Setting::SettingNumeric.new("queue.checkpoint.interval", 1000), # 0 is no time-based checkpointing
9292
Setting::Boolean.new("queue.checkpoint.retry", true),
9393
Setting::Boolean.new("dead_letter_queue.enable", false),
9494
Setting::Bytes.new("dead_letter_queue.max_bytes", "1024mb"),
95-
Setting::Numeric.new("dead_letter_queue.flush_interval", 5000),
95+
Setting::SettingNumeric.new("dead_letter_queue.flush_interval", 5000),
9696
Setting::SettingString.new("dead_letter_queue.storage_policy", "drop_newer", true, ["drop_newer", "drop_older"]),
9797
Setting::SettingNullableString.new("dead_letter_queue.retain.age"), # example 5d
9898
Setting::TimeValue.new("slowlog.threshold.warn", "-1"),

logstash-core/lib/logstash/settings.rb

+1-20
Original file line numberDiff line numberDiff line change
@@ -413,26 +413,7 @@ def coerce(value)
413413
### Specific settings #####
414414

415415
java_import org.logstash.settings.Boolean
416-
417-
class Numeric < Coercible
418-
def initialize(name, default = nil, strict = true)
419-
super(name, ::Numeric, default, strict)
420-
end
421-
422-
def coerce(v)
423-
return v if v.is_a?(::Numeric)
424-
425-
# I hate these "exceptions as control flow" idioms
426-
# but Ruby's `"a".to_i => 0` makes it hard to do anything else.
427-
coerced_value = (Integer(v) rescue nil) || (Float(v) rescue nil)
428-
429-
if coerced_value.nil?
430-
raise ArgumentError.new("Failed to coerce value to Numeric. Received #{v} (#{v.class})")
431-
else
432-
coerced_value
433-
end
434-
end
435-
end
416+
java_import org.logstash.settings.SettingNumeric
436417

437418
class Integer < Coercible
438419
def initialize(name, default = nil, strict = true)

logstash-core/spec/logstash/queue_factory_spec.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626
LogStash::Setting::SettingString.new("queue.type", "memory", true, ["persisted", "memory"]),
2727
LogStash::Setting::Bytes.new("queue.page_capacity", "8mb"),
2828
LogStash::Setting::Bytes.new("queue.max_bytes", "64mb"),
29-
LogStash::Setting::Numeric.new("queue.max_events", 0),
30-
LogStash::Setting::Numeric.new("queue.checkpoint.acks", 1024),
31-
LogStash::Setting::Numeric.new("queue.checkpoint.writes", 1024),
32-
LogStash::Setting::Numeric.new("queue.checkpoint.interval", 1000),
29+
LogStash::Setting::SettingNumeric.new("queue.max_events", 0),
30+
LogStash::Setting::SettingNumeric.new("queue.checkpoint.acks", 1024),
31+
LogStash::Setting::SettingNumeric.new("queue.checkpoint.writes", 1024),
32+
LogStash::Setting::SettingNumeric.new("queue.checkpoint.interval", 1000),
3333
LogStash::Setting::Boolean.new("queue.checkpoint.retry", false),
3434
LogStash::Setting::SettingString.new("pipeline.id", pipeline_id),
3535
LogStash::Setting::PositiveInteger.new("pipeline.batch.size", 125),

logstash-core/spec/logstash/settings/numeric_spec.rb

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@
1818
require "spec_helper"
1919
require "logstash/settings"
2020

21-
describe LogStash::Setting::Numeric do
21+
# Mirrored in java class org.logstash.settings.SettingNumeric
22+
describe LogStash::Setting::SettingNumeric do
2223
subject { described_class.new("a number", nil, false) }
2324
describe "#set" do
2425
context "when giving a string which doesn't represent a string" do
2526
it "should raise an exception" do
26-
expect { subject.set("not-a-number") }.to raise_error(ArgumentError)
27+
expect { subject.set("not-a-number") }.to raise_error(java.lang.IllegalArgumentException)
2728
end
2829
end
2930
context "when giving a string which represents a " do
3031
context "float" do
3132
it "should coerce that string to the number" do
3233
subject.set("1.1")
33-
expect(subject.value).to eq(1.1)
34+
expect(subject.value).to be_within(0.01).of(1.1)
3435
end
3536
end
3637
context "int" do
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.logstash.settings;
20+
21+
public class SettingNumeric extends Coercible<Number> {
22+
23+
public SettingNumeric(String name, Number defaultValue) {
24+
super(name, defaultValue, true, noValidator());
25+
}
26+
27+
// constructor used only in tests, but needs to be public to be used in Ruby spec
28+
public SettingNumeric(String name, Number defaultValue, boolean strict) {
29+
super(name, defaultValue, strict, noValidator());
30+
}
31+
32+
@Override
33+
public Number coerce(Object obj) {
34+
if (obj == null) {
35+
throw new IllegalArgumentException("Failed to coerce value to SettingNumeric. Received null");
36+
}
37+
if (obj instanceof Number) {
38+
return (Number) obj;
39+
}
40+
try {
41+
return Integer.parseInt(obj.toString());
42+
} catch (NumberFormatException e) {
43+
// ugly flow control
44+
}
45+
try {
46+
return Float.parseFloat(obj.toString());
47+
} catch (NumberFormatException e) {
48+
// ugly flow control
49+
}
50+
51+
// no integer neither float parsing succeed, invalid coercion
52+
throw new IllegalArgumentException(coercionFailureMessage(obj));
53+
}
54+
55+
private String coercionFailureMessage(Object obj) {
56+
return String.format("Failed to coerce value to SettingNumeric. Received %s (%s)", obj, obj.getClass());
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.logstash.settings;
20+
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
24+
import static org.junit.Assert.*;
25+
26+
// Mirrored from spec/logstash/settings/numeric_spec.rb
27+
public class SettingNumericTest {
28+
29+
private SettingNumeric sut;
30+
31+
@Before
32+
public void setUp() {
33+
sut = new SettingNumeric("a number", null, false);
34+
}
35+
36+
@Test(expected = IllegalArgumentException.class)
37+
public void givenValueThatIsNotStringWhenSetIsInvokedThrowsException() {
38+
sut.set("not-a-number");
39+
}
40+
41+
@Test
42+
public void givenValueStringThatRepresentFloatWhenSetIsInvokedShouldCoerceThatStringToTheNumber() {
43+
sut.set("1.1");
44+
45+
float value = (Float) sut.value();
46+
assertEquals(1.1f, value, 0.001);
47+
}
48+
49+
@Test
50+
public void givenValueStringThatRepresentIntegerWhenSetIsInvokedShouldCoerceThatStringToTheNumber() {
51+
sut.set("1");
52+
53+
int value = (Integer) sut.value();
54+
assertEquals(1, value);
55+
}
56+
57+
@Test(expected = IllegalArgumentException.class)
58+
public void givenNullValueThenCoerceThrowSpecificError() {
59+
sut.set(null);
60+
}
61+
62+
}

0 commit comments

Comments
 (0)