Package com.google.speedtracer.client

Source Code of com.google.speedtracer.client.SymbolServerController

/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.speedtracer.client;

import com.google.gwt.coreext.client.IterableFastStringMap;
import com.google.gwt.coreext.client.JSON;
import com.google.gwt.coreext.client.JSON.JSONParseException;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.google.speedtracer.client.SourceViewer.SourcePresenter;
import com.google.speedtracer.client.SymbolServerManifest.ResourceSymbolInfo;
import com.google.speedtracer.client.model.JsSymbol;
import com.google.speedtracer.client.model.JsSymbolMap;
import com.google.speedtracer.client.util.Url;
import com.google.speedtracer.client.util.Xhr;
import com.google.speedtracer.client.util.Xhr.XhrCallback;

import java.util.ArrayList;
import java.util.List;

/**
* Class resolving the symbol map file and original source files for
* re-symbolization.
*/
public class SymbolServerController {
  /**
   * Listener interface for getting notified when a symbols or source are
   * available.
   */
  public interface Callback {
    /**
     * Called if the request for the symbols failed.
     */
    void onSymbolsFetchFailed(int errorReason);

    /**
     * Called when the symbols for a resource have been loaded.
     */
    void onSymbolsReady(JsSymbolMap symbols);
  }

  /**
   * Callback interface for renderers that wish to consume a resymbolized
   * symbol.
   */
  public interface Resymbolizeable {
    void reSymbolize(String sourceServer,
        SourceViewerServer sourceViewerServer, JsSymbol sourceSymbol,
        SourcePresenter sourcePresenter);
  }

  /**
   * A request that has been queued for servicing.
   */
  private class PendingRequest {
    final Callback callback;
    final String resourceUrl;

    PendingRequest(String resourceUrl, Callback callback) {
      this.resourceUrl = resourceUrl;
      this.callback = callback;
    }
  }

  public static final int ERROR_MANIFEST_NOT_LOADED = 0;

  public static final int ERROR_SYMBOL_FETCH_FAIL = 1;

  private static IterableFastStringMap<JsSymbolMap> resourceSymbols = new IterableFastStringMap<JsSymbolMap>();

  /**
   * Retrieve a Symbol Map for a particular resource URL.
   *
   * @param resource The resource we want the symbol mapping for.
   * @return The symbol map.
   */
  public static JsSymbolMap get(String resource) {
    return resourceSymbols.get(resource);
  }

  /**
   * Inserts a symbol map into the set of available symbol maps, keyed by
   * resource URL.
   *
   * @param resource The resource we want the symbol mapping for.
   */
  static void put(String resource, JsSymbolMap symbols) {
    resourceSymbols.put(resource, symbols);
  }

  private final Url mainResourceUrl;

  private boolean manifestLoaded = false;

  private final List<PendingRequest> pendingRequests;

  private final IterableFastStringMap<List<XhrCallback>> queuedSymbolMapRequests = new IterableFastStringMap<List<XhrCallback>>();

  private final Url symbolManifestUrl;

  private SymbolServerManifest symbolServerManifest;

  SymbolServerController(Url mainResourceUrl, Url symbolManifestUrl) {
    this.mainResourceUrl = mainResourceUrl;
    this.symbolManifestUrl = symbolManifestUrl;
    this.pendingRequests = new ArrayList<PendingRequest>();
    // Start xhr for fetching our associated symbol manifest.
    init();
  }

  /**
   * Attempts to resymbolize a given symbol name within a given resource.
   *
   * @param resourceUrl the url of the resource that contains the symbol we want
   *          to resymblize
   * @param symbolName the symbol we want to resymbolize
   * @param renderer the recipient of the resymbolization
   * @param sourcePresenter the entity responsible for displaying the source of
   *          the resymbolized symbol
   */
  public void attemptResymbolization(final String resourceUrl,
      final String symbolName, final Resymbolizeable renderer,
      final SourcePresenter sourcePresenter) {
    requestSymbolsFor(resourceUrl, new Callback() {
      public void onSymbolsFetchFailed(int errorReason) {
        // TODO (jaimeyap): Do something here... or not.
      }

      public void onSymbolsReady(final JsSymbolMap symbols) {
        // Extract the source symbol.
        final JsSymbol sourceSymbol = symbols.lookup(symbolName);

        if (sourceSymbol == null) {
          return;
        }
        // Enhance the rendered frame with the resymbolization.
        renderer.reSymbolize(symbols.getSourceServer(),
            symbols.getSourceViewerServer(), sourceSymbol, sourcePresenter);
      }
    });
  }

