ladle 0.1.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 (55) hide show
  1. data/ASL20-LICENSE +343 -0
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE +20 -0
  4. data/NOTICE +52 -0
  5. data/README.md +121 -0
  6. data/lib/ladle.rb +13 -0
  7. data/lib/ladle/Ladle.iml +13 -0
  8. data/lib/ladle/apacheds/antlr-2.7.6.jar +0 -0
  9. data/lib/ladle/apacheds/apacheds-core-1.0.2.jar +0 -0
  10. data/lib/ladle/apacheds/apacheds-core-shared-1.0.2.jar +0 -0
  11. data/lib/ladle/apacheds/apacheds-kerberos-shared-1.0.2.jar +0 -0
  12. data/lib/ladle/apacheds/apacheds-protocol-changepw-1.0.2.jar +0 -0
  13. data/lib/ladle/apacheds/apacheds-protocol-kerberos-1.0.2.jar +0 -0
  14. data/lib/ladle/apacheds/apacheds-protocol-ldap-1.0.2.jar +0 -0
  15. data/lib/ladle/apacheds/apacheds-protocol-ntp-1.0.2.jar +0 -0
  16. data/lib/ladle/apacheds/apacheds-protocol-shared-1.0.2.jar +0 -0
  17. data/lib/ladle/apacheds/apacheds-server-jndi-1.0.2.jar +0 -0
  18. data/lib/ladle/apacheds/apacheds-server-main-1.0.2.jar +0 -0
  19. data/lib/ladle/apacheds/apacheds-server-ssl-1.0.2.jar +0 -0
  20. data/lib/ladle/apacheds/backport-util-concurrent-2.2.jar +0 -0
  21. data/lib/ladle/apacheds/commons-cli-1.0.jar +0 -0
  22. data/lib/ladle/apacheds/commons-collections-3.2.jar +0 -0
  23. data/lib/ladle/apacheds/commons-io-1.4.jar +0 -0
  24. data/lib/ladle/apacheds/commons-lang-2.1.jar +0 -0
  25. data/lib/ladle/apacheds/jcl-over-slf4j-1.5.6.jar +0 -0
  26. data/lib/ladle/apacheds/jdbm-1.0.jar +0 -0
  27. data/lib/ladle/apacheds/log4j-1.2.14.jar +0 -0
  28. data/lib/ladle/apacheds/mina-core-1.0.2.jar +0 -0
  29. data/lib/ladle/apacheds/mina-filter-ssl-1.0.2.jar +0 -0
  30. data/lib/ladle/apacheds/shared-asn1-0.9.5.5.jar +0 -0
  31. data/lib/ladle/apacheds/shared-asn1-codec-0.9.5.5.jar +0 -0
  32. data/lib/ladle/apacheds/shared-ldap-0.9.5.5.jar +0 -0
  33. data/lib/ladle/apacheds/slf4j-api-1.5.6.jar +0 -0
  34. data/lib/ladle/apacheds/slf4j-log4j12-1.5.6.jar +0 -0
  35. data/lib/ladle/apacheds/spring-beans-1.2.8.jar +0 -0
  36. data/lib/ladle/apacheds/spring-context-1.2.8.jar +0 -0
  37. data/lib/ladle/apacheds/spring-core-1.2.8.jar +0 -0
  38. data/lib/ladle/apacheds/xercesImpl-2.0.2.jar +0 -0
  39. data/lib/ladle/default.ldif +292 -0
  40. data/lib/ladle/java/net/detailedbalance/ladle/LadleFatalException.class +0 -0
  41. data/lib/ladle/java/net/detailedbalance/ladle/LadleFatalException.java +14 -0
  42. data/lib/ladle/java/net/detailedbalance/ladle/Main$1.class +0 -0
  43. data/lib/ladle/java/net/detailedbalance/ladle/Main.class +0 -0
  44. data/lib/ladle/java/net/detailedbalance/ladle/Main.java +141 -0
  45. data/lib/ladle/java/net/detailedbalance/ladle/Server.class +0 -0
  46. data/lib/ladle/java/net/detailedbalance/ladle/Server.java +170 -0
  47. data/lib/ladle/jruby_process.rb +61 -0
  48. data/lib/ladle/ruby_process.rb +46 -0
  49. data/lib/ladle/server.rb +334 -0
  50. data/lib/ladle/version.rb +5 -0
  51. data/spec/ladle/animals.ldif +28 -0
  52. data/spec/ladle/server_spec.rb +306 -0
  53. data/spec/ladle/version_spec.rb +11 -0
  54. data/spec/spec_helper.rb +32 -0
  55. metadata +172 -0
