001/** 002 * Copyright 2011 The Kuali Foundation Licensed under the 003 * Educational Community License, Version 2.0 (the "License"); you may 004 * not use this file except in compliance with the License. You may 005 * obtain a copy of the License at 006 * 007 * http://www.osedu.org/licenses/ECL-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, 010 * software distributed under the License is distributed on an "AS IS" 011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 012 * or implied. See the License for the specific language governing 013 * permissions and limitations under the License. 014 */ 015 016package org.kuali.mobility.push.controllers; 017 018import java.sql.Timestamp; 019import java.text.DecimalFormat; 020import java.util.Iterator; 021import java.util.List; 022import java.util.Map; 023 024import javax.servlet.http.Cookie; 025import javax.servlet.http.HttpServletRequest; 026import javax.servlet.http.HttpServletResponse; 027 028import net.sf.json.JSONException; 029import net.sf.json.JSONObject; 030import net.sf.json.JSONSerializer; 031 032import org.apache.commons.collections.map.HashedMap; 033import org.apache.log4j.Logger; 034import org.kuali.mobility.push.entity.Device; 035import org.kuali.mobility.push.service.DeviceService; 036import org.kuali.mobility.security.authn.util.AuthenticationConstants; 037import org.kuali.mobility.security.user.api.User; 038import org.kuali.mobility.security.user.api.UserDao; 039import org.springframework.beans.factory.annotation.Autowired; 040import org.springframework.beans.factory.annotation.Qualifier; 041import org.springframework.http.HttpStatus; 042import org.springframework.http.ResponseEntity; 043import org.springframework.stereotype.Controller; 044import org.springframework.ui.Model; 045import org.springframework.web.bind.annotation.PathVariable; 046import org.springframework.web.bind.annotation.RequestMapping; 047import org.springframework.web.bind.annotation.RequestMethod; 048import org.springframework.web.bind.annotation.RequestParam; 049import org.springframework.web.bind.annotation.ResponseBody; 050 051 052/** 053 * Controller for managing devices 054 * 055 * @author Kuali Mobility Team (mobility.dev@kuali.org) 056 * @since 2.0.0 057 */ 058@Controller 059@RequestMapping("/device") 060public class DeviceController { 061 062 /** A reference to a logger */ 063 private static final Logger LOG = Logger.getLogger(DeviceController.class); 064 065 /** 066 * A reference to the Controllers <code>DeviceService</code> object. 067 */ 068 @Autowired 069 private DeviceService deviceService; 070 071 /** 072 * A reference to the Controllers <code>UserDao</code> object. 073 */ 074 @Autowired 075 @Qualifier("kmeUserDao") 076 private UserDao userDao; 077 078 /** 079 * A controller method for loading the main index.jsp for the Device tool. 080 * 081 * @param uiModel 082 * @return 083 */ 084 @RequestMapping(method = RequestMethod.GET) 085 public String index(Model uiModel, HttpServletRequest request) { 086 Cookie cks[] = request.getCookies(); 087 String deviceIdentifier = ""; 088 if (cks != null) { 089 for(Cookie c : cks){ 090 if(c.getName().equals("device.identifier")){ 091 deviceIdentifier = c.getValue(); 092 } 093 } 094 } 095 096 Device thisDevice = null; 097 if(!deviceIdentifier.equals("")){ 098 thisDevice = deviceService.findDeviceByDeviceId(deviceIdentifier); 099 } 100 101 DecimalFormat df = new DecimalFormat("###,###,###,###.###"); 102 uiModel.addAttribute("totalDeviceCount", df.format(deviceService.countDevices())); 103 uiModel.addAttribute("thisDevice", thisDevice); 104 return "device/index"; 105 } 106 107 /** 108 * A controller method for loading the keyword device search page. 109 * 110 * @param uiModel 111 * @param request 112 * @return 113 */ 114 @RequestMapping(value="search", method = RequestMethod.GET) 115 public String search(Model uiModel, HttpServletRequest request) { 116 return "device/search"; 117 } 118 119 /** 120 * A Controller method for accessing the details of a given device and displaying it in the association jsp file. 121 * 122 * @param uiModel 123 * @param request 124 * @param deviceHash Id of the device to show. 125 * @return 126 */ 127 @SuppressWarnings("unchecked") 128 @RequestMapping(value = "/detail/{deviceHash}", method = RequestMethod.GET) 129 public String getUserDetails(Model uiModel, HttpServletRequest request, @PathVariable("deviceHash") String deviceHash) { 130 if (deviceHash != null) { 131 LOG.debug("DeviceID is :" + deviceHash); 132 }else{ 133 return "push"; 134 } 135 Device device = getDeviceService().findDeviceByDeviceId(deviceHash); 136 uiModel.addAttribute("device", device); 137 uiModel.addAttribute("deviceHash", deviceHash); 138 return "device/devicedetail"; 139 } 140 141 /** 142 * A controller method for accessing a deleting a specified device. 143 * 144 * @param uiModel 145 * @param request 146 * @param deviceHash Id of the device to delete. 147 * @return 148 */ 149 @SuppressWarnings("unchecked") 150 @RequestMapping(value = "/remove/{deviceHash}", method = RequestMethod.GET) 151 public String removeDevice(Model uiModel, HttpServletRequest request, @PathVariable("deviceHash") String deviceHash) { 152 Device deviceToDelete = getDeviceService().findDeviceByDeviceId(deviceHash); 153 if(deviceToDelete != null){ 154 LOG.debug("Will delete device with Id: " + deviceToDelete.getId()); 155 if(getDeviceService().removeDevice(deviceToDelete)){ 156 LOG.debug("Did delete device."); 157 } 158 } 159// return "redirect:../list"; 160 return "redirect:/device"; 161 } 162 163 /** 164 * A controller method for accessing all devices registered with push notifications. 165 * 166 * @param request 167 * @param uiModel 168 * @param key 169 * @return 170 */ 171 @RequestMapping(value = "/list", method = RequestMethod.GET) 172 public String devices(HttpServletRequest request, Model uiModel, @RequestParam(value="key", required=false) String key) { 173 174 Long deviceCount = getDeviceService().countDevices(); 175 int deviceNoUserCount = getDeviceService().countDevicesWithoutUsername(); 176 List<String> deviceTypes = getDeviceService().getSupportedDeviceTypes(); 177 Map<String, List<Device>> devicesMap = getDeviceService().findDevicesMap(); 178 Map<String, Long> deviceCountMap = new HashedMap(); 179 for(String deviceType : deviceTypes){ 180 Long count = new Long(getDeviceService().countDevices(deviceType)); 181 deviceCountMap.put(deviceType, count); 182 } 183 184 uiModel.addAttribute("deviceNoUserCount", deviceNoUserCount); 185 uiModel.addAttribute("deviceCount", deviceCount); 186 uiModel.addAttribute("deviceTypes", deviceTypes); 187 uiModel.addAttribute("devicesMap", devicesMap); 188 uiModel.addAttribute("deviceCountMap", deviceCountMap); 189 190 return "device/devices"; 191 } 192 193 /** 194 * A controller method for registering a device for push notifications as defined by a JSON formatted string. 195 * 196 * @param request 197 * @param data JSON formatted string describing a device to be register for push notifications. 198 * @return 199 */ 200 @RequestMapping(value = "/register", method = RequestMethod.GET) 201 public ResponseEntity<String> register(HttpServletRequest request, @RequestParam(value="data", required=true) String data) { 202 LOG.info("-----Register-----"); 203 if(data == null){ 204 return new ResponseEntity<String>(HttpStatus.NO_CONTENT); 205 } 206 207 JSONObject queryParams; 208 try{ 209 queryParams = (JSONObject) JSONSerializer.toJSON(data); 210 LOG.info(queryParams.toString()); 211 }catch(JSONException je){ 212 LOG.error("JSONException in :" + data + " : " + je.getMessage()); 213 return new ResponseEntity<String>(HttpStatus.BAD_REQUEST); 214 } 215 216 Device device = new Device(); 217 device.setDeviceName(queryParams.getString("name")); 218 device.setDeviceId(queryParams.getString("deviceId")); 219 device.setRegId(queryParams.getString("regId")); 220 device.setType(queryParams.getString("type")); 221 222 // We might not have a username yet 223 if (queryParams.containsKey("username")){ 224 device.setUsername(queryParams.getString("username")); 225 } 226 device.setPostedTimestamp(new Timestamp(System.currentTimeMillis())); 227 LOG.info("\n-----New Device-----" + device.toString()); 228 229 Device temp = getDeviceService().findDeviceByDeviceId(device.getDeviceId()); 230 231 User user = (User)request.getSession().getAttribute(AuthenticationConstants.KME_USER_KEY); 232 if( user == null ) { 233 LOG.error("No user found in request. This should never happen!"); 234 } else if( user.isPublicUser() ) { 235 LOG.debug("Public user found, no user profile updates necessary."); 236 } else if( !user.getLoginName().equals(device.getUsername()) ) { 237 LOG.debug("User on device does not match user in session! This should never happen either."); 238 } else if( user.attributeExists(AuthenticationConstants.DEVICE_ID,device.getDeviceId())) { 239 LOG.debug("Device id already exists on user and no action needs to be taken."); 240 } else { 241 user.addAttribute(AuthenticationConstants.DEVICE_ID,device.getDeviceId()); 242 getUserDao().saveUser(user); 243 } 244 245 // If the device already exists, update. 246 try { 247 if(temp != null){ 248 LOG.info("-----Device already exists." + temp.toString()); 249 temp.setDeviceName(queryParams.getString("name")); 250 temp.setRegId(queryParams.getString("regId")); 251 temp.setType(queryParams.getString("type")); 252 // We might not have a username yet 253 if (queryParams.containsKey("username")){ 254 temp.setUsername(queryParams.getString("username")); 255 } 256 temp.setPostedTimestamp(new Timestamp(System.currentTimeMillis())); 257 getDeviceService().saveDevice(temp); 258 }else{ 259 LOG.info("-----Device Doesn't already exist." + device.toString()); 260 getDeviceService().saveDevice(device); 261 } 262 } catch (Exception e) { 263 LOG.error("Exception while trying to update device", e); 264 return new ResponseEntity<String>(HttpStatus.BAD_REQUEST); 265 } 266 267 return new ResponseEntity<String>(HttpStatus.OK); 268 } 269 270 /** 271 * A controller method for updating a pre-existing registered device. 272 * 273 * @param request 274 * @param data A JSON formatted string describing details of a device to update. 275 * @return 276 */ 277 @RequestMapping(value = "/update", method = RequestMethod.GET) 278 public ResponseEntity<String> update(HttpServletRequest request,@RequestParam(value="data", required=true) String data) { 279 LOG.info("-----Register-----"); 280 if(data == null){ 281 return new ResponseEntity<String>(HttpStatus.NO_CONTENT); 282 } 283 284 JSONObject queryParams; 285 try{ 286 queryParams = (JSONObject) JSONSerializer.toJSON(data); 287 LOG.info(queryParams.toString()); 288 }catch(JSONException je){ 289 LOG.error("JSONException in :" + data + " : " + je.getMessage()); 290 return new ResponseEntity<String>(HttpStatus.BAD_REQUEST); 291 } 292 293 Device temp = getDeviceService().findDeviceByDeviceId(queryParams.getString("deviceId")); 294 295 // If the device already exists, update. 296 if(temp != null){ 297 LOG.info("-----Device already exists." + temp.toString()); 298 // Remove this device from the logged user's profile if it exists. 299 User user = getUserDao().loadUserByLoginName(temp.getUsername()); 300 if( user != null ) { 301 user.removeAttribute(AuthenticationConstants.DEVICE_ID,temp.getDeviceId()); 302 getUserDao().saveUser(user); 303 } 304 temp.setDeviceName(queryParams.getString("name")); 305 temp.setUsername(queryParams.getString("username")); 306 temp.setPostedTimestamp(new Timestamp(System.currentTimeMillis())); 307 getDeviceService().saveDevice(temp); 308 309 // Find the real user and set the new device. 310 User user2 = (User)request.getSession().getAttribute(AuthenticationConstants.KME_USER_KEY); 311 if( user2 == null ) { 312 LOG.error("No user found in request. This should never happen!"); 313 } else if( user2.isPublicUser() ) { 314 LOG.debug("Public user found, no user profile updates necessary."); 315 } else if( !user2.getLoginName().equals(temp.getUsername()) ) { 316 LOG.debug("User on device does not match user in session! This should never happen either."); 317 } else if( user2.attributeExists(AuthenticationConstants.DEVICE_ID,temp.getDeviceId())) { 318 LOG.debug("Device id already exists on user and no action needs to be taken."); 319 } else { 320 user2.addAttribute(AuthenticationConstants.DEVICE_ID,temp.getDeviceId()); 321 getUserDao().saveUser(user2); 322 } 323 324 } 325 326 return new ResponseEntity<String>(HttpStatus.OK); 327 } 328 329 /** 330 * A controller method for checking the existance of a device, and accessing basic details of said device. 331 * 332 * @param uiModel 333 * @param request 334 * @param deviceId Id of device to check. 335 * @return String - A JSON formatted string giving basic details of the specified device. 336 */ 337 @RequestMapping(value = "/deviceid", method = RequestMethod.POST) 338 @ResponseBody 339 public String getUsernameFromDeviceID(Model uiModel, HttpServletRequest request, @RequestParam(value="deviceid", required=false) String deviceId) { 340 Device device = getDeviceService().findDeviceByDeviceId(deviceId); 341 if(device == null){ 342 return "{\"deviceExists\":false}"; 343 } 344 if(device.getUsername() == null || device.getUsername().length() == 0){ 345 return "{\"deviceExists\":true,\"hasUsername\":false}"; 346 }else{ 347 return "{\"deviceExists\":true,\"hasUsername\":true,\"username\":\"" + device.getUsername() + "\"}"; 348 } 349 } 350 351 /** 352 * A controller method for accessing all devices registered the correspond to a specified user. 353 * 354 * @param uiModel 355 * @param request 356 * @param username Username associated with devices to access. 357 * @return String - A JSON formatted string listing devices associated with a username. 358 */ 359 @RequestMapping(value = "/username", method = RequestMethod.POST) 360 @ResponseBody 361 public String getDevicesFromUsername(Model uiModel, HttpServletRequest request, @RequestParam(value="username", required=false) String username) { 362 // TODO use JSONObject to build json here?? 363 List<Device> devices = getDeviceService().findDevicesByUsername(username); 364 String result = "{\"username\":\"" + username + "\",\"devices\":["; 365 String sDevices = ""; 366 Iterator<Device> i = devices.iterator(); 367 368 while(i.hasNext()){ 369 Device d = (Device)i.next(); 370 sDevices += "\"" + d.getDeviceId() + "\"," ; 371 } 372 373 if(sDevices.length() > 0){ 374 sDevices = sDevices.substring(0, sDevices.length() - 1); 375 } 376 377 result += sDevices + "]}"; 378 return result; 379 } 380 381 /** 382 * A controller method that provides an interface for search devices via keyword and returns a JSON list to the requesting JSP/HMLT page. 383 * 384 * @param request 385 * @param response 386 * @param uiModel 387 * @param keyword 388 * @return 389 */ 390 @RequestMapping(value = "keyword/{keyword}", method = RequestMethod.GET, headers = "Accept=application/json") 391 @ResponseBody 392 public String devicesByKeyword(HttpServletRequest request, HttpServletResponse response, Model uiModel, @PathVariable("keyword") String keyword) { 393 List<Device> devices = deviceService.findDevicesByKeyword(keyword); 394 LOG.info(devices.size() + " elements returned."); 395 396 String json = ""; 397 if(devices.size() > 0){ 398 Iterator<Device> i = devices.iterator(); 399 if(devices.size() > 1){ 400 json = "{\"total\":\"" + devices.size() + " Devices Found.\" , \"devices\":["; 401 }else{ 402 json = "{\"total\":\"" + devices.size() + " Device Found.\" , \"devices\":["; 403 } 404 while(i.hasNext()){ 405 json += ((Device)i.next()).toJson(); 406 json += ","; 407 } 408 json = json.substring(0, json.length()-1); 409 json += "]}"; 410 }else{ 411 json = "{\"total\":\"No Devices Found\"}"; 412 } 413 414 return json; 415 } 416 417 418 /** 419 * Simple controller which responds with 200. 420 * This controller can be used by devices to check if the server is online 421 * @return 422 */ 423 @RequestMapping(value = "/ping", method = RequestMethod.GET) 424 public ResponseEntity<String> ping() { 425 return new ResponseEntity<String>(HttpStatus.OK); 426 } 427 428 /** 429 * Sets the reference to the <code>DeviceService</code> 430 * @param deviceService 431 */ 432 public void setDeviceService(DeviceService deviceService){ 433 this.deviceService = deviceService; 434 } 435 436 /** 437 * A reference to the <code>DeviceService</code>. 438 */ 439 public DeviceService getDeviceService() { 440 return deviceService; 441 } 442 443 /** 444 * Get the controllers <code>UserDao</code> object. 445 * @return 446 */ 447 public UserDao getUserDao() { 448 return userDao; 449 } 450 451 /** 452 * A method for setting the <code>UserDao</code> of the controller. 453 * 454 * @param userDao 455 */ 456 public void setUserDao(UserDao userDao) { 457 this.userDao = userDao; 458 } 459}