logstash-core 5.4.3-java → 5.5.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/lib/logstash-core/logstash-core.jar +0 -0
  3. data/lib/logstash-core/version.rb +1 -1
  4. data/lib/logstash/api/commands/hot_threads_reporter.rb +2 -2
  5. data/lib/logstash/api/commands/node.rb +0 -1
  6. data/lib/logstash/api/commands/stats.rb +0 -1
  7. data/lib/logstash/config/mixin.rb +5 -43
  8. data/lib/logstash/config/modules_common.rb +71 -0
  9. data/lib/logstash/elasticsearch_client.rb +120 -0
  10. data/lib/logstash/environment.rb +14 -3
  11. data/lib/logstash/errors.rb +1 -0
  12. data/lib/logstash/execution_context.rb +11 -3
  13. data/lib/logstash/inputs/base.rb +2 -0
  14. data/lib/logstash/instrument/global_metrics.rb +13 -0
  15. data/lib/logstash/instrument/metric_type/mean.rb +5 -0
  16. data/lib/logstash/instrument/periodic_poller/jvm.rb +5 -5
  17. data/lib/logstash/logging/logger.rb +26 -1
  18. data/lib/logstash/modules/cli_parser.rb +74 -0
  19. data/lib/logstash/modules/elasticsearch_config.rb +22 -0
  20. data/lib/logstash/modules/elasticsearch_resource.rb +10 -0
  21. data/lib/logstash/modules/file_reader.rb +36 -0
  22. data/lib/logstash/modules/importer.rb +37 -0
  23. data/lib/logstash/modules/kibana_base_resource.rb +10 -0
  24. data/lib/logstash/modules/kibana_config.rb +104 -0
  25. data/lib/logstash/modules/kibana_resource.rb +10 -0
  26. data/lib/logstash/modules/logstash_config.rb +48 -0
  27. data/lib/logstash/modules/resource_base.rb +37 -0
  28. data/lib/logstash/modules/scaffold.rb +44 -0
  29. data/lib/logstash/namespace.rb +1 -0
  30. data/lib/logstash/outputs/base.rb +2 -0
  31. data/lib/logstash/pipeline.rb +18 -4
  32. data/lib/logstash/plugin.rb +1 -0
  33. data/lib/logstash/plugins/registry.rb +5 -0
  34. data/lib/logstash/runner.rb +42 -2
  35. data/lib/logstash/settings.rb +7 -1
  36. data/lib/logstash/timestamp.rb +4 -0
  37. data/lib/logstash/util/dead_letter_queue_manager.rb +61 -0
  38. data/lib/logstash/util/safe_uri.rb +130 -11
  39. data/lib/logstash/util/thread_dump.rb +3 -1
  40. data/lib/logstash/util/wrapped_acked_queue.rb +24 -6
  41. data/lib/logstash/util/wrapped_synchronous_queue.rb +19 -5
  42. data/lib/logstash/version.rb +1 -1
  43. data/locales/en.yml +46 -0
  44. data/logstash-core.gemspec +7 -2
  45. data/spec/{api/lib/commands/stats.rb → logstash/api/commands/stats_spec.rb} +7 -2
  46. data/spec/{api/lib → logstash/api}/errors_spec.rb +1 -1
  47. data/spec/{api/lib/api → logstash/api/modules}/logging_spec.rb +1 -10
  48. data/spec/{api/lib/api → logstash/api/modules}/node_plugins_spec.rb +2 -3
  49. data/spec/{api/lib/api → logstash/api/modules}/node_spec.rb +6 -7
  50. data/spec/{api/lib/api → logstash/api/modules}/node_stats_spec.rb +2 -2
  51. data/spec/{api/lib/api → logstash/api/modules}/plugins_spec.rb +4 -3
  52. data/spec/{api/lib/api → logstash/api/modules}/root_spec.rb +3 -3
  53. data/spec/{api/lib → logstash/api}/rack_app_spec.rb +0 -0
  54. data/spec/logstash/config/mixin_spec.rb +2 -2
  55. data/spec/logstash/execution_context_spec.rb +20 -1
  56. data/spec/logstash/filter_delegator_spec.rb +2 -1
  57. data/spec/logstash/inputs/base_spec.rb +1 -1
  58. data/spec/logstash/output_delegator_spec.rb +2 -1
  59. data/spec/logstash/outputs/base_spec.rb +1 -1
  60. data/spec/logstash/pipeline_dlq_commit_spec.rb +107 -0
  61. data/spec/logstash/pipeline_pq_file_spec.rb +1 -1
  62. data/spec/logstash/plugin_spec.rb +1 -1
  63. data/spec/logstash/plugins/registry_spec.rb +22 -5
  64. data/spec/logstash/runner_spec.rb +122 -19
  65. data/spec/logstash/settings_spec.rb +91 -0
  66. data/spec/logstash/timestamp_spec.rb +6 -0
  67. data/spec/support/helpers.rb +80 -1
  68. data/spec/support/matchers.rb +13 -0
  69. data/spec/support/shared_contexts.rb +38 -0
  70. data/spec/support/shared_examples.rb +1 -1
  71. metadata +95 -40
  72. data/spec/api/lib/api/support/resource_dsl_methods.rb +0 -87
  73. data/spec/api/spec_helper.rb +0 -111
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/logging"
4
+ require "erb"
5
+
6
+ require_relative "elasticsearch_config"
7
+ require_relative "kibana_config"
8
+ require_relative "logstash_config"
9
+
10
+ module LogStash module Modules class Scaffold
11
+ include LogStash::Util::Loggable
12
+
13
+ attr_reader :directory, :module_name, :logstash_configuration, :kibana_configuration, :elasticsearch_configuration
14
+
15
+ def initialize(name, directory)
16
+ @module_name = name
17
+ @directory = directory # this is the 'configuration folder in the GEM root.'
18
+ end
19
+
20
+ def import(import_engine)
21
+ @elasticsearch_configuration.resources.each do |resource|
22
+ import_engine.put(resource)
23
+ end
24
+ @kibana_configuration.resources.each do |resource|
25
+ import_engine.put(resource)
26
+ end
27
+ end
28
+
29
+ def with_settings(module_settings)
30
+ @logstash_configuration = LogStashConfig.new(self, module_settings)
31
+ @kibana_configuration = KibanaConfig.new(self, module_settings)
32
+ @elasticsearch_configuration = ElasticsearchConfig.new(self, module_settings)
33
+ self
34
+ end
35
+
36
+ def config_string()
37
+ # settings should be set earlier by the caller.
38
+ # settings should be the subset from the YAML file with a structure like
39
+ # {"name" => "plugin name", "k1" => "v1", "k2" => "v2"}, etc.
40
+ return nil if @logstash_configuration.nil?
41
+ @logstash_configuration.config_string
42
+ end
43
+ end end end # class LogStash::Modules::Scaffold
44
+
@@ -11,4 +11,5 @@ module LogStash
11
11
  module PluginMixins; end
