http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/init/ReportingTaskingInitializer.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/init/ReportingTaskingInitializer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/init/ReportingTaskingInitializer.java new file mode 100644 index 0000000..546e67c --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/init/ReportingTaskingInitializer.java @@ -0,0 +1,57 @@ +/* + * 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.init; + +import org.apache.nifi.annotation.lifecycle.OnShutdown; +import org.apache.nifi.components.ConfigurableComponent; +import org.apache.nifi.mock.MockComponentLogger; +import org.apache.nifi.mock.MockConfigurationContext; +import org.apache.nifi.mock.MockReportingInitializationContext; +import org.apache.nifi.nar.ExtensionManager; +import org.apache.nifi.nar.NarCloseable; +import org.apache.nifi.reporting.InitializationException; +import org.apache.nifi.reporting.ReportingInitializationContext; +import org.apache.nifi.reporting.ReportingTask; + +/** + * Initializes a ReportingTask using a MockReportingInitializationContext; + * + * + */ +public class ReportingTaskingInitializer implements ConfigurableComponentInitializer { + + @Override + public void initialize(ConfigurableComponent component) throws InitializationException { + ReportingTask reportingTask = (ReportingTask) component; + ReportingInitializationContext context = new MockReportingInitializationContext(); + try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(component.getClass(), context.getIdentifier())) { + reportingTask.initialize(context); + } + } + + @Override + public void teardown(ConfigurableComponent component) { + ReportingTask reportingTask = (ReportingTask) component; + try (NarCloseable narCloseable = NarCloseable.withComponentNarLoader(component.getClass(), component.getIdentifier())) { + + final MockConfigurationContext context = new MockConfigurationContext(); + ReflectionUtils.quietlyInvokeMethodsWithAnnotation(OnShutdown.class, reportingTask, new MockComponentLogger(), context); + } finally { + ExtensionManager.removeInstanceClassLoaderIfExists(component.getIdentifier()); + } + } +}
http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockComponentLogger.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockComponentLogger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockComponentLogger.java new file mode 100644 index 0000000..920d7eb --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockComponentLogger.java @@ -0,0 +1,258 @@ +/* + * 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.mock; + +import org.apache.nifi.logging.ComponentLog; +import org.apache.nifi.logging.LogLevel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Stubs out the functionality of a ComponentLog so that it can + * be used during initialization of a component. + * + */ +public class MockComponentLogger implements ComponentLog { + + private static final Logger logger = LoggerFactory + .getLogger(MockComponentLogger.class); + + @Override + public void warn(String msg, Throwable t) { + logger.warn(msg, t); + } + + @Override + public void warn(String msg, Object[] os) { + logger.warn(msg, os); + } + + @Override + public void warn(String msg, Object[] os, Throwable t) { + logger.warn(msg, os); + logger.warn("", t); + } + + @Override + public void warn(String msg) { + logger.warn(msg); + } + + @Override + public void trace(String msg, Throwable t) { + logger.trace(msg, t); + } + + @Override + public void trace(String msg, Object[] os) { + logger.trace(msg, os); + } + + @Override + public void trace(String msg) { + logger.trace(msg); + } + + @Override + public void trace(String msg, Object[] os, Throwable t) { + logger.trace(msg, os); + logger.trace("", t); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + @Override + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + @Override + public void info(String msg, Throwable t) { + logger.info(msg, t); + } + + @Override + public void info(String msg, Object[] os) { + logger.info(msg, os); + } + + @Override + public void info(String msg) { + logger.info(msg); + + } + + @Override + public void info(String msg, Object[] os, Throwable t) { + logger.trace(msg, os); + logger.trace("", t); + + } + + @Override + public String getName() { + return logger.getName(); + } + + @Override + public void error(String msg, Throwable t) { + logger.error(msg, t); + } + + @Override + public void error(String msg, Object[] os) { + logger.error(msg, os); + } + + @Override + public void error(String msg) { + logger.error(msg); + } + + @Override + public void error(String msg, Object[] os, Throwable t) { + logger.error(msg, os); + logger.error("", t); + } + + @Override + public void debug(String msg, Throwable t) { + logger.debug(msg, t); + } + + @Override + public void debug(String msg, Object[] os) { + logger.debug(msg, os); + } + + @Override + public void debug(String msg, Object[] os, Throwable t) { + logger.debug(msg, os); + logger.debug("", t); + } + + @Override + public void debug(String msg) { + logger.debug(msg); + } + + @Override + public void log(LogLevel level, String msg, Throwable t) { + switch (level) { + case DEBUG: + debug(msg, t); + break; + case ERROR: + case FATAL: + error(msg, t); + break; + case INFO: + info(msg, t); + break; + case TRACE: + trace(msg, t); + break; + case WARN: + warn(msg, t); + break; + } + } + + @Override + public void log(LogLevel level, String msg, Object[] os) { + switch (level) { + case DEBUG: + debug(msg, os); + break; + case ERROR: + case FATAL: + error(msg, os); + break; + case INFO: + info(msg, os); + break; + case TRACE: + trace(msg, os); + break; + case WARN: + warn(msg, os); + break; + } + } + + @Override + public void log(LogLevel level, String msg) { + switch (level) { + case DEBUG: + debug(msg); + break; + case ERROR: + case FATAL: + error(msg); + break; + case INFO: + info(msg); + break; + case TRACE: + trace(msg); + break; + case WARN: + warn(msg); + break; + } + } + + @Override + public void log(LogLevel level, String msg, Object[] os, Throwable t) { + switch (level) { + case DEBUG: + debug(msg, os, t); + break; + case ERROR: + case FATAL: + error(msg, os, t); + break; + case INFO: + info(msg, os, t); + break; + case TRACE: + trace(msg, os, t); + break; + case WARN: + warn(msg, os, t); + break; + } + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockConfigurationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockConfigurationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockConfigurationContext.java new file mode 100644 index 0000000..d1e73fb --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockConfigurationContext.java @@ -0,0 +1,48 @@ +/* + * 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.mock; + +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.PropertyValue; +import org.apache.nifi.controller.ConfigurationContext; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class MockConfigurationContext implements ConfigurationContext { + + @Override + public PropertyValue getProperty(PropertyDescriptor property) { + return null; + } + + @Override + public Map<PropertyDescriptor, String> getProperties() { + return Collections.emptyMap(); + } + + @Override + public String getSchedulingPeriod() { + return "0 secs"; + } + + @Override + public Long getSchedulingPeriod(final TimeUnit timeUnit) { + return 0L; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceInitializationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceInitializationContext.java new file mode 100644 index 0000000..b111ad2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceInitializationContext.java @@ -0,0 +1,68 @@ +/* + * 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.mock; + +import org.apache.nifi.components.state.StateManager; +import org.apache.nifi.controller.ControllerServiceInitializationContext; +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.logging.ComponentLog; + +import java.io.File; + +/** + * A Mock ControllerServiceInitializationContext so that ControllerServices can + * be initialized for the purpose of generating documentation. + * + * + */ +public class MockControllerServiceInitializationContext implements ControllerServiceInitializationContext { + + @Override + public String getIdentifier() { + return "mock-controller-service"; + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } + + @Override + public ComponentLog getLogger() { + return new MockComponentLogger(); + } + + @Override + public StateManager getStateManager() { + return null; + } + + @Override + public String getKerberosServicePrincipal() { + return null; + } + + @Override + public File getKerberosServiceKeytab() { + return null; + } + + @Override + public File getKerberosConfigurationFile() { + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceLookup.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceLookup.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceLookup.java new file mode 100644 index 0000000..5307ac4 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockControllerServiceLookup.java @@ -0,0 +1,63 @@ +/* + * 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.mock; + +import org.apache.nifi.controller.ControllerService; +import org.apache.nifi.controller.ControllerServiceLookup; + +import java.util.Collections; +import java.util.Set; + +/** + * A Mock ControllerServiceLookup that can be used so that + * ConfigurableComponents can be initialized for the purpose of generating + * documentation + * + * + */ +public class MockControllerServiceLookup implements ControllerServiceLookup { + + @Override + public ControllerService getControllerService(final String serviceIdentifier) { + return null; + } + + @Override + public boolean isControllerServiceEnabled(final String serviceIdentifier) { + return false; + } + + @Override + public boolean isControllerServiceEnabled(final ControllerService service) { + return false; + } + + @Override + public Set<String> getControllerServiceIdentifiers(final Class<? extends ControllerService> serviceType) throws IllegalArgumentException { + return Collections.emptySet(); + } + + @Override + public boolean isControllerServiceEnabling(final String serviceIdentifier) { + return false; + } + + @Override + public String getControllerServiceName(final String serviceIdentifier) { + return serviceIdentifier; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockNodeTypeProvider.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockNodeTypeProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockNodeTypeProvider.java new file mode 100644 index 0000000..61390e1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockNodeTypeProvider.java @@ -0,0 +1,40 @@ +/* + * 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.mock; + +import org.apache.nifi.controller.NodeTypeProvider; + +/** + * A Mock NodeTypeProvider that can be used so that + * ConfigurableComponents can be initialized for the purpose of generating + * documentation + * + * + */ +public class MockNodeTypeProvider implements NodeTypeProvider { + + @Override + public boolean isClustered() { + return false; + } + + @Override + public boolean isPrimary() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessContext.java new file mode 100644 index 0000000..cf2e2cf --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessContext.java @@ -0,0 +1,116 @@ +/* + * 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.mock; + +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.PropertyValue; +import org.apache.nifi.components.state.StateManager; +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.processor.ProcessContext; +import org.apache.nifi.processor.Relationship; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public class MockProcessContext implements ProcessContext { + + @Override + public PropertyValue getProperty(PropertyDescriptor descriptor) { + return null; + } + + @Override + public PropertyValue getProperty(String propertyName) { + return null; + } + + @Override + public PropertyValue newPropertyValue(String rawValue) { + return null; + } + + @Override + public void yield() { + + } + + @Override + public int getMaxConcurrentTasks() { + return 0; + } + + @Override + public String getAnnotationData() { + return ""; + } + + @Override + public Map<PropertyDescriptor, String> getProperties() { + return Collections.emptyMap(); + } + + @Override + public String encrypt(String unencrypted) { + return unencrypted; + } + + @Override + public String decrypt(String encrypted) { + return encrypted; + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } + + @Override + public Set<Relationship> getAvailableRelationships() { + return Collections.emptySet(); + } + + @Override + public boolean hasIncomingConnection() { + return true; + } + + @Override + public boolean hasNonLoopConnection() { + return true; + } + + @Override + public boolean hasConnection(Relationship relationship) { + return false; + } + + @Override + public boolean isExpressionLanguagePresent(PropertyDescriptor property) { + return false; + } + + @Override + public StateManager getStateManager() { + return null; + } + + @Override + public String getName() { + return null; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessorInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessorInitializationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessorInitializationContext.java new file mode 100644 index 0000000..d9320b2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockProcessorInitializationContext.java @@ -0,0 +1,68 @@ +/* + * 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.mock; + +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.controller.NodeTypeProvider; +import org.apache.nifi.logging.ComponentLog; +import org.apache.nifi.processor.ProcessorInitializationContext; + +import java.io.File; + +/** + * A Mock ProcessorInitializationContext that can be used so that Processors can + * be initialized for the purpose of generating documentation. + * + * + */ +public class MockProcessorInitializationContext implements ProcessorInitializationContext { + + @Override + public String getIdentifier() { + return "mock-processor"; + } + + @Override + public ComponentLog getLogger() { + return new MockComponentLogger(); + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } + + @Override + public NodeTypeProvider getNodeTypeProvider() { + return new MockNodeTypeProvider(); + } + + @Override + public String getKerberosServicePrincipal() { + return null; + } + + @Override + public File getKerberosServiceKeytab() { + return null; + } + + @Override + public File getKerberosConfigurationFile() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockReportingInitializationContext.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockReportingInitializationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockReportingInitializationContext.java new file mode 100644 index 0000000..630c657 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/mock/MockReportingInitializationContext.java @@ -0,0 +1,83 @@ +/* + * 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.mock; + +import org.apache.nifi.controller.ControllerServiceLookup; +import org.apache.nifi.logging.ComponentLog; +import org.apache.nifi.reporting.ReportingInitializationContext; +import org.apache.nifi.scheduling.SchedulingStrategy; + +import java.io.File; +import java.util.concurrent.TimeUnit; + +/** + * A Mock ReportingInitializationContext that can be used to initialize a + * ReportingTask for the purposes of documentation generation. + * + */ +public class MockReportingInitializationContext implements ReportingInitializationContext { + + @Override + public String getIdentifier() { + return "mock-reporting-task"; + } + + @Override + public String getName() { + return ""; + } + + @Override + public long getSchedulingPeriod(TimeUnit timeUnit) { + return 0; + } + + @Override + public ControllerServiceLookup getControllerServiceLookup() { + return new MockControllerServiceLookup(); + } + + @Override + public String getSchedulingPeriod() { + return ""; + } + + @Override + public SchedulingStrategy getSchedulingStrategy() { + return SchedulingStrategy.TIMER_DRIVEN; + } + + @Override + public ComponentLog getLogger() { + return new MockComponentLogger(); + } + + @Override + public String getKerberosServicePrincipal() { + return null; + } + + @Override + public File getKerberosServiceKeytab() { + return null; + } + + @Override + public File getKerberosConfigurationFile() { + return null; + } +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java index 745ed9c..1cff3af 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionManager.java @@ -19,29 +19,43 @@ package org.apache.nifi.nar; import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading; import org.apache.nifi.authentication.LoginIdentityProvider; import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.bundle.Bundle; +import org.apache.nifi.bundle.BundleCoordinate; +import org.apache.nifi.bundle.BundleDetails; +import org.apache.nifi.components.ConfigurableComponent; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.state.StateProvider; import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.repository.ContentRepository; import org.apache.nifi.controller.repository.FlowFileRepository; import org.apache.nifi.controller.repository.FlowFileSwapManager; import org.apache.nifi.controller.status.history.ComponentStatusRepository; import org.apache.nifi.flowfile.FlowFilePrioritizer; +import org.apache.nifi.init.ConfigurableComponentInitializer; +import org.apache.nifi.init.ConfigurableComponentInitializerFactory; import org.apache.nifi.processor.Processor; import org.apache.nifi.provenance.ProvenanceRepository; +import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.ReportingTask; +import org.apache.nifi.util.NiFiProperties; import org.apache.nifi.util.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.File; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * Scans through the classpath to load all FlowFileProcessors, FlowFileComparators, and ReportingTasks using the service provider API and running through all classloaders (root, NARs). @@ -53,10 +67,15 @@ public class ExtensionManager { private static final Logger logger = LoggerFactory.getLogger(ExtensionManager.class); + public static final BundleCoordinate SYSTEM_BUNDLE_COORDINATE = new BundleCoordinate( + BundleCoordinate.DEFAULT_GROUP, "system", BundleCoordinate.DEFAULT_VERSION); + // Maps a service definition (interface) to those classes that implement the interface private static final Map<Class, Set<Class>> definitionMap = new HashMap<>(); - private static final Map<String, ClassLoader> extensionClassloaderLookup = new HashMap<>(); + private static final Map<String, List<Bundle>> classNameBundleLookup = new HashMap<>(); + private static final Map<BundleCoordinate, Bundle> bundleCoordinateBundleLookup = new HashMap<>(); + private static final Map<ClassLoader, Bundle> classLoaderBundleLookup = new HashMap<>(); private static final Set<String> requiresInstanceClassLoading = new HashSet<>(); private static final Map<String, ClassLoader> instanceClassloaderLookup = new ConcurrentHashMap<>(); @@ -73,28 +92,32 @@ public class ExtensionManager { definitionMap.put(FlowFileRepository.class, new HashSet<>()); definitionMap.put(FlowFileSwapManager.class, new HashSet<>()); definitionMap.put(ContentRepository.class, new HashSet<>()); + definitionMap.put(StateProvider.class, new HashSet<>()); } /** * Loads all FlowFileProcessor, FlowFileComparator, ReportingTask class types that can be found on the bootstrap classloader and by creating classloaders for all NARs found within the classpath. - * @param extensionLoaders the loaders to scan through in search of extensions + * @param narBundles the bundles to scan through in search of extensions */ - public static void discoverExtensions(final Set<ClassLoader> extensionLoaders) { - final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); - + public static void discoverExtensions(final Bundle systemBundle, final Set<Bundle> narBundles) { // get the current context class loader ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); - // consider the system class loader - loadExtensions(systemClassLoader); + // load the system bundle first so that any extensions found in JARs directly in lib will be registered as + // being from the system bundle and not from all the other NARs + loadExtensions(systemBundle); + bundleCoordinateBundleLookup.put(systemBundle.getBundleDetails().getCoordinate(), systemBundle); // consider each nar class loader - for (final ClassLoader ncl : extensionLoaders) { - + for (final Bundle bundle : narBundles) { // Must set the context class loader to the nar classloader itself // so that static initialization techniques that depend on the context class loader will work properly + final ClassLoader ncl = bundle.getClassLoader(); Thread.currentThread().setContextClassLoader(ncl); - loadExtensions(ncl); + loadExtensions(bundle); + + // Create a look-up from coordinate to bundle + bundleCoordinateBundleLookup.put(bundle.getBundleDetails().getCoordinate(), bundle); } // restore the current context class loader if appropriate @@ -104,73 +127,195 @@ public class ExtensionManager { } /** - * Loads extensions from the specified class loader. + * Returns a bundle representing the system class loader. + * + * @param niFiProperties a NiFiProperties instance which will be used to obtain the default NAR library path, + * which will become the working directory of the returned bundle + * @return a bundle for the system class loader + */ + public static Bundle createSystemBundle(final NiFiProperties niFiProperties) { + final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); + + final String narLibraryDirectory = niFiProperties.getProperty(NiFiProperties.NAR_LIBRARY_DIRECTORY); + if (StringUtils.isBlank(narLibraryDirectory)) { + throw new IllegalStateException("Unable to create system bundle because " + NiFiProperties.NAR_LIBRARY_DIRECTORY + " was null or empty"); + } + + final BundleDetails systemBundleDetails = new BundleDetails.Builder() + .workingDir(new File(narLibraryDirectory)) + .coordinate(SYSTEM_BUNDLE_COORDINATE) + .build(); + + return new Bundle(systemBundleDetails, systemClassLoader); + } + + /** + * Loads extensions from the specified bundle. * - * @param classLoader from which to load extensions + * @param bundle from which to load extensions */ @SuppressWarnings("unchecked") - private static void loadExtensions(final ClassLoader classLoader) { + private static void loadExtensions(final Bundle bundle) { for (final Map.Entry<Class, Set<Class>> entry : definitionMap.entrySet()) { - final ServiceLoader<?> serviceLoader = ServiceLoader.load(entry.getKey(), classLoader); + final boolean isControllerService = ControllerService.class.equals(entry.getKey()); + final boolean isProcessor = Processor.class.equals(entry.getKey()); + final boolean isReportingTask = ReportingTask.class.equals(entry.getKey()); + final ServiceLoader<?> serviceLoader = ServiceLoader.load(entry.getKey(), bundle.getClassLoader()); for (final Object o : serviceLoader) { - registerServiceClass(o.getClass(), extensionClassloaderLookup, classLoader, entry.getValue()); + // only consider extensions discovered directly in this bundle + boolean registerExtension = bundle.getClassLoader().equals(o.getClass().getClassLoader()); + + if (registerExtension) { + final Class extensionType = o.getClass(); + if (isControllerService && !checkControllerServiceEligibility(extensionType)) { + registerExtension = false; + logger.error(String.format( + "Skipping Controller Service %s because it is bundled with its supporting APIs and requires instance class loading.", extensionType.getName())); + } + + final boolean canReferenceControllerService = (isControllerService || isProcessor || isReportingTask) && o instanceof ConfigurableComponent; + if (canReferenceControllerService && !checkControllerServiceReferenceEligibility((ConfigurableComponent) o, bundle.getClassLoader())) { + registerExtension = false; + logger.error(String.format( + "Skipping component %s because it is bundled with its referenced Controller Service APIs and requires instance class loading.", extensionType.getName())); + } + + if (registerExtension) { + registerServiceClass(o.getClass(), classNameBundleLookup, bundle, entry.getValue()); + } + } } + + classLoaderBundleLookup.put(bundle.getClassLoader(), bundle); } } + private static boolean checkControllerServiceReferenceEligibility(final ConfigurableComponent component, final ClassLoader classLoader) { + // if the extension does not require instance classloading, its eligible + final boolean requiresInstanceClassLoading = component.getClass().isAnnotationPresent(RequiresInstanceClassLoading.class); + + ConfigurableComponentInitializer initializer = null; + try { + initializer = ConfigurableComponentInitializerFactory.createComponentInitializer(component.getClass()); + initializer.initialize(component); + + final Set<Class> cobundledApis = new HashSet<>(); + try (final NarCloseable closeable = NarCloseable.withComponentNarLoader(component.getClass().getClassLoader())) { + final List<PropertyDescriptor> descriptors = component.getPropertyDescriptors(); + if (descriptors != null && !descriptors.isEmpty()) { + for (final PropertyDescriptor descriptor : descriptors) { + final Class<? extends ControllerService> serviceApi = descriptor.getControllerServiceDefinition(); + if (serviceApi != null && classLoader.equals(serviceApi.getClassLoader())) { + cobundledApis.add(serviceApi); + } + } + } + } + + if (!cobundledApis.isEmpty()) { + logger.warn(String.format( + "Component %s is bundled with its referenced Controller Service APIs %s. The service APIs should not be bundled with component implementations that reference it.", + component.getClass().getName(), StringUtils.join(cobundledApis.stream().map(cls -> cls.getName()).collect(Collectors.toSet()), ", "))); + } + + // the component is eligible when it does not require instance classloading or when the supporting APIs are bundled in a parent NAR + return requiresInstanceClassLoading == false || cobundledApis.isEmpty(); + } catch (final InitializationException e) { + logger.warn(String.format("Unable to verify if component %s references any bundled Controller Service APIs due to %s", component.getClass().getName(), e.getMessage())); + return true; + } finally { + if (initializer != null) { + initializer.teardown(component); + } + } + } + + private static boolean checkControllerServiceEligibility(Class extensionType) { + final Class originalExtensionType = extensionType; + final ClassLoader originalExtensionClassLoader = extensionType.getClassLoader(); + + // if the extension does not require instance classloading, its eligible + final boolean requiresInstanceClassLoading = extensionType.isAnnotationPresent(RequiresInstanceClassLoading.class); + + final Set<Class> cobundledApis = new HashSet<>(); + while (extensionType != null) { + for (final Class i : extensionType.getInterfaces()) { + if (originalExtensionClassLoader.equals(i.getClassLoader())) { + cobundledApis.add(i); + } + } + + extensionType = extensionType.getSuperclass(); + } + + if (!cobundledApis.isEmpty()) { + logger.warn(String.format("Controller Service %s is bundled with its supporting APIs %s. The service APIs should not be bundled with the implementations.", + originalExtensionType.getName(), StringUtils.join(cobundledApis.stream().map(cls -> cls.getName()).collect(Collectors.toSet()), ", "))); + } + + // the service is eligible when it does not require instance classloading or when the supporting APIs are bundled in a parent NAR + return requiresInstanceClassLoading == false || cobundledApis.isEmpty(); + } + /** - * Registers extension for the specified type from the specified ClassLoader. + * Registers extension for the specified type from the specified Bundle. * * @param type the extension type - * @param classloaderMap mapping of classname to classloader - * @param classLoader the classloader being mapped to + * @param classNameBundleMap mapping of classname to Bundle + * @param bundle the Bundle being mapped to * @param classes to map to this classloader but which come from its ancestors */ - private static void registerServiceClass(final Class<?> type, final Map<String, ClassLoader> classloaderMap, final ClassLoader classLoader, final Set<Class> classes) { + private static void registerServiceClass(final Class<?> type, final Map<String, List<Bundle>> classNameBundleMap, final Bundle bundle, final Set<Class> classes) { final String className = type.getName(); - final ClassLoader registeredClassLoader = classloaderMap.get(className); - // see if this class is already registered (this should happen when the class is loaded by an ancestor of the specified classloader) - if (registeredClassLoader == null) { - classloaderMap.put(className, classLoader); - classes.add(type); + // get the bundles that have already been registered for the class name + List<Bundle> registeredBundles = classNameBundleMap.get(className); - // keep track of which classes require a class loader per component instance - if (type.isAnnotationPresent(RequiresInstanceClassLoading.class)) { - requiresInstanceClassLoading.add(className); + if (registeredBundles == null) { + registeredBundles = new ArrayList<>(); + classNameBundleMap.put(className, registeredBundles); + } + + boolean alreadyRegistered = false; + for (final Bundle registeredBundle : registeredBundles) { + final BundleCoordinate registeredCoordinate = registeredBundle.getBundleDetails().getCoordinate(); + + // if the incoming bundle has the same coordinate as one of the registered bundles then consider it already registered + if (registeredCoordinate.equals(bundle.getBundleDetails().getCoordinate())) { + alreadyRegistered = true; + break; } - } else { - boolean loadedFromAncestor = false; - - // determine if this class was loaded from an ancestor - ClassLoader ancestorClassLoader = classLoader.getParent(); - while (ancestorClassLoader != null) { - if (ancestorClassLoader == registeredClassLoader) { - loadedFromAncestor = true; - break; - } - ancestorClassLoader = ancestorClassLoader.getParent(); + // if the type wasn't loaded from an ancestor, and the type isn't a processor, cs, or reporting task, then + // fail registration because we don't support multiple versions of any other types + if (!multipleVersionsAllowed(type)) { + throw new IllegalStateException("Attempt was made to load " + className + " from " + + bundle.getBundleDetails().getCoordinate().getCoordinate() + + " but that class name is already loaded/registered from " + registeredBundle.getBundleDetails().getCoordinate() + + " and multiple versions are not supported for this type" + ); } + } - // if this class was loaded from a non ancestor class loader, report potential unexpected behavior - if (!loadedFromAncestor) { - logger.warn("Attempt was made to load " + className + " from " + classLoader - + " but that class name is already loaded/registered from " + registeredClassLoader - + ". This may cause unpredictable behavior. Order of NARs is not guaranteed."); + // if none of the above was true then register the new bundle + if (!alreadyRegistered) { + registeredBundles.add(bundle); + classes.add(type); + + if (type.isAnnotationPresent(RequiresInstanceClassLoading.class)) { + requiresInstanceClassLoading.add(className); } } + } /** - * Determines the effective classloader for classes of the given type. If returns null it indicates the given type is not known or was not detected. - * - * @param classType to lookup the classloader of - * @return String of fully qualified class name; null if not a detected type + * @param type a Class that we found from a service loader + * @return true if the given class is a processor, controller service, or reporting task */ - public static ClassLoader getClassLoader(final String classType) { - return extensionClassloaderLookup.get(classType); + private static boolean multipleVersionsAllowed(Class<?> type) { + return Processor.class.isAssignableFrom(type) || ControllerService.class.isAssignableFrom(type) || ReportingTask.class.isAssignableFrom(type); } /** @@ -178,40 +323,50 @@ public class ExtensionManager { * * @param classType the type of class to lookup the ClassLoader for * @param instanceIdentifier the identifier of the specific instance of the classType to look up the ClassLoader for + * @param bundle the bundle where the classType exists * @return the ClassLoader for the given instance of the given type, or null if the type is not a detected extension type */ - public static ClassLoader getClassLoader(final String classType, final String instanceIdentifier) { - if (StringUtils.isEmpty(classType) || StringUtils.isEmpty(instanceIdentifier)) { - throw new IllegalArgumentException("Class Type and Instance Identifier must be provided"); + public static ClassLoader createInstanceClassLoader(final String classType, final String instanceIdentifier, final Bundle bundle) { + if (StringUtils.isEmpty(classType)) { + throw new IllegalArgumentException("Class-Type is required"); } - // Check if we already have a ClassLoader for this instance - ClassLoader instanceClassLoader = instanceClassloaderLookup.get(instanceIdentifier); + if (StringUtils.isEmpty(instanceIdentifier)) { + throw new IllegalArgumentException("Instance Identifier is required"); + } - // If we don't then we'll create a new ClassLoader for this instance and add it to the map for future lookups - if (instanceClassLoader == null) { - final ClassLoader registeredClassLoader = getClassLoader(classType); - if (registeredClassLoader == null) { - return null; - } + if (bundle == null) { + throw new IllegalArgumentException("Bundle is required"); + } - // If the class is annotated with @RequiresInstanceClassLoading and the registered ClassLoader is a URLClassLoader - // then make a new InstanceClassLoader that is a full copy of the NAR Class Loader, otherwise create an empty - // InstanceClassLoader that has the NAR ClassLoader as a parent - if (requiresInstanceClassLoading.contains(classType) && (registeredClassLoader instanceof URLClassLoader)) { - final URLClassLoader registeredUrlClassLoader = (URLClassLoader) registeredClassLoader; - instanceClassLoader = new InstanceClassLoader(instanceIdentifier, classType, registeredUrlClassLoader.getURLs(), registeredUrlClassLoader.getParent()); - } else { - instanceClassLoader = new InstanceClassLoader(instanceIdentifier, classType, new URL[0], registeredClassLoader); - } + final ClassLoader bundleClassLoader = bundle.getClassLoader(); - instanceClassloaderLookup.put(instanceIdentifier, instanceClassLoader); + // If the class is annotated with @RequiresInstanceClassLoading and the registered ClassLoader is a URLClassLoader + // then make a new InstanceClassLoader that is a full copy of the NAR Class Loader, otherwise create an empty + // InstanceClassLoader that has the NAR ClassLoader as a parent + ClassLoader instanceClassLoader; + if (requiresInstanceClassLoading.contains(classType) && (bundleClassLoader instanceof URLClassLoader)) { + final URLClassLoader registeredUrlClassLoader = (URLClassLoader) bundleClassLoader; + instanceClassLoader = new InstanceClassLoader(instanceIdentifier, classType, registeredUrlClassLoader.getURLs(), registeredUrlClassLoader.getParent()); + } else { + instanceClassLoader = new InstanceClassLoader(instanceIdentifier, classType, new URL[0], bundleClassLoader); } + instanceClassloaderLookup.put(instanceIdentifier, instanceClassLoader); return instanceClassLoader; } /** + * Retrieves the InstanceClassLoader for the component with the given identifier. + * + * @param instanceIdentifier the identifier of a component + * @return the instance class loader for the component + */ + public static ClassLoader getInstanceClassLoader(final String instanceIdentifier) { + return instanceClassloaderLookup.get(instanceIdentifier); + } + + /** * Removes the ClassLoader for the given instance and closes it if necessary. * * @param instanceIdentifier the identifier of a component to remove the ClassLoader for @@ -241,10 +396,56 @@ public class ExtensionManager { * @return true if the class is found in the set of classes requiring instance level class loading, false otherwise */ public static boolean requiresInstanceClassLoading(final String classType) { + if (classType == null) { + throw new IllegalArgumentException("Class type cannot be null"); + } return requiresInstanceClassLoading.contains(classType); } + /** + * Retrieves the bundles that have a class with the given name. + * + * @param classType the class name of an extension + * @return the list of bundles that contain an extension with the given class name + */ + public static List<Bundle> getBundles(final String classType) { + if (classType == null) { + throw new IllegalArgumentException("Class type cannot be null"); + } + final List<Bundle> bundles = classNameBundleLookup.get(classType); + return bundles == null ? Collections.emptyList() : new ArrayList<>(bundles); + } + + /** + * Retrieves the bundle with the given coordinate. + * + * @param bundleCoordinate a coordinate to look up + * @return the bundle with the given coordinate, or null if none exists + */ + public static Bundle getBundle(final BundleCoordinate bundleCoordinate) { + if (bundleCoordinate == null) { + throw new IllegalArgumentException("BundleCoordinate cannot be null"); + } + return bundleCoordinateBundleLookup.get(bundleCoordinate); + } + + /** + * Retrieves the bundle for the given class loader. + * + * @param classLoader the class loader to look up the bundle for + * @return the bundle for the given class loader + */ + public static Bundle getBundle(final ClassLoader classLoader) { + if (classLoader == null) { + throw new IllegalArgumentException("ClassLoader cannot be null"); + } + return classLoaderBundleLookup.get(classLoader); + } + public static Set<Class> getExtensions(final Class<?> definition) { + if (definition == null) { + throw new IllegalArgumentException("Class cannot be null"); + } final Set<Class> extensions = definitionMap.get(definition); return (extensions == null) ? Collections.<Class>emptySet() : extensions; } @@ -252,12 +453,21 @@ public class ExtensionManager { public static void logClassLoaderMapping() { final StringBuilder builder = new StringBuilder(); - builder.append("Extension Type Mapping to Classloader:"); + builder.append("Extension Type Mapping to Bundle:"); for (final Map.Entry<Class, Set<Class>> entry : definitionMap.entrySet()) { - builder.append("\n\t=== ").append(entry.getKey().getSimpleName()).append(" type || Classloader ==="); + builder.append("\n\t=== ").append(entry.getKey().getSimpleName()).append(" Type ==="); for (final Class type : entry.getValue()) { - builder.append("\n\t").append(type.getName()).append(" || ").append(getClassLoader(type.getName())); + final List<Bundle> bundles = classNameBundleLookup.containsKey(type.getName()) + ? classNameBundleLookup.get(type.getName()) : Collections.emptyList(); + + builder.append("\n\t").append(type.getName()); + + for (final Bundle bundle : bundles) { + final String coordinate = bundle.getBundleDetails().getCoordinate().getCoordinate(); + final String workingDir = bundle.getBundleDetails().getWorkingDirectory().getPath(); + builder.append("\n\t\t").append(coordinate).append(" || ").append(workingDir); + } } builder.append("\n\t=== End ").append(entry.getKey().getSimpleName()).append(" types ==="); http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionMapping.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionMapping.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionMapping.java index c478d97..698eb57 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionMapping.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/ExtensionMapping.java @@ -16,58 +16,108 @@ */ package org.apache.nifi.nar; -import java.util.ArrayList; +import org.apache.nifi.bundle.BundleCoordinate; + import java.util.Collection; import java.util.Collections; -import java.util.List; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiFunction; public class ExtensionMapping { - private final List<String> processorNames = new ArrayList<>(); - private final List<String> controllerServiceNames = new ArrayList<>(); - private final List<String> reportingTaskNames = new ArrayList<>(); + private final Map<String, Set<BundleCoordinate>> processorNames = new HashMap<>(); + private final Map<String, Set<BundleCoordinate>> controllerServiceNames = new HashMap<>(); + private final Map<String, Set<BundleCoordinate>> reportingTaskNames = new HashMap<>(); + + private final BiFunction<Set<BundleCoordinate>, Set<BundleCoordinate>, Set<BundleCoordinate>> merger = (oldValue, newValue) -> { + final Set<BundleCoordinate> merged = new HashSet<>(); + merged.addAll(oldValue); + merged.addAll(newValue); + return merged; + }; - void addProcessor(final String processorName) { - processorNames.add(processorName); + void addProcessor(final BundleCoordinate coordinate, final String processorName) { + processorNames.computeIfAbsent(processorName, name -> new HashSet<>()).add(coordinate); } - void addAllProcessors(final Collection<String> processorNames) { - this.processorNames.addAll(processorNames); + void addAllProcessors(final BundleCoordinate coordinate, final Collection<String> processorNames) { + processorNames.forEach(name -> { + addProcessor(coordinate, name); + }); } - void addControllerService(final String controllerServiceName) { - controllerServiceNames.add(controllerServiceName); + void addControllerService(final BundleCoordinate coordinate, final String controllerServiceName) { + controllerServiceNames.computeIfAbsent(controllerServiceName, name -> new HashSet<>()).add(coordinate); } - void addAllControllerServices(final Collection<String> controllerServiceNames) { - this.controllerServiceNames.addAll(controllerServiceNames); + void addAllControllerServices(final BundleCoordinate coordinate, final Collection<String> controllerServiceNames) { + controllerServiceNames.forEach(name -> { + addControllerService(coordinate, name); + }); } - void addReportingTask(final String reportingTaskName) { - reportingTaskNames.add(reportingTaskName); + void addReportingTask(final BundleCoordinate coordinate, final String reportingTaskName) { + reportingTaskNames.computeIfAbsent(reportingTaskName, name -> new HashSet<>()).add(coordinate); } - void addAllReportingTasks(final Collection<String> reportingTaskNames) { - this.reportingTaskNames.addAll(reportingTaskNames); + void addAllReportingTasks(final BundleCoordinate coordinate, final Collection<String> reportingTaskNames) { + reportingTaskNames.forEach(name -> { + addReportingTask(coordinate, name); + }); } - public List<String> getProcessorNames() { - return Collections.unmodifiableList(processorNames); + void merge(final ExtensionMapping other) { + other.getProcessorNames().forEach((name, otherCoordinates) -> { + processorNames.merge(name, otherCoordinates, merger); + }); + other.getControllerServiceNames().forEach((name, otherCoordinates) -> { + controllerServiceNames.merge(name, otherCoordinates, merger); + }); + other.getReportingTaskNames().forEach((name, otherCoordinates) -> { + reportingTaskNames.merge(name, otherCoordinates, merger); + }); } - public List<String> getControllerServiceNames() { - return Collections.unmodifiableList(controllerServiceNames); + public Map<String, Set<BundleCoordinate>> getProcessorNames() { + return Collections.unmodifiableMap(processorNames); } - public List<String> getReportingTaskNames() { - return Collections.unmodifiableList(reportingTaskNames); + public Map<String, Set<BundleCoordinate>> getControllerServiceNames() { + return Collections.unmodifiableMap(controllerServiceNames); } - public List<String> getAllExtensionNames() { - final List<String> extensionNames = new ArrayList<>(); - extensionNames.addAll(processorNames); - extensionNames.addAll(controllerServiceNames); - extensionNames.addAll(reportingTaskNames); + public Map<String, Set<BundleCoordinate>> getReportingTaskNames() { + return Collections.unmodifiableMap(reportingTaskNames); + } + + public Map<String, Set<BundleCoordinate>> getAllExtensionNames() { + final Map<String, Set<BundleCoordinate>> extensionNames = new HashMap<>(); + extensionNames.putAll(processorNames); + extensionNames.putAll(controllerServiceNames); + extensionNames.putAll(reportingTaskNames); return extensionNames; } + + public int size() { + int size = 0; + + for (final Set<BundleCoordinate> coordinates : processorNames.values()) { + size += coordinates.size(); + } + for (final Set<BundleCoordinate> coordinates : controllerServiceNames.values()) { + size += coordinates.size(); + } + for (final Set<BundleCoordinate> coordinates : reportingTaskNames.values()) { + size += coordinates.size(); + } + + return size; + } + + public boolean isEmpty() { + return processorNames.isEmpty() && controllerServiceNames.isEmpty() && reportingTaskNames.isEmpty(); + } } http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarBundleUtil.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarBundleUtil.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarBundleUtil.java new file mode 100644 index 0000000..62afb37 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarBundleUtil.java @@ -0,0 +1,74 @@ +/* + * 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.nar; + +import org.apache.nifi.bundle.BundleCoordinate; +import org.apache.nifi.bundle.BundleDetails; +import org.apache.nifi.util.StringUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +public class NarBundleUtil { + + /** + * Creates a BundleDetails from the given NAR working directory. + * + * @param narDirectory the directory of an exploded NAR which contains a META-INF/MANIFEST.MF + * + * @return the BundleDetails constructed from the information in META-INF/MANIFEST.MF + */ + public static BundleDetails fromNarDirectory(final File narDirectory) throws IOException, IllegalStateException { + if (narDirectory == null) { + throw new IllegalArgumentException("NAR Directory cannot be null"); + } + + final File manifestFile = new File(narDirectory, "META-INF/MANIFEST.MF"); + try (final FileInputStream fis = new FileInputStream(manifestFile)) { + final Manifest manifest = new Manifest(fis); + final Attributes attributes = manifest.getMainAttributes(); + + final BundleDetails.Builder builder = new BundleDetails.Builder(); + builder.workingDir(narDirectory); + + final String group = attributes.getValue(NarManifestEntry.NAR_GROUP.getManifestName()); + final String id = attributes.getValue(NarManifestEntry.NAR_ID.getManifestName()); + final String version = attributes.getValue(NarManifestEntry.NAR_VERSION.getManifestName()); + builder.coordinate(new BundleCoordinate(group, id, version)); + + final String dependencyGroup = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_GROUP.getManifestName()); + final String dependencyId = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_ID.getManifestName()); + final String dependencyVersion = attributes.getValue(NarManifestEntry.NAR_DEPENDENCY_VERSION.getManifestName()); + if (!StringUtils.isBlank(dependencyId)) { + builder.dependencyCoordinate(new BundleCoordinate(dependencyGroup, dependencyId, dependencyVersion)); + } + + builder.buildBranch(attributes.getValue(NarManifestEntry.BUILD_BRANCH.getManifestName())); + builder.buildTag(attributes.getValue(NarManifestEntry.BUILD_TAG.getManifestName())); + builder.buildRevision(attributes.getValue(NarManifestEntry.BUILD_REVISION.getManifestName())); + builder.buildTimestamp(attributes.getValue(NarManifestEntry.BUILD_TIMESTAMP.getManifestName())); + builder.buildJdk(attributes.getValue(NarManifestEntry.BUILD_JDK.getManifestName())); + builder.builtBy(attributes.getValue(NarManifestEntry.BUILT_BY.getManifestName())); + + return builder.build(); + } + } + +} http://git-wip-us.apache.org/repos/asf/nifi/blob/d90cf846/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarClassLoader.java ---------------------------------------------------------------------- diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarClassLoader.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarClassLoader.java index 8d55169..eb4539c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarClassLoader.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-nar-utils/src/main/java/org/apache/nifi/nar/NarClassLoader.java @@ -21,6 +21,8 @@ import java.io.FileFilter; import java.io.IOException; import java.net.URL; import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.Comparator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -192,8 +194,12 @@ public class NarClassLoader extends URLClassLoader { } addURL(dependencies.toURI().toURL()); if (dependencies.isDirectory()) { - for (File libJar : dependencies.listFiles(JAR_FILTER)) { - addURL(libJar.toURI().toURL()); + final File[] jarFiles = dependencies.listFiles(JAR_FILTER); + if (jarFiles != null) { + Arrays.sort(jarFiles, Comparator.comparing(File::getName)); + for (File libJar : jarFiles) { + addURL(libJar.toURI().toURL()); + } } } }
