Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Side by Side Diff: pkg/analysis_server/lib/src/analysis_manager.dart

Issue 180253005: Error handling and code cleanup (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: add null check Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:convert'; 6 import 'dart:convert';
7 import 'dart:io'; 7 import 'dart:io';
8 import 'package:analysis_server/src/channel.dart';
9 import 'package:analysis_server/src/domain_server.dart';
10 import 'package:analysis_server/src/protocol.dart';
8 11
9 /** 12 /**
10 * [AnalysisManager] is used to launch and manage an analysis server 13 * [AnalysisManager] is used to launch and manage an analysis server
11 * running in a separate process using the static [start] method. 14 * running in a separate process using either the [start] or [connect] methods.
12 */ 15 */
13 class AnalysisManager { 16 class AnalysisManager {
14 // TODO dynamically allocate port and/or allow client to specify port 17 // TODO dynamically allocate port and/or allow client to specify port
15 static const int PORT = 3333; 18 static const int PORT = 3333;
16 19
17 /** 20 /**
18 * The analysis server process being managed 21 * The analysis server process being managed
19 * or `null` if managing an analysis server that was already running. 22 * or `null` if managing an analysis server that was already running.
20 */ 23 */
21 final Process process; 24 Process process;
22 25
23 /** 26 /**
24 * The websocket used to communicate with the analysis server. 27 * The socket used to communicate with the analysis server.
25 */ 28 */
26 final WebSocket socket; 29 WebSocket socket;
27 30
28 /** 31 /**
29 * Launch analysis server in a separate process and return a 32 * Launch analysis server in a separate process
30 * [Future<AnalysisManager>] for managing that analysis server. 33 * and return a future with a manager for that analysis server.
31 */ 34 */
32 static Future<AnalysisManager> start(String pathToServer) { 35 static Future<AnalysisManager> start(String serverPath) {
36 return new AnalysisManager()._launchServer(serverPath);
37 }
38
39 /**
40 * Open a connection to a running analysis server
41 * and return a future with a manager for that analysis server.
42 */
43 static Future<AnalysisManager> connect(String serverUrl) {
44 return new AnalysisManager()._openConnection(serverUrl);
45 }
46
47 /**
48 * Launch an analysis server and open a connection to that server.
49 */
50 Future<AnalysisManager> _launchServer(String pathToServer) {
33 // TODO dynamically allocate port and/or allow client to specify port 51 // TODO dynamically allocate port and/or allow client to specify port
34 return Process.start(Platform.executable, [pathToServer, "--port", 52 var serverArgs = [pathToServer, '--port', PORT.toString()];
35 PORT.toString()]).then(_attach).catchError((error) { 53 return Process.start(Platform.executable, serverArgs)
36 print("Failed to launch analysis server: $error"); 54 .catchError((error) {
37 exitCode = 1; 55 exitCode = 1;
38 throw error; 56 throw 'Failed to launch analysis server: $error';
57 })
58 .then(_listenForPort);
59 }
60
61 /**
62 * Listen for a port from the given analysis server process.
63 */
64 Future<AnalysisManager> _listenForPort(Process process) {
65 this.process = process;
66
67 // Echo stdout and stderr
68 Stream out = process.stdout.transform(UTF8.decoder).asBroadcastStream();
69 out.listen((line) => print(line));
70 process.stderr.pipe(stderr);
71
72 // Listen for port from server
73 const String pattern = 'Listening on port ';
74 return out.firstWhere((String line) => line.startsWith(pattern))
75 .timeout(new Duration(seconds: 10))
76 .catchError((error) {
77 exitCode = 1;
78 process.kill();
79 throw 'Expected port from analysis server';
80 })
81 .then((String line) {
82 String port = line.substring(pattern.length).trim();
83 var url = 'ws://${InternetAddress.LOOPBACK_IP_V4.address}:$port/';
84 return _openConnection(url);
39 }); 85 });
40 } 86 }
41 87
42 /** 88 /**
43 * Open a connection to a running analysis server 89 * Open a connection to the analysis server using the given URL.
44 */ 90 */
45 static Future<AnalysisManager> connect(String url) { 91 Future<AnalysisManager> _openConnection(String serverUrl) {
46 return WebSocket.connect(url) 92 var onError = (error) {
47 .then((WebSocket socket) => new AnalysisManager(null, socket)); 93 exitCode = 1;
94 if (process != null) {
95 process.kill();
96 }
97 throw 'Failed to connect to analysis server at $serverUrl\n $error';
98 };
99 try {
100 return WebSocket.connect(serverUrl)
101 .catchError(onError)
102 .then((WebSocket socket) {
103 this.socket = socket;
104 return this;
105 });
106 } catch (error) {
107 onError(error);
108 }
48 } 109 }
49 110
50 /** 111 /**
51 * Attach this process to the newly launched analysis server process,
52 * and open a connection to the analysis server.
53 */
54 static Future<AnalysisManager> _attach(Process process) {
55 var url = 'ws://${InternetAddress.LOOPBACK_IP_V4.address}:$PORT/';
56 process.stderr.pipe(stderr);
57 Stream out = process.stdout.transform(UTF8.decoder).asBroadcastStream();
58 out.listen((line) {
59 print(line);
60 });
61 return out
62 .any((String line) => line.startsWith("Listening on port"))
63 .then((bool listening) {
64 if (!listening) {
65 throw "Expected analysis server to listen on a port";
66 }
67 })
68 .then((_) => WebSocket.connect(url))
69 .then((WebSocket socket) => new AnalysisManager(process, socket))
70 .catchError((error) {
71 process.kill();
72 print("Failed to connect to analysis server: $error");
73 exitCode = 1;
74 throw error;
75 });
76 }
77
78 /**
79 * Create a new instance that manages the specified analysis server process.
80 */
81 AnalysisManager(this.process, this.socket);
82
83 /**
84 * Stop the analysis server. 112 * Stop the analysis server.
85 * 113 *
86 * Returns `true` if the signal is successfully sent and process is killed. 114 * Returns `true` if the signal is successfully sent and process terminates.
87 * Otherwise there was no attached process or the signal could not be sent, 115 * Otherwise there was no attached process or the signal could not be sent,
88 * usually meaning that the process is already dead. 116 * usually meaning that the process is already dead.
89 */ 117 */
90 // TODO request analysis server stop 118 Future<bool> stop() {
91 bool stop() => process != null ? process.kill() : false; 119 if (process == null) {
120 return new Future.value(false);
121 }
122 var shutdownRequest = {
123 Request.ID : '0',
124 Request.METHOD : ServerDomainHandler.SHUTDOWN_METHOD };
125 socket.add(JSON.encoder.convert(shutdownRequest));
126 return process.exitCode
127 .timeout(new Duration(seconds: 10))
128 .catchError((error) {
129 process.kill();
130 throw 'Expected server to shutdown';
131 })
132 .then((result) {
133 if (result != 0) {
134 exitCode = result;
135 }
136 return true;
137 });
138 }
139
92 } 140 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698