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