12
12
  module PluginManager; end
13
13
  module Api; end
14
+ module Modules; end
14
15
  end # module LogStash
@@ -109,6 +109,8 @@ class LogStash::Outputs::Base < LogStash::Plugin
109
109
  super
110
110
  # There is no easy way to propage an instance variable into the codec, because the codec
111
111
  # are created at the class level
112
+ # TODO(talevy): Codecs should have their own execution_context, for now they will inherit their
113
+ # parent plugin's
112
114
  @codec.execution_context = context
113
115
  context
114
116
  end
@@ -18,11 +18,15 @@ require "logstash/instrument/null_metric"
18
18
  require "logstash/instrument/namespaced_null_metric"
19
19
  require "logstash/instrument/collector"
20
20
  require "logstash/instrument/wrapped_write_client"
21
+ require "logstash/util/dead_letter_queue_manager"
21
22
  require "logstash/output_delegator"
22
23
  require "logstash/filter_delegator"
23
24
  require "logstash/queue_factory"
24
25
  require "logstash/execution_context"
25
26
 
27
+ java_import org.logstash.common.DeadLetterQueueFactory
28
+ java_import org.logstash.common.io.DeadLetterQueueWriter
29
+
26
30
  module LogStash; class BasePipeline
27
31
  include LogStash::Util::Loggable
