Implement scheduled application install/uninstall task

Fixes entgra/product-iots#160
4.x.x
Madawa Soysa 5 years ago committed by Dharmakeerthi Lasantha
parent 03e28c3eb9
commit c9f12d47cb

@ -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<ErrorListItem> errorItems = new ArrayList<>();
public ErrorResponse() {
}
public ErrorResponse(String message) {
this.message = message;
}
@JsonProperty(value = "code")
@ApiModelProperty(required = true, value = "")
public Integer getCode() {

@ -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
}

@ -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 <SUBSCRIPTION-TYPE>_<ACTION>_<HASH-VALUE>}
* {@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;
}
}

@ -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;
}
}

@ -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. <code>DEVICE, USER, ROLE, GROUP</code> {@see {
* @param action subscription action. E.g. <code>INSTALL/UNINSTALL</code> {@see {
* @param <T> 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}}
*/
<T> ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List<T> 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<ScheduledSubscriptionDTO> 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;
}

@ -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,

@ -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<Integer> 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<Integer> 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<ScheduledSubscriptionDTO> 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<ScheduledSubscriptionDTO> getNonExecutedSubscriptions() throws ApplicationManagementDAOException;
/**
* Retrieves a subscription by taskName which is in the <code>ExecutionStatus.PENDING</code> 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;
}

@ -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<Integer> 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<ScheduledSubscriptionDTO> 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<ScheduledSubscriptionDTO> 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);
}
}
}

@ -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);
}
}

@ -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<ScheduledSubscriptionDTO> cleanScheduledSubscriptions() throws SubscriptionManagementException {
try {
// Cleaning up already executed, missed and failed tasks
ConnectionManagerUtil.beginDBTransaction();
List<ScheduledSubscriptionDTO> taskList = subscriptionDAO.getScheduledSubscriptionByStatus(
ExecutionStatus.EXECUTED, false);
taskList.addAll(subscriptionDAO.getNonExecutedSubscriptions());
taskList.addAll(subscriptionDAO.getScheduledSubscriptionByStatus(ExecutionStatus.FAILED, false));
List<Integer> 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 <T> void validateRequest(List<T> 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<Integer> deviceSubIds = new ArrayList<>();
if (SubsciptionType.USER.toString().equalsIgnoreCase(subType)) {
if (SubscriptionType.USER.toString().equalsIgnoreCase(subType)) {
List<String> 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<String> 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<String> subscribedEntities = subscriptionDAO.getSubscribedGroupNames(params, tenantId);
if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) {
params.removeAll(subscribedEntities);

@ -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);
}
}

@ -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;
}
}

@ -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<String, String> 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.");
}
}
}

@ -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<String, String> 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<DeviceIdentifier> deviceIdentifiers = new Gson().fromJson(this.subscribers,
new TypeToken<List<DeviceIdentifier>>() {
}.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<String> 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();
}
}
}

@ -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. <code>DEVICE, USER, ROLE, GROUP</code>
* {@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<String, String> 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<ScheduledSubscriptionDTO> 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<ScheduledSubscriptionDTO> 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);
}
}
}

@ -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.
*/

@ -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<ScheduledSubscriptionDTO> 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<ScheduledSubscriptionDTO> loadScheduledSubscriptions(ResultSet rs) throws SQLException {
List<ScheduledSubscriptionDTO> 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<DeviceIdentifier> deviceIdentifiers = new Gson().fromJson(rs.getString("SUBSCRIBER_LIST"),
new TypeToken<List<DeviceIdentifier>>() {
}.getType());
subscription.setSubscriberList(deviceIdentifiers);
} else {
List<String> 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
*

@ -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<DeviceIdentifier> deviceIdentifiers
@Valid List<DeviceIdentifier> 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<String> subscribers
@Valid List<String> subscribers,
@ApiParam(
name = "timestamp",
value = "Timestamp of scheduled install/uninstall operation"
)
@QueryParam("timestamp") String timestamp
);
}

@ -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<DeviceIdentifier> deviceIdentifiers) {
@Valid List<DeviceIdentifier> 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<String> subscribers) {
@Valid List<String> 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. <code>DEVICE, USER, ROLE, GROUP</code>
* {@see {@link org.wso2.carbon.device.application.mgt.common.SubscriptionType}}
* @param subAction action subscription action. E.g. <code>INSTALL/UNINSTALL</code>
* {@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();
}
}

@ -17,31 +17,31 @@
-->
<datasources-configuration xmlns:svns="http://org.wso2.securevault/configuration">
<providers>
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
</providers>
<providers>
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
</providers>
<datasources>
<datasource>
<name>APPM_DS</name>
<description>The datasource used for CDM Application Management</description>
<jndiConfig>
<name>jdbc/APPM_DS</name>
</jndiConfig>
<definition type="RDBMS">
<configuration>
<url>jdbc:h2:repository/database/WSO2DM_APPM_DB;DB_CLOSE_ON_EXIT=FALSE</url>
<username>wso2carbon</username>
<password>wso2carbon</password>
<driverClassName>org.h2.Driver</driverClassName>
<maxActive>50</maxActive>
<maxWait>60000</maxWait>
<testOnBorrow>true</testOnBorrow>
<validationQuery>SELECT 1</validationQuery>
<validationInterval>30000</validationInterval>
</configuration>
</definition>
</datasource>
</datasources>
<datasources>
<datasource>
<name>APPM_DS</name>
<description>The datasource used for CDM Application Management</description>
<jndiConfig>
<name>jdbc/APPM_DS</name>
</jndiConfig>
<definition type="RDBMS">
<configuration>
<url>jdbc:h2:repository/database/WSO2DM_APPM_DB;DB_CLOSE_ON_EXIT=FALSE</url>
<username>wso2carbon</username>
<password>wso2carbon</password>
<driverClassName>org.h2.Driver</driverClassName>
<maxActive>50</maxActive>
<maxWait>60000</maxWait>
<testOnBorrow>true</testOnBorrow>
<validationQuery>SELECT 1</validationQuery>
<validationInterval>30000</validationInterval>
</configuration>
</definition>
</datasource>
</datasources>
</datasources-configuration>

@ -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
);

@ -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);

@ -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;

@ -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)
)
/

@ -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
);

Loading…
Cancel
Save