From f5feb897f907a897706eae5f5573ab10c5456242 Mon Sep 17 00:00:00 2001 From: megala21 Date: Fri, 18 Aug 2017 09:29:14 +0530 Subject: [PATCH] Adding initial application release related artifacts --- .../device/application/mgt/api/APIUtil.java | 50 ++++- .../mgt/api/MultipartCustomProvider.java | 68 +++++++ .../services/ApplicationManagementAPI.java | 117 ++++++++++- .../impl/ApplicationManagementAPIImpl.java | 128 +++++++++++- .../src/main/webapp/WEB-INF/cxf-servlet.xml | 2 + .../src/main/webapp/WEB-INF/web.xml | 2 +- .../application/mgt/common/Application.java | 6 + .../mgt/common/ApplicationRelease.java | 23 +-- .../ApplicationManagementException.java | 3 +- ...ApplicationStorageManagementException.java | 33 ++++ .../common/services/ApplicationManager.java | 2 + .../services/ApplicationReleaseManager.java | 27 +++ .../services/ApplicationStorageManager.java | 51 +++++ .../mgt/core/config/Artifacts.java | 49 +++++ .../mgt/core/config/Configuration.java | 11 ++ .../mgt/core/config/Extension.java | 3 +- .../mgt/core/dao/ApplicationReleaseDAO.java | 13 ++ .../mgt/core/dao/common/DAOFactory.java | 19 ++ .../GenericApplicationReleaseDAOImpl.java | 100 ++++++++++ .../mgt/core/impl/ApplicationManagerImpl.java | 8 + .../impl/ApplicationReleaseManagerImpl.java | 53 +++++ .../impl/ApplicationStorageManagerImpl.java | 182 ++++++++++++++++++ .../mgt/core/internal/DataHolder.java | 20 +- .../mgt/core/internal/ServiceComponent.java | 19 +- .../core/util/ApplicationManagementUtil.java | 18 +- .../application/mgt/core/util/Constants.java | 2 + .../main/resources/conf/application-mgt.xml | 7 + .../dbscripts/cdm/application-mgt/h2.sql | 5 +- .../dbscripts/cdm/application-mgt/mysql.sql | 3 +- 29 files changed, 959 insertions(+), 65 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/MultipartCustomProvider.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationStorageManagementException.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Artifacts.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/release/GenericApplicationReleaseDAOImpl.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/APIUtil.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/APIUtil.java index 6817698a35..c7a3ca4115 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/APIUtil.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/APIUtil.java @@ -23,9 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.application.mgt.api.beans.ErrorResponse; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; -import org.wso2.carbon.device.application.mgt.common.services.LifecycleStateManager; -import org.wso2.carbon.device.application.mgt.common.services.PlatformManager; +import org.wso2.carbon.device.application.mgt.common.services.*; import javax.ws.rs.core.Response; @@ -40,6 +38,8 @@ public class APIUtil { private static ApplicationManager applicationManager; private static PlatformManager platformManager; private static LifecycleStateManager lifecycleStateManager; + private static ApplicationReleaseManager applicationReleaseManager; + private static ApplicationStorageManager applicationStorageManager; public static ApplicationManager getApplicationManager() { if (applicationManager == null) { @@ -96,6 +96,50 @@ public class APIUtil { return lifecycleStateManager; } + /** + * To get the Application Release Manager from the osgi context. + * + * @return ApplicationRelease Manager instance in the current osgi context. + */ + public static ApplicationReleaseManager getApplicationReleaseManager() { + if (applicationReleaseManager == null) { + synchronized (APIUtil.class) { + if (applicationReleaseManager == null) { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + applicationReleaseManager = (ApplicationReleaseManager) ctx + .getOSGiService(ApplicationReleaseManager.class, null); + if (applicationReleaseManager == null) { + String msg = "Application Release Manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + } + } + } + return applicationReleaseManager; + } + + /** + * To get the Application Storage Manager from the osgi context. + * @return ApplicationStoreManager instance in the current osgi context. + */ + public static ApplicationStorageManager getApplicationStorageManager() { + if (applicationStorageManager == null) { + synchronized (APIUtil.class) { + if (applicationStorageManager == null) { + PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext(); + applicationStorageManager = (ApplicationStorageManager) ctx + .getOSGiService(ApplicationStorageManager.class, null); + if (applicationStorageManager == null) { + String msg = "Application Storage Manager service has not initialized."; + log.error(msg); + throw new IllegalStateException(msg); + } + } + } + } + return applicationStorageManager; + } public static Response getResponse(ApplicationManagementException ex, Response.Status status) { return getResponse(ex.getMessage(), status); } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/MultipartCustomProvider.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/MultipartCustomProvider.java new file mode 100644 index 0000000000..2fcd51c295 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/MultipartCustomProvider.java @@ -0,0 +1,68 @@ +package org.wso2.carbon.device.application.mgt.api; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.provider.MultipartProvider; +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.jaxrs.AnnotationExclusionStrategy; + +import javax.mail.Multipart; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyReader; +import javax.ws.rs.ext.Provider; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import static java.nio.charset.StandardCharsets.UTF_8; + +@Provider +@Consumes(MediaType.TEXT_PLAIN) +public class MultipartCustomProvider implements MessageBodyReader { + private Gson gson; + + @Override + public boolean isReadable(Class aClass, Type type, Annotation[] annotations, MediaType mediaType) { + return true; + } + + public Object readFrom(Class c, Type t, Annotation[] anns, MediaType mt, MultivaluedMap headers, InputStream inputStream) throws IOException, WebApplicationException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + result.write(buffer, 0, length); + } + String jsonString = result.toString(); + + JsonObject obj = new JsonParser().parse(jsonString).getAsJsonObject(); + + return getGson().fromJson(obj, t); + + } + + private Gson getGson() { + if (gson == null) { + final GsonBuilder gsonBuilder = new GsonBuilder() + .setDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz") + .setExclusionStrategies(new AnnotationExclusionStrategy()); + gson = gsonBuilder.create(); + } + return gson; + } + + +} + + diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java index f463fc9500..668e2594c1 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/ApplicationManagementAPI.java @@ -28,11 +28,14 @@ import io.swagger.annotations.ExtensionProperty; import io.swagger.annotations.Info; import io.swagger.annotations.SwaggerDefinition; import io.swagger.annotations.Tag; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.apimgt.annotations.api.Scopes; import org.wso2.carbon.device.application.mgt.api.beans.ErrorResponse; 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.ApplicationRelease; import java.util.List; import javax.validation.Valid; @@ -106,7 +109,6 @@ import javax.ws.rs.core.Response; @Api(value = "Application Management", description = "This API carries all application management related operations " + "such as get all the applications, add application, etc.") @Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) public interface ApplicationManagementAPI { String SCOPE = "scope"; @@ -270,6 +272,82 @@ public interface ApplicationManagementAPI { required = true) @Valid Application application); + @POST + @Path("upload-artifacts/{uuid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation( + consumes = MediaType.MULTIPART_FORM_DATA, + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Upload artifacts", + notes = "This will create a new application", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application:create") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 201, + message = "OK. \n Successfully uploaded artifacts."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while getting the application list.", + response = ErrorResponse.class) + }) + Response uploadApplicationArtifacts( + @ApiParam( + name = "uuid", + value = "UUID of the application", + required = true) + @PathParam("uuid") String applicationUUID, + @Multipart(value = "icon") Attachment iconFile, + @Multipart(value = "banner") Attachment bannerFile, + @Multipart(value = "screenshot") List screenshots); + + @PUT + @Path("upload-artifacts/{uuid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation( + consumes = MediaType.MULTIPART_FORM_DATA, + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Upload artifacts", + notes = "This will create a new application", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application:create") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 201, + message = "OK. \n Successfully uploaded artifacts."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while getting the application list.", + response = ErrorResponse.class) + }) + Response updateApplicationArtifacts( + @ApiParam( + name = "uuid", + value = "UUID of the application", + required = true) + @PathParam("uuid") String applicationUUID, + @Multipart(value = "icon", required = false) Attachment iconFile, + @Multipart(value = "banner", required = false) Attachment bannerFile, + @Multipart(value = "screenshot", required = false) List screenshots); + + + @PUT @Consumes("application/json") @Path("/{uuid}/lifecycle") @@ -376,4 +454,41 @@ public interface ApplicationManagementAPI { value = "Unique identifier of the Application", required = true) @PathParam("appuuid") String applicationUUID); + + @POST + @Path("/release/{uuid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation( + consumes = MediaType.MULTIPART_FORM_DATA, + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Create an application release", + notes = "This will create a new application release", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:application:create") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 201, + message = "OK. \n Successfully created an application release.", + response = ApplicationRelease.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while releasing the application.", + response = ErrorResponse.class) + }) + Response createApplicationRelease( + @ApiParam( + name = "UUID", + value = "Unique identifier of the Application", + required = true) + @PathParam("uuid") String applicationUUID, + @Multipart(value = "applicationRelease", type = "application/json") ApplicationRelease applicationRelease, + @Multipart(value = "binaryFile") Attachment binaryFile); } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java index a833e8a730..cdf3a47200 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/java/org/wso2/carbon/device/application/mgt/api/services/impl/ApplicationManagementAPIImpl.java @@ -20,16 +20,20 @@ package org.wso2.carbon.device.application.mgt.api.services.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.cxf.jaxrs.ext.multipart.Attachment; +import org.apache.cxf.jaxrs.ext.multipart.Multipart; import org.wso2.carbon.device.application.mgt.api.APIUtil; import org.wso2.carbon.device.application.mgt.api.services.ApplicationManagementAPI; 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.ApplicationRelease; import org.wso2.carbon.device.application.mgt.common.Filter; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; +import org.wso2.carbon.device.application.mgt.common.services.ApplicationReleaseManager; +import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager; import org.wso2.carbon.device.application.mgt.core.util.Constants; -import java.util.Arrays; import javax.validation.Valid; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -41,12 +45,16 @@ import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; /** * Implementation of Application Management related APIs. */ @Produces({"application/json"}) -@Consumes({"application/json"}) @Path("/applications") public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { @@ -160,6 +168,92 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { } } + @Override + @POST + @Path("upload-image-artifacts/{uuid}") + public Response uploadApplicationArtifacts(@PathParam("uuid") String applicationUUID, + @Multipart("icon")Attachment iconFile, @Multipart("banner") Attachment bannerFile, @Multipart + ("screenshot") List attachmentList) { + ApplicationStorageManager applicationStorageManager = APIUtil.getApplicationStorageManager(); + try { + InputStream iconFileStream; + InputStream bannerFileStream; + List attachments = new ArrayList<>(); + + if (iconFile != null) { + iconFileStream = iconFile.getDataHandler().getInputStream(); + } else { + throw new ApplicationManagementException( + "Icon file is not uploaded for the application " + applicationUUID); + } + if (bannerFile != null) { + bannerFileStream = bannerFile.getDataHandler().getInputStream(); + } else { + throw new ApplicationManagementException( + "Banner file is not uploaded for the application " + applicationUUID); + } + if (attachmentList != null && !attachmentList.isEmpty()) { + for (Attachment screenshot : attachmentList) { + attachments.add(screenshot.getDataHandler().getInputStream()); + } + } else { + throw new ApplicationManagementException( + "Screen-shot are not uploaded for the application " + applicationUUID); + } + applicationStorageManager + .uploadImageArtifacts(applicationUUID, iconFileStream, bannerFileStream, attachments); + return Response.status(Response.Status.OK) + .entity("Successfully uploaded artifacts for the application " + applicationUUID).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while creating the application"; + log.error(msg, e); + return APIUtil.getResponse(e, Response.Status.BAD_REQUEST); + } catch (IOException e) { + log.error("Exception while trying to read icon, banner files for the application " + applicationUUID); + return APIUtil.getResponse(new ApplicationManagementException( + "Exception while trying to read icon, " + "banner files for the application " + + applicationUUID, e), Response.Status.BAD_REQUEST); + } + } + + @Override + @PUT + @Path("upload-image-artifacts/{uuid}") + public Response updateApplicationArtifacts(@PathParam("uuid") String applicationUUID, + @Multipart("icon")Attachment iconFile, @Multipart("banner") Attachment bannerFile, @Multipart + ("screenshot") List attachmentList) { + ApplicationStorageManager applicationStorageManager = APIUtil.getApplicationStorageManager(); + try { + InputStream iconFileStream = null; + InputStream bannerFileStream = null; + List attachments = new ArrayList<>(); + + if (iconFile != null) { + iconFileStream = iconFile.getDataHandler().getInputStream(); + } + if (bannerFile != null) { + bannerFileStream = bannerFile.getDataHandler().getInputStream(); + } + if (attachmentList != null) { + for (Attachment screenshot : attachmentList) { + attachments.add(screenshot.getDataHandler().getInputStream()); + } + } + applicationStorageManager + .uploadImageArtifacts(applicationUUID, iconFileStream, bannerFileStream, attachments); + return Response.status(Response.Status.OK) + .entity("Successfully updated artifacts for the application " + applicationUUID).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while updating the artifact for the application " + applicationUUID; + log.error(msg, e); + return APIUtil.getResponse(e, Response.Status.BAD_REQUEST); + } catch (IOException e) { + log.error("Exception while trying to read icon, banner files for the application " + applicationUUID); + return APIUtil.getResponse(new ApplicationManagementException( + "Exception while trying to read icon, banner files for the application " + + applicationUUID, e), Response.Status.BAD_REQUEST); + } + } @PUT @Consumes("application/json") @@ -191,4 +285,34 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI { String responseMsg = "Successfully deleted the application: " + uuid; return Response.status(Response.Status.OK).entity(responseMsg).build(); } + + @Override + @POST + @Path("/release/{uuid}") + public Response createApplicationRelease(@PathParam("uuid") String applicationUUID, + @Multipart("applicationRelease") ApplicationRelease applicationRelease, + @Multipart("binaryFile") Attachment binaryFile) { + ApplicationReleaseManager applicationReleaseManager = APIUtil.getApplicationReleaseManager(); + ApplicationStorageManager applicationStorageManager = APIUtil.getApplicationStorageManager(); + try { + applicationRelease = applicationReleaseManager.createRelease(applicationUUID, applicationRelease); + + if (binaryFile != null) { + applicationStorageManager.uploadReleaseArtifacts(applicationUUID, applicationRelease.getVersionName(), + binaryFile.getDataHandler().getInputStream()); + } + return Response.status(Response.Status.CREATED).entity(applicationRelease).build(); + } catch (ApplicationManagementException e) { + log.error("Error while creating an application release for the application with UUID " + applicationUUID, + e); + return APIUtil.getResponse(e, Response.Status.INTERNAL_SERVER_ERROR); + } catch (IOException e) { + String errorMessage = + "Error while uploading binary file for the application release of the application with UUID " + + applicationUUID; + log.error(errorMessage, e); + return APIUtil.getResponse(new ApplicationManagementException(errorMessage, e), + Response.Status.INTERNAL_SERVER_ERROR); + } + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/cxf-servlet.xml b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/cxf-servlet.xml index 5b4af27dbe..1dacaaef3e 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/cxf-servlet.xml +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/cxf-servlet.xml @@ -31,6 +31,7 @@ http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> + @@ -38,6 +39,7 @@ http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd"> + diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/web.xml b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/web.xml index 55fe863d2e..7574e19e4c 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/web.xml +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.api/src/main/webapp/WEB-INF/web.xml @@ -27,7 +27,7 @@ org.apache.cxf.transport.servlet.CXFServlet - + CXFServlet /* diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/Application.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/Application.java index bce0cad7a7..0afbeff1ee 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/Application.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/Application.java @@ -250,4 +250,10 @@ public class Application { public void setUser(User user) { this.user = user; } + + @Override + public String toString() { + return "UUID : " + uuid + "\tIdentifier : " + identifier + "\tName : " + name + "\tShort Description : " + + shortDescription + "\tLifecycle State : " + currentLifecycle.getLifecycleState() ; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationRelease.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationRelease.java index e78d3e8926..3f4eb2a622 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationRelease.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationRelease.java @@ -18,6 +18,8 @@ */ package org.wso2.carbon.device.application.mgt.common; +import org.wso2.carbon.device.application.mgt.common.jaxrs.Exclude; + import java.util.Date; import java.util.Map; @@ -30,6 +32,7 @@ public class ApplicationRelease { PRODUCTION, ALPHA, BETA } + @Exclude private int id; private int versionId; @@ -46,10 +49,6 @@ public class ApplicationRelease { private Application application; - private Date lifecycleStateModifiedAt; - - private String lifecycleStateModifiedBy; - private Map properties; private boolean isDefault; @@ -118,22 +117,6 @@ public class ApplicationRelease { this.application = application; } - public Date getLifecycleStateModifiedAt() { - return lifecycleStateModifiedAt; - } - - public void setLifecycleStateModifiedAt(Date lifecycleStateModifiedAt) { - this.lifecycleStateModifiedAt = lifecycleStateModifiedAt; - } - - public String getLifecycleStateModifiedBy() { - return lifecycleStateModifiedBy; - } - - public void setLifecycleStateModifiedBy(String lifecycleStateModifiedBy) { - this.lifecycleStateModifiedBy = lifecycleStateModifiedBy; - } - public Map getProperties() { return properties; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationManagementException.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationManagementException.java index ce5ed1cb39..a74c6e3e70 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationManagementException.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationManagementException.java @@ -22,8 +22,7 @@ package org.wso2.carbon.device.application.mgt.common.exception; * Represents the exception thrown during application management. */ public class ApplicationManagementException extends Exception { - - String message; + private String message; public ApplicationManagementException(String message, Throwable throwable) { super(message, throwable); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationStorageManagementException.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationStorageManagementException.java new file mode 100644 index 0000000000..7b661e4d5d --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/exception/ApplicationStorageManagementException.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.application.mgt.common.exception; + +/** + * Represents the exception thrown during storing and retrieving the artifacts. + */ +public class ApplicationStorageManagementException extends ApplicationManagementException{ + public ApplicationStorageManagementException(String message, Throwable ex) { + super(message, ex); + } + + public ApplicationStorageManagementException(String message) { + super(message); + } +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java index 0bbd63c1ca..2899f997c6 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java @@ -21,6 +21,7 @@ package org.wso2.carbon.device.application.mgt.common.services; import org.wso2.carbon.device.application.mgt.common.*; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; +import java.io.InputStream; import java.util.List; /** @@ -88,4 +89,5 @@ public interface ApplicationManager { */ public Application getApplication(String uuid) throws ApplicationManagementException; + } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationReleaseManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationReleaseManager.java index 129404c821..013a4fe5b4 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationReleaseManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationReleaseManager.java @@ -18,6 +18,9 @@ */ package org.wso2.carbon.device.application.mgt.common.services; +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; + /** * ApplicationReleaseManager is responsible for handling all the operations related with * {@link org.wso2.carbon.device.application.mgt.common.ApplicationRelease} which involving addition, updation , @@ -26,4 +29,28 @@ package org.wso2.carbon.device.application.mgt.common.services; */ public interface ApplicationReleaseManager { + /** + * To create an application release for an Application. + * + * @param UUID UUID of the Application + * @param applicationRelease ApplicatonRelease that need to be be created. + * @return the unique id of the application release, if the application release succeeded else -1 + */ + public ApplicationRelease createRelease(String UUID, ApplicationRelease applicationRelease) throws + ApplicationManagementException; + + /** + * To make a release as the default one for an application. + * + * @param id ID of the ApplicationRelease, that need to be made default. + */ + public void makeDefaultRelease(int id) throws ApplicationManagementException; + + /** + * To update with a new release for an Application. + * + * @param applicationRelease ApplicationRelease + * @throws ApplicationManagementException Application Management Exception. + */ + public void updateRelease(ApplicationRelease applicationRelease) throws ApplicationManagementException; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java new file mode 100644 index 0000000000..ddc38a2506 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.application.mgt.common.services; + +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationStorageManagementException; + +import java.io.InputStream; +import java.util.List; + +/** + * This manages all the storage related requirements of Application. + */ +public interface ApplicationStorageManager { + /** + * To upload image artifacts related with an Application. + * + * @param applicationUUID UUID of the application + * @param iconFile Icon File input stream + * @param bannerFile Banner File input stream + * @throws ApplicationStorageManagementException Application Storage Management Exception. + */ + public void uploadImageArtifacts(String applicationUUID, InputStream iconFile, InputStream bannerFile, + List screenshots) throws ApplicationStorageManagementException; + + /** + * To upload release artifacts for an Application. + * @param applicationUUID UUID of the application related with the release. + * @param versionName Name of version of the Applcation Release. + * @param binaryFile Binary File for the release. + * @throws ApplicationStorageManagementException Application Storage Management Exception. + */ + public void uploadReleaseArtifacts(String applicationUUID, String versionName, InputStream binaryFile) throws + ApplicationStorageManagementException; +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Artifacts.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Artifacts.java new file mode 100644 index 0000000000..0c451ec7c9 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Artifacts.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.application.mgt.core.config; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +/** + * This represents Artifacts element in application-mgt configuration file. + */ +@XmlRootElement(name = "Artifacts") +public class Artifacts { + private String imageLocation; + private String binaryLocation; + + @XmlElement(name = "ImageLocation", required = true) + public String getImageLocation() { + return imageLocation; + } + + public void setImageLocation(String imageLocation) { + this.imageLocation = imageLocation; + } + + @XmlElement(name = "BinaryLocation", required = true) + public String getBinaryLocation() { + return binaryLocation; + } + + public void setBinaryLocation(String binaryLocation) { + this.binaryLocation = binaryLocation; + } +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Configuration.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Configuration.java index 36fadc7af0..2d076c1d37 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Configuration.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Configuration.java @@ -33,6 +33,8 @@ public class Configuration { private List extensions; + private Artifacts artifacts; + @XmlElement(name = "DatasourceName", required = true) public String getDatasourceName() { return datasourceName; @@ -51,6 +53,15 @@ public class Configuration { public void setExtensions(List extensions) { this.extensions = extensions; } + + @XmlElement(name = "Artifacts") + public Artifacts getArtifacts() { + return artifacts; + } + + public void setArtifacts(Artifacts artifacts) { + this.artifacts = artifacts; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Extension.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Extension.java index 45f0cdcf73..5ad1d05440 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Extension.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/config/Extension.java @@ -87,7 +87,8 @@ public class Extension { PlatformManager, VisibilityTypeManager, SubscriptionManager, - VisibilityManager + VisibilityManager, + ApplicationStorageManager } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationReleaseDAO.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationReleaseDAO.java index 621cbd5821..432be683b7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationReleaseDAO.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/ApplicationReleaseDAO.java @@ -18,9 +18,22 @@ */ package org.wso2.carbon.device.application.mgt.core.dao; +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; + /** * This is responsible for Application Release related DAO operations. */ public interface ApplicationReleaseDAO { + /** + * To create an Application release. + * + * @param applicationRelease Application Release that need to be created. + * @return Unique ID of the relevant release. + * @throws ApplicationManagementDAOException Application Management DAO Exception. + */ + ApplicationRelease createRelease(ApplicationRelease applicationRelease) throws + ApplicationManagementDAOException; + } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/DAOFactory.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/DAOFactory.java index 87b485527f..5f97b518fa 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/DAOFactory.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/common/DAOFactory.java @@ -23,9 +23,11 @@ import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.application.mgt.common.exception.UnsupportedDatabaseEngineException; import org.wso2.carbon.device.application.mgt.core.config.ConfigurationManager; import org.wso2.carbon.device.application.mgt.core.dao.ApplicationDAO; +import org.wso2.carbon.device.application.mgt.core.dao.ApplicationReleaseDAO; import org.wso2.carbon.device.application.mgt.core.dao.LifecycleStateDAO; import org.wso2.carbon.device.application.mgt.core.dao.PlatformDAO; import org.wso2.carbon.device.application.mgt.core.dao.impl.application.GenericApplicationDAOImpl; +import org.wso2.carbon.device.application.mgt.core.dao.impl.application.release.GenericApplicationReleaseDAOImpl; import org.wso2.carbon.device.application.mgt.core.dao.impl.lifecyclestate.GenericLifecycleStateImpl; import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.GenericPlatformDAOImpl; import org.wso2.carbon.device.application.mgt.core.dao.impl.platform.OracleMsSQLPlatformDAOImpl; @@ -96,6 +98,23 @@ public class DAOFactory { throw new IllegalStateException("Database engine has not initialized properly."); } + /** + * To get the instance of ApplicationReleaseDAOImplementation of the particular database engine. + * @return specific ApplicationReleaseDAOImplementation + */ + public static ApplicationReleaseDAO getApplicationReleaseDAO() { + if (databaseEngine != null) { + switch (databaseEngine) { + case Constants.DataBaseTypes.DB_TYPE_H2: + case Constants.DataBaseTypes.DB_TYPE_MYSQL: + return new GenericApplicationReleaseDAOImpl(); + default: + throw new UnsupportedDatabaseEngineException("Unsupported database engine : " + databaseEngine); + } + } + throw new IllegalStateException("Database engine has not initialized properly."); + } + /** * This method initializes the databases by creating the database. * diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/release/GenericApplicationReleaseDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/release/GenericApplicationReleaseDAOImpl.java new file mode 100644 index 0000000000..9a2a2bad93 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/release/GenericApplicationReleaseDAOImpl.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.application.mgt.core.dao.impl.application.release; + +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.exception.DBConnectionException; +import org.wso2.carbon.device.application.mgt.core.dao.ApplicationReleaseDAO; +import org.wso2.carbon.device.application.mgt.core.dao.common.Util; +import org.wso2.carbon.device.application.mgt.core.dao.impl.AbstractDAOImpl; +import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; +import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil; + +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; + +/** + * GenericApplicationReleaseDAOImpl holds the implementation of ApplicationRelease related DAO operations. + */ +public class GenericApplicationReleaseDAOImpl extends AbstractDAOImpl implements ApplicationReleaseDAO { + + @Override + public ApplicationRelease createRelease(ApplicationRelease applicationRelease) throws + ApplicationManagementDAOException { + Connection connection; + PreparedStatement statement = null; + ResultSet resultSet = null; + String sql = "insert into APPM_APPLICATION_RELEASE(VERSION_NAME, RESOURCE, RELEASE_CHANNEL ," + + "RELEASE_DETAILS, CREATED_AT, APPM_APPLICATION_ID, IS_DEFAULT) values (?, ?, ?, ?, ?, ?, ?)"; + int index = 0; + boolean isBatchExecutionSupported = ConnectionManagerUtil.isBatchQuerySupported(); + + try { + connection = this.getDBConnection(); + statement = connection.prepareStatement(sql); + statement.setString(++index, applicationRelease.getVersionName()); + statement.setString(++index, applicationRelease.getResource()); + statement.setString(++index, String.valueOf(applicationRelease.getReleaseChannel())); + statement.setString(++index, applicationRelease.getReleaseDetails()); + statement.setDate(++index, new Date(applicationRelease.getCreatedAt().getTime())); + statement.setInt(++index, applicationRelease.getApplication().getId()); + statement.setBoolean(++index, applicationRelease.isDefault()); + statement.executeUpdate(); + resultSet = statement.getGeneratedKeys(); + if (resultSet.next()) { + applicationRelease.setId(resultSet.getInt(1)); + } + + if (applicationRelease.getProperties() != null && applicationRelease.getProperties().size() != 0) { + sql = "INSERT INTO APPM_RELEASE_PROPERTY (PROP_KEY, PROP_VALUE, APPLICATION_RELEASE_ID) VALUES (?,?,?)"; + statement = connection.prepareStatement(sql); + for (Object entry : applicationRelease.getProperties().entrySet()) { + Map.Entry property = (Map.Entry) entry; + statement.setString(1, property.getKey()); + statement.setString(2, property.getValue()); + statement.setInt(3, applicationRelease.getId()); + if (isBatchExecutionSupported) { + statement.addBatch(); + } else { + statement.execute(); + } + } + if (isBatchExecutionSupported) { + statement.executeBatch(); + } + } + return applicationRelease; + } catch (SQLException e) { + throw new ApplicationManagementDAOException( + "SQL Exception while trying to release an application (UUID : " + applicationRelease + .getApplication().getUuid() + "), by executing the query " + sql, e); + } catch (DBConnectionException e) { + throw new ApplicationManagementDAOException( + "Database Connection Exception while trying to release the " + "applcation with UUID " + + applicationRelease.getApplication().getUuid(), e); + } finally { + Util.cleanupResources(statement, resultSet); + } + } +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java index 05afbb555c..4641c6734f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java @@ -41,11 +41,13 @@ import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException; import org.wso2.carbon.device.application.mgt.core.exception.ValidationException; import org.wso2.carbon.device.application.mgt.core.internal.DataHolder; import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil; +import org.wso2.carbon.device.application.mgt.core.util.Constants; import org.wso2.carbon.device.application.mgt.core.util.HelperUtil; import org.wso2.carbon.user.api.UserRealm; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.utils.multitenancy.MultitenantUtils; +import java.io.*; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -282,6 +284,12 @@ public class ApplicationManagerImpl implements ApplicationManager { } } + public void uploadArtifacts(String applicationUUID, InputStream iconFileStream, InputStream bannerFileStream, + List screenShotStreams) + throws ApplicationManagementException { + + } + /** * To check whether current user is application owner or admin. * diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationReleaseManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationReleaseManagerImpl.java index 874164efee..a093b09b8f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationReleaseManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationReleaseManagerImpl.java @@ -18,8 +18,61 @@ */ package org.wso2.carbon.device.application.mgt.core.impl; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.context.PrivilegedCarbonContext; +import org.wso2.carbon.device.application.mgt.common.Application; +import org.wso2.carbon.device.application.mgt.common.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; import org.wso2.carbon.device.application.mgt.common.services.ApplicationReleaseManager; +import org.wso2.carbon.device.application.mgt.core.dao.common.DAOFactory; +import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; +import org.wso2.carbon.device.application.mgt.core.internal.DataHolder; +import org.wso2.carbon.device.application.mgt.core.util.ApplicationManagementUtil; +import org.wso2.carbon.device.application.mgt.core.util.ConnectionManagerUtil; + +import java.util.Date; public class ApplicationReleaseManagerImpl implements ApplicationReleaseManager { + private static Log log = LogFactory.getLog(ApplicationReleaseManagerImpl.class); + + @Override + public ApplicationRelease createRelease(String UUID, ApplicationRelease applicationRelease) throws + ApplicationManagementException { + if (UUID == null) { + throw new ApplicationManagementException("Application UUID is null. Application UUID is a required " + + "parameter to do the application release"); + } + Application application = DataHolder.getInstance().getApplicationManager().getApplication(UUID); + if (application == null) { + throw new ApplicationManagementException("Application with UUID " + UUID + " does not exist. Cannot " + + "release an application that is not existing"); + } + if (log.isDebugEnabled()) { + log.debug("Application release request is received for the application " + application.toString()); + } + applicationRelease.setCreatedAt(new Date()); + try { + ConnectionManagerUtil.beginDBTransaction(); + applicationRelease.setApplication(application); + applicationRelease = DAOFactory.getApplicationReleaseDAO().createRelease(applicationRelease); + ConnectionManagerUtil.commitDBTransaction(); + return applicationRelease; + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + throw e; + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + + @Override + public void makeDefaultRelease(int id) throws ApplicationManagementException { + + } + + @Override + public void updateRelease(ApplicationRelease applicationRelease) throws ApplicationManagementException { + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java new file mode 100644 index 0000000000..112570b963 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package org.wso2.carbon.device.application.mgt.core.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.application.mgt.common.Application; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; +import org.wso2.carbon.device.application.mgt.common.exception.ApplicationStorageManagementException; +import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager; +import org.wso2.carbon.device.application.mgt.core.internal.DataHolder; +import org.wso2.carbon.device.application.mgt.core.util.Constants; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +/** + * This class contains the default concrete implementation of ApplicationStorage Management. + */ +public class ApplicationStorageManagerImpl implements ApplicationStorageManager{ + private static final Log log = LogFactory.getLog(ApplicationStorageManagerImpl.class); + + @Override + public void uploadImageArtifacts(String applicationUUID, InputStream iconFileStream, InputStream bannerFileStream, + List screenShotStreams) throws ApplicationStorageManagementException { + Application application; + try { + application = DataHolder.getInstance().getApplicationManager().getApplication(applicationUUID); + } catch (ApplicationManagementException e) { + throw new ApplicationStorageManagementException("Exception while retrieving the application details for " + + "the application with UUID " + applicationUUID); + } + if (application == null) { + throw new ApplicationStorageManagementException("Application with UUID " + applicationUUID + " does not " + + "exist. Cannot upload the artifacts to non-existing application."); + } + String artifactDirectoryPath = Constants.ARTIFACT_PATH + application.getId(); + if (log.isDebugEnabled()) { + log.debug("Artifact Directory Path for saving the artifacts related with application " + applicationUUID + + " is " + artifactDirectoryPath); + } + createArtifactDirectory(artifactDirectoryPath); + if (iconFileStream != null) { + String iconName = application.getIconName(); + iconName = (iconName == null) ? "icon" : iconName; + try { + saveFile(iconFileStream, artifactDirectoryPath + File.separator + iconName); + } catch (IOException e) { + throw new ApplicationStorageManagementException( + "IO Exception while saving the icon file in the server for " + "the application " + + applicationUUID, e); + } + } + if (bannerFileStream != null) { + String bannerName = application.getBannerName(); + bannerName = (bannerName == null) ? "banner" : bannerName; + try { + saveFile(bannerFileStream, artifactDirectoryPath + File.separator + bannerName); + } catch (IOException e) { + throw new ApplicationStorageManagementException( + "IO Exception while saving the banner file in the server for" + " the application " + + applicationUUID, e); + } + } + if (screenShotStreams != null) { + int count = 1; + String screenshotName; + List screenShotNames = application.getScreenshots(); + boolean isScreenShotNameExist = (screenShotNames == null || screenShotNames.isEmpty()); + int screenShotNameLength = isScreenShotNameExist ? screenShotNames.size() : 0; + for (InputStream screenshotStream : screenShotStreams) { + try { + if (isScreenShotNameExist && count <= screenShotNameLength) { + screenshotName = screenShotNames.get(count); + } else { + screenshotName = "screenshot_" + count; + } + saveFile(screenshotStream, artifactDirectoryPath + File.separator + screenshotName); + count++; + } catch (IOException e) { + throw new ApplicationStorageManagementException( + "IO Exception while saving the screens hots for the " + "application " + applicationUUID, + e); + } + } + } + } + + @Override + public void uploadReleaseArtifacts(String applicationUUID, String versionName, InputStream binaryFile) + throws ApplicationStorageManagementException { + Application application; + try { + application = DataHolder.getInstance().getApplicationManager().getApplication(applicationUUID); + } catch (ApplicationManagementException e) { + throw new ApplicationStorageManagementException("Exception while retrieving the application details for " + + "the application with UUID " + applicationUUID); + } + if (application == null) { + throw new ApplicationStorageManagementException("Application with UUID " + applicationUUID + " does not " + + "exist. Cannot upload release artifacts for not existing application."); + } + String artifactDirectoryPath = Constants.ARTIFACT_PATH + application.getId(); + if (log.isDebugEnabled()) { + log.debug("Artifact Directory Path for saving the application release related artifacts related with " + + "application " + applicationUUID + " is " + artifactDirectoryPath); + } + + createArtifactDirectory(artifactDirectoryPath); + if (binaryFile != null) { + try { + saveFile(binaryFile, artifactDirectoryPath + File.separator + versionName); + } catch (IOException e) { + throw new ApplicationStorageManagementException( + "IO Exception while saving the release artifacts in the server for the application " + + applicationUUID, e); + } + } + + } + + /** + * To save a file in a given location. + * + * @param inputStream Stream of the file. + * @param path Path the file need to be saved in. + */ + private void saveFile(InputStream inputStream, String path) throws IOException { + OutputStream outStream = null; + try { + byte[] buffer = new byte[inputStream.available()]; + inputStream.read(buffer); + outStream = new FileOutputStream(new File(path)); + outStream.write(buffer); + } finally { + if (inputStream != null) { + inputStream.close(); + } + if (outStream != null) { + outStream.close(); + } + } + } + + /** + * This method is responsible for creating artifact parent directories in the given path. + * + * @param artifactDirectoryPath Path for the artifact directory. + * @throws ApplicationStorageManagementException Application Storage Management Exception. + */ + private void createArtifactDirectory(String artifactDirectoryPath) throws ApplicationStorageManagementException { + File artifactDirectory = new File(artifactDirectoryPath); + + if (!artifactDirectory.exists()) { + if (!artifactDirectory.mkdirs()) { + throw new ApplicationStorageManagementException( + "Cannot create directories in the path to save the application related artifacts"); + } + } + } +} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java index 5c4bac0495..2f7331e4a4 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/DataHolder.java @@ -18,16 +18,7 @@ */ package org.wso2.carbon.device.application.mgt.core.internal; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationReleaseManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationUploadManager; -import org.wso2.carbon.device.application.mgt.common.services.CategoryManager; -import org.wso2.carbon.device.application.mgt.common.services.CommentsManager; -import org.wso2.carbon.device.application.mgt.common.services.LifecycleStateManager; -import org.wso2.carbon.device.application.mgt.common.services.PlatformManager; -import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityTypeManager; +import org.wso2.carbon.device.application.mgt.common.services.*; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.user.core.service.RealmService; @@ -60,6 +51,8 @@ public class DataHolder { private ApplicationUploadManager applicationUploadManager; + private ApplicationStorageManager applicationStorageManager; + private static final DataHolder applicationMgtDataHolder = new DataHolder(); private DataHolder() { @@ -166,4 +159,11 @@ public class DataHolder { this.realmService = realmService; } + public void setApplicationStorageManager(ApplicationStorageManager applicationStorageManager) { + this.applicationStorageManager = applicationStorageManager; + } + + public ApplicationStorageManager getApplicationStorageManager() { + return applicationStorageManager; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ServiceComponent.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ServiceComponent.java index 1a5884852d..68bb3a9af2 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ServiceComponent.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/internal/ServiceComponent.java @@ -23,20 +23,12 @@ import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; import org.osgi.service.component.ComponentContext; import org.wso2.carbon.device.application.mgt.common.exception.InvalidConfigurationException; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationReleaseManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationUploadManager; -import org.wso2.carbon.device.application.mgt.common.services.CategoryManager; -import org.wso2.carbon.device.application.mgt.common.services.CommentsManager; -import org.wso2.carbon.device.application.mgt.common.services.LifecycleStateManager; -import org.wso2.carbon.device.application.mgt.common.services.PlatformManager; -import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityTypeManager; +import org.wso2.carbon.device.application.mgt.common.services.*; import org.wso2.carbon.device.application.mgt.core.config.ConfigurationManager; import org.wso2.carbon.device.application.mgt.core.dao.common.DAOFactory; import org.wso2.carbon.device.application.mgt.core.exception.ApplicationManagementDAOException; import org.wso2.carbon.device.application.mgt.core.util.ApplicationManagementUtil; +import org.wso2.carbon.device.application.mgt.core.util.Constants; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.ndatasource.core.DataSourceService; import org.wso2.carbon.user.core.service.RealmService; @@ -77,6 +69,8 @@ public class ServiceComponent { String datasourceName = ConfigurationManager.getInstance().getConfiguration().getDatasourceName(); DAOFactory.init(datasourceName); + Constants.ARTIFACT_PATH = ConfigurationManager.getInstance().getConfiguration().getArtifacts() + .getBinaryLocation(); ApplicationManager applicationManager = ApplicationManagementUtil.getApplicationManagerInstance(); DataHolder.getInstance().setApplicationManager(applicationManager); bundleContext.registerService(ApplicationManager.class.getName(), applicationManager, null); @@ -118,6 +112,11 @@ public class ServiceComponent { DataHolder.getInstance().setApplicationUploadManager(uploadManager); bundleContext.registerService(ApplicationUploadManager.class.getName(), uploadManager, null); + ApplicationStorageManager applicationStorageManager = ApplicationManagementUtil + .getApplicationStorageManagerInstance(); + DataHolder.getInstance().setApplicationStorageManager(applicationStorageManager); + bundleContext.registerService(ApplicationStorageManager.class.getName(), applicationStorageManager, null); + bundleContext.registerService(Axis2ConfigurationContextObserver.class.getName(), new PlatformManagementAxis2ConfigurationObserverImpl(), null); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/ApplicationManagementUtil.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/ApplicationManagementUtil.java index 004ae7dda7..ca43e84807 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/ApplicationManagementUtil.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/ApplicationManagementUtil.java @@ -21,16 +21,7 @@ package org.wso2.carbon.device.application.mgt.core.util; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.device.application.mgt.common.exception.InvalidConfigurationException; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationReleaseManager; -import org.wso2.carbon.device.application.mgt.common.services.ApplicationUploadManager; -import org.wso2.carbon.device.application.mgt.common.services.CategoryManager; -import org.wso2.carbon.device.application.mgt.common.services.CommentsManager; -import org.wso2.carbon.device.application.mgt.common.services.LifecycleStateManager; -import org.wso2.carbon.device.application.mgt.common.services.PlatformManager; -import org.wso2.carbon.device.application.mgt.common.services.SubscriptionManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityManager; -import org.wso2.carbon.device.application.mgt.common.services.VisibilityTypeManager; +import org.wso2.carbon.device.application.mgt.common.services.*; import org.wso2.carbon.device.application.mgt.core.config.ConfigurationManager; import org.wso2.carbon.device.application.mgt.core.config.Extension; @@ -105,6 +96,13 @@ public class ApplicationManagementUtil { return getInstance(extension, ApplicationUploadManager.class); } + public static ApplicationStorageManager getApplicationStorageManagerInstance() throws + InvalidConfigurationException { + ConfigurationManager configurationManager = ConfigurationManager.getInstance(); + Extension extension = configurationManager.getExtension(Extension.Name.ApplicationStorageManager); + return getInstance(extension, ApplicationStorageManager.class); + } + private static T getInstance(Extension extension, Class cls) throws InvalidConfigurationException { try { Class theClass = Class.forName(extension.getClassName()); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index a8701495bb..7b28b499f4 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -53,4 +53,6 @@ public class Constants { */ public static final String[] LIFE_CYCLES = {"CREATED", "IN REVIEW", "APPROVED", "REJECTED", "PUBLISHED", "UNPUBLISHED", "RETIRED"}; + + public static String ARTIFACT_PATH = ""; } diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/application-mgt.xml b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/application-mgt.xml index fd99ffe3de..1fb22c2b4a 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/application-mgt.xml +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/conf/application-mgt.xml @@ -55,6 +55,13 @@ org.wso2.carbon.device.application.mgt.core.impl.VisibilityTypeManagerImpl + + org.wso2.carbon.device.application.mgt.core.impl.ApplicationStorageManagerImpl + + + ${carbon.home}/repository/resources/mobileapps/images/ + ${carbon.home}/repository/resources/mobileapps/binary/ + \ No newline at end of file diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql index c2b9a615b4..f344747a4a 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/h2.sql @@ -181,15 +181,14 @@ CREATE INDEX FK_APPLICATION_PROPERTY_APPLICATION ON APPM_APPLICATION_PROPERTY(AP -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS APPM_APPLICATION_RELEASE ( ID INT NOT NULL AUTO_INCREMENT, - VERSION_ID INT NOT NULL, VERSION_NAME VARCHAR(100) NOT NULL, RESOURCE TEXT NULL, RELEASE_CHANNEL VARCHAR(50) NULL, RELEASE_DETAILS TEXT NULL, CREATED_AT DATETIME NOT NULL, APPM_APPLICATION_ID INT NOT NULL, - PUBLISHED TINYINT NULL, - PRIMARY KEY (ID, APPM_APPLICATION_ID), + IS_DEFAULT TINYINT NULL, + PRIMARY KEY (APPM_APPLICATION_ID, VERSION_NAME), CONSTRAINT FK_APPLICATION_VERSION_APPLICATION FOREIGN KEY (APPM_APPLICATION_ID) REFERENCES APPM_APPLICATION (ID) diff --git a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql index 40766adc0c..d0785ca9b0 100644 --- a/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql +++ b/features/application-mgt/org.wso2.carbon.device.application.mgt.server.feature/src/main/resources/dbscripts/cdm/application-mgt/mysql.sql @@ -189,7 +189,6 @@ CREATE TABLE IF NOT EXISTS `APPM_APPLICATION_PROPERTY` ( -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `APPM_APPLICATION_RELEASE` ( `ID` INT NOT NULL AUTO_INCREMENT, - `VERSION_ID` INT NOT NULL, `VERSION_NAME` VARCHAR(100) NOT NULL, `RESOURCE` TEXT NULL, `RELEASE_CHANNEL` VARCHAR(50) NULL, @@ -197,7 +196,7 @@ CREATE TABLE IF NOT EXISTS `APPM_APPLICATION_RELEASE` ( `CREATED_AT` DATETIME NOT NULL, `APPM_APPLICATION_ID` INT NOT NULL, `IS_DEFAULT` TINYINT(1) NULL, - PRIMARY KEY (`ID`, `APPM_APPLICATION_ID`), + PRIMARY KEY (`APPM_APPLICATION_ID`, `VERSION_NAME`), INDEX `FK_APPLICATION_VERSION_APPLICATION` (`APPM_APPLICATION_ID` ASC), CONSTRAINT `FK_APPLICATION_VERSION_APPLICATION` FOREIGN KEY (`APPM_APPLICATION_ID`)