/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.marketplace.service.impl;
import com.liferay.document.library.kernel.exception.NoSuchFileException;
import com.liferay.document.library.kernel.store.DLStoreUtil;
import com.liferay.marketplace.bundle.BundleManagerUtil;
import com.liferay.marketplace.exception.AppPropertiesException;
import com.liferay.marketplace.exception.AppTitleException;
import com.liferay.marketplace.exception.AppVersionException;
import com.liferay.marketplace.model.App;
import com.liferay.marketplace.model.Module;
import com.liferay.marketplace.service.base.AppLocalServiceBaseImpl;
import com.liferay.marketplace.util.ContextUtil;
import com.liferay.marketplace.util.comparator.AppTitleComparator;
import com.liferay.portal.kernel.deploy.DeployManagerUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.CompanyConstants;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.plugin.PluginPackage;
import com.liferay.portal.kernel.util.FileUtil;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.PortalUtil;
import com.liferay.portal.kernel.util.PropertiesUtil;
import com.liferay.portal.kernel.util.ReleaseInfo;
import com.liferay.portal.kernel.util.StreamUtil;
import com.liferay.portal.kernel.util.StringBundler;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.SystemProperties;
import com.liferay.portal.kernel.util.Validator;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.osgi.framework.Bundle;
/**
* @author Ryan Park
* @author Joan Kim
*/
public class AppLocalServiceImpl extends AppLocalServiceBaseImpl {
@Override
public void clearInstalledAppsCache() {
_installedApps = null;
_prepackagedApps = null;
}
@Override
public App deleteApp(App app) {
// App
clearInstalledAppsCache();
appPersistence.remove(app);
// Module
List<Module> modules = modulePersistence.findByAppId(app.getAppId());
for (Module module : modules) {
moduleLocalService.deleteModule(module);
}
// File
try {
DLStoreUtil.deleteFile(
app.getCompanyId(), CompanyConstants.SYSTEM, app.getFilePath());
}
catch (Exception e) {
if (_log.isWarnEnabled()) {
_log.warn(e, e);
}
}
return app;
}
@Override
public App deleteApp(long appId) throws PortalException {
App app = appPersistence.findByPrimaryKey(appId);
return deleteApp(app);
}
@Override
public App fetchRemoteApp(long remoteAppId) {
return appPersistence.fetchByRemoteAppId(remoteAppId);
}
@Override
public List<App> getApps(String category) {
return appPersistence.findByCategory(category);
}
@Override
public List<App> getInstalledApps() {
if (_installedApps != null) {
return _installedApps;
}
List<App> installedApps = new ArrayList<>();
// Core app
App coreApp = appPersistence.create(0);
coreApp.setTitle("Liferay Core");
coreApp.setDescription("Plugins bundled with Liferay Portal.");
coreApp.setVersion(ReleaseInfo.getVersion());
coreApp.addContextName(PortalUtil.getServletContextName());
installedApps.add(coreApp);
// Deployed apps
List<PluginPackage> pluginPackages =
DeployManagerUtil.getInstalledPluginPackages();
for (PluginPackage pluginPackage : pluginPackages) {
List<Module> modules = modulePersistence.findByContextName(
pluginPackage.getContext());
boolean installedApp = false;
for (Module module : modules) {
App app = appPersistence.fetchByPrimaryKey(module.getAppId());
if ((app != null) && app.isInstalled()) {
installedApp = true;
break;
}
}
if (installedApp) {
continue;
}
App app = appPersistence.create(0);
app.setTitle(pluginPackage.getName());
app.setDescription(pluginPackage.getLongDescription());
app.setVersion(pluginPackage.getVersion());
app.setRequired(true);
app.addContextName(pluginPackage.getContext());
installedApps.add(app);
}
// Marketplace apps
List<App> apps = appPersistence.findAll();
for (App app : apps) {
if (app.isInstalled()) {
installedApps.add(app);
}
}
installedApps = ListUtil.sort(installedApps, new AppTitleComparator());
_installedApps = installedApps;
return _installedApps;
}
@Override
public List<App> getInstalledApps(String category) {
List<App> apps = appPersistence.findByCategory(category);
List<App> installedApps = new ArrayList<>(apps.size());
for (App app : apps) {
if (app.isInstalled()) {
installedApps.add(app);
}
}
return installedApps;
}
@Override
public Map<String, String> getPrepackagedApps() {
if (_prepackagedApps != null) {
return _prepackagedApps;
}
Map<String, String> prepackagedApps = new HashMap<>();
List<Bundle> bundles = BundleManagerUtil.getInstalledBundles();
for (Bundle bundle : bundles) {
Dictionary<String, String> headers = bundle.getHeaders();
boolean liferayRelengBundle = GetterUtil.getBoolean(
headers.get("Liferay-Releng-Bundle"));
if (!liferayRelengBundle) {
continue;
}
prepackagedApps.put(
bundle.getSymbolicName(), String.valueOf(bundle.getVersion()));
}
_prepackagedApps = prepackagedApps;
return _prepackagedApps;
}
@Override
public void installApp(long remoteAppId) throws PortalException {
App app = appPersistence.findByRemoteAppId(remoteAppId);
if (!DLStoreUtil.hasFile(
app.getCompanyId(), CompanyConstants.SYSTEM,
app.getFilePath())) {
throw new NoSuchFileException();
}
InputStream inputStream = null;
try {
inputStream = DLStoreUtil.getFileAsStream(
app.getCompanyId(), CompanyConstants.SYSTEM, app.getFilePath());
if (inputStream == null) {
throw new IOException(
"Unable to open file at " + app.getFilePath());
}
StringBundler sb = new StringBundler(5);
sb.append(SystemProperties.get(SystemProperties.TMP_DIR));
sb.append(StringPool.SLASH);
sb.append(encodeSafeFileName(app.getTitle()));
sb.append(StringPool.PERIOD);
sb.append(FileUtil.getExtension(app.getFileName()));
File file = new File(sb.toString());
FileUtil.write(file, inputStream);
List<Bundle> bundles = BundleManagerUtil.installLPKG(file);
moduleLocalService.deleteModules(app.getAppId());
for (int i = 1; i < bundles.size(); i++) {
Bundle bundle = bundles.get(i);
Dictionary<String, String> headers = bundle.getHeaders();
String contextName = ContextUtil.getContextName(
GetterUtil.getString(headers.get("Web-ContextPath")));
moduleLocalService.addModule(
app.getAppId(), bundle.getSymbolicName(),
String.valueOf(bundle.getVersion()), contextName);
}
}
catch (IOException ioe) {
throw new PortalException(ioe);
}
catch (Exception e) {
_log.error(e, e);
}
finally {
StreamUtil.cleanUp(inputStream);
clearInstalledAppsCache();
}
}
@Override
public void uninstallApp(long remoteAppId) throws PortalException {
clearInstalledAppsCache();
App app = appPersistence.findByRemoteAppId(remoteAppId);
List<Module> modules = modulePersistence.findByAppId(app.getAppId());
for (Module module : modules) {
moduleLocalService.deleteModule(module.getModuleId());
if (module.isBundle()) {
BundleManagerUtil.uninstallBundle(
module.getBundleSymbolicName(), module.getBundleVersion());
continue;
}
if (hasDependentApp(module)) {
continue;
}
try {
DeployManagerUtil.undeploy(module.getContextName());
}
catch (Exception e) {
_log.error(e, e);
}
}
}
@Override
public App updateApp(long userId, File file) throws PortalException {
Properties properties = getMarketplaceProperties(file);
if (properties == null) {
throw new AppPropertiesException(
"Unable to read liferay-marketplace.properties");
}
long remoteAppId = GetterUtil.getLong(
properties.getProperty("remote-app-id"));
String title = properties.getProperty("title");
String description = properties.getProperty("description");
String category = properties.getProperty("category");
String iconURL = properties.getProperty("icon-url");
String version = properties.getProperty("version");
boolean required = GetterUtil.getBoolean(
properties.getProperty("required"));
return updateApp(
userId, remoteAppId, title, description, category, iconURL, version,
required, file);
}
@Override
public App updateApp(
long userId, long remoteAppId, String title, String description,
String category, String iconURL, String version, boolean required,
File file)
throws PortalException {
// App
User user = userLocalService.fetchUser(userId);
Date now = new Date();
validate(title, version);
App app = appPersistence.fetchByRemoteAppId(remoteAppId);
if (app == null) {
long appId = counterLocalService.increment();
app = appPersistence.create(appId);
}
if (user != null) {
app.setCompanyId(user.getCompanyId());
app.setUserId(user.getUserId());
app.setUserName(user.getFullName());
}
app.setCreateDate(now);
app.setModifiedDate(now);
app.setRemoteAppId(remoteAppId);
app.setTitle(title);
app.setDescription(description);
app.setCategory(category);
app.setIconURL(iconURL);
app.setVersion(version);
app.setRequired(required);
appPersistence.update(app);
// File
if (file != null) {
try {
DLStoreUtil.deleteFile(
app.getCompanyId(), CompanyConstants.SYSTEM,
app.getFilePath());
}
catch (Exception e) {
}
DLStoreUtil.addFile(
app.getCompanyId(), CompanyConstants.SYSTEM, app.getFilePath(),
false, file);
}
clearInstalledAppsCache();
return app;
}
protected String encodeSafeFileName(String fileName) {
if (fileName == null) {
return StringPool.BLANK;
}
fileName = FileUtil.encodeSafeFileName(fileName);
return StringUtil.replace(
fileName, _SAFE_FILE_NAME_1, _SAFE_FILE_NAME_2);
}
protected Properties getMarketplaceProperties(File liferayPackageFile) {
InputStream inputStream = null;
ZipFile zipFile = null;
try {
zipFile = new ZipFile(liferayPackageFile);
ZipEntry zipEntry = zipFile.getEntry(
"liferay-marketplace.properties");
inputStream = zipFile.getInputStream(zipEntry);
String propertiesString = StringUtil.read(inputStream);
return PropertiesUtil.load(propertiesString);
}
catch (IOException ioe) {
return null;
}
finally {
if (zipFile != null) {
try {
zipFile.close();
}
catch (IOException ioe) {
}
}
StreamUtil.cleanUp(inputStream);
}
}
protected boolean hasDependentApp(Module module) throws PortalException {
List<Module> modules = modulePersistence.findByContextName(
module.getContextName());
for (Module curModule : modules) {
App app = appPersistence.findByPrimaryKey(curModule.getAppId());
if (curModule.getAppId() == module.getAppId()) {
continue;
}
if (app.isInstalled()) {
return true;
}
}
return false;
}
protected void validate(String title, String version)
throws PortalException {
if (Validator.isNull(title)) {
throw new AppTitleException();
}
if (Validator.isNull(version)) {
throw new AppVersionException();
}
}
/**
* @see com.liferay.portal.util.FileImpl#_SAFE_FILE_NAME_1
*/
private static final String[] _SAFE_FILE_NAME_1 = {
StringPool.BACK_SLASH, StringPool.COLON, StringPool.GREATER_THAN,
StringPool.LESS_THAN, StringPool.PIPE, StringPool.QUESTION,
StringPool.QUOTE, StringPool.SLASH, StringPool.STAR
};
/**
* @see com.liferay.portal.util.FileImpl#_SAFE_FILE_NAME_2
*/
private static final String[] _SAFE_FILE_NAME_2 = {
"_BSL_", "_COL_", "_GT_", "_LT_", "_PIP_", "_QUE_", "_QUO_", "_SL_",
"_ST_"
};
private static final Log _log = LogFactoryUtil.getLog(
AppLocalServiceImpl.class);
private List<App> _installedApps;
private Map<String, String> _prepackagedApps;
}