Package org.waveprotocol.box.server.robots.passive

Source Code of org.waveprotocol.box.server.robots.passive.RobotsGateway

/**
* Copyright 2010 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 org.waveprotocol.box.server.robots.passive;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.wave.api.RobotSerializer;
import com.google.wave.api.data.converter.EventDataConverterManager;
import com.google.wave.api.robot.CapabilityFetchException;
import com.google.wave.api.robot.RobotName;

import org.waveprotocol.box.common.DeltaSequence;
import org.waveprotocol.box.server.account.AccountData;
import org.waveprotocol.box.server.account.RobotAccountData;
import org.waveprotocol.box.server.persistence.AccountStore;
import org.waveprotocol.box.server.persistence.PersistenceException;
import org.waveprotocol.box.server.robots.operations.NotifyOperationService;
import org.waveprotocol.box.server.robots.util.ConversationUtil;
import org.waveprotocol.box.server.waveserver.WaveBus;
import org.waveprotocol.box.server.waveserver.WaveletProvider;
import org.waveprotocol.wave.model.id.WaveletName;
import org.waveprotocol.wave.model.operation.OperationException;
import org.waveprotocol.wave.model.operation.wave.AddParticipant;
import org.waveprotocol.wave.model.operation.wave.TransformedWaveletDelta;
import org.waveprotocol.wave.model.operation.wave.WaveletOperation;
import org.waveprotocol.wave.model.version.HashedVersion;
import org.waveprotocol.wave.model.wave.ParticipantId;
import org.waveprotocol.wave.model.wave.data.ReadableWaveletData;
import org.waveprotocol.wave.util.logging.Log;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;


/**
* Gateway for the Passive Robot API, this class can be subscribed to the
* WaveBus and fires of separate threads to handle any updates for Robots.
*
* @author ljvderijk@google.com (Lennard de Rijk)
*/
public class RobotsGateway implements WaveBus.Subscriber {

  private static final Log LOG = Log.get(RobotsGateway.class);

  private final WaveletProvider waveletProvider;
  private final AccountStore accountStore;
  private final EventDataConverterManager converterManager;
  private final RobotConnector connector;
  private final Map<RobotName, Robot> allRobots = Maps.newHashMap();
  private final Set<RobotName> runnableRobots = Sets.newHashSet();
  private final Executor executor;
  private final ConversationUtil conversationUtil;
  private final NotifyOperationService notifyOpService;

  @Inject
  @VisibleForTesting
  RobotsGateway(WaveletProvider waveletProvider, RobotConnector connector,
      AccountStore accountStore, RobotSerializer serializer,
      EventDataConverterManager converterManager, @Named("GatewayExecutor") Executor executor,
      ConversationUtil conversationUtil, NotifyOperationService notifyOpService) {
    this.waveletProvider = waveletProvider;
    this.accountStore = accountStore;
    this.converterManager = converterManager;
    this.connector = connector;
    this.executor = executor;
    this.conversationUtil = conversationUtil;
    this.notifyOpService = notifyOpService;
  }

  @Override
  public void waveletCommitted(WaveletName waveletName, HashedVersion version) {
    // We ignore this event.
  }

  @Override
  public void waveletUpdate(ReadableWaveletData wavelet, DeltaSequence deltas) {
    Set<ParticipantId> currentAndNewParticipants = Sets.newHashSet(wavelet.getParticipants());
    for (TransformedWaveletDelta delta : deltas) {
      // Participants added or removed in this delta get the whole delta.
      for (WaveletOperation op : delta) {
        if (op instanceof AddParticipant) {
          ParticipantId p = ((AddParticipant) op).getParticipantId();
          currentAndNewParticipants.add(p);
        }
      }
    }
    // Robot should receive also deltas that contain AddParticipant ops.
    // EventGenerator will take care to filter out events before the add.
    for (ParticipantId participant : currentAndNewParticipants) {
      RobotName robotName = RobotName.fromAddress(participant.getAddress());
      if (robotName == null) {
        // Not a valid robot name, next.
        continue;
      }

      ParticipantId robotId = ParticipantId.ofUnsafe(robotName.toEmailAddress());
      AccountData account;
      try {
        account = accountStore.getAccount(robotId);
      } catch (PersistenceException e) {
        LOG.severe("Failed to retrieve the account data for " + robotId.getAddress(), e);
        continue;
      }

      if (account != null && account.isRobot()) {
        RobotAccountData robotAccount = account.asRobot();
        if (robotAccount.isVerified()) {
          Robot robot = getOrCreateRobot(robotName, robotAccount);
          updateRobot(robot, wavelet, deltas);
        }
      }
    }
  }

