ladle 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
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