com.google.speedtracer.client.model.NetworkEventDispatcher.java Source code

Java tutorial

Introduction

Here is the source code for com.google.speedtracer.client.model.NetworkEventDispatcher.java

Source

/*
 * Copyright 2008 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.model;

import com.google.gwt.coreext.client.JSOArray;
import com.google.gwt.coreext.client.JsIntegerMap;
import com.google.gwt.coreext.client.JsStringMap;
import com.google.speedtracer.client.model.DataDispatcher.DataDispatcherDelegate;
import com.google.speedtracer.client.model.DataDispatcher.EventRecordDispatcher;
import com.google.speedtracer.client.model.NetworkResponseReceivedEvent.Response;
import com.google.speedtracer.client.model.ResourceUpdateEvent.UpdateResource;
import com.google.speedtracer.shared.EventRecordType;

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

/**
 * Native dispatcher which sources Network Events for the UI. Hooks into
 * underlying DataInstance.
 */
public class NetworkEventDispatcher implements DataDispatcherDelegate {

    /**
     * Listener Interface for a NetworkEventDispatcher.
     */
    public interface Listener {
        void onNetworkResourceRequestStarted(NetworkResource resource, boolean isRedirect);

        void onNetworkResourceResponseFinished(NetworkResource resource);

        void onNetworkResourceResponseStarted(NetworkResource resource);

        void onNetworkResourceUpdated(NetworkResource resource);
    }

