/* Copyright (c) 2009 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.appengine.demos.sticky.client.model; import java.util.HashMap; import java.util.Map; import com.google.appengine.demos.sticky.client.model.Service.GetSurfacesResult; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; /** * Controls all aspects of loading the set of {@link Surface}s associated with * the current author. This class takes care of performing (and possibly * retrying) a query for the initial set of Notes and then continues polling the * server for updates. * * @author knorton@google.com (Kelly Norton) */ class SurfaceLoader extends Timer implements AsyncCallback<GetSurfacesResult> { /** * Controls the initial laod of surfaces from the server and will retry on * failure. */ private class InitialLoader extends RetryTimer implements AsyncCallback<GetSurfacesResult> { /** * Constructs and starts an initial load. */ public InitialLoader() { start(); } public void onFailure(Throwable caught) { model.onServerFailed(caught instanceof Service.AccessDeniedException); retryLater(); } public void onSuccess(GetSurfacesResult result) { model.onServerSucceeded(); startPolling(); assert result != null; timestamp = result.getTimestamp(); final Surface[] surfaces = result.getSurfaces(); for (int i = 0, n = surfaces.length; i < n; ++i) { final Surface surface = surfaces[i]; surface.initialize(model); surfaceCache.put(surface.getKey(), surface); } model.notifySurfacesReceived(surfaces); } private void start() { model.getService().getSurfaces(null /* timestamp */, this); } @Override protected void retry() { start(); } } /** * The model being controlled by this loader. */ private final Model model; /** * A cache of {@link Surface}s that have been loaded. Used to ensure that * there is only one instance of each surface. */ private final Map<String, Surface> surfaceCache = new HashMap<String, Surface>(); /** * The polling period. */ private final int interval; /** * The most recent timestamp returned by the server. */ private String timestamp; /** * Constructs a new loader for the given model. * * @param model * the model this loader will control * @param interval * the polling interval */ public SurfaceLoader(Model model, int interval) { this.model = model; this.interval = interval; } public void onFailure(Throwable caught) { model.onServerFailed(caught instanceof Service.AccessDeniedException); } public void onSuccess(GetSurfacesResult result) { model.onServerSucceeded(); if (result != null) { timestamp = result.getTimestamp(); final Surface[] surfaces = result.getSurfaces(); for (int i = 0, n = surfaces.length; i < n; ++i) { final Surface surface = surfaces[i]; final Surface existing = surfaceCache.get(surface.getKey()); if (existing == null) { surface.initialize(model); surfaceCache.put(surface.getKey(), surface); model.notifySurfaceCreated(surface); } else { existing.update(model, surface); } } } } @Override public void run() { model.getService().getSurfaces(timestamp, this); } /** * Starts the loader by issuing an initial load. When that load completes, the * loader will start polling. */ public void start() { new InitialLoader(); } private void startPolling() { scheduleRepeating(interval); } /** * Invoked by {@link Model} when it creates a {@link Surface}. * * @param key * the key for the newly saved surface * @param surface * a reference to the surface */ void cacheSurface(String key, Surface surface) { assert key != null; surfaceCache.put(key, surface); } }