Skip to content

Commit

Permalink
LDEV-4763 - add possibility to remove extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeloffner committed Dec 7, 2023
1 parent 79f3f9b commit 153e5fc
Show file tree
Hide file tree
Showing 10 changed files with 315 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ public static Resource toResourceExisting(Config config, String path, Resource d
Resource res;
if (config == null) res = ResourcesImpl.getFileResourceProvider().getResource(path);
else res = config.getResource(path);

if (res.exists()) return res;
return defaultValue;
}
Expand Down Expand Up @@ -1637,4 +1636,10 @@ public static boolean allowMatching(ResourceProvider provider, boolean defaultVa
}
return am;
}

public static void createParentDirectoryIfNecessary(Resource file) throws IOException {
Resource parent = file.getParentResource();
if (parent == null) throw new IOException("there is no parent directory for [" + file + "]");
if (!parent.isDirectory()) parent.createDirectory(true);
}
}
107 changes: 86 additions & 21 deletions core/src/main/java/lucee/runtime/config/ConfigAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -4436,8 +4436,7 @@ public void removeRHExtension(String id) throws PageException {
if (child == null) continue;

try {
rhe = new RHExtension(config, Caster.toString(child.get(KeyConstants._id), null), Caster.toString(child.get(KeyConstants._version), null), null,
RHExtension.INSTALL_OPTION_NOT);
rhe = new RHExtension(config, Caster.toString(child.get(KeyConstants._id), null), Caster.toString(child.get(KeyConstants._version), null));
}
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
Expand Down Expand Up @@ -4577,20 +4576,22 @@ public void updateArchive(Config config, Resource archive) throws PageException
}
}

public static void _updateRHExtension(ConfigPro config, Resource ext, boolean reload, boolean force, boolean moveIfNecessary) throws PageException {
public static RHExtension _updateRHExtension(ConfigPro config, Resource ext, boolean reload, boolean force, short action) throws PageException {
try {
ConfigAdmin admin = new ConfigAdmin(config, null);
admin.updateRHExtension(config, ext, reload, force, moveIfNecessary);
return admin.updateRHExtension(config, ext, reload, force, action);
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}

public void updateRHExtension(Config config, Resource ext, boolean reload, boolean force, boolean moveIfNecessary) throws PageException {
public RHExtension updateRHExtension(Config config, Resource ext, boolean reload, boolean force, short action) throws PageException {
RHExtension rhext;
try {
rhext = new RHExtension(config, ext, moveIfNecessary);
rhext = new RHExtension(config, ext);
if (RHExtension.ACTION_COPY == action) rhext.copyToInstalled();
else if (RHExtension.ACTION_MOVE == action) rhext.moveToInstalled();
rhext.validate();
}
catch (Throwable t) {
Expand All @@ -4599,10 +4600,30 @@ public void updateRHExtension(Config config, Resource ext, boolean reload, boole
throw Caster.toPageException(t);
}
updateRHExtension(config, rhext, reload, force);
return rhext;
}

public void updateRHExtension(Config config, RHExtension rhext, boolean reload, boolean force) throws PageException {
public static void _updateRHExtension(ConfigPro config, RHExtension rhext, boolean reload, boolean force) throws PageException {
try {
ConfigAdmin admin = new ConfigAdmin(config, null);
admin.updateRHExtension(config, rhext, reload, force);
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}

public static void _removeRHExtension(ConfigPro config, RHExtension rhext, RHExtension replacementRH, boolean deleteExtension) throws PageException {
try {
ConfigAdmin admin = new ConfigAdmin(config, null);
admin.removeRHExtension(config, rhext, replacementRH, deleteExtension);
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}

public void updateRHExtension(Config config, RHExtension rhext, boolean reload, boolean force) throws PageException {
try {
if (!force && ConfigAdmin.hasRHExtensionInstalled((ConfigPro) config, rhext.toExtensionDefinition()) != null) {
throw new ApplicationException("the extension " + rhext.getName() + " (id: " + rhext.getId() + ") in version " + rhext.getVersion() + " is already installed");
Expand All @@ -4622,7 +4643,9 @@ public void updateRHExtension(Config config, RHExtension rhext, boolean reload,
if (existingRH.getVersion().compareTo(rhext.getVersion()) == 0) {
removeRHExtension(config, existingRH, rhext, false);
}
else removeRHExtension(config, existingRH, rhext, true);
else {
removeRHExtension(config, existingRH, rhext, true);
}

}
// INSTALL
Expand Down Expand Up @@ -5285,7 +5308,7 @@ private void removeRHExtension(Config config, RHExtension rhe, RHExtension repla
ExceptionUtil.rethrowIfNecessary(t);
// failed to uninstall, so we install it again
try {
updateRHExtension(config, rhe.getExtensionFile(), true, true, true);
updateRHExtension(config, rhe.getExtensionFile(), true, true, RHExtension.ACTION_MOVE);
// RHExtension.install(config, rhe.getExtensionFile());
}
catch (Throwable t2) {
Expand Down Expand Up @@ -6214,7 +6237,7 @@ public BundleDefinition[] _removeExtension(ConfigPro config, String extensionID,
String version = Caster.toString(el.get(KeyConstants._version, null), null);
Resource file = RHExtension.getMetaDataFile(config, id, version);
if (file.isFile()) file.delete();
file = RHExtension.getExtensionInstalledFile(config, id, version);
file = RHExtension.getExtensionInstalledFile(config, id, version, false);
if (file.isFile()) file.delete();

return bundles;
Expand Down Expand Up @@ -6320,23 +6343,65 @@ public BundleDefinition[] _updateExtension(ConfigPro config, RHExtension ext) th
Struct el;
String id;
BundleDefinition[] old;
boolean hasNoneId = false;

// update check by id match
for (int i = keys.length - 1; i >= 0; i--) {
key = keys[i];
el = Caster.toStruct(children.get(key, null), null);
if (el == null) continue;
id = Caster.toString(el.get(KeyConstants._id), null);
if (ext.getId().equalsIgnoreCase(id)) {
old = RHExtension.toBundleDefinitions(ConfigWebUtil.getAsString("bundles", el, null)); // get existing bundles before populate new ones
ext.populate(el, false);
old = minus(old, OSGiUtil.toBundleDefinitions(ext.getBundles()));
return old;
try {
key = keys[i];
el = Caster.toStruct(children.get(key, null), null);
if (el == null) continue;
id = Caster.toString(el.get(KeyConstants._id, null), null);
if (StringUtil.isEmpty(id)) hasNoneId = true;
if (ext.getId().equalsIgnoreCase(id)) {
old = RHExtension.toBundleDefinitions(ConfigWebUtil.getAsString("bundles", el, null)); // get existing bundles before populate new ones
ext.populate(el, false);
old = minus(old, OSGiUtil.toBundleDefinitions(ext.getBundles()));
return old;
}
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}
// update everything else than id TODO make more streamline
if (hasNoneId) {
for (int i = keys.length - 1; i >= 0; i--) {
try {
key = keys[i];
el = Caster.toStruct(children.get(key, null), null);
if (el == null || el.get(KeyConstants._id, null) != null) continue;

String res = Caster.toString(el.get(KeyConstants._resource, null), null);
if (StringUtil.isEmpty(res)) res = Caster.toString(el.get(KeyConstants._path, null), null);
if (StringUtil.isEmpty(res)) res = Caster.toString(el.get(KeyConstants._url, null), null);

Resource r;
if (!StringUtil.isEmpty(res) && (r = ResourceUtil.toResourceExisting(config, res, null)) != null) {
try {
RHExtension _ext = new RHExtension(config, r);// TODO not load it again!
if (_ext != null && ext.getId().equalsIgnoreCase(ext.getId())) {
old = RHExtension.toBundleDefinitions(ConfigWebUtil.getAsString("bundles", el, null)); // get existing bundles before populate new ones
ext.populate(el, false);
old = minus(old, OSGiUtil.toBundleDefinitions(ext.getBundles()));
return old;
}
}
catch (Exception ee) {
}
}
}
catch (Exception e) {
throw Caster.toPageException(e);
}
}
}

// Insert
el = new StructImpl(Struct.TYPE_LINKED);
ext.populate(el, false);
children.appendEL(el);

return null;
}

Expand Down Expand Up @@ -6370,7 +6435,7 @@ private RHExtension getRHExtension(final ConfigPro config, final String id, fina
if (!id.equals(_id)) continue;

try {
return new RHExtension(config, _id, Caster.toString(tmp.get(KeyConstants._version), null), null, RHExtension.INSTALL_OPTION_NOT);
return new RHExtension(config, _id, Caster.toString(tmp.get(KeyConstants._version), null));
}
catch (Exception e) {
return defaultValue;
Expand Down Expand Up @@ -6409,7 +6474,7 @@ private RHExtension _hasRHExtensionInstalled(ConfigPro config, ExtensionDefintio
v = Caster.toString(sct.get(KeyConstants._version, null), null);
if (!RHExtension.isInstalled(config, id, v)) continue;

if (ed.equals(new ExtensionDefintion(id, v))) return new RHExtension(config, id, v, null, RHExtension.INSTALL_OPTION_NOT);
if (ed.equals(new ExtensionDefintion(id, v))) return new RHExtension(config, id, v);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ public List<ExtensionDefintion> loadLocalExtensions(boolean validate) {
}
if (ed == null) {
try {
ext = new RHExtension(this, locReses[i], false);
ext = new RHExtension(this, locReses[i]);
ed = new ExtensionDefintion(ext.getId(), ext.getVersion());
ed.setSource(ext);

Expand Down
52 changes: 48 additions & 4 deletions core/src/main/java/lucee/runtime/config/ConfigWebFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import lucee.commons.io.log.LoggerAndSourceData;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.ResourcesImpl;
import lucee.commons.io.res.filter.ExtensionResourceFilter;
import lucee.commons.io.res.type.cfml.CFMLResourceProvider;
import lucee.commons.io.res.type.s3.DummyS3ResourceProvider;
import lucee.commons.io.res.util.ResourceUtil;
Expand Down Expand Up @@ -4848,13 +4849,23 @@ private static void _loadCFX(ConfigServerImpl configServer, ConfigImpl config, S
* @param log
*/
private static void _loadExtensionBundles(ConfigServerImpl cs, ConfigImpl config, Struct root, Log log) {
Log deployLog = config.getLog("deploy");
if (deployLog != null) log = deployLog;

try {
boolean changed = false;
if (config instanceof ConfigServer) {
changed = ConfigFactory.modeChange(config.getConfigDir(), config.getAdminMode() == ConfigImpl.ADMINMODE_MULTI ? "multi" : "single", false);
}
Array children = ConfigWebUtil.getAsArray("extensions", root);
RHExtension.removeDuplicates(children);
try {
RHExtension.removeDuplicates(children);
}
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
log(config, log, t);
}

String strBundles;
List<RHExtension> extensions = new ArrayList<RHExtension>();
RHExtension rhe;
Expand All @@ -4863,29 +4874,62 @@ private static void _loadExtensionBundles(ConfigServerImpl cs, ConfigImpl config
Entry<Key, Object> e;
Struct child;
String id;
Set<Resource> installedFiles = new HashSet<>();
Set<String> installedIds = new HashSet<>();
// load and install extension if necessary
while (it.hasNext()) {
child = Caster.toStruct(it.next(), null);
if (child == null) continue;
id = Caster.toString(child.get(KeyConstants._id, null), null);
if (StringUtil.isEmpty(id)) continue;

BundleInfo[] bfsq;
try {
String res = Caster.toString(child.get(KeyConstants._resource, null), null);
if (StringUtil.isEmpty(res)) res = Caster.toString(child.get(KeyConstants._path, null), null);
if (StringUtil.isEmpty(res)) res = Caster.toString(child.get(KeyConstants._url, null), null);

rhe = new RHExtension(config, id, Caster.toString(child.get(KeyConstants._version, null), null), res,
changed ? RHExtension.INSTALL_OPTION_FORCE : RHExtension.INSTALL_OPTION_IF_NECESSARY);
if (StringUtil.isEmpty(id) && StringUtil.isEmpty(res)) continue;

// we force a new installation if we have switched from single to multi mode, because extension can
// act completely different if that is the case
rhe = RHExtension.installExtension(config, id, Caster.toString(child.get(KeyConstants._version, null), null), res, changed);
if (rhe.getStartBundles()) rhe.deployBundles(config);
extensions.add(rhe);
installedFiles.add(rhe.getExtensionFile());
installedIds.add(rhe.getId());
}
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
log(config, log, t);
continue;
}
}

// uninstall extensions no longer used
Resource[] installed = RHExtension.getExtensionInstalledDir(config).listResources(new ExtensionResourceFilter("lex"));
if (installed != null) {
for (Resource r: installed) {
if (!installedFiles.contains(r)) {

// is maybe a diff version installed?
RHExtension ext = new RHExtension(config, r);
if (!installedIds.contains(ext.getId())) {
if (log != null) log.info("extension",
"Found the extension [" + ext + "] in the installed folder that is not present in the configuration in any version, so we will uninstall it");
ConfigAdmin._removeRHExtension(config, ext, null, true);
if (log != null) log.info("extension", "removed extension [" + ext + "]");
}
else {
if (log != null) log.info("extension", "Found the extension [" + ext
+ "] in the installed folder that is in a different version in the configuraton, so we delete that extension file.");
r.delete();
}

}
}
}

// set
config.setExtensions(extensions.toArray(new RHExtension[extensions.size()]));
}
catch (Throwable t) {
Expand Down
Loading

0 comments on commit 153e5fc

Please sign in to comment.