28
32
 
@@ -43,7 +47,12 @@ module LogStash; class BasePipeline
43
47
  @inputs = nil
44
48
  @filters = nil
45
49
  @outputs = nil
46
- @execution_context = LogStash::ExecutionContext.new(@pipeline_id)
50
+
51
+ if settings.get_value("dead_letter_queue.enable")
52
+ @dlq_writer = DeadLetterQueueFactory.getWriter(pipeline_id, settings.get_value("path.dead_letter_queue"))
53
+ else
54
+ @dlq_writer = LogStash::Util::DummyDeadLetterQueueWriter.new
55
+ end
47
56
 
48
57
  grammar = LogStashConfigParser.new
49
58
  parsed_config = grammar.parse(config_str)
@@ -90,16 +99,18 @@ module LogStash; class BasePipeline
90
99
 
91
100
  klass = Plugin.lookup(plugin_type, name)
92
101
 
102
+ execution_context = ExecutionContext.new(self, id, klass.config_name, @dlq_writer)
103
+
93
104
  if plugin_type == "output"
94
- OutputDelegator.new(@logger, klass, type_scoped_metric, @execution_context, OutputDelegatorStrategyRegistry.instance, args)
105
+ OutputDelegator.new(@logger, klass, type_scoped_metric, execution_context, OutputDelegatorStrategyRegistry.instance, args)
95
106
  elsif plugin_type == "filter"
96
- FilterDelegator.new(@logger, klass, type_scoped_metric, @execution_context, args)
107
+ FilterDelegator.new(@logger, klass, type_scoped_metric, execution_context, args)
97
108
  else # input
98
109
  input_plugin = klass.new(args)
99
110
  scoped_metric = type_scoped_metric.namespace(id.to_sym)
100
111
  scoped_metric.gauge(:name, input_plugin.config_name)
101
112
  input_plugin.metric = scoped_metric
102
- input_plugin.execution_context = @execution_context
113
+ input_plugin.execution_context = execution_context
103
114
  input_plugin
104
115
  end
105
116
  end
@@ -162,6 +173,7 @@ module LogStash; class Pipeline < BasePipeline
162
173
  )
163
174
  @drain_queue = @settings.get_value("queue.drain")
164
175
 
176
+
165
177
  @events_filtered = Concurrent::AtomicFixnum.new(0)
166
178
  @events_consumed = Concurrent::AtomicFixnum.new(0)
167
179
 
@@ -239,6 +251,7 @@ module LogStash; class Pipeline < BasePipeline
239
251
  def close
240
252
  @filter_queue_client.close
241
253
  @queue.close
254
+ @dlq_writer.close
242
255
  end
243
256
 
244
257
  def transition_to_running
@@ -599,6 +612,7 @@ module LogStash; class Pipeline < BasePipeline
599
612
  n.gauge(:page_capacity_in_bytes, queue.page_capacity)
600
613
  n.gauge(:max_queue_size_in_bytes, queue.max_size_in_bytes)
601
614
  n.gauge(:max_unread_events, queue.max_unread_events)
615
+ n.gauge(:queue_size_in_bytes, queue.persisted_size_in_bytes)
602
616
  end
603
617
  pipeline_metric.namespace([:data]).tap do |n|
604
618
  n.gauge(:free_space_in_bytes, file_store.get_unallocated_space)
