Merge branch 'application-mgt-new' into 'application-mgt-new'

Adding app release creating API

See merge request entgra/carbon-device-mgt!26
feature/appm-store/pbac
Rephy Quaphy 6 years ago
commit c8daa00ad2

@ -1,18 +1,17 @@
image: maven:latest
image: shinyay/docker-mvn-jdk8:3.5.0
variables:
MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode"
MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode --errors --show-version"
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
cache:
paths:
- .m2/repository/
- target/
build:
stage: build
script:
- mvn $MAVEN_CLI_OPTS compile
- mvn $MAVEN_CLI_OPTS clean install -Dmaven.test.skip=true
test:
stage: test

@ -22,13 +22,13 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.apimgt.annotations</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Management Annotations</name>
<description>WSO2 Carbon - API Management Custom Annotation Module</description>

@ -21,12 +21,12 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<artifactId>org.wso2.carbon.apimgt.application.extension.api</artifactId>
<packaging>war</packaging>
<name>WSO2 Carbon - API Application Management API</name>

@ -22,12 +22,12 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<artifactId>org.wso2.carbon.apimgt.application.extension</artifactId>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Application Management</name>

@ -21,13 +21,13 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.apimgt.handlers</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Security Handler Component</name>
<description>WSO2 Carbon - API Management Security Handler Module</description>

@ -13,13 +13,13 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.apimgt.integration.client</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Management Integration Client</name>
<description>WSO2 Carbon - API Management Integration Client</description>

@ -13,13 +13,13 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.apimgt.integration.generated.client</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Management Integration Generated Client</name>
<description>WSO2 Carbon - API Management Integration Client</description>

@ -22,13 +22,13 @@
<parent>
<artifactId>apimgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.apimgt.webapp.publisher</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - API Management Webapp Publisher</name>
<description>WSO2 Carbon - API Management Webapp Publisher</description>

@ -22,13 +22,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>apimgt-extensions</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>WSO2 Carbon - API Management Extensions Component</name>
<url>http://wso2.org</url>

@ -5,12 +5,12 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.addons</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<name>WSO2 Carbon - Application Management Add-Ons</name>
<description>WSO2 Carbon - Application Management Add-Ons</description>
<url>http://wso2.org</url>

@ -24,11 +24,11 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<artifactId>org.wso2.carbon.device.application.mgt.authhandler</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Application Management Authentication Handler API</name>
<description>Proxy Service for Authentication Handling in WSO2 App Manager.</description>

@ -21,13 +21,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.common</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Application Management Common</name>
<description>WSO2 Carbon - Application Management Common</description>