  /**
   * Cancels all pending requests.
   */
  public void cancelPendingRequests() {
    pendingRequests.clear();
  }

  /**
   * Looks up the {@link JsSymbolMap} associated with a particular resource url
   * and calls the specified callback when it has been fetched.
   *
   * If the symbol manifest is not loaded, this method will queue the request to
   * be serviced as soon as the manifest loads.
   *
   * This call may or may not call back synchronously. Do not bank on
   * asynchronous behavior.
   *
   * @param resourceUrl the resource that we intend to get the symbols for.
   * @param callback the {@link Callback} that gets invoked when the symbols are
   *          loaded.
   */
  public void requestSymbolsFor(final String resourceUrl,
      final Callback callback) {
    PendingRequest request = new PendingRequest(resourceUrl, callback);
    if (!manifestLoaded) {
      // Queue a pending request.
      pendingRequests.add(request);
      return;
    }

    serviceRequest(request);
  }

  private void init() {
    Xhr.get(symbolManifestUrl.getUrl(), new XhrCallback() {

      public void onFail(XMLHttpRequest xhr) {
        // Let pending requests know that the manifest failed to load.
        for (PendingRequest request : pendingRequests) {
          request.callback.onSymbolsFetchFailed(ERROR_MANIFEST_NOT_LOADED);
        }
        cancelPendingRequests();
        SymbolServerService.unregisterSymbolServerController(mainResourceUrl);
        if (ClientConfig.isDebugMode()) {
          Logging.getLogger().logText(
              "Fetching manifest " + symbolManifestUrl.getUrl() + " failed.");
        }
      }

      public void onSuccess(XMLHttpRequest xhr) {
        // TODO (jaimeyap): This needs to be validated... and we should handle
        // any parsing errors as well.
        try {
          SymbolServerController.this.symbolServerManifest = JSON.parse(
              xhr.getResponseText()).cast();
        } catch (JSONParseException jpe) {
          if (ClientConfig.isDebugMode()) {
            Logging.getLogger().logText(
                "Unable to parse manifest for " + symbolManifestUrl.getUrl());
          }
          onFail(xhr); // Treat an invalid manifest like a network failure.
          return;
        }
       
        SymbolServerController.this.manifestLoaded = true;
        // Now service all the pending requests.
        while (!pendingRequests.isEmpty()) {
          serviceRequest(pendingRequests.remove(0));
        }
        if (ClientConfig.isDebugMode()) {
          Logging.getLogger().logText(
              "Manifest " + symbolManifestUrl.getUrl() + " loaded.");
        }
      }
    });
  }

  private ResourceSymbolInfo lookupEntryInManifest(String resourceUrl) {
    ResourceSymbolInfo resourceSymbolInfo = null;
   
    // If the resourceUrl begins with a '/' then we assume it is relative to the
    // origin.
    String relativeUrl = "";
    if (resourceUrl.charAt(0) == '/') {
      relativeUrl = Url.convertToRelativeUrl(mainResourceUrl.getOrigin(),
          resourceUrl);
      resourceSymbolInfo = symbolServerManifest.getResourceSymbolInfo(relativeUrl);

    } else {
      // First try looking for the resource using the full URL.
      resourceSymbolInfo = symbolServerManifest.getResourceSymbolInfo(resourceUrl);
      // If the lookup was null, then attempt a relative url lookup.
      if (resourceSymbolInfo == null) {
        relativeUrl = Url.convertToRelativeUrl(
            mainResourceUrl.getResourceBase(), resourceUrl);
        resourceSymbolInfo = symbolServerManifest.getResourceSymbolInfo(relativeUrl);
      }
    }
    if (ClientConfig.isDebugMode() && resourceSymbolInfo == null) {
      Logging.getLogger().logText(
          "Failed to find url " + resourceUrl + " relative to " + relativeUrl
              + " base: " + mainResourceUrl.getResourceBase());
    }
    return resourceSymbolInfo;
  }