@@ -3,6 +3,7 @@ require "logstash/namespace"
3
3
  require "logstash/logging"
4
4
  require "logstash/config/mixin"
5
5
  require "logstash/instrument/null_metric"
6
+ require "logstash/util/dead_letter_queue_manager"
6
7
  require "concurrent"
7
8
  require "securerandom"
8
9
 
@@ -3,6 +3,7 @@ require "rubygems/package"
3
3
  require "logstash/util/loggable"
4
4
  require "logstash/plugin"
5
5
  require "logstash/plugins/hooks_registry"
6
+ require "logstash/modules/scaffold"
6
7
 
7
8
  module LogStash module Plugins
8
9
  class Registry
@@ -109,6 +110,10 @@ module LogStash module Plugins
109
110
  .each { |specification| specification.register(hooks, LogStash::SETTINGS) }
110
111
  end
111
112
 
113
+ def plugins_with_type(type)
114
+ @registry.values.select { |specification| specification.type.to_sym == type.to_sym }.collect(&:klass)
115
+ end
116
+
112
117
  def load_available_plugins
113
118
  GemRegistry.logstash_plugins.each do |plugin_context|
114
119
  # When a plugin has a HOOK_FILE defined, its the responsibility of the plugin
@@ -9,11 +9,13 @@ require "net/http"
9
9
  require "logstash/namespace"
10
10
  require "logstash-core/logstash-core"
11
11
  require "logstash/environment"
12
+ require "logstash/modules/cli_parser"
12
13
 
13
14
  LogStash::Environment.load_locale!
14
15
 
15
16
  require "logstash/agent"
16
17
  require "logstash/config/defaults"
18
+ require "logstash/config/modules_common"
17
19
  require "logstash/shutdown_watcher"
18
20
  require "logstash/patches/clamp"
19
21
  require "logstash/settings"
@@ -22,6 +24,17 @@ require "logstash/plugins/registry"
22
24
 
23
25
  java_import 'org.logstash.FileLockFactory'
24
26
 
27
+ def register_local_modules(path)
28
+ modules_path = File.join(path, File::Separator, "modules")
29
+ Dir.foreach(modules_path) do |item|
30
+ # Ignore unix relative path ids
31
+ next if item == '.' or item == '..'
32
+ # Ignore non-directories
33
+ next if !File.directory?(File.join(modules_path, File::Separator, item))
34
+ LogStash::PLUGIN_REGISTRY.add(:modules, item, LogStash::Modules::Scaffold.new(item, File.join(modules_path, File::Separator, item, File::Separator, "configuration")))
35
+ end
36
+ end
37
+
25
38
  class LogStash::Runner < Clamp::StrictCommand
26
39
  include LogStash::Util::Loggable
27
40
  # The `path.settings` and `path.logs` need to be defined in the runner instead of the `logstash-core/lib/logstash/environment.rb`
@@ -49,6 +62,17 @@ class LogStash::Runner < Clamp::StrictCommand
49
62
  :default => LogStash::SETTINGS.get_default("config.string"),
50
63
  :attribute_name => "config.string"
51
64
 
65
+ # Module settings
66
+ option ["--modules"], "MODULES",
67
+ I18n.t("logstash.runner.flag.modules"),
68
+ :multivalued => true,
69
+ :attribute_name => "modules_list"
70
+
71
+ option ["-M", "--modules.variable"], "MODULES_VARIABLE",
72
+ I18n.t("logstash.runner.flag.modules_variable"),
73
+ :multivalued => true,
74
+ :attribute_name => "modules_variable_list"
75
+
52
76
  # Pipeline settings
53
77
  option ["-w", "--pipeline.workers"], "COUNT",
54
78
  I18n.t("logstash.runner.flag.pipeline-workers"),
@@ -190,7 +214,7 @@ class LogStash::Runner < Clamp::StrictCommand
190
214
  # We invoke post_process to apply extra logic to them.
