Package com.google.walkaround.wave.server.servlet

Source Code of com.google.walkaround.wave.server.servlet.UndercurrentHandler

/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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.walkaround.wave.server.servlet;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.gxp.base.GxpContext;
import com.google.gxp.html.HtmlClosure;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.walkaround.proto.ClientVars.ErrorVars;
import com.google.walkaround.proto.ClientVars.LiveClientVars;
import com.google.walkaround.proto.ClientVars.StaticClientVars;
import com.google.walkaround.proto.gson.ClientVarsGsonImpl;
import com.google.walkaround.proto.gson.ClientVarsGsonImpl.ErrorVarsGsonImpl;
import com.google.walkaround.proto.gson.ClientVarsGsonImpl.LiveClientVarsGsonImpl;
import com.google.walkaround.proto.gson.ClientVarsGsonImpl.StaticClientVarsGsonImpl;
import com.google.walkaround.proto.gson.ClientVarsGsonImpl.UdwLoadDataGsonImpl;
import com.google.walkaround.slob.server.AccessDeniedException;
import com.google.walkaround.slob.server.GsonProto;
import com.google.walkaround.slob.server.SlobFacilities;
import com.google.walkaround.slob.server.SlobNotFoundException;
import com.google.walkaround.slob.shared.ClientId;
import com.google.walkaround.slob.shared.SlobId;
import com.google.walkaround.util.server.Util;
import com.google.walkaround.util.server.servlet.AbstractHandler;
import com.google.walkaround.util.shared.RandomBase64Generator;
import com.google.walkaround.wave.server.Flag;
import com.google.walkaround.wave.server.FlagName;
import com.google.walkaround.wave.server.ObjectSession;
import com.google.walkaround.wave.server.ObjectSessionHelper;
import com.google.walkaround.wave.server.WaveLoader;
import com.google.walkaround.wave.server.WaveLoader.LoadedWave;
import com.google.walkaround.wave.server.auth.UserContext;
import com.google.walkaround.wave.server.conv.ConvStore;
import com.google.walkaround.wave.server.gxp.Wave;
import com.google.walkaround.wave.server.udw.UdwStore;

import org.json.JSONException;
import org.json.JSONObject;
import org.waveprotocol.wave.model.wave.ParticipantId;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.util.Collections;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Serves the undercurrent client.
*
* @author ohler@google.com (Christian Ohler)
*/
public class UndercurrentHandler extends AbstractHandler {
  private static final String nocacheJs = Util.slurpRequired(
      "com.google.walkaround.wave.client.Walkaround.nocache.js");

  @SuppressWarnings("unused")
  private static final Logger log = Logger.getLogger(UndercurrentHandler.class.getName());

  @Inject @Flag(FlagName.CLIENT_VERSION) int clientVersion;
  @Inject WaveLoader loader;
  @Inject Random random;
  @Inject RandomBase64Generator random64;
  @Inject ParticipantId participantId;
  @Inject @Named("channel api url") String channelApiUrl;
  @Inject ObjectSessionHelper sessionHelper;
  @Inject @Flag(FlagName.ANALYTICS_ACCOUNT) String analyticsAccount;
  @Inject UserContext userContext;
  @Inject @UdwStore SlobFacilities udwStore;
  @Inject @ConvStore SlobFacilities convStore;

  private void setResponseHeaders(HttpServletResponse resp) {
    // TODO(ohler): Figure out how to make static versions cacheable.  The
    // problem is that we inline GWT's nocache JS, and having that cached is
    // very confusing when debugging, and could lead to situations where a
    // browser caches the nocache JS but not the cacheable JS and expects the
    // server to still have the cacheable JS -- this won't be true if a new
    // client binary has been deployed to the server.

    // TODO(ohler): Find a systematic way of setting no-cache headers in all
    // handlers that need them.

    // http://www.mnot.net/cache_docs/#PRAGMA recommends not to use this;
    // TODO(ohler): Figure out if it does any good.  Walkaround doesn't work in
    // old browsers anyway, so we shouldn't need hacks.
    resp.setHeader("Pragma", "No-cache");
    // NOTE(ohler): Using this rather than just "no-cache" seems to fix the
    // client crash on back/forward due to re-use of client id.
    resp.setHeader("Cache-Control", "no-store, must-revalidate");

    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
  }

  private JSONObject clientVarString(
      @Nullable LiveClientVars liveClientVars,
      @Nullable StaticClientVars staticClientVars,
      @Nullable ErrorVars errorVars) {
    Preconditions.checkArgument(Collections.frequency(
            ImmutableList.of(liveClientVars != null, staticClientVars != null, errorVars != null),
            true) == 1,
        "%s/%s/%s", liveClientVars, staticClientVars, errorVars);
    ClientVarsGsonImpl clientVars = new ClientVarsGsonImpl();
    if (liveClientVars != null) {
      clientVars.setLiveClientVars(liveClientVars);
    } else if (staticClientVars != null) {
      clientVars.setStaticClientVars(staticClientVars);
    } else {
      clientVars.setErrorVars(errorVars);
    }
    String jsonString = GsonProto.toJson(clientVars);
    try {
      // TODO(ohler): Find a way to embed a GSON-generated JSON literal in GXP
      // without serializing and re-parsing to org.json.JSONObject.  Perhaps
      // through JavascriptClosure?
      return new JSONObject(jsonString);
    } catch (JSONException e) {
      throw new RuntimeException("org.json failed to parse GSON's output: " + jsonString, e);
    }
  }

