mirror of https://github.com/sbt/sbt.git
Updated Client server discovery lifecycle (markdown)
parent
187398e978
commit
b3e4fbf1d7
|
|
@ -80,7 +80,7 @@ Here's a more concrete proposal of how launching a server would work. However,
|
||||||
|
|
||||||
1. the sbt launcher's locking mechanism is sufficient for us to find a means to avoid double-server-startup.
|
1. the sbt launcher's locking mechanism is sufficient for us to find a means to avoid double-server-startup.
|
||||||
2. We want clients to have *one* means of attempting to fork the sbt server process. This is assumed to
|
2. We want clients to have *one* means of attempting to fork the sbt server process. This is assumed to
|
||||||
be the sbt launcher.
|
be a new "service locator" feature of the sbt-launcher.
|
||||||
3. Upon network failure, or inability to communicate to the server, clients will attempt to spawn another
|
3. Upon network failure, or inability to communicate to the server, clients will attempt to spawn another
|
||||||
server. This will only succeed if the server is actually down. Clients are also responsible for
|
server. This will only succeed if the server is actually down. Clients are also responsible for
|
||||||
restoring any state they need on the server upon reconnect.
|
restoring any state they need on the server upon reconnect.
|
||||||
|
|
@ -88,31 +88,104 @@ Here's a more concrete proposal of how launching a server would work. However,
|
||||||
no more clients.
|
no more clients.
|
||||||
|
|
||||||
|
|
||||||
*TODO - more assumptions?*
|
So, first We create a new interface for the sbt launcher, called `xsbt.ServerMain`:
|
||||||
|
|
||||||
We create a new interface for the sbt launcher, called `xsbt.ServerMain`:
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
package xsbti;
|
package xsbti;
|
||||||
|
|
||||||
public interface ServerMain {
|
public interface ServerMain {
|
||||||
public java.net.URI[] start(AppConfiguration configuration);
|
public java.net.URI start(AppConfiguration configuration);
|
||||||
public boolean isAlive(java.net.URI[] listenPorts);
|
public boolean isAlive(java.net.URI active);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The interface has two methods. One responsible for starting the Server, and another which can be used to check whether or not another server is alive, based on a given set of server port connections.
|
||||||
|
|
||||||
|
The sbt launcher can now also support launching servers, via the following pseudocode:
|
||||||
|
|
||||||
|
```scala
|
||||||
|
def launchServer(config: ServerConfiguration): Unit = {
|
||||||
|
val oldOut = System.out
|
||||||
|
System.setErr(getErr(config))
|
||||||
|
System.setOut(getOut(config))
|
||||||
|
val uri = runServer(config)
|
||||||
|
oldOut.println(uri)
|
||||||
|
// Let this thread die, server thread will continue running.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is done via a new configuration similar to AppConfiguration, only with an additional values. E.g. here's a possibly launch configuration for the sbt server:
|
||||||
|
|
||||||
|
```
|
||||||
|
[scala]
|
||||||
|
version: auto
|
||||||
|
|
||||||
|
[server]
|
||||||
|
org: org.scala-sbt
|
||||||
|
name: sbt
|
||||||
|
version: ${sbt.version-read(sbt.version)[${{sbt.version}}]}
|
||||||
|
class: sbt.xServer
|
||||||
|
components: xsbti,extra
|
||||||
|
cross-versioned: false
|
||||||
|
resources: ${sbt.extraClasspath-}
|
||||||
|
lock: .active.properties
|
||||||
|
|
||||||
|
[repositories]
|
||||||
|
local
|
||||||
|
maven-central
|
||||||
|
|
||||||
|
[boot]
|
||||||
|
directory: ${sbt.boot.directory-${sbt.global.base-${user.home}/.sbt}/boot/}
|
||||||
|
|
||||||
|
[ivy]
|
||||||
|
ivy-home: ${sbt.ivy.home-${user.home}/.ivy2/}
|
||||||
|
checksums: ${sbt.checksums-sha1,md5}
|
||||||
|
override-build-repos: ${sbt.override.build.repos-false}
|
||||||
|
repository-config: ${sbt.repository.config-${sbt.global.base-${user.home}/.sbt}/repositories}
|
||||||
|
```
|
||||||
|
|
||||||
|
The only new features in the above launch configuration are:
|
||||||
|
|
||||||
|
1. using [server] rather than [app] to inform the launcher which interface to expect.
|
||||||
|
2. An additional `lock` attribute that specifies where the service-locator can find this servers lock file.
|
||||||
|
|
||||||
|
When launching a server, you'll note that the lock attribute is not inspected. Instead, the sbt-launcher will also support a "service locator" functionality where this lock will be critical.
|
||||||
|
|
||||||
|
The Service locator will be executed by clients via the command line:
|
||||||
|
|
||||||
|
`java -jar sbt-launch.jar locate:MyServerBootConfigurationFile`
|
||||||
|
|
||||||
|
This will return the URI of an active launched server. It will use the below pseudo-code as a possible implementation:
|
||||||
|
|
||||||
|
```scala
|
||||||
|
def serviceLocator(config: ServerConfiguration, launcher: Launcher): URI = {
|
||||||
|
def isReachable(info: URI): Boolean =
|
||||||
|
launcher.server(config).isAlive(info)
|
||||||
|
|
||||||
|
withLockFile(config.lockFile) {
|
||||||
|
readServerInfo(config.lockFile).filter(isReachable) match {
|
||||||
|
case Some(info) => info
|
||||||
|
case None =>
|
||||||
|
val uri = startServer(config)
|
||||||
|
writeServerInfo(config.lockFile, uri)
|
||||||
|
uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def startServer(config: ServerConfiguration): URI = {
|
||||||
|
val bootProps = makeTemporaryBootProps(config)
|
||||||
|
val launcherJar = lookUpFromMyself()
|
||||||
|
val process = s"java -jar ${launcherJar} @${bootprops}".in(cwd).run
|
||||||
|
val uri = fromStdOut(process)
|
||||||
|
if(process isn't running) fail("Couldn't start server")
|
||||||
|
uri
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The interface has two methods. One responsible for starting the Server, and another which can be used to check whether or not another server is alive, based on a given set of server port connections. The sbt launcher is responsible for:
|
|
||||||
|
|
||||||
1. Obtaining the lock file for the CWD + AppConfiguratoin-hash pairing. (That is there can only be one server of a given configuration (organization + module id) in any working directory).
|
|
||||||
2. Reading the ServerPorts from the active configuration.
|
|
||||||
3. Instantiating an instance of the ServerMain class denoted in the configuration.
|
|
||||||
3. Checking to see if the old server is alive using the ServerMain instance. If so, report these ports to the client (TODO - how?)
|
|
||||||
4. If the old server was not alive, start up a new server using the ServerMain instance. Write the new server bindings to the lock file and release the lock.
|
|
||||||
5. Notify the client (via stdout) what the bound ports are.
|
|
||||||
|
|
||||||
Port bindings come in java.net.URI, which includes an address, a protocol and a port. The server will actually also write down the PID (process ID) of the server (assuming it can obtain the pid). This enables clients to "kill rogue sbt server" directly.
|
|
||||||
|
|
||||||
Clients should be able to be notified of all open ServerPort bindings created by a server.
|
|
||||||
|
|
||||||
## Sample code
|
## Sample code
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue