Skip to content

Commit

Permalink
fix(classloader): classloader reset in ServerAPIImpl (#3269)
Browse files Browse the repository at this point in the history
Only in some specific cases we need to reset the current classloader after an API call.

For example, when calling an API method in a connector, the current classloader will be switched from PROCESS classloader to TENANT classloader which may produce undesired side effects.

However, in other case it is important to not reset it.

For example, when calling the cleanAndUninstallBdm API the service will refresh the tenant classloader removing the BDM classes. We want to keep this version of the classloader and not resest it to the version containing the removed classes.

Closes RUNTIME-1919
  • Loading branch information
rbioteau authored Dec 3, 2024
1 parent f349f3a commit 8835137
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.apache.commons.io.IOUtils;
import org.assertj.core.api.Assertions;
import org.bonitasoft.engine.api.TenantAdministrationAPI;
import org.bonitasoft.engine.bpm.bar.BarResource;
import org.bonitasoft.engine.bpm.process.ProcessDefinition;
import org.bonitasoft.engine.bpm.process.impl.ProcessDefinitionBuilder;
Expand All @@ -37,6 +38,7 @@
import org.bonitasoft.engine.io.IOUtil;
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.search.SearchResult;
import org.bonitasoft.engine.tenant.TenantResource;
import org.bonitasoft.engine.test.APITestUtil;
import org.bonitasoft.engine.test.junit.BonitaEngineRule;
import org.junit.Rule;
Expand Down Expand Up @@ -74,9 +76,24 @@ private void clean() throws BonitaException {
cleanRoles();
cleanSupervisors();
checkThereAreNoWaitingEventsLeft();
cleanBdm();
logoutOnTenant();
}

private void cleanBdm() throws BonitaException {
TenantAdministrationAPI tenantAdministrationAPI = getTenantAdministrationAPI();
if (tenantAdministrationAPI.getBusinessDataModelResource() != TenantResource.NONE) {
if (!tenantAdministrationAPI.isPaused()) {
tenantAdministrationAPI.pause();
}
try {
tenantAdministrationAPI.cleanAndUninstallBusinessDataModel();
} finally {
tenantAdministrationAPI.resume();
}
}
}

private void checkThereAreNoWaitingEventsLeft() throws BonitaException {
SearchResult<?> searchResult = (SearchResult<?>) getCommandAPI()
.execute("searchWaitingEventsCommand",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
import org.bonitasoft.engine.search.SearchOptionsBuilder;
import org.bonitasoft.engine.service.ServiceAccessorSingleton;
import org.bonitasoft.engine.tenant.TenantResource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

Expand All @@ -78,16 +77,6 @@ public void before() throws Exception {
initFirstInstall();
}

@After
public void after() throws Exception {
if (!getTenantAdministrationAPI().isPaused()) {
getTenantAdministrationAPI().pause();
getTenantAdministrationAPI().cleanAndUninstallBusinessDataModel();
getTenantAdministrationAPI().resume();
}
logoutOnTenant();
}

private void initFirstInstall() throws Exception {
// ensure application did not exist initially:
assertThatExceptionOfType(ApplicationNotFoundException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ public class APITestUtil extends PlatformTestUtil {
public void clearSynchroRepository() {
try {
loginOnDefaultTenantWithDefaultTechnicalUser();
if (getTenantAdministrationAPI().isPaused()) {
getTenantAdministrationAPI().resume();
}
ClientEventUtil.clearRepo(getCommandAPI());
logoutOnTenant();
} catch (final Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.text.MessageFormat;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
Expand All @@ -34,6 +35,7 @@
import org.bonitasoft.engine.api.impl.transaction.CustomTransactions;
import org.bonitasoft.engine.api.internal.ServerAPI;
import org.bonitasoft.engine.api.internal.ServerWrappedException;
import org.bonitasoft.engine.classloader.BonitaClassLoader;
import org.bonitasoft.engine.classloader.ClassLoaderIdentifier;
import org.bonitasoft.engine.classloader.ClassLoaderService;
import org.bonitasoft.engine.classloader.SClassLoaderException;
Expand All @@ -42,7 +44,13 @@
import org.bonitasoft.engine.core.login.LoginService;
import org.bonitasoft.engine.core.platform.login.PlatformLoginService;
import org.bonitasoft.engine.dependency.model.ScopeType;
import org.bonitasoft.engine.exception.*;
import org.bonitasoft.engine.exception.BonitaContextException;
import org.bonitasoft.engine.exception.BonitaException;
import org.bonitasoft.engine.exception.BonitaHomeConfigurationException;
import org.bonitasoft.engine.exception.BonitaHomeNotSetException;
import org.bonitasoft.engine.exception.BonitaRuntimeException;
import org.bonitasoft.engine.exception.TenantStatusException;
import org.bonitasoft.engine.exception.UnavailableLockException;
import org.bonitasoft.engine.lock.BonitaLock;
import org.bonitasoft.engine.lock.LockService;
import org.bonitasoft.engine.maintenance.MaintenanceDetails;
Expand All @@ -56,7 +64,11 @@
import org.bonitasoft.engine.service.APIAccessResolver;
import org.bonitasoft.engine.service.ServiceAccessor;
import org.bonitasoft.engine.service.impl.ServiceAccessorFactory;
import org.bonitasoft.engine.session.*;
import org.bonitasoft.engine.session.APISession;
import org.bonitasoft.engine.session.InvalidSessionException;
import org.bonitasoft.engine.session.PlatformSession;
import org.bonitasoft.engine.session.Session;
import org.bonitasoft.engine.session.SessionService;
import org.bonitasoft.engine.sessionaccessor.SessionAccessor;
import org.bonitasoft.engine.transaction.UserTransactionService;
import org.slf4j.Logger;
Expand Down Expand Up @@ -136,25 +148,35 @@ public Object invokeMethod(final Map<String, Serializable> options, final String
}
} catch (final BonitaRuntimeException | BonitaException bre) {
fillGlobalContextForException(session, bre);
// reset class loader
Thread.currentThread().setContextClassLoader(baseClassLoader);
throw createServerWrappedException(bre);
} catch (final UndeclaredThrowableException ute) {
// reset class loader
Thread.currentThread().setContextClassLoader(baseClassLoader);
throw createServerWrappedException(ute);
} catch (final Throwable cause) {
final BonitaRuntimeException throwableToWrap = wrapThrowable(cause);
fillGlobalContextForException(session, throwableToWrap);
// reset class loader
Thread.currentThread().setContextClassLoader(baseClassLoader);
throw createServerWrappedException(throwableToWrap);
} finally {
cleanSessionIfNeeded(sessionAccessor);
// Reset the original classloader when not an equivalent BonitaClassLoader
// We do not reset the the classloader if the original classloader and the current classloader are BonitaClassloader having the same name
// e.g. for the cleanAndUninstallBusinessDataModel API method that reset the current classloader
if (shouldResetClassloader(baseClassLoader, Thread.currentThread().getContextClassLoader())) {
Thread.currentThread().setContextClassLoader(baseClassLoader);
}
logger.trace("End Server API call {} {}", apiInterfaceName, methodName);
}
}

private boolean shouldResetClassloader(ClassLoader baseClassLoader, ClassLoader currentClassloader) {
if (currentClassloader instanceof BonitaClassLoader bonitaClassLoader
&& baseClassLoader instanceof BonitaClassLoader bonitaBaseClassLoader) {
// Classloader name is different, reset it
return !Objects.equals(bonitaClassLoader.getName(), bonitaBaseClassLoader.getName());
} else {
return true;
}
}

protected BonitaRuntimeException wrapThrowable(final Throwable cause) {
return new BonitaRuntimeException(cause);
}
Expand Down

0 comments on commit 8835137

Please sign in to comment.