From a7bc005f15cb7a69332dca17e8a40a56ebdaccb9 Mon Sep 17 00:00:00 2001 From: Kemix Koo Date: Sat, 29 Sep 2018 09:37:33 +0800 Subject: [PATCH] NIFI-5648: Enable to launch NiFi and ignore the failed extensions. --- .../StandardControllerServiceProvider.java | 15 +++- ...ntrollerServiceProviderFailedInitTest.java | 88 +++++++++++++++++++ .../util/FailedInitControllerService.java | 64 ++++++++++++++ .../org/apache/nifi/nar/ExtensionManager.java | 13 ++- 4 files changed, 177 insertions(+), 3 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderFailedInitTest.java create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/util/FailedInitControllerService.java diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java index 3e212c0071c5..48a79a0b8b0b 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/service/StandardControllerServiceProvider.java @@ -84,6 +84,8 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi private static final Logger logger = LoggerFactory.getLogger(StandardControllerServiceProvider.class); + public static final String PRE_MISSING = "(Missing)"; + private final StandardProcessScheduler processScheduler; private final BulletinRepository bulletinRepo; private final StateManagerProvider stateManagerProvider; @@ -137,7 +139,16 @@ public ControllerServiceNode createControllerService(final String type, final St final Class controllerServiceClass = rawClass.asSubclass(ControllerService.class); - final ControllerService originalService = controllerServiceClass.newInstance(); + ControllerService originalService = null; + try { + originalService = controllerServiceClass.newInstance(); + } catch (Throwable e) { // init failure + logger.error(String.format("Unable to create the Controller Service %s from ID %s due to %s; then creating \"Ghost\" implementation", type, id, e.getMessage()), e); + // if can't create the instance, use ghost instead + Thread.currentThread().setContextClassLoader(currentContextClassLoader); + return createGhostControllerService(type, id, bundleCoordinate); + } + final StandardControllerServiceInvocationHandler invocationHandler = new StandardControllerServiceInvocationHandler(originalService); // extract all interfaces... controllerServiceClass is non null so getAllInterfaces is non null @@ -237,7 +248,7 @@ public void setServiceNode(ControllerServiceNode serviceNode) { new Class[]{ControllerService.class}, invocationHandler); final String simpleClassName = type.contains(".") ? StringUtils.substringAfterLast(type, ".") : type; - final String componentType = "(Missing) " + simpleClassName; + final String componentType = PRE_MISSING + ' ' + simpleClassName; final LoggableComponent proxiedLoggableComponent = new LoggableComponent<>(proxiedService, bundleCoordinate, null); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderFailedInitTest.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderFailedInitTest.java new file mode 100644 index 000000000000..9d1c28dcfad3 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/StandardControllerServiceProviderFailedInitTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.controller.service; + +import org.apache.nifi.bundle.Bundle; +import org.apache.nifi.components.state.StateManager; +import org.apache.nifi.components.state.StateManagerProvider; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarClassLoaders; +import org.apache.nifi.nar.SystemBundle; +import org.apache.nifi.registry.VariableRegistry; +import org.apache.nifi.registry.variable.FileBasedVariableRegistry; +import org.apache.nifi.util.NiFiProperties; +import org.apache.nifi.util.SynchronousValidationTrigger; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; + +public class StandardControllerServiceProviderFailedInitTest { + + private static VariableRegistry variableRegistry; + private static NiFiProperties nifiProperties; + private static Bundle systemBundle; + + @BeforeClass + public static void setupSuite() throws Exception { + System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, StandardControllerServiceProviderFailedInitTest.class.getResource("/conf/nifi.properties").getFile()); + nifiProperties = NiFiProperties.createBasicNiFiProperties(null, null); + + NarClassLoaders.getInstance().init(nifiProperties.getFrameworkWorkingDirectory(), nifiProperties.getExtensionsWorkingDirectory()); + + // load the system bundle + systemBundle = SystemBundle.create(nifiProperties); + ExtensionManager.discoverExtensions(systemBundle, NarClassLoaders.getInstance().getBundles()); + + variableRegistry = new FileBasedVariableRegistry(nifiProperties.getVariableRegistryPropertiesPaths()); + } + + @Test + public void testGhostControllerService() throws Exception { + String id = "id"; + String clazz = "org.apache.nifi.controller.service.util.FailedInitControllerService"; + ControllerServiceProvider provider = new StandardControllerServiceProvider(null, null, null, new StateManagerProvider() { + @Override + public StateManager getStateManager(final String componentId) { + return Mockito.mock(StateManager.class); + } + + @Override + public void shutdown() { + } + + @Override + public void enableClusterProvider() { + } + + @Override + public void disableClusterProvider() { + } + + @Override + public void onComponentRemoved(String componentId) { + } + }, variableRegistry, nifiProperties, new SynchronousValidationTrigger()); + ControllerServiceNode node = provider.createControllerService(clazz, id, systemBundle.getBundleDetails().getCoordinate(), null, true); + + Assert.assertTrue(node instanceof StandardControllerServiceNode); + Assert.assertThat(node.getComponentType(), CoreMatchers.startsWith(StandardControllerServiceProvider.PRE_MISSING)); + Assert.assertThat(node.toString(), CoreMatchers.startsWith("GhostControllerService")); + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/util/FailedInitControllerService.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/util/FailedInitControllerService.java new file mode 100644 index 000000000000..9b92c015a359 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/service/util/FailedInitControllerService.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.controller.service.util; + +import java.util.Collection; +import java.util.List; + +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.ValidationContext; +import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.controller.ControllerServiceInitializationContext; +import org.apache.nifi.reporting.InitializationException; + +public class FailedInitControllerService implements ControllerService { + + public FailedInitControllerService() { + // FIXME, make one exception when instance + throw new NullPointerException(); + } + + @Override + public Collection validate(ValidationContext context) { + return null; + } + + @Override + public PropertyDescriptor getPropertyDescriptor(String name) { + return null; + } + + @Override + public void onPropertyModified(PropertyDescriptor descriptor, String oldValue, String newValue) { + } + + @Override + public List getPropertyDescriptors() { + return null; + } + + @Override + public String getIdentifier() { + return "id"; + } + + @Override + public void initialize(ControllerServiceInitializationContext context) throws InitializationException { + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java index f4a59f8f1529..101a3965486e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java @@ -49,6 +49,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -145,7 +146,17 @@ private static void loadExtensions(final Bundle bundle) { final boolean isReportingTask = ReportingTask.class.equals(entry.getKey()); final ServiceLoader serviceLoader = ServiceLoader.load(entry.getKey(), bundle.getClassLoader()); - for (final Object o : serviceLoader) { + final Iterator iterator = serviceLoader.iterator(); + while (iterator.hasNext()) { + Object o = null; + try { + o = iterator.next(); + } catch (Throwable e) { // load failure + logger.error(String.format( + "Unable to create the component %s from %s due to %s", entry.getKey().getSimpleName(), bundle.getBundleDetails().toString(), e.getMessage()), e); + continue; // ignore current one, don't block others to load + } + // create a cache of temp ConfigurableComponent instances, the initialize here has to happen before the checks below if ((isControllerService || isProcessor || isReportingTask) && o instanceof ConfigurableComponent) { final ConfigurableComponent configurableComponent = (ConfigurableComponent) o;