Package com.google.walkaround.wave.server.googleimport

Source Code of com.google.walkaround.wave.server.googleimport.ImportOverviewHandler

/*
* 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.googleimport;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import com.google.common.net.UriEscapers;
import com.google.gxp.base.GxpContext;
import com.google.gxp.html.HtmlClosure;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.walkaround.proto.ImportSettings;
import com.google.walkaround.proto.ImportSettings.ImportSharingMode;
import com.google.walkaround.proto.ImportTaskPayload;
import com.google.walkaround.proto.ImportWaveletTask;
import com.google.walkaround.proto.gson.ImportSettingsGsonImpl;
import com.google.walkaround.proto.gson.ImportTaskPayloadGsonImpl;
import com.google.walkaround.proto.gson.ImportWaveletTaskGsonImpl;
import com.google.walkaround.slob.shared.SlobId;
import com.google.walkaround.util.server.HtmlEscaper;
import com.google.walkaround.util.server.RetryHelper;
import com.google.walkaround.util.server.RetryHelper.PermanentFailure;
import com.google.walkaround.util.server.RetryHelper.RetryableFailure;
import com.google.walkaround.util.server.appengine.CheckedDatastore;
import com.google.walkaround.util.server.appengine.CheckedDatastore.CheckedTransaction;
import com.google.walkaround.util.server.auth.InvalidSecurityTokenException;
import com.google.walkaround.util.server.servlet.AbstractHandler;
import com.google.walkaround.util.server.servlet.BadRequestException;
import com.google.walkaround.util.shared.Assert;
import com.google.walkaround.wave.server.Flag;
import com.google.walkaround.wave.server.FlagName;
import com.google.walkaround.wave.server.auth.NeedNewOAuthTokenException;
import com.google.walkaround.wave.server.auth.StableUserId;
import com.google.walkaround.wave.server.auth.UserContext;
import com.google.walkaround.wave.server.auth.XsrfHelper;
import com.google.walkaround.wave.server.auth.XsrfHelper.XsrfTokenExpiredException;
import com.google.walkaround.wave.server.gxp.ImportOverviewFragment;
import com.google.walkaround.wave.server.gxp.ImportWaveletDisplayRecord;
import com.google.walkaround.wave.server.gxp.SourceInstance;
import com.google.walkaround.wave.server.servlet.PageSkinWriter;

import org.joda.time.Instant;
import org.joda.time.LocalDate;
import org.waveprotocol.wave.model.id.WaveId;
import org.waveprotocol.wave.model.id.WaveletId;
import org.waveprotocol.wave.model.id.WaveletName;
import org.waveprotocol.wave.model.util.Pair;
import org.waveprotocol.wave.model.wave.ParticipantId;

import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;

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

/**
* Interactive entry point for import features.
*
* @author ohler@google.com (Christian Ohler)
*/
// For now, this shows a lot of detail, more than typical users would want.
// Once it works well, we should simplify it.
public class ImportOverviewHandler extends AbstractHandler {

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

  private static final String XSRF_ACTION = "importaction";

  @Inject ParticipantId participantId;
  @Inject StableUserId userId;
  @Inject XsrfHelper xsrfHelper;
  @Inject SourceInstance.Factory sourceInstanceFactory;
  @Inject TaskDispatcher taskDispatcher;
  // Providers because the values are only needed in some branches of the code.
  @Inject Provider<CheckedDatastore> datastore;
  @Inject Provider<PerUserTable> perUserTable;
  @Inject Provider<FindRemoteWavesProcessor> findProcessor;
  @Inject PageSkinWriter pageSkinWriter;
  @Inject UserContext userContext;
  @Inject @Flag(FlagName.IMPORT_PRESERVE_HISTORY) boolean preserveHistory;

  private String makeLocalWaveLink(SlobId convSlobId) {
    return "/wave?id=" + UriEscapers.uriQueryStringEscaper(false).escape(convSlobId.getId());
  }

