logstash-core 5.4.3-java → 5.5.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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