diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java index bc2eaabc47..4c64d8c4fa 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/SubscriptionManager.java @@ -33,24 +33,43 @@ import java.util.Properties; * This interface manages all the operations related with ApplicationDTO Subscription. */ public interface SubscriptionManager { + /** * Performs bulk subscription operation for a given application and a subscriber list. - * @param applicationUUID UUID of the application to subscribe/unsubscribe - * @param params list of subscribers. This list can be of either - * {@link DeviceIdentifier} if {@param subType} is equal - * to DEVICE or - * {@link String} if {@param subType} is USER, ROLE or GROUP - * @param subType subscription type. E.g. DEVICE, USER, ROLE, GROUP {@see { - * @param action subscription action. E.g. INSTALL/UNINSTALL {@see { - * @param generic type of the method. - * @return {@link ApplicationInstallResponse} - * @throws ApplicationManagementException if error occurs when subscribing to the given application - * @link org.wso2.carbon.device.application.mgt.common.SubscriptionType}} - * @link org.wso2.carbon.device.application.mgt.common.SubAction}} - * @param properties + * @param applicationUUID UUID of the application to subscribe/unsubscribe + * @param params list of subscribers. + * This list can be of either {@link DeviceIdentifier} if {@param subType} is equal to + * DEVICE or {@link String} if {@param subType} is USER, ROLE or GROUP + * @param subType subscription type. E.g. DEVICE, USER, ROLE, GROUP + * @param action subscription action. E.g. INSTALL/UNINSTALL + * @param generic type of the method. + * @param properties Application properties that need to be sent with operation payload to the device + * @return {@link ApplicationInstallResponse} + * @throws ApplicationManagementException if error occurs when subscribing to the given application + */ + ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, String subType, + String action, Properties properties) + throws ApplicationManagementException; + + /** + * Performs bulk subscription operation for a given application and a subscriber list. + * @param applicationUUID UUID of the application to subscribe/unsubscribe + * @param params list of subscribers. + * This list can be of either {@link DeviceIdentifier} if {@param subType} is equal to + * DEVICE or {@link String} if {@param subType} is USER, ROLE or GROUP + * @param subType subscription type. E.g. DEVICE, USER, ROLE, GROUP + * @param action subscription action. E.g. INSTALL/UNINSTALL + * @param generic type of the method. + * @param properties Application properties that need to be sent with operation payload to the device + * @param isOperationReExecutingDisabled To prevent adding the application subscribing operation to devices that are + * already subscribed application successfully. + * @return {@link ApplicationInstallResponse} + * @throws ApplicationManagementException if error occurs when subscribing to the given application */ ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, String subType, - String action, Properties properties) throws ApplicationManagementException; + String action, Properties properties, + boolean isOperationReExecutingDisabled) + throws ApplicationManagementException; /** * Create an entry related to the scheduled task in the database. @@ -119,7 +138,7 @@ public interface SubscriptionManager { * This is used in enterprise app installing policy. * * @param deviceIdentifier Device identifiers - * @param releaseUUID UUIs of applicatios + * @param apps Applications * @throws ApplicationManagementException if error occurred while installing given applications into the given * device */ diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java index 2de1094c1f..5d2f3c6d6b 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java @@ -121,7 +121,16 @@ public class SubscriptionManagerImpl implements SubscriptionManager { @Override public ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, - String subType, String action, Properties properties) throws ApplicationManagementException { + String subType, String action, Properties properties) + throws ApplicationManagementException { + return performBulkAppOperation(applicationUUID, params, subType, action, properties, false); + } + + @Override + public ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, + String subType, String action, Properties properties, + boolean isOperationReExecutingDisabled) + throws ApplicationManagementException { if (log.isDebugEnabled()) { log.debug("Install application release which has UUID " + applicationUUID + " to " + params.size() + " users."); @@ -134,7 +143,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { params); ApplicationInstallResponse applicationInstallResponse = performActionOnDevices( applicationSubscriptionInfo.getAppSupportingDeviceTypeName(), applicationSubscriptionInfo.getDevices(), - applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action, properties); + applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action, properties, isOperationReExecutingDisabled); applicationInstallResponse.setErrorDeviceIdentifiers(applicationSubscriptionInfo.getErrorDeviceIdentifiers()); return applicationInstallResponse; @@ -617,12 +626,17 @@ public class SubscriptionManagerImpl implements SubscriptionManager { * @param subType Subscription type (i.e USER, ROLE, GROUP or DEVICE) * @param subscribers Subscribers * @param action Performing action. (i.e INSTALL or UNINSTALL) + * @param isOperationReExecutingDisabled To prevent adding the application subscribing operation to devices that are + * already subscribed application successfully. * @return {@link ApplicationInstallResponse} - * @throws ApplicationManagementException if error occured when adding operation on device or updating subscription + * @throws ApplicationManagementException if error occurred when adding operation on device or updating subscription * data. */ private ApplicationInstallResponse performActionOnDevices(String deviceType, List devices, - ApplicationDTO applicationDTO, String subType, List subscribers, String action, Properties properties) + ApplicationDTO applicationDTO, String subType, + List subscribers, String action, + Properties properties, + boolean isOperationReExecutingDisabled) throws ApplicationManagementException { //Get app subscribing info of each device @@ -637,15 +651,19 @@ public class SubscriptionManagerImpl implements SubscriptionManager { if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { deviceIdentifiers.addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstallableDevices().keySet())); deviceIdentifiers.addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppReInstallableDevices().keySet())); - deviceIdentifiers.addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstalledDevices().keySet())); - } else { - if (SubAction.UNINSTALL.toString().equalsIgnoreCase(action)) { + if (!isOperationReExecutingDisabled) { deviceIdentifiers.addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstalledDevices().keySet())); - deviceIdentifiers - .addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppReUnInstallableDevices().keySet())); - ignoredDeviceIdentifiers - .addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstallableDevices().keySet())); } + } else if (SubAction.UNINSTALL.toString().equalsIgnoreCase(action)) { + deviceIdentifiers.addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstalledDevices().keySet())); + deviceIdentifiers + .addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppReUnInstallableDevices().keySet())); + ignoredDeviceIdentifiers + .addAll(new ArrayList<>(subscribingDeviceIdHolder.getAppInstallableDevices().keySet())); + } else { + String msg = "Found invalid Action: " + action + ". Hence, terminating the application subscribing."; + log.error(msg); + throw new ApplicationManagementException(msg); } if (deviceIdentifiers.isEmpty()) { @@ -717,15 +735,14 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } else { if (deviceSubscriptionDTO.isUnsubscribed()) { if (!Operation.Status.COMPLETED.toString().equals(deviceSubscriptionDTO.getStatus())) { - /*We can't ensure whether app is uninstalled successfully or not hence allow to perform both - install and uninstall operations*/ + /*If the uninstalling operation has failed, we can't ensure whether the app is uninstalled + successfully or not. Therefore, allowing to perform both install and uninstall operations*/ subscribingDeviceIdHolder.getAppReUnInstallableDevices() .put(deviceIdentifier, device.getId()); } subscribingDeviceIdHolder.getAppReInstallableDevices().put(deviceIdentifier, device.getId()); } else { - if (!deviceSubscriptionDTO.isUnsubscribed() && Operation.Status.COMPLETED.toString() - .equals(deviceSubscriptionDTO.getStatus())) { + if (Operation.Status.COMPLETED.toString().equals(deviceSubscriptionDTO.getStatus())) { subscribingDeviceIdHolder.getAppInstalledDevices().put(deviceIdentifier, device.getId()); } else { subscribingDeviceIdHolder.getAppReInstallableDevices() @@ -960,11 +977,11 @@ public class SubscriptionManagerImpl implements SubscriptionManager { ConnectionManagerUtil.openDBConnection(); return this.subscriptionDAO.getDeviceSubscriptions(deviceIds, appReleaseId, tenantId); } catch (ApplicationManagementDAOException e) { - String msg = "Error occured when getting device subscriptions for given device IDs"; + String msg = "Error occurred when getting device subscriptions for given device IDs"; log.error(msg, e); throw new ApplicationManagementException(msg, e); } catch (DBConnectionException e) { - String msg = "Error occured while getting database connection for getting device subscriptions."; + String msg = "Error occurred while getting database connection for getting device subscriptions."; log.error(msg, e); throw new ApplicationManagementException(msg, e); } finally { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java index d8f491e37d..2a4e03d205 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java @@ -42,7 +42,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { - private static Log log = LogFactory.getLog(ScheduledAppSubscriptionTask.class); + private static final Log log = LogFactory.getLog(ScheduledAppSubscriptionTask.class); private static final String TASK_NAME = "SCHEDULE_APP_SUBSCRIPTION"; private SubscriptionManager subscriptionManager; @@ -55,6 +55,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { private String tenantDomain; private String taskName; private int tenantId; + private boolean isOperationReExecutingDisabled; @Override public void setProperties(Map map) { @@ -67,6 +68,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { this.tenantDomain = map.get(Constants.TENANT_DOMAIN); this.tenantId = Integer.parseInt(map.get(Constants.TENANT_ID)); this.taskName = map.get(Constants.TASK_NAME); + this.isOperationReExecutingDisabled = Boolean.parseBoolean(map.get(Constants.OPERATION_RE_EXECUtING)); } @Override @@ -108,7 +110,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask { try { Properties properties = new Gson().fromJson(payload, Properties.class); subscriptionManager.performBulkAppOperation(this.application, subscriberList, - this.subscriptionType, this.action, properties); + this.subscriptionType, this.action, properties, isOperationReExecutingDisabled); subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); } catch (ApplicationManagementException e) { log.error( diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java index b274b1850f..f68c213ebc 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java @@ -71,10 +71,14 @@ public class ScheduledAppSubscriptionTaskManager { * @param action action subscription action. E.g. {@code INSTALL/UNINSTALL} * {@see {@link SubAction}} * @param timestamp timestamp to schedule the application subscription + * @param properties Properties sending to the device via operation + * @param isOperationReExecutingDisabled To prevent adding the application subscribing + * already subscribed application successfully. * @throws ApplicationOperationTaskException if error occurred while scheduling the subscription */ public void scheduleAppSubscriptionTask(String applicationUUID, List subscribers, - SubscriptionType subscriptionType, SubAction action, long timestamp, Properties properties) + SubscriptionType subscriptionType, SubAction action, long timestamp, + Properties properties, boolean isOperationReExecutingDisabled) throws ApplicationOperationTaskException { Calendar calendar = Calendar.getInstance(); calendar.setTime(new Date(timestamp * 1000)); @@ -107,14 +111,15 @@ public class ScheduledAppSubscriptionTaskManager { taskProperties.put(Constants.APP_UUID, applicationUUID); taskProperties.put(Constants.TENANT_DOMAIN, carbonContext.getTenantDomain(true)); taskProperties.put(Constants.SUBSCRIBER, carbonContext.getUsername()); + taskProperties.put(Constants.OPERATION_RE_EXECUtING, String.valueOf(isOperationReExecutingDisabled)); String subscribersString; if (SubscriptionType.DEVICE.equals(subscriptionType)) { subscribersString = new Gson().toJson(subscribers); - taskProperties.put(Constants.SUBSCRIBERS, subscribersString); } else { subscribersString = subscribers.stream().map(String.class::cast).collect(Collectors.joining(",")); - taskProperties.put(Constants.SUBSCRIBERS, subscribersString); } + taskProperties.put(Constants.SUBSCRIBERS, subscribersString); + if(properties != null) { String payload = new Gson().toJson(properties); taskProperties.put(Constants.PAYLOAD, payload); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index 27498f77bb..845789ec21 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -60,6 +60,7 @@ public class Constants { public static final String APP_UUID = "APP_UUID"; public static final String APP_PROPERTIES = "APP_PROPERTIES"; public static final String SUBSCRIBER = "SUBSCRIBER"; + public static final String OPERATION_RE_EXECUtING = "OPERATION_RE_EXECUtING"; public static final String TENANT_DOMAIN = "TENANT_DOMAIN"; public static final String TENANT_ID = "__TENANT_ID_PROP__"; public static final String TASK_NAME = "TASK_NAME"; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java index 174b6921ee..b0d91fdf89 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/SubscriptionManagementAPI.java @@ -193,7 +193,12 @@ public interface SubscriptionManagementAPI { name = "block-uninstall", value = "App removal status of the install operation" ) - @QueryParam("block-uninstall") Boolean isUninstallBlocked + @QueryParam("block-uninstall") Boolean isUninstallBlocked, + @ApiParam( + name = "disable-operation-re-executing", + value = "Disable Operation re-executing" + ) + @QueryParam("disable-operation-re-executing") boolean isOperationReExecutingDisabled ); @POST diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java index 4a51353997..473694714d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.store.api/src/main/java/org/wso2/carbon/device/application/mgt/store/api/services/impl/SubscriptionManagementAPIImpl.java @@ -123,22 +123,24 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ @PathParam("action") String action, @Valid List subscribers, @QueryParam("timestamp") long timestamp, - @QueryParam("block-uninstall") Boolean isUninstallBlocked + @QueryParam("block-uninstall") Boolean isUninstallBlocked, + @QueryParam("disable-operation-re-executing") boolean isOperationReExecutingDisabled ) { Properties properties = new Properties(); - if(isUninstallBlocked != null) { + if (isUninstallBlocked != null) { properties.put(MDMAppConstants.AndroidConstants.IS_BLOCK_UNINSTALL, isUninstallBlocked); } try { if (0 == timestamp) { SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); - ApplicationInstallResponse response = subscriptionManager - .performBulkAppOperation(uuid, subscribers, subType, action, properties); + ApplicationInstallResponse response = + subscriptionManager.performBulkAppOperation(uuid, subscribers, subType, action, properties, + isOperationReExecutingDisabled); return Response.status(Response.Status.OK).entity(response).build(); } else { return scheduleApplicationOperationTask(uuid, subscribers, SubscriptionType.valueOf(subType.toUpperCase()), SubAction.valueOf(action.toUpperCase()), - timestamp, properties); + timestamp, properties, isOperationReExecutingDisabled); } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload"; @@ -252,6 +254,28 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ } } + /** + * Schedule the application subscription for the given timestamp + * + * @param applicationUUID UUID of the application to install + * @param subscribers list of subscribers. This list can be of + * either {@link org.wso2.carbon.device.mgt.common.DeviceIdentifier} if {@param subType} is + * equal to DEVICE or {@link String} if {@param subType} is USER, ROLE or GROUP + * @param subType subscription type. E.g. DEVICE, USER, ROLE, GROUP + * {@see {@link org.wso2.carbon.device.application.mgt.common.SubscriptionType}} + * @param subAction action subscription action. E.g. INSTALL/UNINSTALL + * {@see {@link org.wso2.carbon.device.application.mgt.common.SubAction}} + * @param payload Properties sending to the device via operation + * @param timestamp timestamp to schedule the application subscription + * @return {@link Response} of the operation + */ + private Response scheduleApplicationOperationTask(String applicationUUID, List subscribers, + SubscriptionType subType, SubAction subAction, long timestamp, + Properties payload) { + return scheduleApplicationOperationTask(applicationUUID, subscribers, subType, subAction, timestamp, payload, + false); + } + /** * Schedule the application subscription for the given timestamp * @@ -264,14 +288,18 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ * @param subAction action subscription action. E.g. INSTALL/UNINSTALL * {@see {@link org.wso2.carbon.device.application.mgt.common.SubAction}} * @param timestamp timestamp to schedule the application subscription + * @param payload Properties sending to the device via operation + * @param isOperationReExecutingDisabled To prevent adding the application subscribing operation to devices that are + * already subscribed application successfully. * @return {@link Response} of the operation */ private Response scheduleApplicationOperationTask(String applicationUUID, List subscribers, - SubscriptionType subType, SubAction subAction, long timestamp, Properties payload) { + SubscriptionType subType, SubAction subAction, long timestamp, + Properties payload, boolean isOperationReExecutingDisabled) { try { ScheduledAppSubscriptionTaskManager subscriptionTaskManager = new ScheduledAppSubscriptionTaskManager(); subscriptionTaskManager.scheduleAppSubscriptionTask(applicationUUID, subscribers, subType, subAction, - timestamp, payload); + timestamp, payload, isOperationReExecutingDisabled); } catch (ApplicationOperationTaskException e) { String msg = "Error occurred while scheduling the application install operation"; log.error(msg, e);