Change the device status and see the device status lifecycle history (#58)

## Purpose
* Fixes https://roadmap.entgra.net/issues/9266

## Feature
* Change device's status and view device's status life-cycle history

## Approach
* Created a API for change status and store changed status in to database.
* Created another API to view the device's life-cycle status history.
* lifecycle of the status is as shown in the diagram.
* lifecycle Status are taken from a xml file.

Co-authored-by: prathabanKavin <kavinprathaban025@gmail.com>
Co-authored-by: Pahansith Gunathilake <pahansith@entgra.io>
Reviewed-on: community/device-mgt-core#58
Co-authored-by: Kavin Prathaban <kavin@entgra.io>
Co-committed-by: Kavin Prathaban <kavin@entgra.io>
improvement/fix-task-deletion
Kavin Prathaban 2 years ago committed by Pahansith Gunathilake
parent 2e2e9abc90
commit 7f327bac08

@ -0,0 +1,47 @@
/*
* 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 +
'}';
}
}

@ -34,6 +34,7 @@
*/
package org.wso2.carbon.device.mgt.jaxrs.service.api.admin;
import io.entgra.application.mgt.common.dto.ApplicationDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@ -49,6 +50,8 @@ import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.util.Constants;
@ -119,6 +122,13 @@ import java.util.List;
roles = {"Internal/devicemgt-admin"},
permissions = {"/device-mgt/admin/devices/usage/view"}
),
@Scope(
name = "Change device status.",
description = "Change device status.",
key = "perm:admin:devices:change-status",
roles = {"Internal/devicemgt-admin"},
permissions = {"/device-mgt/admin/devices/change-status"}
),
}
)
public interface DeviceManagementAdminService {
@ -505,4 +515,140 @@ public interface DeviceManagementAdminService {
@QueryParam("endDate")
Timestamp endDate);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/status")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
consumes = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Update and post device's State",
notes = "Use this API to change the state of the device",
tags = "Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:admin:devices:change-status")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 201,
message = "OK. \n Successfully added a lifecycle state.",
response = ApplicationDTO.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Lifecycle State changing request contains unacceptable or vulnerable data"),
@ApiResponse(
code = 403,
message = "Don't have permission to change the lifecycle state of a lifecycle state."),
@ApiResponse(
code = 404,
message = "NOT FOUND. \n Error occurred while adding new lifecycle state.",
response = io.entgra.application.mgt.common.ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred adding a lifecycle state.",
response = io.entgra.application.mgt.common.ErrorResponse.class)
})
Response changeDeviceStatus(
@ApiParam(
name = "deviceId",
value = "The device identifier of the device.",
required = true)
@QueryParam("deviceId")
@Size(max = 45)
String deviceId,
@ApiParam(
name = "nextStatus",
value = "The device's next state.",
required = true)
@QueryParam("nextStatus")
EnrolmentInfo.Status nextStatus);
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{type}/{deviceId}/lifecycle")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Get Device's lifecycle history",
notes = "Get the lifecycle history of the device, since the enrolement",
tags = "Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:admin:devices:view")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully fetched the status history of matching devices.",
response = List.class,
responseHeaders = {
@ResponseHeader(
name = "Content-Type",
description = "The content type of the body"),
@ResponseHeader(
name = "ETag",
description = "Entity Tag of the response resource.\n" +
"Used by caches, or in conditional requests."),
@ResponseHeader(
name = "Last-Modified",
description = "Date and time the resource was last modified.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. Empty body because the client already has the latest version" +
" of the requested resource.\n"),
@ApiResponse(
code = 400,
message = "Bad Request. \n Invalid request or validation error.",
response = ErrorResponse.class),
@ApiResponse(
code = 404,
message = "Not Found. \n A device with the specified device type and id was not found.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n " +
"Server error occurred while retrieving the device details.",
response = ErrorResponse.class)
})
Response getDeviceLifecycle(
@ApiParam(
name = "DeviceId",
value = "Device ID.",
required = true)
@PathParam("deviceId")
@Size(max = 45)
String id,
@ApiParam(
name = "type",
value = "The device type, such as ios, android, or windows.",
required = true)
@PathParam("type")
@Size(max = 45)
String type,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
required = false,
defaultValue = "0")
@QueryParam("offset")
int offset,
@ApiParam(
name = "limit",
value = "Provide how many device details you require from the starting pagination index/offset.",
required = false,
defaultValue = "5")
@QueryParam("limit")
int limit) throws DeviceManagementException;
}

