@RequestMapping("EDIT")
  public void savePreferences(
      ActionRequest request,
      ActionResponse response,
      @RequestParam("topicsToUpdate") Integer topicsToUpdate)
      throws PortletException {

    List<TopicSubscription> newSubscription = new ArrayList<TopicSubscription>();

    for (int i = 0; i < topicsToUpdate; i++) {
      Long topicId = Long.valueOf(request.getParameter("topicId_" + i));

      // Will be numeric for existing, persisted TopicSubscription
      // instances;  blank (due to null id field) otherwise
      String topicSubId = request.getParameter("topicSubId_" + i).trim();

      Boolean subscribed = Boolean.valueOf(request.getParameter("subscribed_" + i));
      Topic topic = announcementService.getTopic(topicId);

      // Make sure that any pushed_forced topics weren't sneakingly removed (by tweaking the URL,
      // for example)
      if (topic.getSubscriptionMethod() == Topic.PUSHED_FORCED) {
        subscribed = new Boolean(true);
      }

      TopicSubscription ts = new TopicSubscription(request.getRemoteUser(), topic, subscribed);
      if (topicSubId.length() > 0) {
        // This TopicSubscription represents an existing, persisted entity
        try {
          ts.setId(Long.valueOf(topicSubId));
        } catch (NumberFormatException nfe) {
          logger.debug(nfe.getMessage(), nfe);
        }
      }

      newSubscription.add(ts);
    }

    if (newSubscription.size() > 0) {
      try {
        announcementService.addOrSaveTopicSubscription(newSubscription);
      } catch (Exception e) {
        logger.error(
            "ERROR saving TopicSubscriptions for user "
                + request.getRemoteUser()
                + ". Message: "
                + e.getMessage());
      }
    }

    response.setPortletMode(PortletMode.VIEW);
    response.setRenderParameter("action", "displayAnnouncements");
  }
  /**
   * Main method of this display controller. Calculates which topics should be shown to this user
   * and which announcements to show from those topics.
   *
   * @param model
   * @param request
   * @param from
   * @param to
   * @return
   * @throws PortletException
   */
  @SuppressWarnings("unchecked")
  @RequestMapping("VIEW")
  public String mainView(
      Model model,
      RenderRequest request,
      @RequestParam(value = "from", required = false) Integer from,
      @RequestParam(value = "to", required = false) Integer to)
      throws PortletException {

    PortletPreferences prefs = request.getPreferences();
    int pageSize = Integer.valueOf(prefs.getValue(PREFERENCE_PAGE_SIZE, "5"));
    ;

    if (from == null || to == null) {
      from = 0;
      to = pageSize;
    }

    boolean isGuest =
        (request.getRemoteUser() == null
            || request.getRemoteUser().equalsIgnoreCase(GUEST_USERNAME));
    logger.debug("isGuest is: " + Boolean.toString(isGuest));
    logger.debug("remoteUser is: " + request.getRemoteUser());

    List<Announcement> announcements;
    List<Announcement> emergencyAnnouncements;

    Element guestCacheElement = null;
    Element emergCacheElement = null;
    guestCacheElement = guestAnnouncementCache.get("guest");
    emergCacheElement = guestAnnouncementCache.get("emergency");

    if (!isGuest || (guestCacheElement == null || emergCacheElement == null)) {

      // create a new announcement list
      announcements = new ArrayList<Announcement>();
      emergencyAnnouncements = new ArrayList<Announcement>();

      // fetch the user's topic subscription from the database
      List<TopicSubscription> myTopics = tss.getTopicSubscription(request);

      // add all the published announcements of each subscribed topic to the announcement list
      // to emergency announcements into their own list
      for (TopicSubscription ts : myTopics) {
        if (ts.getSubscribed() && ts.getTopic().getSubscriptionMethod() != Topic.EMERGENCY) {
          announcements.addAll(ts.getTopic().getPublishedAnnouncements());
        } else if (ts.getSubscribed() && ts.getTopic().getSubscriptionMethod() == Topic.EMERGENCY) {
          emergencyAnnouncements.addAll(ts.getTopic().getPublishedAnnouncements());
        }
      }

      // sort the list (since they are not sorted from the database)
      Collections.sort(announcements);
      Collections.sort(emergencyAnnouncements);

      if (isGuest) {
        if (logger.isDebugEnabled()) logger.debug("Guest cache expired. Regenerating guest cache.");

        guestAnnouncementCache.put(new Element("guest", announcements));
        guestAnnouncementCache.put(new Element("emergency", emergencyAnnouncements));
      }
    } else {
      // we're a guest and we're within the cache timeout period, so return the cached announcements
      if (logger.isDebugEnabled()) logger.debug("Guest cache valid. Using guest cache.");
      announcements = (List<Announcement>) guestCacheElement.getObjectValue();
      emergencyAnnouncements = (List<Announcement>) emergCacheElement.getObjectValue();
    }

    // create a shortened list
    List<Announcement> announcementsShort = new ArrayList<Announcement>();

    // if the announcement list is already short, then just reference it
    if (announcements.size() < to - from) {
      announcementsShort = announcements;
    }
    // otherwise, just take the range requested and pass it along to the view
    else {
      for (int i = from; i < to && announcements.size() > i; i++) {
        if (announcements.get(i) != null) {
          announcementsShort.add(announcements.get(i));
        }
      }
    }

    // add a marker to the view to render different content for guest user
    model.addAttribute("isGuest", Boolean.valueOf(isGuest));

    // Disable the edit link where appropriate
    Boolean disableEdit = Boolean.valueOf(prefs.getValue(PREFERENCE_DISABLE_EDIT, "false"));
    model.addAttribute("disableEdit", disableEdit);

    model.addAttribute("showDate", showDate);
    model.addAttribute("from", new Integer(from));
    model.addAttribute("to", new Integer(to));
    model.addAttribute("hasMore", (announcements.size() > to));
    model.addAttribute("increment", new Integer(pageSize));
    model.addAttribute("announcements", announcementsShort);
    model.addAttribute("emergency", emergencyAnnouncements);

    return viewNameSelector.select(request, "displayAnnouncements");
  }