forked from community/device-mgt-core
Merge pull request 'Revert "Change the device status and see the device status lifecycle history (#58)"' (#61) from pahansith/device-mgt-core:revert-lifecycle-history into master
Reviewed-on: community/device-mgt-core#61pull/1/head
commit
215c0f44a0
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.jaxrs.beans;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import org.wso2.carbon.device.mgt.common.LifecycleStateDevice;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class LifecycleStateDeviceList extends BasePaginatedResult {
|
||||
|
||||
private List<LifecycleStateDevice> lifecycleStates = new ArrayList<>();
|
||||
|
||||
@ApiModelProperty(value = "List of lifecycleStates history returned")
|
||||
public List<LifecycleStateDevice> getLifecycleStates() {
|
||||
return lifecycleStates;
|
||||
}
|
||||
|
||||
public void setLifecycleStates(List<LifecycleStateDevice> lifecycleStates) {
|
||||
this.lifecycleStates = lifecycleStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LifecycleStateDeviceList{" +
|
||||
"lifecycleStates=" + lifecycleStates +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Information of the device lifecycle
|
||||
*/
|
||||
public class LifecycleStateDevice {
|
||||
|
||||
private int deviceId;
|
||||
private String currentStatus;
|
||||
private String previousStatus;
|
||||
private String updatedBy;
|
||||
private Date updatedAt;
|
||||
|
||||
public LifecycleStateDevice() {
|
||||
}
|
||||
|
||||
public LifecycleStateDevice(int deviceId, String currentStatus, String previousStatus, String updatedBy,
|
||||
Date updatedAt) {
|
||||
this.deviceId = deviceId;
|
||||
this.currentStatus = currentStatus;
|
||||
this.previousStatus = previousStatus;
|
||||
this.updatedBy = updatedBy;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public String getCurrentStatus() {
|
||||
return currentStatus;
|
||||
}
|
||||
|
||||
public void setCurrentStatus(String currentStatus) {
|
||||
this.currentStatus = currentStatus;
|
||||
}
|
||||
|
||||
public String getPreviousStatus() {
|
||||
return previousStatus;
|
||||
}
|
||||
|
||||
public void setPreviousStatus(String previousStatus) {
|
||||
this.previousStatus = previousStatus;
|
||||
}
|
||||
|
||||
public String getUpdatedBy() {
|
||||
return updatedBy;
|
||||
}
|
||||
|
||||
public void setUpdatedBy(String updatedBy) {
|
||||
this.updatedBy = updatedBy;
|
||||
}
|
||||
|
||||
public Date getUpdatedAt() {
|
||||
return updatedAt;
|
||||
}
|
||||
|
||||
public void setUpdatedAt(Date updatedAt) {
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
|
||||
public int getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public void setDeviceId(int deviceId) {
|
||||
this.deviceId = deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LifecycleStateDevice{" +
|
||||
"deviceId=" + deviceId +
|
||||
", currentStatus='" + currentStatus + '\'' +
|
||||
", previousStatus='" + previousStatus + '\'' +
|
||||
", updatedBy='" + updatedBy + '\'' +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 2023, 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.configuration.mgt;
|
||||
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* Bean of the DeviceLifecycleState
|
||||
*/
|
||||
@XmlRootElement(name = "LifecycleState")
|
||||
public class DeviceLifecycleState {
|
||||
|
||||
private String name;
|
||||
private List<String> proceedingStates;
|
||||
|
||||
@XmlAttribute(name = "name")
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@XmlElementWrapper(name = "ProceedingStates")
|
||||
@XmlElement(name = "State")
|
||||
public List<String> getProceedingStates() {
|
||||
return proceedingStates;
|
||||
}
|
||||
|
||||
public void setProceedingStates(List<String> proceedingStates) {
|
||||
this.proceedingStates = proceedingStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DeviceLifecycleState{" +
|
||||
"name='" + name + '\'' +
|
||||
", proceedingStates=" + proceedingStates +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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.exceptions;
|
||||
|
||||
public class DeviceStatusException extends Exception{
|
||||
|
||||
private static final long serialVersionUID = 1608833587090532707L;
|
||||
|
||||
public DeviceStatusException() {
|
||||
}
|
||||
|
||||
public DeviceStatusException(String msg, Exception nestedEx) {
|
||||
super(msg, nestedEx);
|
||||
}
|
||||
|
||||
|
||||
public DeviceStatusException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public DeviceStatusException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public DeviceStatusException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public DeviceStatusException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 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.exceptions;
|
||||
|
||||
public class InvalidStatusException extends Exception{
|
||||
|
||||
private static final long serialVersionUID = -7379721600057895944L;
|
||||
|
||||
|
||||
public InvalidStatusException() {
|
||||
}
|
||||
|
||||
public InvalidStatusException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidStatusException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public InvalidStatusException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public InvalidStatusException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.config.lifecycleState;
|
||||
|
||||
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceLifecycleState;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents Device lifecycle status configuration.
|
||||
*/
|
||||
@XmlRootElement(name = "LifecycleManagementConfiguration")
|
||||
public class DeviceLifecycleConfig {
|
||||
|
||||
private List<DeviceLifecycleState> deviceLifecycleStates;
|
||||
|
||||
@XmlElementWrapper(name = "LifecycleStates")
|
||||
@XmlElement(name = "LifecycleState")
|
||||
public List<DeviceLifecycleState> getDeviceLifecycleStates() {
|
||||
return deviceLifecycleStates;
|
||||
}
|
||||
|
||||
public void setDeviceLifecycleStates(List<DeviceLifecycleState> deviceLifecycleStates) {
|
||||
this.deviceLifecycleStates = deviceLifecycleStates;
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.config.lifecycleState;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
|
||||
import org.wso2.carbon.utils.CarbonUtils;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Class responsible for the LifecycleStates configuration initialization.
|
||||
*/
|
||||
public class DeviceLifecycleConfigManager {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DeviceLifecycleConfigManager.class);
|
||||
private DeviceLifecycleConfig deviceLifecycleConfig;
|
||||
private static volatile DeviceLifecycleConfigManager deviceLifecycleConfigManager;
|
||||
private static final String DEVICE_LIFECYCLE_PATH = CarbonUtils.getCarbonConfigDirPath() + File.separator
|
||||
+ DeviceManagementConstants.DataSourceProperties.DEVICE_LIFECYCLE_STATES_XML_NAME;
|
||||
|
||||
private DeviceLifecycleConfigManager() {
|
||||
}
|
||||
|
||||
public static DeviceLifecycleConfigManager getInstance() {
|
||||
if (deviceLifecycleConfigManager == null) {
|
||||
synchronized (DeviceLifecycleConfigManager.class) {
|
||||
if (deviceLifecycleConfigManager == null) {
|
||||
deviceLifecycleConfigManager = new DeviceLifecycleConfigManager();
|
||||
try {
|
||||
deviceLifecycleConfigManager.initConfig();
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
deviceLifecycleConfigManager.initConfig();
|
||||
} catch (Exception e) {
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deviceLifecycleConfigManager;
|
||||
}
|
||||
|
||||
public synchronized void initConfig() {
|
||||
try {
|
||||
File deviceLifecycleConfig = new File(DEVICE_LIFECYCLE_PATH);
|
||||
JAXBContext jaxbContext = JAXBContext.newInstance(DeviceLifecycleConfig.class);
|
||||
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
|
||||
this.deviceLifecycleConfig = (DeviceLifecycleConfig) unmarshaller.unmarshal(deviceLifecycleConfig);
|
||||
|
||||
} catch (JAXBException e) {
|
||||
String msg = "Error occured while initializing deviceLifecycle config";
|
||||
log.error(msg, e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (Exception e) {
|
||||
String msg = "Error(Exception) occured while initializing deviceLifecycle config";
|
||||
log.error(msg, e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public DeviceLifecycleConfig getDeviceLifecycleConfig() {
|
||||
return deviceLifecycleConfig;
|
||||
}
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.dao;
|
||||
|
||||
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
|
||||
import org.wso2.carbon.device.mgt.common.LifecycleStateDevice;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Device status relevent DAO activity
|
||||
*/
|
||||
public interface DeviceLifecycleDAO {
|
||||
|
||||
/**
|
||||
* can change the relevent device's status
|
||||
*
|
||||
* @param enrolmentId Enrolment Id
|
||||
* @param status changing status
|
||||
* @param tenantId tenantId
|
||||
* @return true or false
|
||||
* @throws DeviceManagementDAOException when device no found
|
||||
*/
|
||||
boolean changeStatus(int enrolmentId, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException;
|
||||
|
||||
/**
|
||||
* Add the changed status
|
||||
*
|
||||
* @param enrolmentId Enrolment Id
|
||||
* @param currentStatus Current Status
|
||||
* @param previousStatus Previous Status
|
||||
* @param deviceId Id of the device
|
||||
* @return Added or not, true or false
|
||||
* @throws DeviceManagementDAOException When device not found
|
||||
*/
|
||||
boolean addStatus(int enrolmentId, EnrolmentInfo.Status currentStatus, EnrolmentInfo.Status previousStatus,
|
||||
int deviceId) throws DeviceManagementDAOException;
|
||||
|
||||
/**
|
||||
* Get Device ID
|
||||
*
|
||||
* @param enrolmentId Enrolment ID
|
||||
* @return Device id
|
||||
* @throws DeviceManagementDAOException when device not found
|
||||
*/
|
||||
int getDeviceId(int enrolmentId) throws DeviceManagementDAOException;
|
||||
|
||||
/**
|
||||
* Get the lifecycle history of the device
|
||||
*
|
||||
* @param id id of the device
|
||||
* @return List of LifecycleStateDevice
|
||||
*/
|
||||
List<LifecycleStateDevice> getDeviceLifecycle(int id) throws DeviceManagementDAOException;
|
||||
}
|
@ -1,174 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.dao.impl;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
import org.wso2.carbon.device.mgt.common.DeviceManagementConstants;
|
||||
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
|
||||
import org.wso2.carbon.device.mgt.common.LifecycleStateDevice;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceLifecycleDAO;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
|
||||
import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class DeviceLifecycleDAOImpl implements DeviceLifecycleDAO {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DeviceLifecycleDAOImpl.class);
|
||||
|
||||
private Connection getConnection() throws SQLException {
|
||||
return DeviceManagementDAOFactory.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changeStatus(int enrolmentId, EnrolmentInfo.Status status, int tenantId)
|
||||
throws DeviceManagementDAOException {
|
||||
Connection conn;
|
||||
PreparedStatement stmt = null;
|
||||
Timestamp updateTime = new Timestamp(new Date().getTime());
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE ID = ? AND TENANT_ID = ?";
|
||||
stmt = conn.prepareStatement(sql);
|
||||
stmt.setString(1, status.toString());
|
||||
stmt.setTimestamp(2, updateTime);
|
||||
stmt.setInt(3, enrolmentId);
|
||||
stmt.setInt(4, tenantId);
|
||||
int updatedRowCount = stmt.executeUpdate();
|
||||
if (updatedRowCount != 1) {
|
||||
throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: " +
|
||||
updatedRowCount + " rows were updated instead of one row!!!");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error occurred while changing status of the device which has " + enrolmentId + " enrolmentId";
|
||||
log.error(msg, e);
|
||||
throw new DeviceManagementDAOException(msg, e);
|
||||
} finally {
|
||||
DeviceManagementDAOUtil.cleanupResources(stmt, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addStatus(int enrolmentId, EnrolmentInfo.Status currentStatus, EnrolmentInfo.Status previousStatus,
|
||||
int deviceId) throws DeviceManagementDAOException {
|
||||
Connection conn;
|
||||
String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
|
||||
if (changedBy == null) {
|
||||
changedBy = DeviceManagementConstants.MaintenanceProperties.MAINTENANCE_USER;
|
||||
}
|
||||
PreparedStatement stmt = null;
|
||||
Timestamp updateTime = new Timestamp(new Date().getTime());
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
String sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY, " +
|
||||
"PREVIOUS_STATUS) VALUES(?, ?, ?, ?, ?, ?)";
|
||||
stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, enrolmentId);
|
||||
stmt.setInt(2, deviceId);
|
||||
stmt.setString(3, currentStatus.toString());
|
||||
stmt.setTimestamp(4, updateTime);
|
||||
stmt.setString(5, changedBy);
|
||||
stmt.setString(6, previousStatus.toString());
|
||||
stmt.executeUpdate();
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error occurred while inserting device lifecycle";
|
||||
log.error(msg, e);
|
||||
throw new DeviceManagementDAOException(msg, e);
|
||||
} finally {
|
||||
DeviceManagementDAOUtil.cleanupResources(stmt, null);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getDeviceId(int enrolmentId) throws DeviceManagementDAOException {
|
||||
int deviceId;
|
||||
Connection conn;
|
||||
PreparedStatement stmt = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
String sql = "SELECT DEVICE_ID FROM DM_ENROLMENT WHERE ID = ?";
|
||||
stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, enrolmentId);
|
||||
rs = stmt.executeQuery();
|
||||
if (rs.next()) {
|
||||
deviceId = rs.getInt("DEVICE_ID");
|
||||
} else {
|
||||
// if there were no records corresponding to the enrolment id this is a problem. i.e. enrolment
|
||||
// id is invalid
|
||||
throw new DeviceManagementDAOException("Error occurred while getting the status of device enrolment: " +
|
||||
"no record for enrolment id " + enrolmentId);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error occurred while getiing the device if which has " + enrolmentId + " enrolmentId";
|
||||
log.error(msg, e);
|
||||
throw new DeviceManagementDAOException(msg, e);
|
||||
} finally {
|
||||
DeviceManagementDAOUtil.cleanupResources(stmt, null);
|
||||
}
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LifecycleStateDevice> getDeviceLifecycle(int id) throws DeviceManagementDAOException {
|
||||
List<LifecycleStateDevice> result = new ArrayList<>();
|
||||
Connection conn;
|
||||
PreparedStatement stmt = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
conn = this.getConnection();
|
||||
String sql = "SELECT DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY, PREVIOUS_STATUS FROM DM_DEVICE_STATUS " +
|
||||
"WHERE DEVICE_ID = ?";
|
||||
|
||||
stmt = conn.prepareStatement(sql);
|
||||
stmt.setInt(1, id);
|
||||
rs = stmt.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
LifecycleStateDevice lifecycleStateDevice = new LifecycleStateDevice(
|
||||
rs.getInt("DEVICE_ID"),
|
||||
rs.getString("STATUS"),
|
||||
rs.getString("PREVIOUS_STATUS"),
|
||||
rs.getString("CHANGED_BY"),
|
||||
new Date(rs.getTimestamp("UPDATE_TIME").getTime())
|
||||
);
|
||||
result.add(lifecycleStateDevice);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
String msg = "Error occurred while getiing the device lifecycle which has " + id + " deviceId";
|
||||
log.error(msg, e);
|
||||
throw new DeviceManagementDAOException(msg, e);
|
||||
} finally {
|
||||
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.lifeCycle;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceLifecycleState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/* this class has the methods to check whether the status is valid or status change is valid
|
||||
deviceLifecycleStates details are coming from lifecycle-states.xml file*/
|
||||
public class DeviceLifecycleStateManager {
|
||||
private Map<String, DeviceLifecycleState> deviceLifecycleStates;
|
||||
private static final Log log = LogFactory.getLog(DeviceLifecycleStateManager.class);
|
||||
|
||||
public Map<String, DeviceLifecycleState> getDeviceLifecycleStates() {
|
||||
return deviceLifecycleStates;
|
||||
}
|
||||
|
||||
public void setDeviceLifecycleStates(Map<String, DeviceLifecycleState> deviceLifecycleStates) {
|
||||
this.deviceLifecycleStates = deviceLifecycleStates;
|
||||
}
|
||||
|
||||
public void init(List<DeviceLifecycleState> states) {
|
||||
deviceLifecycleStates = new HashMap<>();
|
||||
for (DeviceLifecycleState deviceLifecycleState : states) {
|
||||
deviceLifecycleStates.put(deviceLifecycleState.getName(), deviceLifecycleState);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getNextLifecycleStates(String currentLifecycleState) {
|
||||
return deviceLifecycleStates.get(currentLifecycleState).getProceedingStates();
|
||||
}
|
||||
|
||||
public boolean isValidStateChange(String currentStatus, String nextStatus) {
|
||||
boolean validChange = false;
|
||||
List<String> proceedingstates = deviceLifecycleStates.get(currentStatus).getProceedingStates();
|
||||
for (String proceedingState : proceedingstates) {
|
||||
if (proceedingState.equals(nextStatus)) {
|
||||
validChange = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return validChange;
|
||||
}
|
||||
|
||||
public boolean isValidState(String nextStatus) {
|
||||
boolean isValid = false;
|
||||
List<String> states = new ArrayList<>(deviceLifecycleStates.keySet());
|
||||
for (String state : states) {
|
||||
if (state.equals(nextStatus)) {
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isValid;
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.service;
|
||||
|
||||
import org.wso2.carbon.device.mgt.common.*;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.DeviceStatusException;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.InvalidStatusException;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This interface manages the device lifecycle, such as status change and add status to table
|
||||
*/
|
||||
public interface DeviceStateManagementService {
|
||||
|
||||
/**
|
||||
* This method change the device status and store it in a table
|
||||
*
|
||||
* @param enrolmentInfo Enrollment Information about the device
|
||||
* @param nextStatus Next status of the device
|
||||
* @return LifecycleStateDevice which contain current and previous status
|
||||
* @throws InvalidStatusException If there is a invalid status or invalid status change
|
||||
* @throws DeviceManagementDAOException if the device cannot be found
|
||||
*/
|
||||
LifecycleStateDevice changeDeviceStatus(EnrolmentInfo enrolmentInfo, EnrolmentInfo.Status nextStatus)
|
||||
throws InvalidStatusException, DeviceStatusException;
|
||||
|
||||
/**
|
||||
* Get the lifecycle history of the relevant device
|
||||
*
|
||||
* @param device Device
|
||||
* @return List of LifecycleStateDevice
|
||||
*/
|
||||
PaginationResult getDeviceLifecycleHistory(PaginationRequest request, Device device) throws DeviceStatusException;
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. 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.service;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.context.CarbonContext;
|
||||
import org.wso2.carbon.device.mgt.common.*;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.DeviceStatusException;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.IllegalTransactionStateException;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.InvalidStatusException;
|
||||
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceLifecycleDAO;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
|
||||
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
|
||||
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
|
||||
import org.wso2.carbon.device.mgt.core.lifeCycle.DeviceLifecycleStateManager;
|
||||
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public class DeviceStateManagementServiceImpl implements DeviceStateManagementService {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DeviceStateManagementServiceImpl.class);
|
||||
private final DeviceLifecycleDAO deviceLifecycleDAO;
|
||||
private final DeviceLifecycleStateManager deviceLifecycleStateManager;
|
||||
|
||||
public DeviceStateManagementServiceImpl() {
|
||||
this.deviceLifecycleDAO = DeviceManagementDAOFactory.getDeviceLifecycleDAO();
|
||||
deviceLifecycleStateManager = DeviceManagementDataHolder.getInstance().getDeviceLifecycleStateManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifecycleStateDevice changeDeviceStatus(EnrolmentInfo enrolmentInfo, EnrolmentInfo.Status nextStatus) throws
|
||||
InvalidStatusException, DeviceStatusException {
|
||||
LifecycleStateDevice lifecycleStateDevice = new LifecycleStateDevice();
|
||||
EnrolmentInfo.Status currentStatus = enrolmentInfo.getStatus();
|
||||
if (deviceLifecycleStateManager.isValidState(nextStatus.toString())){
|
||||
if (deviceLifecycleStateManager.isValidStateChange(currentStatus.toString(), nextStatus.toString())) {
|
||||
lifecycleStateDevice.setCurrentStatus(nextStatus.toString());
|
||||
lifecycleStateDevice.setPreviousStatus(currentStatus.toString());
|
||||
} else {
|
||||
String msg ="'" + currentStatus + "' to '" + nextStatus + "' is not a valid Status change. Enter " +
|
||||
"valid Status";
|
||||
log.error(msg);
|
||||
throw new InvalidStatusException(msg);
|
||||
}
|
||||
} else {
|
||||
String msg = "'" + nextStatus +"' is not a valid Status. Check the Status";
|
||||
log.error(msg);
|
||||
throw new InvalidStatusException(msg);
|
||||
}
|
||||
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
|
||||
int enrolmentId = enrolmentInfo.getId();
|
||||
try {
|
||||
DeviceManagementDAOFactory.beginTransaction();
|
||||
int deviceId = deviceLifecycleDAO.getDeviceId(enrolmentId);
|
||||
deviceLifecycleDAO.changeStatus(enrolmentId, EnrolmentInfo.Status.valueOf(
|
||||
lifecycleStateDevice.getCurrentStatus()), tenantId);
|
||||
deviceLifecycleDAO.addStatus(enrolmentId,
|
||||
EnrolmentInfo.Status.valueOf(lifecycleStateDevice.getCurrentStatus()),
|
||||
EnrolmentInfo.Status.valueOf(lifecycleStateDevice.getPreviousStatus()), deviceId);
|
||||
DeviceManagementDAOFactory.commitTransaction();
|
||||
return lifecycleStateDevice;
|
||||
} catch (DeviceManagementDAOException e) {
|
||||
DeviceManagementDAOFactory.rollbackTransaction();
|
||||
String msg = "Error occurred in updating status or storing device status";
|
||||
log.error(msg, e);
|
||||
throw new DeviceStatusException(msg, e);
|
||||
} catch (IllegalTransactionStateException e) {
|
||||
String msg = "Error occurred while updating and storing(Transaction Error) device status";
|
||||
log.error(msg, e);
|
||||
throw new DeviceStatusException(msg, e);
|
||||
} catch (TransactionManagementException e) {
|
||||
String msg = "Error occurred in DeviceManagementDAOFactory";
|
||||
log.error(msg, e);
|
||||
throw new InvalidStatusException(msg, e);
|
||||
} finally {
|
||||
DeviceManagementDAOFactory.closeConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PaginationResult getDeviceLifecycleHistory(PaginationRequest request, Device device) throws DeviceStatusException {
|
||||
int id = device.getId();
|
||||
PaginationResult paginationResult = new PaginationResult();
|
||||
try {
|
||||
DeviceManagementDAOFactory.openConnection();
|
||||
List<LifecycleStateDevice> listLifecycle = deviceLifecycleDAO.getDeviceLifecycle(id);
|
||||
paginationResult.setData(listLifecycle);
|
||||
return paginationResult;
|
||||
} catch (DeviceManagementDAOException e) {
|
||||
String msg = "Error occurred while getting lifrcycle history";
|
||||
log.error(msg, e);
|
||||
throw new DeviceStatusException(msg, e);
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
DeviceManagementDAOFactory.closeConnection();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<!--
|
||||
~ Copyright (c) 2023, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. 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.
|
||||
-->
|
||||
|
||||
<LifecycleManagementConfiguration>
|
||||
<LifecycleStates>
|
||||
<LifecycleState name="CREATED">
|
||||
<ProceedingStates>
|
||||
<State>ACTIVE</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="ACTIVE">
|
||||
<ProceedingStates>
|
||||
<State>UNREACHABLE</State>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="UNREACHABLE">
|
||||
<ProceedingStates>
|
||||
<State>INACTIVE</State>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="INACTIVE">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="UNCLAIMED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="SUSPENDED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="BLOCKED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="DEFECTIVE">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="CONFIGURED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="RETURNED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="READY_TO_CONNECT">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="DISENROLLMENT_REQUESTED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="ASSIGNED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="RETURN_PENDING">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="WARRANTY_PENDING">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="WARRANTY_SENT">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="WARRANTY_REPLACED">
|
||||
<ProceedingStates>
|
||||
<State>REMOVED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
<LifecycleState name="REMOVED">
|
||||
<ProceedingStates>
|
||||
<State>ACTIVE</State>
|
||||
<State>CREATED</State>
|
||||
</ProceedingStates>
|
||||
</LifecycleState>
|
||||
</LifecycleStates>
|
||||
</LifecycleManagementConfiguration>
|
Loading…
Reference in new issue