    /**
     * Sets up mapping of NetworkResourceRecord types to their respective
     * handlers.
     *
     * @param proxy the {@link NetworkEventDispatcher}
     * @param typeMap the {@link FastStringMap}
     */
    private static void setNetworkEventCallbacks(final NetworkEventDispatcher proxy,
            JsIntegerMap<EventRecordDispatcher> typeMap) {

        typeMap.put(EventRecordType.RESOURCE_SEND_REQUEST, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkResourceStarted(data.<ResourceWillSendEvent>cast());
            }
        });

        typeMap.put(EventRecordType.RESOURCE_RECEIVE_RESPONSE, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkResourceResponse(data.<ResourceResponseEvent>cast());
            }
        });

        typeMap.put(EventRecordType.RESOURCE_FINISH, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                ResourceFinishEvent finish = data.cast();
                proxy.onNetworkResourceFinished(finish);
            }
        });

        typeMap.put(EventRecordType.NETWORK_LOADING_FINISHED, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                ResourceFinishEvent finish = data.cast();
                proxy.onNetworkResourceFinished(finish);
            }
        });

        typeMap.put(EventRecordType.RESOURCE_UPDATED, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkResourceUpdated(data.<ResourceUpdateEvent>cast());
            }
        });

        typeMap.put(EventRecordType.NETWORK_DATA_RECEIVED, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkDataReceived(data.<NetworkDataReceivedEvent>cast());
            }
        });

        typeMap.put(EventRecordType.NETWORK_RESPONSE_RECEIVED, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkResponseReceived(data.<NetworkResponseReceivedEvent>cast());
            }
        });

        typeMap.put(EventRecordType.NETWORK_REQUEST_WILL_BE_SENT, new EventRecordDispatcher() {
            public void onEventRecord(EventRecord data) {
                proxy.onNetworkRequestWillBeSent(data.<NetworkRequestWillBeSentEvent>cast());
            }
        });
    }

    private final List<Listener> listeners = new ArrayList<Listener>();

    private final List<ResourceRecord> networkEvents = new ArrayList<ResourceRecord>();

    private JsStringMap<JSOArray<NetworkResource>> redirects = JsStringMap.create();

    /**
     * Map of NetworkResource POJOs. Information about a network resource is
     * filled in progressively as we get chunks of information about it.
     */
    private final JsStringMap<NetworkResource> resourceStore = JsStringMap.create();

    private final JsIntegerMap<EventRecordDispatcher> typeMap = JsIntegerMap.create();

    public NetworkEventDispatcher() {
        setNetworkEventCallbacks(this, typeMap);
    }

    public void addListener(Listener listener) {
        listeners.add(listener);
    }

    public void clearData() {
        redirects = JsStringMap.create();
        networkEvents.clear();
    }

    public List<ResourceRecord> getNetworkEvents() {
        return networkEvents;
    }

    /**
     * Gets a stored resource from our book keeping, or null if it hasnt been
     * stored before.
     *
     * @param id the request id of our {@link NetworkResource}
     * @return returns the {@link NetworkResource}
     */
    public NetworkResource getResource(String id) {
        return resourceStore.get(id);
    }

    public void onEventRecord(EventRecord data) {
        final EventRecordDispatcher handler = typeMap.get(data.getType());
        if (handler != null) {
            handler.onEventRecord(data);
        }
    }

    public void removeListener(Listener listener) {
        listeners.remove(listener);
    }

    /**
     * @param identifier Resource ID
     * @param url The redirect URL that we are using to match a redirect
     *        candidate.
     */
    private NetworkResource findAndRemoveRedirectCandidate(String identifier, String url) {
        // Look for it.
        JSOArray<NetworkResource> redirectCandidates = redirects.get(identifier);
        if (redirectCandidates == null) {
            return null;
        }

        for (int i = 0; i < redirectCandidates.size(); i++) {
            NetworkResource redirectCandidate = redirectCandidates.get(i);
            if (redirectCandidate.getUrl().equals(url)) {
                // Should not be a concurrent modification, since we now bail out of
                // the loop right after mutating the queue.
                redirectCandidates.splice(i, 1);
                return redirectCandidate;
            }
        }

        return null;
    }

    private void insertRedirectCandidate(String identifier, NetworkResource previousResource) {
        // We have a redirect.
        JSOArray<NetworkResource> redirectQueue = redirects.get(identifier);
        if (redirectQueue == null) {
            redirectQueue = JSOArray.create().cast();
            redirects.put(identifier, redirectQueue);
        }
        redirectQueue.push(previousResource);
    }

    private void onNetworkDataReceived(NetworkDataReceivedEvent dataLengthChange) {
        NetworkResource resource = getResource(dataLengthChange.getRequestId());
        if (resource != null) {
            resource.update(dataLengthChange);
        }
    }

    private void onNetworkResponseReceived(NetworkResponseReceivedEvent response) {
        NetworkResource resource = getResource(response.getRequestId());
        if (resource != null) {
            resource.update(response);
        }
    }

    private void onNetworkRequestWillBeSent(NetworkRequestWillBeSentEvent requestWillBeSent) {
        NetworkResource resource = getResource(requestWillBeSent.getRequestId());
        if (resource != null) {
            resource.update(requestWillBeSent);
            // We depend on the fact that any redirects that we encounter will have
            // a corresponding timeline agent event that will add it to the redirects
            // map. We look for one here.
            NetworkRequestWillBeSentEvent.Data data = requestWillBeSent.getData().cast();
            Response redirectResponse = data.getRedirectResponse();
            if (redirectResponse != null) {
                // look for a redirect.
                NetworkResource redirect = findAndRemoveRedirectCandidate(requestWillBeSent.getRequestId(),
                        redirectResponse.getUrl());
                if (redirect != null) {
                    redirect.updateResponse(redirectResponse);
                    redirect.setResponseReceivedTime(requestWillBeSent.getTime());
                    redirect.setEndTime(requestWillBeSent.getTime());
                    redirectUpdated(redirect);
                }
            }
        }
    }

    private void onNetworkResourceFinished(ResourceFinishEvent resourceFinish) {
        networkEvents.add(resourceFinish);
        NetworkResource resource = getResource(resourceFinish.getRequestId());
        if (resource != null) {
            resource.update(resourceFinish);
            for (int i = 0, n = listeners.size(); i < n; i++) {
                Listener listener = listeners.get(i);
                listener.onNetworkResourceResponseFinished(resource);
            }
        }
    }

    private void onNetworkResourceResponse(ResourceResponseEvent resourceResponse) {
        networkEvents.add(resourceResponse);
        NetworkResource resource = getResource(resourceResponse.getRequestId());
        if (resource != null) {
            resource.update(resourceResponse);
            for (int i = 0, n = listeners.size(); i < n; i++) {
                Listener listener = listeners.get(i);
                listener.onNetworkResourceResponseStarted(resource);
            }
        }
    }

    private void onNetworkResourceStarted(ResourceWillSendEvent resourceStart) {
        networkEvents.add(resourceStart);
        // Check for dupe IDs. If we find one, assume it is a redirect.
        NetworkResource previousResource = getResource(resourceStart.getRequestId());
        boolean isRedirect = false;
        if (previousResource != null) {
            insertRedirectCandidate(previousResource.getIdentifier(), previousResource);
            isRedirect = true;
        }

        NetworkResource resource = new NetworkResource(resourceStart);
        resourceStore.put(resourceStart.getRequestId(), resource);
        for (int i = 0, n = listeners.size(); i < n; i++) {
            Listener listener = listeners.get(i);
            listener.onNetworkResourceRequestStarted(resource, isRedirect);
        }
    }

    /**
     * This should now be dead code in the live tracing case, and is only kept to
     * support loading old saved dumps.
     */
    private void onNetworkResourceUpdated(ResourceUpdateEvent update) {
        NetworkResource resource = getResource(update.getRequestId());
        if (resource != null) {
            resource.update(update);
            for (int i = 0, n = listeners.size(); i < n; i++) {
                Listener listener = listeners.get(i);
                listener.onNetworkResourceUpdated(resource);
            }
        } else {
            // We are dealing potentially with an update for a redirect.
            UpdateResource updateResource = update.getUpdate();
            NetworkResource redirectCandidate = findAndRemoveRedirectCandidate(update.getRequestId(),
                    updateResource.getUrl());
            if (redirectCandidate != null) {
                redirectCandidate.update(update);
                redirectUpdated(redirectCandidate);
            }
        }
    }

    private void redirectUpdated(NetworkResource redirectCandidate) {
        for (int i = 0, n = listeners.size(); i < n; i++) {
            Listener listener = listeners.get(i);
            listener.onNetworkResourceUpdated(redirectCandidate);
        }
    }
}