View Javadoc

1   /**
2    * Copyright 2011-2013 The Kuali Foundation Licensed under the Educational
3    * Community License, Version 2.0 (the "License"); you may not use this file
4    * except in compliance with the License. You may obtain a copy of the License
5    * at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12   * License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  package org.kuali.mobility.push.service.send;
16  
17  import org.apache.log4j.Logger;
18  import org.kuali.mobility.push.entity.Device;
19  import org.kuali.mobility.push.entity.Preference;
20  import org.kuali.mobility.push.entity.Push;
21  import org.kuali.mobility.push.service.PreferenceService;
22  import org.kuali.mobility.push.service.SendService;
23  import org.springframework.beans.factory.annotation.Autowired;
24  import org.springframework.stereotype.Service;
25  
26  import java.util.*;
27  import java.util.concurrent.ExecutorService;
28  import java.util.concurrent.Executors;
29  
30  /**
31   * This implementation of the <code>SendService</code>
32   * Can send Push notifications to a configured set of Operating systems.
33   * 
34   * At this moment, this service will not perform any persistence of messages sent, nor will the 
35   * implementations that the messages will be delegated to. The persist message being sent, rather
36   * call the <code>PushService</code> which does persistence, and uses this service to perform the
37   * actual sending of messages.
38   * 
39   * This service will maintains a thread pool and notifications are put in a queue to be sent 
40   * (it uses <codde>Executors.newCachedThreadPool()</code>)
41   * 
42   * @author Kuali Mobility Team (mobility.dev@kuali.org)
43   * @since 2.2.0
44   */
45  @Service("sendServiceDelegator")
46  public class SendServiceDelegator implements SendService {
47      private static final Logger LOG = Logger.getLogger(SendServiceDelegator.class);
48  
49  	/** A list of senders that this service can use */
50  	private final Map<String, SendService> senders = new HashMap<String, SendService>();
51  
52  	/** A cached thread pool which allows us to queue notification tasks */
53  	private final ExecutorService exeService = Executors.newCachedThreadPool();
54  
55  	/** A PreferenceService object used for checking if Sender is blocked for a given user. */
56  	@Autowired
57  	private PreferenceService preferenceService;
58  	
59  	/**
60  	 * Sets the senders that this SendService can send to.
61  	 * This is typically set using Spring
62  	 * @param senders
63  	 */
64  	public void setSenders(Map<String, SendService> senders){
65  		this.senders.putAll(senders);
66  	}
67  
68  
69  	/**
70  	 * Gets the map of senders
71  	 * @return returns a map of available sender services
72  	 */
73  	private Map<String, SendService> getSenders(){
74  		return this.senders;
75  	}
76  
77  
78  	/**
79  	 * Returns a list of device types that this <code>SendServiceDelegator</code>supports
80  	 * @return
81  	 */
82  	public List<String> getSupportedDeviceTypes(){
83  		return new Vector<String>(this.senders.keySet());
84  	}
85  
86  
87  	/**
88  	 * This method will delegate the push notification to the appropriate implementation for the destination device
89  	 * The message will first be queued in this class, before being delegated to the implementation in a
90  	 * seperate thread.
91  	 */
92  	public void sendPush(Push push, Device device){
93  		this.exeService.execute(new SendRunner(push, device));
94  	}
95  
96  
97  	/**
98  	 * This method will deletegate the push message to the appropriate implementations for the destination devices.
99  	 * This message will first be queued in this service, before being delegated to the different implementations
100 	 */
101 	public void sendPush(Push push, List<Device> devices){
102 		this.exeService.execute(new SendRunner(push, devices));
103 	}
104 
105 	/**
106 	 * This method is used to set the <code>PreferenceService</code> for the <code>SendServiceDelegator</code> object. 
107 	 * 
108 	 * @param preferenceService
109 	 */
110 	public void setPreferenceService(PreferenceService preferenceService) {
111 		this.preferenceService = preferenceService;
112 	}
113 
114 
115 	/**
116 	 * A runnable that will be used to send the notification using a device
117 	 * specific implementation of the send service.
118 	 */
119 	private class SendRunner implements Runnable{
120 
121 		/** The Push message to be sent */
122 		private Push push;
123 
124 		/** List of devices that the message should be sent to */
125 		private List<Device> devices;
126 
127 		/**
128 		 * Creates a new instance of the <code>SendRunner</code>
129 		 * @param push Push message to send
130 		 * @param device The device to sent to
131 		 */
132 		public SendRunner(Push push, Device device){
133 			this(push, Collections.nCopies(1, device));
134 		}
135 
136 
137 		/**
138 		 * Creates a new instance of the <code>SendRunner</code>
139 		 * @param push Push message to send.
140 		 * @param devices List of devices the message should be sent to.
141 		 */
142 		public SendRunner(Push push, List<Device> devices){
143 			this.push = push;
144 			this.devices = devices;
145 		}
146 
147 
148 		/*
149 		 * (non-Javadoc)
150 		 * @see java.lang.Runnable#run()
151 		 */
152 		@Override
153 		public void run() {
154 			for(String type : senders.keySet()){
155 				List<Device> deviceList = getDevicesOfType(type, devices);
156 				if (deviceList.size() > 0){
157 					SendService sender = getSenders().get(type);
158 					if (sender != null){
159 						sender.sendPush(push, deviceList);
160 					}
161 				}
162 			}
163 		}
164 
165 
166 		/**
167 		 * Filters all the devices of a specific type into a list.
168 		 * @param deviceType
169 		 * @param devices
170 		 * @return
171 		 */
172 		private List<Device> getDevicesOfType(String deviceType, List<Device> devices){
173 			List<Device> deviceList = new ArrayList<Device>();
174 			for(Device d : devices){
175 				if (deviceType.equals(d.getType())){			
176 					
177 					Preference preference = preferenceService.findPreference(d.getUsername(), push.getSender());
178 					if(preference == null || !preference.isSenderBlocked()){ 					
179 						deviceList.add(d);
180 					} else {
181                         LOG.debug("Device excluded from push by preferences: "+d.getDeviceId());
182                     }
183 					
184 				}
185 			}
186 			return deviceList;
187 		}
188 
189 	}
190 }