From c9f12d47cbf8897ce12fe629b33d1531f7c759da Mon Sep 17 00:00:00 2001 From: Madawa Soysa Date: Thu, 3 Oct 2019 09:50:39 +0000 Subject: [PATCH] Implement scheduled application install/uninstall task Fixes entgra/product-iots#160 --- .../application/mgt/common/ErrorResponse.java | 9 +- .../mgt/common/ExecutionStatus.java | 23 ++ ...ciptionType.java => SubscriptionType.java} | 2 +- .../common/dto/ScheduledSubscriptionDTO.java | 172 +++++++++++ .../SubscriptionManagementException.java | 45 +++ .../common/services/SubscriptionManager.java | 54 ++++ .../pom.xml | 4 +- .../mgt/core/dao/SubscriptionDAO.java | 85 ++++++ .../GenericSubscriptionDAOImpl.java | 275 +++++++++++++++++- .../ApplicationOperationTaskException.java | 30 ++ .../core/impl/SubscriptionManagerImpl.java | 140 ++++++++- ...ApplicationManagementServiceComponent.java | 41 ++- .../mgt/core/internal/DataHolder.java | 11 + .../ScheduledAppSubscriptionCleanupTask.java | 54 ++++ .../task/ScheduledAppSubscriptionTask.java | 132 +++++++++ .../ScheduledAppSubscriptionTaskManager.java | 220 ++++++++++++++ .../application/mgt/core/util/Constants.java | 29 ++ .../application/mgt/core/util/DAOUtil.java | 69 +++++ .../services/SubscriptionManagementAPI.java | 15 +- .../impl/SubscriptionManagementAPIImpl.java | 114 ++++++-- .../application-mgt-datasources.xml | 50 ++-- .../dbscripts/cdm/application-mgt/h2.sql | 19 ++ .../dbscripts/cdm/application-mgt/mssql.sql | 15 + .../dbscripts/cdm/application-mgt/mysql.sql | 21 ++ .../dbscripts/cdm/application-mgt/oracle.sql | 17 ++ .../cdm/application-mgt/postgresql.sql | 24 ++ 26 files changed, 1597 insertions(+), 73 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ExecutionStatus.java rename components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/{SubsciptionType.java => SubscriptionType.java} (96%) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/dto/ScheduledSubscriptionDTO.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/SubscriptionManagementException.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/exception/ApplicationOperationTaskException.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ErrorResponse.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ErrorResponse.java index 3f3a40219e..65f3adb1bb 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ErrorResponse.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ErrorResponse.java @@ -21,7 +21,6 @@ package org.wso2.carbon.device.application.mgt.common; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import org.wso2.carbon.device.application.mgt.common.ErrorListItem; import java.util.ArrayList; import java.util.List; @@ -39,6 +38,14 @@ public class ErrorResponse { private String moreInfo = null; private List errorItems = new ArrayList<>(); + public ErrorResponse() { + + } + + public ErrorResponse(String message) { + this.message = message; + } + @JsonProperty(value = "code") @ApiModelProperty(required = true, value = "") public Integer getCode() { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ExecutionStatus.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ExecutionStatus.java new file mode 100644 index 0000000000..c24a65593f --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ExecutionStatus.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.common; + +public enum ExecutionStatus { + PENDING, EXECUTED, FAILED +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubsciptionType.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubscriptionType.java similarity index 96% rename from components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubsciptionType.java rename to components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubscriptionType.java index 0e6e772ec5..08af098d8f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubsciptionType.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/SubscriptionType.java @@ -17,6 +17,6 @@ package org.wso2.carbon.device.application.mgt.common; -public enum SubsciptionType { +public enum SubscriptionType { USER, ROLE, GROUP, DEVICE } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/dto/ScheduledSubscriptionDTO.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/dto/ScheduledSubscriptionDTO.java new file mode 100644 index 0000000000..65801e9415 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/dto/ScheduledSubscriptionDTO.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.common.dto; + +import com.google.gson.Gson; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +/** + * This class represents a DTO for AP_SCHEDULED_SUBSCRIPTION table + */ +public class ScheduledSubscriptionDTO { + /** + * Generated ID of the subscription. + */ + private int id; + + /** + * Name of the task which is related to the subscription. + * + * Task name is a generated field and in the following pattern: + * {@code __} + * {@code SUBSCRIPTION-TYPE} - {@see {@linkplain SubscriptionType}} + * {@code ACTION} - {@see {@linkplain org.wso2.carbon.device.application.mgt.common.SubAction} + * {@code HASH-VALUE} - this is a hash value of the combination of application uuid and the subscriber list. + * + * Example: {@code DEVICE_INSTALL_e593e00e8ef55efc764295b6aa9ad56b} + */ + private String taskName; + + /** + * UUID of the application release which is subscribed to. + * {@see {@link org.wso2.carbon.device.application.mgt.common.response.ApplicationRelease}} + */ + private String applicationUUID; + + /** + * List of subscribers for the application release. The type of the list depends on the subscription type. + * {@see {@link SubscriptionType}}. If the subscription type is {@code SubscriptionType.DEVICE} the type will be + * {@link org.wso2.carbon.device.mgt.common.DeviceIdentifier} and if not the type will be {@link String}. + */ + private List subscriberList; + + /** + * Status of the subscription. {@see {@link ExecutionStatus}} + */ + private ExecutionStatus status; + + /** + * Scheduled time of subscription. + */ + private LocalDateTime scheduledAt; + + /** + * Username of the scheduler. + */ + private String scheduledBy; + + /** + * If the subscription is marked as deleted or not. + * {@code true} means that the related task is removed from the {@link org.wso2.carbon.ntask.core.TaskManager}. + */ + private boolean deleted; + + public ScheduledSubscriptionDTO() { + + } + + public ScheduledSubscriptionDTO(String taskName, String applicationUUID, LocalDateTime scheduledAt, + List subscriberList, String scheduledBy) { + this.taskName = taskName; + this.applicationUUID = applicationUUID; + this.scheduledAt = scheduledAt; + this.subscriberList = subscriberList; + this.scheduledBy = scheduledBy; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getApplicationUUID() { + return applicationUUID; + } + + public void setApplicationUUID(String applicationUUID) { + this.applicationUUID = applicationUUID; + } + + public List getSubscriberList() { + return subscriberList; + } + + public void setSubscriberList(List subscriberList) { + this.subscriberList = subscriberList; + } + + public ExecutionStatus getStatus() { + return status; + } + + public void setStatus(ExecutionStatus status) { + this.status = status; + } + + public LocalDateTime getScheduledAt() { + return scheduledAt; + } + + public void setScheduledAt(LocalDateTime scheduledAt) { + this.scheduledAt = scheduledAt; + } + + public String getScheduledBy() { + return scheduledBy; + } + + public void setScheduledBy(String scheduledBy) { + this.scheduledBy = scheduledBy; + } + + /** + * @return the string representation of the subscriber list. + */ + public String getSubscribersString() { + if (this.taskName.startsWith(SubscriptionType.DEVICE.toString())) { + return new Gson().toJson(this.subscriberList); + } else { + return this.subscriberList.stream().map(String.class::cast).collect(Collectors.joining(",")); + } + } + + public boolean isDeleted() { + return deleted; + } + + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/SubscriptionManagementException.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/SubscriptionManagementException.java new file mode 100644 index 0000000000..e3c48164d0 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/SubscriptionManagementException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.common.exception; + +public class SubscriptionManagementException extends Exception { + private String message; + + public SubscriptionManagementException(String message, Throwable throwable) { + super(message, throwable); + setMessage(message); + } + + public SubscriptionManagementException(String message) { + super(message); + setMessage(message); + } + + public SubscriptionManagementException() { + + } + + @Override public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} 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 8cb861dc29..03c970d5fb 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 @@ -19,7 +19,10 @@ package org.wso2.carbon.device.application.mgt.common.services; import org.wso2.carbon.device.application.mgt.common.ApplicationInstallResponse; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; +import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManagementException; import java.util.List; @@ -27,6 +30,57 @@ import java.util.List; * 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 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 { + * @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}} + */ ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List params, String subType, String action) throws ApplicationManagementException; + + /** + * Create an entry related to the scheduled task in the database. + * + * @param subscriptionDTO {@link ScheduledSubscriptionDTO} with details of the subscription + * @throws SubscriptionManagementException if unable to create/update entry for the scheduled task + */ + void createScheduledSubscription(ScheduledSubscriptionDTO subscriptionDTO) throws SubscriptionManagementException; + + /** + * Mark already executed, misfired and failed tasks as deleted. + * + * @return deleted list of subscriptions + * @throws SubscriptionManagementException if error occurred while cleaning up subscriptions. + */ + List cleanScheduledSubscriptions() throws SubscriptionManagementException; + + /** + * Retrieves the subscription entry which is pending by task name. At a given time, there should be only a single + * entry in the status {@code PENDING} and not marked as deleted. + * + * @param taskName name of the task to retrieve + * @return {@link ScheduledSubscriptionDTO} + * @throws SubscriptionManagementException if error occurred while retrieving the subscription details + */ + ScheduledSubscriptionDTO getPendingScheduledSubscription(String taskName) throws SubscriptionManagementException; + + /** + * Updates the status of a subscription. + * + * @param id id of the subscription + * @param status new status of the subscription. {@see {@link ExecutionStatus}} + * @throws SubscriptionManagementException if error occurred while updating the status of the subscription + */ + void updateScheduledSubscriptionStatus(int id, ExecutionStatus status) throws SubscriptionManagementException; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml index d3aa277f61..dc5cbbc1df 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/pom.xml @@ -60,7 +60,7 @@ org.w3c.dom, org.json, javax.sql, - com.google.gson, + com.google.gson.*, javax.naming, javax.xml.bind.annotation, javax.xml.bind, @@ -69,6 +69,8 @@ org.wso2.carbon.device.mgt.common.*, org.wso2.carbon.user.core.*, org.wso2.carbon.user.api.*, + org.wso2.carbon.ntask.*, + org.quartz.*, org.wso2.carbon.ndatasource.core, org.wso2.carbon, org.xml.sax, diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/SubscriptionDAO.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/SubscriptionDAO.java index 469ee44030..e6ff3a3f53 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/SubscriptionDAO.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/SubscriptionDAO.java @@ -16,12 +16,33 @@ * under the License. * */ + +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.dao; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationReleaseDTO; import org.wso2.carbon.device.application.mgt.common.dto.DeviceSubscriptionDTO; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -85,4 +106,68 @@ public interface SubscriptionDAO { boolean updateDeviceSubStatus(int deviceId, List deviceSubIds, String status, int tenantcId) throws ApplicationManagementDAOException; + /** + * Creates a scheduled subscription entry in the data store. + * + * @param subscriptionDTO {@link ScheduledSubscriptionDTO} which contains the details of the subscription + * @throws ApplicationManagementDAOException if error occurred while creating an entry in the data store. + */ + boolean createScheduledSubscription(ScheduledSubscriptionDTO subscriptionDTO) throws ApplicationManagementDAOException; + + /** + * Updates the existing entry of a scheduled subscription. + * + * @param id id of the existing subscription + * @param scheduledAt scheduled time + * @param scheduledBy username of the user who scheduled the subscription + * @throws ApplicationManagementDAOException if error occurred while updating the entry + */ + boolean updateScheduledSubscription(int id, LocalDateTime scheduledAt, String scheduledBy) + throws ApplicationManagementDAOException; + + /** + * Marks A list of given scheduled subscription as deleted. + * + * @param subscriptionIdList list of ids of the subscriptions to delete + * @throws ApplicationManagementDAOException if error occurred while deleting the subscription + */ + boolean deleteScheduledSubscription(List subscriptionIdList) throws ApplicationManagementDAOException; + + /** + * Update the status of an existing subscription. + * + * @param id id of the existing subscription + * @param status changed status {@see {@link ExecutionStatus}} + * @throws ApplicationManagementDAOException if error occurs while changing the status of the subscription + */ + boolean updateScheduledSubscriptionStatus(int id, ExecutionStatus status) throws ApplicationManagementDAOException; + + /** + * Retrieve a list of scheduled subscriptions of a given state + * + * @param status status of the subscriptions + * @param deleted is the subscription marked as deleted + * @return list of {@link ScheduledSubscriptionDTO} + * @throws ApplicationManagementDAOException if error occurred while retrieving the subscriptions + */ + List getScheduledSubscriptionByStatus(ExecutionStatus status, boolean deleted) + throws ApplicationManagementDAOException; + + /** + * Retrieves a list of subscriptions that are not executed on the scheduled time. + * + * @return list of {@link ScheduledSubscriptionDTO} + * @throws ApplicationManagementDAOException if error occurred while retrieving the subscriptions. + */ + List getNonExecutedSubscriptions() throws ApplicationManagementDAOException; + + /** + * Retrieves a subscription by taskName which is in the ExecutionStatus.PENDING state. + * + * @param taskName name of the task to retrieve. + * @return {@link ScheduledSubscriptionDTO} + * @throws ApplicationManagementDAOException if error occurred while retrieving the subscription + */ + ScheduledSubscriptionDTO getPendingScheduledSubscriptionByTaskName(String taskName) + throws ApplicationManagementDAOException; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java index bb3faf75a4..8eb297f4da 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/subscription/GenericSubscriptionDAOImpl.java @@ -15,25 +15,48 @@ * specific language governing permissions and limitations * under the License. */ + +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.dao.impl.subscription; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; import org.wso2.carbon.device.application.mgt.common.SubAction; -import org.wso2.carbon.device.application.mgt.common.SubsciptionType; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; import org.wso2.carbon.device.application.mgt.common.dto.DeviceSubscriptionDTO; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; import org.wso2.carbon.device.application.mgt.common.exception.DBConnectionException; import org.wso2.carbon.device.application.mgt.core.dao.SubscriptionDAO; -import org.wso2.carbon.device.application.mgt.core.util.DAOUtil; import org.wso2.carbon.device.application.mgt.core.dao.impl.AbstractDAOImpl; import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; +import org.wso2.carbon.device.application.mgt.core.exception.UnexpectedServerErrorException; +import org.wso2.carbon.device.application.mgt.core.util.DAOUtil; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.List; @@ -579,11 +602,11 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc try { Connection conn = this.getDBConnection(); String sql = "UPDATE "; - if (SubsciptionType.USER.toString().equalsIgnoreCase(subType)) { + if (SubscriptionType.USER.toString().equalsIgnoreCase(subType)) { sql += "AP_USER_SUBSCRIPTION SET "; - } else if (SubsciptionType.ROLE.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.ROLE.toString().equalsIgnoreCase(subType)) { sql += "AP_ROLE_SUBSCRIPTION SET "; - } else if (SubsciptionType.GROUP.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) { sql += "AP_GROUP_SUBSCRIPTION SET "; } @@ -593,11 +616,11 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc sql += "SUBSCRIBED_BY = ?, SUBSCRIBED_TIMESTAMP = ? "; } - if (SubsciptionType.USER.toString().equalsIgnoreCase(subType)) { + if (SubscriptionType.USER.toString().equalsIgnoreCase(subType)) { sql += "WHERE USER_NAME = ? "; - } else if (SubsciptionType.ROLE.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.ROLE.toString().equalsIgnoreCase(subType)) { sql += "WHERE ROLE_NAME = ? "; - } else if (SubsciptionType.GROUP.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) { sql += "WHERE GROUP_NAME = ? "; } @@ -696,4 +719,240 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc throw new ApplicationManagementDAOException(msg, e); } } + + @Override + public boolean createScheduledSubscription(ScheduledSubscriptionDTO subscriptionDTO) + throws ApplicationManagementDAOException { + String sql = "INSERT INTO " + + "AP_SCHEDULED_SUBSCRIPTION (" + + "TASK_NAME, " + + "APPLICATION_UUID, " + + "SUBSCRIBER_LIST, " + + "STATUS, " + + "SCHEDULED_AT, " + + "SCHEDULED_TIMESTAMP," + + "SCHEDULED_BY," + + "DELETED) " + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + Calendar calendar = Calendar.getInstance(); + stmt.setString(1, subscriptionDTO.getTaskName()); + stmt.setString(2, subscriptionDTO.getApplicationUUID()); + stmt.setString(3, subscriptionDTO.getSubscribersString()); + stmt.setString(4, ExecutionStatus.PENDING.toString()); + stmt.setTimestamp(5, Timestamp.valueOf(subscriptionDTO.getScheduledAt())); + stmt.setTimestamp(6, new Timestamp(calendar.getTime().getTime())); + stmt.setString(7, subscriptionDTO.getScheduledBy()); + stmt.setBoolean(8, false); + return stmt.executeUpdate() > 0; + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to insert the subscription status of the " + + "scheduled subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to insert the " + "subscription status of the scheduled " + + "subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public boolean updateScheduledSubscription(int id, LocalDateTime scheduledAt, String scheduledBy) + throws ApplicationManagementDAOException { + String sql = "UPDATE AP_SCHEDULED_SUBSCRIPTION AP " + + "SET " + + "AP.SCHEDULED_AT = ?, " + + "AP.SCHEDULED_BY = ?, " + + "AP.SCHEDULED_TIMESTAMP = ? " + + "WHERE AP.ID = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + Calendar calendar = Calendar.getInstance(); + stmt.setTimestamp(1, Timestamp.valueOf(scheduledAt)); + stmt.setString(2, scheduledBy); + stmt.setTimestamp(3, new Timestamp(calendar.getTime().getTime())); + stmt.setInt(4, id); + return stmt.executeUpdate() > 0; + } + } catch (DBConnectionException e) { + String msg = + "Error occurred while obtaining the DB connection to update the existing entry of the scheduled " + + "subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to update the existing entry of the scheduled subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public boolean deleteScheduledSubscription(List subscriptionIdList) throws ApplicationManagementDAOException { + String sql = "UPDATE AP_SCHEDULED_SUBSCRIPTION AP " + + "SET AP.DELETED = ? " + + "WHERE AP.ID = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + for (Integer id: subscriptionIdList) { + stmt.setBoolean(1, true); + stmt.setInt(2, id); + stmt.addBatch(); + } + int[] results = stmt.executeBatch(); + return Arrays.stream(results).allMatch(r -> r > 0); + } + } catch (DBConnectionException e) { + String msg = + "Error occurred while obtaining the DB connection to delete the existing entry of the scheduled " + + "subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to delete the existing entry of the scheduled subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public boolean updateScheduledSubscriptionStatus(int id, ExecutionStatus status) + throws ApplicationManagementDAOException { + String sql = "UPDATE AP_SCHEDULED_SUBSCRIPTION AP " + + "SET AP.STATUS = ? " + + "WHERE AP.ID = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, status.toString()); + stmt.setInt(2, id); + return stmt.executeUpdate() > 0; + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to update the status of the scheduled " + + "subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to update the status of the scheduled subscription."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public List getScheduledSubscriptionByStatus(ExecutionStatus status, boolean deleted) + throws ApplicationManagementDAOException { + String sql = "SELECT " + + "ID, " + + "TASK_NAME, " + + "APPLICATION_UUID, " + + "SUBSCRIBER_LIST, " + + "STATUS, " + + "SCHEDULED_AT, " + + "SCHEDULED_BY, " + + "DELETED " + + "FROM AP_SCHEDULED_SUBSCRIPTION " + + "WHERE STATUS = ? AND DELETED = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, status.toString()); + stmt.setBoolean(2, deleted); + try (ResultSet rs = stmt.executeQuery()) { + return DAOUtil.loadScheduledSubscriptions(rs); + } + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to retrieve" + status.toString() + + " subscriptions"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to retrieve" + status.toString() + " subscriptions"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public List getNonExecutedSubscriptions() throws ApplicationManagementDAOException { + String sql = "SELECT " + + "ID, " + + "TASK_NAME, " + + "APPLICATION_UUID, " + + "SUBSCRIBER_LIST, " + + "STATUS, " + + "SCHEDULED_AT, " + + "SCHEDULED_BY, " + + "DELETED " + + "FROM AP_SCHEDULED_SUBSCRIPTION " + + "WHERE STATUS = ? AND DELETED = ? AND SCHEDULED_AT < ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, ExecutionStatus.PENDING.toString()); + stmt.setBoolean(2, false); + stmt.setTimestamp(3, new Timestamp(Calendar.getInstance().getTime().getTime())); + try (ResultSet rs = stmt.executeQuery()) { + return DAOUtil.loadScheduledSubscriptions(rs); + } + } + } catch (DBConnectionException e) { + String msg = "Error occurred while obtaining the DB connection to retrieve missed subscriptions"; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to retrieve missed subscriptions."; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } + + @Override + public ScheduledSubscriptionDTO getPendingScheduledSubscriptionByTaskName(String taskName) + throws ApplicationManagementDAOException { + String sql = "SELECT " + + "ID, " + + "TASK_NAME, " + + "APPLICATION_UUID, " + + "SUBSCRIBER_LIST, " + + "STATUS, " + + "SCHEDULED_AT, " + + "SCHEDULED_BY, " + + "DELETED " + + "FROM AP_SCHEDULED_SUBSCRIPTION " + + "WHERE TASK_NAME = ? AND STATUS = ? AND DELETED = ?"; + try { + Connection conn = this.getDBConnection(); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, taskName); + stmt.setString(2, ExecutionStatus.PENDING.toString()); + stmt.setBoolean(3, false); + try (ResultSet rs = stmt.executeQuery()) { + return DAOUtil.loadScheduledSubscription(rs); + } + } + } catch (DBConnectionException e) { + String msg = + "Error occurred while obtaining the DB connection to retrieve pending subscriptions of " + taskName; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred when processing SQL to retrieve pending subscriptions of " + taskName; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } catch (UnexpectedServerErrorException e) { + String msg = "More than one pending subscriptions exist for " + taskName; + log.error(msg, e); + throw new ApplicationManagementDAOException(msg, e); + } + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/exception/ApplicationOperationTaskException.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/exception/ApplicationOperationTaskException.java new file mode 100644 index 0000000000..bfec70afc5 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/exception/ApplicationOperationTaskException.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.exception; + +public class ApplicationOperationTaskException extends Exception { + + public ApplicationOperationTaskException(String message, Throwable cause) { + super(message, cause); + } + + public ApplicationOperationTaskException(String message) { + super(message); + } +} 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 0c3fb21deb..be5cbbd913 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 @@ -24,14 +24,17 @@ import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.application.mgt.common.ApplicationInstallResponse; import org.wso2.carbon.device.application.mgt.common.ApplicationType; import org.wso2.carbon.device.application.mgt.common.DeviceTypes; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; import org.wso2.carbon.device.application.mgt.common.SubAction; -import org.wso2.carbon.device.application.mgt.common.SubsciptionType; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; import org.wso2.carbon.device.application.mgt.common.SubscribingDeviceIdHolder; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationDTO; import org.wso2.carbon.device.application.mgt.common.dto.DeviceSubscriptionDTO; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; import org.wso2.carbon.device.application.mgt.common.exception.DBConnectionException; import org.wso2.carbon.device.application.mgt.common.exception.LifecycleManagementException; +import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManagementException; import org.wso2.carbon.device.application.mgt.common.exception.TransactionManagementException; import org.wso2.carbon.device.application.mgt.common.response.Application; import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; @@ -114,7 +117,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { //todo validate users, groups and roles ApplicationDTO applicationDTO = getApplicationDTO(applicationUUID); - if (SubsciptionType.DEVICE.toString().equals(subType)) { + if (SubscriptionType.DEVICE.toString().equals(subType)) { for (T param : params) { DeviceIdentifier deviceIdentifier = (DeviceIdentifier) param; if (StringUtils.isEmpty(deviceIdentifier.getId()) || StringUtils @@ -136,19 +139,19 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } devices.add(deviceManagementProviderService.getDevice(deviceIdentifier, false)); } - } else if (SubsciptionType.USER.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.USER.toString().equalsIgnoreCase(subType)) { for (T param : params) { String username = (String) param; subscribers.add(username); devices.addAll(deviceManagementProviderService.getDevicesOfUser(username)); } - } else if (SubsciptionType.ROLE.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.ROLE.toString().equalsIgnoreCase(subType)) { for (T param : params) { String roleName = (String) param; subscribers.add(roleName); devices.addAll(deviceManagementProviderService.getAllDevicesOfRole(roleName)); } - } else if (SubsciptionType.GROUP.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) { for (T param : params) { String groupName = (String) param; subscribers.add(groupName); @@ -184,6 +187,125 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } } + @Override + public void createScheduledSubscription(ScheduledSubscriptionDTO subscriptionDTO) + throws SubscriptionManagementException { + try { + ConnectionManagerUtil.beginDBTransaction(); + ScheduledSubscriptionDTO existingEntry = subscriptionDAO.getPendingScheduledSubscriptionByTaskName( + subscriptionDTO.getTaskName()); + boolean transactionStatus; + if (existingEntry == null) { + transactionStatus = subscriptionDAO.createScheduledSubscription(subscriptionDTO); + } else { + transactionStatus = subscriptionDAO.updateScheduledSubscription(existingEntry.getId(), + subscriptionDTO.getScheduledAt(), subscriptionDTO.getScheduledBy()); + } + if (!transactionStatus) { + ConnectionManagerUtil.rollbackDBTransaction(); + } + ConnectionManagerUtil.commitDBTransaction(); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occurred while creating the scheduled subscription entry."; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while executing database transaction"; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while observing the database connection to update subscription status."; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public List cleanScheduledSubscriptions() throws SubscriptionManagementException { + try { + // Cleaning up already executed, missed and failed tasks + ConnectionManagerUtil.beginDBTransaction(); + List taskList = subscriptionDAO.getScheduledSubscriptionByStatus( + ExecutionStatus.EXECUTED, false); + taskList.addAll(subscriptionDAO.getNonExecutedSubscriptions()); + taskList.addAll(subscriptionDAO.getScheduledSubscriptionByStatus(ExecutionStatus.FAILED, false)); + List tasksToClean = taskList.stream().map(ScheduledSubscriptionDTO::getId).collect( + Collectors.toList()); + if (!subscriptionDAO.deleteScheduledSubscription(tasksToClean)) { + ConnectionManagerUtil.rollbackDBTransaction(); + } + ConnectionManagerUtil.commitDBTransaction(); + return taskList; + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occurred while cleaning up the old subscriptions."; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while executing database transaction"; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while retrieving the database connection"; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public ScheduledSubscriptionDTO getPendingScheduledSubscription(String taskName) + throws SubscriptionManagementException { + try { + ConnectionManagerUtil.openDBConnection(); + return subscriptionDAO.getPendingScheduledSubscriptionByTaskName(taskName); + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while retrieving subscription for task: " + taskName; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while retrieving the database connection"; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public void updateScheduledSubscriptionStatus(int id, ExecutionStatus status) + throws SubscriptionManagementException { + try { + ConnectionManagerUtil.beginDBTransaction(); + if (!subscriptionDAO.updateScheduledSubscriptionStatus(id, status)) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Unable to update the status of the subscription: " + id; + log.error(msg); + throw new SubscriptionManagementException(msg); + } + ConnectionManagerUtil.commitDBTransaction(); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occurred while updating the status of the subscription."; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while executing database transaction."; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } catch (DBConnectionException e) { + String msg = "Error occurred while retrieving the database connection"; + log.error(msg, e); + throw new SubscriptionManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + private void validateRequest(List params, String subType, String action) throws BadRequestException { if (params.isEmpty()) { String msg = "In order to install application release, you should provide list of subscribers. " @@ -191,7 +313,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { log.error(msg); throw new BadRequestException(msg); } - boolean isValidSubType = Arrays.stream(SubsciptionType.values()) + boolean isValidSubType = Arrays.stream(SubscriptionType.values()) .anyMatch(sub -> sub.name().equalsIgnoreCase(subType)); if (!isValidSubType) { String msg = "Found invalid subscription type " + subType+ " to install application release" ; @@ -336,7 +458,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { ConnectionManagerUtil.beginDBTransaction(); List deviceSubIds = new ArrayList<>(); - if (SubsciptionType.USER.toString().equalsIgnoreCase(subType)) { + if (SubscriptionType.USER.toString().equalsIgnoreCase(subType)) { List subscribedEntities = subscriptionDAO.getSubscribedUserNames(params, tenantId); if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { params.removeAll(subscribedEntities); @@ -344,7 +466,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } subscriptionDAO.updateSubscriptions(tenantId, username, subscribedEntities, applicationReleaseId, subType, action); - } else if (SubsciptionType.ROLE.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.ROLE.toString().equalsIgnoreCase(subType)) { List subscribedEntities = subscriptionDAO.getSubscribedRoleNames(params, tenantId); if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { params.removeAll(subscribedEntities); @@ -352,7 +474,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { } subscriptionDAO.updateSubscriptions(tenantId, username, subscribedEntities, applicationReleaseId, subType, action); - } else if (SubsciptionType.GROUP.toString().equalsIgnoreCase(subType)) { + } else if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) { List subscribedEntities = subscriptionDAO.getSubscribedGroupNames(params, tenantId); if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { params.removeAll(subscribedEntities); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ApplicationManagementServiceComponent.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ApplicationManagementServiceComponent.java index 54a511e573..648b3c86d0 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ApplicationManagementServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ApplicationManagementServiceComponent.java @@ -22,20 +22,22 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.osgi.service.component.ComponentContext; +import org.wso2.carbon.device.application.mgt.common.config.LifecycleState; +import org.wso2.carbon.device.application.mgt.common.config.UIConfiguration; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager; import org.wso2.carbon.device.application.mgt.common.services.AppmDataHandler; import org.wso2.carbon.device.application.mgt.common.services.ReviewManager; import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; import org.wso2.carbon.device.application.mgt.core.config.ConfigurationManager; -import org.wso2.carbon.device.application.mgt.common.config.UIConfiguration; import org.wso2.carbon.device.application.mgt.core.dao.common.ApplicationManagementDAOFactory; import org.wso2.carbon.device.application.mgt.core.impl.AppmDataHandlerImpl; import org.wso2.carbon.device.application.mgt.core.lifecycle.LifecycleStateManager; -import org.wso2.carbon.device.application.mgt.common.config.LifecycleState; +import org.wso2.carbon.device.application.mgt.core.task.ScheduledAppSubscriptionTaskManager; import org.wso2.carbon.device.application.mgt.core.util.ApplicationManagementUtil; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.ndatasource.core.DataSourceService; +import org.wso2.carbon.ntask.core.service.TaskService; import org.wso2.carbon.user.core.service.RealmService; import java.util.List; @@ -61,7 +63,14 @@ import java.util.List; * policy="dynamic" * bind="setDataSourceService" * unbind="unsetDataSourceService" + * @scr.reference name="app.mgt.ntask.component" + * interface="org.wso2.carbon.ntask.core.service.TaskService" + * cardinality="1..1" + * policy="dynamic" + * bind="setTaskService" + * unbind="unsetTaskService" */ +@SuppressWarnings("unused") public class ApplicationManagementServiceComponent { private static Log log = LogFactory.getLog(ApplicationManagementServiceComponent.class); @@ -69,8 +78,6 @@ public class ApplicationManagementServiceComponent { @SuppressWarnings("unused") protected void activate(ComponentContext componentContext) { - - log.info("CALLING ACTIVATE ............."); BundleContext bundleContext = componentContext.getBundleContext(); try { String dataSourceName = ConfigurationManager.getInstance().getConfiguration().getDatasourceName(); @@ -108,16 +115,21 @@ public class ApplicationManagementServiceComponent { DataHolder.getInstance().setConfigManager(configManager); bundleContext.registerService(AppmDataHandler.class.getName(), configManager, null); + ScheduledAppSubscriptionTaskManager taskManager = new ScheduledAppSubscriptionTaskManager(); + taskManager.scheduleCleanupTask(); + log.info("ApplicationManagement core bundle has been successfully initialized"); } catch (Throwable e) { log.error("Error occurred while initializing app management core bundle", e); } } + @SuppressWarnings("unused") protected void deactivate(ComponentContext componentContext) { //do nothing } + @SuppressWarnings("unused") protected void setDeviceManagementService(DeviceManagementProviderService deviceManagementProviderService) { if (log.isDebugEnabled()) { log.debug("Setting ApplicationDTO Management OSGI Manager"); @@ -125,6 +137,7 @@ public class ApplicationManagementServiceComponent { DataHolder.getInstance().setDeviceManagementService(deviceManagementProviderService); } + @SuppressWarnings("unused") protected void unsetDeviceManagementService(DeviceManagementProviderService deviceManagementProviderService) { if (log.isDebugEnabled()) { log.debug("Removing ApplicationDTO Management OSGI Manager"); @@ -132,21 +145,41 @@ public class ApplicationManagementServiceComponent { DataHolder.getInstance().setDeviceManagementService(null); } + @SuppressWarnings("unused") protected void setRealmService(RealmService realmService) { DataHolder.getInstance().setRealmService(realmService); } + @SuppressWarnings("unused") protected void unsetRealmService(RealmService realmService) { DataHolder.getInstance().setRealmService(null); } + @SuppressWarnings("unused") protected void setDataSourceService(DataSourceService dataSourceService) { /*Not implemented. Not needed but to make sure the datasource service are registered, as it is needed create databases. */ } + @SuppressWarnings("unused") protected void unsetDataSourceService(DataSourceService dataSourceService) { /*Not implemented. Not needed but to make sure the datasource service are registered, as it is needed to create databases.*/ } + + @SuppressWarnings("unused") + public void setTaskService(TaskService taskService) { + if (log.isDebugEnabled()) { + log.debug("Setting the task service to Application Management SC."); + } + DataHolder.getInstance().setTaskService(taskService); + } + + @SuppressWarnings("unused") + protected void unsetTaskService(TaskService taskService) { + if (log.isDebugEnabled()) { + log.debug("Removing the task service from Application Management SC"); + } + DataHolder.getInstance().setTaskService(null); + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java index 73efa24598..b7b4e1fea0 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java @@ -25,6 +25,7 @@ import org.wso2.carbon.device.application.mgt.common.services.ReviewManager; import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; import org.wso2.carbon.device.application.mgt.core.lifecycle.LifecycleStateManager; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.ntask.core.service.TaskService; import org.wso2.carbon.user.core.service.RealmService; /** @@ -48,6 +49,8 @@ public class DataHolder { private AppmDataHandler configManager; + private TaskService taskService; + private static final DataHolder applicationMgtDataHolder = new DataHolder(); private DataHolder() { @@ -121,4 +124,12 @@ public class DataHolder { public void setConfigManager(AppmDataHandler configManager) { this.configManager = configManager; } + + public TaskService getTaskService() { + return taskService; + } + + public void setTaskService(TaskService taskService) { + this.taskService = taskService; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java new file mode 100644 index 0000000000..38077daf93 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.task; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManagementException; +import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; +import org.wso2.carbon.device.application.mgt.core.impl.SubscriptionManagerImpl; +import org.wso2.carbon.ntask.core.Task; + +import java.util.Map; + +public class ScheduledAppSubscriptionCleanupTask implements Task { + private static Log log = LogFactory.getLog(ScheduledAppSubscriptionCleanupTask.class); + private SubscriptionManager subscriptionManager; + + @Override + public void setProperties(Map properties) { + //no properties required + } + + @Override + public void init() { + if (this.subscriptionManager == null) { + this.subscriptionManager = new SubscriptionManagerImpl(); + } + } + + @Override + public void execute() { + try { + subscriptionManager.cleanScheduledSubscriptions(); + } catch (SubscriptionManagementException e) { + log.error("Error occurred while cleaning up tasks."); + } + } +} 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 new file mode 100644 index 0000000000..afbab64d6d --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.task; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; +import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManagementException; +import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; +import org.wso2.carbon.device.application.mgt.core.impl.SubscriptionManagerImpl; +import org.wso2.carbon.device.application.mgt.core.util.Constants; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.ntask.core.Task; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class ScheduledAppSubscriptionTask implements Task { + private static Log log = LogFactory.getLog(ScheduledAppSubscriptionTask.class); + private SubscriptionManager subscriptionManager; + private String subscribers; + private String subscriptionType; + private String application; + private String action; + private String subscriber; + private String tenantDomain; + private String taskName; + private int tenantId; + + @Override + public void setProperties(Map map) { + this.subscribers = map.get(Constants.SUBSCRIBERS); + this.subscriptionType = map.get(Constants.SUB_TYPE); + this.application = map.get(Constants.APP_UUID); + this.action = map.get(Constants.ACTION); + this.subscriber = map.get(Constants.SUBSCRIBER); + this.tenantDomain = map.get(Constants.TENANT_DOMAIN); + this.tenantId = Integer.parseInt(map.get(Constants.TENANT_ID)); + this.taskName = map.get(Constants.TASK_NAME); + } + + @Override + public void init() { + if (this.subscriptionManager == null) { + this.subscriptionManager = new SubscriptionManagerImpl(); + } + } + + @Override + public void execute() { + try { + ScheduledSubscriptionDTO subscriptionDTO = subscriptionManager.getPendingScheduledSubscription( + this.taskName); + if (subscriptionDTO == null) { + log.error("Unable to execute the task. Task entry for [" + this.taskName + "] cannot be retrieved " + + "from the database."); + return; + } + if (StringUtils.isNotEmpty(this.subscribers)) { + PrivilegedCarbonContext.startTenantFlow(); + PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + carbonContext.setTenantDomain(this.tenantDomain); + carbonContext.setTenantId(this.tenantId); + carbonContext.setUsername(this.subscriber); + + if (this.subscriptionType.equals(SubscriptionType.DEVICE.toString())) { + List deviceIdentifiers = new Gson().fromJson(this.subscribers, + new TypeToken>() { + }.getType()); + try { + subscriptionManager.performBulkAppOperation(this.application, deviceIdentifiers, + this.subscriptionType, this.action); + subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); + } catch (ApplicationManagementException e) { + log.error( + "Error occurred while " + this.action + "ing application " + this.application + + "to/from the following devices: " + this.subscribers, e); + subscriptionDTO.setStatus(ExecutionStatus.FAILED); + } + } else { + List subscriberList = Pattern.compile(",").splitAsStream(this.subscribers).collect( + Collectors.toList()); + try { + subscriptionManager.performBulkAppOperation(this.application, subscriberList, + this.subscriptionType, this.action); + subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); + } catch (ApplicationManagementException e) { + log.error( + "Error occurred while " + this.action + "ing application " + this.application + + "to/from the following " + this.subscriptionType + "s: " + this.subscribers, e); + subscriptionDTO.setStatus(ExecutionStatus.FAILED); + } + } + } else { + log.warn( + "Subscriber list is empty. Therefore skipping scheduled task to " + this.action + "application " + + this.application); + subscriptionDTO.setStatus(ExecutionStatus.FAILED); + } + subscriptionManager.updateScheduledSubscriptionStatus(subscriptionDTO.getId(), subscriptionDTO.getStatus()); + } catch (SubscriptionManagementException e) { + log.error("Error occurred while executing the task: " + this.taskName, e); + } finally { + PrivilegedCarbonContext.endTenantFlow(); + } + } +} 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 new file mode 100644 index 0000000000..c07053f3d5 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTaskManager.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.task; + +import com.google.gson.Gson; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.CronExpression; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.application.mgt.common.SubAction; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; +import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManagementException; +import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; +import org.wso2.carbon.device.application.mgt.core.exception.ApplicationOperationTaskException; +import org.wso2.carbon.device.application.mgt.core.impl.SubscriptionManagerImpl; +import org.wso2.carbon.device.application.mgt.core.internal.DataHolder; +import org.wso2.carbon.device.application.mgt.core.util.Constants; +import org.wso2.carbon.ntask.common.TaskException; +import org.wso2.carbon.ntask.core.TaskInfo; +import org.wso2.carbon.ntask.core.TaskManager; +import org.wso2.carbon.ntask.core.service.TaskService; + +import java.time.LocalDateTime; +import java.time.format.TextStyle; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; + +public class ScheduledAppSubscriptionTaskManager { + private static Log log = LogFactory.getLog(ScheduledAppSubscriptionTaskManager.class); + private static final String SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE = "SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE"; + private static final String NAME_SEPARATOR = "_"; + private SubscriptionManager subscriptionManager; + + public ScheduledAppSubscriptionTaskManager() { + this.subscriptionManager = new SubscriptionManagerImpl(); + } + + /** + * Schedule a task to install/uninstall application for a list of subscribers + * + * @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 subscriptionType subscription type. E.g. DEVICE, USER, ROLE, GROUP + * {@see {@link org.wso2.carbon.device.application.mgt.common.SubscriptionType}} + * @param action action subscription action. E.g. {@code INSTALL/UNINSTALL} + * {@see {@link org.wso2.carbon.device.application.mgt.common.SubAction}} + * @param timestamp timestamp to schedule the application subscription + * @throws ApplicationOperationTaskException if error occurred while scheduling the subscription + */ + public void scheduleAppSubscriptionTask(String applicationUUID, List subscribers, + SubscriptionType subscriptionType, SubAction action, LocalDateTime timestamp) + throws ApplicationOperationTaskException { + String space = " "; + String cronExpression = + String.valueOf(timestamp.getSecond()) + space + timestamp.getMinute() + space + timestamp.getHour() + + space + timestamp.getDayOfMonth() + space + timestamp.getMonth().getDisplayName(TextStyle.SHORT, + Locale.getDefault()).toUpperCase() + " ? " + timestamp.getYear(); + + if (!CronExpression.isValidExpression(cronExpression)) { + String msg = "The cron expression [" + cronExpression + "] generated by the" + " timestamp [" + timestamp + .toString() + "] is invalid"; + log.error(msg); + throw new ApplicationOperationTaskException(msg); + } + + try { + PrivilegedCarbonContext carbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + TaskService taskService = initializeTaskType(); + + TaskManager taskManager = taskService.getTaskManager(SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE); + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + triggerInfo.setCronExpression(cronExpression); + triggerInfo.setRepeatCount(0); + + Map taskProperties = new HashMap<>(); + taskProperties.put(Constants.SUB_TYPE, subscriptionType.toString()); + taskProperties.put(Constants.ACTION, action.toString()); + taskProperties.put(Constants.APP_UUID, applicationUUID); + taskProperties.put(Constants.TENANT_DOMAIN, carbonContext.getTenantDomain(true)); + taskProperties.put(Constants.SUBSCRIBER, carbonContext.getUsername()); + + 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); + } + if (log.isDebugEnabled()) { + log.debug("Scheduling a task to " + action.toString() + " application: " + applicationUUID + + " to/from the following " + subscriptionType.toString() + "S [" + subscribersString + "] at: " + + timestamp); + } + String taskName = subscriptionType.toString() + NAME_SEPARATOR + action.toString() + NAME_SEPARATOR + + DigestUtils.md5Hex(applicationUUID + NAME_SEPARATOR + subscribersString); + taskProperties.put(Constants.TASK_NAME, taskName); + TaskInfo taskInfo = new TaskInfo(taskName, ScheduledAppSubscriptionTask.class.getName(), + taskProperties, triggerInfo); + + ScheduledSubscriptionDTO subscriptionDTO = new ScheduledSubscriptionDTO(taskName, applicationUUID, + timestamp, subscribers, carbonContext.getUsername()); + subscriptionManager.createScheduledSubscription(subscriptionDTO); + + taskManager.registerTask(taskInfo); + if (!taskManager.isTaskScheduled(taskName)) { + taskManager.scheduleTask(taskName); + } else { + taskManager.rescheduleTask(taskName); + } + } catch (TaskException e) { + String msg = "Error occurred while scheduling a task to " + action.toString() + " application: " + + applicationUUID + " to/from given " + subscriptionType.toString() + "S"; + log.error(msg, e); + throw new ApplicationOperationTaskException(msg, e); + } catch (SubscriptionManagementException e) { + String msg = "Error occurred while writing the subscription to database"; + log.error(msg, e); + throw new ApplicationOperationTaskException(msg, e); + } + } + + /** + * Schedules a task to clean up the scheduled tasks from {@link org.wso2.carbon.ntask.core.TaskRepository} + */ + public void scheduleCleanupTask() { + try { + TaskService taskService = initializeTaskType(); + TaskManager taskManager = taskService.getTaskManager(SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE); + + TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo(); + triggerInfo.setCronExpression("0 0 0/24 ? * * *"); + + String taskName = "SCHEDULED_APP_SUBSCRIPTION_CLEANUP_TASK"; + TaskInfo taskInfo = new TaskInfo(taskName, ScheduledAppSubscriptionCleanupTask.class.getName(), null, + triggerInfo); + + taskManager.registerTask(taskInfo); + if (!taskManager.isTaskScheduled(taskName)) { + taskManager.scheduleTask(taskName); + } else { + taskManager.rescheduleTask(taskName); + } + } catch (TaskException e) { + log.error("Error occurred while scheduling a cleanup task."); + } catch (ApplicationOperationTaskException e) { + log.error(e.getMessage()); + } + } + + /** + * Initialize task type. + * + * @return {@link TaskService} + * @throws TaskException if error occurred while initializing task type + * @throws ApplicationOperationTaskException if unable to load {@link TaskService} + */ + private TaskService initializeTaskType() throws TaskException, ApplicationOperationTaskException { + TaskService taskService = DataHolder.getInstance().getTaskService(); + if (taskService == null) { + String msg = "Unable to load TaskService, hence unable to schedule the task."; + log.error(msg); + throw new ApplicationOperationTaskException(msg); + } + if (!taskService.getRegisteredTaskTypes().contains(SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE)) { + try { + List removedTaskList = subscriptionManager.cleanScheduledSubscriptions(); + removeScheduledSubscriptions(taskService, removedTaskList); + } catch (SubscriptionManagementException e) { + log.error("Error occurred while retrieving tasks to cleanup", e); + } + taskService.registerTaskType(SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE); + } + return taskService; + } + + /** + * Cleans up already scheduled subscriptions. + * + * @param taskList list of {@link ScheduledSubscriptionDTO}s to remove + */ + private void removeScheduledSubscriptions(TaskService taskService, List taskList) { + try { + TaskManager taskManager = taskService.getTaskManager(SCHEDULED_APP_SUBSCRIPTION_TASK_TYPE); + taskManager.getAllTasks().forEach( + task -> taskList.stream().filter(t -> t.getTaskName().equals(task.getName())).forEach(t -> { + try { + taskManager.deleteTask(t.getTaskName()); + } catch (TaskException e) { + log.error("Error occurred while removing the task: " + t.getTaskName(), e); + } + })); + } catch (TaskException e) { + log.error("Error occurred while removing tasks", e); + } + } +} 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 3de99585db..48939a36cb 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 @@ -16,6 +16,25 @@ * under the License. * */ + +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.util; import org.wso2.carbon.utils.CarbonUtils; @@ -49,6 +68,16 @@ public class Constants { public static final String GOOGLE_PLAY_STORE_URL = "https://play.google.com/store/apps/details?id="; public static final String APPLE_STORE_URL = "https://itunes.apple.com/country/app/app-name/id"; + // Subscription task related constants + public static final String SUBSCRIBERS = "SUBSCRIBERS"; + public static final String SUB_TYPE = "SUBSCRIPTION_TYPE"; + public static final String ACTION = "ACTION"; + public static final String APP_UUID = "APP_UUID"; + public static final String SUBSCRIBER = "SUBSCRIBER"; + 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"; + /** * Database types supported by Application Management. */ diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/DAOUtil.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/DAOUtil.java index 25dbacd6c2..00fb1e4cd6 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/DAOUtil.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/DAOUtil.java @@ -16,21 +16,45 @@ * under the License. * */ + +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.core.util; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONException; import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.application.mgt.common.ExecutionStatus; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationDTO; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationReleaseDTO; import org.wso2.carbon.device.application.mgt.common.dto.DeviceSubscriptionDTO; import org.wso2.carbon.device.application.mgt.common.dto.ReviewDTO; +import org.wso2.carbon.device.application.mgt.common.dto.ScheduledSubscriptionDTO; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager; import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; import org.wso2.carbon.device.application.mgt.core.exception.UnexpectedServerErrorException; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderServiceImpl; @@ -39,6 +63,8 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * This class is responsible for handling the utils of the Application Management DAO. @@ -237,6 +263,49 @@ public class DAOUtil { return reviewDTOs; } + public static ScheduledSubscriptionDTO loadScheduledSubscription(ResultSet rs) + throws SQLException, UnexpectedServerErrorException { + List subscriptionDTOs = loadScheduledSubscriptions(rs); + + if (subscriptionDTOs.isEmpty()) { + return null; + } + if (subscriptionDTOs.size() > 1) { + String msg = "Internal server error. Found more than one subscription for requested pending subscription"; + log.error(msg); + throw new UnexpectedServerErrorException(msg); + } + return subscriptionDTOs.get(0); + } + + public static List loadScheduledSubscriptions(ResultSet rs) throws SQLException { + List subscriptionDTOS = new ArrayList<>(); + while (rs.next()) { + ScheduledSubscriptionDTO subscription = new ScheduledSubscriptionDTO(); + subscription.setId(rs.getInt("ID")); + subscription.setTaskName(rs.getString("TASK_NAME")); + subscription.setApplicationUUID(rs.getString("APPLICATION_UUID")); + + if (subscription.getTaskName().startsWith(SubscriptionType.DEVICE.toString())) { + List deviceIdentifiers = new Gson().fromJson(rs.getString("SUBSCRIBER_LIST"), + new TypeToken>() { + }.getType()); + subscription.setSubscriberList(deviceIdentifiers); + } else { + List subscriberList = Pattern.compile(",").splitAsStream(rs.getString("SUBSCRIBER_LIST")) + .collect(Collectors.toList()); + subscription.setSubscriberList(subscriberList); + } + + subscription.setStatus(ExecutionStatus.valueOf(rs.getString("STATUS"))); + subscription.setScheduledAt(rs.getTimestamp("SCHEDULED_AT").toLocalDateTime()); + subscription.setScheduledBy(rs.getString("SCHEDULED_BY")); + subscription.setDeleted(rs.getBoolean("DELETED")); + subscriptionDTOS.add(subscription); + } + return subscriptionDTOS; + } + /** * Cleans up the statement and resultset after executing the query * 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 ebe2112ee1..937a4f55ba 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 @@ -35,6 +35,7 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.List; @@ -120,7 +121,12 @@ public interface SubscriptionManagementAPI { value = "The application ID and list of devices/users/roles", required = true ) - @Valid List deviceIdentifiers + @Valid List deviceIdentifiers, + @ApiParam( + name = "timestamp", + value = "Timestamp of scheduled install/uninstall operation" + ) + @QueryParam("timestamp") String timestamp ); @POST @@ -168,6 +174,11 @@ public interface SubscriptionManagementAPI { value = "Subscriber list of the application release.", required = true ) - @Valid List subscribers + @Valid List subscribers, + @ApiParam( + name = "timestamp", + value = "Timestamp of scheduled install/uninstall operation" + ) + @QueryParam("timestamp") String timestamp ); } 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 6dd826bd6d..ad7bfd8d62 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 @@ -15,19 +15,43 @@ * specific language governing permissions and limitations * under the License. */ + +/* + * Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.store.api.services.impl; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.application.mgt.common.ApplicationInstallResponse; -import org.wso2.carbon.device.application.mgt.common.SubsciptionType; +import org.wso2.carbon.device.application.mgt.common.ErrorResponse; +import org.wso2.carbon.device.application.mgt.common.SubAction; +import org.wso2.carbon.device.application.mgt.common.SubscriptionType; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; +import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; +import org.wso2.carbon.device.application.mgt.core.exception.ApplicationOperationTaskException; import org.wso2.carbon.device.application.mgt.core.exception.BadRequestException; import org.wso2.carbon.device.application.mgt.core.exception.ForbiddenException; import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException; -import org.wso2.carbon.device.application.mgt.store.api.services.SubscriptionManagementAPI; -import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; -import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; +import org.wso2.carbon.device.application.mgt.core.task.ScheduledAppSubscriptionTaskManager; import org.wso2.carbon.device.application.mgt.core.util.APIUtil; +import org.wso2.carbon.device.application.mgt.store.api.services.SubscriptionManagementAPI; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import javax.validation.Valid; @@ -35,7 +59,10 @@ import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; /** @@ -53,30 +80,36 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ public Response performAppOperationForDevices( @PathParam("uuid") String uuid, @PathParam("action") String action, - @Valid List deviceIdentifiers) { + @Valid List deviceIdentifiers, + @QueryParam("timestamp") String timestamp) { try { - SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); - ApplicationInstallResponse response = subscriptionManager - .performBulkAppOperation(uuid, deviceIdentifiers, SubsciptionType.DEVICE.toString(), action); - return Response.status(Response.Status.OK).entity(response).build(); + if (StringUtils.isEmpty(timestamp)) { + SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); + ApplicationInstallResponse response = subscriptionManager + .performBulkAppOperation(uuid, deviceIdentifiers, SubscriptionType.DEVICE.toString(), action); + return Response.status(Response.Status.OK).entity(response).build(); + } else { + return scheduleApplicationOperationTask(uuid, deviceIdentifiers, SubscriptionType.DEVICE, + SubAction.valueOf(action.toUpperCase()), timestamp); + } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUI: " + uuid; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); } catch (BadRequestException e) { String msg = "Found invalid payload for installing application which has UUID: " + uuid + ". Hence verify the payload"; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ForbiddenException e) { String msg = "Application release is not in the installable state. Hence you are not permitted to install " + "the application."; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while installing the application release which has UUID: " + uuid + " for devices"; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } @@ -88,31 +121,68 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{ @PathParam("uuid") String uuid, @PathParam("subType") String subType, @PathParam("action") String action, - @Valid List subscribers) { + @Valid List subscribers, + @QueryParam("timestamp") String timestamp) { try { - SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); - ApplicationInstallResponse response = subscriptionManager - .performBulkAppOperation(uuid, subscribers, subType, action); - return Response.status(Response.Status.OK).entity(response).build(); + if (StringUtils.isEmpty(timestamp)) { + SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager(); + ApplicationInstallResponse response = subscriptionManager + .performBulkAppOperation(uuid, subscribers, subType, action); + return Response.status(Response.Status.OK).entity(response).build(); + } else { + return scheduleApplicationOperationTask(uuid, subscribers, + SubscriptionType.valueOf(subType.toUpperCase()), SubAction.valueOf(action.toUpperCase()), + timestamp); + } } catch (NotFoundException e) { String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload"; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); } catch (BadRequestException e) { String msg = "Found invalid payload for installing application which has UUID: " + uuid + ". Hence verify the payload"; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ForbiddenException e) { String msg = "Application release is not in the installable state. Hence you are not permitted to install " + "the application."; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while installing the application release which has UUID: " + uuid + " for user devices"; - log.error(msg); + log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } + + /** + * 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 timestamp timestamp to schedule the application subscription + * @return {@link Response} of the operation + */ + private Response scheduleApplicationOperationTask(String applicationUUID, List subscribers, + SubscriptionType subType, SubAction subAction, String timestamp) { + try { + ScheduledAppSubscriptionTaskManager subscriptionTaskManager = new ScheduledAppSubscriptionTaskManager(); + subscriptionTaskManager.scheduleAppSubscriptionTask(applicationUUID, subscribers, subType, subAction, + LocalDateTime.parse(timestamp, DateTimeFormatter.ISO_LOCAL_DATE_TIME)); + } catch (ApplicationOperationTaskException e) { + String msg = "Error occurred while scheduling the application install operation"; + log.error(msg, e); + ErrorResponse errorResponse = new ErrorResponse(msg); + errorResponse.setDescription(e.getMessage()); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(errorResponse).build(); + } + return Response.status(Response.Status.CREATED).build(); + } } diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/datasources/application-mgt-datasources.xml b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/datasources/application-mgt-datasources.xml index 7038d24c55..7b564716e4 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/datasources/application-mgt-datasources.xml +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/datasources/application-mgt-datasources.xml @@ -17,31 +17,31 @@ --> - - org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader - + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + - - - APPM_DS - The datasource used for CDM Application Management - - jdbc/APPM_DS - - - - jdbc:h2:repository/database/WSO2DM_APPM_DB;DB_CLOSE_ON_EXIT=FALSE - wso2carbon - wso2carbon - org.h2.Driver - 50 - 60000 - true - SELECT 1 - 30000 - - - - + + + APPM_DS + The datasource used for CDM Application Management + + jdbc/APPM_DS + + + + jdbc:h2:repository/database/WSO2DM_APPM_DB;DB_CLOSE_ON_EXIT=FALSE + wso2carbon + wso2carbon + org.h2.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql index f184784d64..40f92fc669 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql @@ -256,3 +256,22 @@ CREATE TABLE IF NOT EXISTS AP_APP_SUB_OP_MAPPING ( REFERENCES AP_DEVICE_SUBSCRIPTION (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); CREATE INDEX fk_AP_APP_SUB_OP_MAPPING_AP_DEVICE_SUBSCRIPTION1_idx ON AP_APP_SUB_OP_MAPPING (AP_DEVICE_SUBSCRIPTION_ID ASC); + +-- ----------------------------------------------------- +-- Table AP_SCHEDULED_SUBSCRIPTION +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION( + ID INTEGER NOT NULL AUTO_INCREMENT, + TASK_NAME VARCHAR(100) NOT NULL, + APPLICATION_UUID VARCHAR(36) NOT NULL, + SUBSCRIBER_LIST LONGVARCHAR NOT NULL, + STATUS VARCHAR(15) NOT NULL, + SCHEDULED_AT TIMESTAMP NOT NULL, + SCHEDULED_BY VARCHAR(100) NOT NULL, + SCHEDULED_TIMESTAMP TIMESTAMP NOT NULL, + DELETED BOOLEAN, + PRIMARY KEY (ID), + CONSTRAINT fk_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE + FOREIGN KEY (APPLICATION_UUID) + REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION +); diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql index 8fa1a42b4b..dd65570dd4 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mssql.sql @@ -40,4 +40,19 @@ FOREIGN KEY(PLATFORM_ID) REFERENCES APPM_PLATFORM(ID) ON DELETE CASCADE, PRIMARY KEY (PLATFORM_ID, NAME) ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[AP_SCHEDULED_SUBSCRIPTION]') AND TYPE IN (N'U')) +CREATE TABLE AP_SCHEDULED_SUBSCRIPTION( + ID INT IDENTITY(1, 1) NOT NULL, + TASK_NAME VARCHAR(100) NOT NULL, + APPLICATION_UUID VARCHAR(36) NOT NULL, + SUBSCRIBER_LIST VARCHAR(MAX) NOT NULL, + STATUS VARCHAR(15) NOT NULL, + SCHEDULED_AT DATETIME NOT NULL, + SCHEDULED_BY VARCHAR(100) NOT NULL, + SCHEDULED_TIMESTAMP DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + DELETED BIT NULL, + PRIMARY KEY (ID), + FOREIGN KEY (APPLICATION_UUID) REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION +) + CREATE INDEX FK_PLATFROM_TENANT_MAPPING_PLATFORM ON APPM_PLATFORM_TENANT_MAPPING(PLATFORM_ID ASC); \ No newline at end of file diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql index ff4b953c9e..18fc3b90cb 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql @@ -331,3 +331,24 @@ CREATE TABLE IF NOT EXISTS `APP_MANAGER`.`AP_APP_OP_DEVICE_MAPPING` ( ON UPDATE NO ACTION) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; + +-- ----------------------------------------------------- +-- Table `APP_MANAGER`.`AP_SCHEDULED_SUBSCRIPTION` +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS `APP_MANAGER`.`AP_SCHEDULED_SUBSCRIPTION`( + `ID` INT NOT NULL AUTO_INCREMENT, + `TASK_NAME` VARCHAR(100) NOT NULL, + `APPLICATION_UUID` VARCHAR(36) NOT NULL, + `SUBSCRIBER_LIST` TEXT NOT NULL, + `STATUS` VARCHAR(15) NOT NULL, + `SCHEDULED_AT` TIMESTAMP NOT NULL, + `SCHEDULED_BY` VARCHAR(100) NOT NULL, + `SCHEDULED_TIMESTAMP` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `DELETED` BOOLEAN NOT NULL DEFAULT FALSE, + PRIMARY KEY (`ID`), + CONSTRAINT `fk_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE` + FOREIGN KEY (`APPLICATION_UUID`) + REFERENCES `AP_APP_RELEASE` (`UUID`) ON DELETE NO ACTION ON UPDATE NO ACTION +) +ENGINE = InnoDB +DEFAULT CHARACTER SET = utf8; diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql index 39f8aba3d9..5c426f682c 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/oracle.sql @@ -365,3 +365,20 @@ CREATE TABLE APPM_PLATFORM_TAG ( CREATE INDEX FK_APPM_PLATFORM_TAG_APP ON APPM_PLATFORM_TAG(PLATFORM_ID ASC) / + +CREATE TABLE AP_SCHEDULED_SUBSCRIPTION ( + ID number GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL, + TASK_NAME VARCHAR(100) NOT NULL, + APPLICATION_UUID VARCHAR(36) NOT NULL, + SUBSCRIBER_LIST VARCHAR(4000) NOT NULL, + STATUS VARCHAR(15) NOT NULL, + SCHEDULED_AT TIMESTAMP NOT NULL, + SCHEDULED_BY VARCHAR(100) NOT NULL, + SCHEDULED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + DELETED NUMBER(1) NOT_NULL DEFAULT 0, + PRIMARY KEY (ID), + CONSTRAINT FK_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE + FOREIGN KEY (APPLICATION_UUID) + REFERENCES AP_APP_RELEASE (UUID) +) +/ diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql index cfe8495940..b4c8587ec6 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/postgresql.sql @@ -253,3 +253,27 @@ CREATE TABLE IF NOT EXISTS APPM_RELEASE_PROPERTY ( ON UPDATE NO ACTION); CREATE INDEX FK_RELEASE_PROPERTY_APPLICATION_RELEASE ON APPM_RELEASE_PROPERTY(APPLICATION_RELEASE_ID ASC); + + +DROP TABLE IF EXISTS AP_SCHEDULED_SUBSCRIPTION; +DROP SEQUENCE IF EXISTS APPM_SCHEDULED_SUBSCRIPTION_PK_SEQ; +CREATE SEQUENCE APPM_SCHEDULED_SUBSCRIPTION_PK_SEQ; + +-- ----------------------------------------------------- +-- Table AP_SCHEDULED_SUBSCRIPTION +-- ----------------------------------------------------- +CREATE TABLE IF NOT EXISTS AP_SCHEDULED_SUBSCRIPTION( + ID INT DEFAULT NEXTVAL('APPM_SCHEDULED_SUBSCRIPTION_PK_SEQ') UNIQUE, + TASK_NAME VARCHAR(100) NOT NULL, + APPLICATION_UUID VARCHAR(36) NOT NULL, + SUBSCRIBER_LIST VARCHAR NOT NULL, + STATUS VARCHAR(15) NOT NULL, + SCHEDULED_AT TIMESTAMP NOT NULL, + SCHEDULED_BY VARCHAR(100) NOT NULL, + SCHEDULED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + DELETED BOOLEAN NOT NULL DEFAULT FALSE + PRIMARY KEY (ID), + CONSTRAINT FK_AP_SCHEDULED_SUBSCRIPTION_AP_APP_RELEASE + FOREIGN KEY (APPLICATION_UUID) + REFERENCES AP_APP_RELEASE (UUID) ON DELETE NO ACTION ON UPDATE NO ACTION +);