|
1 | 1 | package bloop |
2 | 2 |
|
3 | | -import java.net.InetAddress |
| 3 | +sealed abstract class Server |
4 | 4 |
|
5 | | -import bloop.logging.BloopLogger |
6 | | -import bloop.logging.Logger |
7 | | -import bloop.util.ProxySetup |
8 | | - |
9 | | -import java.io.InputStream |
10 | | -import java.io.PrintStream |
11 | | -import java.nio.channels.ReadableByteChannel |
12 | | -import java.nio.file.Files |
13 | | -import java.nio.file.Paths |
14 | | -import java.util.concurrent.atomic.AtomicBoolean |
15 | | - |
16 | | -import com.martiansoftware.nailgun.NGListeningAddress |
17 | | -import com.martiansoftware.nailgun.NGConstants |
18 | | -import com.martiansoftware.nailgun.{Alias, NGContext, NGServer} |
19 | | -import libdaemonjvm._ |
20 | | -import libdaemonjvm.internal.{LockProcess, SocketHandler} |
21 | | -import libdaemonjvm.server._ |
22 | | - |
23 | | -import scala.util.Properties |
24 | | -import scala.util.Try |
25 | | -import java.net.ServerSocket |
26 | | -import java.net.Socket |
27 | | -import java.io.OutputStream |
28 | | -import java.net.SocketAddress |
29 | | -import java.nio.channels.Channels |
30 | | -import libdaemonjvm.internal.SocketMaker |
31 | | -import java.nio.ByteBuffer |
32 | | -import java.io.File |
33 | | -import org.slf4j |
34 | | - |
35 | | -class Server |
36 | 5 | object Server { |
37 | | - private val defaultPort: Int = 8212 // 8100 + 'p' |
38 | | - def main(args: Array[String]): Unit = { |
39 | | - def toPortNumber(userPort: String) = Try(userPort.toInt).getOrElse(Server.defaultPort) |
40 | | - val lockFilesOrHostPort = args match { |
41 | | - case Array() => |
42 | | - val lockFiles = LockFiles.under( |
43 | | - bloop.io.Paths.daemonDir.underlying, |
44 | | - bloop.io.Paths.pipeName |
45 | | - ) |
46 | | - Right(lockFiles) |
47 | | - case Array(daemonArg) if daemonArg.startsWith("daemon:") => |
48 | | - val arg = daemonArg.stripPrefix("daemon:") |
49 | | - val (dirStr, pipeName) = arg.split(File.pathSeparator, 2) match { |
50 | | - case Array(dir, pipeName) => (dir, pipeName) |
51 | | - case Array(dir) => (dir, bloop.io.Paths.pipeName) |
52 | | - } |
53 | | - val dir = Paths.get(dirStr) |
54 | | - val lockFiles = LockFiles.under(dir, pipeName) |
55 | | - Right(lockFiles) |
56 | | - case Array(arg) => |
57 | | - Left((InetAddress.getLoopbackAddress(), toPortNumber(args(0)))) |
58 | | - case Array(host, portStr) => |
59 | | - val addr = InetAddress.getByName(host) |
60 | | - Left((addr, toPortNumber(portStr))) |
61 | | - case _ => |
62 | | - throw new IllegalArgumentException( |
63 | | - s"Invalid arguments to bloop server: $args, expected: ([address] [port] | [daemon:path] [pipeName])" |
64 | | - ) |
65 | | - } |
66 | | - |
67 | | - lockFilesOrHostPort match { |
68 | | - case Left(hostPort) => |
69 | | - startServer(Left(hostPort)) |
70 | | - case Right(lockFiles) => |
71 | | - Lock.tryAcquire(lockFiles, LockProcess.default) { |
72 | | - startServer(Right(lockFiles.socketPaths)) |
73 | | - } match { |
74 | | - case Left(err) => throw new Exception(err) |
75 | | - case Right(()) => |
76 | | - } |
77 | | - } |
78 | | - } |
79 | | - |
80 | | - def startServer(socketPathsOrHostPort: Either[(InetAddress, Int), SocketPaths]): Unit = { |
81 | | - val socketAndPathOrHostPort = socketPathsOrHostPort.map { socketPaths => |
82 | | - val socket = SocketHandler.server(socketPaths) match { |
83 | | - case Left(socket) => socket |
84 | | - case Right(channel) => libdaemonjvm.Util.serverSocketFromChannel(channel) |
85 | | - } |
86 | | - val (socketPathStr, socketPathOpt) = |
87 | | - if (SocketHandler.usesWindowsPipe) (socketPaths.windowsPipeName, None) |
88 | | - else (socketPaths.path.toString, Some(socketPaths.path)) |
89 | | - (socket, socketPathStr, socketPathOpt) |
90 | | - } |
91 | | - val server = instantiateServer(socketAndPathOrHostPort.map { |
92 | | - case (sock, path, _) => (sock, path) |
93 | | - }) |
94 | | - val runServer: Runnable = () => |
95 | | - try server.run() |
96 | | - finally { |
97 | | - for (path <- socketAndPathOrHostPort.toOption.flatMap(_._3)) |
98 | | - Files.deleteIfExists(path) |
99 | | - } |
100 | | - // FIXME Small delay between the time this method returns, and the time we actually |
101 | | - // accept connections on the socket. This might make concurrent attempts to start a server |
102 | | - // think we are a zombie server, and attempt to listen on the socket too. |
103 | | - new Thread(runServer, "bloop-server").start() |
104 | | - } |
105 | | - |
106 | | - private[bloop] def instantiateServer( |
107 | | - socketAndPathOrHostPort: Either[(InetAddress, Int), (ServerSocket, String)] |
108 | | - ): NGServer = { |
109 | | - val logger = BloopLogger.default("bloop-nailgun-main") |
110 | | - socketAndPathOrHostPort match { |
111 | | - case Left((addr, port)) => |
112 | | - val tcpAddress = new NGListeningAddress(addr, port) |
113 | | - launchServer(System.in, System.out, System.err, tcpAddress, logger, None) |
114 | | - case Right((socket, socketPath)) => |
115 | | - val socketAddress = new NGListeningAddress(socketPath) |
116 | | - launchServer(System.in, System.out, System.err, socketAddress, logger, Some(socket)) |
117 | | - } |
118 | | - } |
119 | | - |
120 | | - private[bloop] def launchServer( |
121 | | - in: InputStream, |
122 | | - out: PrintStream, |
123 | | - err: PrintStream, |
124 | | - address: NGListeningAddress, |
125 | | - logger: Logger, |
126 | | - serverSocketOpt: Option[ServerSocket] |
127 | | - ): NGServer = { |
128 | | - val javaLogger = slf4j.LoggerFactory.getLogger(classOf[NGServer]) |
129 | | - val poolSize = NGServer.DEFAULT_SESSIONPOOLSIZE |
130 | | - val heartbeatMs = NGConstants.HEARTBEAT_TIMEOUT_MILLIS.toInt |
131 | | - |
132 | | - val domainSocketProvider: NGServer.DomainSocketProvider = { () => |
133 | | - serverSocketOpt.getOrElse( |
134 | | - sys.error("Shouldn't be called") |
135 | | - ) |
136 | | - } |
137 | | - |
138 | | - val server = |
139 | | - new NGServer(address, poolSize, heartbeatMs, in, out, err, javaLogger, domainSocketProvider) |
140 | | - registerAliases(server) |
141 | | - ProxySetup.init() |
142 | | - server |
143 | | - } |
144 | | - |
145 | | - def nailMain(ngContext: NGContext): Unit = { |
146 | | - val server = ngContext.getNGServer |
147 | | - |
148 | | - val soft = ngContext.getArgs.contains("--soft") |
149 | | - |
150 | | - // Passing true by default to force exiting the JVM (System.exit). |
151 | | - // When using JNI/JNA-based domain sockets, it seems the call to accept() |
152 | | - // isn't interrupted when the underlying socket is closed (on Linux at least). |
153 | | - // So we have to force a call to System.exit to actually exit. |
154 | | - server.shutdown(!soft) |
155 | | - } |
156 | | - |
157 | | - private def registerAliases(server: NGServer): Unit = { |
158 | | - val aliasManager = server.getAliasManager |
159 | | - aliasManager.addAlias(new Alias("about", "Show bloop information.", classOf[Cli])) |
160 | | - aliasManager.addAlias(new Alias("clean", "Clean project(s) in the build.", classOf[Cli])) |
161 | | - aliasManager.addAlias(new Alias("compile", "Compile project(s) in the build.", classOf[Cli])) |
162 | | - aliasManager.addAlias(new Alias("test", "Run project(s)' tests in the build.", classOf[Cli])) |
163 | | - aliasManager.addAlias( |
164 | | - new Alias("run", "Run a main entrypoint for project(s) in the build.", classOf[Cli]) |
165 | | - ) |
166 | | - aliasManager.addAlias(new Alias("bsp", "Spawn a build server protocol instance.", classOf[Cli])) |
167 | | - aliasManager.addAlias( |
168 | | - new Alias("console", "Run the console for project(s) in the build.", classOf[Cli]) |
169 | | - ) |
170 | | - aliasManager.addAlias(new Alias("projects", "Show projects in the build.", classOf[Cli])) |
171 | | - aliasManager.addAlias(new Alias("configure", "Configure the bloop server.", classOf[Cli])) |
172 | | - aliasManager.addAlias(new Alias("help", "Show bloop help message.", classOf[Cli])) |
173 | | - aliasManager.addAlias( |
174 | | - new Alias( |
175 | | - "exit", |
176 | | - "Kill the bloop server.", |
177 | | - classOf[Server] |
178 | | - ) |
179 | | - ) |
180 | | - |
181 | | - // Register the default entrypoint in case the user doesn't use the right alias |
182 | | - server.setDefaultNailClass(classOf[Cli]) |
183 | | - // Disable nails by class name so that we prevent classloading incorrect aliases |
184 | | - server.setAllowNailsByClassName(false) |
185 | | - } |
| 6 | + def main(args: Array[String]): Unit = |
| 7 | + Bloop.main(args) |
186 | 8 | } |
0 commit comments