  /**
   * Gets or creates a {@link Robot} for the given name.
   *
   * @param robotName the name of the robot.
   * @param account the {@link RobotAccountData} belonging to the given
   *        {@link RobotName}.
   */
  private Robot getOrCreateRobot(RobotName robotName, RobotAccountData account) {
    Robot robot = allRobots.get(robotName);

    if (robot == null) {
      robot = createNewRobot(robotName, account);
      allRobots.put(robotName, robot);
    }
    return robot;
  }

  /**
   * Creates a new {@link Robot}.
   *
   * @param robotName the name of the robot.
   * @param account the {@link RobotAccountData} belonging to the given
   *        {@link RobotName}.
   */
  private Robot createNewRobot(RobotName robotName, RobotAccountData account) {
    EventGenerator eventGenerator = new EventGenerator(robotName, conversationUtil);
    RobotOperationApplicator operationApplicator =
        new RobotOperationApplicator(converterManager, waveletProvider,
            new OperationServiceRegistryImpl(notifyOpService), conversationUtil);
    return new Robot(robotName, account, this, connector, converterManager, waveletProvider,
        eventGenerator, operationApplicator);
  }

  /**
   * Updates a {@link Robot} with information about a waveletUpdate event.
   *
   * @param robot The robot to process the update for.
   * @param wavelet the wavelet on which the update is occuring.
   * @param deltas the deltas the have been applied to the given wavelet.
   */
  private void updateRobot(Robot robot, ReadableWaveletData wavelet, DeltaSequence deltas) {
    try {
      robot.waveletUpdate(wavelet, deltas);
      ensureScheduled(robot);
    } catch (OperationException e) {
      LOG.warning("Unable to update robot(" + robot.getRobotName() + ")", e);
    }
  }

  /**
   * Ensures that a robot is submitted to the executor, might submit the
   * {@link Robot} if it hasn't been submitted yet.
   *
   * <p>
   * Synchronized in combination with done() to keep proper track of the robots
   * that have been submitted.
   *
   * @param robot the {@link Robot} to enqueue
   */
  public synchronized void ensureScheduled(Robot robot) {
    if (!runnableRobots.contains(robot.getRobotName())) {
      LOG.info("Enqueing robot: " + robot.getRobotName());
      runnableRobots.add(robot.getRobotName());
      executor.execute(robot);
    }
  }

  /**
   * Signal that a robot is done running. Synchronized with ensureRunnable since
   * that method needs to have a synchronized view on the runnableRobots for
   * submitting task to the executor.
   *
   * @param robot the {@link Robot} which is done working.
   */
  public synchronized void doneRunning(Robot robot) {
    runnableRobots.remove(robot.getRobotName());
  }

  /**
   * Updates the account for the given {@link Robot}.
   *
   * @param robot the {@link Robot} to update.
   * @throws CapabilityFetchException if the capabilities could not be fetched
   *         or parsed.
   */
  public void updateRobotAccount(Robot robot) throws CapabilityFetchException,
      PersistenceException {
    // TODO: Pass in activeAPIUrl
    String activeApiUrl = "";
    RobotAccountData newAccount = connector.fetchCapabilities(robot.getAccount(), activeApiUrl);
    accountStore.putAccount(newAccount);
    robot.setAccount(newAccount);
  }
}
TOP

Related Classes of org.waveprotocol.box.server.robots.passive.RobotsGateway

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.