Merge branch 'lasantha' into 'master'

Adding heart beat management functionality for dynamic task distribution

See merge request entgra/carbon-device-mgt!695
revert-70ac1926
Inosh Perara 4 years ago
commit 5297dfa73d

@ -235,4 +235,4 @@ public class Utils {
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
return loadKeyStore(trustStorePath,tsPassword,TRUST_STORE_TYPE);
}
}
}

@ -23,13 +23,14 @@ 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 org.wso2.carbon.device.mgt.core.task.impl.RandomlyAssignedScheduleTask;
import java.util.Map;
public class ScheduledAppSubscriptionCleanupTask implements Task {
public class ScheduledAppSubscriptionCleanupTask extends RandomlyAssignedScheduleTask {
private static Log log = LogFactory.getLog(ScheduledAppSubscriptionCleanupTask.class);
private SubscriptionManager subscriptionManager;
private static final String TASK_NAME = "SCHEDULE_APP_SUBSCRIPTION_CLEANUP";
@Override
public void setProperties(Map<String, String> properties) {
@ -37,18 +38,25 @@ public class ScheduledAppSubscriptionCleanupTask implements Task {
}
@Override
public void init() {
public void executeRandomlyAssignedTask() {
try {
if(super.isQualifiedToExecuteTask()) {
subscriptionManager.cleanScheduledSubscriptions();
}
} catch (SubscriptionManagementException e) {
log.error("Error occurred while cleaning up tasks.");
}
}
@Override
protected void setup() {
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.");
}
public String getTaskName() {
return TASK_NAME;
}
}

@ -33,15 +33,17 @@ import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManage
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 org.wso2.carbon.device.mgt.core.task.impl.RandomlyAssignedScheduleTask;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class ScheduledAppSubscriptionTask implements Task {
public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask {
private static Log log = LogFactory.getLog(ScheduledAppSubscriptionTask.class);
private static final String TASK_NAME = "SCHEDULE_APP_SUBSCRIPTION";
private SubscriptionManager subscriptionManager;
private String subscribers;
private String subscriptionType;
@ -65,68 +67,75 @@ public class ScheduledAppSubscriptionTask implements Task {
}
@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);
public void executeRandomlyAssignedTask() {
if(super.isQualifiedToExecuteTask()) {
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);
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 {
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);
}
log.warn(
"Subscriber list is empty. Therefore skipping scheduled task to " + this.action + "application "
+ this.application);
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();
}
subscriptionManager.updateScheduledSubscriptionStatus(subscriptionDTO.getId(), subscriptionDTO.getStatus());
} catch (SubscriptionManagementException e) {
log.error("Error occurred while executing the task: " + this.taskName, e);
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
@Override
protected void setup() {
if (this.subscriptionManager == null) {
this.subscriptionManager = new SubscriptionManagerImpl();
}
}
@Override
public String getTaskName() {
return TASK_NAME;
}
}

@ -298,11 +298,7 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService
}
activityList.setList(activities);
activityList.setCount(count);
if (activities == null || activities.size() == 0) {
if (isIfModifiedSinceSet) {
return Response.notModified().build();
}
}
return Response.ok().entity(activityList).build();
} catch (OperationManagementException e) {
String msg

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://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.mgt.common;
public class DynamicTaskContext {
private int serverHashIndex;
private int activeServerCount;
private boolean partitioningEnabled = false;
public int getServerHashIndex() {
return serverHashIndex;
}
public void setServerHashIndex(int serverHashIndex) {
this.serverHashIndex = serverHashIndex;
}
public int getActiveServerCount() {
return activeServerCount;
}
public void setActiveServerCount(int activeServerCount) {
this.activeServerCount = activeServerCount;
}
public boolean isPartitioningEnabled() {
return partitioningEnabled;
}
public void setPartitioningEnabled(boolean partitioningEnabled) {
this.partitioningEnabled = partitioningEnabled;
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://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.mgt.common;
public class ServerCtxInfo {
private int activeServerCount;
private int localServerHashIdx;
public ServerCtxInfo(int activeServerCount, int localServerHashIdx){
this.activeServerCount = activeServerCount;
this.localServerHashIdx = localServerHashIdx;
}
public int getActiveServerCount() {
return activeServerCount;
}
public void setActiveServerCount(int activeServerCount) {
this.activeServerCount = activeServerCount;
}
public int getLocalServerHashIdx() {
return localServerHashIdx;
}
public void setLocalServerHashIdx(int localServerHashIdx) {
this.localServerHashIdx = localServerHashIdx;
}
}

@ -19,6 +19,7 @@ package org.wso2.carbon.device.mgt.common.operation.mgt;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
@ -47,7 +48,7 @@ public interface OperationManager {
void addTaskOperation(List<Device> devices, Operation operation) throws OperationManagementException;
void addTaskOperation(String deviceType, Operation operation) throws OperationManagementException;
void addTaskOperation(String deviceType, Operation operation, DynamicTaskContext dynamicTaskContext) throws OperationManagementException;
/**
* Method to retrieve the list of all operations to a device.

@ -114,7 +114,8 @@
org.scannotation.*,
org.wso2.carbon.event.processor.stub,
org.wso2.carbon.identity.jwt.client.extension.service,
org.apache.commons.codec.binary
org.apache.commons.codec.binary,
io.entgra.server.bootup.heartbeat.beacon
</Import-Package>
<Export-Package>
!org.wso2.carbon.device.mgt.core.internal,
@ -364,6 +365,10 @@
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
</dependency>
</dependencies>
</project>

@ -295,6 +295,18 @@ public interface DeviceDAO {
*/
List<Device> getDevices(PaginationRequest request, int tenantId) throws DeviceManagementDAOException;
/**
* This method is used to retrieve the devices of a given tenant as a paginated result, along the lines of
* activeServerCount and serverIndex
*
* @param request
* @param tenantId
* @param activeServerCount
* @param serverIndex
* @return
*/
List<Device> getAllocatedDevices(PaginationRequest request, int tenantId, int activeServerCount, int serverIndex) throws DeviceManagementDAOException;
/**
* This method is used to search for devices within a specific group.
*
@ -325,6 +337,18 @@ public interface DeviceDAO {
*/
List<Device> getDevices(String type, int tenantId) throws DeviceManagementDAOException;
/**
* This method is used to retrieve the list of devices attributed to a specific node
* when using dynamic partitioning to allocate tasks given the tenant and device type
* along with activeServerCount and serverIndex
*
* @param type device type.
* @param tenantId tenant id.
* @return returns list of devices of provided type.
* @throws DeviceManagementDAOException
*/
List<Device> getAllocatedDevices(String type, int tenantId, int activeServerCount, int serverIndex) throws DeviceManagementDAOException;
List<Device> getDevices(long timestamp, int tenantId) throws DeviceManagementDAOException;
/**

@ -788,13 +788,81 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
}
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while listing devices for type '" + type + "'", e);
String msg = "Error occurred while listing devices for type '" + type + "'";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return devices;
}
@Override
public List<Device> getAllocatedDevices(String type, int tenantId, int activeServerCount,
int serverIndex) throws DeviceManagementDAOException {
List<Device> devices = null;
try {
Connection conn = this.getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID," +
" d1.DESCRIPTION," +
" d1.NAME AS DEVICE_NAME," +
" d1.DEVICE_TYPE," +
" d1.DEVICE_IDENTIFICATION," +
" e.OWNER," +
" e.OWNERSHIP," +
" e.STATUS," +
" e.IS_TRANSFERRED," +
" e.DATE_OF_LAST_UPDATE," +
" e.DATE_OF_ENROLMENT," +
" e.ID AS ENROLMENT_ID " +
"FROM DM_ENROLMENT e," +
" (SELECT d.ID," +
" d.DESCRIPTION," +
" d.NAME," +
" d.DEVICE_IDENTIFICATION," +
" t.NAME AS DEVICE_TYPE" +
" FROM DM_DEVICE d, DM_DEVICE_TYPE t" +
" WHERE DEVICE_TYPE_ID = t.ID" +
" AND t.NAME = ?" +
" AND t.ID = d.DEVICE_TYPE_ID" +
" AND d.TENANT_ID = ?) d1 " +
"WHERE d1.ID = e.DEVICE_ID" +
" AND TENANT_ID = ?" +
" AND MOD(d1.ID, ?) = ? " +
"ORDER BY e.DATE_OF_LAST_UPDATE DESC";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, type);
stmt.setInt(2, tenantId);
stmt.setInt(3, tenantId);
stmt.setInt(4, activeServerCount);
stmt.setInt(5, serverIndex);
devices = new ArrayList<>();
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadActiveDevice(rs, false);
if (device != null) {
devices.add(device);
}
}
} catch (Exception e) {
String msg = "Error encountered while populating allocated active devices for server with index : " + serverIndex +
" active-server-count " + activeServerCount + " device-type " + type + " tenant-id " + tenantId;
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
} catch (SQLException e) {
String msg = "Error encountered while retrieving allocated devices for server with index : " + serverIndex +
" active-server-count " + activeServerCount + " device-type " + type + " tenant-id " + tenantId;
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
return devices;
}
@Override
public List<Device> getDevicesOfUser(String username, int tenantId) throws DeviceManagementDAOException {
Connection conn;

@ -172,6 +172,141 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getAllocatedDevices(PaginationRequest request, int tenantId, int activeServerCount, int serverIndex)
throws DeviceManagementDAOException {
List<Device> devices;
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
List<String> statusList = request.getStatusList();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
boolean isPartitionedTask = false;
try {
Connection conn = getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID, " +
"d1.DESCRIPTION, " +
"d1.NAME AS DEVICE_NAME, " +
"d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, " +
"e.OWNER, " +
"e.OWNERSHIP, " +
"e.STATUS, " +
"e.IS_TRANSFERRED, " +
"e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, " +
"e.ID AS ENROLMENT_ID " +
"FROM DM_ENROLMENT e, " +
"(SELECT d.ID, " +
"d.DESCRIPTION, " +
"d.NAME, " +
"d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE " +
"FROM DM_DEVICE d, DM_DEVICE_TYPE t ";
//Add the query to filter active devices on timestamp
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ?";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = d.ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") d1 WHERE d1.ID = e.DEVICE_ID AND TENANT_ID = ?";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
if (statusList != null && !statusList.isEmpty()) {
sql += buildStatusQuery(statusList);
isStatusProvided = true;
}
if (activeServerCount > 0){
sql = sql + " AND MOD(d1.ID, ?) = ?";
isPartitionedTask = true;
}
sql = sql + " LIMIT ?,?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
for (String status : statusList) {
stmt.setString(paramIdx++, status);
}
}
if (isPartitionedTask) {
stmt.setInt(paramIdx++, activeServerCount);
stmt.setInt(paramIdx++, serverIndex);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
try (ResultSet rs = stmt.executeQuery()) {
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all " +
"registered devices";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

@ -172,6 +172,144 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getAllocatedDevices(PaginationRequest request, int tenantId,
int activeServerCount, int serverIndex)
throws DeviceManagementDAOException {
Connection conn;
List<Device> devices = null;
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
List<String> statusList = request.getStatusList();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
boolean isPartitionedTask = false;
try {
conn = getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID, " +
"d1.DESCRIPTION, " +
"d1.NAME AS DEVICE_NAME, " +
"d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, " +
"e.OWNER, " +
"e.OWNERSHIP, " +
"e.STATUS, " +
"e.IS_TRANSFERRED, " +
"e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, " +
"e.ID AS ENROLMENT_ID " +
"FROM DM_ENROLMENT e, " +
"(SELECT d.ID, " +
"d.DESCRIPTION, " +
"d.NAME, " +
"d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE " +
"FROM DM_DEVICE d, " +
"DM_DEVICE_TYPE t ";
//Add the query to filter active devices on timestamp
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ?";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = d.ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") d1 WHERE d1.ID = e.DEVICE_ID AND TENANT_ID = ?";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
if (statusList != null && !statusList.isEmpty()) {
sql += buildStatusQuery(statusList);
isStatusProvided = true;
}
if (activeServerCount > 0){
sql = sql + " AND MOD(d1.ID, ?) = ?";
isPartitionedTask = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
for (String status : statusList) {
stmt.setString(paramIdx++, status);
}
}
if (isPartitionedTask) {
stmt.setInt(paramIdx++, activeServerCount);
stmt.setInt(paramIdx++, serverIndex);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
try (ResultSet rs = stmt.executeQuery()) {
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all " +
"registered devices";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

@ -165,6 +165,133 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getAllocatedDevices(PaginationRequest request, int tenantId,
int activeServerCount, int serverIndex)
throws DeviceManagementDAOException {
Connection conn;
List<Device> devices = null;
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
List<String> statusList = request.getStatusList();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
boolean isPartitionedTask = false;
try {
conn = getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID, " +
"d1.DESCRIPTION, " +
"d1.NAME AS DEVICE_NAME, " +
"d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, " +
"e.OWNER, " +
"e.OWNERSHIP, " +
"e.STATUS, " +
"e.IS_TRANSFERRED, " +
"e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, " +
"e.ID AS ENROLMENT_ID " +
"FROM DM_ENROLMENT e, " +
"(SELECT d.ID, " +
"d.DESCRIPTION, " +
"d.NAME, " +
"d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE " +
"FROM DM_DEVICE d, " +
"DM_DEVICE_TYPE t " +
"WHERE DEVICE_TYPE_ID = t.ID " +
"AND d.TENANT_ID = ?";
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") d1 WHERE d1.ID = e.DEVICE_ID AND TENANT_ID = ?";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
if (statusList != null && !statusList.isEmpty()) {
sql += buildStatusQuery(statusList);
isStatusProvided = true;
}
if (activeServerCount > 0){
sql = sql + " AND MOD(d1.ID, ?) = ?";
isPartitionedTask = true;
}
sql = sql + " LIMIT ? OFFSET ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setInt(paramIdx++, tenantId);
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
for (String status : statusList) {
stmt.setString(paramIdx++, status);
}
}
if (isPartitionedTask) {
stmt.setInt(paramIdx++, activeServerCount);
stmt.setInt(paramIdx++, serverIndex);
}
stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex());
try (ResultSet rs = stmt.executeQuery()) {
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all " +
"registered devices";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

@ -172,6 +172,143 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getAllocatedDevices(PaginationRequest request, int tenantId,
int activeServerCount, int serverIndex)
throws DeviceManagementDAOException {
Connection conn;
List<Device> devices = null;
String deviceType = request.getDeviceType();
boolean isDeviceTypeProvided = false;
String deviceName = request.getDeviceName();
boolean isDeviceNameProvided = false;
String owner = request.getOwner();
boolean isOwnerProvided = false;
String ownerPattern = request.getOwnerPattern();
boolean isOwnerPatternProvided = false;
String ownership = request.getOwnership();
boolean isOwnershipProvided = false;
List<String> statusList = request.getStatusList();
boolean isStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
boolean isPartitionedTask = false;
try {
conn = getConnection();
String sql = "SELECT d1.ID AS DEVICE_ID, " +
"d1.DESCRIPTION, " +
"d1.NAME AS DEVICE_NAME, " +
"d1.DEVICE_TYPE, " +
"d1.DEVICE_IDENTIFICATION, " +
"e.OWNER, " +
"e.OWNERSHIP, " +
"e.STATUS, " +
"e.IS_TRANSFERRED, " +
"e.DATE_OF_LAST_UPDATE, " +
"e.DATE_OF_ENROLMENT, " +
"e.ID AS ENROLMENT_ID " +
"FROM DM_ENROLMENT e, " +
"(SELECT d.ID, " +
"d.DESCRIPTION, " +
"d.NAME, " +
"d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE " +
"FROM DM_DEVICE d, DM_DEVICE_TYPE t ";
//Add the query to filter active devices on timestamp
if (since != null) {
sql = sql + ", DM_DEVICE_DETAIL dt";
isSinceProvided = true;
}
sql = sql + " WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ?";
//Add query for last updated timestamp
if (isSinceProvided) {
sql = sql + " AND dt.DEVICE_ID = d.ID AND dt.UPDATE_TIMESTAMP > ?";
}
//Add the query for device-type
if (deviceType != null && !deviceType.isEmpty()) {
sql = sql + " AND t.NAME = ?";
isDeviceTypeProvided = true;
}
//Add the query for device-name
if (deviceName != null && !deviceName.isEmpty()) {
sql = sql + " AND d.NAME LIKE ?";
isDeviceNameProvided = true;
}
sql = sql + ") d1 WHERE d1.ID = e.DEVICE_ID AND TENANT_ID = ?";
//Add the query for ownership
if (ownership != null && !ownership.isEmpty()) {
sql = sql + " AND e.OWNERSHIP = ?";
isOwnershipProvided = true;
}
//Add the query for owner
if (owner != null && !owner.isEmpty()) {
sql = sql + " AND e.OWNER = ?";
isOwnerProvided = true;
} else if (ownerPattern != null && !ownerPattern.isEmpty()) {
sql = sql + " AND e.OWNER LIKE ?";
isOwnerPatternProvided = true;
}
if (statusList != null && !statusList.isEmpty()) {
sql += buildStatusQuery(statusList);
isStatusProvided = true;
}
if (activeServerCount > 0){
sql = sql + " AND d1.ID % ? = ?";
isPartitionedTask = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIdx = 1;
stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) {
stmt.setLong(paramIdx++, since.getTime());
}
if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType);
}
if (isDeviceNameProvided) {
stmt.setString(paramIdx++, deviceName + "%");
}
stmt.setInt(paramIdx++, tenantId);
if (isOwnershipProvided) {
stmt.setString(paramIdx++, ownership);
}
if (isOwnerProvided) {
stmt.setString(paramIdx++, owner);
} else if (isOwnerPatternProvided) {
stmt.setString(paramIdx++, ownerPattern + "%");
}
if (isStatusProvided) {
for (String status : statusList) {
stmt.setString(paramIdx++, status);
}
}
if (isPartitionedTask) {
stmt.setInt(paramIdx++, activeServerCount);
stmt.setInt(paramIdx++, serverIndex);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
try (ResultSet rs = stmt.executeQuery()) {
devices = new ArrayList<>();
while (rs.next()) {
Device device = DeviceManagementDAOUtil.loadDevice(rs);
devices.add(device);
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all " +
"registered devices";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public List<Device> searchDevicesInGroup(PaginationRequest request, int tenantId)
throws DeviceManagementDAOException {

@ -18,6 +18,7 @@
package org.wso2.carbon.device.mgt.core.internal;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.wso2.carbon.device.mgt.common.DeviceStatusTaskPluginConfig;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManager;
@ -58,6 +59,7 @@ public class DeviceManagementDataHolder {
private DeviceInformationManager deviceInformationManager;
private LicenseManager licenseManager;
private RegistryService registryService;
private HeartBeatManagementService heartBeatService;
private LicenseConfig licenseConfig;
private ApplicationManager appManager;
private AppManagementConfig appManagerConfig;
@ -292,6 +294,15 @@ public class DeviceManagementDataHolder {
this.deviceInformationManager = deviceInformationManager;
}
public HeartBeatManagementService getHeartBeatService() {
return heartBeatService;
}
public void setHeartBeatService(
HeartBeatManagementService heartBeatService) {
this.heartBeatService = heartBeatService;
}
public void setEventConfigurationProviderService(EventConfigurationProviderService eventConfigurationService) {
this.eventConfigurationService = eventConfigurationService;
}
@ -307,4 +318,4 @@ public class DeviceManagementDataHolder {
public void setGeoLocationProviderService(GeoLocationProviderService geoLocationProviderService) {
this.geoLocationProviderService = geoLocationProviderService;
}
}
}

@ -17,6 +17,7 @@
*/
package org.wso2.carbon.device.mgt.core.internal;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
@ -136,6 +137,12 @@ import java.util.concurrent.TimeUnit;
* policy="dynamic"
* bind="setDeviceTypeGeneratorService"
* unbind="unsetDeviceTypeGeneratorService"
* @scr.reference name="entgra.heart.beat.service"
* interface="io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService"
* cardinality="0..1"
* policy="dynamic"
* bind="setHeartBeatService"
* unbind="unsetHeartBeatService"
*/
public class DeviceManagementServiceComponent {
@ -487,6 +494,28 @@ public class DeviceManagementServiceComponent {
DeviceManagementDataHolder.getInstance().setRegistryService(null);
}
/**
* Sets HeartBeatManagementService Service.
*
* @param heartBeatService An instance of HeartBeatManagementService
*/
protected void setHeartBeatService(HeartBeatManagementService heartBeatService) {
if (log.isDebugEnabled()) {
log.debug("Setting Heart Beat Service");
}
DeviceManagementDataHolder.getInstance().setHeartBeatService(heartBeatService);
}
/**
* Unsets Registry Service.
*/
protected void unsetHeartBeatService(HeartBeatManagementService heartBeatService) {
if (log.isDebugEnabled()) {
log.debug("Un setting Heart Beat Service");
}
DeviceManagementDataHolder.getInstance().setHeartBeatService(null);
}
protected void setDataSourceService(DataSourceService dataSourceService) {
/* This is to avoid mobile device management component getting initialized before the underlying datasources
are registered */

@ -24,6 +24,7 @@ import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
@ -306,7 +307,7 @@ public class OperationManagerImpl implements OperationManager {
}
@Override
public void addTaskOperation(String deviceType, Operation operation) throws OperationManagementException {
public void addTaskOperation(String deviceType, Operation operation, DynamicTaskContext dynamicTaskContext) throws OperationManagementException {
List<String> validStatuses = Arrays.asList(EnrolmentInfo.Status.ACTIVE.toString(),
EnrolmentInfo.Status.INACTIVE.toString(),
EnrolmentInfo.Status.UNREACHABLE.toString());
@ -322,7 +323,16 @@ public class OperationManagerImpl implements OperationManager {
paginationRequest = new PaginationRequest(start, batchSize);
paginationRequest.setStatusList(validStatuses);
paginationRequest.setDeviceType(deviceType);
List<Device> devices = deviceDAO.getDevices(paginationRequest, tenantId);
List<Device> devices;
if(dynamicTaskContext != null && dynamicTaskContext.isPartitioningEnabled()) {
devices = deviceDAO.getAllocatedDevices(paginationRequest, tenantId,
dynamicTaskContext.getActiveServerCount(),
dynamicTaskContext.getServerHashIndex());
} else {
devices = deviceDAO.getDevices(paginationRequest, tenantId);
}
if (devices.size() == batchSize) {
hasRecords = true;
start += batchSize;
@ -338,6 +348,9 @@ public class OperationManagerImpl implements OperationManager {
Map<Integer, Device> enrolments = new HashMap<>();
for (Device device : devices) {
enrolments.put(device.getEnrolmentInfo().getId(), device);
if(log.isDebugEnabled()){
log.info("Adding operation for device Id : " + device.getDeviceIdentifier());
}
}
if (operationDto.getControl() ==
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation.Control.NO_REPEAT) {

@ -55,6 +55,24 @@ public interface OperationMappingDAO {
long maxDuration, int deviceTypeId)
throws OperationManagementDAOException;
/**
* This method returns first pending/repeated operation available for each active enrolment of given device-type
* in a task partitioned execution scenario
* where the operation was created after the given timestamp.
*
* @param minDuration
* @param maxDuration
* @param deviceTypeId
* @param activeServerCount
* @param serverHashIndex
* @return
*/
List<OperationEnrolmentMapping> getFirstPendingOperationMappingsForActiveEnrolments(long minDuration,
long maxDuration, int deviceTypeId,
int activeServerCount, int serverHashIndex)
throws OperationManagementDAOException;
/**
* This method returns the timestamp of last completed Operation for each active enrolment of given device-type
* where the operation was completed after the given timestamp.
@ -67,4 +85,19 @@ public interface OperationMappingDAO {
Map<Integer, Long> getLastConnectedTimeForActiveEnrolments(long timeStamp, int deviceTypeId)
throws OperationManagementDAOException;
/**
* This method returns the timestamp of last completed Operation for each active enrolment of given device-type
* in a task partitioned execution scenario
* where the operation was completed after the given timestamp.
*
* @param timeStamp
* @param deviceTypeId
* @param activeServerCount
* @param serverHashIndex
* @return
*/
Map<Integer, Long> getLastConnectedTimeForActiveEnrolments(long timeStamp, int deviceTypeId, int activeServerCount, int serverHashIndex)
throws OperationManagementDAOException;
}

@ -18,6 +18,8 @@
*/
package org.wso2.carbon.device.mgt.core.operation.mgt.dao.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation;
@ -39,6 +41,8 @@ import java.util.Map;
public class OperationMappingDAOImpl implements OperationMappingDAO {
private static final Log log = LogFactory.getLog(OperationMappingDAOImpl.class);
@Override
public void addOperationMapping(Operation operation, Integer deviceId, boolean isScheduled, Device device, Integer tenantId) throws
OperationManagementDAOException {
@ -227,6 +231,47 @@ public class OperationMappingDAOImpl implements OperationMappingDAO {
return enrolmentOperationMappingList;
}
@Override
public List<OperationEnrolmentMapping> getFirstPendingOperationMappingsForActiveEnrolments(
long minDuration,
long maxDuration, int deviceTypeId,
int activeServerCount, int serverHashIndex) throws OperationManagementDAOException {
List<OperationEnrolmentMapping> enrolmentOperationMappingList;
try {
Connection conn = OperationManagementDAOFactory.getConnection();
//We are specifically looking for operation mappings in 'Pending' & 'Repeated' states. Further we want
//devices to be active at that moment. Hence filtering by 'ACTIVE' & 'UNREACHABLE' device states.
String sql = "SELECT ENROLMENT_ID, D.DEVICE_IDENTIFICATION AS DEVICE_IDENTIFIER, MIN(CREATED_TIMESTAMP) " +
"AS CREATED_TIMESTAMP, E.STATUS AS ENROLMENT_STATUS, E.TENANT_ID FROM " +
"DM_ENROLMENT_OP_MAPPING OP INNER JOIN DM_ENROLMENT E ON OP.ENROLMENT_ID = E.ID INNER JOIN " +
"DM_DEVICE D ON E.DEVICE_ID = D.ID WHERE " +
"OP.STATUS IN ('" + Operation.Status.PENDING.name() + "','" + Operation.Status.REPEATED.name() + "') " +
"AND OP.CREATED_TIMESTAMP BETWEEN ? AND ? AND E.STATUS IN ('" + EnrolmentInfo.Status.ACTIVE.name() +
"','" + EnrolmentInfo.Status.UNREACHABLE.name() + "') AND D.DEVICE_TYPE_ID = ? AND MOD(D.ID, ?) = ? GROUP BY ENROLMENT_ID," +
" D.DEVICE_IDENTIFICATION, E.STATUS, E.TENANT_ID";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, maxDuration);
stmt.setLong(2, minDuration);
stmt.setInt(3, deviceTypeId);
stmt.setInt(4, activeServerCount);
stmt.setInt(5, serverHashIndex);
try (ResultSet rs = stmt.executeQuery()) {
enrolmentOperationMappingList = new ArrayList<>();
while (rs.next()) {
OperationEnrolmentMapping enrolmentOperationMapping = this.getEnrolmentOpMapping(rs);
enrolmentOperationMappingList.add(enrolmentOperationMapping);
}
}
}
} catch (SQLException e) {
String msg = "Error occurred while fetching pending operation mappings for " +
"active devices of type '" + deviceTypeId + "'";
log.error(msg, e);
throw new OperationManagementDAOException(msg, e);
}
return enrolmentOperationMappingList;
}
@Override
public Map<Integer, Long> getLastConnectedTimeForActiveEnrolments(long timeStamp, int deviceTypeId) throws OperationManagementDAOException {
PreparedStatement stmt = null;
@ -259,6 +304,44 @@ public class OperationMappingDAOImpl implements OperationMappingDAO {
return lastConnectedTimeMap;
}
@Override
public Map<Integer, Long> getLastConnectedTimeForActiveEnrolments(long timeStamp,
int deviceTypeId,
int activeServerCount,
int serverHashIndex)
throws OperationManagementDAOException {
Map<Integer, Long> lastConnectedTimeMap = null;
try {
Connection conn = OperationManagementDAOFactory.getConnection();
//We are specifically looking for operation mappings in 'Pending' & 'Repeated' states. Further we want
//devices to be active at that moment. Hence filtering by 'ACTIVE' & 'UNREACHABLE' device states.
String sql = "SELECT OP.ENROLMENT_ID AS EID, MAX(OP.UPDATED_TIMESTAMP) AS LAST_CONNECTED_TIME FROM " +
"DM_ENROLMENT_OP_MAPPING OP INNER JOIN DM_ENROLMENT E ON OP.ENROLMENT_ID = E.ID INNER JOIN " +
"DM_DEVICE D ON E.DEVICE_ID = D.ID WHERE " +
"OP.STATUS = '" + Operation.Status.COMPLETED.name() + "'" +
"AND OP.UPDATED_TIMESTAMP >= ? AND E.STATUS IN ('" + EnrolmentInfo.Status.ACTIVE.name() +
"','" + EnrolmentInfo.Status.UNREACHABLE.name() + "') AND D.DEVICE_TYPE_ID = ? AND MOD(D.ID, ?) = ? GROUP BY ENROLMENT_ID";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setLong(1, timeStamp);
stmt.setInt(2, deviceTypeId);
stmt.setInt(3, activeServerCount);
stmt.setInt(4, serverHashIndex);
try (ResultSet rs = stmt.executeQuery()) {
lastConnectedTimeMap = new HashMap<>();
while (rs.next()) {
lastConnectedTimeMap.put(rs.getInt("EID"), rs.getLong("LAST_CONNECTED_TIME"));
}
}
}
} catch (SQLException e) {
String msg = "Error occurred while fetching last connected time for " +
"active devices of type '" + deviceTypeId + "'";
log.error(msg, e);
throw new OperationManagementDAOException(msg, e);
}
return lastConnectedTimeMap;
}
private OperationEnrolmentMapping getEnrolmentOpMapping(ResultSet rs) throws SQLException {
OperationEnrolmentMapping enrolmentOperationMapping = new OperationEnrolmentMapping();
enrolmentOperationMapping.setEnrolmentId(rs.getInt("ENROLMENT_ID"));

@ -231,6 +231,10 @@ public class OperationDAOUtil {
if (rs.getBoolean("IS_LARGE_RESPONSE")) {
largeResponseIDs.add(rs.getInt("OP_RES_ID"));
} else {
if (activityStatus.getResponses() == null) {
List<OperationResponse> operationResponses = new ArrayList<>();
activityStatus.setResponses(operationResponses);
}
activityStatus.getResponses().add(OperationDAOUtil.getOperationResponse(rs));
}
}

@ -37,6 +37,7 @@ package org.wso2.carbon.device.mgt.core.service;
import org.apache.commons.collections.map.SingletonMap;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
@ -108,6 +109,17 @@ public interface DeviceManagementProviderService {
*/
List<Device> getAllDevices(String deviceType, boolean requireDeviceInfo) throws DeviceManagementException;
/**
* Method returns a list of devices allocated to a specific node of the server, given the serverIndex and active server count
* @param deviceType
* @param activeServerCount
* @param serverIndex
* @return
* @throws DeviceManagementException
*/
List<Device> getAllocatedDevices(String deviceType, int activeServerCount, int serverIndex) throws DeviceManagementException;
/**
* Method to retrieve all the devices registered in the system.
*
@ -654,7 +666,7 @@ public interface DeviceManagementProviderService {
Activity addOperation(String type, Operation operation,
List<DeviceIdentifier> devices) throws OperationManagementException, InvalidDeviceException;
void addTaskOperation(String deviceType, Operation operation) throws OperationManagementException;
void addTaskOperation(String deviceType, Operation operation, DynamicTaskContext taskContext) throws OperationManagementException;
void addTaskOperation(String type, List<Device> devices, Operation operation)
throws OperationManagementException;

@ -57,6 +57,7 @@ import org.wso2.carbon.device.mgt.common.DeviceManager;
import org.wso2.carbon.device.mgt.common.DeviceNotification;
import org.wso2.carbon.device.mgt.common.DevicePropertyNotification;
import org.wso2.carbon.device.mgt.common.DeviceTransferRequest;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.InitialOperationConfig;
@ -770,6 +771,46 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
return allDevices;
}
@Override
public List<Device> getAllocatedDevices(String deviceType, int activeServerCount, int serverIndex) throws DeviceManagementException {
if (deviceType == null) {
String msg = "Device type is empty for method getAllDevices";
log.error(msg);
throw new DeviceManagementException(msg);
}
if (log.isDebugEnabled()) {
log.debug("Getting allocated Devices for Server with index "+ serverIndex + " and" +
" type '" + deviceType);
}
List<Device> allocatedDevices;
try {
DeviceManagementDAOFactory.openConnection();
allocatedDevices = deviceDAO.getAllocatedDevices(deviceType, this.getTenantId(), activeServerCount, serverIndex);
if (allocatedDevices == null) {
if (log.isDebugEnabled()) {
log.debug("No device is found upon the type '" + deviceType + "'");
}
return null;
}
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving all devices of type '" +
deviceType + "' that are being managed within the scope of current tenant";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the data source";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} catch (Exception e) {
String msg = "Error occurred while getting all devices of device type '" + deviceType + "'";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
return allocatedDevices;
}
@Override
public List<Device> getAllDevices() throws DeviceManagementException {
return this.getAllDevices(true);
@ -1839,8 +1880,8 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
}
@Override
public void addTaskOperation(String type, Operation operation) throws OperationManagementException {
pluginRepository.getOperationManager(type, this.getTenantId()).addTaskOperation(type, operation);
public void addTaskOperation(String type, Operation operation, DynamicTaskContext taskContext) throws OperationManagementException {
pluginRepository.getOperationManager(type, this.getTenantId()).addTaskOperation(type, operation, taskContext);
}
@Override

@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceStatusTaskPluginConfig;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
@ -36,6 +37,7 @@ import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOE
import org.wso2.carbon.device.mgt.core.operation.mgt.dao.OperationManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.status.task.DeviceStatusTaskException;
import org.wso2.carbon.device.mgt.core.task.impl.DynamicPartitionedScheduleTask;
import org.wso2.carbon.ntask.core.Task;
import java.sql.SQLException;
@ -47,7 +49,7 @@ import java.util.Map;
* This implements the Task service which monitors the device activity periodically & update the device-status if
* necessary.
*/
public class DeviceStatusMonitoringTask implements Task {
public class DeviceStatusMonitoringTask extends DynamicPartitionedScheduleTask {
private static final Log log = LogFactory.getLog(DeviceStatusMonitoringTask.class);
private String deviceType;
@ -64,12 +66,12 @@ public class DeviceStatusMonitoringTask implements Task {
}
@Override
public void init() {
protected void setup() {
}
@Override
public void execute() {
public void executeDynamicTask() {
List<OperationEnrolmentMapping> operationEnrolmentMappings;
List<EnrolmentInfo> enrolmentInfoTobeUpdated = new ArrayList<>();
Map<Integer, Long> lastActivities = null;
@ -77,9 +79,9 @@ public class DeviceStatusMonitoringTask implements Task {
DeviceIdentifier deviceIdentifier;
Device device;
try {
operationEnrolmentMappings = this.getOperationEnrolmentMappings();
operationEnrolmentMappings = this.getOperationEnrolmentMappings(super.getTaskContext());
if (!operationEnrolmentMappings.isEmpty()) {
lastActivities = this.getLastDeviceActivities();
lastActivities = this.getLastDeviceActivities(super.getTaskContext());
}
} catch (DeviceStatusTaskException e) {
log.error("Error occurred while fetching OperationEnrolment mappings of deviceType '" + deviceType + "'", e);
@ -120,6 +122,9 @@ public class DeviceStatusMonitoringTask implements Task {
DeviceCacheManagerImpl.getInstance().addDeviceToCache(deviceIdentifier, device, mapping.getTenantId());
}
enrolmentInfoTobeUpdated.add(enrolmentInfo);
if(log.isDebugEnabled()){
log.debug("Enrollment Information updated for device ID : " + device.getDeviceIdentifier());
}
}
}
@ -179,13 +184,21 @@ public class DeviceStatusMonitoringTask implements Task {
return updateStatus;
}
private List<OperationEnrolmentMapping> getOperationEnrolmentMappings() throws DeviceStatusTaskException {
private List<OperationEnrolmentMapping> getOperationEnrolmentMappings(DynamicTaskContext ctx) throws DeviceStatusTaskException {
List<OperationEnrolmentMapping> operationEnrolmentMappings;
try {
OperationManagementDAOFactory.openConnection();
operationEnrolmentMappings = OperationManagementDAOFactory.
getOperationMappingDAO().getFirstPendingOperationMappingsForActiveEnrolments(this.getMinTimeWindow(),
this.getMaxTimeWindow(), this.deviceTypeId);
if(ctx != null && ctx.isPartitioningEnabled()){
operationEnrolmentMappings = OperationManagementDAOFactory.
getOperationMappingDAO().getFirstPendingOperationMappingsForActiveEnrolments(this.getMinTimeWindow(),
this.getMaxTimeWindow(), this.deviceTypeId,
ctx.getActiveServerCount(), ctx.getServerHashIndex());
} else {
operationEnrolmentMappings = OperationManagementDAOFactory.
getOperationMappingDAO().getFirstPendingOperationMappingsForActiveEnrolments(this.getMinTimeWindow(),
this.getMaxTimeWindow(), this.deviceTypeId);
}
} catch (SQLException e) {
throw new DeviceStatusTaskException("Error occurred while getting Enrolment operation mappings for " +
"determining device status of deviceType '" + deviceType + "'", e);
@ -198,13 +211,20 @@ public class DeviceStatusMonitoringTask implements Task {
return operationEnrolmentMappings;
}
private Map<Integer, Long> getLastDeviceActivities() throws DeviceStatusTaskException {
private Map<Integer, Long> getLastDeviceActivities(DynamicTaskContext ctx) throws DeviceStatusTaskException {
Map<Integer, Long> lastActivities;
try {
OperationManagementDAOFactory.openConnection();
lastActivities = OperationManagementDAOFactory.
getOperationMappingDAO().getLastConnectedTimeForActiveEnrolments(this.getMaxTimeWindow(),
this.deviceTypeId);
if(ctx != null && ctx.isPartitioningEnabled()) {
lastActivities = OperationManagementDAOFactory.
getOperationMappingDAO().getLastConnectedTimeForActiveEnrolments(this.getMaxTimeWindow(),
this.deviceTypeId,
ctx.getActiveServerCount(), ctx.getServerHashIndex());
} else {
lastActivities = OperationManagementDAOFactory.
getOperationMappingDAO().getLastConnectedTimeForActiveEnrolments(this.getMaxTimeWindow(),
this.deviceTypeId);
}
} catch (SQLException e) {
throw new DeviceStatusTaskException("Error occurred while getting last activities for " +
"determining device status of deviceType '" + deviceType + "'", e);

@ -19,6 +19,8 @@
package org.wso2.carbon.device.mgt.core.task;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
public interface DeviceTaskManager {
// /**
@ -56,7 +58,7 @@ public interface DeviceTaskManager {
* This method will add the operations to devices
* @throws DeviceMgtTaskException
*/
void addOperations() throws DeviceMgtTaskException;
void addOperations(DynamicTaskContext dynamicTaskContext) throws DeviceMgtTaskException;
// /**

@ -39,6 +39,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
@ -52,7 +53,7 @@ import org.wso2.carbon.user.api.UserStoreException;
import java.util.List;
import java.util.Map;
public class DeviceDetailsRetrieverTask implements Task {
public class DeviceDetailsRetrieverTask extends DynamicPartitionedScheduleTask {
private static Log log = LogFactory.getLog(DeviceDetailsRetrieverTask.class);
private String deviceType;
@ -64,11 +65,7 @@ public class DeviceDetailsRetrieverTask implements Task {
}
@Override
public void init() {
}
@Override
public void execute() {
public void executeDynamicTask() {
deviceManagementProviderService = DeviceManagementDataHolder.getInstance()
.getDeviceManagementProvider();
OperationMonitoringTaskConfig operationMonitoringTaskConfig = deviceManagementProviderService
@ -125,7 +122,7 @@ public class DeviceDetailsRetrieverTask implements Task {
//pass the configurations also from here, monitoring tasks
try {
if (deviceManagementProviderService.isDeviceMonitoringEnabled(deviceType)) {
deviceTaskManager.addOperations();
deviceTaskManager.addOperations(super.getTaskContext());
}
} catch (DeviceMgtTaskException e) {
log.error("Error occurred while trying to add the operations to " +
@ -133,4 +130,8 @@ public class DeviceDetailsRetrieverTask implements Task {
}
}
@Override
protected void setup() {
}
}

@ -44,6 +44,7 @@ import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation;
import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation;
@ -115,7 +116,7 @@ public class DeviceTaskManagerImpl implements DeviceTaskManager {
@Override
public void addOperations() throws DeviceMgtTaskException {
public void addOperations(DynamicTaskContext dynamicTaskContext) throws DeviceMgtTaskException {
DeviceManagementProviderService deviceManagementProviderService = DeviceManagementDataHolder.getInstance().
getDeviceManagementProvider();
//list operations for device type
@ -133,7 +134,7 @@ public class DeviceTaskManagerImpl implements DeviceTaskManager {
operation.setType(Operation.Type.COMMAND);
operation.setCode(str);
try {
deviceManagementProviderService.addTaskOperation(deviceType, operation);
deviceManagementProviderService.addTaskOperation(deviceType, operation, dynamicTaskContext);
} catch (OperationManagementException e) {
throw new DeviceMgtTaskException("Error occurred while adding task operations to devices", e);
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://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.mgt.core.task.impl;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.ntask.core.Task;
public abstract class DynamicPartitionedScheduleTask implements Task {
private static final Log log = LogFactory.getLog(DynamicPartitionedScheduleTask.class);
private static DynamicTaskContext taskContext = null;
@Override
public final void init() {
try {
boolean dynamicTaskEnabled = DeviceManagementDataHolder.getInstance().getHeartBeatService().isTaskPartitioningEnabled();
if(dynamicTaskEnabled){
taskContext = new DynamicTaskContext();
taskContext.setPartitioningEnabled(true);
} else {
log.info("Error Instantiating Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function.");
}
} catch (HeartBeatManagementException e) {
log.error("Error Instantiating Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function." , e);
}
setup();
}
@Override
public final void execute() {
refreshContext();
executeDynamicTask();
}
public void refreshContext(){
if(taskContext != null && taskContext.isPartitioningEnabled()) {
try {
updateContext();
} catch (HeartBeatManagementException e) {
log.error("Error refreshing Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function.", e);
}
}
}
private void updateContext() throws HeartBeatManagementException {
ServerCtxInfo ctxInfo = DeviceManagementDataHolder.getInstance().getHeartBeatService().getServerCtxInfo();
if(ctxInfo != null) {
populateContext(ctxInfo);
} else {
log.info("Dynamic Task Context not present. Tasks will run on regular worker/manager mode.");
}
}
private void populateContext(ServerCtxInfo ctxInfo) {
taskContext.setActiveServerCount(ctxInfo.getActiveServerCount());
taskContext.setServerHashIndex(ctxInfo.getLocalServerHashIdx());
if(log.isDebugEnabled()){
log.debug("Initiating execution of dynamic task for server : " + taskContext.getServerHashIndex() +
" where active server count is : " + taskContext.getActiveServerCount() +
" partitioning task enabled : " + taskContext.isPartitioningEnabled());
}
}
protected abstract void setup();
protected abstract void executeDynamicTask();
public static DynamicTaskContext getTaskContext() {
return taskContext;
}
public static boolean isDynamicTaskEligible(){
if(taskContext != null && taskContext.isPartitioningEnabled()) {
return true;
} else {
return false;
}
}
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://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.mgt.core.task.impl;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.ntask.core.Task;
public abstract class RandomlyAssignedScheduleTask implements Task {
private static final Log log = LogFactory.getLog(RandomlyAssignedScheduleTask.class);
private static String taskName = "UNSPECIFIED";
private static boolean qualifiedToExecuteTask = false;
private static boolean dynamicTaskEnabled = false;
@Override
public final void init() {
try {
dynamicTaskEnabled = DeviceManagementDataHolder.getInstance().getHeartBeatService().isTaskPartitioningEnabled();
} catch (HeartBeatManagementException e) {
log.error("Error Instantiating Variables necessary for Randomly Assigned Task Scheduling." , e);
}
//This is done so that sub class extending this abstract class is forced to specify a task name.
taskName = getTaskName();
setup();
}
@Override
public final void execute() {
refreshContext();
executeRandomlyAssignedTask();
}
public void refreshContext(){
if(dynamicTaskEnabled) {
try {
qualifiedToExecuteTask = DeviceManagementDataHolder.getInstance().getHeartBeatService().isQualifiedToExecuteTask();
log.info("## NODE Qualified to execute Randomly Assigned Task : " + taskName);
DeviceManagementDataHolder.getInstance().getHeartBeatService().updateTaskExecutionAcknowledgement(taskName);
} catch (HeartBeatManagementException e) {
log.error("Error refreshing Variables necessary for Randomly Assigned Scheduled Task. " +
"Dynamic Tasks will not function.", e);
}
}
}
protected abstract void setup();
protected abstract void executeRandomlyAssignedTask();
public static boolean isQualifiedToExecuteTask() {
return qualifiedToExecuteTask;
}
public abstract String getTaskName();
}

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) 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.mgt.core;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
public class TestHeartBeatManagementService implements HeartBeatManagementService {
@Override
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
return false;
}
@Override
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
return null;
}
@Override
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
return null;
}
@Override
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
return false;
}
@Override
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
}
@Override
public boolean updateTaskExecutionAcknowledgement(String newTask)
throws HeartBeatManagementException {
return false;
}
@Override
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
return false;
}
}

@ -17,6 +17,7 @@
*/
package org.wso2.carbon.device.mgt.core.task;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.powermock.api.mockito.PowerMockito;
@ -32,6 +33,7 @@ import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementExcept
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManager;
import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService;
import org.wso2.carbon.device.mgt.core.TestDeviceManagementService;
import org.wso2.carbon.device.mgt.core.TestHeartBeatManagementService;
import org.wso2.carbon.device.mgt.core.TestUtils;
import org.wso2.carbon.device.mgt.core.authorization.DeviceAccessAuthorizationServiceImpl;
import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest;
@ -83,6 +85,9 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
DeviceManagementDataHolder.getInstance().setDeviceTaskManagerService(null);
DeviceManagementService deviceManagementService = new TestDeviceManagementService(
TestDataHolder.TEST_DEVICE_TYPE, MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
HeartBeatManagementService heartBeatManagementService = new TestHeartBeatManagementService();
DeviceManagementDataHolder.getInstance()
.setHeartBeatService(heartBeatManagementService);
this.operationManager = PowerMockito.spy(
new OperationManagerImpl(TestDataHolder.TEST_DEVICE_TYPE, deviceManagementService));
try {
@ -117,7 +122,7 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
@Test(groups = "Device Task Manager Test Group", description = "Testing adding operations to devices.")
public void testAddOperation() throws DeviceMgtTaskException, OperationManagementException {
log.info("Attempting to add operations for devices.");
this.deviceTaskManager.addOperations();
this.deviceTaskManager.addOperations(null);
for (DeviceIdentifier deviceId : deviceIds) {
List<? extends Operation> operationList = this.operationManager.getOperations(deviceId);
Assert.assertNotNull(operationList);
@ -133,7 +138,7 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
new TestDeviceManagementService(NEW_DEVICE_TYPE, TestDataHolder.SUPER_TENANT_DOMAIN));
DeviceTaskManager taskManager = new DeviceTaskManagerImpl(NEW_DEVICE_TYPE,
TestDataHolder.generateMonitoringTaskConfig(true, 50000, 3));
taskManager.addOperations();
taskManager.addOperations(null);
}
@Test(groups = "Device Task Manager Test Group", dependsOnMethods = "testAddOperationsWithoutDevices",
@ -141,7 +146,7 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
public void testAddOperationsWithoutOperations() throws DeviceMgtTaskException {
DeviceTaskManager taskManager = new DeviceTaskManagerImpl(NEW_DEVICE_TYPE,
TestDataHolder.generateMonitoringTaskConfig(true, 50000, 3));
taskManager.addOperations();
taskManager.addOperations(null);
}
@Test(groups = "Device Task Manager Test Group", description = "Testing device detail retriever task execution")

@ -0,0 +1,185 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>heartbeat-management</artifactId>
<version>4.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
<packaging>bundle</packaging>
<name>Entgra - Heartbeat Beacon</name>
<description>Entgra - Server Startup and Heartbeat Monitoring Component</description>
<url>http://www.entgra.io</url>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Bundle-Version>${carbon.device.mgt.version}</Bundle-Version>
<Bundle-Description>Server Startup Heart Beat Beacon Bundle</Bundle-Description>
<Private-Package>io.entgra.server.bootup.heartbeat.beacon.internal</Private-Package>
<Import-Package>
org.apache.axis2.*;version="${axis2.osgi.version.range}",
org.apache.axiom.*; version="${axiom.osgi.version.range}",
org.osgi.framework,
org.osgi.service.component,
org.apache.commons.logging,
javax.xml.parsers;version="${javax.xml.parsers.import.pkg.version}";resolution:=optional,
org.wso2.carbon.context,
org.wso2.carbon.utils.*,
org.wso2.carbon.ndatasource.core,
org.w3c.dom,
org.apache.velocity;version="${velocity.version}",
org.apache.velocity.app;version="${velocity.version}",
org.apache.velocity.context;version="${velocity.version}",
org.apache.velocity.exception;version="${velocity.version}",
org.apache.velocity.runtime.resource;version="${velocity.version}",
org.apache.velocity.runtime.resource.loader;version="${velocity.version}",
org.apache.commons.io,
org.apache.axis2.transport.mail,
org.apache.commons.collections,
org.wso2.carbon.device.mgt.common.*
</Import-Package>
<Export-Package>
!io.entgra.server.bootup.heartbeat.beacon.internal,
io.entgra.server.bootup.heartbeat.beacon.*
</Export-Package>
</instructions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<log4j.configuration>file:src/test/resources/log4j.properties</log4j.configuration>
</systemPropertyVariables>
<suiteXmlFiles>
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<configuration>
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
</configuration>
<executions>
<execution>
<id>jacoco-initialize</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>jacoco-site</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
<outputDirectory>${basedir}/target/coverage-reports/site</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.osgi</groupId>
<artifactId>org.eclipse.osgi.services</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.logging</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.utils</artifactId>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.base</artifactId>
</dependency>
<dependency>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.orbit.org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</dependency>
<dependency>
<groupId>commons-io.wso2</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.axis2.transport</groupId>
<artifactId>axis2-transport-mail</artifactId>
</dependency>
<dependency>
<groupId>commons-collections.wso2</groupId>
<artifactId>commons-collections</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>org.wso2.carbon.ndatasource.core</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon;
public class HeartBeatBeaconConfigurationException extends Exception {
private static final long serialVersionUID = -1043317705230442099L;
public HeartBeatBeaconConfigurationException(String msg, Exception nestedEx) {
super(msg, nestedEx);
}
public HeartBeatBeaconConfigurationException(String message, Throwable cause) {
super(message, cause);
}
public HeartBeatBeaconConfigurationException(String msg) {
super(msg);
}
public HeartBeatBeaconConfigurationException() {
super();
}
public HeartBeatBeaconConfigurationException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,130 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon;
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Hashtable;
import java.util.Properties;
public class HeartBeatBeaconUtils {
private static Log log = LogFactory.getLog(HeartBeatBeaconUtils.class);
public static Document convertToDocument(File file)
throws HeartBeatBeaconConfigurationException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setNamespaceAware(true);
try {
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
return docBuilder.parse(file);
} catch (Exception e) {
String msg = "Error occurred while parsing file, while converting " +
"to a org.w3c.dom.Document";
log.error(msg, e);
throw new HeartBeatBeaconConfigurationException(msg, e);
}
}
/**
* Lookup datasource using name and jndi properties
*
* @param dataSourceName Name of datasource to lookup
* @param jndiProperties Hash table of JNDI Properties
* @return datasource looked
*/
public static DataSource lookupDataSource(String dataSourceName,
final Hashtable<Object, Object> jndiProperties) {
try {
if (jndiProperties == null || jndiProperties.isEmpty()) {
return (DataSource) InitialContext.doLookup(dataSourceName);
}
final InitialContext context = new InitialContext(jndiProperties);
return (DataSource) context.doLookup(dataSourceName);
} catch (Exception e) {
String msg = "Error in looking up data source: " + e.getMessage();
log.error(msg);
throw new RuntimeException(msg, e);
}
}
public static ServerContext getServerDetails() throws UnknownHostException, SocketException {
InetAddress localHost = InetAddress.getLocalHost();
int iotsCorePort = Integer.parseInt(System.getProperty("iot.core.https.port"));
ServerContext ctx = new ServerContext();
ctx.setHostName(localHost.getHostName());
ctx.setCarbonServerPort(iotsCorePort);
return ctx;
}
public static void saveUUID(String text) throws IOException {
File serverDetails = new File(HeartBeatBeaconConfig.getInstance().getServerUUIDFileLocation());
serverDetails.createNewFile(); // if file already exists will do nothing
FileOutputStream fos = new FileOutputStream(serverDetails, false);
Properties prop = new Properties();
prop.setProperty("server.uuid", text);
prop.store(fos, null);
fos.close();
}
public static String readUUID() {
InputStream input = null;
String uuid = null;
try {
input = new FileInputStream(HeartBeatBeaconConfig.getInstance().getServerUUIDFileLocation());
Properties props = new Properties();
props.load(input);
uuid = props.getProperty("server.uuid");
input.close();
} catch (FileNotFoundException e) {
String msg = "File : server-credentials.properties does not exist, new UUID will be generated for server.";
if(log.isDebugEnabled()){
log.debug(msg, e);
}
log.info(msg);
} catch (IOException e) {
log.error("Error accessing server-credentials.properties to locate server.uuid.", e);
}
return uuid;
}
}

@ -0,0 +1,138 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.config;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconConfigurationException;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
import io.entgra.server.bootup.heartbeat.beacon.exception.InvalidConfigurationStateException;
import org.w3c.dom.Document;
import org.wso2.carbon.utils.CarbonUtils;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.File;
@XmlRootElement(name = "HeartBeatBeaconConfig")
public class HeartBeatBeaconConfig {
private boolean enabled;
private int notifierFrequency;
private int notifierDelay;
private int serverTimeOutIntervalInSeconds;
private int timeSkew;
private DataSourceConfig dataSourceConfig;
private static HeartBeatBeaconConfig config;
private static final String HEART_BEAT_NOTIFIER_CONFIG_PATH =
CarbonUtils.getCarbonConfigDirPath() + File.separator + "heart-beat-config.xml";
private static final String SERVER_UUID_FILE_LOCATION =
CarbonUtils.getCarbonConfigDirPath() + File.separator + "server-credentials.properties";
private HeartBeatBeaconConfig() {
}
public static HeartBeatBeaconConfig getInstance() {
if (config == null) {
throw new InvalidConfigurationStateException("Webapp Authenticator Configuration is not " +
"initialized properly");
}
return config;
}
@XmlElement(name = "NotifierInitialDelayInSeconds", required = true)
public int getNotifierDelay() {
return notifierDelay;
}
public void setNotifierDelay(int notifierDelay) {
this.notifierDelay = notifierDelay;
}
@XmlElement(name = "NotifierFrequencyInSeconds", required = true)
public int getNotifierFrequency() {
return notifierFrequency;
}
public void setNotifierFrequency(int notifierFrequency) {
this.notifierFrequency = notifierFrequency;
}
@XmlElement(name = "TimeSkewInSeconds", required = true)
public int getTimeSkew() {
return timeSkew;
}
public void setTimeSkew(int timeSkew) {
this.timeSkew = timeSkew;
}
@XmlElement(name = "ServerTimeOutIntervalInSeconds", required = true)
public int getServerTimeOutIntervalInSeconds() {
return serverTimeOutIntervalInSeconds;
}
public void setServerTimeOutIntervalInSeconds(int serverTimeOutIntervalInSeconds) {
this.serverTimeOutIntervalInSeconds = serverTimeOutIntervalInSeconds;
}
@XmlElement(name = "DataSourceConfiguration", required = true)
public DataSourceConfig getDataSourceConfig() {
return dataSourceConfig;
}
public void setDataSourceConfig(DataSourceConfig dataSourceConfig) {
this.dataSourceConfig = dataSourceConfig;
}
@XmlElement(name = "Enable", required = true)
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getServerUUIDFileLocation(){
return SERVER_UUID_FILE_LOCATION;
}
public static void init() throws HeartBeatBeaconConfigurationException {
try {
File emailSenderConfig = new File(HEART_BEAT_NOTIFIER_CONFIG_PATH);
Document doc = HeartBeatBeaconUtils.convertToDocument(emailSenderConfig);
/* Un-marshaling Email Sender configuration */
JAXBContext ctx = JAXBContext.newInstance(HeartBeatBeaconConfig.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
//unmarshaller.setSchema(getSchema());
config = (HeartBeatBeaconConfig) unmarshaller.unmarshal(doc);
} catch (JAXBException e) {
throw new HeartBeatBeaconConfigurationException("Error occurred while un-marshalling " +
"heart beat configuration file", e);
}
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.config.datasource;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Class for holding data source configuration in cdm-config.xml at parsing with JAXB
*/
@XmlRootElement(name = "DataSourceConfiguration")
public class DataSourceConfig {
private JNDILookupDefinition jndiLookupDefinition;
@XmlElement(name = "JndiLookupDefinition", nillable = true)
public JNDILookupDefinition getJndiLookupDefinition() {
return jndiLookupDefinition;
}
public void setJndiLookupDefinition(JNDILookupDefinition jndiLookupDefinition) {
this.jndiLookupDefinition = jndiLookupDefinition;
}
}

@ -0,0 +1,83 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.config.datasource;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlValue;
import java.util.List;
/**
* Class for hold JndiLookupDefinition of cdm-config.xml at parsing with JAXB
*/
@XmlRootElement(name = "JndiLookupDefinition")
public class JNDILookupDefinition {
private String jndiName;
private List<JNDIProperty> jndiProperties;
@XmlElement(name = "Name", nillable = false)
public String getJndiName() {
return jndiName;
}
public void setJndiName(String jndiName) {
this.jndiName = jndiName;
}
@XmlElementWrapper(name = "Environment", nillable = false)
@XmlElement(name = "Property", nillable = false)
public List<JNDIProperty> getJndiProperties() {
return jndiProperties;
}
public void setJndiProperties(List<JNDIProperty> jndiProperties) {
this.jndiProperties = jndiProperties;
}
@XmlRootElement(name = "Property")
public static class JNDIProperty {
private String name;
private String value;
@XmlAttribute(name = "Name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@XmlValue
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}

@ -0,0 +1,204 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dao;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.JNDILookupDefinition;
import io.entgra.server.bootup.heartbeat.beacon.dao.impl.GenericHeartBeatDAOImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.List;
/**
* This class represents factory for group management data operations
*/
public class HeartBeatBeaconDAOFactory {
private static final Log log = LogFactory.getLog(HeartBeatBeaconDAOFactory.class);
private static DataSource dataSource;
private static ThreadLocal<Connection> currentConnection = new ThreadLocal<>();
/**
* Get instance of GroupDAO
*
* @return instance of GroupDAO implementation
*/
public static HeartBeatDAO getHeartBeatDAO() {
return new GenericHeartBeatDAOImpl();
}
public static void init(DataSourceConfig config) {
dataSource = resolveDataSource(config);
}
public static void init(DataSource dtSource) {
dataSource = dtSource;
}
/**
* Begin transaction with datasource for write data
*
* @throws TransactionManagementException
*/
public static void beginTransaction() throws TransactionManagementException {
Connection conn = currentConnection.get();
if (conn != null) {
throw new IllegalTransactionStateException("A transaction is already active within the context of " +
"this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " +
"transaction is already active is a sign of improper transaction handling");
}
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);
currentConnection.set(conn);
} catch (SQLException e) {
throw new TransactionManagementException("Error occurred while retrieving config.datasource connection", e);
}
}
/**
* Open connection to the datasource for read data
*
* @throws SQLException
*/
public static void openConnection() throws SQLException {
Connection conn = currentConnection.get();
if (conn != null) {
throw new IllegalTransactionStateException("A transaction is already active within the context of " +
"this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " +
"transaction is already active is a sign of improper transaction handling");
}
conn = dataSource.getConnection();
currentConnection.set(conn);
}
/**
* Get current connection to datasource
*
* @return current connection
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
"This might have ideally been caused by not properly initiating the transaction via " +
"'beginTransaction'/'openConnection' methods");
}
return conn;
}
/**
* Commit current transaction to the datasource
*/
public static void commitTransaction() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
"This might have ideally been caused by not properly initiating " +
"the transaction via 'beginTransaction'/'openConnection' methods");
}
try {
conn.commit();
} catch (SQLException e) {
log.error("Error occurred while committing the transaction", e);
}
}
/**
* Rollback current transaction on failure
*/
public static void rollbackTransaction() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
"This might have ideally been caused by not properly initiating " +
"the transaction via 'beginTransaction'/'openConnection' methods");
}
try {
conn.rollback();
} catch (SQLException e) {
log.warn("Error occurred while roll-backing the transaction", e);
}
}
/**
* Close data connection associated with current transaction
*/
public static void closeConnection() {
Connection conn = currentConnection.get();
if (conn == null) {
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
"This might have ideally been caused by not properly " +
"initiating the transaction via " +
"'beginTransaction'/'openConnection' methods");
}
try {
conn.close();
} catch (SQLException e) {
log.warn("Error occurred while close the connection");
}
currentConnection.remove();
}
/**
* Resolve data source from the data source definition
*
* @param config data source configuration
* @return data source resolved from the data source definition
*/
private static DataSource resolveDataSource(DataSourceConfig config) {
DataSource dataSource = null;
if (config == null) {
throw new RuntimeException(
"Device Management Repository data source configuration " + "is null and " +
"thus, is not initialized");
}
JNDILookupDefinition jndiConfig = config.getJndiLookupDefinition();
if (jndiConfig != null) {
if (log.isDebugEnabled()) {
log.debug("Initializing Device Management Repository data source using the JNDI " +
"Lookup Definition");
}
List<JNDILookupDefinition.JNDIProperty> jndiPropertyList =
jndiConfig.getJndiProperties();
if (jndiPropertyList != null) {
Hashtable<Object, Object> jndiProperties = new Hashtable<Object, Object>();
for (JNDILookupDefinition.JNDIProperty prop : jndiPropertyList) {
jndiProperties.put(prop.getName(), prop.getValue());
}
dataSource = HeartBeatBeaconUtils.lookupDataSource(jndiConfig.getJndiName(), jndiProperties);
} else {
dataSource = HeartBeatBeaconUtils.lookupDataSource(jndiConfig.getJndiName(), null);
}
}
return dataSource;
}
}

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dao;
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import java.util.List;
import java.util.Map;
/**
* This interface represents the key operations associated with persisting group related information.
*/
public interface HeartBeatDAO {
String recordServerCtx(ServerContext ctx) throws HeartBeatDAOException;
boolean recordHeatBeat(HeartBeatEvent event) throws HeartBeatDAOException;
boolean checkUUIDValidity(String uuid) throws HeartBeatDAOException;
String retrieveExistingServerCtx(ServerContext ctx) throws HeartBeatDAOException;
Map<String, ServerContext> getActiveServerDetails(int elapsedTimeInSeconds) throws HeartBeatDAOException;
boolean recordElectedCandidate(String serverUUID) throws HeartBeatDAOException;
void purgeCandidates() throws HeartBeatDAOException;
ElectedCandidate retrieveCandidate() throws HeartBeatDAOException;
boolean acknowledgeTask(String uuid, List<String> taskList) throws HeartBeatDAOException;
}

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dao.exception;
/**
* Custom exception class for group management data access related exceptions.
*/
public class HeartBeatDAOException extends Exception {
private static final long serialVersionUID = 2021891706072918864L;
private String message;
/**
* Constructs a new exception with the specified detail message and nested exception.
*
* @param message error message
* @param nestedException exception
*/
public HeartBeatDAOException(String message, Exception nestedException) {
super(message, nestedException);
setErrorMessage(message);
}
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message the detail message.
* @param cause the cause of this exception.
*/
public HeartBeatDAOException(String message, Throwable cause) {
super(message, cause);
setErrorMessage(message);
}
/**
* Constructs a new exception with the specified detail message
*
* @param message the detail message.
*/
public HeartBeatDAOException(String message) {
super(message);
setErrorMessage(message);
}
/**
* Constructs a new exception with the specified and cause.
*
* @param cause the cause of this exception.
*/
public HeartBeatDAOException(Throwable cause) {
super(cause);
}
public String getMessage() {
return message;
}
public void setErrorMessage(String errorMessage) {
this.message = errorMessage;
}
}

@ -0,0 +1,251 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dao.impl;
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatDAO;
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
import io.entgra.server.bootup.heartbeat.beacon.dao.util.HeartBeatBeaconDAOUtil;
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* This class represents implementation of HeartBeatDAO
*/
public class GenericHeartBeatDAOImpl implements HeartBeatDAO {
private static final Log log = LogFactory.getLog(GenericHeartBeatDAOImpl.class);
@Override
public String recordServerCtx(ServerContext ctx) throws HeartBeatDAOException {
String uuid = null;
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String serverUUID = UUID.randomUUID().toString();
String sql;
sql = "INSERT INTO SERVER_HEART_BEAT_EVENTS(HOST_NAME, UUID, SERVER_PORT) VALUES (?, ?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, ctx.getHostName());
stmt.setString(2, serverUUID);
stmt.setInt(3, ctx.getCarbonServerPort());
if (stmt.executeUpdate() > 0) {
uuid = serverUUID;
}
}
} catch (SQLException e) {
String msg = "Error occurred while persisting server context for : '" +
"port '" + ctx.getCarbonServerPort() + "' " +
"hostname : '" + ctx.getHostName() + "'";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
return uuid;
}
@Override
public boolean recordElectedCandidate(String serverUUID) throws HeartBeatDAOException {
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql;
sql = "INSERT INTO ELECTED_LEADER_META_INFO(UUID) VALUES (?)";
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, serverUUID);
return stmt.executeUpdate() > 0;
}
} catch (SQLException e) {
String msg = "Error occurred while persisting UUID of chosen " +
"elected dynamic task execution candidate : " + serverUUID;
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
}
@Override
public void purgeCandidates() throws HeartBeatDAOException {
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
conn.setAutoCommit(false);
String sql = "TRUNCATE TABLE ELECTED_LEADER_META_INFO";
try (Statement stmt = conn.createStatement()) {
stmt.execute(sql);
conn.commit();
}
} catch (SQLException e) {
String msg = "Error occurred while truncating ELECTED_LEADER_META_INFO table.";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
}
@Override
public ElectedCandidate retrieveCandidate() throws HeartBeatDAOException {
ElectedCandidate candidate = null;
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql = "SELECT * from ELECTED_LEADER_META_INFO";
try (Statement stmt = conn.createStatement()) {
try (ResultSet resultSet = stmt.executeQuery(sql)) {
while (resultSet.next()) {
candidate = HeartBeatBeaconDAOUtil.populateCandidate(resultSet);
}
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving meta information of elected candidate";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
return candidate;
}
@Override
public boolean acknowledgeTask(String uuid, List<String> taskList)
throws HeartBeatDAOException {
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql;
sql = "UPDATE ELECTED_LEADER_META_INFO SET ACKNOWLEDGED_TASK_LIST = ? WHERE UUID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"UUID"})) {
stmt.setString(1, String.join(",", taskList));
stmt.setString(2, uuid);
return stmt.executeUpdate() > 0;
}
} catch (SQLException e) {
String msg = "Error occurred while updating task list of elected server : '" +
uuid + "' and task list " + taskList;
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
}
@Override
public boolean recordHeatBeat(HeartBeatEvent event) throws HeartBeatDAOException {
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql;
sql = "UPDATE SERVER_HEART_BEAT_EVENTS SET LAST_UPDATED_TIMESTAMP = ? WHERE UUID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"ID"})) {
stmt.setTimestamp(1, event.getTime());
stmt.setString(2, event.getServerUUID());
return stmt.executeUpdate() > 0;
}
} catch (SQLException e) {
String msg = "Error occurred while updating heartbeat event against server with UUID : '" +
event.getServerUUID() + "' and timestamp " + event.getTime();
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
}
@Override
public boolean checkUUIDValidity(String uuid) throws HeartBeatDAOException {
boolean result = false;
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql = "SELECT ID FROM SERVER_HEART_BEAT_EVENTS WHERE UUID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, uuid);
try (ResultSet resultSet = stmt.executeQuery()) {
if (resultSet.next()) {
result = true;
}
}
}
} catch (SQLException e) {
String msg = "Error occurred checking existense of UUID" + uuid +
" amongst heartbeat meta info.";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
return result;
}
@Override
public String retrieveExistingServerCtx(ServerContext ctx) throws HeartBeatDAOException {
String uuid = null;
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql = "SELECT UUID FROM SERVER_HEART_BEAT_EVENTS WHERE HOST_NAME = ? AND SERVER_PORT = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"UUID"})) {
stmt.setString(1, ctx.getHostName());
stmt.setInt(2, ctx.getCarbonServerPort());
try (ResultSet resultSet = stmt.executeQuery()) {
if (resultSet.next()) {
uuid = resultSet.getString("UUID");
}
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving meta information for heart beat event from " +
"port '" + ctx.getCarbonServerPort() + "' " +
"hostname : '" + ctx.getHostName() + "'";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
return uuid;
}
@Override
public Map<String, ServerContext> getActiveServerDetails(int elapsedTimeInSeconds)
throws HeartBeatDAOException {
Map<String, ServerContext> ctxList = new HashMap<>();
try {
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
String sql = "SELECT (@row_number:=@row_number + 1) AS IDX, UUID, HOST_NAME, SERVER_PORT from " +
"SERVER_HEART_BEAT_EVENTS, (SELECT @row_number:=-1) AS TEMP " +
"WHERE LAST_UPDATED_TIMESTAMP > ? " +
"ORDER BY UUID";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(elapsedTimeInSeconds)));
try (ResultSet resultSet = stmt.executeQuery()) {
while (resultSet.next()) {
ctxList.put(resultSet.getString("UUID"), HeartBeatBeaconDAOUtil.populateContext(resultSet));
}
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving acting server count with " +
"heartbeat updates within " + elapsedTimeInSeconds + " seconds.";
log.error(msg, e);
throw new HeartBeatDAOException(msg, e);
}
return ctxList;
}
}

@ -0,0 +1,105 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dao.util;
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Hashtable;
/**
* This class represents utilities required to work with group management data
*/
public final class HeartBeatBeaconDAOUtil {
private static final Log log = LogFactory.getLog(HeartBeatBeaconDAOUtil.class);
/**
* Cleanup resources used to transaction
*
* @param stmt Prepared statement used
* @param rs Obtained results set
*/
public static void cleanupResources(PreparedStatement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
log.warn("Error occurred while closing result set", e);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
log.warn("Error occurred while closing prepared statement", e);
}
}
}
/**
* Lookup datasource using name and jndi properties
*
* @param dataSourceName Name of datasource to lookup
* @param jndiProperties Hash table of JNDI Properties
* @return datasource looked
*/
public static DataSource lookupDataSource(String dataSourceName,
final Hashtable<Object, Object> jndiProperties) {
try {
if (jndiProperties == null || jndiProperties.isEmpty()) {
return (DataSource) InitialContext.doLookup(dataSourceName);
}
final InitialContext context = new InitialContext(jndiProperties);
return (DataSource) context.lookup(dataSourceName);
} catch (Exception e) {
throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e);
}
}
public static ServerContext populateContext(ResultSet resultSet) throws SQLException {
ServerContext ctx = new ServerContext();
ctx.setIndex(resultSet.getInt("IDX"));
ctx.setUuid(resultSet.getString("UUID"));
ctx.setHostName(resultSet.getString("HOST_NAME"));
ctx.setCarbonServerPort(resultSet.getInt("SERVER_PORT"));
return ctx;
}
public static ElectedCandidate populateCandidate(ResultSet resultSet) throws SQLException {
ElectedCandidate candidate = new ElectedCandidate();
candidate.setServerUUID(resultSet.getString("UUID"));
candidate.setTimeOfElection(resultSet.getTimestamp("ELECTED_TIME"));
String tasksList = resultSet.getString("ACKNOWLEDGED_TASK_LIST");
if(tasksList != null && !tasksList.isEmpty()){
candidate.setAcknowledgedTaskList(Arrays.asList(tasksList.split(",")));
}
return candidate;
}
}

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dto;
import java.sql.Timestamp;
import java.util.List;
public class ElectedCandidate {
private String serverUUID;
private Timestamp timeOfElection;
private List<String> acknowledgedTaskList = null;
public List<String> getAcknowledgedTaskList() {
return acknowledgedTaskList;
}
public void setAcknowledgedTaskList(List<String> acknowledgedTaskList) {
this.acknowledgedTaskList = acknowledgedTaskList;
}
public String getServerUUID() {
return serverUUID;
}
public void setServerUUID(String serverUUID) {
this.serverUUID = serverUUID;
}
public Timestamp getTimeOfElection() {
return timeOfElection;
}
public void setTimeOfElection(Timestamp timeOfElection) {
this.timeOfElection = timeOfElection;
}
}

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dto;
import java.sql.Timestamp;
public class HeartBeatEvent {
private String serverUUID;
private Timestamp time;
public HeartBeatEvent(String serverUUID){
this.serverUUID = serverUUID;
this.time = new Timestamp(System.currentTimeMillis());
}
public String getServerUUID() {
return serverUUID;
}
public void setServerUUID(String serverUUID) {
this.serverUUID = serverUUID;
}
public Timestamp getTime() {
return time;
}
public void setTime(Timestamp time) {
this.time = time;
}
}

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.dto;
public class ServerContext {
private String hostName;
private int carbonServerPort;
private String uuid;
private int index;
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public int getCarbonServerPort() {
return carbonServerPort;
}
public void setCarbonServerPort(int carbonServerPort) {
this.carbonServerPort = carbonServerPort;
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.exception;
public class HeartBeatManagementException extends Exception {
private static final long serialVersionUID = 5304352685379661115L;
public HeartBeatManagementException(String msg, Exception nestedEx) {
super(msg, nestedEx);
}
public HeartBeatManagementException(String message, Throwable cause) {
super(message, cause);
}
public HeartBeatManagementException(String msg) {
super(msg);
}
public HeartBeatManagementException() {
super();
}
public HeartBeatManagementException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,74 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.exception;
public class InvalidConfigurationStateException extends RuntimeException {
private static final long serialVersionUID = -3151279311329070297L;
private String errorMessage;
private int errorCode;
public InvalidConfigurationStateException(int errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public InvalidConfigurationStateException(int errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public int getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public InvalidConfigurationStateException(String msg, Exception nestedEx) {
super(msg, nestedEx);
setErrorMessage(msg);
}
public InvalidConfigurationStateException(String message, Throwable cause) {
super(message, cause);
setErrorMessage(message);
}
public InvalidConfigurationStateException(String msg) {
super(msg);
setErrorMessage(msg);
}
public InvalidConfigurationStateException() {
super();
}
public InvalidConfigurationStateException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.exception;
public class ServerStatusUpdationFailedException extends Exception {
private static final long serialVersionUID = -2610630531027402610L;
public ServerStatusUpdationFailedException(String msg, Exception nestedEx) {
super(msg, nestedEx);
}
public ServerStatusUpdationFailedException(String message, Throwable cause) {
super(message, cause);
}
public ServerStatusUpdationFailedException(String msg) {
super(msg);
}
public ServerStatusUpdationFailedException() {
super();
}
public ServerStatusUpdationFailedException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,99 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.internal;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementServiceImpl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.ndatasource.core.DataSourceService;
/**
* @scr.component name="io.entgra.server.bootup.heartbeat.beacon.heartbeatBeaconComponent"
* immediate="true"
* @scr.reference name="org.wso2.carbon.ndatasource"
* interface="org.wso2.carbon.ndatasource.core.DataSourceService"
* cardinality="1..1"
* policy="dynamic"
* bind="setDataSourceService"
* unbind="unsetDataSourceService"
*/
public class HeartBeatBeaconComponent {
private static Log log = LogFactory.getLog(HeartBeatBeaconComponent.class);
@SuppressWarnings("unused")
protected void activate(ComponentContext componentContext) {
try {
if (log.isDebugEnabled()) {
log.debug("Initializing heart beat management bundle");
}
this.registerHeartBeatServices(componentContext);
//heart beat notifier configuration */
HeartBeatBeaconConfig.init();
if(HeartBeatBeaconConfig.getInstance().isEnabled()) {
DataSourceConfig dsConfig = HeartBeatBeaconConfig.getInstance().getDataSourceConfig();
HeartBeatBeaconDAOFactory.init(dsConfig);
//Setting up executors to notify heart beat status */
HeartBeatExecutor.setUpNotifiers(HeartBeatBeaconUtils.getServerDetails());
}
if (log.isDebugEnabled()) {
log.debug("Heart Beat Notifier bundle has been successfully initialized");
}
} catch (Throwable e) {
log.error("Error occurred while initializing Heart Beat Notifier bundle", e);
}
}
@SuppressWarnings("unused")
protected void deactivate(ComponentContext componentContext) {
//do nothing
}
private void registerHeartBeatServices(ComponentContext componentContext) {
if (log.isDebugEnabled()) {
log.debug("Registering Heart Beat Management service");
}
HeartBeatManagementService heartBeatServiceProvider = new HeartBeatManagementServiceImpl();
HeartBeatBeaconDataHolder.getInstance().setHeartBeatManagementService(heartBeatServiceProvider);
componentContext.getBundleContext().registerService(HeartBeatManagementService.class, heartBeatServiceProvider, null);
}
protected void setDataSourceService(DataSourceService dataSourceService) {
/* This is to avoid mobile device management component getting initialized before the underlying datasources
are registered */
if (log.isDebugEnabled()) {
log.debug("Data source service set to heart beat management component");
}
}
protected void unsetDataSourceService(DataSourceService dataSourceService) {
//do nothing
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.internal;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
public class HeartBeatBeaconDataHolder {
private HeartBeatManagementService heartBeatManagementService;
private String localServerUUID;
private static HeartBeatBeaconDataHolder thisInstance = new HeartBeatBeaconDataHolder();
private HeartBeatBeaconDataHolder() {}
public static HeartBeatBeaconDataHolder getInstance() {
return thisInstance;
}
public HeartBeatManagementService getHeartBeatManagementService() {
return heartBeatManagementService;
}
public void setHeartBeatManagementService(HeartBeatManagementService heartBeatManagementService) {
this.heartBeatManagementService = heartBeatManagementService;
}
public String getLocalServerUUID() {
return localServerUUID;
}
public void setLocalServerUUID(String localServerUUID) {
this.localServerUUID = localServerUUID;
}
}

@ -0,0 +1,102 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.internal;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconConfigurationException;
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class HeartBeatExecutor {
private static Log log = LogFactory.getLog(HeartBeatExecutor.class);
private static final int DEFAULT__NOTIFIER_INTERVAL = 5;
private static final int DEFAULT_NOTIFIER_DELAY = 5;
private static HeartBeatBeaconConfig CONFIG;
static {
CONFIG = HeartBeatBeaconConfig.getInstance();
}
static void setUpNotifiers(ServerContext ctx) throws HeartBeatBeaconConfigurationException {
ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor();
if (CONFIG == null) {
String msg = "Error while initiating schedule taks for recording heartbeats.";
log.error(msg);
throw new HeartBeatBeaconConfigurationException(msg);
}
try {
String uuid = HeartBeatBeaconUtils.readUUID();
if (uuid == null) {
uuid = HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().updateServerContext(ctx);
HeartBeatBeaconUtils.saveUUID(uuid);
}
int timeOutIntervalInSeconds = CONFIG.getServerTimeOutIntervalInSeconds();
int timeSkew = CONFIG.getTimeSkew();
int cumilativeTimeOut = timeOutIntervalInSeconds + timeSkew;
final String designatedUUID = uuid;
HeartBeatBeaconDataHolder.getInstance().setLocalServerUUID(designatedUUID);
Runnable periodicTask = new Runnable() {
public void run() {
try {
recordHeartBeat(designatedUUID);
electDynamicTaskExecutionCandidate(cumilativeTimeOut);
} catch (Exception e) {
log.error("Error while executing record heart beat task. This will result in schedule operation malfunction.", e);
}
}
};
executor.scheduleAtFixedRate(periodicTask,
CONFIG.getNotifierDelay() != 0 ? CONFIG.getNotifierDelay() : DEFAULT_NOTIFIER_DELAY,
CONFIG.getNotifierFrequency() != 0 ? CONFIG.getNotifierFrequency() : DEFAULT__NOTIFIER_INTERVAL,
TimeUnit.SECONDS);
} catch (HeartBeatManagementException e) {
String msg = "Error occured while updating initial server context.";
log.error(msg);
throw new HeartBeatBeaconConfigurationException(msg, e);
} catch (IOException e) {
String msg = "Error while persisting UUID of server.";
log.error(msg);
throw new HeartBeatBeaconConfigurationException(msg, e);
}
}
static void recordHeartBeat(String uuid) throws HeartBeatManagementException {
HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().recordHeartBeat(new HeartBeatEvent(uuid));
}
static void electDynamicTaskExecutionCandidate(int cumilativeTimeOut)
throws HeartBeatManagementException {
HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().electCandidate(cumilativeTimeOut);
}
}

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.service;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
public interface HeartBeatManagementService {
boolean isTaskPartitioningEnabled() throws HeartBeatManagementException;
ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException;
String updateServerContext(ServerContext ctx) throws HeartBeatManagementException;
boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException;
void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException;
boolean updateTaskExecutionAcknowledgement(String newTask) throws HeartBeatManagementException;
boolean isQualifiedToExecuteTask() throws HeartBeatManagementException;
}

@ -0,0 +1,305 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
*
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package io.entgra.server.bootup.heartbeat.beacon.service;
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatDAO;
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import io.entgra.server.bootup.heartbeat.beacon.internal.HeartBeatBeaconDataHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class HeartBeatManagementServiceImpl implements HeartBeatManagementService {
private static final Log log = LogFactory.getLog(HeartBeatManagementServiceImpl.class);
private final HeartBeatDAO heartBeatDAO;
public HeartBeatManagementServiceImpl() {
this.heartBeatDAO = HeartBeatBeaconDAOFactory.getHeartBeatDAO();
}
@Override
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
int hashIndex = -1;
ServerContext localServerCtx = null;
ServerCtxInfo serverCtxInfo = null;
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
HeartBeatBeaconDAOFactory.openConnection();
int timeOutIntervalInSeconds = HeartBeatBeaconConfig.getInstance().getServerTimeOutIntervalInSeconds();
int timeSkew = HeartBeatBeaconConfig.getInstance().getTimeSkew();
int cumilativeTimeOut = timeOutIntervalInSeconds + timeSkew;
String localServerUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
Map<String, ServerContext> serverCtxMap = heartBeatDAO.getActiveServerDetails(cumilativeTimeOut);
if (!serverCtxMap.isEmpty()) {
localServerCtx = serverCtxMap.get(localServerUUID);
if (localServerCtx != null) {
hashIndex = localServerCtx.getIndex();
serverCtxInfo = new ServerCtxInfo(serverCtxMap.size(), hashIndex);
}
}
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the underlying data source";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} catch (HeartBeatDAOException e) {
String msg = "Error occurred while retrieving active server count.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Server Context Information Not available.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return serverCtxInfo;
}
@Override
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
boolean enabled = false;
if (HeartBeatBeaconConfig.getInstance() != null) {
enabled = HeartBeatBeaconConfig.getInstance().isEnabled();
} else {
String msg = "Issue instantiating heart beat config.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return enabled;
}
@Override
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
String uuid = null;
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
HeartBeatBeaconDAOFactory.beginTransaction();
uuid = heartBeatDAO.retrieveExistingServerCtx(ctx);
if (uuid == null) {
uuid = heartBeatDAO.recordServerCtx(ctx);
HeartBeatBeaconDAOFactory.commitTransaction();
}
} catch (HeartBeatDAOException e) {
String msg = "Error Occured while retrieving server context.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} catch (TransactionManagementException e) {
HeartBeatBeaconDAOFactory.rollbackTransaction();
String msg = "Error occurred while updating server context. Issue in opening a connection to the underlying data source";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Updating Server Context Failed.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return uuid;
}
@Override
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
boolean isQualified = false;
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
String localServerUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
HeartBeatBeaconDAOFactory.openConnection();
ElectedCandidate candidate = heartBeatDAO.retrieveCandidate();
if (candidate != null && candidate.getServerUUID().equalsIgnoreCase(localServerUUID)) {
isQualified = true;
if (log.isDebugEnabled()) {
log.debug("Node : " + localServerUUID + " Qualified to execute randomly assigned task.");
}
}
} catch (HeartBeatDAOException e) {
String msg = "Error occurred while checking if server is qualified to execute randomly designated task.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the underlying data source";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Error occurred while checking if server is qualified to execute randomly designated task.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return isQualified;
}
@Override
public boolean updateTaskExecutionAcknowledgement(String newTask)
throws HeartBeatManagementException {
boolean result = false;
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
String serverUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
HeartBeatBeaconDAOFactory.beginTransaction();
ElectedCandidate candidate = heartBeatDAO.retrieveCandidate();
if (candidate != null && candidate.getServerUUID().equals(serverUUID)) {
List<String> taskList = candidate.getAcknowledgedTaskList();
boolean taskExecuted = false;
for (String task : taskList) {
if (task.equalsIgnoreCase(newTask)) {
taskExecuted = true;
break;
}
}
if (!taskExecuted) {
taskList.add(newTask);
result = heartBeatDAO.acknowledgeTask(serverUUID, taskList);
HeartBeatBeaconDAOFactory.commitTransaction();
}
}
} catch (HeartBeatDAOException e) {
String msg = "Error occurred while updating acknowledged task.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} catch (TransactionManagementException e) {
HeartBeatBeaconDAOFactory.rollbackTransaction();
String msg = "Error occurred while updating acknowledged task.. Issue in opening a connection to the underlying data source";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Updating acknowledged task list failed.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return result;
}
@Override
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
HeartBeatBeaconDAOFactory.beginTransaction();
Map<String, ServerContext> servers = heartBeatDAO.getActiveServerDetails(elapsedTimeInSeconds);
if (servers != null && !servers.isEmpty()) {
ElectedCandidate presentCandidate = heartBeatDAO.retrieveCandidate();
if (presentCandidate != null) {
//if candidate is older than stipulated elapsed-time, purge and re-elect
if (presentCandidate.getTimeOfElection().before(new Timestamp(System.currentTimeMillis()
- TimeUnit.SECONDS.toMillis(elapsedTimeInSeconds)))) {
heartBeatDAO.purgeCandidates();
electCandidate(servers);
}
} else {
//first time execution, elect if not present
electCandidate(servers);
}
HeartBeatBeaconDAOFactory.commitTransaction();
}
} catch (HeartBeatDAOException e) {
String msg = "Error occurred while electing candidate for dynamic task execution.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} catch (TransactionManagementException e) {
HeartBeatBeaconDAOFactory.rollbackTransaction();
String msg = "Error occurred while electing candidate for dynamic task execution. Issue in opening a connection to the underlying data source";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Error electing candidate for dynamic task execution.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
}
private void electCandidate(Map<String, ServerContext> servers) throws HeartBeatDAOException {
String electedCandidate = getRandomElement(servers.keySet());
heartBeatDAO.recordElectedCandidate(electedCandidate);
}
private String getRandomElement(Set<String> valueSet) {
Random rand = new Random();
List<String> items = new ArrayList<>(valueSet);
return items.get(rand.nextInt(items.size()));
}
@Override
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
boolean operationSuccess = false;
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
try {
HeartBeatBeaconDAOFactory.beginTransaction();
if (heartBeatDAO.checkUUIDValidity(event.getServerUUID())) {
operationSuccess = heartBeatDAO.recordHeatBeat(event);
HeartBeatBeaconDAOFactory.commitTransaction();
} else {
String msg = "Server UUID Does not exist, heartbeat not recorded.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
} catch (HeartBeatDAOException e) {
String msg = "Error occurred while recording heart beat.";
log.error(msg);
throw new HeartBeatManagementException(msg, e);
} catch (TransactionManagementException e) {
HeartBeatBeaconDAOFactory.rollbackTransaction();
String msg = "Error occurred performing heart beat record transaction. " +
"Transaction rolled back.";
log.error(msg, e);
throw new HeartBeatManagementException(msg, e);
} finally {
HeartBeatBeaconDAOFactory.closeConnection();
}
} else {
String msg = "Heart Beat Configuration Disabled. Recording Heart Beat Failed.";
log.error(msg);
throw new HeartBeatManagementException(msg);
}
return operationSuccess;
}
}

@ -0,0 +1,32 @@
#
# Copyright 2020 Entgra Pvt. Ltd.. (http://entgra.io)
#
# Licensed 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.
#
#
# This is the log4j configuration file used by Entgra Pvt. Ltd.
#
# IMPORTANT : Please do not remove or change the names of any
# of the Appenders defined here. The layout pattern & log file
# can be changed using the WSO2 Carbon Management Console, and those
# settings will override the settings in this file.
#
log4j.rootLogger=INFO, STD_OUT
# Redirect log messages to console
log4j.appender.STD_OUT=org.apache.log4j.ConsoleAppender
log4j.appender.STD_OUT.Target=System.out
log4j.appender.STD_OUT.layout=org.apache.log4j.PatternLayout
log4j.appender.STD_OUT.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

@ -0,0 +1,29 @@
<!--
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://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.
-->
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="DeviceManagementCore">
<parameter name="useDefaultListeners" value="false"/>
<test name="HeartBeatBeaconTests" preserve-order="true">
<classes>
</classes>
</test>
</suite>

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>4.1.11-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>heartbeat-management</artifactId>
<packaging>pom</packaging>
<name>Entgra - Task Allocation Framework</name>
<url>http://entgra.io</url>
<modules>
<module>io.entgra.server.bootup.heartbeat.beacon</module>
</modules>
</project>

@ -18,6 +18,7 @@
package org.wso2.carbon.policy.mgt.common;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.device.mgt.common.policy.mgt.Profile;

@ -19,6 +19,7 @@
package org.wso2.carbon.policy.mgt.core.cache;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;

@ -21,6 +21,7 @@ package org.wso2.carbon.policy.mgt.core.cache.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import org.wso2.carbon.policy.mgt.core.cache.PolicyCacheManager;

@ -21,6 +21,7 @@ package org.wso2.carbon.policy.mgt.core.dao.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Profile;
import org.wso2.carbon.policy.mgt.core.dao.PolicyManagementDAOFactory;
import org.wso2.carbon.policy.mgt.core.dao.ProfileDAO;

@ -26,7 +26,7 @@ import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;
import org.wso2.carbon.device.mgt.core.config.policy.PolicyConfiguration;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.ntask.core.Task;
import org.wso2.carbon.device.mgt.core.task.impl.DynamicPartitionedScheduleTask;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import org.wso2.carbon.policy.mgt.core.cache.impl.PolicyCacheManagerImpl;
import org.wso2.carbon.policy.mgt.core.internal.PolicyManagementDataHolder;
@ -38,7 +38,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class DelegationTask implements Task {
public class DelegationTask extends DynamicPartitionedScheduleTask {
private static final Log log = LogFactory.getLog(DelegationTask.class);
private PolicyConfiguration policyConfiguration = DeviceConfigurationManager.getInstance()
@ -50,13 +50,7 @@ public class DelegationTask implements Task {
}
@Override
public void init() {
}
@Override
public void execute() {
public void executeDynamicTask() {
try {
PolicyManager policyManager = new PolicyManagerImpl();
UpdatedPolicyDeviceListBean updatedPolicyDeviceList = policyManager.applyChangesMadeToPolicies();
@ -74,13 +68,23 @@ public class DelegationTask implements Task {
List<Device> toBeNotified;
for (String deviceType : deviceTypes) {
try {
devices = new ArrayList<>();
toBeNotified = new ArrayList<>();
devices = new ArrayList<>(service.getAllDevices(deviceType, false));
if (super.isDynamicTaskEligible()) {
devices.addAll(service.getAllocatedDevices(deviceType,
super.getTaskContext().getActiveServerCount(),
super.getTaskContext().getServerHashIndex()));
} else {
devices.addAll(service.getAllDevices(deviceType, false));
}
for (Device device : devices) {
if (device != null && device.getEnrolmentInfo() != null
&& device.getEnrolmentInfo().getStatus() != EnrolmentInfo.Status.REMOVED) {
&& device.getEnrolmentInfo().getStatus() != EnrolmentInfo.Status.REMOVED) {
toBeNotified.add(device);
}
if (log.isDebugEnabled()) {
log.debug("Adding policy operation to device : " + device.getDeviceIdentifier());
}
}
if (!toBeNotified.isEmpty()) {
PolicyEnforcementDelegator enforcementDelegator = new PolicyEnforcementDelegatorImpl(
@ -99,4 +103,9 @@ public class DelegationTask implements Task {
log.error("Error occurred while getting the policies applied to devices.", e);
}
}
@Override
protected void setup() {
}
}

@ -22,6 +22,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.device.mgt.common.policy.mgt.Profile;
import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;

@ -19,6 +19,7 @@ package org.wso2.carbon.policy.mgt.core.mgt;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import org.wso2.carbon.policy.mgt.core.mgt.bean.UpdatedPolicyDeviceListBean;

@ -40,6 +40,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;

@ -20,6 +20,7 @@ package org.wso2.carbon.policy.mgt.core.mgt.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
import org.wso2.carbon.device.mgt.common.policy.mgt.Profile;
import org.wso2.carbon.device.mgt.common.policy.mgt.ProfileFeature;
import org.wso2.carbon.policy.mgt.common.ProfileManagementException;

@ -28,7 +28,7 @@ import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager;
import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.PolicyComplianceException;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.ntask.core.Task;
import org.wso2.carbon.device.mgt.core.task.impl.DynamicPartitionedScheduleTask;
import org.wso2.carbon.policy.mgt.core.internal.PolicyManagementDataHolder;
import org.wso2.carbon.policy.mgt.core.mgt.MonitoringManager;
@ -36,7 +36,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class MonitoringTask implements Task {
public class MonitoringTask extends DynamicPartitionedScheduleTask {
private static final Log log = LogFactory.getLog(MonitoringTask.class);
@ -45,16 +45,10 @@ public class MonitoringTask implements Task {
}
@Override
public void init() {
}
@Override
public void execute() {
public void executeDynamicTask() {
if (log.isDebugEnabled()) {
log.debug("Monitoring task started to run.");
}
this.executeforAllTenants();
}
@ -100,7 +94,6 @@ public class MonitoringTask implements Task {
}
private void executeTask() {
MonitoringManager monitoringManager = PolicyManagementDataHolder.getInstance().getMonitoringManager();
List<String> deviceTypes = new ArrayList<>();
List<String> configDeviceTypes = new ArrayList<>();
@ -125,7 +118,14 @@ public class MonitoringTask implements Task {
PolicyMonitoringManager monitoringService =
PolicyManagementDataHolder.getInstance().getDeviceManagementService()
.getPolicyMonitoringManager(deviceType);
List<Device> devices = deviceManagementProviderService.getAllDevices(deviceType, false);
List<Device> devices;
if(super.isDynamicTaskEligible()){
devices = deviceManagementProviderService.getAllocatedDevices(deviceType,
super.getTaskContext().getActiveServerCount(),
super.getTaskContext().getServerHashIndex());
} else {
devices = deviceManagementProviderService.getAllDevices(deviceType, false);
}
if (monitoringService != null && !devices.isEmpty()) {
List<Device> notifiableDevices = new ArrayList<>();
if (log.isDebugEnabled()) {
@ -139,6 +139,9 @@ public class MonitoringTask implements Task {
status.equals(EnrolmentInfo.Status.UNREACHABLE)) {
notifiableDevices.add(device);
}
if (log.isDebugEnabled()) {
log.debug("Adding monitoring operation to device : " + device.getDeviceIdentifier());
}
}
if (log.isDebugEnabled()) {
log.debug("Following '" + deviceType + "' devices selected to send the notification " +
@ -163,4 +166,8 @@ public class MonitoringTask implements Task {
}
}
@Override
protected void setup() {
}
}

@ -17,6 +17,7 @@
*/
package org.wso2.carbon.policy.mgt.core;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.testng.Assert;
@ -51,6 +52,7 @@ import org.wso2.carbon.policy.mgt.core.enforcement.DelegationTask;
import org.wso2.carbon.policy.mgt.core.internal.PolicyManagementDataHolder;
import org.wso2.carbon.policy.mgt.core.mgt.MonitoringManager;
import org.wso2.carbon.policy.mgt.core.mgt.impl.MonitoringManagerImpl;
import org.wso2.carbon.policy.mgt.core.mock.TestHeartBeatManagementService;
import org.wso2.carbon.policy.mgt.core.mock.TypeXDeviceManagementService;
import org.wso2.carbon.policy.mgt.core.task.MonitoringTask;
import org.wso2.carbon.policy.mgt.core.task.TaskScheduleService;
@ -93,6 +95,8 @@ public class PolicyManagerServiceImplTest extends BasePolicyManagementDAOTest {
DeviceManagementService deviceManagementService = new TypeXDeviceManagementService(DEVICE_TYPE_A);
deviceMgtService.registerDeviceType(deviceManagementService);
operationManager = new OperationManagerImpl(DEVICE_TYPE_A, deviceManagementService);
HeartBeatManagementService heartBeatManagementService = new TestHeartBeatManagementService();
DeviceManagementDataHolder.getInstance().setHeartBeatService(heartBeatManagementService);
enrollDevice(DEVICE1, DEVICE_TYPE_A);
createDeviceGroup(GROUP1);
DeviceGroup group1 = groupMgtService.getGroup(GROUP1, false);
@ -417,4 +421,4 @@ public class PolicyManagerServiceImplTest extends BasePolicyManagementDAOTest {
Assert.assertNotNull(currentProfile.getProfileFeaturesList().get(0).getFeatureCode(),
updatedProfile.getProfileFeaturesList().get(0).getFeatureCode());
}
}
}

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) 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.policy.mgt.core.mock;
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
public class TestHeartBeatManagementService implements HeartBeatManagementService {
@Override
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
return false;
}
@Override
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
return null;
}
@Override
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
return null;
}
@Override
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
return false;
}
@Override
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
}
@Override
public boolean updateTaskExecutionAcknowledgement(String newTask)
throws HeartBeatManagementException {
return false;
}
@Override
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
return false;
}
}

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://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.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>heart-beat-feature</artifactId>
<version>4.1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>io.entgra.server.heart.beat.feature</artifactId>
<packaging>pom</packaging>
<name>Entgra IoT - Heart Beat Feature</name>
<url>http://entgra.io</url>
<description>
This feature bundles for the heart beat beacon for Entgra IoT Server
</description>
<dependencies>
<dependency>
<groupId>com.h2database.wso2</groupId>
<artifactId>h2-database-engine</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
<artifactId>org.wso2.carbon.identity.oauth.stub</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.axis2.wso2</groupId>
<artifactId>axis2-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.geronimo.specs.wso2</groupId>
<artifactId>geronimo-stax-api_1.0_spec</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
<artifactId>axiom</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
</exclusion>
<exclusion>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>wstx-asl</artifactId>
</exclusion>
<exclusion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.analytics.data.publisher</artifactId>
</exclusion>
<exclusion>
<groupId>org.wso2.carbon.analytics</groupId>
<artifactId>org.wso2.carbon.analytics.api</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>generate-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>src/main/resources</outputDirectory>
<resources>
<resource>
<directory>resources</directory>
<includes>
<include>build.properties</include>
<include>p2.inf</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.wso2.maven</groupId>
<artifactId>carbon-p2-plugin</artifactId>
<version>${carbon.p2.plugin.version}</version>
<executions>
<execution>
<id>p2-feature-generation</id>
<phase>package</phase>
<goals>
<goal>p2-feature-gen</goal>
</goals>
<configuration>
<id>io.entgra.server.heart.beat</id>
<propertiesFile>../../../../features/etc/feature.properties
</propertiesFile>
<adviceFile>
<properties>
<propertyDef>org.wso2.carbon.p2.category.type:server
</propertyDef>
<propertyDef>org.eclipse.equinox.p2.type.group:true
</propertyDef>
</properties>
</adviceFile>
<bundles>
<bundleDef>
org.wso2.carbon.devicemgt:io.entgra.server.bootup.heartbeat.beacon:${carbon.device.mgt.version}
</bundleDef>
</bundles>
<importFeatures>
<importFeatureDef>org.wso2.carbon.core.server:${carbon.kernel.version}</importFeatureDef>
</importFeatures>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,22 @@
#
# Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
#
# Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
#
# Licensed under the Entgra Commercial License, Version 1.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://entgra.io/licenses/entgra-commercial/1.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.
#
jarProcessor.unsign=true
signJars=true
custom = true

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
~ Copyright (C) 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
~
~ Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
~
~ Licensed under the Entgra Commercial License, Version 1.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://entgra.io/licenses/entgra-commercial/1.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.
-->
<HeartBeatBeaconConfig>
<!--
~ Only to be turned on in distributed mode, in which case
~ Dynamic Partitoned Task, Randomly Assigned Task modes will be activated.
~ These modes are deviations from the regular worker manager mode, in which the server will function.
~
~ * DynamicPartitionedScheduleTask -> Tasks will execute on all nodes but will only focus on a subset of operations
~ broken up amongst active nodes based on following parameters that are made available via DynamicTaskContext abstract class.
~ - serverHashIndex : dynamic index assigned to each server based on number of active servers present
~ - activeServerCount : total no. of active servers present at a given time
~ - partitioningEnabled : whether or not heart-beat-config/dynamic task config is enabled.
~ Any task extending the DynamicPartitionedScheduleTask class will inherit the above variables.
~ These can then be used to split up the no. of devices said node is allocated with.
~
~ * RandomlyAssignedScheduleTask -> Tasks will only execute on a particular node elected at a time interval determined by ServerTimeOutIntervalInSeconds, parameter.
~ When running tasks in this mode, it is mandatory that each task be given a unique name, so as not to run in to scenarios where tasks execution is duplicated.
~ Each node, on a periodic basis will check to see if it is qualfied to execute the random task, if the following variable is true only said node will execute the task.
~ - qualifiedToExecuteTask : dynamic index assigned to each server based on number of active servers present
~ Any task extending the RandomlyAssignedScheduleTask class will inherit the above variable.
~ Note that random task may execute on any node.
-->
<Enable>false</Enable>
<!-- Heart Beat Data Source Name, this should be either MySQL, PostGreSQL, Oracle or MSSQL -->
<DataSourceConfiguration>
<JndiLookupDefinition>
<Name>jdbc/HeartBeat_DS</Name>
</JndiLookupDefinition>
</DataSourceConfiguration>
<NotifierInitialDelayInSeconds>30</NotifierInitialDelayInSeconds>
<NotifierFrequencyInSeconds>300</NotifierFrequencyInSeconds>
<TimeSkewInSeconds>5</TimeSkewInSeconds>
<ServerTimeOutIntervalInSeconds>600</ServerTimeOutIntervalInSeconds>
</HeartBeatBeaconConfig>

@ -0,0 +1,46 @@
<!--
~ Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
~
~ Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
~
~ Licensed under the Entgra Commercial License, Version 1.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://entgra.io/licenses/entgra-commercial/1.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.
-->
<datasources-configuration xmlns:svns="http://org.wso2.securevault/configuration">
<providers>
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
</providers>
<datasources>
<datasource>
<name>HeartBeat_DS</name>
<description>The datasource Server Heart Beat</description>
<jndiConfig>
<name>jdbc/HeartBeat_DS</name>
</jndiConfig>
<definition type="RDBMS">
<configuration>
<url>jdbc:mysql://localhost:3306/heart_beat</url>
<username>root</username>
<password>root</password>
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<maxActive>50</maxActive>
<maxWait>60000</maxWait>
<testOnBorrow>true</testOnBorrow>
<validationQuery>SELECT 1</validationQuery>
<validationInterval>30000</validationInterval>
</configuration>
</definition>
</datasource>
</datasources>
</datasources-configuration>

@ -0,0 +1,18 @@
-- -----------------------------------------------------
-- Table `SERVER_HEART_BEAT_EVENTS`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
ID INTEGER AUTO_INCREMENT NOT NULL,
HOST_NAME VARCHAR(100) NOT NULL,
UUID VARCHAR(100) NOT NULL,
SERVER_PORT INTEGER NOT NULL,
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (ID)
);
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
UUID VARCHAR(100) NOT NULL,
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
PRIMARY KEY (UUID)
);

@ -0,0 +1,19 @@
-- -----------------------------------------------------
-- Table `SERVER_HEART_BEAT_EVENTS`
-- -----------------------------------------------------
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[SERVER_HEART_BEAT_EVENTS]') AND TYPE IN (N'U'))
CREATE TABLE SERVER_HEART_BEAT_EVENTS (
ID INT NOT NULL AUTO_INCREMENT,
HOST_NAME VARCHAR(100) NOT NULL,
UUID VARCHAR(100) NOT NULL,
SERVER_PORT INT NOT NULL,
LAST_UPDATED_TIMESTAMP DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ID));
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[ELECTED_LEADER_META_INFO]') AND TYPE IN (N'U'))
CREATE TABLE ELECTED_LEADER_META_INFO (
UUID VARCHAR(100) NOT NULL,
ELECTED_TIME DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
ACKNOWLEDGED_TASK_LIST VARCHAR(MAX) DEFAULT NULL,
PRIMARY KEY (UUID));

@ -0,0 +1,18 @@
-- -----------------------------------------------------
-- Table `SERVER_HEART_BEAT_EVENTS`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
ID INTEGER AUTO_INCREMENT NOT NULL,
HOST_NAME VARCHAR(100) NOT NULL,
UUID VARCHAR(100) NOT NULL,
SERVER_PORT INTEGER NOT NULL,
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (ID)
);
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
UUID VARCHAR(100) NOT NULL,
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
PRIMARY KEY (UUID)
);

@ -0,0 +1,21 @@
-- -----------------------------------------------------
-- Table `SERVER_HEART_BEAT_EVENTS`
-- -----------------------------------------------------
CREATE TABLE SERVER_HEART_BEAT_EVENTS (
ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL,
HOST_NAME VARCHAR(100) NOT NULL,
UUID VARCHAR(100) NOT NULL,
SERVER_PORT INTEGER NOT NULL,
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT PK_SERVER_HEART_BEAT_EVENTS PRIMARY KEY (ID)
)
/
CREATE TABLE ELECTED_LEADER_META_INFO (
UUID VARCHAR(100) NOT NULL,
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ACKNOWLEDGED_TASK_LIST BLOB DEFAULT NULL,
CONSTRAINT PK_SERVER_HEART_BEAT_EVENTS PRIMARY KEY (UUID)
)
/

@ -0,0 +1,18 @@
-- -----------------------------------------------------
-- Table SERVER_HEART_BEAT_EVENTS
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
ID INTEGER AUTO_INCREMENT NOT NULL,
HOST_NAME VARCHAR(100) NOT NULL,
UUID VARCHAR(100) NOT NULL,
SERVER_PORT INTEGER NOT NULL,
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (ID)
);
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
UUID VARCHAR(100) NOT NULL,
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
PRIMARY KEY (UUID)
);

@ -0,0 +1,8 @@
instructions.configure = \
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/datasources/,target:${installFolder}/../../conf/datasources/,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/conf/heart-beat-config.xml,target:${installFolder}/../../conf/heart-beat-config.xml,overwrite:true);\
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/dbscripts/heart-beat/,target:${installFolder}/../../../dbscripts/heart-beat,overwrite:true);\
instructions.unconfigure = \
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../conf/datasources/heart-beat-datasources.xml);\
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../../dbscripts/heart-beat);\

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>4.1.11-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>heart-beat-feature</artifactId>
<version>4.1.11-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Entgra - Heart Beat Feature</name>
<url>http://entgra.io</url>
<modules>
<module>io.entgra.server.heart.beat.feature</module>
</modules>
</project>

@ -35,6 +35,7 @@
</parent>
<modules>
<module>components/heartbeat-management</module>
<module>components/device-mgt</module>
<module>components/device-mgt-extensions</module>
<module>components/identity-extensions</module>
@ -53,6 +54,7 @@
<module>features/certificate-mgt</module>
<module>features/oauth-extensions</module>
<module>features/email-sender</module>
<module>features/heartbeat-management</module>
<module>features/ui-request-interceptor</module>
<module>features/jwt-client</module>
<module>features/device-mgt-extensions</module>
@ -376,6 +378,11 @@
<type>zip</type>
<version>${carbon.device.mgt.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
<version>${carbon.device.mgt.version}</version>
</dependency>
<!-- Device Management dependencies -->
<!-- Governance dependencies -->

Loading…
Cancel
Save