content: /*
   *  Copyright (C) 2010-2012 Stichting Akvo (Akvo Foundation)
   *
   *  This file is part of Akvo FLOW.
   *
   *  Akvo FLOW is free software: you can redistribute it and modify it under the terms of
   *  the GNU Affero General Public License (AGPL) as published by the Free Software Foundation,
   *  either version 3 of the License or any later version.
   *
   *  Akvo FLOW is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   *  See the GNU Affero General Public License included below for more details.
   *
   *  The full license text can also be seen at <http://www.gnu.org/licenses/agpl.html>.
   */
  
  package com.gallatinsystems.notification.helper;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  
  import com.gallatinsystems.notification.NotificationRequest;
  import com.gallatinsystems.notification.dao.NotificationSubscriptionDao;
  import com.gallatinsystems.notification.domain.NotificationSubscription;
  import com.google.appengine.api.taskqueue.Queue;
  import com.google.appengine.api.taskqueue.QueueFactory;
  import com.google.appengine.api.taskqueue.TaskOptions;
  
  /**
   * Finds current notification subscriptions from the data store and spawns async jobs to generate
   * data and send the notification. To use this class you must define a queue called notification
   * 
   * @author Christopher Fagiani
   */
  public class NotificationHelper {
  
      private static final String UNSPECIFIED = "unspecified";
      private static final String QUEUE_NAME = "notification";
      private static final String PROCESSOR_URL = "/notificationprocessor";
      private String notificationType;
      private Long subscriptionEntityId;
      private Long notificationEntityId;
      private NotificationSubscriptionDao notificationDao;
  
      public NotificationHelper(String type, Long subEntityId) {
          this(type, subEntityId, subEntityId);
      }
  
      public NotificationHelper(String type, Long subEntityId, Long notifEntityId) {
          notificationDao = new NotificationSubscriptionDao();
          notificationType = type;
          this.subscriptionEntityId = subEntityId;
          notificationEntityId = subEntityId;
          if (notificationEntityId == null) {
              notificationEntityId = subscriptionEntityId;
          }
      }
  
      /**
       * this method will find all unexpired notification subscriptions, collate them and then
       * generate messages that will be placed on the notification processor queue (so the actual
       * notification can be handled by the appropriate notification handler class).
       */
      public void execute() {
          // find all notifications that have not yet expired
          List<NotificationSubscription> subs = notificationDao
                  .listSubscriptions(subscriptionEntityId, notificationType, true);
  
          // group the subs by entity id and type
          Map<String, Map<Long, List<NotificationSubscription>>> subMap = collateSubscriptions(subs);
          if (subMap != null) {
              // now spawn a notification job for each notificationType/entity
              // combo
              Queue queue = QueueFactory.getQueue(QUEUE_NAME);
              for (Entry<String, Map<Long, List<NotificationSubscription>>> entry : subMap
                      .entrySet()) {
                  for (Entry<Long, List<NotificationSubscription>> notifEntry : entry
                          .getValue().entrySet()) {
                      StringBuilder builder = new StringBuilder();
                      StringBuilder optBuilder = new StringBuilder();
                      if (notifEntry.getValue() != null
                              && notifEntry.getValue().size() > 0) {
                          for (int i = 0; i < notifEntry.getValue().size(); i++) {
                              if (i > 0) {
                                  builder.append(NotificationRequest.DELIMITER);
                                  optBuilder
                                          .append(NotificationRequest.DELIMITER);
                              }
                              builder.append(notifEntry.getValue().get(i)
                                      .getNotificationDestination());
                              String opt = notifEntry.getValue().get(i)
                                      .getNotificationOption();
                              optBuilder.append(opt != null ? opt : UNSPECIFIED);
                          }
                          // now dump the item on the queue
                          TaskOptions opts = TaskOptions.Builder
                                  .withUrl(PROCESSOR_URL)
                                  .param(NotificationRequest.DEST_PARAM,
                                          builder.toString())
                                  .param(NotificationRequest.DEST_OPT_PARAM,
                                          optBuilder.toString())
                                  .param(NotificationRequest.SUB_ENTITY_PARAM,
                                          notifEntry.getKey().toString())
                                  .param(NotificationRequest.TYPE_PARAM,
                                          entry.getKey());
                          if (notificationEntityId != null) {
                              opts.param(NotificationRequest.NOTIF_ENTITY_PARAM,
                                      notificationEntityId.toString());
                          }
                          queue.add(opts);
                      }
                  }
              }
          }
      }
  
      /**
       * this method will collate subscriptions based on notification type and delivery type. this
       * allows us to reduce processing by generating a list of all addresses to be notified via a
       * specific delivery mechanism
       * 
       * @param subs
       * @return
       */
      private Map<String, Map<Long, List<NotificationSubscription>>> collateSubscriptions(
              List<NotificationSubscription> subs) {
          Map<String, Map<Long, List<NotificationSubscription>>> subMap = new HashMap<String, Map<Long, List<NotificationSubscription>>>();
          if (subs != null) {
              for (NotificationSubscription sub : subs) {
                  Map<Long, List<NotificationSubscription>> tempMap = subMap
                          .get(sub.getNotificationType());
                  if (tempMap == null) {
                      tempMap = new HashMap<Long, List<NotificationSubscription>>();
                      subMap.put(sub.getNotificationType(), tempMap);
                  }
                  List<NotificationSubscription> tempList = tempMap.get(sub
                          .getEntityId());
                  if (tempList == null) {
                      tempList = new ArrayList<NotificationSubscription>();
                      tempMap.put(sub.getEntityId(), tempList);
                  }
                  tempList.add(sub);
              }
          }
          return subMap;
      }
  }
  