191
215
  # The post_process callbacks have been added in environment.rb
192
216
  @settings.post_process
193
-
217
+
194
218
  require "logstash/util"
195
219
  require "logstash/util/java_version"
196
220
  require "stud/task"
@@ -211,6 +235,9 @@ class LogStash::Runner < Clamp::StrictCommand
211
235
  logger.warn("--config.debug was specified, but log.level was not set to \'debug\'! No config info will be logged.")
212
236
  end
213
237
 
238
+ # Add local modules to the registry before everything else
239
+ register_local_modules(LogStash::Environment::LOGSTASH_HOME)
240
+
214
241
  # We configure the registry and load any plugin that can register hooks
215
242
  # with logstash, this need to be done before any operation.
216
243
  LogStash::PLUGIN_REGISTRY.setup!
@@ -230,6 +257,10 @@ class LogStash::Runner < Clamp::StrictCommand
230
257
  return 1
231
258
  end
232
259
 
260
+ module_parser = LogStash::Modules::CLIParser.new(@modules_list, @modules_variable_list)
261
+ # Now populate Setting for modules.list with our parsed array.
262
+ @settings.set("modules.cli", module_parser.output)
263
+
233
264
  LogStash::ShutdownWatcher.unsafe_shutdown = setting("pipeline.unsafe_shutdown")
234
265
 
235
266
  configure_plugin_paths(setting("path.plugins"))
@@ -243,6 +274,15 @@ class LogStash::Runner < Clamp::StrictCommand
243
274
 
244
275
  @settings.format_settings.each {|line| logger.debug(line) }
245
276
 
277
+ module_configs = LogStash::Config::ModulesCommon.pipeline_configs(@settings)
278
+ module_config_hash = module_configs.first
279
+ if !module_config_hash.nil?
280
+ @settings.set_value("config.string", module_config_hash["config_string"])
281
+ end
282
+ if module_configs.size > 1
283
+ logger.warn "Multiple modules defined in logstash.yml - using the first one: #{module_config_hash["pipeline_id"]}"
284
+ end
285
+
246
286
  if setting("config.string").nil? && setting("path.config").nil?
247
287
  fail(I18n.t("logstash.runner.missing-configuration"))
248
288
  end
@@ -434,7 +474,7 @@ class LogStash::Runner < Clamp::StrictCommand
434
474
  nil
435
475
  end
436
476
  end
437
-
477
+
438
478
  # is the user asking for CLI help subcommand?
439
479
  def cli_help?(args)
440
480
  # I know, double negative
@@ -2,11 +2,14 @@
2
2
  require "logstash/util/loggable"
3
3
  require "fileutils"
4
4
  require "logstash/util/byte_value"
5
+ require "logstash/util/environment_variables"
5
6
  require "logstash/util/time_value"
6
7
 
7
8
  module LogStash
8
9
  class Settings
9
10
 
11
+ include LogStash::Util::EnvironmentVariables
12
+
10
13
  def initialize
11
14
  @settings = {}
12
15
  # Theses settings were loaded from the yaml file
@@ -107,7 +110,10 @@ module LogStash
107
110
 
108
111
  def from_yaml(yaml_path)
109
112
  settings = read_yaml(::File.join(yaml_path, "logstash.yml"))
110
- self.merge(flatten_hash(settings), true)
113
+ self.merge(
114
+ deep_replace(flatten_hash(settings)),
115
+ true
116
+ )
111
117
  self
112
118
  end
113
119
 
@@ -13,6 +13,10 @@ module LogStash
13
13
  self.time <=> other.time
14
14
  end
15
15
 
16
+ def eql?(other)
17
+ self.== other
18
+ end
19
+
16
20
  # TODO (colin) implement in Java
17
21
  def +(other)
18
22
  self.time + other
