/** * Gets the bundle with the given coordinate. * * @param bundleCoordinate the coordinate of the bundle to find * @return the bundle with the coordinate, or an empty optional */ private Optional<Bundle> getBundle(final BundleCoordinate bundleCoordinate) { return initContext.bundles.values().stream() .filter(b -> b.getBundleDetails().getCoordinate().equals(bundleCoordinate)) .findFirst(); }
private static BundleCoordinate findBundleForType(final ExtensionManager extensionManager, final String type, final BundleCoordinate desiredCoordinate) { final List<Bundle> bundles = extensionManager.getBundles(type); if (bundles.isEmpty()) { throw new IllegalStateException(String.format("%s is not known to this NiFi instance.", type)); } else if (bundles.size() > 1) { if (desiredCoordinate == null) { throw new IllegalStateException(String.format("Multiple versions of %s exist.", type)); } else { throw new IllegalStateException(String.format("Multiple versions of %s exist. No exact match for %s.", type, desiredCoordinate)); } } else { return bundles.get(0).getBundleDetails().getCoordinate(); } }
@Override public void logClassLoaderMapping() { final StringBuilder builder = new StringBuilder(); 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 ==="); for (final Class type : entry.getValue()) { 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 ==="); } logger.info(builder.toString()); } }
@Override public void discoverExtensions(final Set<Bundle> narBundles) { // get the current context class loader ClassLoader currentContextClassLoader = Thread.currentThread().getContextClassLoader(); // consider each nar class loader 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(bundle); // Create a look-up from coordinate to bundle bundleCoordinateBundleLookup.put(bundle.getBundleDetails().getCoordinate(), bundle); } // restore the current context class loader if appropriate if (currentContextClassLoader != null) { Thread.currentThread().setContextClassLoader(currentContextClassLoader); } }
/** * Find the bundle coordinates for any service APIs that are referenced by this component and not part of the same bundle. * * @param component the component being instantiated */ protected Set<BundleCoordinate> findReachableApiBundles(final ConfigurableComponent component) { final Set<BundleCoordinate> reachableApiBundles = 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 && !component.getClass().getClassLoader().equals(serviceApi.getClassLoader())) { final Bundle apiBundle = classLoaderBundleLookup.get(serviceApi.getClassLoader()); reachableApiBundles.add(apiBundle.getBundleDetails().getCoordinate()); } } } } return reachableApiBundles; }
@Override public void discoverExtensions(final Bundle systemBundle, final Set<Bundle> narBundles) { // 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); discoverExtensions(narBundles); }
protected void verifyCreate(final ExtensionManager extensionManager, final String type, final BundleDTO bundle) { final List<Bundle> bundles = extensionManager.getBundles(type); if (bundle != null) { final BundleCoordinate coordinate = new BundleCoordinate(bundle.getGroup(), bundle.getArtifact(), bundle.getVersion()); if (bundles.stream().filter(b -> b.getBundleDetails().getCoordinate().equals(coordinate)).count() == 0) { throw new IllegalStateException(String.format("%s is not known to this NiFi instance.", coordinate.toString())); } } else { if (bundles.isEmpty()) { throw new IllegalStateException(String.format("%s is not known to this NiFi instance.", type)); } else if (bundles.size() > 1) { throw new IllegalStateException(String.format("Multiple versions of %s exist. Please specify the desired bundle.", type)); } } } }
private List<BundleDetails> loadBundleDetails(List<File> unpackedNars) { final List<BundleDetails> narDetails = new ArrayList<>(); for (final File unpackedNar : unpackedNars) { try { final BundleDetails narDetail = getNarDetails(unpackedNar); final BundleCoordinate unpackedNarCoordinate = narDetail.getCoordinate(); // Skip this NAR if there is another NAR with the same group, id, and version final Optional<Bundle> existingBundle = getBundle(unpackedNarCoordinate); if (existingBundle.isPresent()) { final BundleDetails existingBundleDetails = existingBundle.get().getBundleDetails(); final String existingNarWorkingDir = existingBundleDetails.getWorkingDirectory().getCanonicalPath(); final String unpackedNarWorkingDir = narDetail.getWorkingDirectory().getCanonicalPath(); logger.error("Unable to load NAR with coordinates {} and working directory {} " + "because another NAR with the same coordinates already exists at {}", new Object[]{unpackedNarCoordinate, unpackedNarWorkingDir, existingNarWorkingDir}); } else { narDetails.add(narDetail); } } catch (Exception e) { logger.error("Unable to load NAR {} due to {}, skipping...", new Object[]{unpackedNar.getAbsolutePath(), e.getMessage()}); } } return narDetails; }
private static BundleCoordinate findCompatibleBundle(final ExtensionManager extensionManager, final String type, final BundleDTO bundleDTO, final boolean allowCompatibleBundle) { final BundleCoordinate coordinate = new BundleCoordinate(bundleDTO.getGroup(), bundleDTO.getArtifact(), bundleDTO.getVersion()); final Bundle bundle = extensionManager.getBundle(coordinate); if (bundle == null) { if (allowCompatibleBundle) { return findBundleForType(extensionManager, type, coordinate); } else { throw new IllegalStateException(String.format("%s from %s is not known to this NiFi instance.", type, coordinate)); } } else { final List<BundleCoordinate> bundlesForType = extensionManager.getBundles(type).stream().map(b -> b.getBundleDetails().getCoordinate()).collect(Collectors.toList()); if (bundlesForType.contains(coordinate)) { return coordinate; } else { throw new IllegalStateException(String.format("Found bundle %s but does not support %s", coordinate, type)); } } }
private List<ControllerServiceAPI> mapControllerServiceApis(final ControllerServiceNode service) { final Class<?> serviceClass = service.getControllerServiceImplementation().getClass(); final Set<Class<?>> serviceApiClasses = new HashSet<>(); // get all of it's interfaces to determine the controller service api's it implements final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(serviceClass); for (final Class<?> i : interfaces) { // add all controller services that's not ControllerService itself if (ControllerService.class.isAssignableFrom(i) && !ControllerService.class.equals(i)) { serviceApiClasses.add(i); } } final List<ControllerServiceAPI> serviceApis = new ArrayList<>(); for (final Class<?> serviceApiClass : serviceApiClasses) { final BundleCoordinate bundleCoordinate = extensionManager.getBundle(serviceApiClass.getClassLoader()).getBundleDetails().getCoordinate(); final ControllerServiceAPI serviceApi = new ControllerServiceAPI(); serviceApi.setType(serviceApiClass.getName()); serviceApi.setBundle(mapBundle(bundleCoordinate)); serviceApis.add(serviceApi); } return serviceApis; }
/** * Documents a type of configurable component. * * @param extensionClasses types of a configurable component * @param explodedNiFiDocsDir base directory of component documentation */ public static void documentConfigurableComponent(final Set<Class> extensionClasses, final File explodedNiFiDocsDir, final ExtensionManager extensionManager) { for (final Class<?> extensionClass : extensionClasses) { if (ConfigurableComponent.class.isAssignableFrom(extensionClass)) { final String extensionClassName = extensionClass.getCanonicalName(); final Bundle bundle = extensionManager.getBundle(extensionClass.getClassLoader()); if (bundle == null) { logger.warn("No coordinate found for {}, skipping...", new Object[] {extensionClassName}); continue; } final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate(); final String path = coordinate.getGroup() + "/" + coordinate.getId() + "/" + coordinate.getVersion() + "/" + extensionClassName; final File componentDirectory = new File(explodedNiFiDocsDir, path); componentDirectory.mkdirs(); final Class<? extends ConfigurableComponent> componentClass = extensionClass.asSubclass(ConfigurableComponent.class); try { logger.debug("Documenting: " + componentClass); document(extensionManager, componentDirectory, componentClass, coordinate); } catch (Exception e) { logger.warn("Unable to document: " + componentClass, e); } } } }
for (final Class<?> c : extensionManager.getExtensions(Processor.class)) { final String name = c.getName(); processorClasses.put(name, extensionManager.getBundles(name).stream().map(bundle -> bundle.getBundleDetails().getCoordinate()).collect(Collectors.toSet())); for (final Class<?> c : extensionManager.getExtensions(ControllerService.class)) { final String name = c.getName(); controllerServiceClasses.put(name, extensionManager.getBundles(name).stream().map(bundle -> bundle.getBundleDetails().getCoordinate()).collect(Collectors.toSet()));
for (final Class<?> c : extensionManager.getExtensions(Processor.class)) { final String name = c.getName(); processorClasses.put(name, extensionManager.getBundles(name).stream().map(bundle -> bundle.getBundleDetails().getCoordinate()).collect(Collectors.toSet())); for (final Class<?> c : extensionManager.getExtensions(ControllerService.class)) { final String name = c.getName(); controllerServiceClasses.put(name, extensionManager.getBundles(name).stream().map(bundle -> bundle.getBundleDetails().getCoordinate()).collect(Collectors.toSet()));
private List<ControllerServiceApiDTO> createControllerServiceApiDto(final Class cls) { final Set<Class> serviceApis = new HashSet<>(); // if this is a controller service if (ControllerService.class.isAssignableFrom(cls)) { // get all of it's interfaces to determine the controller service api's it implements final List<Class<?>> interfaces = ClassUtils.getAllInterfaces(cls); for (final Class i : interfaces) { // add all controller services that's not ControllerService itself if (ControllerService.class.isAssignableFrom(i) && !ControllerService.class.equals(i)) { serviceApis.add(i); } } final List<ControllerServiceApiDTO> dtos = new ArrayList<>(); for (final Class serviceApi : serviceApis) { final Bundle bundle = extensionManager.getBundle(serviceApi.getClassLoader()); final BundleCoordinate bundleCoordinate = bundle.getBundleDetails().getCoordinate(); final ControllerServiceApiDTO dto = new ControllerServiceApiDTO(); dto.setType(serviceApi.getName()); dto.setBundle(createBundleDto(bundleCoordinate)); dtos.add(dto); } return dtos; } else { return null; } }
private ValidationResult validateControllerServiceApi(final PropertyDescriptor descriptor, final ControllerServiceNode controllerServiceNode) { final Class<? extends ControllerService> controllerServiceApiClass = descriptor.getControllerServiceDefinition(); final ClassLoader controllerServiceApiClassLoader = controllerServiceApiClass.getClassLoader(); final ExtensionManager extensionManager = serviceProvider.getExtensionManager(); final String serviceId = controllerServiceNode.getIdentifier(); final String propertyName = descriptor.getDisplayName(); final Bundle controllerServiceApiBundle = extensionManager.getBundle(controllerServiceApiClassLoader); if (controllerServiceApiBundle == null) { return createInvalidResult(serviceId, propertyName, "Unable to find bundle for ControllerService API class " + controllerServiceApiClass.getCanonicalName()); } final BundleCoordinate controllerServiceApiCoordinate = controllerServiceApiBundle.getBundleDetails().getCoordinate(); final Bundle controllerServiceBundle = extensionManager.getBundle(controllerServiceNode.getBundleCoordinate()); if (controllerServiceBundle == null) { return createInvalidResult(serviceId, propertyName, "Unable to find bundle for coordinate " + controllerServiceNode.getBundleCoordinate()); } final BundleCoordinate controllerServiceCoordinate = controllerServiceBundle.getBundleDetails().getCoordinate(); final boolean matchesApi = matchesApi(extensionManager, controllerServiceBundle, controllerServiceApiCoordinate); if (!matchesApi) { final String controllerServiceType = controllerServiceNode.getComponentType(); final String controllerServiceApiType = controllerServiceApiClass.getSimpleName(); final String explanation = new StringBuilder() .append(controllerServiceType).append(" - ").append(controllerServiceCoordinate.getVersion()) .append(" from ").append(controllerServiceCoordinate.getGroup()).append(" - ").append(controllerServiceCoordinate.getId()) .append(" is not compatible with ").append(controllerServiceApiType).append(" - ").append(controllerServiceApiCoordinate.getVersion()) .append(" from ").append(controllerServiceApiCoordinate.getGroup()).append(" - ").append(controllerServiceApiCoordinate.getId()) .toString(); return createInvalidResult(serviceId, propertyName, explanation); } return null; }
private ClassLoaderDiagnosticsDTO createClassLoaderDiagnosticsDto(final ClassLoader classLoader) { final ClassLoaderDiagnosticsDTO dto = new ClassLoaderDiagnosticsDTO(); final Bundle bundle = extensionManager.getBundle(classLoader); if (bundle != null) { dto.setBundle(createBundleDto(bundle.getBundleDetails().getCoordinate())); } final ClassLoader parentClassLoader = classLoader.getParent(); if (parentClassLoader != null) { dto.setParentClassLoader(createClassLoaderDiagnosticsDto(parentClassLoader)); } return dto; }
final String dependencyCoordinateStr = bundleDependencyCoordinate.getCoordinate(); logger.warn(String.format("While loading '%s' unable to locate exact NAR dependency '%s'. Only found one possible match '%s'. Continuing...", bundleDetail.getCoordinate().getCoordinate(), dependencyCoordinateStr, coordinate.getCoordinate()));
final BundleCoordinate coordinate = bundle.getBundleDetails().getCoordinate();
/** * Determines if the given controller service node has the required API as an ancestor. * * @param controllerServiceImplBundle the bundle of a controller service being referenced by a processor * @param requiredApiCoordinate the controller service API required by the processor * @return true if the controller service node has the require API as an ancestor, false otherwise */ private boolean matchesApi(final ExtensionManager extensionManager, final Bundle controllerServiceImplBundle, final BundleCoordinate requiredApiCoordinate) { // start with the coordinate of the controller service for cases where the API and service are in the same bundle BundleCoordinate controllerServiceDependencyCoordinate = controllerServiceImplBundle.getBundleDetails().getCoordinate(); boolean foundApiDependency = false; while (controllerServiceDependencyCoordinate != null) { // determine if the dependency coordinate matches the required API if (requiredApiCoordinate.equals(controllerServiceDependencyCoordinate)) { foundApiDependency = true; break; } // move to the next dependency in the chain, or stop if null final Bundle controllerServiceDependencyBundle = extensionManager.getBundle(controllerServiceDependencyCoordinate); if (controllerServiceDependencyBundle == null) { controllerServiceDependencyCoordinate = null; } else { controllerServiceDependencyCoordinate = controllerServiceDependencyBundle.getBundleDetails().getDependencyCoordinate(); } } return foundApiDependency; }
public SystemDiagnosticsSnapshotDTO.VersionInfoDTO createVersionInfoDTO() { final SystemDiagnosticsSnapshotDTO.VersionInfoDTO dto = new SystemDiagnosticsSnapshotDTO.VersionInfoDTO(); dto.setJavaVendor(System.getProperty("java.vendor")); dto.setJavaVersion(System.getProperty("java.version")); dto.setOsName(System.getProperty("os.name")); dto.setOsVersion(System.getProperty("os.version")); dto.setOsArchitecture(System.getProperty("os.arch")); final Bundle frameworkBundle = NarClassLoadersHolder.getInstance().getFrameworkBundle(); if (frameworkBundle != null) { final BundleDetails frameworkDetails = frameworkBundle.getBundleDetails(); dto.setNiFiVersion(frameworkDetails.getCoordinate().getVersion()); // Get build info dto.setBuildTag(frameworkDetails.getBuildTag()); dto.setBuildRevision(frameworkDetails.getBuildRevision()); dto.setBuildBranch(frameworkDetails.getBuildBranch()); dto.setBuildTimestamp(frameworkDetails.getBuildTimestampDate()); } return dto; }