  private void serviceRequest(PendingRequest request) {
    assert manifestLoaded : "Manifest should be loaded";

    final ResourceSymbolInfo resourceSymbolInfo = lookupEntryInManifest(request.resourceUrl);
    final Callback callback = request.callback;
    if (resourceSymbolInfo == null) {
      if (ClientConfig.isDebugMode()) {
        Logging.getLogger().logText(
            "Resymbolization failed: No symbol info for " + request.resourceUrl);
      }
      callback.onSymbolsFetchFailed(ERROR_SYMBOL_FETCH_FAIL);
      return;
    }

    final String symbolMapUrl = resourceSymbolInfo.getSymbolMapUrl();
    JsSymbolMap symbolMap = get(symbolMapUrl);
    // We only want to request and parse for symbolMaps we havn't already
    // parsed.
    if (symbolMap == null) {
      // Create the XhrCallback to service this request.
      XhrCallback xhrCallback = new XhrCallback() {

        public void onFail(XMLHttpRequest xhr) {
          callback.onSymbolsFetchFailed(ERROR_SYMBOL_FETCH_FAIL);
          dequeuePendingXhrs(symbolMapUrl, xhr, false);
          if (ClientConfig.isDebugMode()) {
            Logging.getLogger().logText(
                "Fetching symbol map: " + symbolMapUrl + " failed.");
          }
        }

        public void onSuccess(XMLHttpRequest xhr) {
          // Double check that another XHR didnt pull it down and parse it.
          JsSymbolMap fetchedSymbolMap = get(symbolMapUrl);
          if (fetchedSymbolMap == null) {
            fetchedSymbolMap = JsSymbolMap.parse(
                resourceSymbolInfo.getSourceServer(),
                resourceSymbolInfo.getSourceViewerServer(),
                resourceSymbolInfo.getType(), xhr.getResponseText());
            put(symbolMapUrl, fetchedSymbolMap);
          }
          callback.onSymbolsReady(fetchedSymbolMap);
          dequeuePendingXhrs(symbolMapUrl, xhr, true);
          if (ClientConfig.isDebugMode()) {
            Logging.getLogger().logText("Fetched symbol map: " + symbolMapUrl);
          }
        }

        private void dequeuePendingXhrs(String symbolMapUrl,
            XMLHttpRequest xhr, boolean success) {
          List<XhrCallback> callbacks = queuedSymbolMapRequests.get(symbolMapUrl);
          if (callbacks != null) {
            while (!callbacks.isEmpty()) {
              XhrCallback callback = callbacks.remove(0);
              if (success) {
                callback.onSuccess(xhr);
              } else {
                callback.onFail(xhr);
              }
            }
          }
        }
      };

      // Check to see if we have a request in flight.
      List<XhrCallback> requestCallbacks = queuedSymbolMapRequests.get(symbolMapUrl);
      if (requestCallbacks == null) {
        // Make an entry indicating a request is in flight.
        queuedSymbolMapRequests.put(symbolMapUrl, new ArrayList<XhrCallback>());
        if (ClientConfig.isDebugMode()) {
          Logging.getLogger().logText(
              "Fetching symbol map URL: " + symbolManifestUrl.getResourceBase()
                  + symbolMapUrl);
        }
        Xhr.get(symbolManifestUrl.getResourceBase() + symbolMapUrl, xhrCallback);
      } else {
        // There are pending XHRs out. Which means that we should just queue
        // this request.
        requestCallbacks.add(xhrCallback);
      }
    } else {
      // We have already fetched this and parsed it before. Send it to the
      // callback.
      callback.onSymbolsReady(symbolMap);
    }
  }
}
TOP

Related Classes of com.google.speedtracer.client.SymbolServerController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.