Coverage Report - org.kuali.rice.ken.service.impl.NotificationServiceImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
NotificationServiceImpl
0%
0/95
0%
0/32
3.556
 
 1  
 /*
 2  
  * Copyright 2007 The Kuali Foundation
 3  
  * 
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  * 
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  * 
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.ken.service.impl;
 17  
 
 18  
 import java.io.IOException;
 19  
 import java.sql.Timestamp;
 20  
 import java.util.Collection;
 21  
 import java.util.HashMap;
 22  
 
 23  
 import org.apache.ojb.broker.query.Criteria;
 24  
 import org.kuali.rice.core.dao.GenericDao;
 25  
 import org.kuali.rice.core.util.RiceConstants;
 26  
 import org.kuali.rice.ken.bo.Notification;
 27  
 import org.kuali.rice.ken.bo.NotificationMessageDelivery;
 28  
 import org.kuali.rice.ken.bo.NotificationRecipient;
 29  
 import org.kuali.rice.ken.bo.NotificationResponse;
 30  
 import org.kuali.rice.ken.deliverer.impl.KEWActionListMessageDeliverer;
 31  
 import org.kuali.rice.ken.exception.InvalidXMLException;
 32  
 import org.kuali.rice.ken.service.NotificationAuthorizationService;
 33  
 import org.kuali.rice.ken.service.NotificationMessageContentService;
 34  
 import org.kuali.rice.ken.service.NotificationMessageDeliveryService;
 35  
 import org.kuali.rice.ken.service.NotificationRecipientService;
 36  
 import org.kuali.rice.ken.service.NotificationService;
 37  
 import org.kuali.rice.ken.service.NotificationWorkflowDocumentService;
 38  
 import org.kuali.rice.ken.util.NotificationConstants;
 39  
 
 40  
 /**
 41  
  * NotificationService implementation - this is the default out-of-the-box implementation of the service.
 42  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 43  
  */
 44  
 public class NotificationServiceImpl implements NotificationService {
 45  0
     private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
 46  
         .getLogger(NotificationServiceImpl.class);
 47  
     
 48  
     private GenericDao businessObjectDao;
 49  
     private NotificationMessageContentService messageContentService;
 50  
     private NotificationAuthorizationService notificationAuthorizationService;
 51  
     private NotificationRecipientService notificationRecipientService;
 52  
     private NotificationWorkflowDocumentService notificationWorkflowDocumentService;
 53  
     private NotificationMessageDeliveryService notificationMessageDeliveryService;
 54  
     
 55  
     /**
 56  
      * Constructs a NotificationServiceImpl class instance.
 57  
      * @param businessObjectDao
 58  
      * @param messageContentService
 59  
      * @param notificationAuthorizationService
 60  
      * @param notificationRecipientService
 61  
      * @param notificationWorkflowDocumentService
 62  
      * @param notificationMessageDeliveryService
 63  
      */
 64  
     public NotificationServiceImpl(GenericDao businessObjectDao, NotificationMessageContentService messageContentService, 
 65  
             NotificationAuthorizationService notificationAuthorizationService, NotificationRecipientService notificationRecipientService, 
 66  
             NotificationWorkflowDocumentService notificationWorkflowDocumentService, 
 67  0
             NotificationMessageDeliveryService notificationMessageDeliveryService) {
 68  0
         this.businessObjectDao = businessObjectDao;
 69  0
         this.messageContentService = messageContentService;
 70  0
         this.notificationAuthorizationService = notificationAuthorizationService;
 71  0
         this.notificationRecipientService = notificationRecipientService;
 72  0
         this.notificationWorkflowDocumentService = notificationWorkflowDocumentService;
 73  0
         this.notificationMessageDeliveryService = notificationMessageDeliveryService;
 74  0
     }
 75  
 
 76  
     /**
 77  
      * This is the default implementation that uses the businessObjectDao.
 78  
      * @see org.kuali.rice.ken.service.NotificationService#getNotification(java.lang.Long)
 79  
      */
 80  
     public Notification getNotification(Long id) {
 81  0
         HashMap<String, Long> primaryKeys = new HashMap<String, Long>();
 82  0
         primaryKeys.put(NotificationConstants.BO_PROPERTY_NAMES.ID, id);
 83  
         
 84  0
         return (Notification) businessObjectDao.findByPrimaryKey(Notification.class, primaryKeys);
 85  
     }
 86  
 
 87  
     /**
 88  
      * This method is responsible for parsing out the notification message which is sent in as a String 
 89  
      * of XML.  It calls the appropriate services to validate the message content, converts it to a BO, 
 90  
      * and then passes it to another service where its content and meta-data is validated and if successful, it 
 91  
      * is saved.
 92  
      * @see org.kuali.rice.ken.service.NotificationService#sendNotification(java.lang.String)
 93  
      */
 94  
     public NotificationResponse sendNotification(String notificationMessageAsXml) throws IOException, InvalidXMLException {
 95  
         // try to parse out the XML with the message content service
 96  0
         Notification notification = messageContentService.parseNotificationRequestMessage(notificationMessageAsXml);
 97  
 
 98  
         // now call out to the meat of the notification sending - this will validate users, groups, producers, and save
 99  0
         return sendNotification(notification);
 100  
     }
 101  
 
 102  
     /**
 103  
      * @see org.kuali.rice.ken.service.NotificationService#sendNotification(org.kuali.rice.ken.bo.Notification)
 104  
      */
 105  
     public NotificationResponse sendNotification(Notification notification) {
 106  0
         NotificationResponse response = new NotificationResponse();
 107  
         
 108  
         // make sure that the producer is able to send notifications on behalf of the channel
 109  0
         boolean producerAuthorizedForChannel = notificationAuthorizationService.isProducerAuthorizedToSendNotificationForChannel(notification.getProducer(), notification.getChannel());
 110  0
         if(!producerAuthorizedForChannel) {
 111  0
             LOG.error("Producer " + notification.getProducer() + " is not authorized to send messages to channel " + notification.getChannel());
 112  0
             response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 113  0
             response.setMessage(NotificationConstants.RESPONSE_MESSAGES.PRODUCER_NOT_AUTHORIZED_FOR_CHANNEL);
 114  0
             return response;
 115  
         }
 116  
         
 117  
         // make sure that the recipients are valid
 118  0
         for(int i = 0; i < notification.getRecipients().size(); i++) {
 119  0
             NotificationRecipient recipient = notification.getRecipient(i);
 120  0
             boolean validRecipient = notificationRecipientService.isRecipientValid(recipient.getRecipientId(), recipient.getRecipientType());
 121  0
             if(!validRecipient) {
 122  0
                     response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 123  0
                     response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_RECIPIENT + " - recipientId=" + 
 124  
                             recipient.getRecipientId() + ", recipientType=" + recipient.getRecipientType());
 125  0
                     return response;
 126  
             }
 127  
         }
 128  
         
 129  
         // set the creationDateTime attribute to the current timestamp if it's currently null
 130  0
         if (notification.getCreationDateTime() == null) {
 131  0
             notification.setCreationDateTime(new Timestamp(System.currentTimeMillis()));
 132  
         }
 133  
 
 134  
         // set the sendDateTime attribute to the current timestamp if it's currently null
 135  0
         if(notification.getSendDateTime() == null) {
 136  0
             notification.setSendDateTime(new Timestamp(System.currentTimeMillis()));
 137  
         }
 138  
         
 139  
         // if the autoremove time is before the send date time, reject the notification
 140  0
         if (notification.getAutoRemoveDateTime() != null) {
 141  0
             if (notification.getAutoRemoveDateTime().before(notification.getSendDateTime()))  {
 142  0
                     response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 143  0
                     response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_REMOVE_DATE);
 144  0
                 return response;
 145  
             }
 146  
         }
 147  
         
 148  
         // make sure the delivery types are valid
 149  0
         if(!notification.getDeliveryType().equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.ACK) && 
 150  
                 !notification.getDeliveryType().equalsIgnoreCase(NotificationConstants.DELIVERY_TYPES.FYI)) {
 151  0
             response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 152  0
             response.setMessage(NotificationConstants.RESPONSE_MESSAGES.INVALID_DELIVERY_TYPE + " - deliveryType=" + 
 153  
                             notification.getDeliveryType());
 154  0
             return response;
 155  
         }
 156  
         
 157  
         // now try to persist the object
 158  
         try {
 159  0
             businessObjectDao.save(notification);
 160  0
         } catch(Exception e) {
 161  0
             response.setStatus(NotificationConstants.RESPONSE_STATUSES.FAILURE);
 162  0
             response.setMessage(NotificationConstants.RESPONSE_MESSAGES.ERROR_SAVING_NOTIFICATION);
 163  0
             return response;
 164  0
         }
 165  
         
 166  
         // everything looks good!
 167  0
         response.setMessage(NotificationConstants.RESPONSE_MESSAGES.SUCCESSFULLY_RECEIVED);
 168  0
         response.setNotificationId(notification.getId());
 169  0
         return response;
 170  
     }
 171  
     
 172  
     /**
 173  
      * This is the default implementation that uses the businessObjectDao and its findMatching method.
 174  
      * @see org.kuali.rice.ken.service.NotificationService#getNotificationsForRecipientByType(java.lang.String, java.lang.String)
 175  
      */
 176  
     public Collection getNotificationsForRecipientByType(String contentTypeName, String recipientId) {
 177  0
         HashMap<String, String> queryCriteria = new HashMap<String, String>();
 178  0
         queryCriteria.put(NotificationConstants.BO_PROPERTY_NAMES.CONTENT_TYPE_NAME, contentTypeName);
 179  0
         queryCriteria.put(NotificationConstants.BO_PROPERTY_NAMES.RECIPIENTS_RECIPIENT_ID, recipientId);
 180  
         
 181  0
         return businessObjectDao.findMatching(Notification.class, queryCriteria);
 182  
     }
 183  
 
 184  
     /**
 185  
      * @see org.kuali.rice.ken.service.NotificationService#dismissNotificationMessageDelivery(java.lang.Long, java.lang.String)
 186  
      */
 187  
     public void dismissNotificationMessageDelivery(Long id, String user, String cause) {
 188  
         // TODO: implement pessimistic locking on the message delivery
 189  0
         NotificationMessageDelivery nmd = notificationMessageDeliveryService.getNotificationMessageDelivery(id);
 190  0
         dismissNotificationMessageDelivery(nmd, user, cause);
 191  0
     }
 192  
 
 193  
     /**
 194  
      * @see org.kuali.rice.ken.service.NotificationService#dismissNotificationMessageDelivery(org.kuali.rice.ken.bo.NotificationMessageDelivery, java.lang.String, java.lang.String)
 195  
      */   
 196  
     public void dismissNotificationMessageDelivery(NotificationMessageDelivery nmd, String user, String cause) {
 197  
         // get the notification that generated this particular message delivery
 198  0
         Notification notification = nmd.getNotification();
 199  
 
 200  
         // get all of the other deliveries of this notification for the user
 201  0
         Collection<NotificationMessageDelivery> userDeliveries = notificationMessageDeliveryService.getNotificationMessageDeliveries(notification, nmd.getUserRecipientId());
 202  
 
 203  
         final String targetStatus;
 204  
         // if the cause was our internal "autoremove" cause, then we need to indicate
 205  
         // the message was autoremoved instead of normally dismissed
 206  0
         if (NotificationConstants.AUTO_REMOVE_CAUSE.equals(cause)) {
 207  0
             targetStatus = NotificationConstants.MESSAGE_DELIVERY_STATUS.AUTO_REMOVED;
 208  
         } else {
 209  0
             targetStatus = NotificationConstants.MESSAGE_DELIVERY_STATUS.REMOVED;
 210  
         }
 211  
 
 212  0
         KEWActionListMessageDeliverer deliverer = new KEWActionListMessageDeliverer();
 213  
         // TODO: implement pessimistic locking on all these message deliveries
 214  
         // now, do dispatch in reverse...dismiss each message delivery via the appropriate deliverer
 215  0
         for (NotificationMessageDelivery messageDelivery: userDeliveries) {
 216  
 
 217  
             // don't attempt to dismiss undelivered message deliveries
 218  0
             if (!NotificationConstants.MESSAGE_DELIVERY_STATUS.DELIVERED.equals(messageDelivery.getMessageDeliveryStatus())) {
 219  0
                 LOG.info("Skipping dismissal of non-delivered message delivery #" + messageDelivery.getId());
 220  0
             } else if (targetStatus.equals(messageDelivery.getMessageDeliveryStatus())) {
 221  0
                 LOG.info("Skipping dismissal of already removed message delivery #" + messageDelivery.getId());
 222  
             } else {
 223  0
                 LOG.debug("Dismissing message delivery #" + messageDelivery.getId() + " " + messageDelivery.getLockVerNbr());
 224  
                 
 225  
                 // we have our message deliverer, so tell it to dismiss the message
 226  
                 //try {
 227  0
                     deliverer.dismissMessageDelivery(messageDelivery, user, cause);
 228  
                 //} catch (NotificationMessageDismissalException nmde) {
 229  
                     //LOG.error("Error dismissing message " + messageDelivery, nmde);
 230  
                     //throw new RuntimeException(nmde);
 231  
                 //}
 232  
             }
 233  
 
 234  
             // by definition we have succeeded at this point if no exception was thrown by the messageDeliverer
 235  
             // so update the status of the delivery message instance to indicate its dismissal
 236  
             // if the message delivery was not actually delivered in the first place, we still need to mark it as
 237  
             // removed here so delivery is not attempted again
 238  0
             messageDelivery.setMessageDeliveryStatus(targetStatus);
 239  
             // TODO: locking
 240  
             // mark as unlocked
 241  
             //messageDelivery.setLockedDate(null);
 242  0
             LOG.debug("Saving message delivery #" + messageDelivery.getId() + " " + messageDelivery.getLockVerNbr());
 243  0
             businessObjectDao.save(messageDelivery);
 244  
 
 245  0
             LOG.debug("Message delivery '" + messageDelivery.getId() + "' for notification '" + messageDelivery.getNotification().getId() + "' was successfully dismissed.");
 246  
         }
 247  0
     }
 248  
 
 249  
     /**
 250  
      * This method is responsible for atomically finding all untaken, unresolved notifications that are ready to be sent,
 251  
      * marking them as taken and returning them to the caller for processing.
 252  
      * NOTE: it is important that this method execute in a SEPARATE dedicated transaction; either the caller should
 253  
      * NOT be wrapped by Spring declarative transaction and this service should be wrapped (which is the case), or
 254  
      * the caller should arrange to invoke this from within a newly created transaction).
 255  
      * @return a list of available notifications that have been marked as taken by the caller
 256  
      */
 257  
     public Collection<Notification> takeNotificationsForResolution() {
 258  
         // get all unprocessed notifications with sendDateTime <= current
 259  0
         Criteria criteria = new Criteria();
 260  0
         criteria.addEqualTo(NotificationConstants.BO_PROPERTY_NAMES.PROCESSING_FLAG, NotificationConstants.PROCESSING_FLAGS.UNRESOLVED);
 261  0
         criteria.addLessOrEqualThan(NotificationConstants.BO_PROPERTY_NAMES.SEND_DATE_TIME, new Timestamp(System.currentTimeMillis()));
 262  0
         criteria.addIsNull(NotificationConstants.BO_PROPERTY_NAMES.LOCKED_DATE);
 263  
         //criteria = Util.makeSelectForUpdate(criteria);
 264  
         
 265  0
         Collection<Notification> available_notifications = businessObjectDao.findMatching(Notification.class, criteria, true, RiceConstants.NO_WAIT);
 266  
         
 267  0
         LOG.debug("Available notifications: " + available_notifications.size());
 268  
 
 269  
         // mark as "taken"
 270  0
         for (Notification notification: available_notifications) {
 271  0
             LOG.info("notification: " + notification);
 272  0
             notification.setLockedDate(new Timestamp(System.currentTimeMillis()));
 273  0
             businessObjectDao.save(notification);
 274  
         }
 275  
         
 276  0
         return available_notifications;
 277  
     }
 278  
 
 279  
     /**
 280  
      * Unlocks specified notification
 281  
      * @param notification the notification object to unlock
 282  
      */
 283  
     public void unlockNotification(Notification notification) {
 284  0
         Criteria criteria = new Criteria();
 285  0
         criteria.addEqualTo(NotificationConstants.BO_PROPERTY_NAMES.ID, notification.getId());
 286  
         //criteria = Util.makeSelectForUpdate(criteria);
 287  
 
 288  0
         Collection<Notification> notifications = businessObjectDao.findMatching(Notification.class, criteria, true, RiceConstants.NO_WAIT);
 289  0
         if (notifications == null || notifications.size() == 0) {
 290  0
             throw new RuntimeException("Notification #" + notification.getId() + " not found to unlock");
 291  
         }
 292  
 
 293  0
         Notification n = notifications.iterator().next();
 294  0
         n.setLockedDate(null);
 295  
         
 296  0
         businessObjectDao.save(n);
 297  0
     }
 298  
 }