|
|
@ -17,9 +17,11 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
package io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm;
|
|
|
|
package io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm;
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.gson.JsonArray;
|
|
|
|
import com.google.auth.oauth2.GoogleCredentials;
|
|
|
|
import com.google.gson.JsonObject;
|
|
|
|
import com.google.gson.JsonObject;
|
|
|
|
import com.google.gson.JsonPrimitive;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.core.config.DeviceConfigurationManager;
|
|
|
|
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.core.config.push.notification.ContextMetadata;
|
|
|
|
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.core.config.push.notification.PushNotificationConfiguration;
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
|
import org.apache.commons.logging.Log;
|
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.Device;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.Device;
|
|
|
@ -29,24 +31,34 @@ import io.entgra.device.mgt.core.device.mgt.common.push.notification.Notificatio
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.push.notification.PushNotificationConfig;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.push.notification.PushNotificationConfig;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.push.notification.PushNotificationExecutionFailedException;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.common.push.notification.PushNotificationExecutionFailedException;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm.internal.FCMDataHolder;
|
|
|
|
import io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm.internal.FCMDataHolder;
|
|
|
|
|
|
|
|
import org.wso2.carbon.utils.CarbonUtils;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.io.OutputStream;
|
|
|
|
import java.net.HttpURLConnection;
|
|
|
|
import java.net.HttpURLConnection;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.net.URL;
|
|
|
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
|
|
|
import java.nio.file.Paths;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import java.util.Properties;
|
|
|
|
|
|
|
|
|
|
|
|
public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
|
|
|
|
|
|
|
|
private static final Log log = LogFactory.getLog(FCMNotificationStrategy.class);
|
|
|
|
private static final Log log = LogFactory.getLog(FCMNotificationStrategy.class);
|
|
|
|
|
|
|
|
|
|
|
|
private static final String NOTIFIER_TYPE_FCM = "FCM";
|
|
|
|
private static final String NOTIFIER_TYPE_FCM = "FCM";
|
|
|
|
private static final String FCM_TOKEN = "FCM_TOKEN";
|
|
|
|
private static final String FCM_TOKEN = "FCM_TOKEN";
|
|
|
|
private static final String FCM_ENDPOINT = "https://fcm.googleapis.com/fcm/send";
|
|
|
|
|
|
|
|
private static final String FCM_API_KEY = "fcmAPIKey";
|
|
|
|
private static final String FCM_API_KEY = "fcmAPIKey";
|
|
|
|
private static final int TIME_TO_LIVE = 2419199; // 1 second less that 28 days
|
|
|
|
private static final int TIME_TO_LIVE = 2419199; // 1 second less than 28 days
|
|
|
|
private static final int HTTP_STATUS_CODE_OK = 200;
|
|
|
|
private static final int HTTP_STATUS_CODE_OK = 200;
|
|
|
|
private final PushNotificationConfig config;
|
|
|
|
private final PushNotificationConfig config;
|
|
|
|
|
|
|
|
private static final String FCM_SERVICE_ACCOUNT_PATH = CarbonUtils.getCarbonHome() + File.separator +
|
|
|
|
|
|
|
|
"repository" + File.separator + "resources" + File.separator + "service-account.json";
|
|
|
|
|
|
|
|
private static final String[] FCM_SCOPES = { "https://www.googleapis.com/auth/firebase.messaging" };
|
|
|
|
|
|
|
|
private static final String FCM_ENDPOINT_KEY = "FCM_SERVER_ENDPOINT";
|
|
|
|
|
|
|
|
private Properties contextMetadataProperties;
|
|
|
|
|
|
|
|
private volatile GoogleCredentials defaultApplication;
|
|
|
|
|
|
|
|
|
|
|
|
public FCMNotificationStrategy(PushNotificationConfig config) {
|
|
|
|
public FCMNotificationStrategy(PushNotificationConfig config) {
|
|
|
|
this.config = config;
|
|
|
|
this.config = config;
|
|
|
@ -54,7 +66,8 @@ public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void init() {
|
|
|
|
public void init() {
|
|
|
|
|
|
|
|
initContextConfigs();
|
|
|
|
|
|
|
|
initDefaultOAuthApplication();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -64,12 +77,14 @@ public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
Device device = FCMDataHolder.getInstance().getDeviceManagementProviderService()
|
|
|
|
Device device = FCMDataHolder.getInstance().getDeviceManagementProviderService()
|
|
|
|
.getDeviceWithTypeProperties(ctx.getDeviceId());
|
|
|
|
.getDeviceWithTypeProperties(ctx.getDeviceId());
|
|
|
|
if(device.getProperties() != null && getFCMToken(device.getProperties()) != null) {
|
|
|
|
if(device.getProperties() != null && getFCMToken(device.getProperties()) != null) {
|
|
|
|
this.sendWakeUpCall(ctx.getOperation().getCode(), device);
|
|
|
|
defaultApplication.refresh();
|
|
|
|
|
|
|
|
sendWakeUpCall(defaultApplication.getAccessToken().getTokenValue(),
|
|
|
|
|
|
|
|
getFCMToken(device.getProperties()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (log.isDebugEnabled()) {
|
|
|
|
if (log.isDebugEnabled()) {
|
|
|
|
log.debug("Not using FCM notifier as notifier type is set to " + config.getType() +
|
|
|
|
log.debug("Not using FCM notifier as notifier type is set to " + config.getType() +
|
|
|
|
" in Platform Configurations.");
|
|
|
|
" in Platform Configurations.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (DeviceManagementException e) {
|
|
|
|
} catch (DeviceManagementException e) {
|
|
|
@ -79,71 +94,95 @@ public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
private void initDefaultOAuthApplication() {
|
|
|
|
public NotificationContext buildContext() {
|
|
|
|
if (defaultApplication == null) {
|
|
|
|
return null;
|
|
|
|
synchronized (FCMNotificationStrategy.class) {
|
|
|
|
|
|
|
|
if (defaultApplication == null) {
|
|
|
|
|
|
|
|
Path serviceAccountPath = Paths.get(FCM_SERVICE_ACCOUNT_PATH);
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
this.defaultApplication = GoogleCredentials.
|
|
|
|
|
|
|
|
fromStream(Files.newInputStream(serviceAccountPath)).
|
|
|
|
|
|
|
|
createScoped(FCM_SCOPES);
|
|
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
|
|
log.error("Fail to initialize default OAuth application for FCM communication");
|
|
|
|
|
|
|
|
throw new IllegalStateException(e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
private void initContextConfigs() {
|
|
|
|
public void undeploy() {
|
|
|
|
PushNotificationConfiguration pushNotificationConfiguration = DeviceConfigurationManager.getInstance().
|
|
|
|
|
|
|
|
getDeviceManagementConfig().getPushNotificationConfiguration();
|
|
|
|
|
|
|
|
List<ContextMetadata> contextMetadata = pushNotificationConfiguration.getContextMetadata();
|
|
|
|
|
|
|
|
Properties properties = new Properties();
|
|
|
|
|
|
|
|
if (contextMetadata != null) {
|
|
|
|
|
|
|
|
for (ContextMetadata metadata : contextMetadata) {
|
|
|
|
|
|
|
|
properties.setProperty(metadata.getKey(), metadata.getValue());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
contextMetadataProperties = properties;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void sendWakeUpCall(String message, Device device) throws IOException,
|
|
|
|
private void sendWakeUpCall(String accessToken, String registrationId) throws IOException,
|
|
|
|
PushNotificationExecutionFailedException {
|
|
|
|
PushNotificationExecutionFailedException {
|
|
|
|
if (device.getProperties() != null) {
|
|
|
|
OutputStream os = null;
|
|
|
|
OutputStream os = null;
|
|
|
|
HttpURLConnection conn = null;
|
|
|
|
byte[] bytes = getFCMRequest(message, getFCMToken(device.getProperties())).getBytes();
|
|
|
|
|
|
|
|
|
|
|
|
String fcmServerEndpoint = contextMetadataProperties.getProperty(FCM_ENDPOINT_KEY);
|
|
|
|
HttpURLConnection conn = null;
|
|
|
|
if(fcmServerEndpoint == null) {
|
|
|
|
try {
|
|
|
|
String msg = "Encountered configuration issue. " + FCM_ENDPOINT_KEY + " is not defined";
|
|
|
|
conn = (HttpURLConnection) new URL(FCM_ENDPOINT).openConnection();
|
|
|
|
log.error(msg);
|
|
|
|
conn.setRequestProperty("Content-Type", "application/json");
|
|
|
|
throw new PushNotificationExecutionFailedException(msg);
|
|
|
|
conn.setRequestProperty("Authorization", "key=" + config.getProperty(FCM_API_KEY));
|
|
|
|
}
|
|
|
|
conn.setRequestMethod("POST");
|
|
|
|
|
|
|
|
conn.setDoOutput(true);
|
|
|
|
try {
|
|
|
|
os = conn.getOutputStream();
|
|
|
|
byte[] bytes = getFCMRequest(registrationId).getBytes();
|
|
|
|
os.write(bytes);
|
|
|
|
URL url = new URL(fcmServerEndpoint);
|
|
|
|
} finally {
|
|
|
|
conn = (HttpURLConnection) url.openConnection();
|
|
|
|
if (os != null) {
|
|
|
|
conn.setRequestProperty("Content-Type", "application/json");
|
|
|
|
os.close();
|
|
|
|
conn.setRequestProperty("Authorization", "Bearer " + accessToken);
|
|
|
|
}
|
|
|
|
conn.setRequestMethod("POST");
|
|
|
|
if (conn != null) {
|
|
|
|
conn.setDoOutput(true);
|
|
|
|
conn.disconnect();
|
|
|
|
|
|
|
|
}
|
|
|
|
os = conn.getOutputStream();
|
|
|
|
}
|
|
|
|
os.write(bytes);
|
|
|
|
|
|
|
|
|
|
|
|
int status = conn.getResponseCode();
|
|
|
|
int status = conn.getResponseCode();
|
|
|
|
if (log.isDebugEnabled()) {
|
|
|
|
if (status != 200) {
|
|
|
|
log.debug("Result code: " + status + ", Message: " + conn.getResponseMessage());
|
|
|
|
log.error("Response Status: " + status + ", Response Message: " + conn.getResponseMessage());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (status != HTTP_STATUS_CODE_OK) {
|
|
|
|
} finally {
|
|
|
|
throw new PushNotificationExecutionFailedException("Push notification sending failed with the HTTP " +
|
|
|
|
if (os != null) {
|
|
|
|
"error code '" + status + "'");
|
|
|
|
os.close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conn != null) {
|
|
|
|
|
|
|
|
conn.disconnect();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static String getFCMRequest(String message, String registrationId) {
|
|
|
|
private static String getFCMRequest(String registrationId) {
|
|
|
|
JsonObject fcmRequest = new JsonObject();
|
|
|
|
JsonObject messageObject = new JsonObject();
|
|
|
|
fcmRequest.addProperty("delay_while_idle", false);
|
|
|
|
messageObject.addProperty("token", registrationId);
|
|
|
|
fcmRequest.addProperty("time_to_live", TIME_TO_LIVE);
|
|
|
|
|
|
|
|
fcmRequest.addProperty("priority", "high");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Add message to FCM request
|
|
|
|
|
|
|
|
JsonObject data = new JsonObject();
|
|
|
|
|
|
|
|
if (message != null && !message.isEmpty()) {
|
|
|
|
|
|
|
|
data.addProperty("data", message);
|
|
|
|
|
|
|
|
fcmRequest.add("data", data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Set device reg-id
|
|
|
|
JsonObject fcmRequest = new JsonObject();
|
|
|
|
JsonArray regIds = new JsonArray();
|
|
|
|
fcmRequest.add("message", messageObject);
|
|
|
|
regIds.add(new JsonPrimitive(registrationId));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fcmRequest.add("registration_ids", regIds);
|
|
|
|
|
|
|
|
return fcmRequest.toString();
|
|
|
|
return fcmRequest.toString();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public NotificationContext buildContext() {
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void undeploy() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static String getFCMToken(List<Device.Property> properties) {
|
|
|
|
private static String getFCMToken(List<Device.Property> properties) {
|
|
|
|
String fcmToken = null;
|
|
|
|
String fcmToken = null;
|
|
|
|
for (Device.Property property : properties) {
|
|
|
|
for (Device.Property property : properties) {
|
|
|
@ -159,5 +198,4 @@ public class FCMNotificationStrategy implements NotificationStrategy {
|
|
|
|
public PushNotificationConfig getConfig() {
|
|
|
|
public PushNotificationConfig getConfig() {
|
|
|
|
return config;
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|