Skip to content

Commit b8bc4d8

Browse files
andseljsvd
andauthored
Move Port and PortRange Ruby settings to Java (#17964)
Move the existing Ruby code to Port and PortRange classes to Java, leveraging the JRuby bridge and avoiding to use the JRuby extension mechanism. Co-authored-by: João Duarte <[email protected]>
1 parent 089361d commit b8bc4d8

File tree

10 files changed

+487
-68
lines changed

10 files changed

+487
-68
lines changed

logstash-core/lib/logstash/environment.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ module Environment
3333
end
3434
end
3535

36+
def self.as_java_range(r)
37+
org::logstash::settings::Range.new(r.first.to_java(java.lang.Integer), r.last.to_java(java.lang.Integer))
38+
end
39+
3640
[
3741
Setting::Boolean.new("allow_superuser", false),
3842
Setting::SettingString.new("node.name", Socket.gethostname),
@@ -67,7 +71,7 @@ module Environment
6771
Setting::Boolean.new("log.format.json.fix_duplicate_message_fields", true),
6872
Setting::Boolean.new("api.enabled", true),
6973
Setting::SettingString.new("api.http.host", "127.0.0.1"),
70-
Setting::PortRange.new("api.http.port", 9600..9700),
74+
Setting::PortRangeSetting.new("api.http.port", 9600..9700),
7175
Setting::SettingString.new("api.environment", "production"),
7276
Setting::SettingString.new("api.auth.type", "none", true, %w(none basic)),
7377
Setting::SettingString.new("api.auth.basic.username", nil, false).nullable,

logstash-core/lib/logstash/settings.rb

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -418,55 +418,10 @@ def coerce(value)
418418
java_import org.logstash.settings.SettingInteger
419419

420420
java_import org.logstash.settings.SettingPositiveInteger
421-
422-
class Port < SettingInteger
423-
VALID_PORT_RANGE = 1..65535
424-
425-
def initialize(name, default = nil, strict = true)
426-
super(name, default, strict) { |value| valid?(value) }
427-
end
428-
429-
def valid?(port)
430-
VALID_PORT_RANGE.cover?(port)
431-
end
432-
end
433-
434-
class PortRange < Coercible
435-
PORT_SEPARATOR = "-"
436-
437-
def initialize(name, default = nil, strict = true)
438-
super(name, ::Range, default, strict = true) { |value| valid?(value) }
439-
end
440-
441-
def valid?(range)
442-
Port::VALID_PORT_RANGE.first <= range.first && Port::VALID_PORT_RANGE.last >= range.last
443-
end
444421

445-
def coerce(value)
446-
case value
447-
when ::Range
448-
value
449-
when ::Integer
450-
value..value
451-
when ::String
452-
first, last = value.split(PORT_SEPARATOR)
453-
last = first if last.nil?
454-
begin
455-
(Integer(first))..(Integer(last))
456-
rescue ArgumentError # Trap and reraise a more human error
457-
raise ArgumentError.new("Could not coerce #{value} into a port range")
458-
end
459-
else
460-
raise ArgumentError.new("Could not coerce #{value} into a port range")
461-
end
462-
end
422+
java_import org.logstash.settings.PortSetting # seems unused
463423

464-
def validate(value)
465-
unless valid?(value)
466-
raise ArgumentError.new("Invalid value \"#{name}: #{value}\", valid options are within the range of #{Port::VALID_PORT_RANGE.first}-#{Port::VALID_PORT_RANGE.last}")
467-
end
468-
end
469-
end
424+
java_import org.logstash.settings.PortRangeSetting
470425

471426
class Validator < Setting
472427
def initialize(name, default = nil, strict = true, validator_class = nil)

logstash-core/spec/logstash/runner_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@
321321
it "creates an Agent whose `api.http.port` uses the default value" do
322322
expect(LogStash::Agent).to receive(:new) do |settings|
323323
expect(settings.set?("api.http.port")).to be_falsey
324-
expect(settings.get("api.http.port")).to eq(9600..9700)
324+
expect(settings.get("api.http.port")).to eq(::LogStash.as_java_range(9600..9700))
325325
end
326326

327327
subject.run("bin/logstash", args)
@@ -335,7 +335,7 @@
335335
it "creates an Agent whose `api.http.port` is an appropriate single-element range" do
336336
expect(LogStash::Agent).to receive(:new) do |settings|
337337
expect(settings.set?("api.http.port")).to be(true)
338-
expect(settings.get("api.http.port")).to eq(10000..10000)
338+
expect(settings.get("api.http.port")).to eq(::LogStash.as_java_range(10000..10000))
339339
end
340340

341341
subject.run("bin/logstash", args)
@@ -346,7 +346,7 @@
346346
it "creates an Agent whose `api.http.port` uses the appropriate inclusive-end range" do
347347
expect(LogStash::Agent).to receive(:new) do |settings|
348348
expect(settings.set?("api.http.port")).to be(true)
349-
expect(settings.get("api.http.port")).to eq(10000..20000)
349+
expect(settings.get("api.http.port")).to eq(::LogStash.as_java_range(10000..20000))
350350
end
351351

352352
subject.run("bin/logstash", args)

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,87 +18,87 @@
1818
require "logstash/settings"
1919
require "spec_helper"
2020

21-
describe LogStash::Setting::PortRange do
21+
describe LogStash::Setting::PortRangeSetting do
2222
context "When the value is an Integer" do
23-
subject { LogStash::Setting::PortRange.new("mynewtest", 9000) }
23+
subject { LogStash::Setting::PortRangeSetting.new("mynewtest", 9000) }
2424

2525
it "coerces the value in a range" do
2626
expect { subject }.not_to raise_error
2727
end
2828

2929
it "returns a range" do
30-
expect(subject.value).to eq(9000..9000)
30+
expect(subject.value).to eq(::LogStash.as_java_range(9000..9000))
3131
end
3232

3333
it "can update the range" do
3434
subject.set(10000)
35-
expect(subject.value).to eq(10000..10000)
35+
expect(subject.value).to eq(::LogStash.as_java_range(10000..10000))
3636
end
3737
end
3838

3939
context "When the value is a string" do
40-
subject { LogStash::Setting::PortRange.new("mynewtest", "9000-10000") }
40+
subject { LogStash::Setting::PortRangeSetting.new("mynewtest", "9000-10000") }
4141

4242
it "coerces a string range with the format (9000-10000)" do
4343
expect { subject }.not_to raise_error
4444
end
4545

4646
it "refuses when then upper port is out of range" do
47-
expect { LogStash::Setting::PortRange.new("mynewtest", "1000-95000") }.to raise_error
47+
expect { LogStash::Setting::PortRangeSetting.new("mynewtest", "1000-95000") }.to raise_error
4848
end
4949

5050
it "returns a range" do
51-
expect(subject.value).to eq(9000..10000)
51+
expect(subject.value).to eq(::LogStash.as_java_range(9000..10000))
5252
end
5353

5454
it "can update the range" do
5555
subject.set("500-1000")
56-
expect(subject.value).to eq(500..1000)
56+
expect(subject.value).to eq(::LogStash.as_java_range(500..1000))
5757
end
5858
end
5959

6060
context "when the value is a garbage string" do
61-
subject { LogStash::Setting::PortRange.new("mynewtest", "fsdfnsdkjnfjs") }
61+
subject { LogStash::Setting::PortRangeSetting.new("mynewtest", "fsdfnsdkjnfjs") }
6262

6363
it "raises an argument error" do
6464
expect { subject }.to raise_error
6565
end
6666

6767
it "raises an exception on update" do
68-
expect { LogStash::Setting::PortRange.new("mynewtest", 10000).set("dsfnsdknfksdnfjksdnfjns") }.to raise_error
68+
expect { LogStash::Setting::PortRangeSetting.new("mynewtest", 10000).set("dsfnsdknfksdnfjksdnfjns") }.to raise_error
6969
end
7070
end
7171

7272
context "when the value is an unknown type" do
73-
subject { LogStash::Setting::PortRange.new("mynewtest", 0.1) }
73+
subject { LogStash::Setting::PortRangeSetting.new("mynewtest", 0.1) }
7474

7575
it "raises an argument error" do
7676
expect { subject }.to raise_error
7777
end
7878

7979
it "raises an exception on update" do
80-
expect { LogStash::Setting::PortRange.new("mynewtest", 10000).set(0.1) }.to raise_error
80+
expect { LogStash::Setting::PortRangeSetting.new("mynewtest", 10000).set(0.1) }.to raise_error
8181
end
8282
end
8383

8484
context "When value is a range" do
85-
subject { LogStash::Setting::PortRange.new("mynewtest", 9000..10000) }
85+
subject { LogStash::Setting::PortRangeSetting.new("mynewtest", 9000..10000) }
8686

8787
it "accepts a ruby range as the default value" do
8888
expect { subject }.not_to raise_error
8989
end
9090

9191
it "can update the range" do
9292
subject.set(500..1000)
93-
expect(subject.value).to eq(500..1000)
93+
expect(subject.value).to eq(::LogStash.as_java_range(500..1000))
9494
end
9595

9696
it "refuses when then upper port is out of range" do
97-
expect { LogStash::Setting::PortRange.new("mynewtest", 9000..1000000) }.to raise_error
97+
expect { LogStash::Setting::PortRangeSetting.new("mynewtest", 9000..1000000) }.to raise_error
9898
end
9999

100100
it "raise an exception on when port are out of range" do
101-
expect { LogStash::Setting::PortRange.new("mynewtest", -1000..1000) }.to raise_error
101+
expect { LogStash::Setting::PortRangeSetting.new("mynewtest", -1000..1000) }.to raise_error
102102
end
103103
end
104104
end

logstash-core/spec/logstash/settings_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
describe "#to_hash" do
6666
let(:java_deprecated_alias) { LogStash::Setting::Boolean.new("java.actual", true).with_deprecated_alias("java.deprecated") }
67-
let(:ruby_deprecated_alias) { LogStash::Setting::PortRange.new("ruby.actual", 9600..9700).with_deprecated_alias("ruby.deprecated") }
67+
let(:ruby_deprecated_alias) { LogStash::Setting::PortRangeSetting.new("ruby.actual", 9600..9700).with_deprecated_alias("ruby.deprecated") }
6868
let(:non_deprecated) { LogStash::Setting::Boolean.new("plain_setting", false) }
6969

7070
before :each do
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
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+
20+
package org.logstash.settings;
21+
22+
import org.jruby.RubyInteger;
23+
import org.jruby.RubyRange;
24+
import org.logstash.RubyUtil;
25+
26+
// Ideally would be a Coercible<Range<Integer>>, but given the fact that
27+
// values can be effectively coerced into the constructor, it needs instances
28+
// of Objects to represent Integer, String, Long to be later coerced into Range<Integer>.
29+
@SuppressWarnings({"rawtypes", "unchecked"})
30+
public class PortRangeSetting extends Coercible<Object> {
31+
32+
private static final Range<Integer> VALID_PORT_RANGE = new Range<>(1, 65535);
33+
public static final String PORT_SEPARATOR = "-";
34+
35+
public PortRangeSetting(String name, Object defaultValue) {
36+
super(name, defaultValue, true, PortRangeSetting::isValid);
37+
}
38+
39+
public static boolean isValid(Object range) {
40+
if (!(range instanceof Range)) {
41+
return false;
42+
}
43+
44+
return VALID_PORT_RANGE.contains((Range<Integer>) range);
45+
}
46+
47+
@Override
48+
public Range<Integer> coerce(Object obj) {
49+
if (obj instanceof Range) {
50+
return (Range) obj;
51+
}
52+
53+
if (obj instanceof Integer) {
54+
Integer val = (Integer) obj;
55+
return new Range<>(val, val);
56+
}
57+
58+
if (obj instanceof Long) {
59+
Long val = (Long) obj;
60+
return new Range<>(val.intValue(), val.intValue());
61+
}
62+
63+
if (obj instanceof String) {
64+
String val = ((String) obj).trim();
65+
String[] parts = val.split(PORT_SEPARATOR);
66+
String firstStr = parts[0];
67+
String lastStr;
68+
if (parts.length == 1) {
69+
lastStr = firstStr;
70+
} else {
71+
lastStr = parts[1];
72+
}
73+
try {
74+
int first = Integer.parseInt(firstStr);
75+
int last = Integer.parseInt(lastStr);
76+
return new Range<>(first, last);
77+
} catch(NumberFormatException e) {
78+
throw new IllegalArgumentException("Could not coerce [" + obj + "](type: " + obj.getClass() + ") into a port range");
79+
}
80+
}
81+
82+
if (obj instanceof RubyRange) {
83+
RubyRange rubyRange = (RubyRange) obj;
84+
RubyInteger begin = rubyRange.begin(RubyUtil.RUBY.getCurrentContext()).convertToInteger();
85+
RubyInteger end = rubyRange.end(RubyUtil.RUBY.getCurrentContext()).convertToInteger();
86+
return new Range<>(begin.getIntValue(), end.getIntValue());
87+
}
88+
throw new IllegalArgumentException("Could not coerce [" + obj + "](type: " + obj.getClass() + ") into a port range");
89+
}
90+
91+
@Override
92+
public void validate(Object value) throws IllegalArgumentException {
93+
if (!isValid(value)) {
94+
final String msg = String.format("Invalid value \"%s: %s\", valid options are within the range of %d-%d",
95+
getName(), value, VALID_PORT_RANGE.getFirst(), VALID_PORT_RANGE.getLast());
96+
97+
throw new IllegalArgumentException(msg);
98+
}
99+
}
100+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
20+
package org.logstash.settings;
21+
22+
import java.util.function.Predicate;
23+
24+
public final class PortSetting extends SettingInteger {
25+
26+
public static final Predicate<Integer> VALID_PORT_RANGE = new Predicate<>() {
27+
@Override
28+
public boolean test(Integer integer) {
29+
return isValid(integer);
30+
}
31+
};
32+
33+
public PortSetting(String name, Integer defaultValue) {
34+
super(name, defaultValue);
35+
}
36+
37+
public PortSetting(String name, Integer defaultValue, boolean strict) {
38+
this(name, defaultValue, strict, VALID_PORT_RANGE);
39+
}
40+
41+
protected PortSetting(String name, Integer defaultValue, boolean strict, Predicate<Integer> validator) {
42+
super(name, defaultValue, strict, validator);
43+
}
44+
45+
public static boolean isValid(int port) {
46+
return 1 <= port && port <= 65535;
47+
}
48+
49+
}

0 commit comments

Comments
 (0)