b8da9bc651
R=brianwilkerson@google.com BUG= Review URL: https://codereview.chromium.org//725143004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@41792 260f80e4-7a28-3924-810f-c04153c831b5
137 lines
4.3 KiB
Dart
137 lines
4.3 KiB
Dart
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:analysis_server/src/channel/channel.dart';
|
|
import 'package:analysis_server/src/channel/web_socket_channel.dart';
|
|
import 'package:analysis_server/src/protocol.dart';
|
|
|
|
/**
|
|
* [AnalysisManager] is used to launch and manage an analysis server
|
|
* running in a separate process using either the [start] or [connect] methods.
|
|
*/
|
|
class AnalysisManager {
|
|
// TODO dynamically allocate port and/or allow client to specify port
|
|
static const int PORT = 3333;
|
|
|
|
/**
|
|
* The analysis server process being managed
|
|
* or `null` if managing an analysis server that was already running.
|
|
*/
|
|
Process process;
|
|
|
|
/**
|
|
* The channel used to communicate with the analysis server.
|
|
*/
|
|
ClientCommunicationChannel channel;
|
|
|
|
/**
|
|
* Stop the analysis server.
|
|
*
|
|
* Returns `true` if the signal is successfully sent and process terminates.
|
|
* Otherwise there was no attached process or the signal could not be sent,
|
|
* usually meaning that the process is already dead.
|
|
*/
|
|
Future<bool> stop() {
|
|
if (process == null) {
|
|
return channel.close().then((_) => false);
|
|
}
|
|
return channel.sendRequest(
|
|
new ServerShutdownParams().toRequest(
|
|
'0')).timeout(new Duration(seconds: 2), onTimeout: () {
|
|
print('Expected shutdown response');
|
|
}).then((Response response) {
|
|
return channel.close().then((_) => process.exitCode);
|
|
}).timeout(new Duration(seconds: 2), onTimeout: () {
|
|
print('Expected server to shutdown');
|
|
process.kill();
|
|
}).then((int result) {
|
|
if (result != null && result != 0) {
|
|
exitCode = result;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Launch an analysis server and open a connection to that server.
|
|
*/
|
|
Future<AnalysisManager> _launchServer(String pathToServer) {
|
|
// TODO dynamically allocate port and/or allow client to specify port
|
|
List<String> serverArgs = [pathToServer, '--port', PORT.toString()];
|
|
return Process.start(Platform.executable, serverArgs).catchError((error) {
|
|
exitCode = 1;
|
|
throw 'Failed to launch analysis server: $error';
|
|
}).then(_listenForPort);
|
|
}
|
|
|
|
/**
|
|
* Listen for a port from the given analysis server process.
|
|
*/
|
|
Future<AnalysisManager> _listenForPort(Process process) {
|
|
this.process = process;
|
|
|
|
// Echo stdout and stderr
|
|
Stream out = process.stdout.transform(UTF8.decoder).asBroadcastStream();
|
|
out.listen((line) => print(line));
|
|
process.stderr.pipe(stderr);
|
|
|
|
// Listen for port from server
|
|
const String pattern = 'Listening on port ';
|
|
return out.firstWhere(
|
|
(String line) =>
|
|
line.startsWith(
|
|
pattern)).timeout(new Duration(seconds: 10)).catchError((error) {
|
|
exitCode = 1;
|
|
process.kill();
|
|
throw 'Expected port from analysis server';
|
|
}).then((String line) {
|
|
String port = line.substring(pattern.length).trim();
|
|
String url = 'ws://${InternetAddress.LOOPBACK_IP_V4.address}:$port/';
|
|
return _openConnection(url);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Open a connection to the analysis server using the given URL.
|
|
*/
|
|
Future<AnalysisManager> _openConnection(String serverUrl) {
|
|
Function onError = (error) {
|
|
exitCode = 1;
|
|
if (process != null) {
|
|
process.kill();
|
|
}
|
|
throw 'Failed to connect to analysis server at $serverUrl\n $error';
|
|
};
|
|
try {
|
|
return WebSocket.connect(
|
|
serverUrl).catchError(onError).then((WebSocket socket) {
|
|
this.channel = new WebSocketClientChannel(socket);
|
|
return this;
|
|
});
|
|
} catch (error) {
|
|
onError(error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Open a connection to a running analysis server
|
|
* and return a future with a manager for that analysis server.
|
|
*/
|
|
static Future<AnalysisManager> connect(String serverUrl) {
|
|
return new AnalysisManager()._openConnection(serverUrl);
|
|
}
|
|
|
|
/**
|
|
* Launch analysis server in a separate process
|
|
* and return a future with a manager for that analysis server.
|
|
*/
|
|
static Future<AnalysisManager> start(String serverPath) {
|
|
return new AnalysisManager()._launchServer(serverPath);
|
|
}
|
|
}
|