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