diff --git a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/pom.xml b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/pom.xml
index 674cd1b5018..9bab0bcf9f2 100644
--- a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/pom.xml
+++ b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/pom.xml
@@ -111,6 +111,46 @@
org.wso2.carbon.analytics-common
org.wso2.carbon.event.output.adapter.core
+
+ org.wso2.orbit.com.google.http-client
+ google-http-client
+
+
+ org.wso2.orbit.com.google.auth-library-oauth2-http
+ google-auth-library-oauth2-http
+
+
+ org.wso2.orbit.io.opencensus
+ opencensus
+
+
+ io.opencensus
+ opencensus-api
+
+
+ io.opencensus
+ opencensus-contrib-http-util
+
+
+ org.wso2.orbit.io.grpc
+ grpc-context
+
+
+ com.google.http-client
+ google-http-client-gson
+
+
+ com.google.guava
+ failureaccess
+
+
+ com.google.guava
+ guava
+
+
+ org.wso2.carbon
+ org.wso2.carbon.utils
+
@@ -137,12 +177,27 @@
com.google.gson,
org.osgi.framework.*;version="${imp.package.version.osgi.framework}",
org.osgi.service.*;version="${imp.package.version.osgi.service}",
+ org.wso2.carbon.utils.*,
io.entgra.device.mgt.core.device.mgt.common.operation.mgt,
io.entgra.device.mgt.core.device.mgt.common.push.notification,
org.apache.commons.logging,
io.entgra.device.mgt.core.device.mgt.common.*,
- io.entgra.device.mgt.core.device.mgt.core.service
+ io.entgra.device.mgt.core.device.mgt.core.service,
+ io.entgra.device.mgt.core.device.mgt.core.config.*,
+ io.entgra.device.mgt.core.device.mgt.core.config.push.notification.*,
+ io.entgra.device.mgt.core.device.mgt.extensions.logger.spi,
+ io.entgra.device.mgt.core.notification.logger.*,
+ com.google.auth.oauth2.*
+
+ google-auth-library-oauth2-http;scope=compile|runtime,
+ google-http-client;scope=compile|runtime,
+ grpc-context;scope=compile|runtime,
+ guava;scope=compile|runtime,
+ opencensus;scope=compile|runtime,
+ failureaccess;scope=compile|runtime
+
+ true
diff --git a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMBasedPushNotificationProvider.java b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMBasedPushNotificationProvider.java
index 1dbbacd720f..2f849a43664 100644
--- a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMBasedPushNotificationProvider.java
+++ b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMBasedPushNotificationProvider.java
@@ -32,7 +32,9 @@ public class FCMBasedPushNotificationProvider implements PushNotificationProvide
@Override
public NotificationStrategy getNotificationStrategy(PushNotificationConfig config) {
- return new FCMNotificationStrategy(config);
+ FCMNotificationStrategy fcmNotificationStrategy = new FCMNotificationStrategy(config);
+ fcmNotificationStrategy.init();
+ return fcmNotificationStrategy;
}
}
diff --git a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMNotificationStrategy.java b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMNotificationStrategy.java
index c6100aa4183..39bd1ba7e52 100644
--- a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMNotificationStrategy.java
+++ b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/FCMNotificationStrategy.java
@@ -17,9 +17,8 @@
*/
package io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm;
-import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
-import com.google.gson.JsonPrimitive;
+import io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm.util.FCMUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.entgra.device.mgt.core.device.mgt.common.Device;
@@ -39,14 +38,13 @@ import java.util.List;
public class FCMNotificationStrategy implements NotificationStrategy {
private static final Log log = LogFactory.getLog(FCMNotificationStrategy.class);
-
private static final String NOTIFIER_TYPE_FCM = "FCM";
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 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 final PushNotificationConfig config;
+ private static final String FCM_ENDPOINT_KEY = "FCM_SERVER_ENDPOINT";
public FCMNotificationStrategy(PushNotificationConfig config) {
this.config = config;
@@ -64,12 +62,14 @@ public class FCMNotificationStrategy implements NotificationStrategy {
Device device = FCMDataHolder.getInstance().getDeviceManagementProviderService()
.getDeviceWithTypeProperties(ctx.getDeviceId());
if(device.getProperties() != null && getFCMToken(device.getProperties()) != null) {
- this.sendWakeUpCall(ctx.getOperation().getCode(), device);
+ FCMUtil.getInstance().getDefaultApplication().refresh();
+ sendWakeUpCall(FCMUtil.getInstance().getDefaultApplication().getAccessToken().getTokenValue(),
+ getFCMToken(device.getProperties()));
}
} else {
if (log.isDebugEnabled()) {
log.debug("Not using FCM notifier as notifier type is set to " + config.getType() +
- " in Platform Configurations.");
+ " in Platform Configurations.");
}
}
} catch (DeviceManagementException e) {
@@ -79,71 +79,75 @@ public class FCMNotificationStrategy implements NotificationStrategy {
}
}
- @Override
- public NotificationContext buildContext() {
- return null;
- }
-
- @Override
- public void undeploy() {
- }
+ /**
+ * Send FCM message to the FCM server to initiate the push notification
+ * @param accessToken Access token to authenticate with the FCM server
+ * @param registrationId Registration ID of the device
+ * @throws IOException If an error occurs while sending the request
+ * @throws PushNotificationExecutionFailedException If an error occurs while sending the push notification
+ */
+ private void sendWakeUpCall(String accessToken, String registrationId) throws IOException,
+ PushNotificationExecutionFailedException {
+ HttpURLConnection conn = null;
+
+ String fcmServerEndpoint = FCMUtil.getInstance().getContextMetadataProperties()
+ .getProperty(FCM_ENDPOINT_KEY);
+ if(fcmServerEndpoint == null) {
+ String msg = "Encountered configuration issue. " + FCM_ENDPOINT_KEY + " is not defined";
+ log.error(msg);
+ throw new PushNotificationExecutionFailedException(msg);
+ }
- private void sendWakeUpCall(String message, Device device) throws IOException,
- PushNotificationExecutionFailedException {
- if (device.getProperties() != null) {
- OutputStream os = null;
- byte[] bytes = getFCMRequest(message, getFCMToken(device.getProperties())).getBytes();
-
- HttpURLConnection conn = null;
- try {
- conn = (HttpURLConnection) new URL(FCM_ENDPOINT).openConnection();
- conn.setRequestProperty("Content-Type", "application/json");
- conn.setRequestProperty("Authorization", "key=" + config.getProperty(FCM_API_KEY));
- conn.setRequestMethod("POST");
- conn.setDoOutput(true);
- os = conn.getOutputStream();
+ try {
+ byte[] bytes = getFCMRequest(registrationId).getBytes();
+ URL url = new URL(fcmServerEndpoint);
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestProperty("Content-Type", "application/json");
+ conn.setRequestProperty("Authorization", "Bearer " + accessToken);
+ conn.setRequestMethod("POST");
+ conn.setDoOutput(true);
+
+ try (OutputStream os = conn.getOutputStream()) {
os.write(bytes);
- } finally {
- if (os != null) {
- os.close();
- }
- if (conn != null) {
- conn.disconnect();
- }
}
+
int status = conn.getResponseCode();
- if (log.isDebugEnabled()) {
- log.debug("Result code: " + status + ", Message: " + conn.getResponseMessage());
+ if (status != 200) {
+ log.error("Response Status: " + status + ", Response Message: " + conn.getResponseMessage());
}
- if (status != HTTP_STATUS_CODE_OK) {
- throw new PushNotificationExecutionFailedException("Push notification sending failed with the HTTP " +
- "error code '" + status + "'");
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
}
}
}
- private static String getFCMRequest(String message, String registrationId) {
- JsonObject fcmRequest = new JsonObject();
- fcmRequest.addProperty("delay_while_idle", false);
- 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);
- }
+ /**
+ * Get the FCM request as a JSON string
+ * @param registrationId Registration ID of the device
+ * @return FCM request as a JSON string
+ */
+ private static String getFCMRequest(String registrationId) {
+ JsonObject messageObject = new JsonObject();
+ messageObject.addProperty("token", registrationId);
- //Set device reg-id
- JsonArray regIds = new JsonArray();
- regIds.add(new JsonPrimitive(registrationId));
+ JsonObject fcmRequest = new JsonObject();
+ fcmRequest.add("message", messageObject);
- fcmRequest.add("registration_ids", regIds);
return fcmRequest.toString();
}
+ @Override
+ public NotificationContext buildContext() {
+ return null;
+ }
+
+ @Override
+ public void undeploy() {
+
+ }
+
private static String getFCMToken(List properties) {
String fcmToken = null;
for (Device.Property property : properties) {
@@ -159,5 +163,4 @@ public class FCMNotificationStrategy implements NotificationStrategy {
public PushNotificationConfig getConfig() {
return config;
}
-
}
diff --git a/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/util/FCMUtil.java b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/util/FCMUtil.java
new file mode 100644
index 00000000000..0c6c433cc7c
--- /dev/null
+++ b/components/device-mgt-extensions/io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm/src/main/java/io/entgra/device/mgt/core/device/mgt/extensions/push/notification/provider/fcm/util/FCMUtil.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
+ *
+ * Entgra (Pvt) Ltd. licenses this file to you 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 io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm.util;
+
+import com.google.auth.oauth2.GoogleCredentials;
+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 io.entgra.device.mgt.core.device.mgt.extensions.push.notification.provider.fcm.FCMNotificationStrategy;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.utils.CarbonUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Properties;
+
+public class FCMUtil {
+
+ private static final Log log = LogFactory.getLog(FCMUtil.class);
+ private static volatile FCMUtil instance;
+ private static GoogleCredentials defaultApplication;
+ 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 Properties contextMetadataProperties;
+
+ private FCMUtil() {
+ initContextConfigs();
+ initDefaultOAuthApplication();
+ }
+
+ private void initDefaultOAuthApplication() {
+ if (defaultApplication == null) {
+ Path serviceAccountPath = Paths.get(FCM_SERVICE_ACCOUNT_PATH);
+ try {
+ defaultApplication = GoogleCredentials.
+ fromStream(Files.newInputStream(serviceAccountPath)).
+ createScoped(FCM_SCOPES);
+ } catch (IOException e) {
+ String msg = "Fail to initialize default OAuth application for FCM communication";
+ log.error(msg);
+ throw new IllegalStateException(msg, e);
+ }
+ }
+ }
+
+ /**
+ * Initialize the context metadata properties from the cdm-config.xml. This file includes the fcm server URL
+ * to be invoked when sending the wakeup call to the device.
+ */
+ private void initContextConfigs() {
+ PushNotificationConfiguration pushNotificationConfiguration = DeviceConfigurationManager.getInstance().
+ getDeviceManagementConfig().getPushNotificationConfiguration();
+ List contextMetadata = pushNotificationConfiguration.getContextMetadata();
+ Properties properties = new Properties();
+ if (contextMetadata != null) {
+ for (ContextMetadata metadata : contextMetadata) {
+ properties.setProperty(metadata.getKey(), metadata.getValue());
+ }
+ }
+ contextMetadataProperties = properties;
+ }
+
+ /**
+ * Get the instance of FCMUtil. FCMUtil is a singleton class which should not be
+ * instantiating more than once. Instantiating the class requires to read the service account file from
+ * the filesystem and instantiation of the GoogleCredentials object which are costly operations.
+ * @return FCMUtil instance
+ */
+ public static FCMUtil getInstance() {
+ if (instance == null) {
+ synchronized (FCMUtil.class) {
+ if (instance == null) {
+ instance = new FCMUtil();
+ }
+ }
+ }
+ return instance;
+ }
+
+ public GoogleCredentials getDefaultApplication() {
+ return defaultApplication;
+ }
+
+ public Properties getContextMetadataProperties() {
+ return contextMetadataProperties;
+ }
+}
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/ContextMetadata.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/ContextMetadata.java
new file mode 100644
index 00000000000..a5ead67f0a9
--- /dev/null
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/ContextMetadata.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2018 - 2023, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
+ *
+ * Entgra (Pvt) Ltd. licenses this file to you 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 io.entgra.device.mgt.core.device.mgt.core.config.push.notification;
+
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+@XmlRootElement(name = "ContextMetadata")
+public class ContextMetadata {
+ private String key;
+ private String value;
+
+ @XmlAttribute(name = "key")
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ @XmlValue
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+}
diff --git a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/PushNotificationConfiguration.java b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/PushNotificationConfiguration.java
index 90c6639cb1b..0d64e45cdde 100644
--- a/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/PushNotificationConfiguration.java
+++ b/components/device-mgt/io.entgra.device.mgt.core.device.mgt.core/src/main/java/io/entgra/device/mgt/core/device/mgt/core/config/push/notification/PushNotificationConfiguration.java
@@ -33,6 +33,7 @@ public class PushNotificationConfiguration {
private int schedulerTaskInitialDelay;
private boolean schedulerTaskEnabled;
private List pushNotificationProviders;
+ private List contextMetadata;
@XmlElement(name = "SchedulerBatchSize", required = true)
public int getSchedulerBatchSize() {
@@ -79,4 +80,14 @@ public class PushNotificationConfiguration {
public void setPushNotificationProviders(List pushNotificationProviders) {
this.pushNotificationProviders = pushNotificationProviders;
}
+
+ @XmlElementWrapper(name = "ProviderContextMetadata")
+ @XmlElement(name = "ContextMetadata", required = true)
+ public List getContextMetadata() {
+ return contextMetadata;
+ }
+
+ public void setContextMetadata(List contextMetadata) {
+ this.contextMetadata = contextMetadata;
+ }
}
diff --git a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2 b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2
index 59e026f6793..2d5f7639f80 100644
--- a/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2
+++ b/features/device-mgt/io.entgra.device.mgt.core.device.mgt.basics.feature/src/main/resources/conf_templates/templates/repository/conf/cdm-config.xml.j2
@@ -48,6 +48,11 @@
{% endfor %}
{% endif %}
+ {% if device_mgt_conf.push_notification_conf.fcm_server_endpoint is defined %}
+
+ {{device_mgt_conf.push_notification_conf.fcm_server_endpoint}}
+
+ {% endif %}
{% if device_mgt_conf.pull_notification_conf is defined %}
diff --git a/pom.xml b/pom.xml
index 90835d054ea..f97db94c294 100644
--- a/pom.xml
+++ b/pom.xml
@@ -405,7 +405,6 @@
${io.entgra.device.mgt.core.version}
-
org.wso2.carbon
@@ -1916,6 +1915,51 @@
mockito-inline
${mokito.version}
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ ${com.google.auth.library.auth2.http.version}
+
+
+ org.wso2.orbit.com.google.http-client
+ google-http-client
+ ${com.google.http.client.version}
+
+
+ org.wso2.orbit.com.google.auth-library-oauth2-http
+ google-auth-library-oauth2-http
+ ${com.google.auth.library.wso2.auth2.http.version}
+
+
+ org.wso2.orbit.io.opencensus
+ opencensus
+ ${io.opencensus.version}
+
+
+ io.opencensus
+ opencensus-api
+ ${io.opencensus.api.version}
+
+
+ io.opencensus
+ opencensus-contrib-http-util
+ ${io.opencensus.contrib.http.util.version}
+
+
+ org.wso2.orbit.io.grpc
+ grpc-context
+ ${io.grpc.context.version}
+
+
+ com.google.http-client
+ google-http-client-gson
+ ${com.google.http.client.gson.version}
+
+
+ com.google.guava
+ failureaccess
+ ${com.google.failureaccess.version}
+
@@ -2306,6 +2350,16 @@
4.3.1.wso2v1
1.4.199.wso2v1
1.1.3
+
+ 1.20.0.wso2v1
+ 1.20.0
+ 1.41.2.wso2v2
+ 1.0.1
+ 1.43.3
+ 1.27.2.wso2v1
+ 0.30.0.wso2v1
+ 0.30.0
+ 0.30.0