Java tutorial
/* * 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); } } }