NIFI-13757 Improve handling when a NAR contains an extension that produces an Error (#9274)

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Bryan Bende 2024-09-17 22:41:45 -04:00 committed by GitHub
parent 9b7b246dcc
commit 44abe39983
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 37 additions and 23 deletions

View File

@ -114,13 +114,15 @@ public class DocGenerator {
}
}
case JAVA -> {
final Class<?> extensionClass = extensionManager.getClass(extensionDefinition);
final Class<? extends ConfigurableComponent> componentClass = extensionClass.asSubclass(ConfigurableComponent.class);
// Catch Throwable here to protect against an extension that may have been registered, but throws an Error when attempting to access the Class,
// this method is called during start-up, and we don't want to fail starting just because one bad extension class can't be loaded
try {
final Class<?> extensionClass = extensionManager.getClass(extensionDefinition);
final Class<? extends ConfigurableComponent> componentClass = extensionClass.asSubclass(ConfigurableComponent.class);
logger.debug("Documentation generation started: Component Class [{}]", componentClass);
document(extensionManager, componentDirectory, componentClass, coordinate);
} catch (Exception e) {
logger.warn("Documentation generation failed: Component Class [{}]", componentClass, e);
} catch (Throwable t) {
logger.warn("Documentation generation failed: Component Class [{}]", extensionDefinition.getImplementationClassName(), t);
}
}
}

View File

@ -99,6 +99,13 @@ public class NarInstallTask implements Runnable {
final BundleCoordinate loadedCoordinate = loadedBundle.getBundleDetails().getCoordinate();
LOGGER.info("NAR [{}] was installed", loadedCoordinate);
if (loadedCoordinate.equals(coordinate)) {
// If the NAR that was just uploaded was successfully loaded, attempt to access the class of each extension to prove that each
// class can load successfully, if not then we want to bounce out to the catch block and set the state as FAILED
final Set<ExtensionDefinition> loadedExtensionDefinitions = extensionManager.getTypes(coordinate);
for (final ExtensionDefinition loadedExtensionDefinition : loadedExtensionDefinitions) {
final Class<?> extensionClass = extensionManager.getClass(loadedExtensionDefinition);
LOGGER.debug("Loaded [{}] from bundle [{}]", extensionClass.getCanonicalName(), coordinate);
}
narNode.setState(NarState.INSTALLED);
} else {
try {
@ -133,10 +140,10 @@ public class NarInstallTask implements Runnable {
// Notify the NAR Manager that the install task complete for the current NAR
narManager.completeInstall(narNode.getIdentifier());
} catch (final Exception e) {
LOGGER.error("Failed to install NAR [{}]", coordinate, e);
} catch (final Throwable t) {
LOGGER.error("Failed to install NAR [{}]", coordinate, t);
narNode.setState(NarState.FAILED);
narNode.setFailureMessage(e.getMessage());
narNode.setFailureMessage(t.getMessage());
}
}

View File

@ -257,6 +257,8 @@ import org.apache.nifi.web.controller.ControllerFacade;
import org.apache.nifi.web.revision.RevisionManager;
import jakarta.ws.rs.WebApplicationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.text.Collator;
@ -288,20 +290,17 @@ import java.util.stream.Collectors;
public final class DtoFactory {
@SuppressWarnings("rawtypes")
private final static Comparator<Class> CLASS_NAME_COMPARATOR = new Comparator<Class>() {
@Override
public int compare(final Class class1, final Class class2) {
return Collator.getInstance(Locale.US).compare(class1.getSimpleName(), class2.getSimpleName());
}
};
public static final String SENSITIVE_VALUE_MASK = "********";
private static final Logger logger = LoggerFactory.getLogger(DtoFactory.class);
private BulletinRepository bulletinRepository;
private ControllerServiceProvider controllerServiceProvider;
private EntityFactory entityFactory;
private Authorizer authorizer;
private ExtensionManager extensionManager;
@SuppressWarnings("rawtypes")
private final static Comparator<Class> CLASS_NAME_COMPARATOR = (class1, class2) -> Collator.getInstance(Locale.US).compare(class1.getSimpleName(), class2.getSimpleName());
public static final String SENSITIVE_VALUE_MASK = "********";
private BulletinRepository bulletinRepository;
private ControllerServiceProvider controllerServiceProvider;
private EntityFactory entityFactory;
private Authorizer authorizer;
private ExtensionManager extensionManager;
public ControllerConfigurationDTO createControllerConfigurationDto(final ControllerFacade controllerFacade) {
final ControllerConfigurationDTO dto = new ControllerConfigurationDTO();
@ -3291,9 +3290,15 @@ public final class DtoFactory {
continue;
}
final Class cls = extensionManager.getClass(extensionDefinition);
if (cls != null) {
classBundles.put(cls, extensionDefinition.getBundle());
// Catch Throwable here to protect against an extension that may have been registered, but throws an Error when attempting to access the Class,
// we don't need to fail the overall request since we can still return all the other components that are still usable
try {
final Class cls = extensionManager.getClass(extensionDefinition);
if (cls != null) {
classBundles.put(cls, extensionDefinition.getBundle());
}
} catch (final Throwable t) {
logger.warn("Unable to get extension class for [{}]", extensionDefinition.getImplementationClassName(), t);
}
}