forked from community/device-mgt-core
Adding heart beat management functionality for dynamic task distribution See merge request entgra/carbon-device-mgt!695revert-70ac1926
commit
5297dfa73d
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.device.mgt.common;
|
||||||
|
|
||||||
|
public class DynamicTaskContext {
|
||||||
|
|
||||||
|
private int serverHashIndex;
|
||||||
|
private int activeServerCount;
|
||||||
|
private boolean partitioningEnabled = false;
|
||||||
|
|
||||||
|
public int getServerHashIndex() {
|
||||||
|
return serverHashIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerHashIndex(int serverHashIndex) {
|
||||||
|
this.serverHashIndex = serverHashIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveServerCount() {
|
||||||
|
return activeServerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveServerCount(int activeServerCount) {
|
||||||
|
this.activeServerCount = activeServerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPartitioningEnabled() {
|
||||||
|
return partitioningEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPartitioningEnabled(boolean partitioningEnabled) {
|
||||||
|
this.partitioningEnabled = partitioningEnabled;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.device.mgt.common;
|
||||||
|
|
||||||
|
public class ServerCtxInfo {
|
||||||
|
private int activeServerCount;
|
||||||
|
private int localServerHashIdx;
|
||||||
|
|
||||||
|
public ServerCtxInfo(int activeServerCount, int localServerHashIdx){
|
||||||
|
this.activeServerCount = activeServerCount;
|
||||||
|
this.localServerHashIdx = localServerHashIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveServerCount() {
|
||||||
|
return activeServerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveServerCount(int activeServerCount) {
|
||||||
|
this.activeServerCount = activeServerCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getLocalServerHashIdx() {
|
||||||
|
return localServerHashIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalServerHashIdx(int localServerHashIdx) {
|
||||||
|
this.localServerHashIdx = localServerHashIdx;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.device.mgt.core.task.impl;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
|
||||||
|
import org.wso2.carbon.device.mgt.common.DynamicTaskContext;
|
||||||
|
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
|
||||||
|
import org.wso2.carbon.ntask.core.Task;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class DynamicPartitionedScheduleTask implements Task {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(DynamicPartitionedScheduleTask.class);
|
||||||
|
|
||||||
|
private static DynamicTaskContext taskContext = null;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void init() {
|
||||||
|
try {
|
||||||
|
boolean dynamicTaskEnabled = DeviceManagementDataHolder.getInstance().getHeartBeatService().isTaskPartitioningEnabled();
|
||||||
|
if(dynamicTaskEnabled){
|
||||||
|
taskContext = new DynamicTaskContext();
|
||||||
|
taskContext.setPartitioningEnabled(true);
|
||||||
|
} else {
|
||||||
|
log.info("Error Instantiating Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function.");
|
||||||
|
}
|
||||||
|
} catch (HeartBeatManagementException e) {
|
||||||
|
log.error("Error Instantiating Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function." , e);
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void execute() {
|
||||||
|
refreshContext();
|
||||||
|
executeDynamicTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshContext(){
|
||||||
|
if(taskContext != null && taskContext.isPartitioningEnabled()) {
|
||||||
|
try {
|
||||||
|
updateContext();
|
||||||
|
} catch (HeartBeatManagementException e) {
|
||||||
|
log.error("Error refreshing Variables necessary for Dynamic Task Scheduling. Dynamic Tasks will not function.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateContext() throws HeartBeatManagementException {
|
||||||
|
ServerCtxInfo ctxInfo = DeviceManagementDataHolder.getInstance().getHeartBeatService().getServerCtxInfo();
|
||||||
|
if(ctxInfo != null) {
|
||||||
|
populateContext(ctxInfo);
|
||||||
|
} else {
|
||||||
|
log.info("Dynamic Task Context not present. Tasks will run on regular worker/manager mode.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populateContext(ServerCtxInfo ctxInfo) {
|
||||||
|
taskContext.setActiveServerCount(ctxInfo.getActiveServerCount());
|
||||||
|
taskContext.setServerHashIndex(ctxInfo.getLocalServerHashIdx());
|
||||||
|
|
||||||
|
if(log.isDebugEnabled()){
|
||||||
|
log.debug("Initiating execution of dynamic task for server : " + taskContext.getServerHashIndex() +
|
||||||
|
" where active server count is : " + taskContext.getActiveServerCount() +
|
||||||
|
" partitioning task enabled : " + taskContext.isPartitioningEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setup();
|
||||||
|
|
||||||
|
protected abstract void executeDynamicTask();
|
||||||
|
|
||||||
|
public static DynamicTaskContext getTaskContext() {
|
||||||
|
return taskContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDynamicTaskEligible(){
|
||||||
|
if(taskContext != null && taskContext.isPartitioningEnabled()) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.device.mgt.core.task.impl;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
|
||||||
|
import org.wso2.carbon.ntask.core.Task;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class RandomlyAssignedScheduleTask implements Task {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(RandomlyAssignedScheduleTask.class);
|
||||||
|
|
||||||
|
private static String taskName = "UNSPECIFIED";
|
||||||
|
private static boolean qualifiedToExecuteTask = false;
|
||||||
|
private static boolean dynamicTaskEnabled = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void init() {
|
||||||
|
try {
|
||||||
|
dynamicTaskEnabled = DeviceManagementDataHolder.getInstance().getHeartBeatService().isTaskPartitioningEnabled();
|
||||||
|
} catch (HeartBeatManagementException e) {
|
||||||
|
log.error("Error Instantiating Variables necessary for Randomly Assigned Task Scheduling." , e);
|
||||||
|
}
|
||||||
|
//This is done so that sub class extending this abstract class is forced to specify a task name.
|
||||||
|
taskName = getTaskName();
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void execute() {
|
||||||
|
refreshContext();
|
||||||
|
executeRandomlyAssignedTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshContext(){
|
||||||
|
if(dynamicTaskEnabled) {
|
||||||
|
try {
|
||||||
|
qualifiedToExecuteTask = DeviceManagementDataHolder.getInstance().getHeartBeatService().isQualifiedToExecuteTask();
|
||||||
|
log.info("## NODE Qualified to execute Randomly Assigned Task : " + taskName);
|
||||||
|
DeviceManagementDataHolder.getInstance().getHeartBeatService().updateTaskExecutionAcknowledgement(taskName);
|
||||||
|
} catch (HeartBeatManagementException e) {
|
||||||
|
log.error("Error refreshing Variables necessary for Randomly Assigned Scheduled Task. " +
|
||||||
|
"Dynamic Tasks will not function.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setup();
|
||||||
|
|
||||||
|
protected abstract void executeRandomlyAssignedTask();
|
||||||
|
|
||||||
|
public static boolean isQualifiedToExecuteTask() {
|
||||||
|
return qualifiedToExecuteTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract String getTaskName();
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.device.mgt.core;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
|
||||||
|
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
|
||||||
|
|
||||||
|
public class TestHeartBeatManagementService implements HeartBeatManagementService {
|
||||||
|
@Override
|
||||||
|
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateTaskExecutionAcknowledgement(String newTask)
|
||||||
|
throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,185 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
~ Version 2.0 (the "License"); you may not use this file except
|
||||||
|
~ in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>heartbeat-management</artifactId>
|
||||||
|
<version>4.1.11-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
|
||||||
|
<packaging>bundle</packaging>
|
||||||
|
<name>Entgra - Heartbeat Beacon</name>
|
||||||
|
<description>Entgra - Server Startup and Heartbeat Monitoring Component</description>
|
||||||
|
<url>http://www.entgra.io</url>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-scr-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.felix</groupId>
|
||||||
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
|
<extensions>true</extensions>
|
||||||
|
<configuration>
|
||||||
|
<instructions>
|
||||||
|
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
|
||||||
|
<Bundle-Name>${project.artifactId}</Bundle-Name>
|
||||||
|
<Bundle-Version>${carbon.device.mgt.version}</Bundle-Version>
|
||||||
|
<Bundle-Description>Server Startup Heart Beat Beacon Bundle</Bundle-Description>
|
||||||
|
<Private-Package>io.entgra.server.bootup.heartbeat.beacon.internal</Private-Package>
|
||||||
|
<Import-Package>
|
||||||
|
org.apache.axis2.*;version="${axis2.osgi.version.range}",
|
||||||
|
org.apache.axiom.*; version="${axiom.osgi.version.range}",
|
||||||
|
org.osgi.framework,
|
||||||
|
org.osgi.service.component,
|
||||||
|
org.apache.commons.logging,
|
||||||
|
javax.xml.parsers;version="${javax.xml.parsers.import.pkg.version}";resolution:=optional,
|
||||||
|
org.wso2.carbon.context,
|
||||||
|
org.wso2.carbon.utils.*,
|
||||||
|
org.wso2.carbon.ndatasource.core,
|
||||||
|
org.w3c.dom,
|
||||||
|
org.apache.velocity;version="${velocity.version}",
|
||||||
|
org.apache.velocity.app;version="${velocity.version}",
|
||||||
|
org.apache.velocity.context;version="${velocity.version}",
|
||||||
|
org.apache.velocity.exception;version="${velocity.version}",
|
||||||
|
org.apache.velocity.runtime.resource;version="${velocity.version}",
|
||||||
|
org.apache.velocity.runtime.resource.loader;version="${velocity.version}",
|
||||||
|
org.apache.commons.io,
|
||||||
|
org.apache.axis2.transport.mail,
|
||||||
|
org.apache.commons.collections,
|
||||||
|
org.wso2.carbon.device.mgt.common.*
|
||||||
|
</Import-Package>
|
||||||
|
<Export-Package>
|
||||||
|
!io.entgra.server.bootup.heartbeat.beacon.internal,
|
||||||
|
io.entgra.server.bootup.heartbeat.beacon.*
|
||||||
|
</Export-Package>
|
||||||
|
</instructions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<systemPropertyVariables>
|
||||||
|
<log4j.configuration>file:src/test/resources/log4j.properties</log4j.configuration>
|
||||||
|
</systemPropertyVariables>
|
||||||
|
<suiteXmlFiles>
|
||||||
|
<suiteXmlFile>src/test/resources/testng.xml</suiteXmlFile>
|
||||||
|
</suiteXmlFiles>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<destFile>${basedir}/target/coverage-reports/jacoco-unit.exec</destFile>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>jacoco-initialize</id>
|
||||||
|
<goals>
|
||||||
|
<goal>prepare-agent</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>jacoco-site</id>
|
||||||
|
<phase>test</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>report</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<dataFile>${basedir}/target/coverage-reports/jacoco-unit.exec</dataFile>
|
||||||
|
<outputDirectory>${basedir}/target/coverage-reports/site</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.osgi</groupId>
|
||||||
|
<artifactId>org.eclipse.osgi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.osgi</groupId>
|
||||||
|
<artifactId>org.eclipse.osgi.services</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.logging</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.utils</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.testng</groupId>
|
||||||
|
<artifactId>testng</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.base</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.axis2.wso2</groupId>
|
||||||
|
<artifactId>axis2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.orbit.org.apache.velocity</groupId>
|
||||||
|
<artifactId>velocity</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io.wso2</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.axis2.transport</groupId>
|
||||||
|
<artifactId>axis2-transport-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-collections.wso2</groupId>
|
||||||
|
<artifactId>commons-collections</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.ndatasource.core</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon;
|
||||||
|
|
||||||
|
public class HeartBeatBeaconConfigurationException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -1043317705230442099L;
|
||||||
|
|
||||||
|
public HeartBeatBeaconConfigurationException(String msg, Exception nestedEx) {
|
||||||
|
super(msg, nestedEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatBeaconConfigurationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatBeaconConfigurationException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatBeaconConfigurationException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatBeaconConfigurationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
|
import javax.naming.InitialContext;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class HeartBeatBeaconUtils {
|
||||||
|
|
||||||
|
private static Log log = LogFactory.getLog(HeartBeatBeaconUtils.class);
|
||||||
|
|
||||||
|
public static Document convertToDocument(File file)
|
||||||
|
throws HeartBeatBeaconConfigurationException {
|
||||||
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
|
factory.setNamespaceAware(true);
|
||||||
|
try {
|
||||||
|
factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||||
|
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||||
|
DocumentBuilder docBuilder = factory.newDocumentBuilder();
|
||||||
|
return docBuilder.parse(file);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = "Error occurred while parsing file, while converting " +
|
||||||
|
"to a org.w3c.dom.Document";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatBeaconConfigurationException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup datasource using name and jndi properties
|
||||||
|
*
|
||||||
|
* @param dataSourceName Name of datasource to lookup
|
||||||
|
* @param jndiProperties Hash table of JNDI Properties
|
||||||
|
* @return datasource looked
|
||||||
|
*/
|
||||||
|
public static DataSource lookupDataSource(String dataSourceName,
|
||||||
|
final Hashtable<Object, Object> jndiProperties) {
|
||||||
|
try {
|
||||||
|
if (jndiProperties == null || jndiProperties.isEmpty()) {
|
||||||
|
return (DataSource) InitialContext.doLookup(dataSourceName);
|
||||||
|
}
|
||||||
|
final InitialContext context = new InitialContext(jndiProperties);
|
||||||
|
return (DataSource) context.doLookup(dataSourceName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg = "Error in looking up data source: " + e.getMessage();
|
||||||
|
log.error(msg);
|
||||||
|
throw new RuntimeException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ServerContext getServerDetails() throws UnknownHostException, SocketException {
|
||||||
|
InetAddress localHost = InetAddress.getLocalHost();
|
||||||
|
int iotsCorePort = Integer.parseInt(System.getProperty("iot.core.https.port"));
|
||||||
|
ServerContext ctx = new ServerContext();
|
||||||
|
ctx.setHostName(localHost.getHostName());
|
||||||
|
ctx.setCarbonServerPort(iotsCorePort);
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void saveUUID(String text) throws IOException {
|
||||||
|
File serverDetails = new File(HeartBeatBeaconConfig.getInstance().getServerUUIDFileLocation());
|
||||||
|
serverDetails.createNewFile(); // if file already exists will do nothing
|
||||||
|
FileOutputStream fos = new FileOutputStream(serverDetails, false);
|
||||||
|
Properties prop = new Properties();
|
||||||
|
prop.setProperty("server.uuid", text);
|
||||||
|
prop.store(fos, null);
|
||||||
|
fos.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readUUID() {
|
||||||
|
InputStream input = null;
|
||||||
|
String uuid = null;
|
||||||
|
try {
|
||||||
|
input = new FileInputStream(HeartBeatBeaconConfig.getInstance().getServerUUIDFileLocation());
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.load(input);
|
||||||
|
uuid = props.getProperty("server.uuid");
|
||||||
|
input.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
String msg = "File : server-credentials.properties does not exist, new UUID will be generated for server.";
|
||||||
|
if(log.isDebugEnabled()){
|
||||||
|
log.debug(msg, e);
|
||||||
|
}
|
||||||
|
log.info(msg);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Error accessing server-credentials.properties to locate server.uuid.", e);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.config;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconConfigurationException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.InvalidConfigurationStateException;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.wso2.carbon.utils.CarbonUtils;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
@XmlRootElement(name = "HeartBeatBeaconConfig")
|
||||||
|
public class HeartBeatBeaconConfig {
|
||||||
|
|
||||||
|
private boolean enabled;
|
||||||
|
private int notifierFrequency;
|
||||||
|
private int notifierDelay;
|
||||||
|
private int serverTimeOutIntervalInSeconds;
|
||||||
|
private int timeSkew;
|
||||||
|
private DataSourceConfig dataSourceConfig;
|
||||||
|
|
||||||
|
private static HeartBeatBeaconConfig config;
|
||||||
|
|
||||||
|
private static final String HEART_BEAT_NOTIFIER_CONFIG_PATH =
|
||||||
|
CarbonUtils.getCarbonConfigDirPath() + File.separator + "heart-beat-config.xml";
|
||||||
|
|
||||||
|
private static final String SERVER_UUID_FILE_LOCATION =
|
||||||
|
CarbonUtils.getCarbonConfigDirPath() + File.separator + "server-credentials.properties";
|
||||||
|
|
||||||
|
private HeartBeatBeaconConfig() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HeartBeatBeaconConfig getInstance() {
|
||||||
|
if (config == null) {
|
||||||
|
throw new InvalidConfigurationStateException("Webapp Authenticator Configuration is not " +
|
||||||
|
"initialized properly");
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "NotifierInitialDelayInSeconds", required = true)
|
||||||
|
public int getNotifierDelay() {
|
||||||
|
return notifierDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotifierDelay(int notifierDelay) {
|
||||||
|
this.notifierDelay = notifierDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "NotifierFrequencyInSeconds", required = true)
|
||||||
|
public int getNotifierFrequency() {
|
||||||
|
return notifierFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNotifierFrequency(int notifierFrequency) {
|
||||||
|
this.notifierFrequency = notifierFrequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "TimeSkewInSeconds", required = true)
|
||||||
|
public int getTimeSkew() {
|
||||||
|
return timeSkew;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeSkew(int timeSkew) {
|
||||||
|
this.timeSkew = timeSkew;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "ServerTimeOutIntervalInSeconds", required = true)
|
||||||
|
public int getServerTimeOutIntervalInSeconds() {
|
||||||
|
return serverTimeOutIntervalInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerTimeOutIntervalInSeconds(int serverTimeOutIntervalInSeconds) {
|
||||||
|
this.serverTimeOutIntervalInSeconds = serverTimeOutIntervalInSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "DataSourceConfiguration", required = true)
|
||||||
|
public DataSourceConfig getDataSourceConfig() {
|
||||||
|
return dataSourceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataSourceConfig(DataSourceConfig dataSourceConfig) {
|
||||||
|
this.dataSourceConfig = dataSourceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "Enable", required = true)
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerUUIDFileLocation(){
|
||||||
|
return SERVER_UUID_FILE_LOCATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init() throws HeartBeatBeaconConfigurationException {
|
||||||
|
try {
|
||||||
|
File emailSenderConfig = new File(HEART_BEAT_NOTIFIER_CONFIG_PATH);
|
||||||
|
Document doc = HeartBeatBeaconUtils.convertToDocument(emailSenderConfig);
|
||||||
|
|
||||||
|
/* Un-marshaling Email Sender configuration */
|
||||||
|
JAXBContext ctx = JAXBContext.newInstance(HeartBeatBeaconConfig.class);
|
||||||
|
Unmarshaller unmarshaller = ctx.createUnmarshaller();
|
||||||
|
//unmarshaller.setSchema(getSchema());
|
||||||
|
config = (HeartBeatBeaconConfig) unmarshaller.unmarshal(doc);
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
throw new HeartBeatBeaconConfigurationException("Error occurred while un-marshalling " +
|
||||||
|
"heart beat configuration file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.config.datasource;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for holding data source configuration in cdm-config.xml at parsing with JAXB
|
||||||
|
*/
|
||||||
|
@XmlRootElement(name = "DataSourceConfiguration")
|
||||||
|
public class DataSourceConfig {
|
||||||
|
|
||||||
|
private JNDILookupDefinition jndiLookupDefinition;
|
||||||
|
|
||||||
|
@XmlElement(name = "JndiLookupDefinition", nillable = true)
|
||||||
|
public JNDILookupDefinition getJndiLookupDefinition() {
|
||||||
|
return jndiLookupDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJndiLookupDefinition(JNDILookupDefinition jndiLookupDefinition) {
|
||||||
|
this.jndiLookupDefinition = jndiLookupDefinition;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.config.datasource;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.XmlAttribute;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.XmlValue;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for hold JndiLookupDefinition of cdm-config.xml at parsing with JAXB
|
||||||
|
*/
|
||||||
|
@XmlRootElement(name = "JndiLookupDefinition")
|
||||||
|
public class JNDILookupDefinition {
|
||||||
|
|
||||||
|
private String jndiName;
|
||||||
|
private List<JNDIProperty> jndiProperties;
|
||||||
|
|
||||||
|
@XmlElement(name = "Name", nillable = false)
|
||||||
|
public String getJndiName() {
|
||||||
|
return jndiName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJndiName(String jndiName) {
|
||||||
|
this.jndiName = jndiName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlElementWrapper(name = "Environment", nillable = false)
|
||||||
|
@XmlElement(name = "Property", nillable = false)
|
||||||
|
public List<JNDIProperty> getJndiProperties() {
|
||||||
|
return jndiProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setJndiProperties(List<JNDIProperty> jndiProperties) {
|
||||||
|
this.jndiProperties = jndiProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlRootElement(name = "Property")
|
||||||
|
public static class JNDIProperty {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@XmlAttribute(name = "Name")
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@XmlValue
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,204 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dao;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.JNDILookupDefinition;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.impl.GenericHeartBeatDAOImpl;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
|
||||||
|
import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException;
|
||||||
|
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
|
||||||
|
import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents factory for group management data operations
|
||||||
|
*/
|
||||||
|
public class HeartBeatBeaconDAOFactory {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(HeartBeatBeaconDAOFactory.class);
|
||||||
|
private static DataSource dataSource;
|
||||||
|
private static ThreadLocal<Connection> currentConnection = new ThreadLocal<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get instance of GroupDAO
|
||||||
|
*
|
||||||
|
* @return instance of GroupDAO implementation
|
||||||
|
*/
|
||||||
|
public static HeartBeatDAO getHeartBeatDAO() {
|
||||||
|
return new GenericHeartBeatDAOImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init(DataSourceConfig config) {
|
||||||
|
dataSource = resolveDataSource(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void init(DataSource dtSource) {
|
||||||
|
dataSource = dtSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begin transaction with datasource for write data
|
||||||
|
*
|
||||||
|
* @throws TransactionManagementException
|
||||||
|
*/
|
||||||
|
public static void beginTransaction() throws TransactionManagementException {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn != null) {
|
||||||
|
throw new IllegalTransactionStateException("A transaction is already active within the context of " +
|
||||||
|
"this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " +
|
||||||
|
"transaction is already active is a sign of improper transaction handling");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
conn = dataSource.getConnection();
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
currentConnection.set(conn);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new TransactionManagementException("Error occurred while retrieving config.datasource connection", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open connection to the datasource for read data
|
||||||
|
*
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
public static void openConnection() throws SQLException {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn != null) {
|
||||||
|
throw new IllegalTransactionStateException("A transaction is already active within the context of " +
|
||||||
|
"this particular thread. Therefore, calling 'beginTransaction/openConnection' while another " +
|
||||||
|
"transaction is already active is a sign of improper transaction handling");
|
||||||
|
}
|
||||||
|
conn = dataSource.getConnection();
|
||||||
|
currentConnection.set(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get current connection to datasource
|
||||||
|
*
|
||||||
|
* @return current connection
|
||||||
|
* @throws SQLException
|
||||||
|
*/
|
||||||
|
public static Connection getConnection() throws SQLException {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn == null) {
|
||||||
|
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
|
||||||
|
"This might have ideally been caused by not properly initiating the transaction via " +
|
||||||
|
"'beginTransaction'/'openConnection' methods");
|
||||||
|
}
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commit current transaction to the datasource
|
||||||
|
*/
|
||||||
|
public static void commitTransaction() {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn == null) {
|
||||||
|
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
|
||||||
|
"This might have ideally been caused by not properly initiating " +
|
||||||
|
"the transaction via 'beginTransaction'/'openConnection' methods");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
conn.commit();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.error("Error occurred while committing the transaction", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback current transaction on failure
|
||||||
|
*/
|
||||||
|
public static void rollbackTransaction() {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn == null) {
|
||||||
|
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
|
||||||
|
"This might have ideally been caused by not properly initiating " +
|
||||||
|
"the transaction via 'beginTransaction'/'openConnection' methods");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
conn.rollback();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.warn("Error occurred while roll-backing the transaction", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close data connection associated with current transaction
|
||||||
|
*/
|
||||||
|
public static void closeConnection() {
|
||||||
|
Connection conn = currentConnection.get();
|
||||||
|
if (conn == null) {
|
||||||
|
throw new IllegalTransactionStateException("No connection is associated with the current transaction. " +
|
||||||
|
"This might have ideally been caused by not properly " +
|
||||||
|
"initiating the transaction via " +
|
||||||
|
"'beginTransaction'/'openConnection' methods");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
conn.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.warn("Error occurred while close the connection");
|
||||||
|
}
|
||||||
|
currentConnection.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve data source from the data source definition
|
||||||
|
*
|
||||||
|
* @param config data source configuration
|
||||||
|
* @return data source resolved from the data source definition
|
||||||
|
*/
|
||||||
|
private static DataSource resolveDataSource(DataSourceConfig config) {
|
||||||
|
DataSource dataSource = null;
|
||||||
|
if (config == null) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
"Device Management Repository data source configuration " + "is null and " +
|
||||||
|
"thus, is not initialized");
|
||||||
|
}
|
||||||
|
JNDILookupDefinition jndiConfig = config.getJndiLookupDefinition();
|
||||||
|
if (jndiConfig != null) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Initializing Device Management Repository data source using the JNDI " +
|
||||||
|
"Lookup Definition");
|
||||||
|
}
|
||||||
|
List<JNDILookupDefinition.JNDIProperty> jndiPropertyList =
|
||||||
|
jndiConfig.getJndiProperties();
|
||||||
|
if (jndiPropertyList != null) {
|
||||||
|
Hashtable<Object, Object> jndiProperties = new Hashtable<Object, Object>();
|
||||||
|
for (JNDILookupDefinition.JNDIProperty prop : jndiPropertyList) {
|
||||||
|
jndiProperties.put(prop.getName(), prop.getValue());
|
||||||
|
}
|
||||||
|
dataSource = HeartBeatBeaconUtils.lookupDataSource(jndiConfig.getJndiName(), jndiProperties);
|
||||||
|
} else {
|
||||||
|
dataSource = HeartBeatBeaconUtils.lookupDataSource(jndiConfig.getJndiName(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dao;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface represents the key operations associated with persisting group related information.
|
||||||
|
*/
|
||||||
|
public interface HeartBeatDAO {
|
||||||
|
|
||||||
|
String recordServerCtx(ServerContext ctx) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
boolean recordHeatBeat(HeartBeatEvent event) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
boolean checkUUIDValidity(String uuid) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
String retrieveExistingServerCtx(ServerContext ctx) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
Map<String, ServerContext> getActiveServerDetails(int elapsedTimeInSeconds) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
boolean recordElectedCandidate(String serverUUID) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
void purgeCandidates() throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
ElectedCandidate retrieveCandidate() throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
boolean acknowledgeTask(String uuid, List<String> taskList) throws HeartBeatDAOException;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dao.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom exception class for group management data access related exceptions.
|
||||||
|
*/
|
||||||
|
public class HeartBeatDAOException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2021891706072918864L;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new exception with the specified detail message and nested exception.
|
||||||
|
*
|
||||||
|
* @param message error message
|
||||||
|
* @param nestedException exception
|
||||||
|
*/
|
||||||
|
public HeartBeatDAOException(String message, Exception nestedException) {
|
||||||
|
super(message, nestedException);
|
||||||
|
setErrorMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new exception with the specified detail message and cause.
|
||||||
|
*
|
||||||
|
* @param message the detail message.
|
||||||
|
* @param cause the cause of this exception.
|
||||||
|
*/
|
||||||
|
public HeartBeatDAOException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
setErrorMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new exception with the specified detail message
|
||||||
|
*
|
||||||
|
* @param message the detail message.
|
||||||
|
*/
|
||||||
|
public HeartBeatDAOException(String message) {
|
||||||
|
super(message);
|
||||||
|
setErrorMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new exception with the specified and cause.
|
||||||
|
*
|
||||||
|
* @param cause the cause of this exception.
|
||||||
|
*/
|
||||||
|
public HeartBeatDAOException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorMessage(String errorMessage) {
|
||||||
|
this.message = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dao.impl;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatDAO;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.util.HeartBeatBeaconDAOUtil;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents implementation of HeartBeatDAO
|
||||||
|
*/
|
||||||
|
public class GenericHeartBeatDAOImpl implements HeartBeatDAO {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(GenericHeartBeatDAOImpl.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String recordServerCtx(ServerContext ctx) throws HeartBeatDAOException {
|
||||||
|
String uuid = null;
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String serverUUID = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
String sql;
|
||||||
|
sql = "INSERT INTO SERVER_HEART_BEAT_EVENTS(HOST_NAME, UUID, SERVER_PORT) VALUES (?, ?, ?)";
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
stmt.setString(1, ctx.getHostName());
|
||||||
|
stmt.setString(2, serverUUID);
|
||||||
|
stmt.setInt(3, ctx.getCarbonServerPort());
|
||||||
|
|
||||||
|
if (stmt.executeUpdate() > 0) {
|
||||||
|
uuid = serverUUID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while persisting server context for : '" +
|
||||||
|
"port '" + ctx.getCarbonServerPort() + "' " +
|
||||||
|
"hostname : '" + ctx.getHostName() + "'";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recordElectedCandidate(String serverUUID) throws HeartBeatDAOException {
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql;
|
||||||
|
sql = "INSERT INTO ELECTED_LEADER_META_INFO(UUID) VALUES (?)";
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
|
||||||
|
stmt.setString(1, serverUUID);
|
||||||
|
return stmt.executeUpdate() > 0;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while persisting UUID of chosen " +
|
||||||
|
"elected dynamic task execution candidate : " + serverUUID;
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void purgeCandidates() throws HeartBeatDAOException {
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
conn.setAutoCommit(false);
|
||||||
|
String sql = "TRUNCATE TABLE ELECTED_LEADER_META_INFO";
|
||||||
|
try (Statement stmt = conn.createStatement()) {
|
||||||
|
stmt.execute(sql);
|
||||||
|
conn.commit();
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while truncating ELECTED_LEADER_META_INFO table.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElectedCandidate retrieveCandidate() throws HeartBeatDAOException {
|
||||||
|
ElectedCandidate candidate = null;
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql = "SELECT * from ELECTED_LEADER_META_INFO";
|
||||||
|
|
||||||
|
try (Statement stmt = conn.createStatement()) {
|
||||||
|
try (ResultSet resultSet = stmt.executeQuery(sql)) {
|
||||||
|
while (resultSet.next()) {
|
||||||
|
candidate = HeartBeatBeaconDAOUtil.populateCandidate(resultSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while retrieving meta information of elected candidate";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean acknowledgeTask(String uuid, List<String> taskList)
|
||||||
|
throws HeartBeatDAOException {
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql;
|
||||||
|
sql = "UPDATE ELECTED_LEADER_META_INFO SET ACKNOWLEDGED_TASK_LIST = ? WHERE UUID = ?";
|
||||||
|
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"UUID"})) {
|
||||||
|
stmt.setString(1, String.join(",", taskList));
|
||||||
|
stmt.setString(2, uuid);
|
||||||
|
return stmt.executeUpdate() > 0;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while updating task list of elected server : '" +
|
||||||
|
uuid + "' and task list " + taskList;
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recordHeatBeat(HeartBeatEvent event) throws HeartBeatDAOException {
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql;
|
||||||
|
sql = "UPDATE SERVER_HEART_BEAT_EVENTS SET LAST_UPDATED_TIMESTAMP = ? WHERE UUID = ?";
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"ID"})) {
|
||||||
|
stmt.setTimestamp(1, event.getTime());
|
||||||
|
stmt.setString(2, event.getServerUUID());
|
||||||
|
return stmt.executeUpdate() > 0;
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while updating heartbeat event against server with UUID : '" +
|
||||||
|
event.getServerUUID() + "' and timestamp " + event.getTime();
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkUUIDValidity(String uuid) throws HeartBeatDAOException {
|
||||||
|
boolean result = false;
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql = "SELECT ID FROM SERVER_HEART_BEAT_EVENTS WHERE UUID = ?";
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setString(1, uuid);
|
||||||
|
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||||
|
if (resultSet.next()) {
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred checking existense of UUID" + uuid +
|
||||||
|
" amongst heartbeat meta info.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String retrieveExistingServerCtx(ServerContext ctx) throws HeartBeatDAOException {
|
||||||
|
String uuid = null;
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql = "SELECT UUID FROM SERVER_HEART_BEAT_EVENTS WHERE HOST_NAME = ? AND SERVER_PORT = ?";
|
||||||
|
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql, new String[]{"UUID"})) {
|
||||||
|
stmt.setString(1, ctx.getHostName());
|
||||||
|
stmt.setInt(2, ctx.getCarbonServerPort());
|
||||||
|
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||||
|
if (resultSet.next()) {
|
||||||
|
uuid = resultSet.getString("UUID");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while retrieving meta information for heart beat event from " +
|
||||||
|
"port '" + ctx.getCarbonServerPort() + "' " +
|
||||||
|
"hostname : '" + ctx.getHostName() + "'";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, ServerContext> getActiveServerDetails(int elapsedTimeInSeconds)
|
||||||
|
throws HeartBeatDAOException {
|
||||||
|
Map<String, ServerContext> ctxList = new HashMap<>();
|
||||||
|
try {
|
||||||
|
Connection conn = HeartBeatBeaconDAOFactory.getConnection();
|
||||||
|
String sql = "SELECT (@row_number:=@row_number + 1) AS IDX, UUID, HOST_NAME, SERVER_PORT from " +
|
||||||
|
"SERVER_HEART_BEAT_EVENTS, (SELECT @row_number:=-1) AS TEMP " +
|
||||||
|
"WHERE LAST_UPDATED_TIMESTAMP > ? " +
|
||||||
|
"ORDER BY UUID";
|
||||||
|
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||||
|
stmt.setTimestamp(1, new Timestamp(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(elapsedTimeInSeconds)));
|
||||||
|
try (ResultSet resultSet = stmt.executeQuery()) {
|
||||||
|
while (resultSet.next()) {
|
||||||
|
ctxList.put(resultSet.getString("UUID"), HeartBeatBeaconDAOUtil.populateContext(resultSet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while retrieving acting server count with " +
|
||||||
|
"heartbeat updates within " + elapsedTimeInSeconds + " seconds.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatDAOException(msg, e);
|
||||||
|
}
|
||||||
|
return ctxList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dao.util;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import javax.naming.InitialContext;
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class represents utilities required to work with group management data
|
||||||
|
*/
|
||||||
|
public final class HeartBeatBeaconDAOUtil {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(HeartBeatBeaconDAOUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup resources used to transaction
|
||||||
|
*
|
||||||
|
* @param stmt Prepared statement used
|
||||||
|
* @param rs Obtained results set
|
||||||
|
*/
|
||||||
|
public static void cleanupResources(PreparedStatement stmt, ResultSet rs) {
|
||||||
|
if (rs != null) {
|
||||||
|
try {
|
||||||
|
rs.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.warn("Error occurred while closing result set", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (stmt != null) {
|
||||||
|
try {
|
||||||
|
stmt.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.warn("Error occurred while closing prepared statement", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup datasource using name and jndi properties
|
||||||
|
*
|
||||||
|
* @param dataSourceName Name of datasource to lookup
|
||||||
|
* @param jndiProperties Hash table of JNDI Properties
|
||||||
|
* @return datasource looked
|
||||||
|
*/
|
||||||
|
public static DataSource lookupDataSource(String dataSourceName,
|
||||||
|
final Hashtable<Object, Object> jndiProperties) {
|
||||||
|
try {
|
||||||
|
if (jndiProperties == null || jndiProperties.isEmpty()) {
|
||||||
|
return (DataSource) InitialContext.doLookup(dataSourceName);
|
||||||
|
}
|
||||||
|
final InitialContext context = new InitialContext(jndiProperties);
|
||||||
|
return (DataSource) context.lookup(dataSourceName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Error in looking up data source: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ServerContext populateContext(ResultSet resultSet) throws SQLException {
|
||||||
|
ServerContext ctx = new ServerContext();
|
||||||
|
ctx.setIndex(resultSet.getInt("IDX"));
|
||||||
|
ctx.setUuid(resultSet.getString("UUID"));
|
||||||
|
ctx.setHostName(resultSet.getString("HOST_NAME"));
|
||||||
|
ctx.setCarbonServerPort(resultSet.getInt("SERVER_PORT"));
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ElectedCandidate populateCandidate(ResultSet resultSet) throws SQLException {
|
||||||
|
ElectedCandidate candidate = new ElectedCandidate();
|
||||||
|
candidate.setServerUUID(resultSet.getString("UUID"));
|
||||||
|
candidate.setTimeOfElection(resultSet.getTimestamp("ELECTED_TIME"));
|
||||||
|
String tasksList = resultSet.getString("ACKNOWLEDGED_TASK_LIST");
|
||||||
|
if(tasksList != null && !tasksList.isEmpty()){
|
||||||
|
candidate.setAcknowledgedTaskList(Arrays.asList(tasksList.split(",")));
|
||||||
|
}
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dto;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ElectedCandidate {
|
||||||
|
|
||||||
|
private String serverUUID;
|
||||||
|
private Timestamp timeOfElection;
|
||||||
|
private List<String> acknowledgedTaskList = null;
|
||||||
|
|
||||||
|
public List<String> getAcknowledgedTaskList() {
|
||||||
|
return acknowledgedTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAcknowledgedTaskList(List<String> acknowledgedTaskList) {
|
||||||
|
this.acknowledgedTaskList = acknowledgedTaskList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerUUID() {
|
||||||
|
return serverUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerUUID(String serverUUID) {
|
||||||
|
this.serverUUID = serverUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timestamp getTimeOfElection() {
|
||||||
|
return timeOfElection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeOfElection(Timestamp timeOfElection) {
|
||||||
|
this.timeOfElection = timeOfElection;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dto;
|
||||||
|
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
public class HeartBeatEvent {
|
||||||
|
|
||||||
|
private String serverUUID;
|
||||||
|
private Timestamp time;
|
||||||
|
|
||||||
|
public HeartBeatEvent(String serverUUID){
|
||||||
|
this.serverUUID = serverUUID;
|
||||||
|
this.time = new Timestamp(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerUUID() {
|
||||||
|
return serverUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerUUID(String serverUUID) {
|
||||||
|
this.serverUUID = serverUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timestamp getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(Timestamp time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.dto;
|
||||||
|
|
||||||
|
public class ServerContext {
|
||||||
|
|
||||||
|
private String hostName;
|
||||||
|
private int carbonServerPort;
|
||||||
|
private String uuid;
|
||||||
|
private int index;
|
||||||
|
|
||||||
|
public String getHostName() {
|
||||||
|
return hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHostName(String hostName) {
|
||||||
|
this.hostName = hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUuid() {
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUuid(String uuid) {
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCarbonServerPort() {
|
||||||
|
return carbonServerPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCarbonServerPort(int carbonServerPort) {
|
||||||
|
this.carbonServerPort = carbonServerPort;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.exception;
|
||||||
|
|
||||||
|
public class HeartBeatManagementException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 5304352685379661115L;
|
||||||
|
|
||||||
|
public HeartBeatManagementException(String msg, Exception nestedEx) {
|
||||||
|
super(msg, nestedEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatManagementException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatManagementException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatManagementException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatManagementException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.exception;
|
||||||
|
|
||||||
|
public class InvalidConfigurationStateException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -3151279311329070297L;
|
||||||
|
|
||||||
|
private String errorMessage;
|
||||||
|
private int errorCode;
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(int errorCode, String message) {
|
||||||
|
super(message);
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(int errorCode, String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.errorCode = errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorCode() {
|
||||||
|
return errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorMessage(String errorMessage) {
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(String msg, Exception nestedEx) {
|
||||||
|
super(msg, nestedEx);
|
||||||
|
setErrorMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
setErrorMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
setErrorMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InvalidConfigurationStateException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.exception;
|
||||||
|
|
||||||
|
public class ServerStatusUpdationFailedException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -2610630531027402610L;
|
||||||
|
|
||||||
|
public ServerStatusUpdationFailedException(String msg, Exception nestedEx) {
|
||||||
|
super(msg, nestedEx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerStatusUpdationFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerStatusUpdationFailedException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerStatusUpdationFailedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerStatusUpdationFailedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.internal;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.datasource.DataSourceConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementServiceImpl;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.osgi.service.component.ComponentContext;
|
||||||
|
import org.wso2.carbon.ndatasource.core.DataSourceService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @scr.component name="io.entgra.server.bootup.heartbeat.beacon.heartbeatBeaconComponent"
|
||||||
|
* immediate="true"
|
||||||
|
* @scr.reference name="org.wso2.carbon.ndatasource"
|
||||||
|
* interface="org.wso2.carbon.ndatasource.core.DataSourceService"
|
||||||
|
* cardinality="1..1"
|
||||||
|
* policy="dynamic"
|
||||||
|
* bind="setDataSourceService"
|
||||||
|
* unbind="unsetDataSourceService"
|
||||||
|
*/
|
||||||
|
public class HeartBeatBeaconComponent {
|
||||||
|
|
||||||
|
private static Log log = LogFactory.getLog(HeartBeatBeaconComponent.class);
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected void activate(ComponentContext componentContext) {
|
||||||
|
try {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Initializing heart beat management bundle");
|
||||||
|
}
|
||||||
|
this.registerHeartBeatServices(componentContext);
|
||||||
|
|
||||||
|
//heart beat notifier configuration */
|
||||||
|
HeartBeatBeaconConfig.init();
|
||||||
|
|
||||||
|
if(HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
DataSourceConfig dsConfig = HeartBeatBeaconConfig.getInstance().getDataSourceConfig();
|
||||||
|
HeartBeatBeaconDAOFactory.init(dsConfig);
|
||||||
|
|
||||||
|
//Setting up executors to notify heart beat status */
|
||||||
|
HeartBeatExecutor.setUpNotifiers(HeartBeatBeaconUtils.getServerDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Heart Beat Notifier bundle has been successfully initialized");
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.error("Error occurred while initializing Heart Beat Notifier bundle", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
protected void deactivate(ComponentContext componentContext) {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerHeartBeatServices(ComponentContext componentContext) {
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Registering Heart Beat Management service");
|
||||||
|
}
|
||||||
|
HeartBeatManagementService heartBeatServiceProvider = new HeartBeatManagementServiceImpl();
|
||||||
|
HeartBeatBeaconDataHolder.getInstance().setHeartBeatManagementService(heartBeatServiceProvider);
|
||||||
|
componentContext.getBundleContext().registerService(HeartBeatManagementService.class, heartBeatServiceProvider, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setDataSourceService(DataSourceService dataSourceService) {
|
||||||
|
/* This is to avoid mobile device management component getting initialized before the underlying datasources
|
||||||
|
are registered */
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Data source service set to heart beat management component");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void unsetDataSourceService(DataSourceService dataSourceService) {
|
||||||
|
//do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.internal;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
|
||||||
|
|
||||||
|
public class HeartBeatBeaconDataHolder {
|
||||||
|
|
||||||
|
private HeartBeatManagementService heartBeatManagementService;
|
||||||
|
private String localServerUUID;
|
||||||
|
|
||||||
|
private static HeartBeatBeaconDataHolder thisInstance = new HeartBeatBeaconDataHolder();
|
||||||
|
|
||||||
|
private HeartBeatBeaconDataHolder() {}
|
||||||
|
|
||||||
|
public static HeartBeatBeaconDataHolder getInstance() {
|
||||||
|
return thisInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HeartBeatManagementService getHeartBeatManagementService() {
|
||||||
|
return heartBeatManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeartBeatManagementService(HeartBeatManagementService heartBeatManagementService) {
|
||||||
|
this.heartBeatManagementService = heartBeatManagementService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLocalServerUUID() {
|
||||||
|
return localServerUUID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLocalServerUUID(String localServerUUID) {
|
||||||
|
this.localServerUUID = localServerUUID;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.internal;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconConfigurationException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.HeartBeatBeaconUtils;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class HeartBeatExecutor {
|
||||||
|
|
||||||
|
private static Log log = LogFactory.getLog(HeartBeatExecutor.class);
|
||||||
|
private static final int DEFAULT__NOTIFIER_INTERVAL = 5;
|
||||||
|
private static final int DEFAULT_NOTIFIER_DELAY = 5;
|
||||||
|
private static HeartBeatBeaconConfig CONFIG;
|
||||||
|
|
||||||
|
static {
|
||||||
|
CONFIG = HeartBeatBeaconConfig.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setUpNotifiers(ServerContext ctx) throws HeartBeatBeaconConfigurationException {
|
||||||
|
ScheduledExecutorService executor =
|
||||||
|
Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
if (CONFIG == null) {
|
||||||
|
String msg = "Error while initiating schedule taks for recording heartbeats.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatBeaconConfigurationException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String uuid = HeartBeatBeaconUtils.readUUID();
|
||||||
|
if (uuid == null) {
|
||||||
|
uuid = HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().updateServerContext(ctx);
|
||||||
|
HeartBeatBeaconUtils.saveUUID(uuid);
|
||||||
|
}
|
||||||
|
int timeOutIntervalInSeconds = CONFIG.getServerTimeOutIntervalInSeconds();
|
||||||
|
int timeSkew = CONFIG.getTimeSkew();
|
||||||
|
int cumilativeTimeOut = timeOutIntervalInSeconds + timeSkew;
|
||||||
|
final String designatedUUID = uuid;
|
||||||
|
HeartBeatBeaconDataHolder.getInstance().setLocalServerUUID(designatedUUID);
|
||||||
|
Runnable periodicTask = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
recordHeartBeat(designatedUUID);
|
||||||
|
electDynamicTaskExecutionCandidate(cumilativeTimeOut);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Error while executing record heart beat task. This will result in schedule operation malfunction.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
executor.scheduleAtFixedRate(periodicTask,
|
||||||
|
CONFIG.getNotifierDelay() != 0 ? CONFIG.getNotifierDelay() : DEFAULT_NOTIFIER_DELAY,
|
||||||
|
CONFIG.getNotifierFrequency() != 0 ? CONFIG.getNotifierFrequency() : DEFAULT__NOTIFIER_INTERVAL,
|
||||||
|
TimeUnit.SECONDS);
|
||||||
|
} catch (HeartBeatManagementException e) {
|
||||||
|
String msg = "Error occured while updating initial server context.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatBeaconConfigurationException(msg, e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
String msg = "Error while persisting UUID of server.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatBeaconConfigurationException(msg, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void recordHeartBeat(String uuid) throws HeartBeatManagementException {
|
||||||
|
HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().recordHeartBeat(new HeartBeatEvent(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void electDynamicTaskExecutionCandidate(int cumilativeTimeOut)
|
||||||
|
throws HeartBeatManagementException {
|
||||||
|
HeartBeatBeaconDataHolder.getInstance().getHeartBeatManagementService().electCandidate(cumilativeTimeOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.service;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
|
||||||
|
|
||||||
|
public interface HeartBeatManagementService {
|
||||||
|
|
||||||
|
boolean isTaskPartitioningEnabled() throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
String updateServerContext(ServerContext ctx) throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
boolean updateTaskExecutionAcknowledgement(String newTask) throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
boolean isQualifiedToExecuteTask() throws HeartBeatManagementException;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,305 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.entgra.server.bootup.heartbeat.beacon.service;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.config.HeartBeatBeaconConfig;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatBeaconDAOFactory;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.HeartBeatDAO;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dao.exception.HeartBeatDAOException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ElectedCandidate;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.internal.HeartBeatBeaconDataHolder;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
|
||||||
|
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
|
||||||
|
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class HeartBeatManagementServiceImpl implements HeartBeatManagementService {
|
||||||
|
|
||||||
|
private static final Log log = LogFactory.getLog(HeartBeatManagementServiceImpl.class);
|
||||||
|
|
||||||
|
private final HeartBeatDAO heartBeatDAO;
|
||||||
|
|
||||||
|
public HeartBeatManagementServiceImpl() {
|
||||||
|
this.heartBeatDAO = HeartBeatBeaconDAOFactory.getHeartBeatDAO();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
|
||||||
|
int hashIndex = -1;
|
||||||
|
ServerContext localServerCtx = null;
|
||||||
|
ServerCtxInfo serverCtxInfo = null;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
HeartBeatBeaconDAOFactory.openConnection();
|
||||||
|
int timeOutIntervalInSeconds = HeartBeatBeaconConfig.getInstance().getServerTimeOutIntervalInSeconds();
|
||||||
|
int timeSkew = HeartBeatBeaconConfig.getInstance().getTimeSkew();
|
||||||
|
int cumilativeTimeOut = timeOutIntervalInSeconds + timeSkew;
|
||||||
|
String localServerUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
|
||||||
|
Map<String, ServerContext> serverCtxMap = heartBeatDAO.getActiveServerDetails(cumilativeTimeOut);
|
||||||
|
if (!serverCtxMap.isEmpty()) {
|
||||||
|
localServerCtx = serverCtxMap.get(localServerUUID);
|
||||||
|
if (localServerCtx != null) {
|
||||||
|
hashIndex = localServerCtx.getIndex();
|
||||||
|
serverCtxInfo = new ServerCtxInfo(serverCtxMap.size(), hashIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while opening a connection to the underlying data source";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error occurred while retrieving active server count.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Server Context Information Not available.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return serverCtxInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
|
||||||
|
boolean enabled = false;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance() != null) {
|
||||||
|
enabled = HeartBeatBeaconConfig.getInstance().isEnabled();
|
||||||
|
} else {
|
||||||
|
String msg = "Issue instantiating heart beat config.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
|
||||||
|
String uuid = null;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
HeartBeatBeaconDAOFactory.beginTransaction();
|
||||||
|
uuid = heartBeatDAO.retrieveExistingServerCtx(ctx);
|
||||||
|
if (uuid == null) {
|
||||||
|
uuid = heartBeatDAO.recordServerCtx(ctx);
|
||||||
|
HeartBeatBeaconDAOFactory.commitTransaction();
|
||||||
|
}
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error Occured while retrieving server context.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (TransactionManagementException e) {
|
||||||
|
HeartBeatBeaconDAOFactory.rollbackTransaction();
|
||||||
|
String msg = "Error occurred while updating server context. Issue in opening a connection to the underlying data source";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Updating Server Context Failed.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
|
||||||
|
boolean isQualified = false;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
String localServerUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
|
||||||
|
HeartBeatBeaconDAOFactory.openConnection();
|
||||||
|
ElectedCandidate candidate = heartBeatDAO.retrieveCandidate();
|
||||||
|
if (candidate != null && candidate.getServerUUID().equalsIgnoreCase(localServerUUID)) {
|
||||||
|
isQualified = true;
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Node : " + localServerUUID + " Qualified to execute randomly assigned task.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error occurred while checking if server is qualified to execute randomly designated task.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
String msg = "Error occurred while opening a connection to the underlying data source";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Error occurred while checking if server is qualified to execute randomly designated task.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return isQualified;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateTaskExecutionAcknowledgement(String newTask)
|
||||||
|
throws HeartBeatManagementException {
|
||||||
|
boolean result = false;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
String serverUUID = HeartBeatBeaconDataHolder.getInstance().getLocalServerUUID();
|
||||||
|
HeartBeatBeaconDAOFactory.beginTransaction();
|
||||||
|
ElectedCandidate candidate = heartBeatDAO.retrieveCandidate();
|
||||||
|
if (candidate != null && candidate.getServerUUID().equals(serverUUID)) {
|
||||||
|
List<String> taskList = candidate.getAcknowledgedTaskList();
|
||||||
|
boolean taskExecuted = false;
|
||||||
|
for (String task : taskList) {
|
||||||
|
if (task.equalsIgnoreCase(newTask)) {
|
||||||
|
taskExecuted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!taskExecuted) {
|
||||||
|
taskList.add(newTask);
|
||||||
|
result = heartBeatDAO.acknowledgeTask(serverUUID, taskList);
|
||||||
|
HeartBeatBeaconDAOFactory.commitTransaction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error occurred while updating acknowledged task.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (TransactionManagementException e) {
|
||||||
|
HeartBeatBeaconDAOFactory.rollbackTransaction();
|
||||||
|
String msg = "Error occurred while updating acknowledged task.. Issue in opening a connection to the underlying data source";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Updating acknowledged task list failed.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
HeartBeatBeaconDAOFactory.beginTransaction();
|
||||||
|
Map<String, ServerContext> servers = heartBeatDAO.getActiveServerDetails(elapsedTimeInSeconds);
|
||||||
|
if (servers != null && !servers.isEmpty()) {
|
||||||
|
ElectedCandidate presentCandidate = heartBeatDAO.retrieveCandidate();
|
||||||
|
if (presentCandidate != null) {
|
||||||
|
//if candidate is older than stipulated elapsed-time, purge and re-elect
|
||||||
|
if (presentCandidate.getTimeOfElection().before(new Timestamp(System.currentTimeMillis()
|
||||||
|
- TimeUnit.SECONDS.toMillis(elapsedTimeInSeconds)))) {
|
||||||
|
heartBeatDAO.purgeCandidates();
|
||||||
|
electCandidate(servers);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//first time execution, elect if not present
|
||||||
|
electCandidate(servers);
|
||||||
|
}
|
||||||
|
HeartBeatBeaconDAOFactory.commitTransaction();
|
||||||
|
}
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error occurred while electing candidate for dynamic task execution.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (TransactionManagementException e) {
|
||||||
|
HeartBeatBeaconDAOFactory.rollbackTransaction();
|
||||||
|
String msg = "Error occurred while electing candidate for dynamic task execution. Issue in opening a connection to the underlying data source";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Error electing candidate for dynamic task execution.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void electCandidate(Map<String, ServerContext> servers) throws HeartBeatDAOException {
|
||||||
|
String electedCandidate = getRandomElement(servers.keySet());
|
||||||
|
heartBeatDAO.recordElectedCandidate(electedCandidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getRandomElement(Set<String> valueSet) {
|
||||||
|
Random rand = new Random();
|
||||||
|
List<String> items = new ArrayList<>(valueSet);
|
||||||
|
return items.get(rand.nextInt(items.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
|
||||||
|
boolean operationSuccess = false;
|
||||||
|
if (HeartBeatBeaconConfig.getInstance().isEnabled()) {
|
||||||
|
try {
|
||||||
|
HeartBeatBeaconDAOFactory.beginTransaction();
|
||||||
|
if (heartBeatDAO.checkUUIDValidity(event.getServerUUID())) {
|
||||||
|
operationSuccess = heartBeatDAO.recordHeatBeat(event);
|
||||||
|
HeartBeatBeaconDAOFactory.commitTransaction();
|
||||||
|
} else {
|
||||||
|
String msg = "Server UUID Does not exist, heartbeat not recorded.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
} catch (HeartBeatDAOException e) {
|
||||||
|
String msg = "Error occurred while recording heart beat.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} catch (TransactionManagementException e) {
|
||||||
|
HeartBeatBeaconDAOFactory.rollbackTransaction();
|
||||||
|
String msg = "Error occurred performing heart beat record transaction. " +
|
||||||
|
"Transaction rolled back.";
|
||||||
|
log.error(msg, e);
|
||||||
|
throw new HeartBeatManagementException(msg, e);
|
||||||
|
} finally {
|
||||||
|
HeartBeatBeaconDAOFactory.closeConnection();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String msg = "Heart Beat Configuration Disabled. Recording Heart Beat Failed.";
|
||||||
|
log.error(msg);
|
||||||
|
throw new HeartBeatManagementException(msg);
|
||||||
|
}
|
||||||
|
return operationSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2020 Entgra Pvt. Ltd.. (http://entgra.io)
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# This is the log4j configuration file used by Entgra Pvt. Ltd.
|
||||||
|
#
|
||||||
|
# IMPORTANT : Please do not remove or change the names of any
|
||||||
|
# of the Appenders defined here. The layout pattern & log file
|
||||||
|
# can be changed using the WSO2 Carbon Management Console, and those
|
||||||
|
# settings will override the settings in this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
log4j.rootLogger=INFO, STD_OUT
|
||||||
|
|
||||||
|
# Redirect log messages to console
|
||||||
|
log4j.appender.STD_OUT=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.STD_OUT.Target=System.out
|
||||||
|
log4j.appender.STD_OUT.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.STD_OUT.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
|
@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
~ Version 2.0 (the "License"); you may not use this file except
|
||||||
|
~ in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
|
||||||
|
|
||||||
|
<suite name="DeviceManagementCore">
|
||||||
|
<parameter name="useDefaultListeners" value="false"/>
|
||||||
|
|
||||||
|
<test name="HeartBeatBeaconTests" preserve-order="true">
|
||||||
|
<classes>
|
||||||
|
</classes>
|
||||||
|
</test>
|
||||||
|
</suite>
|
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
~ Version 2.0 (the "License"); you may not use this file except
|
||||||
|
~ in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>carbon-devicemgt</artifactId>
|
||||||
|
<version>4.1.11-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>heartbeat-management</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Entgra - Task Allocation Framework</name>
|
||||||
|
<url>http://entgra.io</url>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>io.entgra.server.bootup.heartbeat.beacon</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, Entgra Pvt Ltd. (http://www.wso2.org) All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Entgra Pvt Ltd. licenses this file to you under the Apache License,
|
||||||
|
* Version 2.0 (the "License"); you may not use this file except
|
||||||
|
* in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.wso2.carbon.policy.mgt.core.mock;
|
||||||
|
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.HeartBeatEvent;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.dto.ServerContext;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.exception.HeartBeatManagementException;
|
||||||
|
import io.entgra.server.bootup.heartbeat.beacon.service.HeartBeatManagementService;
|
||||||
|
import org.wso2.carbon.device.mgt.common.ServerCtxInfo;
|
||||||
|
|
||||||
|
public class TestHeartBeatManagementService implements HeartBeatManagementService {
|
||||||
|
@Override
|
||||||
|
public boolean isTaskPartitioningEnabled() throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerCtxInfo getServerCtxInfo() throws HeartBeatManagementException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String updateServerContext(ServerContext ctx) throws HeartBeatManagementException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean recordHeartBeat(HeartBeatEvent event) throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void electCandidate(int elapsedTimeInSeconds) throws HeartBeatManagementException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateTaskExecutionAcknowledgement(String newTask)
|
||||||
|
throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isQualifiedToExecuteTask() throws HeartBeatManagementException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,153 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
~ Version 2.0 (the "License"); you may not use this file except
|
||||||
|
~ in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>heart-beat-feature</artifactId>
|
||||||
|
<version>4.1.11-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>io.entgra.server.heart.beat.feature</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Entgra IoT - Heart Beat Feature</name>
|
||||||
|
<url>http://entgra.io</url>
|
||||||
|
<description>
|
||||||
|
This feature bundles for the heart beat beacon for Entgra IoT Server
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database.wso2</groupId>
|
||||||
|
<artifactId>h2-database-engine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>io.entgra.server.bootup.heartbeat.beacon</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.device.mgt.common</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.wso2.carbon.identity.inbound.auth.oauth2</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.identity.oauth.stub</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.axis2.wso2</groupId>
|
||||||
|
<artifactId>axis2-client</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.geronimo.specs.wso2</groupId>
|
||||||
|
<artifactId>geronimo-stax-api_1.0_spec</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.ws.commons.axiom.wso2</groupId>
|
||||||
|
<artifactId>axiom</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>woodstox-core-asl</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.codehaus.woodstox</groupId>
|
||||||
|
<artifactId>wstx-asl</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.device.mgt.analytics.data.publisher</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.wso2.carbon.analytics</groupId>
|
||||||
|
<artifactId>org.wso2.carbon.analytics.api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>src/main/resources</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>build.properties</include>
|
||||||
|
<include>p2.inf</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.wso2.maven</groupId>
|
||||||
|
<artifactId>carbon-p2-plugin</artifactId>
|
||||||
|
<version>${carbon.p2.plugin.version}</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>p2-feature-generation</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>p2-feature-gen</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<id>io.entgra.server.heart.beat</id>
|
||||||
|
<propertiesFile>../../../../features/etc/feature.properties
|
||||||
|
</propertiesFile>
|
||||||
|
<adviceFile>
|
||||||
|
<properties>
|
||||||
|
<propertyDef>org.wso2.carbon.p2.category.type:server
|
||||||
|
</propertyDef>
|
||||||
|
<propertyDef>org.eclipse.equinox.p2.type.group:true
|
||||||
|
</propertyDef>
|
||||||
|
</properties>
|
||||||
|
</adviceFile>
|
||||||
|
<bundles>
|
||||||
|
<bundleDef>
|
||||||
|
org.wso2.carbon.devicemgt:io.entgra.server.bootup.heartbeat.beacon:${carbon.device.mgt.version}
|
||||||
|
</bundleDef>
|
||||||
|
</bundles>
|
||||||
|
<importFeatures>
|
||||||
|
<importFeatureDef>org.wso2.carbon.core.server:${carbon.kernel.version}</importFeatureDef>
|
||||||
|
</importFeatures>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,22 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
|
||||||
|
#
|
||||||
|
# Licensed under the Entgra Commercial License, Version 1.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://entgra.io/licenses/entgra-commercial/1.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
jarProcessor.unsign=true
|
||||||
|
signJars=true
|
||||||
|
custom = true
|
@ -0,0 +1,53 @@
|
|||||||
|
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
|
||||||
|
~
|
||||||
|
~ Licensed under the Entgra Commercial License, Version 1.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://entgra.io/licenses/entgra-commercial/1.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<HeartBeatBeaconConfig>
|
||||||
|
<!--
|
||||||
|
~ Only to be turned on in distributed mode, in which case
|
||||||
|
~ Dynamic Partitoned Task, Randomly Assigned Task modes will be activated.
|
||||||
|
~ These modes are deviations from the regular worker manager mode, in which the server will function.
|
||||||
|
~
|
||||||
|
~ * DynamicPartitionedScheduleTask -> Tasks will execute on all nodes but will only focus on a subset of operations
|
||||||
|
~ broken up amongst active nodes based on following parameters that are made available via DynamicTaskContext abstract class.
|
||||||
|
~ - serverHashIndex : dynamic index assigned to each server based on number of active servers present
|
||||||
|
~ - activeServerCount : total no. of active servers present at a given time
|
||||||
|
~ - partitioningEnabled : whether or not heart-beat-config/dynamic task config is enabled.
|
||||||
|
~ Any task extending the DynamicPartitionedScheduleTask class will inherit the above variables.
|
||||||
|
~ These can then be used to split up the no. of devices said node is allocated with.
|
||||||
|
~
|
||||||
|
~ * RandomlyAssignedScheduleTask -> Tasks will only execute on a particular node elected at a time interval determined by ServerTimeOutIntervalInSeconds, parameter.
|
||||||
|
~ When running tasks in this mode, it is mandatory that each task be given a unique name, so as not to run in to scenarios where tasks execution is duplicated.
|
||||||
|
~ Each node, on a periodic basis will check to see if it is qualfied to execute the random task, if the following variable is true only said node will execute the task.
|
||||||
|
~ - qualifiedToExecuteTask : dynamic index assigned to each server based on number of active servers present
|
||||||
|
~ Any task extending the RandomlyAssignedScheduleTask class will inherit the above variable.
|
||||||
|
~ Note that random task may execute on any node.
|
||||||
|
-->
|
||||||
|
<Enable>false</Enable>
|
||||||
|
<!-- Heart Beat Data Source Name, this should be either MySQL, PostGreSQL, Oracle or MSSQL -->
|
||||||
|
<DataSourceConfiguration>
|
||||||
|
<JndiLookupDefinition>
|
||||||
|
<Name>jdbc/HeartBeat_DS</Name>
|
||||||
|
</JndiLookupDefinition>
|
||||||
|
</DataSourceConfiguration>
|
||||||
|
<NotifierInitialDelayInSeconds>30</NotifierInitialDelayInSeconds>
|
||||||
|
<NotifierFrequencyInSeconds>300</NotifierFrequencyInSeconds>
|
||||||
|
<TimeSkewInSeconds>5</TimeSkewInSeconds>
|
||||||
|
<ServerTimeOutIntervalInSeconds>600</ServerTimeOutIntervalInSeconds>
|
||||||
|
</HeartBeatBeaconConfig>
|
@ -0,0 +1,46 @@
|
|||||||
|
<!--
|
||||||
|
~ Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
|
||||||
|
~
|
||||||
|
~ Licensed under the Entgra Commercial License, Version 1.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ https://entgra.io/licenses/entgra-commercial/1.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
<datasources-configuration xmlns:svns="http://org.wso2.securevault/configuration">
|
||||||
|
<providers>
|
||||||
|
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
|
||||||
|
</providers>
|
||||||
|
<datasources>
|
||||||
|
<datasource>
|
||||||
|
<name>HeartBeat_DS</name>
|
||||||
|
<description>The datasource Server Heart Beat</description>
|
||||||
|
<jndiConfig>
|
||||||
|
<name>jdbc/HeartBeat_DS</name>
|
||||||
|
</jndiConfig>
|
||||||
|
<definition type="RDBMS">
|
||||||
|
<configuration>
|
||||||
|
<url>jdbc:mysql://localhost:3306/heart_beat</url>
|
||||||
|
<username>root</username>
|
||||||
|
<password>root</password>
|
||||||
|
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
|
||||||
|
<maxActive>50</maxActive>
|
||||||
|
<maxWait>60000</maxWait>
|
||||||
|
<testOnBorrow>true</testOnBorrow>
|
||||||
|
<validationQuery>SELECT 1</validationQuery>
|
||||||
|
<validationInterval>30000</validationInterval>
|
||||||
|
</configuration>
|
||||||
|
</definition>
|
||||||
|
</datasource>
|
||||||
|
</datasources>
|
||||||
|
</datasources-configuration>
|
||||||
|
|
@ -0,0 +1,18 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `SERVER_HEART_BEAT_EVENTS`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
|
||||||
|
ID INTEGER AUTO_INCREMENT NOT NULL,
|
||||||
|
HOST_NAME VARCHAR(100) NOT NULL,
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
SERVER_PORT INTEGER NOT NULL,
|
||||||
|
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ID)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
|
||||||
|
PRIMARY KEY (UUID)
|
||||||
|
);
|
@ -0,0 +1,19 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `SERVER_HEART_BEAT_EVENTS`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[SERVER_HEART_BEAT_EVENTS]') AND TYPE IN (N'U'))
|
||||||
|
CREATE TABLE SERVER_HEART_BEAT_EVENTS (
|
||||||
|
ID INT NOT NULL AUTO_INCREMENT,
|
||||||
|
HOST_NAME VARCHAR(100) NOT NULL,
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
SERVER_PORT INT NOT NULL,
|
||||||
|
LAST_UPDATED_TIMESTAMP DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ID));
|
||||||
|
|
||||||
|
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[ELECTED_LEADER_META_INFO]') AND TYPE IN (N'U'))
|
||||||
|
CREATE TABLE ELECTED_LEADER_META_INFO (
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
ELECTED_TIME DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ACKNOWLEDGED_TASK_LIST VARCHAR(MAX) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (UUID));
|
@ -0,0 +1,18 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `SERVER_HEART_BEAT_EVENTS`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
|
||||||
|
ID INTEGER AUTO_INCREMENT NOT NULL,
|
||||||
|
HOST_NAME VARCHAR(100) NOT NULL,
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
SERVER_PORT INTEGER NOT NULL,
|
||||||
|
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ID)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
|
||||||
|
PRIMARY KEY (UUID)
|
||||||
|
);
|
@ -0,0 +1,21 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table `SERVER_HEART_BEAT_EVENTS`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
|
||||||
|
CREATE TABLE SERVER_HEART_BEAT_EVENTS (
|
||||||
|
ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL,
|
||||||
|
HOST_NAME VARCHAR(100) NOT NULL,
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
SERVER_PORT INTEGER NOT NULL,
|
||||||
|
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT PK_SERVER_HEART_BEAT_EVENTS PRIMARY KEY (ID)
|
||||||
|
)
|
||||||
|
/
|
||||||
|
|
||||||
|
CREATE TABLE ELECTED_LEADER_META_INFO (
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ACKNOWLEDGED_TASK_LIST BLOB DEFAULT NULL,
|
||||||
|
CONSTRAINT PK_SERVER_HEART_BEAT_EVENTS PRIMARY KEY (UUID)
|
||||||
|
)
|
||||||
|
/
|
@ -0,0 +1,18 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Table SERVER_HEART_BEAT_EVENTS
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
CREATE TABLE IF NOT EXISTS SERVER_HEART_BEAT_EVENTS (
|
||||||
|
ID INTEGER AUTO_INCREMENT NOT NULL,
|
||||||
|
HOST_NAME VARCHAR(100) NOT NULL,
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
SERVER_PORT INTEGER NOT NULL,
|
||||||
|
LAST_UPDATED_TIMESTAMP TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (ID)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS ELECTED_LEADER_META_INFO (
|
||||||
|
UUID VARCHAR(100) NOT NULL,
|
||||||
|
ELECTED_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ACKNOWLEDGED_TASK_LIST TEXT DEFAULT NULL,
|
||||||
|
PRIMARY KEY (UUID)
|
||||||
|
);
|
@ -0,0 +1,8 @@
|
|||||||
|
instructions.configure = \
|
||||||
|
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/datasources/,target:${installFolder}/../../conf/datasources/,overwrite:true);\
|
||||||
|
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/conf/heart-beat-config.xml,target:${installFolder}/../../conf/heart-beat-config.xml,overwrite:true);\
|
||||||
|
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/io.entgra.server.heart.beat_${feature.version}/dbscripts/heart-beat/,target:${installFolder}/../../../dbscripts/heart-beat,overwrite:true);\
|
||||||
|
|
||||||
|
instructions.unconfigure = \
|
||||||
|
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../conf/datasources/heart-beat-datasources.xml);\
|
||||||
|
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../../dbscripts/heart-beat);\
|
@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020, Entgra (Pvt) Ltd. (http://entgra.io) All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||||
|
~ Version 2.0 (the "License"); you may not use this file except
|
||||||
|
~ in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>carbon-devicemgt</artifactId>
|
||||||
|
<version>4.1.11-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>org.wso2.carbon.devicemgt</groupId>
|
||||||
|
<artifactId>heart-beat-feature</artifactId>
|
||||||
|
<version>4.1.11-SNAPSHOT</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Entgra - Heart Beat Feature</name>
|
||||||
|
<url>http://entgra.io</url>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>io.entgra.server.heart.beat.feature</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
</project>
|
Loading…
Reference in new issue