public NiFi(final NiFiProperties properties) throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler() { @Override public void uncaughtException(final Thread t, final Throwable e) { logger.error("An Unknown Error Occurred in Thread {}: {}", t, e.toString()); logger.error("", e); } }); // register the shutdown hook Runtime.getRuntime() .addShutdownHook( new Thread( new Runnable() { @Override public void run() { // shutdown the jetty server shutdownHook(); } })); final String bootstrapPort = System.getProperty(BOOTSTRAP_PORT_PROPERTY); if (bootstrapPort != null) { try { final int port = Integer.parseInt(bootstrapPort); if (port < 1 || port > 65535) { throw new RuntimeException( "Failed to start NiFi because system property '" + BOOTSTRAP_PORT_PROPERTY + "' is not a valid integer in the range 1 - 65535"); } bootstrapListener = new BootstrapListener(this, port); bootstrapListener.start(); } catch (final NumberFormatException nfe) { throw new RuntimeException( "Failed to start NiFi because system property '" + BOOTSTRAP_PORT_PROPERTY + "' is not a valid integer in the range 1 - 65535"); } } else { logger.info( "NiFi started without Bootstrap Port information provided; will not listen for requests from Bootstrap"); bootstrapListener = null; } // delete the web working dir - if the application does not start successfully // the web app directories might be in an invalid state. when this happens // jetty will not attempt to re-extract the war into the directory. by removing // the working directory, we can be assured that it will attempt to extract the // war every time the application starts. File webWorkingDir = properties.getWebWorkingDirectory(); FileUtils.deleteFilesInDirectory(webWorkingDir, null, logger, true, true); FileUtils.deleteFile(webWorkingDir, logger, 3); detectTimingIssues(); // redirect JUL log events SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); // expand the nars final ExtensionMapping extensionMapping = NarUnpacker.unpackNars(properties); // load the extensions classloaders NarClassLoaders.getInstance() .init( properties.getFrameworkWorkingDirectory(), properties.getExtensionsWorkingDirectory()); // load the framework classloader final ClassLoader frameworkClassLoader = NarClassLoaders.getInstance().getFrameworkClassLoader(); if (frameworkClassLoader == null) { throw new IllegalStateException("Unable to find the framework NAR ClassLoader."); } // discover the extensions ExtensionManager.discoverExtensions(NarClassLoaders.getInstance().getExtensionClassLoaders()); ExtensionManager.logClassLoaderMapping(); DocGenerator.generate(properties); // load the server from the framework classloader Thread.currentThread().setContextClassLoader(frameworkClassLoader); Class<?> jettyServer = Class.forName("org.apache.nifi.web.server.JettyServer", true, frameworkClassLoader); Constructor<?> jettyConstructor = jettyServer.getConstructor(NiFiProperties.class); final long startTime = System.nanoTime(); nifiServer = (NiFiServer) jettyConstructor.newInstance(properties); nifiServer.setExtensionMapping(extensionMapping); if (shutdown) { logger.info("NiFi has been shutdown via NiFi Bootstrap. Will not start Controller"); } else { nifiServer.start(); if (bootstrapListener != null) { bootstrapListener.sendStartedStatus(true); } final long endTime = System.nanoTime(); logger.info("Controller initialization took " + (endTime - startTime) + " nanoseconds."); } }
/** Should be called at most once. */ private InitContext load(final File frameworkWorkingDir, final File extensionsWorkingDir) throws IOException, ClassNotFoundException { // get the system classloader final ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); // find all nar files and create class loaders for them. final Map<String, ClassLoader> extensionDirectoryClassLoaderLookup = new LinkedHashMap<>(); final Map<String, ClassLoader> narIdClassLoaderLookup = new HashMap<>(); // make sure the nar directory is there and accessible FileUtils.ensureDirectoryExistAndCanAccess(frameworkWorkingDir); FileUtils.ensureDirectoryExistAndCanAccess(extensionsWorkingDir); final List<File> narWorkingDirContents = new ArrayList<>(); final File[] frameworkWorkingDirContents = frameworkWorkingDir.listFiles(); if (frameworkWorkingDirContents != null) { narWorkingDirContents.addAll(Arrays.asList(frameworkWorkingDirContents)); } final File[] extensionsWorkingDirContents = extensionsWorkingDir.listFiles(); if (extensionsWorkingDirContents != null) { narWorkingDirContents.addAll(Arrays.asList(extensionsWorkingDirContents)); } if (!narWorkingDirContents.isEmpty()) { final List<NarDetails> narDetails = new ArrayList<>(); // load the nar details which includes and nar dependencies for (final File unpackedNar : narWorkingDirContents) { final NarDetails narDetail = getNarDetails(unpackedNar); // ensure the nar contained an identifier if (narDetail.getNarId() == null) { logger.warn("No NAR Id found. Skipping: " + unpackedNar.getAbsolutePath()); continue; } // store the nar details narDetails.add(narDetail); } // attempt to locate the jetty nar ClassLoader jettyClassLoader = null; for (final Iterator<NarDetails> narDetailsIter = narDetails.iterator(); narDetailsIter.hasNext(); ) { final NarDetails narDetail = narDetailsIter.next(); // look for the jetty nar if (JETTY_NAR_ID.equals(narDetail.getNarId())) { // create the jetty classloader jettyClassLoader = createNarClassLoader(narDetail.getNarWorkingDirectory(), systemClassLoader); // remove the jetty nar since its already loaded narIdClassLoaderLookup.put(narDetail.getNarId(), jettyClassLoader); narDetailsIter.remove(); break; } } // ensure the jetty nar was found if (jettyClassLoader == null) { throw new IllegalStateException("Unable to locate Jetty bundle."); } int narCount; do { // record the number of nars to be loaded narCount = narDetails.size(); // attempt to create each nar class loader for (final Iterator<NarDetails> narDetailsIter = narDetails.iterator(); narDetailsIter.hasNext(); ) { final NarDetails narDetail = narDetailsIter.next(); final String narDependencies = narDetail.getNarDependencyId(); // see if this class loader is eligible for loading ClassLoader narClassLoader = null; if (narDependencies == null) { narClassLoader = createNarClassLoader(narDetail.getNarWorkingDirectory(), jettyClassLoader); } else if (narIdClassLoaderLookup.containsKey(narDetail.getNarDependencyId())) { narClassLoader = createNarClassLoader( narDetail.getNarWorkingDirectory(), narIdClassLoaderLookup.get(narDetail.getNarDependencyId())); } // if we were able to create the nar class loader, store it and remove the details if (narClassLoader != null) { extensionDirectoryClassLoaderLookup.put( narDetail.getNarWorkingDirectory().getCanonicalPath(), narClassLoader); narIdClassLoaderLookup.put(narDetail.getNarId(), narClassLoader); narDetailsIter.remove(); } } // attempt to load more if some were successfully loaded this iteration } while (narCount != narDetails.size()); // see if any nars couldn't be loaded for (final NarDetails narDetail : narDetails) { logger.warn( String.format( "Unable to resolve required dependency '%s'. Skipping NAR %s", narDetail.getNarDependencyId(), narDetail.getNarWorkingDirectory().getAbsolutePath())); } } return new InitContext( frameworkWorkingDir, extensionsWorkingDir, narIdClassLoaderLookup.get(FRAMEWORK_NAR_ID), new LinkedHashMap<>(extensionDirectoryClassLoaderLookup)); }