/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.api.impl;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.json.JSONException;
import org.json.JSONObject;
import org.onebusaway.api.services.AlarmDetails;
import org.onebusaway.api.services.AlarmService;
import org.onebusaway.api.services.apns.ApplePushNotificationService;
import org.onebusaway.exceptions.InvalidArgumentServiceException;
import org.onebusaway.transit_data.model.RegisterAlarmQueryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.notnoop.apns.PayloadBuilder;
@Component
class AlarmServiceImpl implements AlarmService {
private static Logger _log = LoggerFactory.getLogger(AlarmServiceImpl.class);
private static final String PREFIX_APNS = "apns:";
private ConcurrentMap<String, AlarmDetails> _alarmsById = new ConcurrentHashMap<String, AlarmDetails>();
private List<ApplePushNotificationService> _applePushNotificationServices = Collections.emptyList();
private String _callbackUrl;
@Autowired(required=false)
public void setApplePushNotificationServices(
List<ApplePushNotificationService> applePushNotificationServices) {
_applePushNotificationServices = applePushNotificationServices;
}
public String getCallbackUrl() {
return _callbackUrl;
}
public void setCallbackUrl(String callbackUrl) {
_callbackUrl = callbackUrl;
}
@Override
public AlarmDetails alterAlarmQuery(RegisterAlarmQueryBean alarm, String data) {
String url = alarm.getUrl();
if (url.startsWith(PREFIX_APNS)) {
String deviceId = url.substring(PREFIX_APNS.length());
/**
* Update the callback url
*/
String callbackUrl = getCallbackUrl();
alarm.setUrl(callbackUrl);
if (_applePushNotificationServices.isEmpty())
_log.warn("apple push notification alarm set but not ApplePushNotificationService was configured");
return new ApnsAlarmDetails(deviceId, data);
}
return null;
}
@Override
public void registerAlarm(String alarmId, AlarmDetails alarmDetails) {
if (alarmId == null)
throw new IllegalArgumentException("alarmId is null");
if (alarmDetails == null)
throw new IllegalArgumentException("alarmDetails is null");
_alarmsById.put(alarmId, alarmDetails);
}
@Override
public void fireAlarm(String alarmId) {
AlarmDetails details = _alarmsById.remove(alarmId);
if (details == null)
return;
if (details instanceof ApnsAlarmDetails) {
ApnsAlarmDetails apnsDetails = (ApnsAlarmDetails) details;
JSONObject data = getDataAsJson(apnsDetails.getData());
String payload = getDataAsApnsPayload(alarmId, data);
boolean isProduction = isProduction(data);
for (ApplePushNotificationService service : _applePushNotificationServices) {
if (service.isProduction() == isProduction) {
service.pushNotification(apnsDetails.getDeviceToken(), payload);
return;
}
}
_log.warn("no appropriate ApplePushNotificationService found for alarm: production="
+ isProduction);
}
}
@Override
public void cancelAlarm(String alarmId) {
_alarmsById.remove(alarmId);
}
/****
* Private Methods
****/
private JSONObject getDataAsJson(String data) {
if (data == null)
return new JSONObject();
try {
return new JSONObject(data);
} catch (JSONException e) {
throw new InvalidArgumentServiceException("data", e.getMessage());
}
}
private String getDataAsApnsPayload(String alarmId, JSONObject data) {
PayloadBuilder b = PayloadBuilder.newPayload();
b.customField("alarmId", alarmId);
try {
if (data.has("actionKey"))
b.actionKey(data.getString("actionKey"));
if (data.has("alertBody"))
b.alertBody(data.getString("alertBody"));
if (data.has("badge"))
b.badge(data.getInt("badge"));
if (data.has("sound"))
b.sound(data.getString("sound"));
} catch (JSONException e) {
throw new InvalidArgumentServiceException("data", e.getMessage());
}
return b.build();
}
private boolean isProduction(JSONObject data) {
try {
if (data.has("production"))
return data.getBoolean("production");
return false;
} catch (JSONException e) {
throw new InvalidArgumentServiceException("data", e.getMessage());
}
}
private static class ApnsAlarmDetails implements AlarmDetails {
private final String _deviceToken;
private final String _data;
public ApnsAlarmDetails(String deviceToken, String data) {
_deviceToken = deviceToken;
_data = data;
}
public String getDeviceToken() {
return _deviceToken;
}
public String getData() {
return _data;
}
}
}