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.dao;
16  
17  import java.io.ByteArrayOutputStream;
18  import java.io.InputStream;
19  import java.sql.Timestamp;
20  
21  import javax.net.ssl.SSLSocket;
22  
23  import org.apache.commons.pool.impl.GenericObjectPool;
24  import org.apache.log4j.Logger;
25  import org.kuali.mobility.push.entity.Device;
26  import org.kuali.mobility.push.service.DeviceService;
27  import org.springframework.beans.factory.annotation.Autowired;
28  
29  /**
30   * A task which will run at a configured interval to check for notifications that has to be sent.
31   * 
32   * The regularity of this Monitor is configured via spring the property push.device.feedbackTimer in kme.config.properites.
33   * Current value for development and testing is 10 seconds (10000 ms). This should be set to something considerable higher
34   * in production so as to not flood logs or be a general nuisance. On the order of hours to days for systems with low device
35   * turnover rates or tens of minutes to hours for higher device turnonver institutions. 
36   * 
37   * </table>
38   * 
39   * @author Kuali Mobility Team (mobility.dev@kuali.org)
40   * @since 2.3.0
41   */
42  public class DeviceFeedbackMonitor {
43  
44  	/** A reference to a logger */
45  	private static final Logger LOG = Logger.getLogger(DeviceFeedbackMonitor.class);
46  	
47  	
48  	/**
49  	 * A reference to the <code>DeviceService</code>
50  	 */
51  	@Autowired
52  	private DeviceService deviceService;
53  
54  	/** Connection pool */
55  	@Autowired
56  	private GenericObjectPool<SSLSocket> iOSFeedbackConnectionPool;
57  	
58  	
59  	/**
60  	 * This is a public method for initiating a system for checking if devices need to be removed from device tables. 
61  	 * TODO: Weigh pros & cons of splitting these out into separate threads. 
62  	 */
63  	public void checkDeviceFeedback(){
64  		LOG.info("Checking Device Feedback");
65  		checkiOSDeviceFeedback();
66  		checkAndroidDeviceFeedback();
67  		checkBlackberryDeviceFeedback();
68  	}
69  	
70  	/**
71  	 * This is a private method that checks Apple's feedback service for devices that need to be removed. 
72  	 * 
73  	 */
74  	private void checkiOSDeviceFeedback(){
75  		LOG.info("Checking iOS Device Feedback");
76      	final int cFEEDBACKTUPLESIZE = 38;
77      	final int cBLOCKSIZE = 1024;
78      	final int cBYTEMASK = 0x000000FF;
79  
80      	
81  //   	SSLSocket feedbackSocket = openAppleSocket(feedbackHost, feedbackPort);
82      	SSLSocket feedbackSocket = null;
83  		try {
84  			feedbackSocket = iOSFeedbackConnectionPool.borrowObject();
85  		} catch (Exception e) {
86  			LOG.info("Was unable to borrow SSQLSocket from Pool");
87  		}
88      	    	
89  		if(null == feedbackSocket){
90  			LOG.info("APNS Feedback Socket is NOT connected.");
91  		}else{
92  			LOG.info("APNS Feedback Socket is connected. Checking Feedback.");
93  			try{
94  				InputStream in	= feedbackSocket.getInputStream();	
95  
96                  // Read bytes        
97                  byte[] b = new byte[cBLOCKSIZE];
98                  ByteArrayOutputStream message = new ByteArrayOutputStream();
99                  int nbBytes = 0;
100                 // socketStream.available can return 0
101                 // http://forums.sun.com/thread.jspa?threadID=5428561
102                 while ((nbBytes = in.read(b, 0, cBLOCKSIZE)) != -1) {
103                         message.write(b, 0, nbBytes);
104                 }
105                 
106                 byte[] listOfDevices = message.toByteArray();
107                 int nbDevices = listOfDevices.length / cFEEDBACKTUPLESIZE;                    
108                 LOG.info(nbDevices + " devices had feedback.");
109                 
110                 for (int j = 0; j < nbDevices; j++) {
111                     int offset = j * cFEEDBACKTUPLESIZE;
112 
113                     // Build date
114                     int index = 0;
115                     int firstByte = 0;
116                     int secondByte = 0;
117                     int thirdByte = 0;
118                     int fourthByte = 0;
119                     long anUnsignedInt = 0;
120 
121 
122                     
123                     firstByte = (cBYTEMASK & ((int) listOfDevices[offset]));
124                     secondByte = (cBYTEMASK & ((int) listOfDevices[offset + 1]));
125                     thirdByte = (cBYTEMASK);
126                     fourthByte = (cBYTEMASK & ((int) listOfDevices[offset + 3]));
127                     index = index + 4;
128                     anUnsignedInt = ((long) (firstByte << 24 | secondByte << 16 | thirdByte << 8 | fourthByte)) & 0xFFFFFFFFL;
129                     Timestamp timestamp = new Timestamp(anUnsignedInt * 1000);
130 
131                     // Build device token length
132                     int deviceTokenLength = listOfDevices[offset + 4] << 8 | listOfDevices[offset + 5];
133 
134                     // Build device token
135                     String deviceToken = "";
136                     int octet = 0;
137                     for (int k = 0; k < 32; k++) {
138                             octet = (cBYTEMASK & ((int) listOfDevices[offset + 6 + k]));
139                             deviceToken = deviceToken.concat(String.format("%02x", octet));
140                     }
141 
142                     LOG.info(timestamp);
143                     LOG.info(deviceToken);
144                     Device dtoDelete = deviceService.findDeviceByRegId(deviceToken);                        
145                     if(deviceService.removeDevice(dtoDelete)){
146                     	LOG.info("Deleted " + dtoDelete.getDeviceName());
147                     }
148                 }
149                 
150                 
151 			}catch(Exception e){
152 
153 				
154 			}finally{
155 				try{
156 					feedbackSocket.close();
157 				}catch(Exception e){
158 					
159 				}
160 			}
161 		}
162 	}
163 	
164 	/**
165 	 * This is a private method that checks Android's feedback service for devices that need to be removed. 
166 	 * STUB
167 	 */
168 
169 	private void checkAndroidDeviceFeedback(){
170 		LOG.info("Checking Android Device Feedback - STUB");
171 	}
172 	
173 	/**
174 	 * This is a private method that checks Blackberries feedback service for devices that need to be removed. 
175 	 * STUB
176 	 */
177 	private void checkBlackberryDeviceFeedback(){
178 		LOG.info("Checking Blackberry Device Feedback - STUB");
179 	}
180 	
181 	
182 	/**
183 	 * This is a method to get this object's <code>DeviceService</code>.
184 	 * @return
185 	 */
186 	public DeviceService getDeviceService() {
187 		return deviceService;
188 	}
189 
190 	/**
191 	 * This is a method to set this object's <code>DeviceService</code> 
192 	 * @param deviceService
193 	 */
194 	public void setDeviceService(DeviceService deviceService) {
195 		this.deviceService = deviceService;
196 	}
197 }