org.chromium.sdk/src/org/chromium/sdk/internal/tools/v8/V8CommandProcessor.java
changeset 2 e4420d2515f1
equal deleted inserted replaced
1:ef76fc2ac88c 2:e4420d2515f1
       
     1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
       
     2 // Use of this source code is governed by a BSD-style license that can be
       
     3 // found in the LICENSE file.
       
     4 
       
     5 package org.chromium.sdk.internal.tools.v8;
       
     6 
       
     7 import java.util.Collection;
       
     8 import java.util.logging.Level;
       
     9 import java.util.logging.Logger;
       
    10 
       
    11 import org.chromium.sdk.SyncCallback;
       
    12 import org.chromium.sdk.internal.CloseableMap;
       
    13 import org.chromium.sdk.internal.protocol.CommandResponse;
       
    14 import org.chromium.sdk.internal.protocol.IncomingMessage;
       
    15 import org.chromium.sdk.internal.protocolparser.JsonProtocolParseException;
       
    16 import org.chromium.sdk.internal.tools.v8.request.DebuggerMessage;
       
    17 import org.json.simple.JSONObject;
       
    18 
       
    19 /**
       
    20  * Sends JSON commands to V8 VM and handles responses. Command is sent
       
    21  * via {@code V8CommandOutput}. Response is passed back to callback if it was provided.
       
    22  * Also all responses and events are dispatched to group of dedicated processors.
       
    23  */
       
    24 public class V8CommandProcessor implements V8CommandSender<DebuggerMessage, RuntimeException> {
       
    25 
       
    26   /**
       
    27    * A callback to handle V8 debugger responses.
       
    28    */
       
    29   public interface V8HandlerCallback {
       
    30     /**
       
    31      * This method is invoked when a debugger command result has become
       
    32      * available.
       
    33      *
       
    34      * @param response from the V8 debugger
       
    35      */
       
    36     void messageReceived(CommandResponse response);
       
    37 
       
    38     /**
       
    39      * This method is invoked when a debugger command has failed.
       
    40      *
       
    41      * @param message containing the failure reason
       
    42      */
       
    43     void failure(String message);
       
    44 
       
    45     /** A no-op callback implementation. */
       
    46     V8HandlerCallback NULL_CALLBACK = new V8HandlerCallback() {
       
    47       public void failure(String message) {
       
    48       }
       
    49 
       
    50       public void messageReceived(CommandResponse response) {
       
    51       }
       
    52     };
       
    53   }
       
    54 
       
    55   /** The class logger. */
       
    56   static final Logger LOGGER = Logger.getLogger(V8CommandProcessor.class.getName());
       
    57 
       
    58   private final CloseableMap<Integer, CallbackEntry> callbackMap = CloseableMap.newLinkedMap();
       
    59 
       
    60   private final V8CommandOutput messageOutput;
       
    61 
       
    62   private final DefaultResponseHandler defaultResponseHandler;
       
    63 
       
    64 
       
    65   public V8CommandProcessor(V8CommandOutput messageOutput,
       
    66       DefaultResponseHandler defaultResponseHandler) {
       
    67     this.messageOutput = messageOutput;
       
    68     this.defaultResponseHandler = defaultResponseHandler;
       
    69   }
       
    70 
       
    71   public void sendV8CommandAsync(DebuggerMessage message, boolean isImmediate,
       
    72       V8HandlerCallback v8HandlerCallback, SyncCallback syncCallback) {
       
    73 
       
    74     if (v8HandlerCallback != null) {
       
    75       // TODO(peter.rybin): should we handle IllegalStateException better than rethrowing it?
       
    76       try {
       
    77         callbackMap.put(message.getSeq(), new CallbackEntry(v8HandlerCallback,
       
    78             syncCallback));
       
    79       } catch (IllegalStateException e) {
       
    80         throw new IllegalStateException("Connection is closed", e);
       
    81       }
       
    82     }
       
    83     try {
       
    84       messageOutput.send(message, isImmediate);
       
    85     } catch (RuntimeException e) {
       
    86       if (v8HandlerCallback != null) {
       
    87         callbackMap.remove(message.getSeq());
       
    88       }
       
    89       throw e;
       
    90     }
       
    91   }
       
    92 
       
    93   public void processIncomingJson(final JSONObject v8Json) {
       
    94     IncomingMessage response;
       
    95     try {
       
    96       response = V8ProtocolUtil.getV8Parser().parse(v8Json, IncomingMessage.class);
       
    97     } catch (JsonProtocolParseException e) {
       
    98       LOGGER.log(Level.SEVERE, "JSON message does not conform to the protocol", e);
       
    99       return;
       
   100     }
       
   101 
       
   102     final CommandResponse commandResponse = response.asCommandResponse();
       
   103 
       
   104     if (commandResponse != null) {
       
   105       int requestSeqInt = (int) commandResponse.getRequestSeq();
       
   106       CallbackEntry callbackEntry = callbackMap.removeIfContains(requestSeqInt);
       
   107       if (callbackEntry != null) {
       
   108         LOGGER.log(
       
   109             Level.INFO,
       
   110             "Request-response roundtrip: {0}ms",
       
   111             getCurrentMillis() - callbackEntry.commitMillis);
       
   112 
       
   113         CallbackCaller caller = new CallbackCaller() {
       
   114           @Override
       
   115           void call(V8HandlerCallback handlerCallback) {
       
   116             handlerCallback.messageReceived(commandResponse);
       
   117           }
       
   118         };
       
   119         try {
       
   120           callThemBack(callbackEntry, caller, requestSeqInt);
       
   121         } catch (RuntimeException e) {
       
   122           LOGGER.log(Level.SEVERE, "Failed to dispatch response to callback", e);
       
   123         }
       
   124       }
       
   125     }
       
   126 
       
   127     defaultResponseHandler.handleResponseWithHandler(response);
       
   128   }
       
   129 
       
   130   public void processEos() {
       
   131     // We should call them in the order they have been submitted.
       
   132     Collection<CallbackEntry> entries = callbackMap.close().values();
       
   133     for (CallbackEntry entry : entries) {
       
   134       callThemBack(entry, failureCaller, -1);
       
   135     }
       
   136   }
       
   137 
       
   138   private static abstract class CallbackCaller {
       
   139     abstract void call(V8HandlerCallback handlerCallback);
       
   140   }
       
   141 
       
   142   private final static CallbackCaller failureCaller = new CallbackCaller() {
       
   143     @Override
       
   144     void call(V8HandlerCallback handlerCallback) {
       
   145       handlerCallback.failure("Detach");
       
   146     }
       
   147   };
       
   148 
       
   149 
       
   150   private void callThemBack(CallbackEntry callbackEntry, CallbackCaller callbackCaller,
       
   151       int requestSeq) {
       
   152     RuntimeException callbackException = null;
       
   153     try {
       
   154       if (callbackEntry.v8HandlerCallback != null) {
       
   155         LOGGER.log(
       
   156             Level.INFO, "Notified debugger command callback, request_seq={0}", requestSeq);
       
   157         callbackCaller.call(callbackEntry.v8HandlerCallback);
       
   158       }
       
   159     } catch (RuntimeException e) {
       
   160       callbackException = e;
       
   161       throw e;
       
   162     } finally {
       
   163       if (callbackEntry.syncCallback != null) {
       
   164         callbackEntry.syncCallback.callbackDone(callbackException);
       
   165       }
       
   166     }
       
   167   }
       
   168 
       
   169   /**
       
   170    * @return milliseconds since the epoch
       
   171    */
       
   172   private static long getCurrentMillis() {
       
   173     return System.currentTimeMillis();
       
   174   }
       
   175 
       
   176   static void checkNull(Object object, String message) {
       
   177     if (object == null) {
       
   178       throw new IllegalArgumentException(message);
       
   179     }
       
   180   }
       
   181 
       
   182 
       
   183   private static class CallbackEntry {
       
   184     final V8HandlerCallback v8HandlerCallback;
       
   185 
       
   186     final SyncCallback syncCallback;
       
   187 
       
   188     final long commitMillis;
       
   189 
       
   190     CallbackEntry(V8HandlerCallback v8HandlerCallback, SyncCallback syncCallback) {
       
   191       this.v8HandlerCallback = v8HandlerCallback;
       
   192       this.commitMillis = getCurrentMillis();
       
   193       this.syncCallback = syncCallback;
       
   194     }
       
   195   }
       
   196 
       
   197   public void removeAllCallbacks() {
       
   198     // TODO(peter.rybin): get rid of this method
       
   199   }
       
   200 }