  private List<ImportWaveletDisplayRecord> getWaves(CheckedTransaction tx,
      Multimap<Pair<SourceInstance, WaveletName>, ImportSharingMode> importsInProgress)
      throws RetryableFailure, PermanentFailure {
    ImmutableList.Builder<ImportWaveletDisplayRecord> out = ImmutableList.builder();
    List<RemoteConvWavelet> wavelets = perUserTable.get().getAllWavelets(tx, userId);
    for (RemoteConvWavelet wavelet : wavelets) {
      WaveletName waveletName = WaveletName.of(
          WaveId.deserialise(wavelet.getDigest().getWaveId()),
          wavelet.getWaveletId());
      out.add(
          new ImportWaveletDisplayRecord(
              wavelet.getSourceInstance(),
              waveletName,
              wavelet.getSourceInstance().getWaveLink(waveletName.waveId),
              // Let's assume that participant 0 is the creator even if that's not always true.
              // Participant lists can be empty.
              wavelet.getDigest().getParticipantSize() == 0
                  ? "<unknown>"
                  : wavelet.getDigest().getParticipant(0),
              wavelet.getDigest().getTitle(),
              "" + new LocalDate(new Instant(wavelet.getDigest().getLastModifiedMillis())),
              importsInProgress.containsEntry(Pair.of(wavelet.getSourceInstance(), waveletName),
                  ImportSharingMode.PRIVATE)
                  || importsInProgress.containsEntry(Pair.of(wavelet.getSourceInstance(),
                      waveletName), ImportSharingMode.PRIVATE_UNLESS_PARTICIPANT),
              wavelet.getPrivateLocalId() == null ? null : wavelet.getPrivateLocalId().getId(),
              wavelet.getPrivateLocalId() == null ? null
                  : makeLocalWaveLink(wavelet.getPrivateLocalId()),
              importsInProgress.containsEntry(Pair.of(wavelet.getSourceInstance(), waveletName),
                  ImportSharingMode.SHARED)
                  || importsInProgress.containsEntry(Pair.of(wavelet.getSourceInstance(),
                      waveletName), ImportSharingMode.PRIVATE_UNLESS_PARTICIPANT),
              wavelet.getSharedLocalId() == null ? null : wavelet.getSharedLocalId().getId(),
              wavelet.getSharedLocalId() == null ? null
                  : makeLocalWaveLink(wavelet.getSharedLocalId())));
    }
    return out.build();
  }

  private String getInstanceSelectionHtml() {
    StringBuilder out = new StringBuilder();
    boolean first = true;
    for (SourceInstance instance : sourceInstanceFactory.getInstances()) {
      String instanceId = instance.serialize();
      Assert.check(instanceId.matches("[a-zA-Z_.]+"),
          "Bad characters in instance id: %s", instance);
      out.append("<input type='radio'" + (first ? " checked='checked'" : "")
          + " name='instance' value='" + instanceId + "'/> "
          + HtmlEscaper.HTML_ESCAPER.escape(instance.getLongName())
          + "<br/>");
      first = false;
    }
    return out.toString();
  }

