diff --git a/components/apimgt-extensions/org.wso2.carbon.apimgt.integration.client/src/main/java/org/wso2/carbon/apimgt/integration/client/util/Utils.java b/components/apimgt-extensions/org.wso2.carbon.apimgt.integration.client/src/main/java/org/wso2/carbon/apimgt/integration/client/util/Utils.java index 29845990d7..369f357ddd 100644 --- a/components/apimgt-extensions/org.wso2.carbon.apimgt.integration.client/src/main/java/org/wso2/carbon/apimgt/integration/client/util/Utils.java +++ b/components/apimgt-extensions/org.wso2.carbon.apimgt.integration.client/src/main/java/org/wso2/carbon/apimgt/integration/client/util/Utils.java @@ -235,4 +235,4 @@ public class Utils { throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { return loadKeyStore(trustStorePath,tsPassword,TRUST_STORE_TYPE); } -} \ No newline at end of file +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java index 38077daf93..113b16cf3f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionCleanupTask.java @@ -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 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; } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java index afbab64d6d..caff4ec460 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/task/ScheduledAppSubscriptionTask.java @@ -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 deviceIdentifiers = new Gson().fromJson(this.subscribers, - new TypeToken>() { - }.getType()); - try { - subscriptionManager.performBulkAppOperation(this.application, deviceIdentifiers, - this.subscriptionType, this.action); - subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); - } catch (ApplicationManagementException e) { - log.error( - "Error occurred while " + this.action + "ing application " + this.application - + "to/from the following devices: " + this.subscribers, e); - subscriptionDTO.setStatus(ExecutionStatus.FAILED); + if (this.subscriptionType.equals(SubscriptionType.DEVICE.toString())) { + List deviceIdentifiers = new Gson().fromJson(this.subscribers, + new TypeToken>() { + }.getType()); + try { + subscriptionManager.performBulkAppOperation(this.application, deviceIdentifiers, + this.subscriptionType, this.action); + subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); + } catch (ApplicationManagementException e) { + log.error( + "Error occurred while " + this.action + "ing application " + this.application + + "to/from the following devices: " + this.subscribers, e); + subscriptionDTO.setStatus(ExecutionStatus.FAILED); + } + } else { + List subscriberList = Pattern.compile(",").splitAsStream(this.subscribers).collect( + Collectors.toList()); + try { + subscriptionManager.performBulkAppOperation(this.application, subscriberList, + this.subscriptionType, this.action); + subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); + } catch (ApplicationManagementException e) { + log.error( + "Error occurred while " + this.action + "ing application " + this.application + + "to/from the following " + this.subscriptionType + "s: " + this.subscribers, e); + subscriptionDTO.setStatus(ExecutionStatus.FAILED); + } } } else { - List subscriberList = Pattern.compile(",").splitAsStream(this.subscribers).collect( - Collectors.toList()); - try { - subscriptionManager.performBulkAppOperation(this.application, subscriberList, - this.subscriptionType, this.action); - subscriptionDTO.setStatus(ExecutionStatus.EXECUTED); - } catch (ApplicationManagementException e) { - log.error( - "Error occurred while " + this.action + "ing application " + this.application - + "to/from the following " + this.subscriptionType + "s: " + this.subscribers, e); - subscriptionDTO.setStatus(ExecutionStatus.FAILED); - } + 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; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java index d115659e18..d0605d5467 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/ActivityProviderServiceImpl.java @@ -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 diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DynamicTaskContext.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DynamicTaskContext.java new file mode 100644 index 0000000000..e125e3a443 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/DynamicTaskContext.java @@ -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; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ServerCtxInfo.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ServerCtxInfo.java new file mode 100644 index 0000000000..898793476a --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/ServerCtxInfo.java @@ -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; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java index 9faa732090..ad59d5c4f8 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java @@ -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 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. diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml index 347c710be6..fafc2fdece 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/pom.xml @@ -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 !org.wso2.carbon.device.mgt.core.internal, @@ -364,6 +365,10 @@ commons-validator commons-validator + + org.wso2.carbon.devicemgt + io.entgra.server.bootup.heartbeat.beacon + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java index 537539864c..2c1df83ad9 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceDAO.java @@ -295,6 +295,18 @@ public interface DeviceDAO { */ List 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 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 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 getAllocatedDevices(String type, int tenantId, int activeServerCount, int serverIndex) throws DeviceManagementDAOException; + List getDevices(long timestamp, int tenantId) throws DeviceManagementDAOException; /** diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractDeviceDAOImpl.java index a9b443b556..29ce88f342 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/AbstractDeviceDAOImpl.java @@ -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 getAllocatedDevices(String type, int tenantId, int activeServerCount, + int serverIndex) throws DeviceManagementDAOException { + List 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 getDevicesOfUser(String username, int tenantId) throws DeviceManagementDAOException { Connection conn; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java index ee9c9a774d..52c5d9d53e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/GenericDeviceDAOImpl.java @@ -172,6 +172,141 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl { } } + @Override + public List getAllocatedDevices(PaginationRequest request, int tenantId, int activeServerCount, int serverIndex) + throws DeviceManagementDAOException { + List 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 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 searchDevicesInGroup(PaginationRequest request, int tenantId) throws DeviceManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java index 86f8cccfea..e918cae3ca 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/OracleDeviceDAOImpl.java @@ -172,6 +172,144 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl { } } + @Override + public List getAllocatedDevices(PaginationRequest request, int tenantId, + int activeServerCount, int serverIndex) + throws DeviceManagementDAOException { + Connection conn; + List 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 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 searchDevicesInGroup(PaginationRequest request, int tenantId) throws DeviceManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java index b39e1fbab2..49522ba74c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/PostgreSQLDeviceDAOImpl.java @@ -165,6 +165,133 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl { } } + @Override + public List getAllocatedDevices(PaginationRequest request, int tenantId, + int activeServerCount, int serverIndex) + throws DeviceManagementDAOException { + Connection conn; + List 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 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 searchDevicesInGroup(PaginationRequest request, int tenantId) throws DeviceManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java index a56f094604..d56407276d 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/device/SQLServerDeviceDAOImpl.java @@ -172,6 +172,143 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl { } } + @Override + public List getAllocatedDevices(PaginationRequest request, int tenantId, + int activeServerCount, int serverIndex) + throws DeviceManagementDAOException { + Connection conn; + List 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 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 searchDevicesInGroup(PaginationRequest request, int tenantId) throws DeviceManagementDAOException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java index 8e8f5bad67..88ea281d62 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementDataHolder.java @@ -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; } -} \ No newline at end of file +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java index c4b0c24779..9064a9d9ec 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/internal/DeviceManagementServiceComponent.java @@ -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 */ diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java index 8514b8f3b8..d3540ca88b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java @@ -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 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 devices = deviceDAO.getDevices(paginationRequest, tenantId); + List 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 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) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationMappingDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationMappingDAO.java index 4b57829274..0c604fa560 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationMappingDAO.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/OperationMappingDAO.java @@ -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 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 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 getLastConnectedTimeForActiveEnrolments(long timeStamp, int deviceTypeId, int activeServerCount, int serverHashIndex) + throws OperationManagementDAOException; + } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/OperationMappingDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/OperationMappingDAOImpl.java index b57d313bdf..5cc7b773dc 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/OperationMappingDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/impl/OperationMappingDAOImpl.java @@ -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 getFirstPendingOperationMappingsForActiveEnrolments( + long minDuration, + long maxDuration, int deviceTypeId, + int activeServerCount, int serverHashIndex) throws OperationManagementDAOException { + List 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 getLastConnectedTimeForActiveEnrolments(long timeStamp, int deviceTypeId) throws OperationManagementDAOException { PreparedStatement stmt = null; @@ -259,6 +304,44 @@ public class OperationMappingDAOImpl implements OperationMappingDAO { return lastConnectedTimeMap; } + @Override + public Map getLastConnectedTimeForActiveEnrolments(long timeStamp, + int deviceTypeId, + int activeServerCount, + int serverHashIndex) + throws OperationManagementDAOException { + Map 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")); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/util/OperationDAOUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/util/OperationDAOUtil.java index c58b75e745..6e20e1da81 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/util/OperationDAOUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/dao/util/OperationDAOUtil.java @@ -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 operationResponses = new ArrayList<>(); + activityStatus.setResponses(operationResponses); + } activityStatus.getResponses().add(OperationDAOUtil.getOperationResponse(rs)); } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java index bf9a95b73a..2affe060f1 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java @@ -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 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 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 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 devices, Operation operation) throws OperationManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java index d453f88a5d..dba4736e0a 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java @@ -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 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 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 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 diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/status/task/impl/DeviceStatusMonitoringTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/status/task/impl/DeviceStatusMonitoringTask.java index 71cc06a6d1..0f9b70cc0e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/status/task/impl/DeviceStatusMonitoringTask.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/status/task/impl/DeviceStatusMonitoringTask.java @@ -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 operationEnrolmentMappings; List enrolmentInfoTobeUpdated = new ArrayList<>(); Map 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 getOperationEnrolmentMappings() throws DeviceStatusTaskException { + private List getOperationEnrolmentMappings(DynamicTaskContext ctx) throws DeviceStatusTaskException { List 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 getLastDeviceActivities() throws DeviceStatusTaskException { + private Map getLastDeviceActivities(DynamicTaskContext ctx) throws DeviceStatusTaskException { Map 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); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManager.java index c4db7c3531..77310bbcf1 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManager.java @@ -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; // /** diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceDetailsRetrieverTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceDetailsRetrieverTask.java index b3f4b850a6..360325e9ab 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceDetailsRetrieverTask.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceDetailsRetrieverTask.java @@ -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() { + + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceTaskManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceTaskManagerImpl.java index 1b15091cde..90d73320fe 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceTaskManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DeviceTaskManagerImpl.java @@ -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); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DynamicPartitionedScheduleTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DynamicPartitionedScheduleTask.java new file mode 100644 index 0000000000..2013105b6e --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/DynamicPartitionedScheduleTask.java @@ -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; + } + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/RandomlyAssignedScheduleTask.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/RandomlyAssignedScheduleTask.java new file mode 100644 index 0000000000..56a1c498a0 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/task/impl/RandomlyAssignedScheduleTask.java @@ -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(); +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/TestHeartBeatManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/TestHeartBeatManagementService.java new file mode 100644 index 0000000000..7a97408415 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/TestHeartBeatManagementService.java @@ -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; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManagerTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManagerTest.java index 5ee04dc92d..358d2fbf3c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManagerTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/task/DeviceTaskManagerTest.java @@ -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 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") diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml new file mode 100644 index 0000000000..42d318379d --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/pom.xml @@ -0,0 +1,185 @@ + + + + + + + org.wso2.carbon.devicemgt + heartbeat-management + 4.1.11-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.server.bootup.heartbeat.beacon + bundle + Entgra - Heartbeat Beacon + Entgra - Server Startup and Heartbeat Monitoring Component + http://www.entgra.io + + + + + org.apache.felix + maven-scr-plugin + + + org.apache.felix + maven-bundle-plugin + true + + + ${project.artifactId} + ${project.artifactId} + ${carbon.device.mgt.version} + Server Startup Heart Beat Beacon Bundle + io.entgra.server.bootup.heartbeat.beacon.internal + + 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.* + + + !io.entgra.server.bootup.heartbeat.beacon.internal, + io.entgra.server.bootup.heartbeat.beacon.* + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + file:src/test/resources/log4j.properties + + + src/test/resources/testng.xml + + + + + org.jacoco + jacoco-maven-plugin + + ${basedir}/target/coverage-reports/jacoco-unit.exec + + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + test + + report + + + ${basedir}/target/coverage-reports/jacoco-unit.exec + ${basedir}/target/coverage-reports/site + + + + + + + + + + org.eclipse.osgi + org.eclipse.osgi + + + org.eclipse.osgi + org.eclipse.osgi.services + + + org.wso2.carbon + org.wso2.carbon.logging + + + org.wso2.carbon + org.wso2.carbon.utils + + + org.testng + testng + + + org.wso2.carbon + org.wso2.carbon.base + + + org.apache.axis2.wso2 + axis2 + + + org.wso2.orbit.org.apache.velocity + velocity + + + commons-io.wso2 + commons-io + + + org.apache.axis2.transport + axis2-transport-mail + + + commons-collections.wso2 + commons-collections + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + + + org.wso2.carbon + org.wso2.carbon.ndatasource.core + + + log4j + log4j + + + + + + + diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconConfigurationException.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconConfigurationException.java new file mode 100644 index 0000000000..fe7a26768d --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconConfigurationException.java @@ -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); + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconUtils.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconUtils.java new file mode 100644 index 0000000000..ec0ec4c915 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/HeartBeatBeaconUtils.java @@ -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 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; + } + + + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/HeartBeatBeaconConfig.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/HeartBeatBeaconConfig.java new file mode 100644 index 0000000000..e427dffc86 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/HeartBeatBeaconConfig.java @@ -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); + } + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/DataSourceConfig.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/DataSourceConfig.java new file mode 100644 index 0000000000..5ab3aa4dd1 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/DataSourceConfig.java @@ -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; + } +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/JNDILookupDefinition.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/JNDILookupDefinition.java new file mode 100644 index 0000000000..befbf304b8 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/config/datasource/JNDILookupDefinition.java @@ -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 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 getJndiProperties() { + return jndiProperties; + } + + public void setJndiProperties(List 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; + } + } + +} + diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatBeaconDAOFactory.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatBeaconDAOFactory.java new file mode 100644 index 0000000000..e8d3011be9 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatBeaconDAOFactory.java @@ -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 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 jndiPropertyList = + jndiConfig.getJndiProperties(); + if (jndiPropertyList != null) { + Hashtable jndiProperties = new Hashtable(); + 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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatDAO.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatDAO.java new file mode 100644 index 0000000000..9013e443f5 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/HeartBeatDAO.java @@ -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 getActiveServerDetails(int elapsedTimeInSeconds) throws HeartBeatDAOException; + + boolean recordElectedCandidate(String serverUUID) throws HeartBeatDAOException; + + void purgeCandidates() throws HeartBeatDAOException; + + ElectedCandidate retrieveCandidate() throws HeartBeatDAOException; + + boolean acknowledgeTask(String uuid, List taskList) throws HeartBeatDAOException; + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/exception/HeartBeatDAOException.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/exception/HeartBeatDAOException.java new file mode 100644 index 0000000000..bf92afaaa8 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/exception/HeartBeatDAOException.java @@ -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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/impl/GenericHeartBeatDAOImpl.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/impl/GenericHeartBeatDAOImpl.java new file mode 100644 index 0000000000..ab20bd0f2c --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/impl/GenericHeartBeatDAOImpl.java @@ -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 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 getActiveServerDetails(int elapsedTimeInSeconds) + throws HeartBeatDAOException { + Map 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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/util/HeartBeatBeaconDAOUtil.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/util/HeartBeatBeaconDAOUtil.java new file mode 100644 index 0000000000..67b91df517 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dao/util/HeartBeatBeaconDAOUtil.java @@ -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 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; + } +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ElectedCandidate.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ElectedCandidate.java new file mode 100644 index 0000000000..a3920d362b --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ElectedCandidate.java @@ -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 acknowledgedTaskList = null; + + public List getAcknowledgedTaskList() { + return acknowledgedTaskList; + } + + public void setAcknowledgedTaskList(List 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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/HeartBeatEvent.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/HeartBeatEvent.java new file mode 100644 index 0000000000..4feb8b7fe7 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/HeartBeatEvent.java @@ -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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ServerContext.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ServerContext.java new file mode 100644 index 0000000000..bdb5abe76e --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/dto/ServerContext.java @@ -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; + } +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/HeartBeatManagementException.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/HeartBeatManagementException.java new file mode 100644 index 0000000000..9c90d3a9cb --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/HeartBeatManagementException.java @@ -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); + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/InvalidConfigurationStateException.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/InvalidConfigurationStateException.java new file mode 100644 index 0000000000..cb7ebc4385 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/InvalidConfigurationStateException.java @@ -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); + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/ServerStatusUpdationFailedException.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/ServerStatusUpdationFailedException.java new file mode 100644 index 0000000000..63379d7c70 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/exception/ServerStatusUpdationFailedException.java @@ -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); + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconComponent.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconComponent.java new file mode 100644 index 0000000000..dc111aef73 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconComponent.java @@ -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 + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconDataHolder.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconDataHolder.java new file mode 100644 index 0000000000..dc332a251e --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatBeaconDataHolder.java @@ -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; + } +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatExecutor.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatExecutor.java new file mode 100644 index 0000000000..1f0d50539b --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/internal/HeartBeatExecutor.java @@ -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); + } + + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementService.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementService.java new file mode 100644 index 0000000000..2edd5babdb --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementService.java @@ -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; + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementServiceImpl.java b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementServiceImpl.java new file mode 100644 index 0000000000..1c312e5368 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/main/java/io/entgra/server/bootup/heartbeat/beacon/service/HeartBeatManagementServiceImpl.java @@ -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 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 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 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 servers) throws HeartBeatDAOException { + String electedCandidate = getRandomElement(servers.keySet()); + heartBeatDAO.recordElectedCandidate(electedCandidate); + } + + + private String getRandomElement(Set valueSet) { + Random rand = new Random(); + List 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; + } + +} diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/log4j.properties b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/log4j.properties new file mode 100644 index 0000000000..18dca3223c --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/log4j.properties @@ -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 diff --git a/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/testng.xml b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/testng.xml new file mode 100644 index 0000000000..43b87672a2 --- /dev/null +++ b/components/heartbeat-management/io.entgra.server.bootup.heartbeat.beacon/src/test/resources/testng.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/components/heartbeat-management/pom.xml b/components/heartbeat-management/pom.xml new file mode 100644 index 0000000000..1143f7e4d7 --- /dev/null +++ b/components/heartbeat-management/pom.xml @@ -0,0 +1,39 @@ + + + + + + + org.wso2.carbon.devicemgt + carbon-devicemgt + 4.1.11-SNAPSHOT + ../../pom.xml + + + 4.0.0 + heartbeat-management + pom + Entgra - Task Allocation Framework + http://entgra.io + + + io.entgra.server.bootup.heartbeat.beacon + + + diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/src/main/java/org/wso2/carbon/policy/mgt/common/PolicyAdministratorPoint.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/src/main/java/org/wso2/carbon/policy/mgt/common/PolicyAdministratorPoint.java index e9bdbdc014..db5ab41c07 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.common/src/main/java/org/wso2/carbon/policy/mgt/common/PolicyAdministratorPoint.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.common/src/main/java/org/wso2/carbon/policy/mgt/common/PolicyAdministratorPoint.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/PolicyCacheManager.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/PolicyCacheManager.java index ce71b7a213..17b5669f3a 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/PolicyCacheManager.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/PolicyCacheManager.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/impl/PolicyCacheManagerImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/impl/PolicyCacheManagerImpl.java index 51c0dc4e81..46e01793a9 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/impl/PolicyCacheManagerImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/cache/impl/PolicyCacheManagerImpl.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/ProfileDAOImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/ProfileDAOImpl.java index 1483e6ebc7..66f7528d1b 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/ProfileDAOImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/dao/impl/ProfileDAOImpl.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/enforcement/DelegationTask.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/enforcement/DelegationTask.java index 3c377b156c..b24e84f029 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/enforcement/DelegationTask.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/enforcement/DelegationTask.java @@ -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 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() { + + } } diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/impl/PolicyAdministratorPointImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/impl/PolicyAdministratorPointImpl.java index 9c4be122af..1d1fa11c7c 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/impl/PolicyAdministratorPointImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/impl/PolicyAdministratorPointImpl.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/PolicyManager.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/PolicyManager.java index 886e0a5715..a31fe3d50a 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/PolicyManager.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/PolicyManager.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/PolicyManagerImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/PolicyManagerImpl.java index e2f42210c4..6a614fb37e 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/PolicyManagerImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/PolicyManagerImpl.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/ProfileManagerImpl.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/ProfileManagerImpl.java index d0a79cb53e..8db445348e 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/ProfileManagerImpl.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/mgt/impl/ProfileManagerImpl.java @@ -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; diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/task/MonitoringTask.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/task/MonitoringTask.java index 0acbac6a60..d51f283a8e 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/task/MonitoringTask.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/main/java/org/wso2/carbon/policy/mgt/core/task/MonitoringTask.java @@ -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 deviceTypes = new ArrayList<>(); List configDeviceTypes = new ArrayList<>(); @@ -125,7 +118,14 @@ public class MonitoringTask implements Task { PolicyMonitoringManager monitoringService = PolicyManagementDataHolder.getInstance().getDeviceManagementService() .getPolicyMonitoringManager(deviceType); - List devices = deviceManagementProviderService.getAllDevices(deviceType, false); + List 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 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() { + + } } diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/PolicyManagerServiceImplTest.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/PolicyManagerServiceImplTest.java index d372eb63d8..310003e829 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/PolicyManagerServiceImplTest.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/PolicyManagerServiceImplTest.java @@ -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()); } -} \ No newline at end of file +} diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/mock/TestHeartBeatManagementService.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/mock/TestHeartBeatManagementService.java new file mode 100644 index 0000000000..38d7dd0437 --- /dev/null +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/mock/TestHeartBeatManagementService.java @@ -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; + } +} diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml new file mode 100644 index 0000000000..fd079f6619 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/pom.xml @@ -0,0 +1,153 @@ + + + + + + + org.wso2.carbon.devicemgt + heart-beat-feature + 4.1.11-SNAPSHOT + ../pom.xml + + + 4.0.0 + io.entgra.server.heart.beat.feature + pom + Entgra IoT - Heart Beat Feature + http://entgra.io + + This feature bundles for the heart beat beacon for Entgra IoT Server + + + + + com.h2database.wso2 + h2-database-engine + + + org.wso2.carbon.devicemgt + io.entgra.server.bootup.heartbeat.beacon + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.common + provided + + + org.wso2.carbon.identity.inbound.auth.oauth2 + org.wso2.carbon.identity.oauth.stub + + + org.apache.axis2.wso2 + axis2-client + + + org.apache.geronimo.specs.wso2 + geronimo-stax-api_1.0_spec + + + org.apache.ws.commons.axiom.wso2 + axiom + + + org.codehaus.woodstox + woodstox-core-asl + + + org.codehaus.woodstox + wstx-asl + + + org.wso2.carbon.devicemgt + org.wso2.carbon.device.mgt.analytics.data.publisher + + + org.wso2.carbon.analytics + org.wso2.carbon.analytics.api + + + + + + + + maven-resources-plugin + 2.6 + + + copy-resources + generate-resources + + copy-resources + + + src/main/resources + + + resources + + build.properties + p2.inf + + + + + + + + + org.wso2.maven + carbon-p2-plugin + ${carbon.p2.plugin.version} + + + p2-feature-generation + package + + p2-feature-gen + + + io.entgra.server.heart.beat + ../../../../features/etc/feature.properties + + + + org.wso2.carbon.p2.category.type:server + + org.eclipse.equinox.p2.type.group:true + + + + + + org.wso2.carbon.devicemgt:io.entgra.server.bootup.heartbeat.beacon:${carbon.device.mgt.version} + + + + org.wso2.carbon.core.server:${carbon.kernel.version} + + + + + + + + diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/build.properties b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/build.properties new file mode 100644 index 0000000000..9404b75803 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/build.properties @@ -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 diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/conf/heart-beat-config.xml b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/conf/heart-beat-config.xml new file mode 100644 index 0000000000..59b17614de --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/conf/heart-beat-config.xml @@ -0,0 +1,53 @@ + + + + + + false + + + + jdbc/HeartBeat_DS + + + 30 + 300 + 5 + 600 + diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/datasources/heart-beat-datasources.xml b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/datasources/heart-beat-datasources.xml new file mode 100644 index 0000000000..a3c656b757 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/datasources/heart-beat-datasources.xml @@ -0,0 +1,46 @@ + + + + org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader + + + + HeartBeat_DS + The datasource Server Heart Beat + + jdbc/HeartBeat_DS + + + + jdbc:mysql://localhost:3306/heart_beat + root + root + com.mysql.jdbc.Driver + 50 + 60000 + true + SELECT 1 + 30000 + + + + + + diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/h2.sql b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/h2.sql new file mode 100644 index 0000000000..2a68137287 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/h2.sql @@ -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) +); diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mssql.sql b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mssql.sql new file mode 100644 index 0000000000..05c4713620 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mssql.sql @@ -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)); diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mysql.sql b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mysql.sql new file mode 100644 index 0000000000..2a68137287 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/mysql.sql @@ -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) +); diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/oracle.sql b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/oracle.sql new file mode 100644 index 0000000000..7d4649187f --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/oracle.sql @@ -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) +) +/ diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/postgresql.sql b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/postgresql.sql new file mode 100644 index 0000000000..9b895c6f31 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/dbscripts/heart-beat/postgresql.sql @@ -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) +); diff --git a/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/p2.inf b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/p2.inf new file mode 100644 index 0000000000..6add373677 --- /dev/null +++ b/features/heartbeat-management/io.entgra.server.heart.beat.feature/src/main/resources/p2.inf @@ -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);\ diff --git a/features/heartbeat-management/pom.xml b/features/heartbeat-management/pom.xml new file mode 100644 index 0000000000..a56f5f9cd1 --- /dev/null +++ b/features/heartbeat-management/pom.xml @@ -0,0 +1,41 @@ + + + + + + + org.wso2.carbon.devicemgt + carbon-devicemgt + 4.1.11-SNAPSHOT + ../../pom.xml + + + 4.0.0 + org.wso2.carbon.devicemgt + heart-beat-feature + 4.1.11-SNAPSHOT + pom + Entgra - Heart Beat Feature + http://entgra.io + + + io.entgra.server.heart.beat.feature + + + diff --git a/pom.xml b/pom.xml index d12b76d9f4..3b8e07f718 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,7 @@ + components/heartbeat-management components/device-mgt components/device-mgt-extensions components/identity-extensions @@ -53,6 +54,7 @@ features/certificate-mgt features/oauth-extensions features/email-sender + features/heartbeat-management features/ui-request-interceptor features/jwt-client features/device-mgt-extensions @@ -376,6 +378,11 @@ zip ${carbon.device.mgt.version} + + org.wso2.carbon.devicemgt + io.entgra.server.bootup.heartbeat.beacon + ${carbon.device.mgt.version} +