@@ -0,0 +1,170 @@
1
+ package net.detailedbalance.ladle;
2
+
3
+ import org.apache.commons.io.FileUtils;
4
+ import org.apache.directory.server.configuration.MutableServerStartupConfiguration;
5
+ import org.apache.directory.server.core.configuration.Configuration;
6
+ import org.apache.directory.server.core.configuration.MutablePartitionConfiguration;
7
+ import org.apache.directory.server.core.configuration.ShutdownConfiguration;
8
+ import org.apache.log4j.Logger;
9
+
10
+ import javax.naming.Context;
11
+ import javax.naming.NamingException;
12
+ import javax.naming.directory.Attribute;
13
+ import javax.naming.directory.Attributes;
14
+ import javax.naming.directory.BasicAttribute;
15
+ import javax.naming.directory.BasicAttributes;
16
+ import javax.naming.directory.InitialDirContext;
17
+ import java.io.File;
18
+ import java.io.IOException;
19
+ import java.util.Collections;
20
+ import java.util.HashSet;
21
+ import java.util.Hashtable;
22
+ import java.util.Set;
23
+ import java.util.UUID;
24
+
25
+ /**
26
+ * The class that creates and controls an embedded ApacheDS instance. This runner is not designed
27
+ * for thread-safety or even to be used in the same JVM as anything else -- it's intended to be run
28
+ * in its own process to provide LDAP access over TCP.
29
+ * <p>
30
+ * The idea of using ApacheDS for this was from Spring Security's LDAP test support. The details
31
+ * are from the ApacheDS embedding and unit testing documentation.
32
+ */
33
+ public class Server {
34
+ private final Logger log = Logger.getLogger(getClass());
35
+
36
+ private final int port;
37
+ private final String domainComponent;
38
+ private final File tempDir;
39
+ private final File ldifDir;
40
+ private boolean running = false;
41
+
42
+ public Server(int port, String domainComponent, File ldifFile, File tempDirBase) {
43
+ this.port = port;
44
+ this.domainComponent = domainComponent;
45
+ this.tempDir = createTempDir(tempDirBase);
46
+ this.ldifDir = prepareLdif(ldifFile);
47
+ }
48
+
49
+ ////// SETUP
50
+
51
+ private File createTempDir(File tempDirBase) {
52
+ File temp = new File(tempDirBase, "ladle-server-" + UUID.randomUUID());
53
+
54
+ if (temp.mkdir()) {
55
+ return temp;
56
+ } else {
57
+ throw new LadleFatalException("Could not create temporary directory " + temp);
58
+ }
59
+ }
60
+
61
+ private static Hashtable<String, String> baseEnvironment() {
62
+ Hashtable<String, String> env = new Hashtable<String, String>();
63
+ env.put(Context.PROVIDER_URL, "");
64
+ env.put(Context.INITIAL_CONTEXT_FACTORY,
65
+ "org.apache.directory.server.jndi.ServerContextFactory");
66
+
67
+ // these values are apparently hardcoded in ApacheDS
68
+ env.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
69
+ env.put(Context.SECURITY_CREDENTIALS, "secret");
70
+ env.put(Context.SECURITY_AUTHENTICATION, "simple");
71
+
72
+ return env;
73
+ }
74
+
75
+ private File prepareLdif(File ldifFile) {
76
+ File dir = new File(tempDir, "ldif");
77
+ if (!dir.mkdir()) {
78
+ throw new LadleFatalException("Could not create LDIF directory " + dir);
79
+ }
80
+
81
+ try {
82
+ FileUtils.copyFileToDirectory(ldifFile, dir);
83
+ } catch (IOException e) {
84
+ throw new LadleFatalException("Copying " + ldifFile + " to " + dir + " failed.", e);
85
+ }
86
+
87
+ return dir;
88
+ }
89
+
90
+ ////// RUNNING
91
+
92
+ public void start() {
93
+ if (running) return;
94
+
95
+ try {
96
+ MutableServerStartupConfiguration cfg = new MutableServerStartupConfiguration();
97
+ cfg.setWorkingDirectory(tempDir);
98
+ cfg.setLdifDirectory(ldifDir);
99
+ cfg.setEnableNetworking(true);
100
+ cfg.setLdapPort(port);
101
+ cfg.setAllowAnonymousAccess(true);
102
+ cfg.setAccessControlEnabled(false);
103
+ cfg.setShutdownHookEnabled(false);
104
+ cfg.setContextPartitionConfigurations(
105
+ Collections.singleton(createPartitionConfiguration()));
106
+
107
+ new InitialDirContext(createJndiEnvironment(cfg));
108
+ } catch (NamingException e) {
109
+ throw new LadleFatalException("Startup failed", e);
110
+ }
111
+
112
+ running = true;
113
+ }
114
+
115
+ // Derived from http://directory.apache.org/apacheds/1.0/using-apacheds-for-unit-tests.html
116
+ private MutablePartitionConfiguration createPartitionConfiguration() throws NamingException {
117
+ MutablePartitionConfiguration pCfg = new MutablePartitionConfiguration();
118
+ pCfg.setName("ladle");
119
+ pCfg.setSuffix(domainComponent);
120
+
121
+ Set<String> indexedAttrs = new HashSet<String>();
122
+ indexedAttrs.add("objectClass");
123
+ indexedAttrs.add("dc");
124
+ indexedAttrs.add("uid");
125
+ pCfg.setIndexedAttributes( indexedAttrs );
126
+
127
+ // Create the root entry
128
+ {
129
+ Attributes attrs = new BasicAttributes(true);
130
+
131
+ Attribute attr = new BasicAttribute("objectClass");
132
+ attr.add("top");
133
+ attr.add("domain");
134
+ attrs.put(attr);
135
+
136
+ attr = new BasicAttribute("dc");
137
+ attr.add(domainComponent.split(",")[0].substring(3));
138
+ attrs.put(attr);
139
+
140
+ pCfg.setContextEntry(attrs);
141
+ }
142
+
143
+ return pCfg;
144
+ }
145
+
146
+ @SuppressWarnings({ "unchecked" })
147
+ private Hashtable<String, String> createJndiEnvironment(Configuration cfg) {
148
+ Hashtable<String, String> env = baseEnvironment();
149
+ env.putAll(cfg.toJndiEnvironment());
150
+ return env;
151
+ }
152
+
153
+ public void stop() {
154
+ if (!running) return;
155
+ try {
156
+ new InitialDirContext(createJndiEnvironment(new ShutdownConfiguration()));
157
+ } catch (NamingException e) {
158
+ throw new LadleFatalException("Shutdown failed", e);
159
+ }
160
+ running = false;
161
+
162
+ if (tempDir.exists()) {
163
+ try {
164
+ FileUtils.deleteDirectory(tempDir);
165
+ } catch (IOException e) {
166
+ log.error("Deleting the temporary directory " + tempDir + " failed", e);
167
+ }
168
+ }
169
+ }
170
+ }
@@ -0,0 +1,61 @@
1
+ require 'ladle'
2
+
3
+ require 'java'
4
+
5
+ module Ladle
6
+ ##
7
+ # Implementations of platform-specific behaviors for JRuby.
8
+ #
9
+ # This separate strategy is necessary because you can't
10
+ # `Process.waitpid2` on the PID returned by JRuby's `IO.popen4`.
11
+ class JRubyProcess
12
+ ##
13
+ # Create a new process for the given command and its args.
14
+ def initialize(*command_and_args)
15
+ @command_and_args = command_and_args
16
+ end
17
+
18
+ ##
19
+ # Start the process and return pipes to its standard streams.
20
+ #
21
+ # @return [[IO, IO, IO]] stdin, stdout, and stderr for the running process.
22
+ def popen
23
+ # You can't wait for the PID returned by JRuby's IO.popen4, so
24
+ # this is necessary.
25
+ cmd = @command_and_args.collect(&:to_s).to_java(:string)
26
+ @process = Java::JavaLang::ProcessBuilder.new(
27
+ cmd
28
+ ).start
29
+
30
+ [
31
+ # java.util.Process flips the meanings of "in" and "out"
32
+ # relative to popen3
33
+ @process.output_stream.to_io,
34
+ @process.input_stream.to_io,
35
+ @process.error_stream.to_io
36
+ ]
37
+ end
38
+
39
+ ##
40
+ # Wait for the process to finish.
41
+ #
42
+ # @return [Fixnum] the return status of the process.
43
+ def wait
44
+ @process.waitFor
45
+ end
46
+
47
+ ##
48
+ # Send signal 15 to the process.
49
+ #
50
+ # @return [void]
51
+ def stop_gracefully
52
+ Process.kill 15, pid
53
+ end
54
+
55
+ ##
56
+ # @return [Fixnum] the PID for the process
57
+ def pid
58
+ @pid ||= Java::OrgJrubyUtil::ShellLauncher.getPidFromProcess(@process)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,46 @@
1
+ require 'ladle'
2
+
3
+ require 'open4'
4
+
5
+ module Ladle
6
+ ##
7
+ # Implementations of platform-specific process handling behaviors for Ruby.
8
+ class RubyProcess
9
+ ##
10
+ # Create a new process for the given command and its args.
11
+ def initialize(*command_and_args)
12
+ @command_and_args = command_and_args
13
+ end
14
+
15
+ ##
16
+ # Start the process and return pipes to its standard streams.
17
+ #
18
+ # @return [[IO, IO, IO]] stdin, stdout, and stderr for the running process.
19
+ def popen
20
+ @pid, i, o, e = Open4.open4(@command_and_args.join(' '))
21
+ [i, o, e]
22
+ end
23
+
24
+ ##
25
+ # Wait for the process to finish.
26
+ #
27
+ # @return [Fixnum] the return status of the process.
28
+ def wait
29
+ Process.waitpid2(@pid)[1]
30
+ end
31
+
32
+ ##
33
+ # Send signal 15 to the process.
34
+ #
35
+ # @return [void]
36
+ def stop_gracefully
37
+ Process.kill 15, pid
38
+ end
39
+
40
+ ##
41
+ # @return [Fixnum] the PID for the process
42
+ def pid
43
+ @pid
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,334 @@
1
+ require 'ladle'
2
+
3
+ module Ladle
4
+ ##
5
+ # Controller for Ladle's core feature, the embedded LDAP server.
6
+ class Server
7
+ ##
8
+ # The port from which this server will be available.
9
+ # @return [Fixnum]
10
+ attr_reader :port
11
+
12
+ ##
13
+ # The domain for the served data.
14
+ # @return [String]
15
+ attr_reader :domain
16
+
17
+ ##
18
+ # The filename of the LDIF data loaded into this server before it
19
+ # started.
20
+ # @return [String]
21
+ attr_reader :ldif
22
+
23
+ ##
24
+ # The time to wait for the server to start up before giving up
25
+ # (seconds).
26
+ # @return [Fixnum]
27
+ attr_reader :timeout
28
+
29
+ ##
30
+ # The base directory into which the server should write its
31
+ # temporary files. Ladle will create a directory under this path
32
+ # on startup and remove it on shutdown.
33
+ # @return [String]
34
+ attr_reader :tmpdir
35
+
36
+ ##
37
+ # The java executable to use to run the embedded server.
38
+ # @return [String]
39
+ attr_reader :java_bin
40
+
41
+ ##
42
+ # @param [Hash] opts the options for the server
43
+ # @option opts [Fixnum] :port (3897) The port to serve from.
44
+ # @option opts [String] :ldif ({path to the gem}/lib/ladle/default.ldif)
45
+ # The filename of the LDIF-formatted data to use for this
46
+ # server. If provide your own data, be sure to set the
47
+ # :domain option to match.
48
+ # @option opts [String] :domain ("dc=example,dc=org") the domain
49
+ # for the data provided in the :ldif option.
50
+ # @option opts [Boolean] :verbose (false) if true, detailed
51
+ # information about the execution of the server will be printed
52
+ # to standard error.
53
+ # @option opts [Boolean] :quiet (false) if true _no_ information
54
+ # about regular execution will be printed. Error information
55
+ # will still be printed. This trumps `:verbose`.
56
+ # @option opts [Fixnum] :timeout (15) the amount of time to wait
57
+ # (seconds) for the server process to start before giving up.
58
+ # @option opts [String] :tmpdir (ENV['TMPDIR'] or ENV['TEMPDIR'])
59
+ # the temporary directory to use for the server's files. If not
60
+ # guessable from the environment, it must be specified. It must
61
+ # already exist.
62
+ # @option opts [String] :java_bin ("java" or
63
+ # File.join(ENV["JAVA_HOME"], "bin", "java")) the java
64
+ # executable to use to run the embedded server.
65
+ def initialize(opts={})
66
+ @port = opts[:port] || 3897
67
+ @domain = opts[:domain] || "dc=example,dc=org"
68
+ @ldif = opts[:ldif] || File.expand_path("../default.ldif", __FILE__)
69
+ @quiet = opts[:quiet]
70
+ @verbose = opts[:verbose]
71
+ @timeout = opts[:timeout] || 15
72
+ @tmpdir = opts[:tmpdir] || ENV['TMPDIR'] || ENV['TEMPDIR']
73
+ @java_bin = opts[:java_bin] ||
74
+ (ENV['JAVA_HOME'] ? File.join(ENV['JAVA_HOME'], "bin", "java") : "java")
75
+
76
+ # Additional arguments that can be passed to the java server
77
+ # process. Used for testing only, so not documented.
78
+ @additional_args = opts[:more_args] || []
79
+
80
+ unless @domain =~ /^dc=/
81
+ raise "The domain component must start with 'dc='. '#{@domain}' does not."
82
+ end
83
+
84
+ if tmpdir.nil?
85
+ raise "Cannot guess tmpdir from the environment. Please specify it."
86
+ elsif !File.directory?(tmpdir)
87
+ raise "Tmpdir #{tmpdir.inspect} does not exist."
88
+ end
89
+
90
+ unless File.readable?(@ldif)
91
+ raise "Cannot read specified LDIF file #{@ldif}."
92
+ end
93
+ end
94
+
95
+ ##
96
+ # Starts up the server in a separate process. This method will
97
+ # not return until the server is listening on the specified port.
98
+ # The same {Server} instance can be started and stopped multiple
99
+ # times, but the runs will be independent.
100
+ #
101
+ # @return [Server] this instance
102
+ def start
103
+ return if @running
104
+ log "Starting server on #{port}"
105
+ trace "- Server command: #{server_cmd.inspect}"
106
+ java_in, java_out, java_err = create_process(*server_cmd).popen
107
+ @running = true
108
+ trace "- Started subprocess #{process.pid}"
109
+
110
+ @log_watcher = LogStreamWatcher.new(java_err, self)
111
+ @log_watcher.start
112
+ @controller = ApacheDSController.new(java_in, java_out, self)
113
+ @controller.start
114
+
115
+ # TODO: perhaps this busywait can be replaced with select?
116
+ trace "- Waiting for server to start"
117
+ started_waiting = Time.now
118
+ until @controller.started? || @controller.error? || Time.now > started_waiting + timeout
119
+ trace " . waited #{Time.now - started_waiting} seconds"
120
+ sleep 0.5
121
+ end
122
+ trace "- Stopped waiting after #{Time.now - started_waiting} seconds"
123
+
124
+ if @controller.error?
125
+ self.stop
126
+ trace "! Subprocess error (see above)"
127
+ raise "LDAP server failed to start"
128
+ elsif !@controller.started?
129
+ self.stop
130
+ trace "! Timed out"
131
+ raise "LDAP server startup did not complete within #{timeout} seconds"
132
+ end
133
+
134
+ trace "- Server started successfully"
135
+ at_exit { stop }
136
+
137
+ self
138
+ end
139
+
140
+ ##
141
+ # Stops the server that was started with {#start}.
142
+ def stop
143
+ return if !@running
144
+ log "Stopping server on #{port}"
145
+ trace "- Stopping server process"
146
+ @controller.stop if @controller
147
+
148
+ trace "- Signalling server process to stop if not already stopped"
149
+ process.stop_gracefully
150
+ process.wait
151
+
152
+ @running = false
153
+ end
154
+
155
+ ##
156
+ # Visible for collaborators.
157
+ # @private
158
+ def log_error(msg)
159
+ $stderr.puts(msg)
160
+ end
161
+
162
+ ##
163
+ # Visible for collaborators.
164
+ # @private
165
+ def log(msg)
166
+ $stderr.puts(msg) unless quiet?
167
+ end
168
+
169
+ ##
170
+ # Visible for collaborators.
171
+ # @private
172
+ def trace(msg)
173
+ $stderr.puts(msg) if verbose? && !quiet?
174
+ end
175
+
176
+ ##
177
+ # If the controller will print anything about what it is doing to
178
+ # stderr. If this is true, all non-error output will be
179
+ # supressed. This value trumps {#verbose?}.
180
+ #
181
+ # @return [Boolean]
182
+ def quiet?
183
+ @quiet
184
+ end
185
+
186
+ ##
187
+ # Whether the controller will print detailed information about
188
+ # what it is doing to stderr. This includes information from the
189
+ # embedded ApacheDS instance.
190
+ #
191
+ # @return [Boolean]
192
+ def verbose?
193
+ @verbose
194
+ end
195
+
196
+ private
197
+
198
+ def create_process(*cmd)
199
+ @process =
200
+ if RUBY_PLATFORM == 'java'
201
+ JRubyProcess.new(*cmd)
202
+ else
203
+ RubyProcess.new(*cmd)
204
+ end
205
+ end
206
+
207
+ def process
208
+ @process
209
+ end
210
+
211
+ def server_cmd
212
+ [
213
+ java_bin,
214
+ "-cp", classpath,
215
+ "net.detailedbalance.ladle.Main",
216
+ "--port", port,
217
+ "--domain", domain,
218
+ "--ldif", ldif,
219
+ "--tmpdir", tmpdir
220
+ ] + @additional_args
221
+ end
222
+
223
+ def classpath
224
+ (
225
+ # ApacheDS
226
+ Dir[File.expand_path("../apacheds/*.jar", __FILE__)] +
227
+ # Wrapper code
228
+ [File.expand_path("../java", __FILE__)]
229
+ ).join(':')
230
+ end
231
+
232
+ ##
233
+ # Encapsulates communication with the child ApacheDS process.
234
+ class ApacheDSController
235
+ def initialize(ds_in, ds_out, server)
236
+ @ds_in = ds_in
237
+ @ds_out = ds_out
238
+ @server = server
239
+ end
240
+
241
+ def start
242
+ Thread.new(self) do |controller|
243
+ controller.watch
244
+ end
245
+ end
246
+
247
+ def watch
248
+ while (line = @ds_out.readline) && !error?
249
+ case line
250
+ when /^STARTED/
251
+ @started = true
252
+ when /^FATAL/
253
+ report_error(line)
254
+ when /^STOPPED/
255
+ @started = false
256
+ else
257
+ report_error("Unexpected server process output: #{line}")
258
+ end
259
+ end
260
+ end
261
+
262
+ def started?
263
+ @started
264
+ end
265
+
266
+ def error?
267
+ @error
268
+ end
269
+
270
+ def stop
271
+ unless @ds_in.closed?
272
+ @ds_in.puts("STOP")
273
+ @ds_in.flush
274
+ @ds_in.close
275
+ end
276
+ end
277
+
278
+ private
279
+
280
+ def report_error(msg)
281
+ @error = true
282
+ @server.log_error "ApacheDS process failed: #{msg}"
283
+ self.stop
284
+ end
285
+ end
286
+
287
+ class LogStreamWatcher
288
+ def initialize(ds_err, server)
289
+ @ds_err = ds_err
290
+ @server = server
291
+ end
292
+
293
+ def start
294
+ Thread.new(self) do |watcher|
295
+ watcher.watch
296
+ end
297
+ end
298
+
299
+ def watch
300
+ begin
301
+ while !@ds_err.closed? && (line = @ds_err.readline)
302
+ if is_error?(line)
303
+ @server.log_error("ApacheDS: #{line}")
304
+ else
305
+ @server.trace("ApacheDS: #{line}")
306
+ end
307
+ end
308
+ rescue EOFError
309
+ # stop naturally
310
+ end
311
+ end
312
+
313
+ private
314
+
315
+ def is_error?(line)
316
+ kind = (line =~ /^([A-Z]+):/) ? $1 : nil
317
+ (kind.nil? || %w(ERROR WARN).include?(kind)) && !bogus?(line)
318
+ end
319
+
320
+ ##
321
+ # Indicates whether the "error" or "warning" emitted from
322
+ # ApacheDS is actually an error or warning.
323
+ def bogus?(line)
324
+ [
325
+ %r{shutdown hook has NOT been registered},
326
+ %r{attributeType w/ OID 2.5.4.16 not registered},
327
+ %r{default.*?cache size},
328
+ %r{change the admin password},
329
+ %r{Attribute \S+ does not have normalizer}
330
+ ].detect { |re| line =~ re }
331
+ end
332
+ end
333
+ end
334
+ end