mirror of
https://repository.entgra.net/community/device-mgt-core.git
synced 2025-10-06 02:01:45 +00:00
Add iOS applications and Web Clip publishing functionality
This commit contains followings Added dependencies for URL validation and plist parsing. Added version retrieval functionality for iOS application Added WEB clip publishing functionality. In order to compatible with above functionalities modified the source
This commit is contained in:
parent
b45d11bfdc
commit
c95733a781
@ -51,8 +51,6 @@ public class Application {
|
||||
|
||||
private List<ApplicationRelease> applicationReleases;
|
||||
|
||||
// private ApplicationRelease releaseVersion;
|
||||
|
||||
private DeviceType devicetype;
|
||||
|
||||
public int getId() {
|
||||
@ -105,14 +103,6 @@ public class Application {
|
||||
this.unrestrictedRoles = unrestrictedRoles;
|
||||
}
|
||||
|
||||
// public ApplicationRelease getReleaseVersion() {
|
||||
// return releaseVersion;
|
||||
// }
|
||||
//
|
||||
// public void setReleaseVersion(ApplicationRelease releaseVersion) {
|
||||
// this.releaseVersion = releaseVersion;
|
||||
// }
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -82,6 +82,8 @@ public class ApplicationRelease {
|
||||
|
||||
private int stars;
|
||||
|
||||
private String url;
|
||||
|
||||
public int getNoOfRatedUsers() {
|
||||
return noOfRatedUsers;
|
||||
}
|
||||
@ -289,4 +291,12 @@ public class ApplicationRelease {
|
||||
public void setIconLoc(String iconLoc) {
|
||||
this.iconLoc = iconLoc;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,6 +23,6 @@ package org.wso2.carbon.device.application.mgt.common;
|
||||
* Application Types.
|
||||
*/
|
||||
public enum ApplicationType {
|
||||
ANDROID, iOS, WEB_CLIP
|
||||
ANDROID, IOS, WEB_CLIP
|
||||
}
|
||||
|
||||
|
||||
@ -186,6 +186,16 @@
|
||||
<artifactId>apk-parser</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.plist</groupId>
|
||||
<artifactId>dd-plist</artifactId>
|
||||
<version>1.20</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
<version>1.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
||||
@ -19,6 +19,10 @@
|
||||
|
||||
package org.wso2.carbon.device.application.mgt.core.impl;
|
||||
|
||||
import com.dd.plist.NSDictionary;
|
||||
import com.dd.plist.NSString;
|
||||
import com.dd.plist.PropertyListFormatException;
|
||||
import com.dd.plist.PropertyListParser;
|
||||
import net.dongliu.apk.parser.ApkFile;
|
||||
import net.dongliu.apk.parser.bean.ApkMeta;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
@ -28,20 +32,32 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
import org.wso2.carbon.device.application.mgt.common.ApplicationRelease;
|
||||
import org.wso2.carbon.device.application.mgt.common.ApplicationType;
|
||||
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.exception.ResourceManagementException;
|
||||
import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager;
|
||||
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.StorageManagementUtil;
|
||||
import org.apache.commons.validator.routines.UrlValidator;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.ParseException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import static org.wso2.carbon.device.application.mgt.core.util.StorageManagementUtil.deleteDir;
|
||||
import static org.wso2.carbon.device.application.mgt.core.util.StorageManagementUtil.saveFile;
|
||||
|
||||
/**
|
||||
@ -51,6 +67,7 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager
|
||||
private static final Log log = LogFactory.getLog(ApplicationStorageManagerImpl.class);
|
||||
private String storagePath;
|
||||
private int screenShotMaxCount;
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
/**
|
||||
* Create a new ApplicationStorageManager Instance
|
||||
@ -163,11 +180,30 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager
|
||||
public ApplicationRelease uploadReleaseArtifact(ApplicationRelease applicationRelease, String appType, InputStream binaryFile)
|
||||
throws ResourceManagementException {
|
||||
|
||||
try {
|
||||
if (ApplicationType.WEB_CLIP.toString().equals(appType)) {
|
||||
applicationRelease.setVersion(Constants.DEFAULT_VERSION);
|
||||
UrlValidator urlValidator = new UrlValidator();
|
||||
if (applicationRelease.getUrl() == null || !urlValidator.isValid(applicationRelease.getUrl())) {
|
||||
throw new ApplicationStorageManagementException("Request payload doesn't contains Web Clip URL " +
|
||||
"with application release object or Web " +
|
||||
"Clip URL is invalid");
|
||||
//todo if we throw this we must send BAD REQUEST to end user
|
||||
}
|
||||
applicationRelease.setAppStoredLoc(applicationRelease.getUrl());
|
||||
applicationRelease.setAppHashValue(null);
|
||||
return applicationRelease;
|
||||
}
|
||||
|
||||
String artifactDirectoryPath;
|
||||
String md5OfApp;
|
||||
md5OfApp = getMD5(binaryFile);
|
||||
|
||||
try {
|
||||
if (md5OfApp == null) {
|
||||
throw new ApplicationStorageManagementException(
|
||||
"Error occurred while md5sum value retrieving process: " +
|
||||
"application UUID " + applicationRelease.getUuid());
|
||||
}
|
||||
|
||||
if (ApplicationType.ANDROID.toString().equals(appType)) {
|
||||
String prefix = "stream2file";
|
||||
@ -184,16 +220,25 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager
|
||||
if (!isTempDelete) {
|
||||
log.error("Temporary created APK file deletion failed");
|
||||
}
|
||||
} else if (ApplicationType.iOS.toString().equals(appType)) {
|
||||
//todo iOS ipa validate
|
||||
} else if (ApplicationType.WEB_CLIP.toString().equals(appType)) {
|
||||
//todo Web Clip validate
|
||||
} else if (ApplicationType.IOS.toString().equals(appType)) {
|
||||
String prefix = "stream2file";
|
||||
String suffix = ".ipa";
|
||||
Boolean isTempDelete;
|
||||
|
||||
File tempFile = File.createTempFile(prefix, suffix);
|
||||
FileOutputStream out = new FileOutputStream(tempFile);
|
||||
IOUtils.copy(binaryFile, out);
|
||||
Map<String, String> plistInfo = getIPAInfo(tempFile);
|
||||
applicationRelease.setVersion(plistInfo.get("CFBundleVersion"));
|
||||
isTempDelete = tempFile.delete();
|
||||
if (!isTempDelete) {
|
||||
log.error("Temporary created ipa file deletion failed");
|
||||
}
|
||||
} else {
|
||||
throw new ApplicationStorageManagementException("Application Type doesn't match with supporting " +
|
||||
"application types " + applicationRelease.getUuid());
|
||||
}
|
||||
|
||||
if (md5OfApp != null) {
|
||||
artifactDirectoryPath = storagePath + md5OfApp;
|
||||
StorageManagementUtil.createArtifactDirectory(artifactDirectoryPath);
|
||||
if (log.isDebugEnabled()) {
|
||||
@ -205,17 +250,13 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager
|
||||
saveFile(binaryFile, artifactPath);
|
||||
applicationRelease.setAppStoredLoc(artifactPath);
|
||||
applicationRelease.setAppHashValue(md5OfApp);
|
||||
} else {
|
||||
throw new ApplicationStorageManagementException("Error occurred while md5sum value retrieving process: " +
|
||||
"application UUID " + applicationRelease.getUuid());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new ApplicationStorageManagementException(
|
||||
"IO Exception while saving the release artifacts in the server for the application UUID "
|
||||
+ applicationRelease.getUuid(), e);
|
||||
}
|
||||
|
||||
|
||||
return applicationRelease;
|
||||
}
|
||||
|
||||
@ -267,4 +308,130 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager
|
||||
}
|
||||
return md5;
|
||||
}
|
||||
|
||||
private synchronized Map<String, String> getIPAInfo(File ipaFile) throws ApplicationStorageManagementException {
|
||||
Map<String, String> ipaInfo = new HashMap<>();
|
||||
|
||||
String ipaDirectory = null;
|
||||
try {
|
||||
String ipaAbsPath = ipaFile.getAbsolutePath();
|
||||
ipaDirectory = new File(ipaAbsPath).getParent();
|
||||
|
||||
if (new File(ipaDirectory + File.separator + Constants.PAYLOAD).exists()) {
|
||||
deleteDir(new File(ipaDirectory + File.separator + Constants.PAYLOAD));
|
||||
}
|
||||
|
||||
// unzip ipa zip file
|
||||
unzip(ipaAbsPath, ipaDirectory);
|
||||
|
||||
// fetch app file name, after unzip ipa
|
||||
String appFileName = "";
|
||||
for (File file : Objects.requireNonNull(
|
||||
new File(ipaDirectory + File.separator + Constants.PAYLOAD).listFiles()
|
||||
)) {
|
||||
if (file.toString().endsWith(Constants.APP_EXTENSION)) {
|
||||
appFileName = new File(file.toString()).getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String plistFilePath = appFileName + File.separator + Constants.PLIST_NAME;
|
||||
|
||||
// parse info.plist
|
||||
File plistFile = new File(plistFilePath);
|
||||
NSDictionary rootDict;
|
||||
rootDict = (NSDictionary) PropertyListParser.parse(plistFile);
|
||||
|
||||
// get version
|
||||
NSString parameter = (NSString) rootDict.objectForKey(Constants.CF_BUNDLE_VERSION);
|
||||
ipaInfo.put(Constants.CF_BUNDLE_VERSION, parameter.toString());
|
||||
|
||||
} catch (ParseException e) {
|
||||
String msg = "Error occurred while parsing the plist data";
|
||||
log.error(msg);
|
||||
throw new ApplicationStorageManagementException(msg, e);
|
||||
} catch (IOException e) {
|
||||
String msg = "Error occurred while accessing the ipa file";
|
||||
log.error(msg);
|
||||
throw new ApplicationStorageManagementException(msg, e);
|
||||
} catch (SAXException | ParserConfigurationException | PropertyListFormatException e) {
|
||||
log.error(e);
|
||||
throw new ApplicationStorageManagementException(e.getMessage(), e);
|
||||
} catch (ApplicationStorageManagementException e) {
|
||||
String msg = "Error occurred while unzipping the ipa file";
|
||||
log.error(msg);
|
||||
throw new ApplicationStorageManagementException(msg, e);
|
||||
} finally {
|
||||
if (ipaDirectory != null) {
|
||||
// remove unzip folder
|
||||
deleteDir(new File(ipaDirectory + File.separator + Constants.PAYLOAD));
|
||||
}
|
||||
}
|
||||
return ipaInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a zip file specified by the zipFilePath to a directory specified by
|
||||
* destDirectory (will be created if does not exists)
|
||||
*
|
||||
* @param zipFilePath file path of the zip
|
||||
* @param destDirectory destination directory path
|
||||
*/
|
||||
private void unzip(String zipFilePath, String destDirectory)
|
||||
throws IOException, ApplicationStorageManagementException {
|
||||
File destDir = new File(destDirectory);
|
||||
Boolean isDirCreated;
|
||||
|
||||
if (!destDir.exists()) {
|
||||
isDirCreated = destDir.mkdir();
|
||||
if (!isDirCreated) {
|
||||
throw new ApplicationStorageManagementException("Directory Creation Is Failed while iOS app vertion " +
|
||||
"retrieval");
|
||||
}
|
||||
}
|
||||
|
||||
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
|
||||
ZipEntry entry = zipIn.getNextEntry();
|
||||
|
||||
// iterates over entries in the zip file
|
||||
while (entry != null) {
|
||||
String filePath = destDirectory + File.separator + entry.getName();
|
||||
|
||||
if (!entry.isDirectory()) {
|
||||
// if the entry is a file, extracts it
|
||||
extractFile(zipIn, filePath);
|
||||
} else {
|
||||
// if the entry is a directory, make the directory
|
||||
File dir = new File(filePath);
|
||||
isDirCreated = dir.mkdir();
|
||||
if (!isDirCreated) {
|
||||
throw new ApplicationStorageManagementException(
|
||||
"Directory Creation Is Failed while iOS app vertion " +
|
||||
"retrieval");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
zipIn.closeEntry();
|
||||
entry = zipIn.getNextEntry();
|
||||
}
|
||||
zipIn.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a zip entry (file entry)
|
||||
*
|
||||
* @param zipIn zip input stream
|
||||
* @param filePath file path
|
||||
*/
|
||||
private void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
|
||||
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
|
||||
byte[] bytesIn = new byte[BUFFER_SIZE];
|
||||
int read;
|
||||
|
||||
while ((read = zipIn.read(bytesIn)) != -1) {
|
||||
bos.write(bytesIn, 0, read);
|
||||
}
|
||||
bos.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,9 +31,11 @@ public class Constants {
|
||||
|
||||
public static final String DEFAULT_CONFIG_FILE_LOCATION = CarbonUtils.getCarbonConfigDirPath() + File.separator +
|
||||
Constants.APPLICATION_CONFIG_XML_FILE;
|
||||
//can remove
|
||||
public static final String PLATFORMS_DEPLOYMENT_DIR_NAME = "platforms";
|
||||
public static final String PLATFORM_DEPLOYMENT_EXT = ".xml";
|
||||
public static final String DEFAULT_VERSION = "1.0.0";
|
||||
public static final String PAYLOAD = "Payload";
|
||||
public static final String PLIST_NAME = "Info.plist";
|
||||
public static final String CF_BUNDLE_VERSION = "CFBundleVersion";
|
||||
public static final String APP_EXTENSION = ".app";
|
||||
|
||||
/**
|
||||
* Database types supported by Application Management.
|
||||
|
||||
@ -397,7 +397,7 @@ public interface ApplicationManagementAPI {
|
||||
response = ErrorResponse.class)
|
||||
})
|
||||
Response updateApplicationArtifact(
|
||||
@ApiParam(name = "appType", value = "Type of the application i.e Android, iOS etc", required = true)
|
||||
@ApiParam(name = "appType", value = "Type of the application i.e Android, IOS etc", required = true)
|
||||
@PathParam("appType") String appType,
|
||||
@ApiParam(name = "id", value = "Id of the application", required = true)
|
||||
@PathParam("uuid") int applicationId,
|
||||
|
||||
@ -138,10 +138,16 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
if (binaryFile == null) {
|
||||
if (binaryFile == null && !ApplicationType.WEB_CLIP.toString().equals(application.getType())) {
|
||||
log.error("Binary file is not uploaded for the application release of " + application.getName() +
|
||||
" of application type " + application.getType());
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}else if(binaryFile == null && ApplicationType.WEB_CLIP.toString().equals(application.getType())){
|
||||
applicationRelease = applicationStorageManager.uploadReleaseArtifact(applicationRelease, application.getType(),
|
||||
null);
|
||||
}else if (binaryFile != null && !ApplicationType.WEB_CLIP.toString().equals(application.getType())){
|
||||
applicationRelease = applicationStorageManager.uploadReleaseArtifact(applicationRelease, application.getType(),
|
||||
binaryFile.getDataHandler().getInputStream());
|
||||
}
|
||||
|
||||
iconFileStream = iconFile.getDataHandler().getInputStream();
|
||||
@ -151,8 +157,6 @@ public class ApplicationManagementAPIImpl implements ApplicationManagementAPI {
|
||||
attachments.add(screenshot.getDataHandler().getInputStream());
|
||||
}
|
||||
|
||||
applicationRelease = applicationStorageManager.uploadReleaseArtifact(applicationRelease, application.getType(),
|
||||
binaryFile.getDataHandler().getInputStream());
|
||||
|
||||
if (applicationRelease.getAppStoredLoc() == null || applicationRelease.getAppHashValue() == null) {
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user