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.
- data/ASL20-LICENSE +343 -0
- data/CHANGELOG.md +4 -0
- data/LICENSE +20 -0
- data/NOTICE +52 -0
- data/README.md +121 -0
- data/lib/ladle.rb +13 -0
- data/lib/ladle/Ladle.iml +13 -0
- data/lib/ladle/apacheds/antlr-2.7.6.jar +0 -0
- data/lib/ladle/apacheds/apacheds-core-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-core-shared-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-kerberos-shared-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-protocol-changepw-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-protocol-kerberos-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-protocol-ldap-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-protocol-ntp-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-protocol-shared-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-server-jndi-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-server-main-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/apacheds-server-ssl-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/backport-util-concurrent-2.2.jar +0 -0
- data/lib/ladle/apacheds/commons-cli-1.0.jar +0 -0
- data/lib/ladle/apacheds/commons-collections-3.2.jar +0 -0
- data/lib/ladle/apacheds/commons-io-1.4.jar +0 -0
- data/lib/ladle/apacheds/commons-lang-2.1.jar +0 -0
- data/lib/ladle/apacheds/jcl-over-slf4j-1.5.6.jar +0 -0
- data/lib/ladle/apacheds/jdbm-1.0.jar +0 -0
- data/lib/ladle/apacheds/log4j-1.2.14.jar +0 -0
- data/lib/ladle/apacheds/mina-core-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/mina-filter-ssl-1.0.2.jar +0 -0
- data/lib/ladle/apacheds/shared-asn1-0.9.5.5.jar +0 -0
- data/lib/ladle/apacheds/shared-asn1-codec-0.9.5.5.jar +0 -0
- data/lib/ladle/apacheds/shared-ldap-0.9.5.5.jar +0 -0
- data/lib/ladle/apacheds/slf4j-api-1.5.6.jar +0 -0
- data/lib/ladle/apacheds/slf4j-log4j12-1.5.6.jar +0 -0
- data/lib/ladle/apacheds/spring-beans-1.2.8.jar +0 -0
- data/lib/ladle/apacheds/spring-context-1.2.8.jar +0 -0
- data/lib/ladle/apacheds/spring-core-1.2.8.jar +0 -0
- data/lib/ladle/apacheds/xercesImpl-2.0.2.jar +0 -0
- data/lib/ladle/default.ldif +292 -0
- data/lib/ladle/java/net/detailedbalance/ladle/LadleFatalException.class +0 -0
- data/lib/ladle/java/net/detailedbalance/ladle/LadleFatalException.java +14 -0
- data/lib/ladle/java/net/detailedbalance/ladle/Main$1.class +0 -0
- data/lib/ladle/java/net/detailedbalance/ladle/Main.class +0 -0
- data/lib/ladle/java/net/detailedbalance/ladle/Main.java +141 -0
- data/lib/ladle/java/net/detailedbalance/ladle/Server.class +0 -0
- data/lib/ladle/java/net/detailedbalance/ladle/Server.java +170 -0
- data/lib/ladle/jruby_process.rb +61 -0
- data/lib/ladle/ruby_process.rb +46 -0
- data/lib/ladle/server.rb +334 -0
- data/lib/ladle/version.rb +5 -0
- data/spec/ladle/animals.ldif +28 -0
- data/spec/ladle/server_spec.rb +306 -0
- data/spec/ladle/version_spec.rb +11 -0
- data/spec/spec_helper.rb +32 -0
- metadata +172 -0
Binary file
|
@@ -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
|
data/lib/ladle/server.rb
ADDED
@@ -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
|