@@ -0,0 +1,61 @@
1
+ require 'logstash/environment'
2
+
3
+ module LogStash; module Util
4
+ class PluginDeadLetterQueueWriter
5
+
6
+ attr_reader :plugin_id, :plugin_type, :inner_writer
7
+
8
+ def initialize(inner_writer, plugin_id, plugin_type)
9
+ @plugin_id = plugin_id
10
+ @plugin_type = plugin_type
11
+ @inner_writer = inner_writer
12
+ end
13
+
14
+ def write(logstash_event, reason)
15
+ if @inner_writer && @inner_writer.is_open
16
+ @inner_writer.writeEntry(logstash_event.to_java, @plugin_type, @plugin_id, reason)
17
+ end
18
+ end
19
+
20
+ def close
21
+ if @inner_writer && @inner_writer.is_open
22
+ @inner_writer.close
23
+ end
24
+ end
25
+ end
26
+
27
+ class DummyDeadLetterQueueWriter
28
+ # class uses to represent a writer when dead_letter_queue is disabled
29
+ def initialize
30
+ end
31
+
32
+ def write(logstash_event, reason)
33
+ # noop
34
+ end
35
+
36
+ def is_open
37
+ false
38
+ end
39
+
40
+ def close
41
+ # noop
42
+ end
43
+ end
44
+
45
+ class DeadLetterQueueFactory
46
+ java_import org.logstash.common.DeadLetterQueueFactory
47
+
48
+ def self.get(pipeline_id)
49
+ if LogStash::SETTINGS.get("dead_letter_queue.enable")
50
+ return DeadLetterQueueWriter.new(
51
+ DeadLetterQueueFactory.getWriter(pipeline_id, LogStash::SETTINGS.get("path.dead_letter_queue")))
52
+ else
53
+ return DeadLetterQueueWriter.new(nil)
54
+ end
55
+ end
56
+
57
+ def self.close(pipeline_id)
58
+ DeadLetterQueueFactory.close(pipeline_id)
59
+ end
60
+ end
61
+ end end
@@ -11,20 +11,21 @@ class LogStash::Util::SafeURI
11
11
 
12
12
  extend Forwardable
13
13
 
14
- def_delegators :@uri, :coerce, :query=, :route_from, :port=, :default_port, :select, :normalize!, :absolute?, :registry=, :path, :password, :hostname, :merge, :normalize, :host, :component_ary, :userinfo=, :query, :set_opaque, :+, :merge!, :-, :password=, :parser, :port, :set_host, :set_path, :opaque=, :scheme, :fragment=, :set_query, :set_fragment, :userinfo, :hostname=, :set_port, :path=, :registry, :opaque, :route_to, :set_password, :hierarchical?, :set_user, :set_registry, :set_userinfo, :fragment, :component, :user=, :set_scheme, :absolute, :host=, :relative?, :scheme=, :user
15
14
 
16
15
  attr_reader :uri
17
-
16
+
18
17
  public
19
18
  def initialize(arg)
20
19
  @uri = case arg
21
20
  when String
22
21
  arg = "//#{arg}" if HOSTNAME_PORT_REGEX.match(arg)
23
- URI.parse(arg)
24
- when URI
22
+ java.net.URI.new(arg)
23
+ when java.net.URI
25
24
  arg
25
+ when URI
26
+ java.net.URI.new(arg.to_s)
26
27
  else
27
- raise ArgumentError, "Expected a string or URI, got a #{arg.class} creating a URL"
28
+ raise ArgumentError, "Expected a string, java.net.URI, or URI, got a #{arg.class} creating a URL"
28
29
  end
29
30
  end
30
31
 
@@ -37,11 +38,11 @@ class LogStash::Util::SafeURI
37
38
  end
38
39
 
39
40
  def sanitized
40
- return uri unless uri.password # nothing to sanitize here!
41
+ return uri unless password # nothing to sanitize here!
41
42
 