@ -45,7 +45,7 @@ public class Application {
@ApiModelProperty(name = "type",
value = "Type of the application",
required = true,
example = "Mobile, Web, Web Clip etc")
example = "ENTERPRISE, PUBLIC, WEB, WEB_CLIP etc")
private String type;
@ApiModelProperty(name = "subType",

@ -93,7 +93,7 @@ public interface ApplicationManager {
String getUuidOfLatestRelease(int appId) throws ApplicationManagementException;
/**
* To get Application with the given Id.
* To get the Application for given Id.
*
* @param id id of the Application
* @param state state of the Application
@ -102,6 +102,16 @@ public interface ApplicationManager {
*/
Application getApplicationById(int id, String state) throws ApplicationManagementException;
/**
* To get the Application for given application relase UUID.
*
* @param uuid UUID of the Application
* @param state state of the Application
* @return the Application identified by the ID
* @throws ApplicationManagementException Application Management Exception.
*/
Application getApplicationByUuid(String uuid, String state) throws ApplicationManagementException;
/**
* To get an application associated with the release.
*
@ -146,11 +156,9 @@ public interface ApplicationManager {
* @param releaseUuid UUID of the Application Release.
* @param state Lifecycle state to change the app
* @param checkExist whether it is needed to check if the app and release already exist in the database
* @param handleDBConnections Whether it is necessary to open connections
* @throws ApplicationManagementException Application Management Exception.
*/
void changeLifecycleState(int applicationId, String releaseUuid, LifecycleState state, Boolean checkExist,
Boolean handleDBConnections)
void changeLifecycleState(int applicationId, String releaseUuid, LifecycleState state, Boolean checkExist)
throws ApplicationManagementException;
/**

@ -21,13 +21,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.core</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Application Management Core</name>
<description>WSO2 Carbon - Application Management Core</description>

@ -96,7 +96,7 @@ public interface ApplicationDAO {
ApplicationManagementDAOException;
/**
* To get the application with the given uuid
* To get the application with the given id
*
* @param applicationId Id of the application to be retrieved.
* @param tenantId ID of the tenant.
@ -105,6 +105,16 @@ public interface ApplicationDAO {
*/
Application getApplicationById(int applicationId, int tenantId) throws ApplicationManagementDAOException;
/**
* To get the application with the given uuid
*
* @param releaseUuid UUID of the application release.
* @param tenantId ID of the tenant.
* @return the application
* @throws ApplicationManagementDAOException Application Management DAO Exception.
*/
Application getApplicationByUUID(String releaseUuid, int tenantId) throws ApplicationManagementDAOException;
/**
* To get the application with the given uuid
*

@ -138,6 +138,26 @@ public interface ApplicationReleaseDAO {
*/
boolean verifyReleaseExistence(int appId, String uuid, int tenantId) throws ApplicationManagementDAOException;
/**
* To verify whether application release exist or not for the given app release version.
*
* @param appId ID of the application.
* @param hashVal Hash value of the application release.
* @param tenantId Tenant Id
* @throws ApplicationManagementDAOException Application Management DAO Exception.
*/
boolean verifyReleaseExistenceByHash(int appId, String hashVal, int tenantId)
throws ApplicationManagementDAOException;
/**
* To verify whether application release exist or not for the given app release version.
*
* @param appId ID of the application.
* @param tenantId Tenant Id
* @throws ApplicationManagementDAOException Application Management DAO Exception.
*/
String getPackageName(int appId, int tenantId) throws ApplicationManagementDAOException;
/**
* To verify whether application release exist or not for given application release uuid.
*

@ -390,8 +390,55 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
}
@Override
public Application getApplicationById(int applicationId, int tenantId) throws
public Application getApplicationByUUID(String releaseUuid, int tenantId) throws
ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Getting application with the release UUID: " + releaseUuid + " from the database");
}
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = this.getDBConnection();
String sql =
"SELECT AP_APP.ID AS APP_ID, AP_APP.NAME AS APP_NAME, AP_APP.TYPE AS APP_TYPE, AP_APP.APP_CATEGORY "
+ "AS APP_CATEGORY, AP_APP.SUB_TYPE AS SUB_TYPE, AP_APP.CURRENCY AS CURRENCY, "
+ "AP_APP.RESTRICTED AS RESTRICTED, AP_APP.DEVICE_TYPE_ID AS DEVICE_TYPE_ID, "
+ "AP_APP_TAG.TAG AS APP_TAG, AP_UNRESTRICTED_ROLE.ROLE AS "
+ "ROLE FROM ((AP_APP LEFT JOIN AP_APP_TAG ON AP_APP.ID = AP_APP_TAG.AP_APP_ID) "
+ "LEFT JOIN AP_UNRESTRICTED_ROLE ON AP_APP.ID = AP_UNRESTRICTED_ROLE.AP_APP_ID) "
+ "WHERE AP_APP.ID = (SELECT AP_APP_ID FROM AP_APP_RELEASE WHERE UUID =? ) AND "
+ "AP_APP.TENANT_ID = ? AND AP_APP.STATUS != ?;";
stmt = conn.prepareStatement(sql);
stmt.setString(1, releaseUuid);
stmt.setInt(2, tenantId);
stmt.setString(3, AppLifecycleState.REMOVED.toString());
rs = stmt.executeQuery();
if (log.isDebugEnabled()) {
log.debug("Successfully retrieved basic details of the application for the application release UUID: "
+ releaseUuid);
}
return Util.loadApplication(rs);
} catch (SQLException e) {
throw new ApplicationManagementDAOException(
"Error occurred while getting application details with app release uuid " + releaseUuid +
" While executing query ", e);
} catch (JSONException e) {
throw new ApplicationManagementDAOException("Error occurred while parsing JSON", e);
} catch (DBConnectionException e) {
throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e);
} finally {
Util.cleanupResources(stmt, rs);
}
}
@Override
public Application getApplicationById(int applicationId, int tenantId) throws
ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Getting application with the id (" + applicationId + ") from the database");
}
@ -417,7 +464,7 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic
if (log.isDebugEnabled()) {
log.debug("Successfully retrieved basic details of the application with the id "
+ applicationId);
+ applicationId);
}
return Util.loadApplication(rs);

@ -431,6 +431,80 @@ public class GenericApplicationReleaseDAOImpl extends AbstractDAOImpl implements
}
}
@Override
public boolean verifyReleaseExistenceByHash(int appId, String hashVal, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Verifying application release existence by application id:" + appId
+ " and application hash value: " + hashVal);
}
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = this.getDBConnection();
String sql =
"SELECT AR.ID AS RELEASE_ID FROM AP_APP_RELEASE AS AR WHERE AR.AP_APP_ID = ? AND "
+ "AR.APP_HASH_VALUE = ? AND AR.TENANT_ID = ?;";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, appId);
stmt.setString(2, hashVal);
stmt.setInt(3, tenantId);
rs = stmt.executeQuery();
if (log.isDebugEnabled()) {
log.debug("Successfully retrieved basic details of the application release with the application ID "
+ appId + " Application release hash value: " + hashVal);
}
return rs.next();
} catch (SQLException e) {
throw new ApplicationManagementDAOException(
"Error occurred while getting application release details with app ID: " + appId
+ " App release hash value: " + hashVal + " While executing query ", e);
} catch (DBConnectionException e) {
throw new ApplicationManagementDAOException("Error occurred while obtaining the DB connection.", e);
} finally {
Util.cleanupResources(stmt, rs);
}
}
@Override
public String getPackageName(int appId, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Getting package name of the application release by application id:" + appId);
}
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = this.getDBConnection();
String sql = "SELECT AR.PACKAGE_NAME AS PACKAGE_NAME FROM AP_APP_RELEASE AS AR WHERE AR.AP_APP_ID = ? "
+ "AND AR.TENANT_ID = ? LIMIT 1;";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, appId);
stmt.setInt(2, tenantId);
rs = stmt.executeQuery();
if (log.isDebugEnabled()) {
log.debug("Successfully retrieved package name of the application release with the application ID "
+ appId);
}
if (rs.next()){
return rs.getString("PACKAGE_NAME");
}
return null;
} catch (SQLException e) {
throw new ApplicationManagementDAOException(
"Error occurred while getting package name of the application release with app ID: " + appId, e);
} catch (DBConnectionException e) {
throw new ApplicationManagementDAOException(
"Error occurred while obtaining the DB connection to get application release package name.", e);
} finally {
Util.cleanupResources(stmt, rs);
}
}
@Override
public boolean verifyReleaseExistence(int appId, String uuid, int tenantId) throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {

@ -35,8 +35,10 @@ import org.wso2.carbon.device.application.mgt.common.Tag;
import org.wso2.carbon.device.application.mgt.common.UnrestrictedRole;
import org.wso2.carbon.device.application.mgt.common.User;
import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException;
import org.wso2.carbon.device.application.mgt.common.exception.DBConnectionException;
import org.wso2.carbon.device.application.mgt.common.exception.RequestValidatingException;
import org.wso2.carbon.device.application.mgt.common.exception.ResourceManagementException;
import org.wso2.carbon.device.application.mgt.common.exception.TransactionManagementException;
import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager;
import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager;
import org.wso2.carbon.device.application.mgt.core.dao.ApplicationDAO;
@ -46,6 +48,7 @@ import org.wso2.carbon.device.application.mgt.core.dao.VisibilityDAO;
import org.wso2.carbon.device.application.mgt.core.dao.common.ApplicationManagementDAOFactory;
import org.wso2.carbon.device.application.mgt.core.dao.common.Util;
import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException;
import org.wso2.carbon.device.application.mgt.core.exception.BadRequestException;
import org.wso2.carbon.device.application.mgt.core.exception.ForbiddenException;
import org.wso2.carbon.device.application.mgt.core.exception.LifeCycleManagementDAOException;
import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException;
@ -108,9 +111,8 @@ public class ApplicationManagerImpl implements ApplicationManager {
List<ApplicationRelease> applicationReleases = new ArrayList<>();
try {
ConnectionManagerUtil.beginDBTransaction();
MAMDeviceConnectorImpl mamDeviceConnector = new MAMDeviceConnectorImpl();
// Getting the device type details to get device type ID for internal mappings
deviceType = mamDeviceConnector.getDeviceManagementService().getDeviceType(application.getDeviceType());
deviceType = Util.getDeviceManagementService().getDeviceType(application.getDeviceType());
if (deviceType == null) {
log.error("Device type is not matched with application type");
@ -165,7 +167,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
LifecycleState lifecycleState = new LifecycleState();
lifecycleState.setCurrentState(AppLifecycleState.CREATED.toString());
lifecycleState.setPreviousState(AppLifecycleState.CREATED.toString());
changeLifecycleState(appId, applicationRelease.getUuid(), lifecycleState, false, false);
changeLifecycleState(appId, applicationRelease.getUuid(), lifecycleState, false);
applicationRelease.setLifecycleState(lifecycleState);
applicationReleases.add(applicationRelease);
@ -245,15 +247,42 @@ public class ApplicationManagerImpl implements ApplicationManager {
log.debug("Application release request is received for the application " + application.toString());
}
try {
ConnectionManagerUtil.getDBConnection();
ConnectionManagerUtil.openDBConnection();
Application existingApplication = this.applicationDAO.getApplicationById(applicationId, tenantId);
if (existingApplication == null){
throw new NotFoundException(
"Couldn't found application for the application Id: " + applicationId);
}
if (this.applicationReleaseDAO
.verifyReleaseExistenceByHash(applicationId, applicationRelease.getAppHashValue(), tenantId)) {
throw new BadRequestException("Application release exists for the application Id: " + applicationId
+ " and uploaded binary file");
}
String packageName = this.applicationReleaseDAO.getPackageName(applicationId, tenantId);
if (packageName != null && !packageName.equals(applicationRelease.getPackageName())) {
throw new BadRequestException(
"Package name in the payload is different from the existing package name of other application releases.");
}
ConnectionManagerUtil.beginDBTransaction();
applicationRelease = this.applicationReleaseDAO
.createRelease(applicationRelease, application.getId(), tenantId);
LifecycleState lifecycleState = new LifecycleState();
lifecycleState.setCurrentState(AppLifecycleState.CREATED.toString());
lifecycleState.setPreviousState(AppLifecycleState.CREATED.toString());
changeLifecycleState(application.getId(), applicationRelease.getUuid(), lifecycleState, true,
false);
changeLifecycleState(application.getId(), applicationRelease.getUuid(), lifecycleState, false);
ConnectionManagerUtil.commitDBTransaction();
return applicationRelease;
} catch (TransactionManagementException e) {
ConnectionManagerUtil.rollbackDBTransaction();
throw new ApplicationManagementException(
"Error occurred while staring application release creating transaction for application Id: "
+ applicationId, e);
} catch (DBConnectionException e) {
ConnectionManagerUtil.rollbackDBTransaction();
throw new ApplicationManagementException(
"Error occurred while adding application release into IoTS app management Application id of the "
+ "application release: " + applicationId, e);
} catch (ApplicationManagementDAOException e) {
throw new ApplicationManagementException(
"Error occurred while adding application release into IoTS app management Application id of the "
@ -321,6 +350,53 @@ public class ApplicationManagerImpl implements ApplicationManager {
}
}
@Override
public Application getApplicationByUuid(String uuid, String state) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
Application application;
boolean isAppAllowed = false;
boolean isOpenConnection = false;
List<ApplicationRelease> applicationReleases = null;
try {
if (state != null) {
ConnectionManagerUtil.openDBConnection();
isOpenConnection = true;
}
application = this.applicationDAO.getApplicationByUUID(uuid, tenantId);
if (application == null) {
throw new NotFoundException("Couldn't find an application for application release UUID:: " + uuid);
}
if (isAdminUser(userName, tenantId, CarbonConstants.UI_ADMIN_PERMISSION_COLLECTION)) {
applicationReleases = getReleases(application, state);
application.setApplicationReleases(applicationReleases);
return application;
}
if (!application.getUnrestrictedRoles().isEmpty()) {
if (isRoleExists(application.getUnrestrictedRoles(), userName)) {
isAppAllowed = true;
}
} else {
isAppAllowed = true;
}
if (!isAppAllowed) {
return null;
}
applicationReleases = getReleases(application, state);
application.setApplicationReleases(applicationReleases);
return application;
} catch (UserStoreException e) {
throw new ApplicationManagementException(
"User-store exception while getting application with the application release UUID " + uuid);
} finally {
if (isOpenConnection) {
ConnectionManagerUtil.closeDBConnection();
}
}
}
private boolean isRoleExists(Collection<UnrestrictedRole> unrestrictedRoleList, String userName)
throws UserStoreException {
String[] roleList;
@ -467,6 +543,14 @@ public class ApplicationManagerImpl implements ApplicationManager {
if (AppLifecycleState.PUBLISHED.toString()
.equals(state) && filteredReleases.size() > 1) {
log.warn("There are more than one application releases is found which is in PUBLISHED state");
filteredReleases.sort((r1, r2) -> {
if (r1.getLifecycleState().getUpdatedAt().after(r2.getLifecycleState().getUpdatedAt())) {
return -1;
} else if (r2.getLifecycleState().getUpdatedAt().after(r1.getLifecycleState().getUpdatedAt())) {
return 1;
}
return 0;
});
}
return filteredReleases;
}
@ -502,7 +586,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
LifecycleState newAppLifecycleState = new LifecycleState();
newAppLifecycleState.setPreviousState(appLifecycleState.getCurrentState());
newAppLifecycleState.setCurrentState(AppLifecycleState.REMOVED.toString());
changeLifecycleState(applicationId, applicationRelease.getUuid(), newAppLifecycleState, true, false);
changeLifecycleState(applicationId, applicationRelease.getUuid(), newAppLifecycleState, false);
storedLocations.add(applicationRelease.getAppHashValue());
}
this.applicationDAO.deleteApplication(applicationId);
@ -537,7 +621,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
LifecycleState newAppLifecycleState = new LifecycleState();
newAppLifecycleState.setPreviousState(appLifecycleState.getCurrentState());
newAppLifecycleState.setCurrentState(AppLifecycleState.REMOVED.toString());
changeLifecycleState(applicationId, applicationRelease.getUuid(), newAppLifecycleState, true, false);
changeLifecycleState(applicationId, applicationRelease.getUuid(), newAppLifecycleState, false);
} else {
throw new ApplicationManagementException("Can't delete the application release, You have to move the " +
"lifecycle state from " + currentState + " to acceptable " +
@ -751,7 +835,6 @@ public class ApplicationManagerImpl implements ApplicationManager {
try {
ConnectionManagerUtil.openDBConnection();
applicationRelease = getAppReleaseIfExists(appId, uuid);
Application application = getApplicationById(appId, null);
List<DeviceType> deviceTypes = Util.getDeviceManagementService().getDeviceTypes();
@ -838,8 +921,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
+ releaseUuid);
}
lifecycleState.setNextStates(new ArrayList<>(getLifecycleManagementService().
getNextLifecycleStates(lifecycleState.getCurrentState())));
lifecycleState.setNextStates(new ArrayList<>(lifecycleStateManger.getNextLifecycleStates(lifecycleState.getCurrentState())));
} catch (ApplicationManagementException e) {
throw new ApplicationManagementException("Failed to get application and application management", e);
@ -849,15 +931,14 @@ public class ApplicationManagerImpl implements ApplicationManager {
return lifecycleState;
}
@Override
public void changeLifecycleState(int applicationId, String releaseUuid, LifecycleState state, Boolean checkExist,
Boolean handleDBConnections) throws ApplicationManagementException {
@Override public void changeLifecycleState(int applicationId, String releaseUuid, LifecycleState state,
Boolean checkExist) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
boolean handleDBConnection = false;
try {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
if (checkExist) {
if (handleDBConnections) {
ConnectionManagerUtil.openDBConnection();
}
ConnectionManagerUtil.openDBConnection();
handleDBConnection = true;
if (!this.applicationDAO.verifyApplicationExistenceById(applicationId, tenantId)){
throw new NotFoundException(
"Couldn't found application for the application Id: " + applicationId);
@ -867,14 +948,20 @@ public class ApplicationManagerImpl implements ApplicationManager {
"Couldn't found application release for the application Id: " + applicationId
+ " application release uuid: " + releaseUuid);
}
LifecycleState currentState = this.lifecycleStateDAO.getLatestLifeCycleState(applicationId, releaseUuid);
if (currentState == null){
throw new ApplicationManagementException(
"Couldn't found latest lifecycle state for the appId: " + applicationId
+ " and application release UUID: " + releaseUuid);
}
state.setPreviousState(currentState.getCurrentState());
}
String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
state.setUpdatedBy(userName);
if (state.getCurrentState() != null && state.getPreviousState() != null) {
if (getLifecycleManagementService()
.isValidStateChange(state.getPreviousState(), state.getCurrentState())) {
if (lifecycleStateManger.isValidStateChange(state.getPreviousState(), state.getCurrentState())) {
//todo if current state of the adding lifecycle state is PUBLISHED, need to check whether is there
//todo any other application release in PUBLISHED state for the application( i.e for the appid)
this.lifecycleStateDAO.addLifecycleState(state, applicationId, releaseUuid, tenantId);
@ -890,7 +977,7 @@ public class ApplicationManagerImpl implements ApplicationManager {
"Failed to add lifecycle state. Application Id: " + applicationId + " Application release UUID: "
+ releaseUuid, e);
} finally {
if (handleDBConnections) {
if (handleDBConnection) {
ConnectionManagerUtil.closeDBConnection();
}
}
@ -997,15 +1084,4 @@ public class ApplicationManagerImpl implements ApplicationManager {
return list;
}
public LifecycleStateManger getLifecycleManagementService() {
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
LifecycleStateManger deviceManagementProviderService =
(LifecycleStateManger) ctx.getOSGiService(LifecycleStateManger.class, null);
if (deviceManagementProviderService == null) {
String msg = "DeviceImpl Management provider service has not initialized.";
log.error(msg);
throw new IllegalStateException(msg);
}
return deviceManagementProviderService;
}
}

@ -22,13 +22,13 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.publisher.api</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Application Management Publisher API</name>
<description>WSO2 Carbon - Application Management Publisher API</description>

@ -313,6 +313,79 @@ public interface ApplicationManagementAPI {
@Multipart(value = "screenshot3") Attachment screenshot3
);
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes("multipart/mixed")
@Path("/{deviceType}/{appType}/{appId}")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "POST",
value = "Create an application",
notes = "This will create a new application",
tags = "Application Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:publisher:update")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 201,
message = "OK. \n Successfully created an application.",
response = Application.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Application creating payload contains unacceptable or vulnerable data"),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while creating the application.",
response = ErrorResponse.class)
})
Response createRelease(
@PathParam("deviceType") String deviceType,
@PathParam("appId") String appType,
@PathParam("appId") int appId,
@ApiParam(
name = "applicationRelease",
value = "The application release that need to be created.",
required = true)
@Multipart("applicationRelease") ApplicationRelease applicationRelease,
@ApiParam(
name = "binaryFile",
value = "Binary file of uploading application",
required = true)
@Multipart(value = "binaryFile") Attachment binaryFile,
@ApiParam(
name = "icon",
value = "Icon of the uploading application",
required = true)
@Multipart(value = "icon") Attachment iconFile,
@ApiParam(
name = "banner",
value = "Banner of the uploading application",
required = true)
@Multipart(value = "banner") Attachment bannerFile,
@ApiParam(
name = "screenshot1",
value = "Screen Shots of the uploading application",
required = true)
@Multipart(value = "screenshot1") Attachment screenshot1,
@ApiParam(
name = "screenshot2",
value = "Screen Shots of the uploading application",
required = false)
@Multipart(value = "screenshot2") Attachment screenshot2,
@ApiParam(
name = "screenshot3",
value = "Screen Shots of the uploading application",
required = false)
@Multipart(value = "screenshot3") Attachment screenshot3
);
@DELETE
@Consumes("application/json")
@Path("/{appid}")
@ -557,6 +630,10 @@ public interface ApplicationManagementAPI {
code = 201,
message = "OK. \n Successfully add a lifecycle state.",
response = Application.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n " +
"Lifecycle State changing request contains unacceptable or vulnerable data"),
@ApiResponse(
code = 404,
message = "NOT FOUND. \n Error occurred while adding new lifecycle state.",
@ -566,7 +643,21 @@ public interface ApplicationManagementAPI {
message = "Internal Server Error. \n Error occurred adding a lifecycle state.",
response = ErrorResponse.class)
})
Response addLifecycleState(@PathParam("appId") int applicationId,
@PathParam("uuid") String applicationUuid,
LifecycleState state);
Response addLifecycleState(
@ApiParam(
name = "appId",
value = "Identifier of the Application",
required = true)
@PathParam("appId") int applicationId,
@ApiParam(
name = "uuid",
value = "UUID of the Application Release",
required = true)
@PathParam("uuid") String applicationUuid,
@ApiParam(
name = "action",
value = "Changing lifecycle state",
required = true)
@QueryParam("action") String action
);
}

@ -96,8 +96,8 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
ApplicationList applications = applicationManager.getApplications(filter);
if (applications.getApplications().isEmpty()) {
return Response.status(Response.Status.NOT_FOUND).entity
("Couldn't find any application for requested query.").build();
return Response.status(Response.Status.NOT_FOUND)
.entity("Couldn't find any application for requested query.").build();
}
return Response.status(Response.Status.OK).entity(applications).build();
} catch (ApplicationManagementException e) {
@ -147,10 +147,10 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
ApplicationRelease applicationRelease;
List<Attachment> attachmentList = new ArrayList<>();
attachmentList.add(screenshot1);
if(screenshot2 != null) {
if (screenshot2 != null) {
attachmentList.add(screenshot2);
}
if(screenshot3 != null) {
if (screenshot3 != null) {
attachmentList.add(screenshot3);
}
@ -183,8 +183,8 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
// Upload images
applicationRelease = applicationStorageManager.uploadImageArtifacts(applicationRelease, iconFileStream,
bannerFileStream, attachments);
applicationRelease = applicationStorageManager
.uploadImageArtifacts(applicationRelease, iconFileStream, bannerFileStream, attachments);
applicationRelease.setUuid(UUID.randomUUID().toString());
applicationReleases.add(applicationRelease);
application.setApplicationReleases(applicationReleases);
@ -202,8 +202,9 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
} catch (ResourceManagementException e) {
log.error("Error occurred while uploading the releases artifacts of the application "
+ application.getName(), e);
log.error(
"Error occurred while uploading the releases artifacts of the application " + application.getName(),
e);
return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR);
} catch (IOException e) {
String errorMessage =
@ -218,24 +219,109 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
}
@Override
@PUT
@POST
@Consumes("multipart/mixed")
@Produces(MediaType.APPLICATION_JSON)
@Path("/image-artifacts/{appId}/{uuid}")
public Response updateApplicationImageArtifacts(
@Path("/{deviceType}/{appType}/{appId}")
public Response createRelease(
@PathParam("deviceType") String deviceType,
@PathParam("appId") String appType,
@PathParam("appId") int appId,
@PathParam("uuid") String applicationUuid,
@Multipart("applicationRelease") ApplicationRelease applicationRelease,
@Multipart("binaryFile") Attachment binaryFile,
@Multipart("icon") Attachment iconFile,
@Multipart("banner") Attachment bannerFile,
@Multipart("screenshot1") Attachment screenshot1,
@Multipart("screenshot2") Attachment screenshot2,
@Multipart("screenshot3") Attachment screenshot3) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
ApplicationStorageManager applicationStorageManager = APIUtil.getApplicationStorageManager();
InputStream iconFileStream;
InputStream bannerFileStream;
List<InputStream> attachments = new ArrayList<>();
List<Attachment> attachmentList = new ArrayList<>();
attachmentList.add(screenshot1);
if (screenshot2 != null) {
attachmentList.add(screenshot2);
}
if (screenshot3 != null) {
attachmentList.add(screenshot3);
}
try {
if (!isValidReleaseCreatingRequest(binaryFile, iconFile, bannerFile, attachmentList, appType)) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
// The application executable artifacts such as apks are uploaded.
if (!ApplicationType.ENTERPRISE.toString().equals(appType)) {
applicationRelease = applicationStorageManager
.uploadReleaseArtifact(applicationRelease, appType, deviceType, null);
} else {
applicationRelease = applicationStorageManager
.uploadReleaseArtifact(applicationRelease, appType, deviceType,
binaryFile.getDataHandler().getInputStream());
if (applicationRelease.getAppStoredLoc() == null || applicationRelease.getAppHashValue() == null) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
iconFileStream = iconFile.getDataHandler().getInputStream();
bannerFileStream = bannerFile.getDataHandler().getInputStream();
for (Attachment screenshot : attachmentList) {
attachments.add(screenshot.getDataHandler().getInputStream());
}
// Upload images
applicationRelease = applicationStorageManager
.uploadImageArtifacts(applicationRelease, iconFileStream, bannerFileStream, attachments);
applicationRelease.setUuid(UUID.randomUUID().toString());
// Created new application release entry
ApplicationRelease release = applicationManager.createRelease(appId, applicationRelease);
if (release != null) {
return Response.status(Response.Status.CREATED).entity(release).build();
} else {
log.error("Application Creation Failed");
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
} catch (ApplicationManagementException e) {
String msg = "Error occurred while creating the application";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
} catch (ResourceManagementException e) {
log.error(
"Error occurred while uploading the releases artifacts of the application ID: " + appId,
e);
return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR);
} catch (IOException e) {
String errorMessage =
"Error while uploading binary file and resources for the application release of the application ID: "
+ appId;
log.error(errorMessage, e);
return APIUtil.getResponse(new ApplicationManagementException(errorMessage, e),
Response.Status.INTERNAL_SERVER_ERROR);
} catch (RequestValidatingException e) {
log.error("Error occurred while handling the application creating request");
return APIUtil.getResponse(e, Response.Status.BAD_REQUEST);
}
}
@Override
@PUT
@Consumes("multipart/mixed")
@Produces(MediaType.APPLICATION_JSON)
@Path("/image-artifacts/{appId}/{uuid}")
public Response updateApplicationImageArtifacts(
@PathParam("appId") int appId, @PathParam("uuid") String applicationUuid,
@Multipart("icon") Attachment iconFile, @Multipart("banner") Attachment bannerFile,
@Multipart("screenshot1") Attachment screenshot1, @Multipart("screenshot2") Attachment screenshot2,
@Multipart("screenshot3") Attachment screenshot3) {
try {
InputStream iconFileStream = null;
InputStream bannerFileStream = null;
List<InputStream> attachments = new ArrayList<>();;
List<InputStream> attachments = new ArrayList<>();
if (iconFile != null) {
iconFileStream = iconFile.getDataHandler().getInputStream();
@ -245,18 +331,18 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
attachments.add(screenshot1.getDataHandler().getInputStream());
if(screenshot2 != null) {
if (screenshot2 != null) {
attachments.add(screenshot2.getDataHandler().getInputStream());
}
if(screenshot3 != null) {
if (screenshot3 != null) {
attachments.add(screenshot3.getDataHandler().getInputStream());
}
ApplicationManager applicationManager = APIUtil.getApplicationManager();
applicationManager.updateApplicationImageArtifact(appId,
applicationUuid, iconFileStream, bannerFileStream, attachments);
applicationManager.updateApplicationImageArtifact(appId, applicationUuid, iconFileStream, bannerFileStream,
attachments);
return Response.status(Response.Status.OK).entity("Successfully uploaded artifacts for the application "
+ applicationUuid).build();
return Response.status(Response.Status.OK)
.entity("Successfully uploaded artifacts for the application " + applicationUuid).build();
} catch (NotFoundException e) {
log.error(e.getMessage(), e);
return APIUtil.getResponse(e, Response.Status.NOT_FOUND);
@ -267,8 +353,8 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
} catch (IOException e) {
String msg = "Exception while trying to read icon, banner files for the application " + applicationUuid;
log.error(msg);
return APIUtil.getResponse(new ApplicationManagementException(msg, e),
Response.Status.INTERNAL_SERVER_ERROR);
return APIUtil
.getResponse(new ApplicationManagementException(msg, e), Response.Status.INTERNAL_SERVER_ERROR);
} catch (ResourceManagementException e) {
log.error("Error occurred while uploading the image artifacts of the application with the uuid "
+ applicationUuid, e);
@ -296,14 +382,13 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
APIUtil.getApplicationManager().updateApplicationArtifact(applicationId, applicationUuid,
binaryFile.getDataHandler().getInputStream());
return Response.status(Response.Status.OK)
.entity("Successfully uploaded artifacts for the application release. UUID is " + applicationUuid).build();
.entity("Successfully uploaded artifacts for the application release. UUID is " + applicationUuid)
.build();
} catch (IOException e) {
String msg =
"Error occurred while trying to read icon, banner files for the application release" +
applicationUuid;
String msg = "Error occurred while trying to read icon, banner files for the application release"
+ applicationUuid;
log.error(msg);
return APIUtil.getResponse(new ApplicationManagementException(msg, e),
Response.Status.BAD_REQUEST);
return APIUtil.getResponse(new ApplicationManagementException(msg, e), Response.Status.BAD_REQUEST);
} catch (ResourceManagementException e) {
log.error("Error occurred while uploading the image artifacts of the application with the uuid "
+ applicationUuid, e);
@ -313,8 +398,9 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
+ applicationUuid, e);
return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR);
} catch (RequestValidatingException e) {
log.error("Error occured while handling the application artifact updating request. application release UUID: "
+ applicationUuid);
log.error(
"Error occured while handling the application artifact updating request. application release UUID: "
+ applicationUuid);
return APIUtil.getResponse(e, Response.Status.BAD_REQUEST);
} catch (DeviceManagementException e) {
log.error("Error occurred while updating the image artifacts of the application with the uuid "
@ -360,8 +446,7 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
try {
Application application = applicationManager.getApplicationIfAccessible(applicationId);
if (!applicationManager.isAcceptableAppReleaseUpdate(application.getId(),
applicationRelease.getUuid())) {
if (!applicationManager.isAcceptableAppReleaseUpdate(application.getId(), applicationRelease.getUuid())) {
String msg = "Application release is in the " + applicationRelease.getLifecycleState().getCurrentState()
+ " state. Hence updating is not acceptable when application in this state";
log.error(msg);
@ -384,9 +469,9 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
}
// applicationRelease = applicationStorageManager
// .updateImageArtifacts(applicationRelease, iconFileStream, bannerFileStream, attachments);
// applicationRelease = applicationManager.updateRelease(applicationId, applicationRelease);
// applicationRelease = applicationStorageManager
// .updateImageArtifacts(applicationRelease, iconFileStream, bannerFileStream, attachments);
// applicationRelease = applicationManager.updateRelease(applicationId, applicationRelease);
return Response.status(Response.Status.OK).entity(applicationRelease).build();
} catch (ApplicationManagementException e) {
@ -402,8 +487,9 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
+ applicationUUID + " for the release " + applicationRelease.getVersion(), e);
return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR);
} catch (RequestValidatingException e) {
log.error("Error occured while handling the application release updating request. application release UUID: "
+ applicationUUID);
log.error(
"Error occured while handling the application release updating request. application release UUID: "
+ applicationUUID);
return APIUtil.getResponse(e, Response.Status.BAD_REQUEST);
}
}
@ -462,13 +548,12 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
lifecycleState = applicationManager.getLifecycleState(applicationId, applicationUuid);
} catch (NotFoundException e){
} catch (NotFoundException e) {
String msg = "Couldn't found application lifecycle details for appid: " + applicationId
+ " and app release UUID: " + applicationUuid;
log.error(msg, e);
return Response.status(Response.Status.NOT_FOUND).build();
}
catch (ApplicationManagementException e) {
} catch (ApplicationManagementException e) {
String msg = "Error occurred while getting lifecycle state.";
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
@ -481,10 +566,17 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
public Response addLifecycleState(
@PathParam("appId") int applicationId,
@PathParam("uuid") String applicationUuid,
LifecycleState state) {
@QueryParam("action") String action) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
applicationManager.changeLifecycleState(applicationId, applicationUuid, state, true, true);
if (action == null || action.isEmpty()) {
String msg = "The Action is null or empty. Please check the request";
log.error(msg);
return Response.status(Response.Status.BAD_REQUEST).build();
}
LifecycleState state = new LifecycleState();
state.setCurrentState(action);
applicationManager.changeLifecycleState(applicationId, applicationUuid, state, true);
} catch (NotFoundException e) {
String msg = "Could,t find application release for application id: " + applicationId
+ " and application release uuid: " + applicationUuid;
@ -499,37 +591,62 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
}
private boolean isValidAppCreatingRequest(Attachment binaryFile, Attachment iconFile, Attachment bannerFile,
List<Attachment> attachmentList, Application application){
List<Attachment> attachmentList, Application application) {
if (application.getApplicationReleases().size() > 1) {
log.error(
"Invalid application creating request. Application creating request must have single application "
+ "release. Application name:" + application.getName() + " and type: " +
application.getType());
log.error("Invalid application creating request. Application creating request must have single application "
+ "release. Application name:" + application.getName() + " and type: " + application.getType());
return false;
}
if (iconFile == null) {
log.error("Icon file is not found for the application release. Application name: " +
application.getName() + " and type: " + application.getType());
log.error("Icon file is not found for the application release. Application name: " + application.getName()
+ " and type: " + application.getType());
return false;
}
if (bannerFile == null) {
log.error("Banner file is not found for the application release. Application name: " +
application.getName() + " and application type: " + application.getType());
log.error("Banner file is not found for the application release. Application name: " + application.getName()
+ " and application type: " + application.getType());
return false;
}
if (attachmentList == null || attachmentList.isEmpty()) {
log.error("Screenshots are not found for the application release. Application name: " +
application.getName() + " Application type: " + application.getType());
log.error(
"Screenshots are not found for the application release. Application name: " + application.getName()
+ " Application type: " + application.getType());
return false;
}
if (binaryFile == null && ApplicationType.ENTERPRISE.toString().equals(application.getType())) {
log.error("Binary file is not found for the application release. Application name: "
+ application.getName() + " Application type: " + application.getType());
log.error("Binary file is not found for the application release. Application name: " + application.getName()
+ " Application type: " + application.getType());
return false;
}
return true;
}
private boolean isValidReleaseCreatingRequest(Attachment binaryFile, Attachment iconFile, Attachment bannerFile,
List<Attachment> attachmentList, String appType) {
if (iconFile == null) {
log.error("Icon file is not found with the application release creating request.");
return false;
}
if (bannerFile == null) {
log.error("Banner file is not found with the application release creating request.");
return false;
}
if (attachmentList == null || attachmentList.isEmpty()) {
log.error("Screenshots are not found with the application release creating request.");
return false;
}
if (binaryFile == null && ApplicationType.ENTERPRISE.toString().equals(appType)) {
log.error("Binary file is not found with the application release creating request. Application type: "
+ appType);
return false;
}
return true;

@ -22,12 +22,12 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.publisher.ui</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<name>WSO2 Carbon - Application Management Publisher UI</name>
<description>WSO2 Carbon - Application Management Publisher UI React Application</description>
<url>http://wso2.org</url>

@ -68,6 +68,7 @@ class Base extends Component {
render() {
if (this.state.user !== null) {
console.log(sessionStorage);
return (
<div>
<BaseLayout user={this.state.user}>

@ -22,13 +22,13 @@
<parent>
<artifactId>application-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.application.mgt.store.api</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>war</packaging>
<name>WSO2 Carbon - Application Management Store API</name>
<description>WSO2 Carbon - Application Management Store API</description>

@ -30,12 +30,10 @@ import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.application.mgt.common.Filter;
import org.wso2.carbon.device.application.mgt.common.ErrorResponse;
import org.wso2.carbon.device.application.mgt.common.Application;
import org.wso2.carbon.device.application.mgt.common.ApplicationList;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@ -69,52 +67,14 @@ import javax.ws.rs.core.Response;
@Scope(
name = "Get Application Details",
description = "Get application details",
key = "perm:application:get",
key = "perm:app:store:view",
permissions = {"/device-mgt/application/get"}
),
@Scope(
name = "Create an Application",
description = "Create an application",
key = "perm:application:create",
permissions = {"/device-mgt/application/create"}
),
@Scope(
name = "Update an Application",
description = "Update an application",
key = "perm:application:update",
permissions = {"/device-mgt/application/update"}
),
@Scope(
name = "Create an Application",
description = "Create an application",
key = "perm:application-mgt:login",
permissions = {"/device-mgt/application-mgt/login"}
),
@Scope(
name = "Delete an Application",
description = "Delete an application",
key = "perm:application:delete",
permissions = {"/device-mgt/application/delete"}
),
@Scope(
name = "Create an application category",
description = "Create an application category",
key = "perm:application-category:create",
permissions = {"/device-mgt/application/category/create"}
),
@Scope(
name = "Delete an Application category",
description = "Delete an application category",
key = "perm:application-category:delete",
permissions = {"/device-mgt/application/category/delete"}
)
}
)
@Path("/store/applications")
@Api(value = "Application Management", description = "This API carries all application management related operations " +
"such as get all the applications, add application, etc.")
@Api(value = "Application Management", description = "This API carries all app store management related operations " +
"such as get all the applications etc.")
@Produces(MediaType.APPLICATION_JSON)
public interface ApplicationManagementAPI {
@ -143,9 +103,8 @@ public interface ApplicationManagementAPI {
message = "OK. \n Successfully got application list.",
response = ApplicationList.class),
@ApiResponse(
code = 304,
message = "Not Modified. Empty body because the client already has the latest version "
+ "of the requested resource."),
code = 404,
message = "Not Found. Not Found Applications."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while getting the application list.",
@ -153,10 +112,21 @@ public interface ApplicationManagementAPI {
})
Response getApplications(
@ApiParam(
name = "filter",
value = "Filter to get application list",
required = true)
@Valid Filter filter,
name = "name",
value = "Name of the application")
@QueryParam("name") String appName,
@ApiParam(
name = "type",
value = "Type of the application")
@QueryParam("type") String appType,
@ApiParam(
name = "category",
value = "Category of the application")
@QueryParam("category") String appCategory,
@ApiParam(
name = "exact-match",
value = "Is it requesting exactly matching application or partially matching application.")
@QueryParam("exact-match") boolean isFullMatch,
@ApiParam(
name = "offset",
value = "Provide from which position apps should return", defaultValue = "0")
@ -164,12 +134,16 @@ public interface ApplicationManagementAPI {
@ApiParam(
name = "limit",
value = "Provide how many apps it should return", defaultValue = "20")
@QueryParam("limit") int limit
@QueryParam("limit") int limit,
@ApiParam(
name = "limit",
value = "Provide how many apps it should return", defaultValue = "ASC")
@QueryParam("sort") String sortBy
);
@GET
@Path("/{appType}")
@Path("/{uuid}")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(
@ -201,15 +175,10 @@ public interface ApplicationManagementAPI {
})
Response getApplication(
@ApiParam(
name = "appType",
name = "uuid",
value = "Type of the application",
required = true)
@PathParam("appType") String appType,
@ApiParam(
name = "appName",
value = "Application name",
required = true)
@QueryParam("appName") String appName
@PathParam("uuid") String uuid
);

@ -21,7 +21,6 @@ package org.wso2.carbon.device.application.mgt.store.api.services.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.application.mgt.common.AppLifecycleState;
import org.wso2.carbon.device.application.mgt.common.ApplicationRelease;
import org.wso2.carbon.device.application.mgt.common.Application;
import org.wso2.carbon.device.application.mgt.common.ApplicationList;
import org.wso2.carbon.device.application.mgt.common.Filter;
@ -31,117 +30,84 @@ import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException;
import org.wso2.carbon.device.application.mgt.core.util.APIUtil;
import org.wso2.carbon.device.application.mgt.store.api.services.ApplicationManagementAPI;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of Application Management related APIs.
*/
@Produces({"application/json"})
@Produces({ "application/json" })
@Path("/store/applications")
public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
private static Log log = LogFactory.getLog(ApplicationManagementAPIImpl.class);
@GET
@Consumes("application/json")
@Override
@Consumes("application/json")
public Response getApplications(
@Valid Filter filter,
@QueryParam("offset") int offset,
@QueryParam("limit") int limit) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
@QueryParam("name") String appName,
@QueryParam("type") String appType,
@QueryParam("category") String appCategory,
@QueryParam("exact-match") boolean isFullMatch,
@DefaultValue("0") @QueryParam("offset") int offset,
@DefaultValue("20") @QueryParam("limit") int limit,
@DefaultValue("ASC") @QueryParam("sort") String sortBy) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
try {
Filter filter = new Filter();
filter.setOffset(offset);
filter.setLimit(limit);
filter.setSortBy(sortBy);
filter.setFullMatch(isFullMatch);
filter.setCurrentAppReleaseState(AppLifecycleState.PUBLISHED.toString());
if (appName != null && !appName.isEmpty()) {
filter.setAppName(appName);
}
if (appType != null && !appType.isEmpty()) {
filter.setAppType(appType);
}
if (appCategory != null && !appCategory.isEmpty()) {
filter.setAppCategory(appCategory);
}
ApplicationList applications = applicationManager.getApplications(filter);
List<ApplicationRelease> publishedApplicationRelease = new ArrayList<>();
for (Application application : applications.getApplications()) {
for (ApplicationRelease appRelease: application.getApplicationReleases()){
if (AppLifecycleState.PUBLISHED.toString()
.equals(appRelease.getLifecycleState().getCurrentState())) {
publishedApplicationRelease.add(appRelease);
}
}
if (publishedApplicationRelease.size()>1){
String msg = "Application " + application.getName()
+ " has more than one PUBLISHED application releases";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(msg).build();
}
application.setApplicationReleases(publishedApplicationRelease);
publishedApplicationRelease.clear();
if (applications.getApplications().isEmpty()) {
return Response.status(Response.Status.NOT_FOUND)
.entity("Couldn't find any application for requested query.").build();
}
return Response.status(Response.Status.OK).entity(applications).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while getting the application list";
String msg = "Error occurred while getting the application list for publisher ";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).build();
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@GET
@Consumes("application/json")
@Path("/{appType}")
public Response getApplication(@PathParam("appType") String appType,
@QueryParam("appName") String appName) {
@Path("/{uuid}")
public Response getApplication(
@PathParam("uuid") String uuid) {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
List<Application> filteredApps = new ArrayList<>();
Filter filter;
try {
filter = new Filter();
filter.setOffset(0);
filter.setLimit(20);
filter.setAppType(appType);
filter.setAppName(appName);
ApplicationList applications = applicationManager.getApplications(filter);
if (applications.getApplications().isEmpty()) {
return Response.status(Response.Status.NOT_FOUND)
.entity("Application with application type: " + appType + " not found").build();
}
for (Application application : applications.getApplications()) {
List<ApplicationRelease> publishedApplicationRelease = new ArrayList<>();
for (ApplicationRelease appRelease : application.getApplicationReleases()) {
if (AppLifecycleState.PUBLISHED.toString()
.equals(appRelease.getLifecycleState().getCurrentState())) {
publishedApplicationRelease.add(appRelease);
}
}
if (publishedApplicationRelease.size() > 1) {
String msg = "Application " + application.getName()
+ " has more than one PUBLISHED application releases";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
application.setApplicationReleases(publishedApplicationRelease);
filteredApps.add(application);
}
applications.setApplications(filteredApps);
return Response.status(Response.Status.OK).entity(applications).build();
Application application = applicationManager
.getApplicationByUuid(uuid, AppLifecycleState.PUBLISHED.toString());
return Response.status(Response.Status.OK).entity(application).build();
} catch (NotFoundException e) {
return Response.status(Response.Status.NOT_FOUND).build();
String msg = "Application with application release UUID: " + uuid + " is not found";
log.error(msg, e);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
} catch (ApplicationManagementException e) {
log.error("Error occurred while getting application with the application type: " + appType
+ " and application name: " + appName, e);
return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR);
String msg = "Error occurred while getting application with the application release uuid: " + uuid;
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
// todo --> get applications by category
}

@ -24,10 +24,10 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<artifactId>org.wso2.carbon.device.application.mgt.store.ui</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<dependencies>
</dependencies>

@ -22,13 +22,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>application-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>WSO2 Carbon - Application Management Component</name>
<description>WSO2 Carbon - Application Management Component</description>

@ -22,7 +22,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -24,7 +24,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -21,13 +21,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>certificate-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.certificate.mgt.core</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Certificate Management Core</name>
<description>WSO2 Carbon - Certificate Management Core</description>

@ -24,7 +24,7 @@
<parent>
<artifactId>certificate-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,14 +22,14 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>certificate-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>WSO2 Carbon - Certificate Management Component</name>
<url>http://wso2.org</url>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>carbon-devicemgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>device-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -20,7 +20,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -49,7 +49,7 @@ public class SubscriptionEndpoint {
*
* @param session - Users registered session.
*/
public void onOpen(Session session) {
void onOpen(Session session) {
if (log.isDebugEnabled()) {
log.debug("WebSocket opened, for Session id: " + session.getId());
}
@ -68,8 +68,7 @@ public class SubscriptionEndpoint {
endpoint += "/";
}
endpoint += session.getRequestURI().getSchemeSpecificPart().replace("secured-websocket-proxy","");
AnalyticsClient analyticsClient = new AnalyticsClient(session);
analyticsClient.connectClient(new URI(endpoint));
AnalyticsClient analyticsClient = new AnalyticsClient(session, new URI(endpoint));
analyticsClients.add(analyticsClient);
} catch (URISyntaxException e) {
log.error("Unable to create URL from: " + endpoint, e);
@ -121,7 +120,7 @@ public class SubscriptionEndpoint {
* @param session - Users registered session.
* @param message - Status code for web-socket close.
*/
public void onMessage(Session session, String message) {
void onMessage(Session session, String message) {
for (AnalyticsClient analyticsClient : analyticsClientsMap.get(session.getId())) {
if (analyticsClient != null) {
analyticsClient.sendMessage(message);

@ -42,24 +42,18 @@ public class AnalyticsClient {
private static final Log log = LogFactory.getLog(AnalyticsClient.class);
private WebSocketContainer container;
private Session analyticsSession = null;
private Session clientSession;
private final Session analyticsSession;
private final Session clientSession;
/**
* Create {@link AnalyticsClient} instance.
*/
public AnalyticsClient(Session clientSession) {
container = ContainerProvider.getWebSocketContainer();
public AnalyticsClient(Session clientSession, URI endpointURI) throws WSProxyException {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
this.clientSession = clientSession;
}
/**
* Create web socket client connection using {@link WebSocketContainer}.
*/
public void connectClient(URI endpointURI) throws WSProxyException {
try {
analyticsSession = container.connectToServer(this, endpointURI);
this.analyticsSession = container.connectToServer(this, endpointURI);
} catch (DeploymentException | IOException e) {
String msg = "Error occurred while connecting to remote endpoint " + endpointURI.toString();
log.error(msg, e);
@ -79,7 +73,6 @@ public class AnalyticsClient {
log.debug("Closing web socket session: '" + userSession.getId() + "'. Code: " +
reason.getCloseCode().toString() + " Reason: " + reason.getReasonPhrase());
}
this.analyticsSession = null;
}
/**
@ -91,7 +84,16 @@ public class AnalyticsClient {
*/
@OnMessage
public void onMessage(String message) {
this.clientSession.getAsyncRemote().sendText(message);
synchronized (this.clientSession) {
try {
this.clientSession.getBasicRemote().sendText(message);
} catch (IOException e) {
log.warn("Sending message to client failed due to " + e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Full stack trace:", e);
}
}
}
}
/**
@ -100,14 +102,23 @@ public class AnalyticsClient {
* @param message the message which is going to send.
*/
public void sendMessage(String message) {
this.analyticsSession.getAsyncRemote().sendText(message);
synchronized (this.analyticsSession) {
try {
this.analyticsSession.getBasicRemote().sendText(message);
} catch (IOException e) {
log.warn("Sending message to analytics failed due to " + e.getMessage());
if (log.isDebugEnabled()) {
log.debug("Full stack trace:", e);
}
}
}
}
/**
* Close current connection.
*/
public void closeConnection(CloseReason closeReason) throws WSProxyException {
if (this.analyticsSession != null) {
if (this.analyticsSession.isOpen()) {
try {
this.analyticsSession.close(closeReason);
} catch (IOException e) {
@ -115,6 +126,8 @@ public class AnalyticsClient {
log.error(msg, e);
throw new WSProxyException(msg, e);
}
} else {
log.warn("Analytics session '" + this.analyticsSession.getId() + "' is already closed");
}
}
}

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -529,6 +529,92 @@ public interface UserManagementService {
defaultValue = "5")
@QueryParam("limit") int limit);
@GET
@Path(("/search"))
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Filter details of users based on the given claims",
notes = "You are able to manage users in WSO2 IoTS by adding, updating and removing users. If you wish to" +
" filter and get a list of users registered with WSO2 IoTS, you can do so using this REST API",
tags = "User Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:users:user-details")
})
}
)
@ApiResponses(value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully fetched the list of users registered with WSO2 IoTS.",
response = BasicUserInfoList.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. \n Empty body because the client already has the latest version of " +
"the requested resource.\n"),
@ApiResponse(
code = 406,
message = "Not Acceptable.\n The requested media type is not supported",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Server error occurred while fetching users.",
response = ErrorResponse.class)
})
Response getUsers(
@ApiParam(
name = "username",
value = "Username of the user",
required = false
)
@QueryParam("username") String username,
@ApiParam(
name = "firstName",
value = "First Name of the user",
required = false
)
@QueryParam("firstName") String firstName,
@ApiParam(
name = "lastName",
value = "Last Name of the user",
required = false
)
@QueryParam("lastName")String lastName,
@ApiParam(
name = "emailAddress",
value = "Email Address of the user",
required = false
)
@QueryParam("emailAddress")String emailAddress,
@HeaderParam("If-Modified-Since") String timestamp,
@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 user details you require from the starting pagination index/offset.",
required = false,
defaultValue = "5")
@QueryParam("limit") int limit
);
@GET
@Path("/count")
@ApiOperation(

@ -100,7 +100,7 @@ public class GeoLocationBasedServiceImpl implements GeoLocationBasedService {
CarbonContext.getThreadLocalCarbonContext().getUsername());
try {
String tenantDomain = MultitenantUtils.getTenantDomain(authorizedUser);
String tenantDomain = CarbonContext.getThreadLocalCarbonContext().getTenantDomain();
int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain);
AnalyticsDataAPI analyticsDataAPI = DeviceMgtAPIUtils.getAnalyticsDataAPI();
List<SearchResultEntry> searchResults = analyticsDataAPI.search(tenantId, tableName, query,

@ -426,6 +426,104 @@ public class UserManagementServiceImpl implements UserManagementService {
}
}
@GET
@Path("/search")
@Override
public Response getUsers(@QueryParam("username") String username, @QueryParam("firstName") String firstName,
@QueryParam("lastName") String lastName, @QueryParam("emailAddress") String emailAddress,
@HeaderParam("If-Modified-Since") String timestamp, @QueryParam("offset") int offset,
@QueryParam("limit") int limit) {
if (RequestValidationUtil.isNonFilterRequest(username,firstName, lastName, emailAddress)) {
return getUsers(null, timestamp, offset, limit);
}
RequestValidationUtil.validatePaginationParameters(offset, limit);
if(log.isDebugEnabled()) {
log.debug("Filtering users - filter: {username: " + username +", firstName: " + firstName + ", lastName: "
+ lastName + ", emailAddress: " + emailAddress + "}");
}
if (limit == 0) {
limit = Constants.DEFAULT_PAGE_LIMIT;
}
List<BasicUserInfo> filteredUserList = new ArrayList<>();
List<String> commonUsers = null, tempList;
try {
if (StringUtils.isNotEmpty(username)) {
commonUsers = getUserList(null, username);
}
if (!skipSearch(commonUsers) && StringUtils.isNotEmpty(firstName)) {
tempList = getUserList(Constants.USER_CLAIM_FIRST_NAME, firstName);
if (commonUsers == null) {
commonUsers = tempList;
} else {
commonUsers.retainAll(tempList);
}
}
if (!skipSearch(commonUsers) && StringUtils.isNotEmpty(lastName)) {
tempList = getUserList(Constants.USER_CLAIM_LAST_NAME, lastName);
if (commonUsers == null || commonUsers.size() == 0) {
commonUsers = tempList;
} else {
commonUsers.retainAll(tempList);
}
}
if (!skipSearch(commonUsers) && StringUtils.isNotEmpty(emailAddress)) {
tempList = getUserList(Constants.USER_CLAIM_EMAIL_ADDRESS, emailAddress);
if (commonUsers == null || commonUsers.size() == 0) {
commonUsers = tempList;
} else {
commonUsers.retainAll(tempList);
}
}
BasicUserInfo basicUserInfo;
if (commonUsers != null) {
for (String user : commonUsers) {
basicUserInfo = new BasicUserInfo();
basicUserInfo.setUsername(user);
basicUserInfo.setEmailAddress(getClaimValue(user, Constants.USER_CLAIM_EMAIL_ADDRESS));
basicUserInfo.setFirstname(getClaimValue(user, Constants.USER_CLAIM_FIRST_NAME));
basicUserInfo.setLastname(getClaimValue(user, Constants.USER_CLAIM_LAST_NAME));
filteredUserList.add(basicUserInfo);
}
}
int toIndex = offset + limit;
int listSize = filteredUserList.size();
int lastIndex = listSize - 1;
List<BasicUserInfo> offsetList;
if (offset <= lastIndex) {
if (toIndex <= listSize) {
offsetList = filteredUserList.subList(offset, toIndex);
} else {
offsetList = filteredUserList.subList(offset, listSize);
}
} else {
offsetList = new ArrayList<>();
}
BasicUserInfoList result = new BasicUserInfoList();
result.setList(offsetList);
result.setCount(commonUsers != null ? commonUsers.size() : 0);
return Response.status(Response.Status.OK).entity(result).build();
} catch (UserStoreException e) {
String msg = "Error occurred while retrieving the list of users.";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
}
@GET
@Path("/count")
@Override
@ -700,4 +798,47 @@ public class UserManagementServiceImpl implements UserManagementService {
return DeviceManagementConstants.EmailAttributes.DEFAULT_ENROLLMENT_TEMPLATE;
}
/**
* Searches users which matches a given filter based on a claim
*
* @param claim the claim value to apply the filter. If <code>null</code> users will be filtered by username.
* @param filter the search query.
* @return <code>List<String></code> of users which matches.
* @throws UserStoreException If unable to search users.
*/
private ArrayList<String> getUserList(String claim, String filter) throws UserStoreException {
String defaultFilter = "*";
org.wso2.carbon.user.core.UserStoreManager userStoreManager =
(org.wso2.carbon.user.core.UserStoreManager) DeviceMgtAPIUtils.getUserStoreManager();
String appliedFilter = filter + defaultFilter;
String[] users;
if (log.isDebugEnabled()) {
log.debug("Searching Users - claim: " + claim + " filter: " + appliedFilter);
}
if (StringUtils.isEmpty(claim)) {
users = userStoreManager.listUsers(appliedFilter, -1);
} else {
users = userStoreManager.getUserList(claim, appliedFilter, null);
}
if (log.isDebugEnabled()) {
log.debug("Returned user count: " + users.length);
}
return new ArrayList<>(Arrays.asList(users));
}
/**
* User search provides an AND search result and if either of the filter returns an empty set of users, there is no
* need to carry on the search further. This method decides whether to carry on the search or not.
*
* @param commonUsers current filtered user list.
* @return <code>true</code> if further search is needed.
*/
private boolean skipSearch(List<String> commonUsers) {
return commonUsers != null && commonUsers.size() == 0;
}
}

@ -18,6 +18,7 @@
*/
package org.wso2.carbon.device.mgt.jaxrs.service.impl.util;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.device.mgt.jaxrs.beans.Scope;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
@ -353,4 +354,9 @@ public class RequestValidationUtil {
}
}
public static boolean isNonFilterRequest(String username, String firstName, String lastName, String emailAddress) {
return StringUtils.isEmpty(username) && StringUtils.isEmpty(firstName) && StringUtils.isEmpty(lastName)
&& StringUtils.isEmpty(emailAddress);
}
}

@ -21,7 +21,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -37,7 +37,7 @@ public class Operation implements Serializable {
}
public enum Status {
IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED
IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW
}
public enum Control {

@ -76,6 +76,9 @@ public interface OperationManager {
*/
List<? extends Operation> getPendingOperations(DeviceIdentifier deviceId) throws OperationManagementException;
Operation getNextPendingOperation(DeviceIdentifier deviceId, long notNowOperationFrequency)
throws OperationManagementException;
Operation getNextPendingOperation(DeviceIdentifier deviceId) throws OperationManagementException;
void updateOperation(DeviceIdentifier deviceId, Operation operation) throws OperationManagementException;

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>device-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -321,7 +321,7 @@ public class DeviceManagementPluginRepository implements DeviceManagerStartupLis
getDeviceTaskManagerService();
OperationMonitoringTaskConfig operationMonitoringTaskConfig = deviceManagementService.
getOperationMonitoringConfig();
if (operationMonitoringTaskConfig != null) {
if (operationMonitoringTaskConfig != null && operationMonitoringTaskConfig.isEnabled()) {
deviceTaskManagerService.stopTask(deviceManagementService.getType(),
deviceManagementService.getOperationMonitoringConfig());
}

@ -23,12 +23,14 @@ import java.util.Properties;
public class Operation implements Serializable {
private static final long serialVersionUID = -353614864628915060L;
public enum Type {
CONFIG, MESSAGE, INFO, COMMAND, PROFILE , POLICY
}
public enum Status {
IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED
IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW
}
public enum Control {

@ -51,6 +51,7 @@ import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerSer
import org.wso2.carbon.registry.api.Registry;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.Resource;
import org.wso2.carbon.stratos.common.util.ClaimsMgtUtil;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
@ -428,6 +429,9 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic
} catch (JWTClientException e) {
throw new GeoLocationBasedServiceException(
"JWT token creation failed while " + action + " geo alert '" + alertType, e);
} catch (Exception e) {
throw new GeoLocationBasedServiceException(
"Error occurred while " + action + " geo alert '" + alertType, e);
} finally {
cleanup(eventprocessorStub);
}
@ -541,6 +545,9 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic
throw new GeoLocationBasedServiceException(
"JWT token creation failed while " + action + " geo alert '" + alertType + "' for " +
identifier.getType() + " device with id:" + identifier.getId(), e);
} catch (Exception e) {
throw new GeoLocationBasedServiceException(
"Error occurred while " + action + " geo alert '" + alertType, e);
} finally {
cleanup(eventprocessorStub);
}
@ -646,6 +653,9 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic
executionPlanName + " for " +
identifier.getType() + " device with id:" + identifier.getId(), e
);
} catch (Exception e) {
throw new GeoLocationBasedServiceException(
"Error occurred while removing geo alert '" + alertType, e);
} finally {
cleanup(eventprocessorStub);
}
@ -672,6 +682,9 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic
"JWT token creation failed while removing geo alert '" + alertType + "': " +
executionPlanName, e
);
} catch (Exception e) {
throw new GeoLocationBasedServiceException(
"Error occurred while removing geo alert '" + alertType, e);
} finally {
cleanup(eventprocessorStub);
}
@ -702,14 +715,16 @@ public class GeoLocationProviderServiceImpl implements GeoLocationProviderServic
}
}
protected EventProcessorAdminServiceStub getEventProcessorAdminServiceStub() throws JWTClientException {
protected EventProcessorAdminServiceStub getEventProcessorAdminServiceStub() throws Exception {
//send alert to event-processing
String eventProcessorAdminServiceWSUrl = Utils.replaceSystemProperty(GeoServices.DAS_URL) +
"/services/EventProcessorAdminService";
//Getting the tenant Domain
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
String username = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
String username = ClaimsMgtUtil.getAdminUserNameFromTenantId(DeviceManagementDataHolder.getInstance().getRealmService(),
tenantId);
String tenantAdminUser = username + "@" + tenantDomain;
try {

@ -89,7 +89,9 @@ public class DeviceTaskManagerServiceComponent {
Map<String, OperationMonitoringTaskConfig> deviceConfigMap = DeviceMonitoringOperationDataHolder
.getInstance().getOperationMonitoringConfigFromMap();
for (String platformType : new ArrayList<>(deviceConfigMap.keySet())) {
deviceTaskManagerService.startTask(platformType, deviceConfigMap.get(platformType));
if (deviceConfigMap.get(platformType).isEnabled()){
deviceTaskManagerService.startTask(platformType, deviceConfigMap.get(platformType));
}
deviceConfigMap.remove(platformType);
}
}

@ -64,6 +64,7 @@ import org.wso2.carbon.device.mgt.core.task.impl.DeviceTaskManagerImpl;
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@ -251,7 +252,7 @@ public class OperationManagerImpl implements OperationManager {
}
OperationManagementDAOFactory.commitTransaction();
if (isScheduled) {
if (!isScheduled) {
for (Device device : authorizedDevices) {
this.sendNotification(operation, device);
}
@ -537,6 +538,13 @@ public class OperationManagerImpl implements OperationManager {
@Override
public Operation getNextPendingOperation(DeviceIdentifier deviceId) throws OperationManagementException {
// setting notNowOperationFrequency to -1 to avoid picking notnow operations
return this.getNextPendingOperation(deviceId, -1);
}
@Override
public Operation getNextPendingOperation(DeviceIdentifier deviceId, long notNowOperationFrequency)
throws OperationManagementException {
if (log.isDebugEnabled()) {
log.debug("device identifier id:[" + deviceId.getId() + "] type:[" + deviceId.getType() + "]");
}
@ -565,8 +573,32 @@ public class OperationManagerImpl implements OperationManager {
try {
OperationManagementDAOFactory.openConnection();
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation dtoOperation = operationDAO.getNextOperation(
enrolmentInfo.getId());
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation dtoOperation = null;
// check whether notnow is set
if (notNowOperationFrequency > 0) {
// retrieve Notnow operations
dtoOperation = operationDAO.getNextOperation(enrolmentInfo.getId(),
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation.Status.NOTNOW);
}
if (dtoOperation != null) {
long currentTime = Calendar.getInstance().getTime().getTime();
log.info("Current timestamp:" + currentTime);
long updatedTime = Timestamp.valueOf(dtoOperation.getReceivedTimeStamp()).getTime();
log.info("Updated timestamp: " + updatedTime);
// check if notnow frequency is met and set next pending operation if not, otherwise let notnow
// operation to proceed
if ((currentTime - updatedTime) < notNowOperationFrequency) {
dtoOperation = operationDAO.getNextOperation(enrolmentInfo.getId(),
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation.Status.PENDING);
}
} else {
dtoOperation = operationDAO.getNextOperation(enrolmentInfo.getId(),
org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation.Status.PENDING);
}
if (dtoOperation != null) {
if (org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation.Type.COMMAND.equals(dtoOperation.getType()
)) {

@ -46,7 +46,7 @@ public interface OperationDAO {
List<? extends Operation> getOperationsForDevice(int enrolmentId, PaginationRequest request) throws OperationManagementDAOException;
Operation getNextOperation(int enrolmentId) throws OperationManagementDAOException;
Operation getNextOperation(int enrolmentId, Operation.Status status) throws OperationManagementDAOException;
boolean updateOperationStatus(int enrolmentId, int operationId,Operation.Status status)
throws OperationManagementDAOException;

@ -1372,7 +1372,7 @@ public class GenericOperationDAOImpl implements OperationDAO {
}
@Override
public Operation getNextOperation(int enrolmentId) throws OperationManagementDAOException {
public Operation getNextOperation(int enrolmentId, Operation.Status status) throws OperationManagementDAOException {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
@ -1383,7 +1383,7 @@ public class GenericOperationDAOImpl implements OperationDAO {
"WHERE dm.ENROLMENT_ID = ? AND dm.STATUS = ?) om ON o.ID = om.OPERATION_ID " +
"ORDER BY om.UPDATED_TIMESTAMP ASC, om.ID ASC LIMIT 1");
stmt.setInt(1, enrolmentId);
stmt.setString(2, Operation.Status.PENDING.toString());
stmt.setString(2, status.toString());
rs = stmt.executeQuery();
Operation operation = null;
@ -1392,11 +1392,7 @@ public class GenericOperationDAOImpl implements OperationDAO {
operation.setType(OperationDAOUtil.getType(rs.getString("TYPE")));
operation.setId(rs.getInt("ID"));
operation.setCreatedTimeStamp(rs.getTimestamp("CREATED_TIMESTAMP").toString());
// if (rs.getTimestamp("RECEIVED_TIMESTAMP") == null) {
// operation.setReceivedTimeStamp("");
// } else {
// operation.setReceivedTimeStamp(rs.getTimestamp("RECEIVED_TIMESTAMP").toString());
// }
if (rs.getLong("UPDATED_TIMESTAMP") == 0) {
operation.setReceivedTimeStamp("");
} else {

@ -53,7 +53,7 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
bao = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bao);
oos.writeObject(operation);
oos.writeObject(operation.getPayLoad());
stmt.setInt(1, operationId);
stmt.setBytes(2, bao.toByteArray());
@ -91,7 +91,8 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
ObjectInputStream ois;
try {
Connection conn = OperationManagementDAOFactory.getConnection();
String sql = "SELECT OPERATION_ID, ENABLED, OPERATION_DETAILS FROM DM_PROFILE_OPERATION WHERE OPERATION_ID=?";
String sql = "SELECT o.ID, po.ENABLED, po.OPERATION_DETAILS, o.CREATED_TIMESTAMP, o.OPERATION_CODE " +
"FROM DM_PROFILE_OPERATION po INNER JOIN DM_OPERATION o ON po.OPERATION_ID = O.ID WHERE po.OPERATION_ID=?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, id);
@ -99,9 +100,20 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
if (rs.next()) {
byte[] operationDetails = rs.getBytes("OPERATION_DETAILS");
int oppId = rs.getInt("ID");
bais = new ByteArrayInputStream(operationDetails);
ois = new ObjectInputStream(bais);
profileOperation = (ProfileOperation) ois.readObject();
Object obj = ois.readObject();
if(obj instanceof String){
profileOperation = new ProfileOperation();
profileOperation.setCode(rs.getString("OPERATION_CODE"));
profileOperation.setId(oppId);
profileOperation.setCreatedTimeStamp(rs.getString("CREATED_TIMESTAMP"));
profileOperation.setId(oppId);
profileOperation.setPayLoad(obj);
} else {
profileOperation = (ProfileOperation) obj;
}
}
} catch (IOException e) {
throw new OperationManagementDAOException("IO Error occurred while de serialize the profile " +
@ -110,7 +122,7 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
throw new OperationManagementDAOException("Class not found error occurred while de serialize the " +
"profile operation object", e);
} catch (SQLException e) {
throw new OperationManagementDAOException("SQL Error occurred while retrieving the command " +
throw new OperationManagementDAOException("SQL Error occurred while retrieving the profile " +
"operation object " + "available for the id '" + id, e);
} finally {
OperationManagementDAOUtil.cleanupResources(stmt, rs);
@ -120,7 +132,7 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
@Override
public List<? extends Operation> getOperationsByDeviceAndStatus(int enrolmentId,
Operation.Status status) throws OperationManagementDAOException {
Operation.Status status) throws OperationManagementDAOException {
PreparedStatement stmt = null;
ResultSet rs = null;
ProfileOperation profileOperation;
@ -132,10 +144,12 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
try {
Connection conn = OperationManagementDAOFactory.getConnection();
String sql = "Select po.OPERATION_ID, ENABLED, OPERATION_DETAILS from DM_PROFILE_OPERATION po " +
"INNER JOIN " +
"(Select * From DM_ENROLMENT_OP_MAPPING WHERE ENROLMENT_ID=? " +
"AND STATUS=?) dm ON dm.OPERATION_ID = po.OPERATION_ID";
String sql = "SELECT o.ID, po1.ENABLED, po1.STATUS, o.TYPE, o.CREATED_TIMESTAMP, o.RECEIVED_TIMESTAMP, " +
"o.OPERATION_CODE, po1.OPERATION_DETAILS " +
"FROM (SELECT po.OPERATION_ID, po.ENABLED, po.OPERATION_DETAILS, dm.STATUS " +
"FROM DM_PROFILE_OPERATION po INNER JOIN (SELECT ENROLMENT_ID, OPERATION_ID, STATUS FROM DM_ENROLMENT_OP_MAPPING " +
"WHERE ENROLMENT_ID = ? AND STATUS = ?) dm ON dm.OPERATION_ID = po.OPERATION_ID) po1 " +
"INNER JOIN DM_OPERATION o ON po1.OPERATION_ID = o.ID ";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, enrolmentId);
@ -147,9 +161,20 @@ public class ProfileOperationDAOImpl extends GenericOperationDAOImpl {
byte[] operationDetails = rs.getBytes("OPERATION_DETAILS");
bais = new ByteArrayInputStream(operationDetails);
ois = new ObjectInputStream(bais);
profileOperation = (ProfileOperation) ois.readObject();
profileOperation.setStatus(status);
operationList.add(profileOperation);
Object obj = ois.readObject();
if(obj instanceof String){
profileOperation = new ProfileOperation();
profileOperation.setCode(rs.getString("OPERATION_CODE"));
profileOperation.setId(rs.getInt("ID"));
profileOperation.setCreatedTimeStamp(rs.getString("CREATED_TIMESTAMP"));
profileOperation.setPayLoad(obj);
profileOperation.setStatus(status);
operationList.add(profileOperation);
} else {
profileOperation = (ProfileOperation) obj;
profileOperation.setStatus(status);
operationList.add(profileOperation);
}
}
} catch (IOException e) {

@ -86,11 +86,18 @@ public class PushNotificationSchedulerTask implements Runnable {
NotificationStrategy notificationStrategy = provider.getNotificationStrategyByDeviceType
(operationMapping.getDeviceIdentifier().getType());
// Send the push notification on given strategy
notificationStrategy.execute(new NotificationContext(operationMapping.getDeviceIdentifier(),
provider.getOperation(operationMapping.getDeviceIdentifier().getType(), operationMapping
.getOperationId())));
operationMapping.setPushNotificationStatus(Operation.PushNotificationStatus.COMPLETED);
operationsCompletedList.add(operationMapping);
if (notificationStrategy != null) {
notificationStrategy.execute(new NotificationContext(operationMapping.getDeviceIdentifier(),
provider.getOperation(operationMapping.getDeviceIdentifier().getType(), operationMapping
.getOperationId())));
operationMapping.setPushNotificationStatus(Operation.PushNotificationStatus.COMPLETED);
operationsCompletedList.add(operationMapping);
} else {
if (log.isDebugEnabled()) {
log.debug("Tenant '" + PrivilegedCarbonContext.getThreadLocalCarbonContext()
.getTenantDomain() + "' does not have push notification strategy.");
}
}
} catch (DeviceManagementException e) {
log.error("Error occurred while getting notification strategy for operation mapping " +
operationMapping.getDeviceIdentifier().getType(), e);
@ -121,7 +128,7 @@ public class PushNotificationSchedulerTask implements Runnable {
log.debug("Push notification job running completed.");
}
} catch (Throwable cause) {
log.error("PushNotificationSchedulerTask failed due to " + cause);
log.error("PushNotificationSchedulerTask failed due to " + cause.getMessage(), cause);
}
}
}

@ -24,6 +24,7 @@ import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.DeviceApplicationMapping;
@ -552,6 +553,9 @@ public interface DeviceManagementProviderService {
Operation getNextPendingOperation(DeviceIdentifier deviceId) throws OperationManagementException;
Operation getNextPendingOperation(DeviceIdentifier deviceId, long notNowOperationFrequency)
throws OperationManagementException;
void updateOperation(DeviceIdentifier deviceId, Operation operation) throws OperationManagementException;
boolean updateProperties(DeviceIdentifier deviceId, List<Device.Property> properties) throws DeviceManagementException;
@ -587,6 +591,8 @@ public interface DeviceManagementProviderService {
int getDeviceMonitoringFrequency(String deviceType);
OperationMonitoringTaskConfig getDeviceMonitoringConfig(String deviceType);
boolean isDeviceMonitoringEnabled(String deviceType);
PolicyMonitoringManager getPolicyMonitoringManager(String deviceType);

@ -1452,8 +1452,15 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
@Override
public Operation getNextPendingOperation(DeviceIdentifier deviceId) throws OperationManagementException {
// // setting notNowOperationFrequency to -1 to avoid picking notnow operations
return pluginRepository.getOperationManager(deviceId.getType(), this.getTenantId())
.getNextPendingOperation(deviceId);
.getNextPendingOperation(deviceId, -1);
}
public Operation getNextPendingOperation(DeviceIdentifier deviceId, long notNowOperationFrequency)
throws OperationManagementException {
return pluginRepository.getOperationManager(deviceId.getType(), this.getTenantId())
.getNextPendingOperation(deviceId, notNowOperationFrequency);
}
@Override
@ -1597,6 +1604,13 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
return operationMonitoringTaskConfig.getFrequency();
}
@Override
public OperationMonitoringTaskConfig getDeviceMonitoringConfig(String deviceType) {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
DeviceManagementService dms = pluginRepository.getDeviceManagementService(deviceType, tenantId);
return dms.getOperationMonitoringConfig();
}
@Override
public boolean isDeviceMonitoringEnabled(String deviceType) {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();

@ -26,14 +26,23 @@ import java.util.Map;
public class Utils {
public static Map<String, Long> getTenantedTaskOperationMap(Map<Integer, Map<String, Long>> map) {
public static Map<String, Long> getTenantedTaskOperationMap(Map<Integer, Map<String, Map<String, Long>>> map,
String deviceType) {
Map<String, Long> taskMap = new HashMap<>();
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
if (map.containsKey(tenantId)) {
return map.get(tenantId);
if (map.get(tenantId).containsKey(deviceType)) {
return map.get(tenantId).get(deviceType);
} else {
Map<String, Map<String, Long>> existingTenantMap = map.get(tenantId);
existingTenantMap.put(deviceType, taskMap);
return taskMap;
}
} else {
Map<String, Long> mp = new HashMap<>();
map.put(tenantId, mp);
return mp;
HashMap<String, Map<String, Long>> typeMap = new HashMap<>();
typeMap.put(deviceType, taskMap);
map.put(tenantId, typeMap);
return taskMap;
}
}

@ -16,16 +16,15 @@
* under the License.
*/
package org.wso2.carbon.device.mgt.core.task.impl;
import com.google.gson.Gson;
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.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.core.task.DeviceMgtTaskException;
import org.wso2.carbon.device.mgt.core.task.DeviceTaskManager;
import org.wso2.carbon.ntask.core.Task;
@ -38,20 +37,13 @@ public class DeviceDetailsRetrieverTask implements Task {
private static Log log = LogFactory.getLog(DeviceDetailsRetrieverTask.class);
private String deviceType;
private String oppConfig;
private OperationMonitoringTaskConfig operationMonitoringTaskConfig;
private boolean executeForTenants = false;
private final String IS_CLOUD = "is.cloud";
private DeviceManagementProviderService deviceManagementProviderService;
@Override
public void setProperties(Map<String, String> map) {
deviceType = map.get("DEVICE_TYPE");
oppConfig = map.get("OPPCONFIG");
Gson gson = new Gson();
operationMonitoringTaskConfig = gson.fromJson(oppConfig,
OperationMonitoringTaskConfig.class);
}
@Override
@ -60,29 +52,33 @@ public class DeviceDetailsRetrieverTask implements Task {
@Override
public void execute() {
deviceManagementProviderService = DeviceManagementDataHolder.getInstance()
.getDeviceManagementProvider();
OperationMonitoringTaskConfig operationMonitoringTaskConfig = deviceManagementProviderService
.getDeviceMonitoringConfig(deviceType);
if(System.getProperty(IS_CLOUD) != null && Boolean.parseBoolean(System.getProperty(IS_CLOUD))){
if (System.getProperty(IS_CLOUD) != null && Boolean.parseBoolean(System.getProperty(IS_CLOUD))) {
executeForTenants = true;
}
if(executeForTenants){
this.executeForAllTenants();
if (executeForTenants) {
this.executeForAllTenants(operationMonitoringTaskConfig);
} else {
if (log.isDebugEnabled()) {
log.debug("Device details retrieving task started to run.");
}
DeviceTaskManager deviceTaskManager = new DeviceTaskManagerImpl(deviceType,
operationMonitoringTaskConfig);
DeviceTaskManager deviceTaskManager = new DeviceTaskManagerImpl(deviceType, operationMonitoringTaskConfig);
//pass the configurations also from here, monitoring tasks
try {
deviceTaskManager.addOperations();
if (deviceManagementProviderService.isDeviceMonitoringEnabled(deviceType)) {
deviceTaskManager.addOperations();
}
} catch (DeviceMgtTaskException e) {
log.error(
"Error occurred while trying to add the operations to device to retrieve device details.", e);
log.error("Error occurred while trying to add the operations to device to retrieve device details.", e);
}
}
}
private void executeForAllTenants() {
private void executeForAllTenants(OperationMonitoringTaskConfig operationMonitoringTaskConfig) {
if (log.isDebugEnabled()) {
log.debug("Device details retrieving task started to run for all tenants.");
@ -101,7 +97,9 @@ public class DeviceDetailsRetrieverTask implements Task {
operationMonitoringTaskConfig);
//pass the configurations also from here, monitoring tasks
try {
deviceTaskManager.addOperations();
if (deviceManagementProviderService.isDeviceMonitoringEnabled(deviceType)) {
deviceTaskManager.addOperations();
}
} catch (DeviceMgtTaskException e) {
log.error("Error occurred while trying to add the operations to " +
"device to retrieve device details.", e);

@ -36,17 +36,13 @@ import org.wso2.carbon.device.mgt.core.task.DeviceTaskManager;
import org.wso2.carbon.device.mgt.core.task.Utils;
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
public class DeviceTaskManagerImpl implements DeviceTaskManager {
private static Log log = LogFactory.getLog(DeviceTaskManagerImpl.class);
private String deviceType;
private static Map<Integer, Map<String, Long>> map = new HashMap<>();
static volatile Map<Integer, Map<String, Map<String, Long>>> map = new HashMap<>();
private OperationMonitoringTaskConfig operationMonitoringTaskConfig;
public DeviceTaskManagerImpl(String deviceType,
@ -126,7 +122,7 @@ public class DeviceTaskManagerImpl implements DeviceTaskManager {
List<String> opNames = new ArrayList<>();
Long milliseconds = System.currentTimeMillis();
int frequency = this.getTaskFrequency();
Map<String, Long> mp = Utils.getTenantedTaskOperationMap(map);
Map<String, Long> mp = Utils.getTenantedTaskOperationMap(map, deviceType);
for (MonitoringOperation top : monitoringOperations) {
if (!mp.containsKey(top.getTaskName())) {

@ -107,10 +107,12 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
throws DeviceMgtTaskException {
try {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
TaskService taskService = DeviceManagementDataHolder.getInstance().getTaskService();
if (taskService.isServerInit()) {
TaskManager taskManager = taskService.getTaskManager(TASK_TYPE);
taskManager.deleteTask(deviceType);
String taskName = deviceType + String.valueOf(tenantId);
taskManager.deleteTask(taskName);
}
} catch (TaskException e) {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
@ -131,8 +133,8 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
TaskManager taskManager = taskService.getTaskManager(TASK_TYPE);
if (taskManager.isTaskScheduled(deviceType)) {
taskManager.deleteTask(deviceType);
String taskName = deviceType + String.valueOf(tenantId);
taskManager.deleteTask(taskName);
TaskInfo.TriggerInfo triggerInfo = new TaskInfo.TriggerInfo();
triggerInfo.setIntervalMillis(operationMonitoringTaskConfig.getFrequency());
triggerInfo.setRepeatCount(-1);
@ -140,7 +142,7 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
Map<String, String> properties = new HashMap<>();
properties.put(TENANT_ID, String.valueOf(tenantId));
TaskInfo taskInfo = new TaskInfo(deviceType, TASK_CLASS, properties, triggerInfo);
TaskInfo taskInfo = new TaskInfo(taskName, TASK_CLASS, properties, triggerInfo);
taskManager.registerTask(taskInfo);
taskManager.rescheduleTask(taskInfo.getName());

@ -43,7 +43,7 @@ public class TestDeviceManagementService implements DeviceManagementService {
public TestDeviceManagementService(String deviceType, String tenantDomain) {
providerType = deviceType;
this.tenantDomain = tenantDomain;
this.operationCode = "default";
this.operationCode = "DEVICE_INFO";
}
@Override

@ -143,7 +143,7 @@ public class GeoLocationProviderServiceTest {
Assert.assertEquals(geoFenceNode.getStationaryTime(), SAMPLE_STATIONARY_TIME);
}
private void initMocks() throws JWTClientException, RemoteException {
private void initMocks() throws Exception {
EventProcessorAdminServiceStub mockEventProcessorAdminServiceStub = Mockito.mock(EventProcessorAdminServiceStub.class);
geoLocationProviderServiceImpl = Mockito.mock(GeoLocationProviderServiceImpl.class, Mockito.CALLS_REAL_METHODS);
mockExecutionPlanConfigurationDto[0] = Mockito.mock(ExecutionPlanConfigurationDto.class);

@ -18,6 +18,8 @@
package org.wso2.carbon.device.mgt.core.operation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.powermock.api.mockito.PowerMockito;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
@ -67,6 +69,7 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
private static final String POLICY_OPERATION_CODE = "POLICY-TEST";
private static final String CONFIG_OPERATION_CODE = "CONFIG-TEST";
private static final String PROFILE_OPERATION_CODE = "PROFILE-TEST";
private static final String PROFILE_NOTIFICATION_CODE = "NOTIFICATION";
private static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";
private static final int NO_OF_DEVICES = 5;
private static final String ADMIN_USER = "admin";
@ -80,6 +83,8 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
private Activity commandActivity;
private long commandActivityBeforeUpdatedTimestamp;
private static Log log = LogFactory.getLog(OperationManagementTests.class);
@BeforeClass
public void init() throws Exception {
for (int i = 0; i < NO_OF_DEVICES; i++) {
@ -101,14 +106,14 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
}
this.deviceMgmtProvider = DeviceManagementDataHolder.getInstance().getDeviceManagementProvider();
deviceManagementService = new TestDeviceManagementService(DEVICE_TYPE,
MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
this.operationMgtService = PowerMockito.spy(new OperationManagerImpl(DEVICE_TYPE, deviceManagementService));
PowerMockito.when(this.operationMgtService, "getNotificationStrategy").thenReturn(new TestNotificationStrategy());
}
@Test
public void addCommandOperation() throws DeviceManagementException, OperationManagementException,
InvalidDeviceException {
InvalidDeviceException {
OperationManager operationManager = PowerMockito.spy(
new OperationManagerImpl(DEVICE_TYPE, deviceManagementService));
try {
@ -154,7 +159,7 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
public void addEmptyDevicesCommandOperation() throws DeviceManagementException, OperationManagementException,
InvalidDeviceException {
this.operationMgtService.addOperation(getOperation(new CommandOperation(), Operation.Type.COMMAND,
COMMAND_OPERATION_CODE), new ArrayList<>());
COMMAND_OPERATION_CODE), new ArrayList<>());
}
@Test(expectedExceptions = InvalidDeviceException.class)
@ -205,8 +210,10 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
@Test(dependsOnMethods = "addConfigOperation")
public void addProfileOperation() throws DeviceManagementException, OperationManagementException,
InvalidDeviceException {
Activity activity = this.operationMgtService.addOperation(getOperation(new ProfileOperation(),
Operation.Type.PROFILE, PROFILE_OPERATION_CODE),
Operation opp = getOperation(new ProfileOperation(),
Operation.Type.PROFILE, PROFILE_NOTIFICATION_CODE);
opp.setPayLoad("{\"messageText\":\"xyz\",\"messageTitle\":\"abc\"}");
Activity activity = this.operationMgtService.addOperation(opp ,
this.deviceIds);
validateOperationResponse(activity, ActivityStatus.Status.PENDING);
}
@ -360,6 +367,35 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
Assert.assertTrue(operation.getType().equals(Operation.Type.POLICY));
}
@Test(dependsOnMethods = "getNextPendingOperation")
public void getNextNotNowOperation() throws OperationManagementException {
//This is required to introduce a delay for the update operation of the device.
try {
Thread.sleep(2000);
} catch (InterruptedException ignored) {
}
DeviceIdentifier deviceIdentifier = this.deviceIds.get(0);
Operation operation = this.operationMgtService.getNextPendingOperation(deviceIdentifier);
int operationId = operation.getId();
operation.setStatus(Operation.Status.NOTNOW);
operation.setOperationResponse("The operation is successfully completed");
this.operationMgtService.updateOperation(deviceIdentifier, operation);
//This is required to introduce a delay for the update operation of the device.
try {
Thread.sleep(2000);
} catch (InterruptedException ignored) {
}
operation = this.operationMgtService.getNextPendingOperation(deviceIdentifier);
Assert.assertTrue(operation.getId() != operationId, "Fetched the incorrect operation");
log.info("Waiting 10000ms for NotNow operation to be fetched");
try {
Thread.sleep(10000);
} catch (InterruptedException ignored) {
}
operation = this.operationMgtService.getNextPendingOperation(deviceIdentifier, 7000);
Assert.assertTrue(operation.getId() == operationId, "Fetched the incorrect NotNow operation");
}
@Test(dependsOnMethods = "updateOperation", expectedExceptions = OperationManagementException.class)
public void getNextPendingOperationAsNonAdmin() throws OperationManagementException {
startTenantFlowAsNonAdmin();
@ -403,7 +439,7 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
DeviceIdentifier deviceIdentifier = this.deviceIds.get(0);
List operation = this.operationMgtService.getOperationsByDeviceAndStatus(deviceIdentifier,
Operation.Status.PENDING);
Assert.assertEquals(operation.size(), 3);
Assert.assertEquals(operation.size(), 2);
}
@Test(dependsOnMethods = "getOperationByDeviceAndOperationId", expectedExceptions = OperationManagementException.class)
@ -476,8 +512,8 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
public void getActivityCountUpdatedAfter() throws OperationManagementException, ParseException {
int activityCount = this.operationMgtService.getActivityCountUpdatedAfter
(this.commandActivityBeforeUpdatedTimestamp / 1000);
Assert.assertTrue(activityCount == 1,
"The activities updated after the created should be 1");
Assert.assertTrue(activityCount == 2,
"The activities updated after the created should be 2");
}
@Test
@ -531,7 +567,7 @@ public class OperationManagementTests extends BaseDeviceManagementTest {
expectedExceptions = OperationManagementException.class)
public void getUpdateOperationForInvalidDevice() throws DeviceManagementException, OperationManagementException {
this.operationMgtService.updateOperation(new DeviceIdentifier(INVALID_DEVICE, DEVICE_TYPE),
getOperation(new CommandOperation(), Operation.Type.COMMAND, COMMAND_OPERATION_CODE));
getOperation(new CommandOperation(), Operation.Type.COMMAND, COMMAND_OPERATION_CODE));
}
@Test(dependsOnMethods = "getUpdateOperationForInvalidDevice",

@ -19,6 +19,7 @@ package org.wso2.carbon.device.mgt.core.task;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
@ -27,6 +28,8 @@ import org.testng.annotations.Test;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManager;
@ -60,8 +63,6 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
private static final Log log = LogFactory.getLog(DeviceTaskManagerTest.class);
private static final String NEW_DEVICE_TYPE = "NEW-DEVICE-TYPE";
private static final String DEVICE_DETAIL_RETRIEVER_OPPCONFIG = "{\"isEnabled\":true,\"frequency\":60000," +
"\"monitoringOperation\":[{\"taskName\":\"DEVICE_INFO\",\"recurrentTimes\":2}]}";
private List<DeviceIdentifier> deviceIds;
private DeviceTaskManager deviceTaskManager;
private DeviceManagementProviderService deviceMgtProviderService;
@ -76,7 +77,6 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
}
List<Device> devices = TestDataHolder.generateDummyDeviceData(this.deviceIds);
this.deviceMgtProviderService = new DeviceManagementProviderServiceImpl();
DeviceManagementServiceComponent.notifyStartupListeners();
DeviceManagementDataHolder.getInstance().setDeviceManagementProvider(this.deviceMgtProviderService);
DeviceManagementDataHolder.getInstance()
@ -152,7 +152,6 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
DeviceDetailsRetrieverTask deviceDetailsRetrieverTask = new DeviceDetailsRetrieverTask();
Map<String, String> map = new HashMap<>();
map.put("DEVICE_TYPE", TestDataHolder.TEST_DEVICE_TYPE);
map.put("OPPCONFIG", DEVICE_DETAIL_RETRIEVER_OPPCONFIG);
deviceDetailsRetrieverTask.setProperties(map);
deviceDetailsRetrieverTask.execute();
for (DeviceIdentifier deviceId : deviceIds) {
@ -172,7 +171,6 @@ public class DeviceTaskManagerTest extends BaseDeviceManagementTest {
System.setProperty("is.cloud", "true");
Map<String, String> map = new HashMap<>();
map.put("DEVICE_TYPE", TestDataHolder.TEST_DEVICE_TYPE);
map.put("OPPCONFIG", DEVICE_DETAIL_RETRIEVER_OPPCONFIG);
deviceDetailsRetrieverTask.setProperties(map);
deviceDetailsRetrieverTask.execute();
for (DeviceIdentifier deviceId : deviceIds) {

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -15,6 +15,9 @@
"iOSConfigRoot": "%https.ip%/ios-enrollment/",
"iOSAPIRoot": "%https.ip%/ios/",
"adminService": "%https.ip%",
"deviceInfoServiceAPI" : "/api/device-mgt/%device-type%/v1.0/admin/devices/info",
"deviceLocationServiceAPI" : "/api/device-mgt/%device-type%/v1.0/admin/devices/location",
"iOSDeviceInfoServiceAPI" : "/api/device-mgt/%device-type%/v1.0/admin/devices/info",
"gatewayEnabled": true,
"oauthProvider": {
"appRegistration": {

@ -39,25 +39,25 @@ $("#view-search-param").click(function () {
$("#view-search-param").addClass('hidden');
});
var dynamicForm = '<div class="dynamic-search-param row"><div class="row"><a class="close-button-div icon fw fw-error">' +
'</a></div><div class="form-group wr-input-control col-md-2"><label class="wr-input-label ">State</label>' +
'<select class="state no-tag form-control select2-custom"><option>AND</option><option>OR</option></select></div><div ' +
'class="form-group wr-input-control col-md-4"><label class="wr-input-label ">Key</label><select class=' +
'"txt-key form-control select2-custom"><option value = "deviceModel">Device Model' +
'</option><option value = "PhoneNumber">Phone Number</option><option value = "vendor">Vendor</option><option value = "osVersion">OS Version' +
'</option><option value = "batteryLevel">Battery Level</option><option value =' +
' "internalTotalMemory">Internal Total Memory</option> <option value ="internalAvailableMemory">' +
'Internal Available Memory</option> <option value = "externalTotalMemory">External Total Memory</option>' +
' <option value = "externalAvailableMemory">External Available Memory' +
'</option> <option value = "connectionType">Connection Type</option> <option value =' +
' "ssid">SSID</option><option value = "cpuUsage">CPU Usage</option><option value = "totalRAMMemory">' +
'Total RAM Memory</option> <option value = "availableRAMMemory">Available RAM Memory</option>' +
'<option value = "pluggedIn">Plugged In</option></select></div>' +
'<div class="form-group wr-input-control col-md-2">' +
'<label class="wr-input-label ">Operator</label><select id = "operators" class="form-control' +
' select2-custom no-tag operator"><option>=</option><option> !=</option><option> %</option>' +
'</select></div><div class="form-group ' + 'wr-input-control col-md-4"><label class="wr-input-label">Value</label>' +
'<input type="text" class="form-control txt-value"/></div></div>';
var dynamicForm = '<div class="dynamic-search-param row"><div class="row"><a class="close-button-div icon fw fw-error">'
+ '</a></div><div class="form-group wr-input-control col-md-2"><label class="wr-input-label ">State</label>'
+ '<select class="state no-tag form-control select2-custom"><option>AND</option><option>OR</option></select></div>'
+ '<div class="form-group wr-input-control col-md-4"><label class="wr-input-label ">Key</label><select class='
+ '"txt-key form-control select2-custom"><option value = "deviceModel">Device Model</option><option value = '
+ '"PhoneNumber">Phone Number</option><option value = "vendor">Vendor</option><option value = "osVersion">'
+ 'OS Version</option><option value = "batteryLevel">Battery Level</option><option value ='
+ ' "internalTotalMemory">Internal Total Memory</option><option value ="internalAvailableMemory">'
+ 'Internal Available Memory</option><option value = "externalTotalMemory">External Total Memory</option>'
+ '<option value = "externalAvailableMemory">External Available Memory'
+ '</option><option value = "connectionType">Connection Type</option> <option value ='
+ ' "ssid">SSID</option><option value= "IMEI">IMEI</option><option value = "cpuUsage">CPU Usage</option><option '
+ 'value = "totalRAMMemory">Total RAM Memory</option><option value = "availableRAMMemory">Available RAM Memory'
+ '</option><option value = "pluggedIn">Plugged In</option></select></div>'
+ '<div class="form-group wr-input-control col-md-2">'
+ '<label class="wr-input-label ">Operator</label><select id = "operators" class="form-control'
+ ' select2-custom no-tag operator"><option>=</option><option>!=</option><option>%</option>'
+ '</select></div><div class="form-group ' + 'wr-input-control col-md-4"><label class="wr-input-label">Value'
+ '</label><input type="text" class="form-control txt-value"/></div></div>';
var nonNumericKeyValuePair = ["deviceModel", "vendor", "osVersion", "connectionType", "ssid", "pluggedIn"];

@ -82,7 +82,7 @@
required.</label>
</div>
<div class="col-md-2 form-group wr-input-control col-fixed-right">
<button id="device-search-btn add-custom-param" class="wr-btn">Search</button>
<button id="device-search-btn" class="wr-btn">Search</button>
</div>
</div>

@ -275,10 +275,11 @@ function loadUsers() {
$(data.users).each(function (index) {
objects.push({
filter: htmlspecialchars(data.users[index].username),
firstname: htmlspecialchars(data.users[index].firstname) ? htmlspecialchars(data.users[index].firstname) : "",
lastname: htmlspecialchars(data.users[index].lastname) ? htmlspecialchars(data.users[index].lastname) : "",
username: htmlspecialchars(data.users[index].username),
firstName: htmlspecialchars(data.users[index].firstname) ? htmlspecialchars(data.users[index].firstname) : "",
lastName: htmlspecialchars(data.users[index].lastname) ? htmlspecialchars(data.users[index].lastname) : "",
emailAddress: htmlspecialchars(data.users[index].emailAddress) ? htmlspecialchars(data.users[index].emailAddress) : "",
namePattern: htmlspecialchars(data.users[index].firstname) + ' ' + htmlspecialchars(data.users[index].lastname),
DT_RowId: "user-" + htmlspecialchars(data.users[index].username)
})
});
@ -304,49 +305,78 @@ function loadUsers() {
//noinspection JSUnusedLocalSymbols
var columns = [
{
targets: 0,
class: "remove-padding icon-only content-fill",
data: null,
render: function (data, type, row, meta) {
return '<div class="thumbnail icon viewEnabledIcon" data-url="' + context + '/user/view?username=' + data.filter + '">' +
data: 'username',
render: function (username, type, row, meta) {
return '<div class="thumbnail icon viewEnabledIcon" data-url="' + context + '/user/view?username=' + username + '">' +
'<i class="square-element text fw fw-user" style="font-size: 74px;"></i>' +
'</div>';
}
},
{
targets: 1,
class: "",
data: null,
render: function (data, type, row, meta) {
if (!data.firstname && !data.lastname) {
data: 'namePattern',
render: function (namePattern, type, row, meta) {
if (!namePattern) {
return "";
} else if (data.firstname && data.lastname) {
return "<h4>" + data.firstname + " " + data.lastname + "</h4>";
} else {
return "<h4>" + namePattern + "</h4>";
}
}
},
{
targets: 2,
class: "remove-padding-top",
data: 'filter',
render: function (filter, type, row, meta) {
return '<i class="fw-user"></i>' + filter;
data: 'username',
render: function (username, type, row, meta) {
return '<i class="fw-user"></i>' + username;
}
},
{
targets: 3,
class: "hidden",
data: 'firstName',
render: function (firstName, type, row, meta) {
if (!firstName) {
return "";
} else if (firstName) {
return "<h4>" + firstName + "</h4>";
}
}
},
{
targets: 4,
class: "hidden",
data: 'lastName',
render: function (lastName, type, row, meta) {
if (!lastName) {
return "";
} else if (lastName) {
return "<h4>" + lastName + "</h4>";
}
}
},
{
targets: 5,
class: "remove-padding-top",
data: null,
render: function (data, type, row, meta) {
if (!data.emailAddress) {
data: 'emailAddress',
render: function (emailAddress, type, row, meta) {
if (!emailAddress) {
return "";
} else {
return "<a href='mailto:" + data.emailAddress + "' ><i class='fw-mail'></i>" + data.emailAddress + "</a>";
return "<a href='mailto:" + emailAddress + "' ><i class='fw-mail'></i>" + emailAddress + "</a>";
}
}
},
{
targets: 6,
class: "text-right content-fill text-left-on-grid-view no-wrap tooltip-overflow-fix",
data: null,
render: function (data, type, row, meta) {
var editbtn = '<a data-toggle="tooltip" data-placement="top" title="Edit User"href="' + context +
'/user/edit?username=' + encodeURIComponent(data.filter) + '" data-username="' + data.filter + '" ' +
'/user/edit?username=' + encodeURIComponent(data.username) + '" data-username="' + data.username + '" ' +
'data-click-event="edit-form" ' +
'class="btn padding-reduce-on-grid-view edit-user-link" data-placement="top" data-toggle="tooltip" data-original-title="Edit"> ' +
'<span class="fw-stack"> ' +
@ -354,18 +384,18 @@ function loadUsers() {
'<i class="fw fw-edit fw-stack-1x"></i>' +
'</span><span class="hidden-xs hidden-on-grid-view">Edit</span></a>';
var resetPasswordbtn = '<a data-toggle="tooltip" data-placement="top" title="Reset Password" href="#" data-username="' + data.filter + '" data-userid="' + data.filter + '" ' +
var resetPasswordbtn = '<a data-toggle="tooltip" data-placement="top" title="Reset Password" href="#" data-username="' + data.username + '" data-userid="' + data.username + '" ' +
'data-click-event="edit-form" ' +
'onclick="javascript:resetPassword(\'' + data.filter + '\')" ' +
'onclick="javascript:resetPassword(\'' + data.username + '\')" ' +
'class="btn padding-reduce-on-grid-view remove-user-link" data-placement="top" data-toggle="tooltip" data-original-title="Reset Password">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
'<i class="fw fw-key fw-stack-1x"></i>' +
'</span><span class="hidden-xs hidden-on-grid-view">Reset Password</span></a>';
var removebtn = '<a data-toggle="tooltip" data-placement="top" title="Remove User" href="#" data-username="' + data.filter + '" data-userid="' + data.filter + '" ' +
var removebtn = '<a data-toggle="tooltip" data-placement="top" title="Remove User" href="#" data-username="' + data.username + '" data-userid="' + data.username + '" ' +
'data-click-event="remove-form" ' +
'onclick="javascript:removeUser(\'' + data.filter + '\')" ' +
'onclick="javascript:removeUser(\'' + data.username + '\')" ' +
'class="btn padding-reduce-on-grid-view remove-user-link" data-placement="top" data-toggle="tooltip" data-original-title="Remove">' +
'<span class="fw-stack">' +
'<i class="fw fw-circle-outline fw-stack-2x"></i>' +
@ -375,20 +405,19 @@ function loadUsers() {
var returnbtnSet = '';
var adminUser = $("#user-table").data("user");
var currentUser = $("#user-table").data("logged-user");
if ($("#can-edit").length > 0 && adminUser !== data.filter) {
if ($("#can-edit").length > 0 && adminUser !== data.username) {
returnbtnSet = returnbtnSet + editbtn;
}
if ($("#can-reset-password").length > 0 && adminUser !== data.filter) {
if ($("#can-reset-password").length > 0 && adminUser !== data.username) {
returnbtnSet = returnbtnSet + resetPasswordbtn;
}
if ($("#can-remove").length > 0 && adminUser !== data.filter && currentUser !== data.filter) {
if ($("#can-remove").length > 0 && adminUser !== data.username && currentUser !== data.username) {
returnbtnSet = returnbtnSet + removebtn;
}
return returnbtnSet;
}
}
];
var options = {
@ -400,18 +429,31 @@ function loadUsers() {
"sorting": false
};
$('#user-grid').datatables_extended_serverside_paging(settings, '/api/device-mgt/v1.0/users', dataFilter, columns, fnCreatedRow, null, options);
$('#user-grid').datatables_extended_serverside_paging(
settings,
'/api/device-mgt/v1.0/users/search',
dataFilter,
columns,
fnCreatedRow,
null,
options
);
$(loadingContentView).hide();
}
$(document).ready(function () {
loadUsers();
$(function () {
$('[data-toggle="tooltip"]').tooltip()
});
if (!$("#can-invite").val()) {
$("#invite-user-button").remove();
}
$("#user-grid_filter").hide();
});

@ -75,10 +75,25 @@
id="user-grid">
<thead>
<tr class="sort-row">
<th class="no-sort"></th>
<th class="no-sort"></th>
<th>By Username</th>
<th>By First Name</th>
<th>By Last Name</th>
<th>By Email</th>
<th class="no-sort"></th>
</tr>
<tr class="filter-row filter-box">
<th class="no-sort"></th>
<th class="no-sort"></th>
<th data-for="By Username" class="text-filter"></th>
<th data-for="By First Name" class="text-filter"></th>
<th data-for="By Last Name" class="text-filter"></th>
<th data-for="By Email" class="text-filter"></th>
<th class="no-sort"></th>
</tr>
<tr class="bulk-action-row hidden">
<th colspan="3">
<th colspan="7">
<ul class="tiles">
<li class="square">
<a id="invite-user-link" href="#" data-click-event="remove-form" class="btn square-element"
@ -101,6 +116,9 @@
<div class="sort-title">Sort By</div>
<div class="sort-options">
<a href="#">By Username</a>
<a href="#">By First Name</a>
<a href="#">By Last Name</a>
<a href="#">By Email</a>
</div>
</div>

@ -279,10 +279,12 @@ var getProviderData = function (timeFrom, timeTo) {
var serviceUrl = '/api/device-mgt/v1.0/geo-services/stats/' + deviceType + '/' + deviceId + '?from=' + timeFrom + '&to=' + timeTo;
invokerUtil.get(serviceUrl,
function (data) {
if(data === ""){showCurrentLocation(tableData);}
if (data === "") {
showCurrentLocation(tableData);
}
tableData = JSON.parse(data);
if (tableData.length === 0) {
showCurrentLocation(tableData);
showCurrentLocation(tableData);
}
}, function (message) {
showCurrentLocation(tableData);
@ -552,5 +554,5 @@ function formatDate(date) {
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? '0'+minutes : minutes;
var strTime = hours + ':' + minutes + ' ' + ampm;
return date.getDate() + "/" + date.getMonth()+1 + "/" + date.getFullYear() + " " + strTime;
return date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear() + " " + strTime;
}

@ -91,7 +91,7 @@ function initializeGeoLocation(geoFencingEnabled) {
InitSpatialObject(geoFencingEnabled);
} else {
noty({text: 'Invalid Access! No device information provided to track!', type: 'error'});
noty({text: 'Invalid Access! No device information provided to track!', type: 'error', timeout: 10000});
}
}
@ -493,7 +493,7 @@ GeoAreaObject.prototype.update = function (geoJSON) {
};
function notifyAlert(message) {
noty({text: "Alert: " + message, type: 'warning'});
noty({text: "Alert: " + message, type: 'warning', timeout: 10000});
}
function Alert(type, message, level) {
@ -505,7 +505,7 @@ function Alert(type, message, level) {
this.level = 'information';
this.notify = function () {
noty({text: this.type + ' ' + this.message, type: level});
noty({text: this.type + ' ' + this.message, type: level, timeout: 10000});
}
}
@ -575,7 +575,7 @@ var webSocketOnAlertError = function (e) {
wsURL = wsURL.replace("wss://","https://");
var uriParts = wsURL.split("/");
wsURL = uriParts[0] + "//" + uriParts[2];
noty({text: 'Something went wrong when trying to connect to <b>' + wsURL + '<b/>', type: 'error'});
noty({text: 'Something went wrong when trying to connect to <b>' + wsURL + '<b/>', type: 'error', timeout: 10000});
};
var webSocketSpatialOnOpen = function () {
@ -606,7 +606,7 @@ var webSocketSpatialOnError = function (err) {
wsURL = wsURL.replace("wss://","https://");
var uriParts = wsURL.split("/");
wsURL = uriParts[0] + "//" + uriParts[2];
noty({text: 'Something went wrong when trying to connect to <b>' + wsURL + '<b/>', type: 'error'});
noty({text: 'Something went wrong when trying to connect to <b>' + wsURL + '<b/>', type: 'error', timeout: 10000});
};

@ -419,7 +419,7 @@ function formatDate(date) {
hours = hours ? hours : 12; // the hour '0' should be '12'
minutes = minutes < 10 ? '0' + minutes : minutes;
var strTime = hours + ':' + minutes + ' ' + ampm;
return date.getDate() + "/" + date.getMonth() + 1 + "/" + date.getFullYear() + " " + strTime;
return date.getDate() + "/" + (date.getMonth() + 1) + "/" + date.getFullYear() + " " + strTime;
}
function timeSince(date) {

@ -113,8 +113,7 @@
conf: appConfigurations
},
uri: errorPage.definition[constants.PAGE_DEFINITION_URI],
uriParams: {},
user: utils.getCurrentUser()
uriParams: {}
};
var templateContext = {status: status, message: message};
var renderer = require("/lib/dynamic-files-renderer.js").renderer;

@ -23,7 +23,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<artifactId>device-mgt</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>email-sender</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

@ -22,13 +22,13 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>identity-extensions</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>org.wso2.carbon.device.mgt.oauth.extensions</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - OAuth Extensions</name>
<url>http://wso2.org</url>

@ -21,7 +21,7 @@
<parent>
<artifactId>identity-extensions</artifactId>
<groupId>org.wso2.carbon.devicemgt</groupId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -22,7 +22,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>identity-extensions</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

@ -22,12 +22,11 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>carbon-devicemgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>identity-extensions</artifactId>
<packaging>pom</packaging>
<name>WSO2 Carbon - Identity Extensions Component</name>

@ -22,14 +22,14 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>policy-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.complex.policy.decision.point</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Policy Decision Point</name>
<description>WSO2 Carbon - Policy Decision Point</description>

@ -3,14 +3,14 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>policy-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.policy.decision.point</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Policy Decision Point</name>
<description>WSO2 Carbon - Policy Decision Point</description>

@ -3,7 +3,7 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>policy-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -11,7 +11,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.policy.information.point</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Policy Information Point</name>
<description>WSO2 Carbon - Policy Information Point</description>

@ -22,14 +22,14 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>policy-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.policy.mgt.common</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Policy Management Common</name>
<description>WSO2 Carbon - Policy Management Common</description>

@ -22,14 +22,14 @@
<parent>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>policy-mgt</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.policy.mgt.core</artifactId>
<version>3.2.1-SNAPSHOT</version>
<version>3.2.2-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>WSO2 Carbon - Policy Management Core</name>
<description>WSO2 Carbon - Policy Management Core</description>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save