powerhome-activeldap 3.2.3

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 (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +6 -0
  3. data/COPYING +340 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +59 -0
  6. data/README.textile +140 -0
  7. data/TODO +32 -0
  8. data/benchmark/README.md +64 -0
  9. data/benchmark/bench-backend.rb +247 -0
  10. data/benchmark/bench-instantiate.rb +98 -0
  11. data/benchmark/config.yaml.sample +5 -0
  12. data/doc/text/development.textile +54 -0
  13. data/doc/text/news.textile +811 -0
  14. data/doc/text/rails.textile +144 -0
  15. data/doc/text/tutorial.textile +1010 -0
  16. data/examples/config.yaml.example +5 -0
  17. data/examples/example.der +0 -0
  18. data/examples/example.jpg +0 -0
  19. data/examples/groupadd +41 -0
  20. data/examples/groupdel +35 -0
  21. data/examples/groupls +49 -0
  22. data/examples/groupmod +42 -0
  23. data/examples/lpasswd +55 -0
  24. data/examples/objects/group.rb +13 -0
  25. data/examples/objects/ou.rb +4 -0
  26. data/examples/objects/user.rb +20 -0
  27. data/examples/ouadd +38 -0
  28. data/examples/useradd +45 -0
  29. data/examples/useradd-binary +53 -0
  30. data/examples/userdel +34 -0
  31. data/examples/userls +50 -0
  32. data/examples/usermod +42 -0
  33. data/examples/usermod-binary-add +50 -0
  34. data/examples/usermod-binary-add-time +54 -0
  35. data/examples/usermod-binary-del +48 -0
  36. data/examples/usermod-lang-add +43 -0
  37. data/lib/active_ldap.rb +85 -0
  38. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
  39. data/lib/active_ldap/acts/tree.rb +78 -0
  40. data/lib/active_ldap/adapter/base.rb +707 -0
  41. data/lib/active_ldap/adapter/jndi.rb +184 -0
  42. data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
  43. data/lib/active_ldap/adapter/ldap.rb +290 -0
  44. data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
  45. data/lib/active_ldap/adapter/net_ldap.rb +309 -0
  46. data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
  47. data/lib/active_ldap/association/belongs_to.rb +47 -0
  48. data/lib/active_ldap/association/belongs_to_many.rb +58 -0
  49. data/lib/active_ldap/association/children.rb +21 -0
  50. data/lib/active_ldap/association/collection.rb +105 -0
  51. data/lib/active_ldap/association/has_many.rb +31 -0
  52. data/lib/active_ldap/association/has_many_utils.rb +44 -0
  53. data/lib/active_ldap/association/has_many_wrap.rb +75 -0
  54. data/lib/active_ldap/association/proxy.rb +107 -0
  55. data/lib/active_ldap/associations.rb +205 -0
  56. data/lib/active_ldap/attribute_methods.rb +23 -0
  57. data/lib/active_ldap/attribute_methods/before_type_cast.rb +24 -0
  58. data/lib/active_ldap/attribute_methods/dirty.rb +43 -0
  59. data/lib/active_ldap/attribute_methods/query.rb +31 -0
  60. data/lib/active_ldap/attribute_methods/read.rb +44 -0
  61. data/lib/active_ldap/attribute_methods/write.rb +38 -0
  62. data/lib/active_ldap/attributes.rb +176 -0
  63. data/lib/active_ldap/base.rb +1410 -0
  64. data/lib/active_ldap/callbacks.rb +71 -0
  65. data/lib/active_ldap/command.rb +49 -0
  66. data/lib/active_ldap/compatible.rb +44 -0
  67. data/lib/active_ldap/configuration.rb +147 -0
  68. data/lib/active_ldap/connection.rb +299 -0
  69. data/lib/active_ldap/distinguished_name.rb +291 -0
  70. data/lib/active_ldap/entry_attribute.rb +78 -0
  71. data/lib/active_ldap/escape.rb +12 -0
  72. data/lib/active_ldap/get_text.rb +20 -0
  73. data/lib/active_ldap/get_text/parser.rb +161 -0
  74. data/lib/active_ldap/helper.rb +92 -0
  75. data/lib/active_ldap/human_readable.rb +133 -0
  76. data/lib/active_ldap/ldap_error.rb +74 -0
  77. data/lib/active_ldap/ldif.rb +930 -0
  78. data/lib/active_ldap/log_subscriber.rb +50 -0
  79. data/lib/active_ldap/object_class.rb +95 -0
  80. data/lib/active_ldap/operations.rb +624 -0
  81. data/lib/active_ldap/persistence.rb +100 -0
  82. data/lib/active_ldap/populate.rb +53 -0
  83. data/lib/active_ldap/railtie.rb +43 -0
  84. data/lib/active_ldap/railties/controller_runtime.rb +48 -0
  85. data/lib/active_ldap/schema.rb +701 -0
  86. data/lib/active_ldap/schema/syntaxes.rb +422 -0
  87. data/lib/active_ldap/timeout.rb +75 -0
  88. data/lib/active_ldap/timeout_stub.rb +17 -0
  89. data/lib/active_ldap/user_password.rb +99 -0
  90. data/lib/active_ldap/validations.rb +200 -0
  91. data/lib/active_ldap/version.rb +3 -0
  92. data/lib/active_ldap/xml.rb +139 -0
  93. data/lib/rails/generators/active_ldap/model/USAGE +18 -0
  94. data/lib/rails/generators/active_ldap/model/model_generator.rb +47 -0
  95. data/lib/rails/generators/active_ldap/model/templates/model_active_ldap.rb +3 -0
  96. data/lib/rails/generators/active_ldap/scaffold/scaffold_generator.rb +14 -0
  97. data/lib/rails/generators/active_ldap/scaffold/templates/ldap.yml +19 -0
  98. data/po/en/active-ldap.po +4029 -0
  99. data/po/ja/active-ldap.po +4060 -0
  100. data/test/add-phonetic-attribute-options-to-slapd.ldif +10 -0
  101. data/test/al-test-utils.rb +428 -0
  102. data/test/command.rb +111 -0
  103. data/test/config.yaml.sample +6 -0
  104. data/test/fixtures/lower_case_object_class_schema.rb +802 -0
  105. data/test/run-test.rb +34 -0
  106. data/test/test_acts_as_tree.rb +60 -0
  107. data/test/test_adapter.rb +121 -0
  108. data/test/test_associations.rb +701 -0
  109. data/test/test_attributes.rb +117 -0
  110. data/test/test_base.rb +1214 -0
  111. data/test/test_base_per_instance.rb +61 -0
  112. data/test/test_bind.rb +62 -0
  113. data/test/test_callback.rb +31 -0
  114. data/test/test_configuration.rb +40 -0
  115. data/test/test_connection.rb +82 -0
  116. data/test/test_connection_per_class.rb +112 -0
  117. data/test/test_connection_per_dn.rb +112 -0
  118. data/test/test_dirty.rb +98 -0
  119. data/test/test_dn.rb +172 -0
  120. data/test/test_find.rb +176 -0
  121. data/test/test_groupadd.rb +50 -0
  122. data/test/test_groupdel.rb +46 -0
  123. data/test/test_groupls.rb +107 -0
  124. data/test/test_groupmod.rb +51 -0
  125. data/test/test_ldif.rb +1890 -0
  126. data/test/test_load.rb +133 -0
  127. data/test/test_lpasswd.rb +75 -0
  128. data/test/test_object_class.rb +74 -0
  129. data/test/test_persistence.rb +131 -0
  130. data/test/test_reflection.rb +175 -0
  131. data/test/test_schema.rb +559 -0
  132. data/test/test_syntax.rb +444 -0
  133. data/test/test_user.rb +217 -0
  134. data/test/test_user_password.rb +108 -0
  135. data/test/test_useradd-binary.rb +62 -0
  136. data/test/test_useradd.rb +57 -0
  137. data/test/test_userdel.rb +48 -0
  138. data/test/test_userls.rb +91 -0
  139. data/test/test_usermod-binary-add-time.rb +65 -0
  140. data/test/test_usermod-binary-add.rb +64 -0
  141. data/test/test_usermod-binary-del.rb +66 -0
  142. data/test/test_usermod-lang-add.rb +59 -0
  143. data/test/test_usermod.rb +58 -0
  144. data/test/test_validation.rb +274 -0
  145. metadata +379 -0
@@ -0,0 +1,10 @@
1
+ # Your LDAP server need to accept 'phonetic' attribute option for test.
2
+ # This is a LDIF file for OpenLDAP to do the confiugration.
3
+ # You can use this file by the following command linne on Debian GNU/Linux
4
+ # or Ubuntu:
5
+ # % sudo -H ldapmodify -Y EXTERNAL -H ldapi:/// -f test/add-phonetic-attribute-options-to-slapd.ldif
6
+ version: 1
7
+ dn: cn=config
8
+ changetype: modify
9
+ add: olcAttributeOptions
10
+ olcAttributeOptions: phonetic lang-
@@ -0,0 +1,428 @@
1
+ require 'test-unit'
2
+
3
+ require 'erb'
4
+ require 'yaml'
5
+ require 'socket'
6
+ require 'rbconfig'
7
+ require 'tempfile'
8
+
9
+ require 'active_ldap'
10
+
11
+ require File.join(File.expand_path(File.dirname(__FILE__)), "command")
12
+
13
+ LDAP_ENV = "test" unless defined?(LDAP_ENV)
14
+
15
+ module AlTestUtils
16
+ def self.included(base)
17
+ base.class_eval do
18
+ include ActiveLdap::GetTextSupport
19
+ include Utilities
20
+ include Config
21
+ include Connection
22
+ include Populate
23
+ include TemporaryEntry
24
+ include CommandSupport
25
+ include MockLogger
26
+ end
27
+ end
28
+
29
+ module Utilities
30
+ def dn(string)
31
+ ActiveLdap::DN.parse(string)
32
+ end
33
+ end
34
+
35
+ module Config
36
+ def setup
37
+ super
38
+ @base_dir = File.expand_path(File.dirname(__FILE__))
39
+ @top_dir = File.expand_path(File.join(@base_dir, ".."))
40
+ @example_dir = File.join(@top_dir, "examples")
41
+ @fixtures_dir = File.join(@base_dir, "fixtures")
42
+ @config_file = File.join(@base_dir, "config.yaml")
43
+ ActiveLdap::Base.configurations = read_config
44
+ end
45
+
46
+ def teardown
47
+ super
48
+ end
49
+
50
+ def current_configuration
51
+ ActiveLdap::Base.configurations[LDAP_ENV]
52
+ end
53
+
54
+ def read_config
55
+ unless File.exist?(@config_file)
56
+ raise "config file for testing doesn't exist: #{@config_file}"
57
+ end
58
+ erb = ERB.new(File.read(@config_file))
59
+ erb.filename = @config_file
60
+ config = YAML.load(erb.result)
61
+ _adapter = adapter
62
+ config.each do |key, value|
63
+ value["adapter"] = _adapter if _adapter
64
+ end
65
+ config
66
+ end
67
+
68
+ def adapter
69
+ ENV["ACTIVE_LDAP_TEST_ADAPTER"]
70
+ end
71
+
72
+ def fixture(*components)
73
+ File.join(@fixtures_dir, *components)
74
+ end
75
+ end
76
+
77
+ module ExampleFile
78
+ def certificate_path
79
+ File.join(@example_dir, 'example.der')
80
+ end
81
+
82
+ @@certificate = nil
83
+ def certificate
84
+ return @@certificate if @@certificate
85
+ if File.exists?(certificate_path)
86
+ @@certificate = read_binary_file(certificate_path)
87
+ return @@certificate
88
+ end
89
+
90
+ require 'openssl'
91
+ rsa = OpenSSL::PKey::RSA.new(512)
92
+ comment = "Generated by Ruby/OpenSSL"
93
+
94
+ cert = OpenSSL::X509::Certificate.new
95
+ cert.version = 3
96
+ cert.serial = 0
97
+ subject = [["OU", "test"],
98
+ ["CN", Socket.gethostname]]
99
+ name = OpenSSL::X509::Name.new(subject)
100
+ cert.subject = name
101
+ cert.issuer = name
102
+ cert.not_before = Time.now
103
+ cert.not_after = Time.now + (365*24*60*60)
104
+ cert.public_key = rsa.public_key
105
+
106
+ ef = OpenSSL::X509::ExtensionFactory.new(nil, cert)
107
+ ef.issuer_certificate = cert
108
+ cert.extensions = [
109
+ ef.create_extension("basicConstraints","CA:FALSE"),
110
+ ef.create_extension("keyUsage", "keyEncipherment"),
111
+ ef.create_extension("subjectKeyIdentifier", "hash"),
112
+ ef.create_extension("extendedKeyUsage", "serverAuth"),
113
+ ef.create_extension("nsComment", comment),
114
+ ]
115
+ aki = ef.create_extension("authorityKeyIdentifier",
116
+ "keyid:always,issuer:always")
117
+ cert.add_extension(aki)
118
+ cert.sign(rsa, OpenSSL::Digest::SHA1.new)
119
+
120
+ @@certificate = cert.to_der
121
+ @@certificate
122
+ end
123
+
124
+ def jpeg_photo_path
125
+ File.join(@example_dir, 'example.jpg')
126
+ end
127
+
128
+ def jpeg_photo
129
+ read_binary_file(jpeg_photo_path)
130
+ end
131
+
132
+ def read_binary_file(path)
133
+ File.open(path, "rb") do |input|
134
+ input.set_encoding("ascii-8bit") if input.respond_to?(:set_encoding)
135
+ input.read
136
+ end
137
+ end
138
+ end
139
+
140
+ module Connection
141
+ def setup
142
+ super
143
+ ActiveLdap::Base.setup_connection
144
+ end
145
+
146
+ def teardown
147
+ ActiveLdap::Base.remove_active_connections!
148
+ super
149
+ end
150
+ end
151
+
152
+ module Populate
153
+ def setup
154
+ @dumped_data = nil
155
+ super
156
+ begin
157
+ @dumped_data = ActiveLdap::Base.dump(:scope => :sub)
158
+ rescue ActiveLdap::ConnectionError
159
+ end
160
+ ActiveLdap::Base.delete_all(nil, :scope => :sub)
161
+ populate
162
+ end
163
+
164
+ def teardown
165
+ if @dumped_data
166
+ ActiveLdap::Base.setup_connection
167
+ ActiveLdap::Base.delete_all(nil, :scope => :sub)
168
+ ActiveLdap::Base.load(@dumped_data)
169
+ end
170
+ super
171
+ end
172
+
173
+ def populate
174
+ populate_base
175
+ populate_ou
176
+ populate_user_class
177
+ populate_group_class
178
+ populate_associations
179
+ end
180
+
181
+ def populate_base
182
+ ActiveLdap::Populate.ensure_base
183
+ end
184
+
185
+ def ou_class(prefix="")
186
+ ou_class = Class.new(ActiveLdap::Base)
187
+ ou_class.ldap_mapping(:dn_attribute => "ou",
188
+ :prefix => prefix,
189
+ :classes => ["top", "organizationalUnit"])
190
+ ou_class
191
+ end
192
+
193
+ def dc_class(prefix="")
194
+ dc_class = Class.new(ActiveLdap::Base)
195
+ dc_class.ldap_mapping(:dn_attribute => "dc",
196
+ :prefix => prefix,
197
+ :classes => ["top", "dcObject", "organization"])
198
+ dc_class
199
+ end
200
+
201
+ def entry_class(prefix="")
202
+ entry_class = Class.new(ActiveLdap::Base)
203
+ entry_class.ldap_mapping(:prefix => prefix,
204
+ :scope => :sub,
205
+ :classes => ["top"])
206
+ entry_class.dn_attribute = nil
207
+ entry_class
208
+ end
209
+
210
+ def populate_ou
211
+ %w(Users Groups).each do |name|
212
+ make_ou(name)
213
+ end
214
+ end
215
+
216
+ def make_ou(name)
217
+ ActiveLdap::Populate.ensure_ou(name)
218
+ end
219
+
220
+ def make_dc(name)
221
+ ActiveLdap::Populate.ensure_dc(name)
222
+ end
223
+
224
+ def populate_user_class
225
+ @user_class = Class.new(ActiveLdap::Base)
226
+ @user_class_classes = ["posixAccount", "person"]
227
+ @user_class.ldap_mapping :dn_attribute => "uid",
228
+ :prefix => "ou=Users",
229
+ :scope => :sub,
230
+ :classes => @user_class_classes
231
+ assign_class_name(@user_class, "User")
232
+ end
233
+
234
+ def populate_group_class
235
+ @group_class = Class.new(ActiveLdap::Base)
236
+ @group_class.ldap_mapping :prefix => "ou=Groups",
237
+ :scope => :sub,
238
+ :classes => ["posixGroup"]
239
+ assign_class_name(@group_class, "Group")
240
+ end
241
+
242
+ def populate_associations
243
+ @user_class.belongs_to :groups, :many => "memberUid"
244
+ @user_class.belongs_to :primary_group,
245
+ :foreign_key => "gidNumber",
246
+ :primary_key => "gidNumber"
247
+ @group_class.has_many :members, :wrap => "memberUid"
248
+ @group_class.has_many :primary_members,
249
+ :foreign_key => "gidNumber",
250
+ :primary_key => "gidNumber"
251
+ @user_class.set_associated_class(:groups, @group_class)
252
+ @user_class.set_associated_class(:primary_group, @group_class)
253
+ @group_class.set_associated_class(:members, @user_class)
254
+ @group_class.set_associated_class(:primary_members, @user_class)
255
+ end
256
+
257
+ def assign_class_name(klass, name)
258
+ singleton_class = class << klass; self; end
259
+ singleton_class.send(:define_method, :name) do
260
+ name
261
+ end
262
+ if Object.const_defined?(klass.name)
263
+ Object.send(:remove_const, klass.name)
264
+ end
265
+ Object.const_set(klass.name, klass)
266
+ end
267
+ end
268
+
269
+ module TemporaryEntry
270
+ include ExampleFile
271
+
272
+ def setup
273
+ super
274
+ @user_index = 0
275
+ @group_index = 0
276
+ end
277
+
278
+ def make_temporary_user(config={})
279
+ @user_index += 1
280
+ uid = config[:uid] || "temp-user#{@user_index}"
281
+ ensure_delete_user(uid) do
282
+ password = config[:password] || "password#{@user_index}"
283
+ uid_number = config[:uid_number] || default_uid
284
+ gid_number = config[:gid_number] || default_gid
285
+ home_directory = config[:home_directory] || "/nonexistent"
286
+ see_also = config[:see_also]
287
+ _wrap_assertion do
288
+ assert(!@user_class.exists?(uid))
289
+ assert_raise(ActiveLdap::EntryNotFound) do
290
+ @user_class.find(uid).dn
291
+ end
292
+ user = @user_class.new(uid)
293
+ assert(user.new_entry?)
294
+ user.cn = user.uid
295
+ user.sn = user.uid
296
+ user.uid_number = uid_number
297
+ user.gid_number = gid_number
298
+ user.home_directory = home_directory
299
+ user.user_password = ActiveLdap::UserPassword.ssha(password)
300
+ user.see_also = see_also
301
+ unless config[:simple]
302
+ user.add_class('shadowAccount', 'inetOrgPerson',
303
+ 'organizationalPerson')
304
+ user.user_certificate = certificate
305
+ user.jpeg_photo = jpeg_photo
306
+ end
307
+ user.save
308
+ assert(!user.new_entry?)
309
+ yield(@user_class.find(user.uid), password)
310
+ end
311
+ end
312
+ end
313
+
314
+ def make_temporary_group(config={})
315
+ @group_index += 1
316
+ cn = config[:cn] || "temp-group#{@group_index}"
317
+ ensure_delete_group(cn) do
318
+ gid_number = config[:gid_number] || default_gid
319
+ _wrap_assertion do
320
+ assert(!@group_class.exists?(cn))
321
+ assert_raise(ActiveLdap::EntryNotFound) do
322
+ @group_class.find(cn)
323
+ end
324
+ group = @group_class.new(cn)
325
+ assert(group.new_entry?)
326
+ group.gid_number = gid_number
327
+ assert(group.save)
328
+ assert(!group.new_entry?)
329
+ yield(@group_class.find(group.cn))
330
+ end
331
+ end
332
+ end
333
+
334
+ def ensure_delete_user(uid)
335
+ yield(uid)
336
+ ensure
337
+ if @user_class.exists?(uid)
338
+ @user_class.search(:value => uid) do |dn, attribute|
339
+ @user_class.remove_connection(dn)
340
+ @user_class.delete(dn)
341
+ end
342
+ end
343
+ end
344
+
345
+ def ensure_delete_group(cn)
346
+ yield(cn)
347
+ ensure
348
+ @group_class.delete(cn) if @group_class.exists?(cn)
349
+ end
350
+
351
+ def default_uid
352
+ "10000#{@user_index}"
353
+ end
354
+
355
+ def default_gid
356
+ "10000#{@group_index}"
357
+ end
358
+ end
359
+
360
+ module CommandSupport
361
+ def setup
362
+ super
363
+ @fakeroot = "fakeroot"
364
+ @ruby = File.join(::RbConfig::CONFIG["bindir"],
365
+ ::RbConfig::CONFIG["RUBY_INSTALL_NAME"])
366
+ @top_dir = File.expand_path(File.join(File.dirname(__FILE__), ".."))
367
+ @examples_dir = File.join(@top_dir, "examples")
368
+ @lib_dir = File.join(@top_dir, "lib")
369
+ @ruby_args = [
370
+ "-I", @examples_dir,
371
+ "-I", @lib_dir,
372
+ ]
373
+ end
374
+
375
+ def run_command(*args, &block)
376
+ file = Tempfile.new("al-command-support")
377
+ file.open
378
+ file.puts(ActiveLdap::Base.configurations["test"].to_yaml)
379
+ file.close
380
+ run_ruby(*[@command, "--config", file.path, *args], &block)
381
+ end
382
+
383
+ def run_ruby(*ruby_args, &block)
384
+ args = [@ruby, *@ruby_args]
385
+ args.concat(ruby_args)
386
+ Command.run(*args, &block)
387
+ end
388
+
389
+ def run_ruby_with_fakeroot(*ruby_args, &block)
390
+ args = [@fakeroot, @ruby, *@ruby_args]
391
+ args.concat(ruby_args)
392
+ Command.run(*args, &block)
393
+ end
394
+ end
395
+
396
+ module MockLogger
397
+ def make_mock_logger
398
+ logger = Object.new
399
+ class << logger
400
+ def messages(type)
401
+ @messages ||= {}
402
+ @messages[type] ||= []
403
+ @messages[type]
404
+ end
405
+
406
+ def info(content=nil)
407
+ messages(:info) << (block_given? ? yield : content)
408
+ end
409
+ def warn(content=nil)
410
+ messages(:warn) << (block_given? ? yield : content)
411
+ end
412
+ def error(content=nil)
413
+ messages(:error) << (block_given? ? yield : content)
414
+ end
415
+ end
416
+ logger
417
+ end
418
+
419
+ def with_mock_logger
420
+ original_logger = ActiveLdap::Base.logger
421
+ mock_logger = make_mock_logger
422
+ ActiveLdap::Base.logger = mock_logger
423
+ yield(mock_logger)
424
+ ensure
425
+ ActiveLdap::Base.logger = original_logger
426
+ end
427
+ end
428
+ end
@@ -0,0 +1,111 @@
1
+ require "thread"
2
+ require "socket"
3
+ require "shellwords"
4
+
5
+ module Command
6
+ class Error < StandardError
7
+ attr_reader :command, :result
8
+ def initialize(command, result)
9
+ @command = command
10
+ @result = result
11
+ super("#{command}: #{result}")
12
+ end
13
+ end
14
+
15
+ module_function
16
+ def detach_io
17
+ require 'fcntl'
18
+ [TCPSocket, ::File].each do |c|
19
+ ObjectSpace.each_object(c) do |io|
20
+ begin
21
+ unless io.closed?
22
+ io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
23
+ end
24
+ rescue SystemCallError, IOError
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def run(cmd, *args, &block)
31
+ raise ArgumentError, "command isn't specified" if cmd.nil?
32
+ if args.any? {|x| x.nil?}
33
+ raise ArgumentError, "args has nil: #{args.inspect}"
34
+ end
35
+ return java_run(cmd, *args, &block) if Object.respond_to?(:java)
36
+ in_r, in_w = IO.pipe
37
+ out_r, out_w = IO.pipe
38
+ pid = nil
39
+ Thread.exclusive do
40
+ verbose = $VERBOSE
41
+ # ruby(>=1.8)'s fork terminates other threads with warning messages
42
+ $VERBOSE = nil
43
+ pid = fork do
44
+ $VERBOSE = verbose
45
+ detach_io
46
+ STDIN.reopen(in_r)
47
+ in_r.close
48
+ STDOUT.reopen(out_w)
49
+ STDERR.reopen(out_w)
50
+ out_w.close
51
+ exec(cmd, *args.collect {|arg| arg.to_s})
52
+ exit!(-1)
53
+ end
54
+ $VERBOSE = verbose
55
+ end
56
+ yield(out_r, in_w) if block_given?
57
+ in_r.close unless in_r.closed?
58
+ out_w.close unless out_w.closed?
59
+ pid, status = Process.waitpid2(pid)
60
+ [status.exited? && status.exitstatus.zero?, out_r.read]
61
+ end
62
+
63
+ def java_run(cmd, *args, &block)
64
+ runtime = java.lang.Runtime.get_runtime
65
+ process = runtime.exec([cmd, *args].to_java(:string))
66
+ input = JavaReaderWrapper.new(process.get_input_stream)
67
+ output = JavaWriterWrapper.new(process.get_output_stream)
68
+ error = JavaReaderWrapper.new(process.get_error_stream)
69
+ yield(input, output) if block_given?
70
+ output.close
71
+ success = process.wait_for.zero?
72
+
73
+ [success, input.read + error.read]
74
+ end
75
+
76
+ class JavaReaderWrapper
77
+ def initialize(input)
78
+ @input = input
79
+ end
80
+
81
+ def read
82
+ result = ""
83
+ while (c = @input.read) != -1
84
+ result << c.chr
85
+ end
86
+ result
87
+ end
88
+ end
89
+
90
+ class JavaWriterWrapper
91
+ def initialize(output)
92
+ output = java.io.OutputStreamWriter.new(output)
93
+ @output = java.io.BufferedWriter.new(output)
94
+ end
95
+
96
+ def puts(*messages)
97
+ messages.each do |message|
98
+ message += "\n" if /\n/ !~ message
99
+ @output.write(message)
100
+ end
101
+ end
102
+
103
+ def flush
104
+ @output.flush
105
+ end
106
+
107
+ def close
108
+ @output.close
109
+ end
110
+ end
111
+ end