  private List<String> describeTasks(List<ImportTask> tasks) {
    ImmutableList.Builder<String> out = ImmutableList.builder();
    for (ImportTask task : tasks) {
      out.add(taskDispatcher.describeTask(task));
    }
    return out.build();
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    if (!userContext.hasOAuthCredentials()) {
      throw new NeedNewOAuthTokenException("No OAuth credentials: " + userContext);
    }
    Pair<List<String>, List<ImportWaveletDisplayRecord>> pair;
    try {
      pair = new RetryHelper().run(
          new RetryHelper.Body<Pair<List<String>, List<ImportWaveletDisplayRecord>>>() {
            @Override public Pair<List<String>, List<ImportWaveletDisplayRecord>> run()
                throws RetryableFailure, PermanentFailure {
              CheckedTransaction tx = datastore.get().beginTransaction();
              try {
                List<ImportTask> tasksInProgress = perUserTable.get().getAllTasks(tx, userId);
                return Pair.of(describeTasks(tasksInProgress),
                    getWaves(tx, taskDispatcher.waveletImportsInProgress(tasksInProgress)));
              } finally {
                tx.rollback();
              }
            }
          });
    } catch (PermanentFailure e) {
      throw new IOException("PermanentFailure retrieving import records", e);
    }
    List<String> tasksInProgress = pair.getFirst();
    List<ImportWaveletDisplayRecord> waveDisplayRecords = pair.getSecond();
    final String instanceSelectionHtml = getInstanceSelectionHtml();
    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    pageSkinWriter.write("Walkaround Import", participantId.getAddress(),
        ImportOverviewFragment.getGxpClosure(
            participantId.getAddress(),
            xsrfHelper.createToken(XSRF_ACTION),
            new HtmlClosure() {
              @Override public void write(Appendable out, GxpContext context) throws IOException {
                out.append(instanceSelectionHtml);
              }
            },
            tasksInProgress,
            waveDisplayRecords));
  }

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    if (!userContext.hasOAuthCredentials()) {
      throw new NeedNewOAuthTokenException("POST with no OAuth credentials: " + userContext);
    }
    try {
      xsrfHelper.verify(XSRF_ACTION, requireParameter(req, "token"));
    } catch (XsrfTokenExpiredException e) {
      throw new BadRequestException(e);
    } catch (InvalidSecurityTokenException e) {
      throw new BadRequestException(e);
    }
    String action = requireParameter(req, "action");
    if ("findwaves".equals(action) || "findandimport".equals(action)) {
      SourceInstance instance =
          sourceInstanceFactory.parseUnchecked(requireParameter(req, "instance"));
      // Rather than enqueueing just one interval 2008-01-01 to 2013-01-01, we
      // split that interval into random parts.  See the note on randomization
      // in FindRemoteWavesProcessor.
      log.info("Enqueueing find waves tasks");
      @Nullable ImportSettings autoImportSettings;
      if ("findwaves".equals(action)) {
        autoImportSettings = null;
      } else {
        autoImportSettings = new ImportSettingsGsonImpl();
        autoImportSettings.setSynthesizeHistory(!preserveHistory);
        if ("private".equals(requireParameter(req, "sharingmode"))) {
          autoImportSettings.setSharingMode(ImportSharingMode.PRIVATE);
        } else if ("shared".equals(requireParameter(req, "sharingmode"))) {
          autoImportSettings.setSharingMode(ImportSharingMode.SHARED);
        } else if ("privateunlessparticipant".equals(requireParameter(req, "sharingmode"))) {
          autoImportSettings.setSharingMode(ImportSharingMode.PRIVATE_UNLESS_PARTICIPANT);
        } else {
          throw new BadRequestException("Bad sharingmode");
        }
      }
      enqueueTasks(findProcessor.get().makeRandomTasksForInterval(instance,
            DaysSinceEpoch.fromYMD(2008, 1, 1),
            DaysSinceEpoch.fromYMD(2013, 1, 1),
            autoImportSettings));
    } else if ("importwavelet".equals(action)) {
      SourceInstance instance =
          sourceInstanceFactory.parseUnchecked(requireParameter(req, "instance"));
      WaveId waveId = WaveId.deserialise(requireParameter(req, "waveid"));
      WaveletId waveletId = WaveletId.deserialise(requireParameter(req, "waveletid"));
      ImportWaveletTask task = new ImportWaveletTaskGsonImpl();
      task.setInstance(instance.serialize());
      task.setWaveId(waveId.serialise());
      task.setWaveletId(waveletId.serialise());
      ImportSettings settings = new ImportSettingsGsonImpl();
      if ("private".equals(requireParameter(req, "sharingmode"))) {
        settings.setSharingMode(ImportSettings.ImportSharingMode.PRIVATE);
      } else if ("shared".equals(requireParameter(req, "sharingmode"))) {
        settings.setSharingMode(ImportSettings.ImportSharingMode.SHARED);
      } else {
        throw new BadRequestException("Unexpected import sharing mode");
      }
      settings.setSynthesizeHistory(!preserveHistory);
      task.setSettings(settings);
      @Nullable String existingSlobIdToIgnore = optionalParameter(req, "ignoreexisting", null);
      if (existingSlobIdToIgnore != null) {
        task.setExistingSlobIdToIgnore(existingSlobIdToIgnore);
      }
      final ImportTaskPayload payload = new ImportTaskPayloadGsonImpl();
      payload.setImportWaveletTask(task);
      log.info("Enqueueing import task for " + waveId
          + "; synthesizeHistory=" + task.getSettings().getSynthesizeHistory());
      enqueueTasks(ImmutableList.of(payload));
    } else if ("canceltasks".equals(action)) {
      log.info("Cancelling all tasks for " + userId);
      try {
        new RetryHelper().run(new RetryHelper.VoidBody() {
          @Override public void run() throws RetryableFailure, PermanentFailure {
            CheckedTransaction tx = datastore.get().beginTransaction();
            try {
              if (perUserTable.get().deleteAllTasks(tx, userId)) {
                tx.commit();
              }
            } finally {
              tx.close();
            }
          }
        });
      } catch (PermanentFailure e) {
        throw new IOException("Failed to delete tasks", e);
      }
    } else if ("forgetwaves".equals(action)) {
      log.info("Forgetting all waves for " + userId);
      try {
        new RetryHelper().run(new RetryHelper.VoidBody() {
          @Override public void run() throws RetryableFailure, PermanentFailure {
            CheckedTransaction tx = datastore.get().beginTransaction();
            try {
              if (perUserTable.get().deleteAllWaves(tx, userId)) {
                tx.commit();
              }
            } finally {
              tx.close();
            }
          }
        });
      } catch (PermanentFailure e) {
        throw new IOException("Failed to delete tasks", e);
      }
    } else {
      throw new BadRequestException("Unknown action: " + action);
    }
    // TODO(ohler): Send 303, not 302.  See
    // http://en.wikipedia.org/wiki/Post/Redirect/Get .
    resp.sendRedirect(req.getServletPath());
  }

  private void enqueueTasks(final List<ImportTaskPayload> payloads) throws IOException {
    try {
      new RetryHelper().run(new RetryHelper.VoidBody() {
        @Override public void run() throws RetryableFailure, PermanentFailure {
          CheckedTransaction tx = datastore.get().beginTransaction();
          try {
            for (ImportTaskPayload payload : payloads) {
              perUserTable.get().addTask(tx, userId, payload);
            }
            tx.commit();
          } finally {
            tx.close();
          }
        }
      });
    } catch (PermanentFailure e) {
      throw new IOException("Failed to enqueue import task", e);
    }
  }

}
TOP

Related Classes of com.google.walkaround.wave.server.googleimport.ImportOverviewHandler

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.