@Override
  public UrlyBirdRoomOffer bookRoomOffer(final BookRoomCommand command) throws Exception {

    checkNotNull(command, "command");

    final RoomOffer clientRoomToBook = command.getRoomToBook();
    final int roomOfferIndex = clientRoomToBook.getIndex();

    long lock = NOT_LOCKED;
    try {
      lock = roomOfferDao.lock(roomOfferIndex);
      final RoomOffer dbRoomToBook = roomOfferDao.read(roomOfferIndex);

      checkStaleRoomData(clientRoomToBook, dbRoomToBook);

      if (!isRoomBookable.isSatisfiedBy(dbRoomToBook)) {
        throw new Exception("the room to book: " + clientRoomToBook + " is already booked");
      }

      final UrlyBirdRoomOffer bookedRoomOffer =
          factory.copyRoomOfferWithNewCustomer(clientRoomToBook, command.getCustomerId());
      roomOfferDao.update(bookedRoomOffer, lock);
      return bookedRoomOffer;
    } catch (final Exception e) {
      logger.throwing(getClass().getName(), "bookRoomOffer", e);
      throw new Exception(e);
    } finally {
      unlockQuietly(roomOfferIndex, lock);
    }
  }
  @Override
  public UrlyBirdRoomOffer updateRoomOffer(final UpdateRoomCommand command) throws Exception {

    checkNotNull(command, "command");

    long lock = NOT_LOCKED;
    final List<String> values = command.getNewValues();
    final RoomOffer clientRoomToUpdate = command.getRoomToUpdate();
    final int index = clientRoomToUpdate.getIndex();
    try {
      lock = roomOfferDao.lock(index);

      final RoomOffer dbRoomToUpdate = roomOfferDao.read(index);
      checkStaleRoomData(clientRoomToUpdate, dbRoomToUpdate);

      final UrlyBirdRoomOffer updatedRoomOffer = factory.createRoomOffer(values, index);

      roomOfferDao.update(updatedRoomOffer, lock);
      return updatedRoomOffer;
    } catch (final Exception e) {
      logger.throwing(getClass().getName(), "updateRoomOffer", e);
      throw new Exception(e);
    } finally {
      unlockQuietly(index, lock);
    }
  }
  /**
   * Checks if the given clientRoom was changed in the meantime by another client and throws an
   * {@link ConcurrentModificationException} in this case. This method guards against the lost
   * update problem in an concurrent multi-client environment.
   *
   * @param clientRoom the given {@link RoomOffer} from the client, which could be stale data.
   * @param dbRoom the actually in the database store room.
   * @throws ConcurrentUpdateException if the actually stored room in the database is not equal to
   *     the clientRoom.
   */
  private void checkStaleRoomData(final RoomOffer clientRoom, final RoomOffer dbRoom)
      throws ConcurrentUpdateException {

    if (!clientRoom.equals(dbRoom)) {
      throw new ConcurrentModificationException(
          "The room with the id:"
              + clientRoom.getIndex()
              + " was concurrently updated by another client. Please refresh your data view and try your changes again.");
    }
  }
  @Override
  public int deleteRoomOffer(final DeleteRoomCommand command) throws Exception {

    checkNotNull(command, "command");

    final RoomOffer clientRoomToDelete = command.getRoomOfferToDelete();
    final int index = clientRoomToDelete.getIndex();
    long lock = NOT_LOCKED;
    try {
      lock = roomOfferDao.lock(index);
      final RoomOffer dbRoomToDelete = roomOfferDao.read(index);

      checkStaleRoomData(clientRoomToDelete, dbRoomToDelete);

      roomOfferDao.delete(index, lock);
      return index;
    } catch (final Exception e) {
      logger.throwing(getClass().getName(), "deleteRoomOffer", e);
      throw new Exception(e);
    } finally {
      unlockQuietly(index, lock);
    }
  }