| /* |
| * 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.registry; |
| |
| import org.apache.nifi.registry.jetty.JettyServer; |
| import org.apache.nifi.registry.properties.NiFiRegistryProperties; |
| import org.apache.nifi.registry.properties.NiFiRegistryPropertiesLoader; |
| import org.apache.nifi.registry.properties.SensitivePropertyProtectionException; |
| import org.apache.nifi.registry.security.crypto.BootstrapFileCryptoKeyProvider; |
| import org.apache.nifi.registry.security.crypto.CryptoKeyProvider; |
| import org.apache.nifi.registry.security.crypto.MissingCryptoKeyException; |
| import org.apache.nifi.registry.util.FileUtils; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.slf4j.bridge.SLF4JBridgeHandler; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Main entry point for NiFiRegistry. |
| */ |
| public class NiFiRegistry { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(NiFiRegistry.class); |
| |
| public static final String BOOTSTRAP_PORT_PROPERTY = "nifi.registry.bootstrap.listen.port"; |
| |
| public static final String REGISTRY_BOOTSTRAP_FILE_LOCATION = "conf/bootstrap.conf"; |
| public static final String REGISTRY_PROPERTIES_FILE_LOCATION = "conf/nifi-registry.properties"; |
| |
| private final JettyServer server; |
| private final BootstrapListener bootstrapListener; |
| private volatile boolean shutdown = false; |
| |
| public NiFiRegistry(final NiFiRegistryProperties properties, CryptoKeyProvider masterKeyProvider) |
| throws ClassNotFoundException, IOException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { |
| |
| Thread.setDefaultUncaughtExceptionHandler(new Thread.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 Registry 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 Registry because system property '" + BOOTSTRAP_PORT_PROPERTY + "' is not a valid integer in the range 1 - 65535"); |
| } |
| } else { |
| LOGGER.info("NiFi Registry 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); |
| |
| // redirect JUL log events |
| SLF4JBridgeHandler.removeHandlersForRootLogger(); |
| SLF4JBridgeHandler.install(); |
| |
| final long startTime = System.nanoTime(); |
| server = new JettyServer(properties, masterKeyProvider); |
| |
| if (shutdown) { |
| LOGGER.info("NiFi Registry has been shutdown via NiFi Registry Bootstrap. Will not start Controller"); |
| } else { |
| server.start(); |
| |
| if (bootstrapListener != null) { |
| bootstrapListener.sendStartedStatus(true); |
| } |
| |
| final long duration = System.nanoTime() - startTime; |
| LOGGER.info("Registry initialization took " + duration + " nanoseconds " |
| + "(" + (int) TimeUnit.SECONDS.convert(duration, TimeUnit.NANOSECONDS) + " seconds)."); |
| } |
| } |
| |
| protected void shutdownHook() { |
| try { |
| this.shutdown = true; |
| |
| LOGGER.info("Initiating shutdown of Jetty web server..."); |
| if (server != null) { |
| server.stop(); |
| } |
| if (bootstrapListener != null) { |
| bootstrapListener.stop(); |
| } |
| LOGGER.info("Jetty web server shutdown completed (nicely or otherwise)."); |
| } catch (final Throwable t) { |
| LOGGER.warn("Problem occurred ensuring Jetty web server was properly terminated due to " + t); |
| } |
| } |
| |
| /** |
| * Main entry point of the application. |
| * |
| * @param args things which are ignored |
| */ |
| public static void main(String[] args) { |
| LOGGER.info("Launching NiFi Registry..."); |
| |
| final CryptoKeyProvider masterKeyProvider; |
| final NiFiRegistryProperties properties; |
| try { |
| masterKeyProvider = new BootstrapFileCryptoKeyProvider(REGISTRY_BOOTSTRAP_FILE_LOCATION); |
| LOGGER.info("Read property protection key from {}", REGISTRY_BOOTSTRAP_FILE_LOCATION); |
| properties = initializeProperties(masterKeyProvider); |
| } catch (final IllegalArgumentException iae) { |
| throw new RuntimeException("Unable to load properties: " + iae, iae); |
| } |
| |
| try { |
| new NiFiRegistry(properties, masterKeyProvider); |
| } catch (final Throwable t) { |
| LOGGER.error("Failure to launch NiFi Registry due to " + t, t); |
| } |
| } |
| |
| private static NiFiRegistryProperties initializeProperties(CryptoKeyProvider masterKeyProvider) { |
| |
| String key = CryptoKeyProvider.EMPTY_KEY; |
| try { |
| key = masterKeyProvider.getKey(); |
| } catch (MissingCryptoKeyException e) { |
| LOGGER.debug("CryptoKeyProvider provided to initializeProperties method was empty - did not contain a key."); |
| // Do nothing. The key can be empty when it is passed to the loader as the loader will only use it if any properties are protected. |
| } |
| |
| try { |
| try { |
| // Load properties using key. If properties are protected and key missing, throw RuntimeException |
| NiFiRegistryProperties properties = NiFiRegistryPropertiesLoader.withKey(key).load(REGISTRY_PROPERTIES_FILE_LOCATION); |
| LOGGER.info("Loaded {} properties", properties.size()); |
| return properties; |
| } catch (SensitivePropertyProtectionException e) { |
| final String msg = "There was an issue decrypting protected properties"; |
| LOGGER.error(msg, e); |
| throw new IllegalArgumentException(msg); |
| } |
| } catch (IllegalArgumentException e) { |
| final String msg = "The bootstrap process did not provide a valid key and there are protected properties present in the properties file"; |
| LOGGER.error(msg, e); |
| throw new IllegalArgumentException(msg); |
| } |
| } |
| |
| } |