@ -40,9 +40,12 @@ import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.LifecycleStateDevice;
import org.wso2.carbon.device.mgt.common.MDMAppConstants;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
@ -50,14 +53,18 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration
import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceStatusException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidStatusException;
import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.beans.LifecycleStateDeviceList;
import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.DeviceManagementAdminService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.Constants;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
@ -283,4 +290,102 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
/**
* This change the device status
*
* @param deviceId Id of the device
* @param nextStatus next status of the device
* @return response
*/
@POST
@Path("/status")
public Response changeDeviceStatus(
@QueryParam("deviceId") String deviceId,
@QueryParam("nextStatus") EnrolmentInfo.Status nextStatus) {
try {
if (nextStatus == null) {
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage("Next status is required")
.build()).build();
}
RequestValidationUtil.validateDeviceIdentifier(Constants.ANY, deviceId);
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService();
Device device = dms.getDevice(deviceId, false);
if (device == null) {
String message = "Device does not exist with id '" + deviceId + "'";
log.error(message);
return Response.status(Response.Status.NOT_FOUND).entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(message).build()).build();
}
LifecycleStateDevice updatedInfo = DeviceMgtAPIUtils.getDeviceStateManagementService()
.changeDeviceStatus(device.getEnrolmentInfo(), nextStatus);
return Response.status(Response.Status.OK).entity(updatedInfo).build();
} catch (InvalidStatusException e) {
String msg = "Error occured while changing status: Invalid status or invalid status change";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (DeviceManagementException e) {
String msg = "Error occurred while getting the device '" + deviceId + "'";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (DeviceStatusException e) {
String msg = "Error occurred while getting device Status";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
}
/**
* Get the devicelifecycle history
*
* @param type Device type
* @param deviceId Device id
* @param offset is starting number of the record which we want tot get
* @param limit is the number of records we want starting from offset
* @return lifecycle history
*/
@Override
@GET
@Path("/{type}/{deviceId}/lifecycle")
public Response getDeviceLifecycle(
@PathParam("type") String type,
@PathParam("deviceId") String deviceId,
@QueryParam("offset") int offset,
@QueryParam("limit") int limit) {
try {
RequestValidationUtil.validatePaginationParameters(offset, limit);
PaginationRequest request = new PaginationRequest(offset, limit);
RequestValidationUtil.validateDeviceIdentifier(type, deviceId);
DeviceManagementProviderService deviceManagementProviderService =
DeviceMgtAPIUtils.getDeviceManagementService();
DeviceIdentifier deviceIdentifier = new DeviceIdentifier(deviceId, type);
Device device = deviceManagementProviderService.getDevice(deviceIdentifier, false);
if (device == null) {
return Response.status(Response.Status.NOT_FOUND).build();
}
LifecycleStateDeviceList states = new LifecycleStateDeviceList();
PaginationResult result = DeviceMgtAPIUtils.getDeviceStateManagementService()
.getDeviceLifecycleHistory(request, device);
states.setLifecycleStates((List<LifecycleStateDevice>) result.getData());
states.setCount(result.getRecordsTotal());
return Response.status(Response.Status.OK).entity(states).build();
} catch (DeviceManagementException e) {
String msg = "Error occurred while getting the device '" + deviceId + "'";
log.error(msg);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} catch (DeviceStatusException e) {
String msg = "Error occurred while getting device Status";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
}
}

@ -58,6 +58,7 @@ public class Constants {
"core.polcy.AndroidPolicyPayloadValidator";
public static final String IOS = "ios";
public static final String WINDOWS = "windows";
public static final String ANY = "any";
public final class OperationStatus {

@ -86,6 +86,7 @@ import org.wso2.carbon.device.mgt.core.permission.mgt.PermissionUtils;
import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider;
import org.wso2.carbon.device.mgt.core.search.mgt.SearchManagerService;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.DeviceStateManagementService;
import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService;
import org.wso2.carbon.device.mgt.core.traccar.api.service.DeviceAPIClientService;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceTypeVersionWrapper;
@ -271,6 +272,18 @@ public class DeviceMgtAPIUtils {
}
}
public static DeviceStateManagementService getDeviceStateManagementService() {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
DeviceStateManagementService deviceStateManagementService =
(DeviceStateManagementService) ctx.getOSGiService(DeviceStateManagementService.class, null);
if (deviceStateManagementService == null) {
String msg = "DeviceStateManagementService service has not initialized.";
log.error(msg);
throw new IllegalStateException(msg);
}
return deviceStateManagementService;
}
public static DeviceManagementProviderService getDeviceManagementService() {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
DeviceManagementProviderService deviceManagementProviderService =

@ -28,6 +28,7 @@ public final class DeviceManagementConstants {
public static final String SECURE_VAULT_NS = "http://org.wso2.securevault/configuration";
public static final String DEVICE_CONFIG_XML_NAME = "cdm-config.xml";
public static final String UI_CONFIG_XML_NAME = "mdm-ui-config.xml";
public static final String DEVICE_LIFECYCLE_STATES_XML_NAME = "lifecycle-states.xml";
}
public static final class SecureValueProperties {

@ -0,0 +1,96 @@
/*
* 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 +
'}';
}
}

@ -0,0 +1,80 @@
/*
* 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 +
'}';
}
}

@ -0,0 +1,48 @@
/*
* 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);
}
}

@ -0,0 +1,46 @@
/*
* 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);
}
}

@ -40,6 +40,7 @@ public class DeviceConfigurationManager {
private static final String DEVICE_MGT_CONFIG_PATH =
CarbonUtils.getCarbonConfigDirPath() + File.separator +
DeviceManagementConstants.DataSourceProperties.DEVICE_CONFIG_XML_NAME;
private static final String DEVICE_MGT_CONFIG_SCHEMA_PATH = "resources/config/schema/device-mgt-config-schema.xsd";
public static DeviceConfigurationManager getInstance() {

@ -0,0 +1,46 @@
/*
* 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;
}
}

@ -0,0 +1,89 @@
/*
* 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;
}
}

@ -0,0 +1,72 @@
/*
* 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;
}

@ -125,6 +125,10 @@ public class DeviceManagementDAOFactory {
return new EnrollmentDAOImpl();
}
public static DeviceLifecycleDAO getDeviceLifecycleDAO() {
return new DeviceLifecycleDAOImpl();
}
public static TrackerDAO getTrackerDAO() {
if (databaseEngine != null) {
switch (databaseEngine) {

@ -0,0 +1,174 @@
/*
* 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;
}
}

@ -348,7 +348,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
DeviceManagementDAOUtil.cleanupResources(stmt, null);
// if there was no record for the enrolment or the previous status is not the same as the current status
// we'll add a record
if (previousStatus == null || previousStatus != status){
if (previousStatus == null || previousStatus != status) {
if (deviceId == -1) {
// we need the device id in order to add a new record, therefore we get it from the enrolment table
sql = "SELECT DEVICE_ID FROM DM_ENROLMENT WHERE ID = ?";
@ -364,8 +364,16 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
}
DeviceManagementDAOUtil.cleanupResources(stmt, null);
}
sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY";
if (previousStatus != null) {
sql += ", PREVIOUS_STATUS";
}
sql += ") VALUES(?, ?, ?, ?, ?";
if (previousStatus != null) {
sql += ", ?";
}
sql += ")";
sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)";
stmt = conn.prepareStatement(sql);
Timestamp updateTime = new Timestamp(new Date().getTime());
stmt.setInt(1, enrolmentId);
@ -373,9 +381,10 @@ public class EnrollmentDAOImpl implements EnrollmentDAO {
stmt.setString(3, status.toString());
stmt.setTimestamp(4, updateTime);
stmt.setString(5, changedBy);
stmt.execute();
} else {
// no need to update status since the last recorded status is the same as the current status
if (previousStatus != null) {
stmt.setString(6, previousStatus.toString());
}
stmt.executeUpdate();
}
} catch (SQLException e) {
throw new DeviceManagementDAOException("Error occurred while setting the status of device", e);

@ -34,6 +34,7 @@ import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManag
import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.dto.DeviceTypeServiceIdentifier;
import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager;
import org.wso2.carbon.device.mgt.core.lifeCycle.DeviceLifecycleStateManager;
import org.wso2.carbon.device.mgt.core.operation.timeout.task.OperationTimeoutTaskManagerService;
import org.wso2.carbon.device.mgt.core.privacy.PrivacyComplianceProvider;
import org.wso2.carbon.device.mgt.core.push.notification.mgt.PushNotificationProviderRepository;
@ -86,6 +87,7 @@ public class DeviceManagementDataHolder {
private OperationTimeoutTaskManagerService operationTimeoutTaskManagerService;
private DeviceAPIClientService deviceAPIClientService;
private DeviceLifecycleStateManager deviceLifecycleStateManager;
private final Map<DeviceType, DeviceStatusTaskPluginConfig> deviceStatusTaskPluginConfigs = Collections.synchronizedMap(
new HashMap<>());
@ -359,4 +361,12 @@ public class DeviceManagementDataHolder {
public void setDeviceAPIClientService(DeviceAPIClientService deviceAPIClientService) {
this.deviceAPIClientService = deviceAPIClientService;
}
public DeviceLifecycleStateManager getDeviceLifecycleStateManager() {
return deviceLifecycleStateManager;
}
public void setDeviceLifecycleStateManager(DeviceLifecycleStateManager deviceLifecycleStateManager) {
this.deviceLifecycleStateManager = deviceLifecycleStateManager;
}
}

@ -25,6 +25,7 @@ import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.core.ServerStartupObserver;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceLifecycleState;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService;
import org.wso2.carbon.device.mgt.common.event.config.EventConfigurationProviderService;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
@ -48,6 +49,7 @@ import org.wso2.carbon.device.mgt.core.authorization.DeviceAccessAuthorizationSe
import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;
import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig;
import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig;
import org.wso2.carbon.device.mgt.core.config.lifecycleState.DeviceLifecycleConfigManager;
import org.wso2.carbon.device.mgt.core.config.tenant.PlatformConfigurationManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.config.ui.UIConfigurationManager;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
@ -58,6 +60,7 @@ import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManag
import org.wso2.carbon.device.mgt.core.device.details.mgt.impl.DeviceInformationManagerImpl;
import org.wso2.carbon.device.mgt.core.event.config.EventConfigurationProviderServiceImpl;
import org.wso2.carbon.device.mgt.core.geo.service.GeoLocationProviderServiceImpl;
import org.wso2.carbon.device.mgt.core.lifeCycle.DeviceLifecycleStateManager;
import org.wso2.carbon.device.mgt.core.metadata.mgt.MetadataManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.metadata.mgt.dao.MetadataManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.notification.mgt.NotificationManagementServiceImpl;
@ -76,6 +79,8 @@ import org.wso2.carbon.device.mgt.core.search.mgt.SearchManagerService;
import org.wso2.carbon.device.mgt.core.search.mgt.impl.SearchManagerServiceImpl;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderServiceImpl;
import org.wso2.carbon.device.mgt.core.service.DeviceStateManagementService;
import org.wso2.carbon.device.mgt.core.service.DeviceStateManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService;
import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderServiceImpl;
import org.wso2.carbon.device.mgt.core.task.DeviceTaskManagerService;
@ -274,6 +279,19 @@ public class DeviceManagementServiceComponent {
if (log.isDebugEnabled()) {
log.debug("Device management core bundle has been successfully initialized");
}
/* Initializing DeviceLifecycleState Configuration */
List<DeviceLifecycleState> deviceLifecycleStates = DeviceLifecycleConfigManager.getInstance().
getDeviceLifecycleConfig().getDeviceLifecycleStates();
DeviceLifecycleStateManager deviceLifecycleStateManager = new DeviceLifecycleStateManager();
deviceLifecycleStateManager.init(deviceLifecycleStates);
DeviceManagementDataHolder.getInstance().setDeviceLifecycleStateManager(deviceLifecycleStateManager);
componentContext.getBundleContext().registerService(DeviceLifecycleStateManager.class.getName(),
deviceLifecycleStateManager, null);
DeviceStateManagementService deviceStateManagementService = new DeviceStateManagementServiceImpl();
componentContext.getBundleContext().registerService(DeviceStateManagementService.class.getName(),
deviceStateManagementService, null);
} catch (Throwable e) {
log.error("Error occurred while initializing device management core bundle", e);
}

@ -0,0 +1,79 @@
/*
* 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;
}
}

@ -2123,10 +2123,11 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
log.debug("get status history of device: " + device.getDeviceIdentifier());
}
try {
DeviceManagementDAOFactory.openConnection();
int tenantId = this.getTenantId();
return deviceStatusDAO.getStatus(device.getId(), tenantId, fromDate, toDate, billingStatus);
} catch (DeviceManagementDAOException e) {
DeviceManagementDAOFactory.rollbackTransaction();
// DeviceManagementDAOFactory.rollbackTransaction();
String msg = "Error occurred while retrieving status history";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
@ -2134,6 +2135,8 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
String msg = "Error occurred in retrieving status history for device :" + device.getDeviceIdentifier();
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
}

@ -0,0 +1,53 @@
/*
* 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;
}

@ -0,0 +1,120 @@
/*
* 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();
}
}
}

@ -106,12 +106,14 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS (
STATUS VARCHAR(50) DEFAULT NULL,
UPDATE_TIME TIMESTAMP DEFAULT NULL,
CHANGED_BY VARCHAR(255) NOT NULL,
PREVIOUS_STATUS VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (ID),
CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES
DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES
DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION
);
CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING (
ID INTEGER AUTO_INCREMENT NOT NULL,
ENROLMENT_ID INTEGER NOT NULL,

@ -0,0 +1,116 @@
<?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>

@ -124,6 +124,7 @@
<Scope>perm:users:send-invitation</Scope>
<Scope>perm:admin-users:view</Scope>
<Scope>perm:admin:devices:update-enrollment</Scope>
<Scope>perm:admin:devices:change-status</Scope>
<Scope>perm:groups:devices</Scope>
<Scope>perm:groups:update</Scope>
<Scope>perm:groups:add</Scope>

@ -115,12 +115,14 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS (
STATUS VARCHAR(50) DEFAULT NULL,
UPDATE_TIME TIMESTAMP DEFAULT NULL,
CHANGED_BY VARCHAR(255) NOT NULL,
PREVIOUS_STATUS VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (ID),
CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES
DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES
DM_ENROLMENT (ID) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING (
ID INTEGER AUTO_INCREMENT NOT NULL,
ENROLMENT_ID INTEGER NOT NULL,

@ -129,6 +129,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS (
STATUS VARCHAR(50) DEFAULT NULL,
UPDATE_TIME TIMESTAMP DEFAULT NULL,
CHANGED_BY VARCHAR(255) NOT NULL,
PREVIOUS_STATUS VARCHAR(50) DEFAULT NULL,
PRIMARY KEY (ID),
CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES
DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION,

Loading…
Cancel
Save