1111
1212package com .microsoft .java .debug .core .adapter ;
1313
14- import java .io .BufferedReader ;
15- import java .io .BufferedWriter ;
16- import java .io .IOException ;
1714import java .io .InputStream ;
18- import java .io .InputStreamReader ;
1915import java .io .OutputStream ;
20- import java .io .OutputStreamWriter ;
21- import java .io .PrintWriter ;
22- import java .io .Reader ;
23- import java .io .Writer ;
24- import java .nio .charset .Charset ;
25- import java .nio .charset .StandardCharsets ;
26- import java .util .concurrent .ConcurrentLinkedQueue ;
27- import java .util .concurrent .atomic .AtomicInteger ;
28- import java .util .logging .Level ;
29- import java .util .logging .Logger ;
30- import java .util .regex .Matcher ;
31- import java .util .regex .Pattern ;
3216
33- import com .microsoft .java .debug .core .Configuration ;
3417import com .microsoft .java .debug .core .UsageDataSession ;
18+ import com .microsoft .java .debug .core .protocol .AbstractProtocolServer ;
19+ import com .microsoft .java .debug .core .protocol .Messages ;
3520
36- public class ProtocolServer {
37- private static final Logger logger = Logger .getLogger (Configuration .LOGGER_NAME );
38- private static final int BUFFER_SIZE = 4096 ;
39- private static final String TWO_CRLF = "\r \n \r \n " ;
40- private static final Pattern CONTENT_LENGTH_MATCHER = Pattern .compile ("Content-Length: (\\ d+)" );
41- private static final Charset PROTOCOL_ENCODING = StandardCharsets .UTF_8 ; // vscode protocol uses UTF-8 as encoding format.
42-
43- private Reader reader ;
44- private Writer writer ;
45-
46- private ByteBuffer rawData ;
47- private boolean terminateSession = false ;
48- private int contentLength = -1 ;
49- private AtomicInteger sequenceNumber = new AtomicInteger (1 );
50-
51- private boolean isDispatchingData ;
52- private ConcurrentLinkedQueue <Messages .Event > eventQueue ;
53-
21+ public class ProtocolServer extends AbstractProtocolServer {
5422 private IDebugAdapter debugAdapter ;
55-
5623 private UsageDataSession usageDataSession = new UsageDataSession ();
5724
5825 /**
@@ -65,18 +32,14 @@ public class ProtocolServer {
6532 * provider context for a series of provider implementation
6633 */
6734 public ProtocolServer (InputStream input , OutputStream output , IProviderContext context ) {
68- this .reader = new BufferedReader (new InputStreamReader (input , PROTOCOL_ENCODING ));
69- this .writer = new PrintWriter (new BufferedWriter (new OutputStreamWriter (output , PROTOCOL_ENCODING )));
70- this .contentLength = -1 ;
71- this .rawData = new ByteBuffer ();
72- this .eventQueue = new ConcurrentLinkedQueue <>();
35+ super (input , output );
7336 this .debugAdapter = new DebugAdapter ((debugEvent , willSendLater ) -> {
7437 // If the protocolServer has been stopped, it'll no longer receive any event.
7538 if (!terminateSession ) {
7639 if (willSendLater ) {
77- this . sendEventLater (debugEvent .type , debugEvent );
40+ sendEventLater (debugEvent .type , debugEvent );
7841 } else {
79- this . sendEvent (debugEvent .type , debugEvent );
42+ sendEvent (debugEvent .type , debugEvent );
8043 }
8144 }
8245 }, context );
@@ -87,178 +50,25 @@ public ProtocolServer(InputStream input, OutputStream output, IProviderContext c
8750 */
8851 public void start () {
8952 usageDataSession .reportStart ();
90- char [] buffer = new char [BUFFER_SIZE ];
91- try {
92- while (!this .terminateSession ) {
93- int read = this .reader .read (buffer , 0 , BUFFER_SIZE );
94- if (read == -1 ) {
95- break ;
96- }
97-
98- this .rawData .append (new String (buffer , 0 , read ).getBytes (PROTOCOL_ENCODING ));
99- this .processData ();
100- }
101- } catch (IOException e ) {
102- logger .log (Level .SEVERE , String .format ("Read data from io exception: %s" , e .toString ()), e );
103- }
53+ super .start ();
10454 }
10555
10656 /**
10757 * Sets terminateSession flag to true. And the dispatcher loop will be terminated after current dispatching operation finishes.
10858 */
10959 public void stop () {
11060 usageDataSession .reportStop ();
111- this . terminateSession = true ;
61+ super . stop () ;
11262 usageDataSession .submitUsageData ();
11363 }
11464
115- /**
116- * Send event to DA immediately.
117- * @param eventType
118- * event type
119- * @param body
120- * event body
121- */
122- private void sendEvent (String eventType , Object body ) {
123- sendMessage (new Messages .Event (eventType , body ));
124- }
125-
126- /**
127- * If the the dispatcher is idle, then send the event immediately.
128- * Else add the new event to an eventQueue first and send them when dispatcher becomes idle again.
129- * @param eventType
130- * event type
131- * @param body
132- * event content
133- */
134- private void sendEventLater (String eventType , Object body ) {
135- synchronized (this ) {
136- if (this .isDispatchingData ) {
137- this .eventQueue .offer (new Messages .Event (eventType , body ));
138- } else {
139- sendMessage (new Messages .Event (eventType , body ));
140- }
65+ protected void dispatchRequest (Messages .Request request ) {
66+ Messages .Response response = this .debugAdapter .dispatchRequest (request );
67+ if (request .command .equals ("disconnect" )) {
68+ stop ();
14169 }
70+ sendMessage (response );
71+ usageDataSession .recordResponse (response );
14272 }
14373
144- private void sendMessage (Messages .ProtocolMessage message ) {
145- message .seq = this .sequenceNumber .getAndIncrement ();
146-
147- String jsonMessage = JsonUtils .toJson (message );
148- byte [] jsonBytes = jsonMessage .getBytes (PROTOCOL_ENCODING );
149-
150- String header = String .format ("Content-Length: %d%s" , jsonBytes .length , TWO_CRLF );
151- byte [] headerBytes = header .getBytes (PROTOCOL_ENCODING );
152-
153- byte [] data = new byte [headerBytes .length + jsonBytes .length ];
154- System .arraycopy (headerBytes , 0 , data , 0 , headerBytes .length );
155- System .arraycopy (jsonBytes , 0 , data , headerBytes .length , jsonBytes .length );
156-
157- String utf8Data = new String (data , PROTOCOL_ENCODING );
158-
159- try {
160- logger .fine ("\n [[RESPONSE]]\n " + new String (data ));
161- this .writer .write (utf8Data );
162- this .writer .flush ();
163- } catch (IOException e ) {
164- logger .log (Level .SEVERE , String .format ("Write data to io exception: %s" , e .toString ()), e );
165- }
166- }
167-
168- private void processData () {
169- while (true ) {
170- /**
171- * In vscode debug protocol, the content length represents the message's byte length with utf8 format.
172- */
173- if (this .contentLength >= 0 ) {
174- if (this .rawData .length () >= this .contentLength ) {
175- byte [] buf = this .rawData .removeFirst (this .contentLength );
176- this .contentLength = -1 ;
177- dispatchRequest (new String (buf , PROTOCOL_ENCODING ));
178- continue ;
179- }
180- } else {
181- String rawMessage = this .rawData .getString (PROTOCOL_ENCODING );
182- int idx = rawMessage .indexOf (TWO_CRLF );
183- if (idx != -1 ) {
184- Matcher matcher = CONTENT_LENGTH_MATCHER .matcher (rawMessage );
185- if (matcher .find ()) {
186- this .contentLength = Integer .parseInt (matcher .group (1 ));
187- int headerByteLength = rawMessage .substring (0 , idx + TWO_CRLF .length ()).getBytes (PROTOCOL_ENCODING ).length ;
188- this .rawData .removeFirst (headerByteLength ); // Remove the header from the raw message.
189- continue ;
190- }
191- }
192- }
193- break ;
194- }
195- }
196-
197- private void dispatchRequest (String request ) {
198- try {
199- logger .fine ("\n [REQUEST]\n " + request );
200- Messages .Request message = JsonUtils .fromJson (request , Messages .Request .class );
201- usageDataSession .recordRequest (message );
202- if (message .type .equals ("request" )) {
203- synchronized (this ) {
204- this .isDispatchingData = true ;
205- }
206-
207- try {
208- Messages .Response response = this .debugAdapter .dispatchRequest (message );
209- if (message .command .equals ("disconnect" )) {
210- this .stop ();
211- }
212- sendMessage (response );
213- usageDataSession .recordResponse (response );
214- } catch (Exception e ) {
215- logger .log (Level .SEVERE , String .format ("Dispatch debug protocol error: %s" , e .toString ()), e );
216- }
217- }
218- } finally {
219- synchronized (this ) {
220- this .isDispatchingData = false ;
221- }
222-
223- while (this .eventQueue .peek () != null ) {
224- sendMessage (this .eventQueue .poll ());
225- }
226- }
227- }
228-
229- class ByteBuffer {
230- private byte [] buffer ;
231-
232- public ByteBuffer () {
233- this .buffer = new byte [0 ];
234- }
235-
236- public int length () {
237- return this .buffer .length ;
238- }
239-
240- public String getString (Charset cs ) {
241- return new String (this .buffer , cs );
242- }
243-
244- public void append (byte [] b ) {
245- append (b , b .length );
246- }
247-
248- public void append (byte [] b , int length ) {
249- byte [] newBuffer = new byte [this .buffer .length + length ];
250- System .arraycopy (buffer , 0 , newBuffer , 0 , this .buffer .length );
251- System .arraycopy (b , 0 , newBuffer , this .buffer .length , length );
252- this .buffer = newBuffer ;
253- }
254-
255- public byte [] removeFirst (int n ) {
256- byte [] b = new byte [n ];
257- System .arraycopy (this .buffer , 0 , b , 0 , n );
258- byte [] newBuffer = new byte [this .buffer .length - n ];
259- System .arraycopy (this .buffer , n , newBuffer , 0 , this .buffer .length - n );
260- this .buffer = newBuffer ;
261- return b ;
262- }
263- }
26474}
0 commit comments