42
- safe = uri.clone
43
- safe.password = PASS_PLACEHOLDER
44
- safe
43
+ user_info = user ? "#{user}:#{PASS_PLACEHOLDER}" : nil
44
+
45
+ make_uri(scheme, user_info, host, port, path, query, fragment)
45
46
  end
46
47
 
47
48
  def ==(other)
@@ -49,8 +50,126 @@ class LogStash::Util::SafeURI
49
50
  end
50
51
 
51
52
  def clone
52
- cloned_uri = uri.clone
53
- self.class.new(cloned_uri)
53
+ # No need to clone the URI, in java its immutable
54
+ self.class.new(uri)
55
+ end
56
+
57
+ def update(field, value)
58
+ new_scheme = scheme
59
+ new_user = user
60
+ new_password = password
61
+ new_host = host
62
+ new_port = port
63
+ new_path = path
64
+ new_query = query
65
+ new_fragment = fragment
66
+
67
+ case field
68
+ when :scheme
69
+ new_scheme = value
70
+ when :user
71
+ new_user = value
72
+ when :password
73
+ new_password = value
74
+ when :host
75
+ new_host = value
76
+ when :port
77
+ new_port = value
78
+ when :path
79
+ new_path = value
80
+ when :query
81
+ new_query = value
82
+ when :fragment
83
+ new_fragment = value
84
+ end
85
+
86
+ user_info = new_user
87
+ if new_user && new_password
88
+ user_info += ":" + new_password
89
+ end
90
+
91
+ @uri = make_uri(new_scheme, user_info, new_host, new_port, new_path, new_query, new_fragment)
92
+ end
93
+
94
+ def user
95
+ if @uri.userInfo
96
+ @uri.userInfo.split(":")[0]
97
+ end
98
+ end
99
+
100
+ def user=(new_user)
101
+ update(:user, new_user)
102
+ end
103
+
104
+ def password
105
+ if @uri.userInfo
106
+ @uri.userInfo.split(":")[1]
107
+ end
108
+ end
109
+
110
+ def password=(new_password)
111
+ update(:password, new_password)
112
+ end
113
+
114
+ def hostname
115
+ # Alias from the ruby library
116
+ host
117
+ end
118
+
119
+ def host=(new_host)
120
+ update(:host, new_host)
121
+ end
122
+
123
+ def port
124
+ # In java this is an int
125
+ uri.port < 1 ? nil : uri.port
126
+ end
127
+
128
+ def port=(new_port)
129
+ update(:port, new_port)
130
+ end
131
+
132
+ def path=(new_path)
133
+ update(:path, new_path)
134
+ end
135
+
136
+ def query=(new_query)
137
+ update(:query, new_query)
138
+ end
139
+
140
+ def fragment=(new_fragment)
141
+ update(:fragment, new_fragment)
142
+ end
143
+
144
+ # Same algorithm as Ruby's URI class uses
145
+ def normalize!
146
+ if path && path == ''
147
+ path = '/'
148
+ end
149
+ if scheme && scheme != scheme.downcase
150
+ scheme = self.scheme.downcase
151
+ end
152
+ if host && host != host.downcase
153
+ host = self.host.downcase
154
+ end
155
+ end
156
+
157
+ def normalize
158
+ d = self.dup
159
+ d.normalize!
160
+ d
161
+ end
162
+
163
+ def_delegators :@uri, :absolute?, :scheme, :host, :path, :query, :fragment, :userinfo
164
+
165
+ private
166
+
167
+ # Jruby doesn't guess the constructor correctly if there are some nil things in place
168
+ # hence, this method
169
+ def make_uri(scheme, user_info, host, port, path, query, fragment)
170
+ # It is lot legal to have a path not starting with a /
171
+ prefixed_path = path && path[0] != "/" ? "/#{path}" : path
172
+ java.net.URI.new(scheme, user_info, host, port || -1, prefixed_path, query, fragment)
54
173
  end
55
174
  end
56
175