  private void writeErrorResponse(HttpServletRequest req, HttpServletResponse resp,
      String errorMessage) throws IOException {
    ErrorVarsGsonImpl errorVars = new ErrorVarsGsonImpl();
    errorVars.setErrorMessage(errorMessage);
    setResponseHeaders(resp);
    Wave.write(resp.getWriter(), new GxpContext(getLocale(req)),
        analyticsAccount, clientVarString(null, null, errorVars), true,
        inlineNocacheJs(), channelApiUrl);
  }

  private void writeLiveClientResponse(HttpServletRequest req, HttpServletResponse resp,
      ClientId clientId, LoadedWave wave) throws IOException {
    LiveClientVarsGsonImpl vars = new LiveClientVarsGsonImpl();
    vars.setClientVersion(clientVersion);
    vars.setRandomSeed(random.nextInt());
    vars.setUserEmail(participantId.getAddress());
    vars.setHaveOauthToken(userContext.hasOAuthCredentials());
    vars.setConvSnapshot(wave.getConvSnapshotWithDiffs());
    vars.setConvConnectResponse(
        sessionHelper.createConnectResponse(
            new ObjectSession(wave.getConvObjectId(), clientId, convStore.getRootEntityKind()),
            wave.getConvConnectResult()));
    if (wave.getUdw() != null) {
      UdwLoadDataGsonImpl udwLoadData = new UdwLoadDataGsonImpl();
      udwLoadData.setConnectResponse(
          sessionHelper.createConnectResponse(
              new ObjectSession(wave.getUdw().getObjectId(), clientId,
                  udwStore.getRootEntityKind()),
              wave.getUdw().getConnectResult()));
      udwLoadData.setSnapshot(wave.getUdw().getSnapshot());
      vars.setUdw(udwLoadData);
    }
    setResponseHeaders(resp);
    Wave.write(resp.getWriter(), new GxpContext(getLocale(req)),
        analyticsAccount, clientVarString(vars, null, null), true,
        inlineNocacheJs(), channelApiUrl);
  }

  private void writeStaticClientResponse(HttpServletRequest req, HttpServletResponse resp,
      LoadedWave wave) throws IOException {
    StaticClientVarsGsonImpl vars = new StaticClientVarsGsonImpl();
    vars.setRandomSeed(random.nextInt());
    vars.setUserEmail(participantId.getAddress());
    vars.setHaveOauthToken(userContext.hasOAuthCredentials());
    vars.setConvObjectId(wave.getConvObjectId().getId());
    vars.setConvSnapshot(wave.getConvSnapshotWithDiffs());
    setResponseHeaders(resp);
    Wave.write(resp.getWriter(), new GxpContext(getLocale(req)),
        analyticsAccount, clientVarString(null, vars, null), true,
        inlineNocacheJs(), channelApiUrl);
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    SlobId convObjectId = new SlobId(requireParameter(req, "id"));
    String versionString = optionalParameter(req, "version", null);
    @Nullable Long version = versionString == null ? null : Long.parseLong(versionString);
    @Nullable ClientId clientId = version != null ? null
        : new ClientId(random64.next(
                // TODO(ohler): justify this number
                8));
    LoadedWave wave;
    try {
      if (version == null) {
        wave = loader.load(convObjectId, clientId);
      } else {
        wave = loader.loadStaticAtVersion(convObjectId, version);
      }
    } catch (AccessDeniedException e) {
      log.log(Level.SEVERE, "Wave not found or access denied", e);
      writeErrorResponse(req, resp, "Wave not found or access denied");
      return;
    } catch (SlobNotFoundException e) {
      log.log(Level.SEVERE, "Wave not found or access denied", e);
      writeErrorResponse(req, resp, "Wave not found or access denied");
      return;
    } catch (IOException e) {
      log.log(Level.SEVERE, "Server error loading wave", e);
      writeErrorResponse(req, resp, "Server error loading wave");
      return;
    }
    if (version == null) {
      writeLiveClientResponse(req, resp, clientId, wave);
    } else {
      writeStaticClientResponse(req, resp, wave);
    }
  }

  private HtmlClosure inlineNocacheJs() {
    return new HtmlClosure() {
      @Override public void write(Appendable out, GxpContext gxpContext) throws IOException {
        out.append("<script type='text/javascript'>\n");
        // No need to escape this, GWT's nocache javascript is already escaped for
        // the purpose of direct inclusion into script elements.
        out.append(nocacheJs);
        out.append("</script>");
      }
    };
  }
}
TOP

Related Classes of com.google.walkaround.wave.server.servlet.UndercurrentHandler

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.