// Path: src/main/java/fabzo/kraken/components/InfrastructureComponent.java
// public abstract class InfrastructureComponent implements Named {
//     private ComponentState state = ComponentState.WAITING;
//     private List<Wait> waitFunctions = List.empty();
// 
//     public InfrastructureComponent withWait(final Wait waitFunc) {
//         this.waitFunctions = waitFunctions.append(waitFunc);
//         return this;
//     }
// 
//     public List<Wait> waitFuncs() {
//         return waitFunctions;
//     }
// 
//     public void setState(final ComponentState state) {
//         this.state = state;
//     }
// 
//     public ComponentState state() {
//         return state;
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/handler/LifecycleHandler.java
// public interface LifecycleHandler {
// 
//     boolean canRun(final Class<? extends InfrastructureComponent> clazz);
// 
//     boolean run(final InfrastructureComponent component, final EnvironmentContext ctx);
// 
//     void stop(final InfrastructureComponent component, final EnvironmentContext ctx);
// }
// 
// Path: src/main/java/fabzo/kraken/utils/ShutdownHookManager.java
// public class ShutdownHookManager {
//     private static final PriorityQueue<ShutdownHookEntry> shutdownHooks = new PriorityQueue<>((o1, o2) -> Integer.compare(o2.priority, o1.priority));
// 
//     static {
//         Runtime.getRuntime().addShutdownHook(new Thread(ShutdownHookManager::executeShutdownHooks));
//     }
// 
//     private static void executeShutdownHooks() {
//         synchronized (shutdownHooks) {
//             ShutdownHookEntry entry;
//             while ((entry = shutdownHooks.poll()) != null) {
//                 entry.hook.run();
//             }
//         }
//     }
// 
//     public static void addHook(final int priority, final Runnable shutdownHook) {
//         ObjectUtil.checkNotNull(shutdownHook, "Shutdown hook should not be null");
//         synchronized (shutdownHooks) {
//             shutdownHooks.add(new ShutdownHookEntry(priority, shutdownHook));
//         }
//     }
// 
//     public static class ShutdownHookEntry {
//         public int priority;
//         public Runnable hook;
// 
//         public ShutdownHookEntry(final int priority, final Runnable hook) {
//             this.priority = priority;
//             this.hook = hook;
//         }
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/utils/Utils.java
// public class Utils {
//     private static final String RUNNING_IN_IDE = "RUNNING_IN_IDE";
//     private static final Object ASSIGNED_PORTS_MONITOR = new Object();
//     private static Set<Integer> assignedPorts = HashSet.empty();
// 
//     public static boolean isRunningInIDE() {
//         final boolean foundIdeaRtJar = System.getProperty("java.class.path").contains("idea_rt.jar");
// 
//         String runningInIDE = System.getenv(RUNNING_IN_IDE);
//         if (runningInIDE == null) {
//             runningInIDE = System.getProperty(RUNNING_IN_IDE);
//         }
// 
//         return foundIdeaRtJar || "true".equalsIgnoreCase(runningInIDE);
//     }
// 
//     public static Try<Integer> getAvailablePort() {
//         synchronized (ASSIGNED_PORTS_MONITOR) {
//             return Try.of(() -> {
//                 int hostPort;
//                 do {
//                     final ServerSocket serverSocket = new ServerSocket(0);
//                     hostPort = serverSocket.getLocalPort();
//                     serverSocket.close();
//                 } while (assignedPorts.contains(hostPort));
// 
//                 assignedPorts = assignedPorts.add(hostPort);
// 
//                 return hostPort;
//             });
//         }
//     }
// 
//     public static Try<String> getPublicFacingIP() {
//         return Try.of(() -> {
//             final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
//             while (interfaces.hasMoreElements()) {
//                 final NetworkInterface iface = interfaces.nextElement();
//                 if (iface.isLoopback() || !iface.isUp() || iface.isVirtual() || iface.isPointToPoint()) {
//                     continue;
//                 }
// 
//                 final Enumeration<InetAddress> addresses = iface.getInetAddresses();
//                 while (addresses.hasMoreElements()) {
//                     final InetAddress addr = addresses.nextElement();
// 
//                     final String ip = addr.getHostAddress();
//                     if (Inet4Address.class == addr.getClass()) {
//                         return ip;
//                     }
//                 }
//             }
// 
//             throw new Exception("Could not find public facing IP");
//         });
//     }
// 
//     public static String nameOf(final Object obj) {
//         val aClass = obj.getClass();
//         if ("".equals(aClass.getSimpleName())) {
//             return aClass.getName();
//         }
//         return aClass.getSimpleName();
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/utils/Utils.java
// public static String nameOf(final Object obj) {
//     val aClass = obj.getClass();
//     if ("".equals(aClass.getSimpleName())) {
//         return aClass.getName();
//     }
//     return aClass.getSimpleName();
// }


import fabzo.kraken.components.InfrastructureComponent;
import fabzo.kraken.handler.LifecycleHandler;
import fabzo.kraken.utils.ShutdownHookManager;
import fabzo.kraken.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import static fabzo.kraken.utils.Utils.nameOf;
package fabzo.kraken;



@Slf4j
public class Environment {
    private final EnvironmentModule module;
    private final EnvironmentContext context;

    public Environment(final EnvironmentModule module) {
        this.module = module;

        val salt = RandomStringUtils.randomAlphabetic(8).toLowerCase();
        log.info("Using environment salt {}", salt);
        this.context = new EnvironmentContext(salt);

        setupEnvironment();
    }

    private void setupEnvironment() {
        Utils.getPublicFacingIP().forEach(context::setPublicFacingIP);
        registerShutdownHook();
    }

    private void registerShutdownHook() {
        ShutdownHookManager.addHook(1000, () -> context.shutdownHooks().forEach(Runnable::run));
    }

    public void start() {
        log.info("Starting all components of {}", nameOf(module));
        module.components().forEach(this::start);
    }

// Path: src/main/java/fabzo/kraken/components/InfrastructureComponent.java
// public abstract class InfrastructureComponent implements Named {
//     private ComponentState state = ComponentState.WAITING;
//     private List<Wait> waitFunctions = List.empty();
// 
//     public InfrastructureComponent withWait(final Wait waitFunc) {
//         this.waitFunctions = waitFunctions.append(waitFunc);
//         return this;
//     }
// 
//     public List<Wait> waitFuncs() {
//         return waitFunctions;
//     }
// 
//     public void setState(final ComponentState state) {
//         this.state = state;
//     }
// 
//     public ComponentState state() {
//         return state;
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/handler/LifecycleHandler.java
// public interface LifecycleHandler {
// 
//     boolean canRun(final Class<? extends InfrastructureComponent> clazz);
// 
//     boolean run(final InfrastructureComponent component, final EnvironmentContext ctx);
// 
//     void stop(final InfrastructureComponent component, final EnvironmentContext ctx);
// }
// 
// Path: src/main/java/fabzo/kraken/utils/ShutdownHookManager.java
// public class ShutdownHookManager {
//     private static final PriorityQueue<ShutdownHookEntry> shutdownHooks = new PriorityQueue<>((o1, o2) -> Integer.compare(o2.priority, o1.priority));
// 
//     static {
//         Runtime.getRuntime().addShutdownHook(new Thread(ShutdownHookManager::executeShutdownHooks));
//     }
// 
//     private static void executeShutdownHooks() {
//         synchronized (shutdownHooks) {
//             ShutdownHookEntry entry;
//             while ((entry = shutdownHooks.poll()) != null) {
//                 entry.hook.run();
//             }
//         }
//     }
// 
//     public static void addHook(final int priority, final Runnable shutdownHook) {
//         ObjectUtil.checkNotNull(shutdownHook, "Shutdown hook should not be null");
//         synchronized (shutdownHooks) {
//             shutdownHooks.add(new ShutdownHookEntry(priority, shutdownHook));
//         }
//     }
// 
//     public static class ShutdownHookEntry {
//         public int priority;
//         public Runnable hook;
// 
//         public ShutdownHookEntry(final int priority, final Runnable hook) {
//             this.priority = priority;
//             this.hook = hook;
//         }
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/utils/Utils.java
// public class Utils {
//     private static final String RUNNING_IN_IDE = "RUNNING_IN_IDE";
//     private static final Object ASSIGNED_PORTS_MONITOR = new Object();
//     private static Set<Integer> assignedPorts = HashSet.empty();
// 
//     public static boolean isRunningInIDE() {
//         final boolean foundIdeaRtJar = System.getProperty("java.class.path").contains("idea_rt.jar");
// 
//         String runningInIDE = System.getenv(RUNNING_IN_IDE);
//         if (runningInIDE == null) {
//             runningInIDE = System.getProperty(RUNNING_IN_IDE);
//         }
// 
//         return foundIdeaRtJar || "true".equalsIgnoreCase(runningInIDE);
//     }
// 
//     public static Try<Integer> getAvailablePort() {
//         synchronized (ASSIGNED_PORTS_MONITOR) {
//             return Try.of(() -> {
//                 int hostPort;
//                 do {
//                     final ServerSocket serverSocket = new ServerSocket(0);
//                     hostPort = serverSocket.getLocalPort();
//                     serverSocket.close();
//                 } while (assignedPorts.contains(hostPort));
// 
//                 assignedPorts = assignedPorts.add(hostPort);
// 
//                 return hostPort;
//             });
//         }
//     }
// 
//     public static Try<String> getPublicFacingIP() {
//         return Try.of(() -> {
//             final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
//             while (interfaces.hasMoreElements()) {
//                 final NetworkInterface iface = interfaces.nextElement();
//                 if (iface.isLoopback() || !iface.isUp() || iface.isVirtual() || iface.isPointToPoint()) {
//                     continue;
//                 }
// 
//                 final Enumeration<InetAddress> addresses = iface.getInetAddresses();
//                 while (addresses.hasMoreElements()) {
//                     final InetAddress addr = addresses.nextElement();
// 
//                     final String ip = addr.getHostAddress();
//                     if (Inet4Address.class == addr.getClass()) {
//                         return ip;
//                     }
//                 }
//             }
// 
//             throw new Exception("Could not find public facing IP");
//         });
//     }
// 
//     public static String nameOf(final Object obj) {
//         val aClass = obj.getClass();
//         if ("".equals(aClass.getSimpleName())) {
//             return aClass.getName();
//         }
//         return aClass.getSimpleName();
//     }
// }
// 
// Path: src/main/java/fabzo/kraken/utils/Utils.java
// public static String nameOf(final Object obj) {
//     val aClass = obj.getClass();
//     if ("".equals(aClass.getSimpleName())) {
//         return aClass.getName();
//     }
//     return aClass.getSimpleName();
// }


// Path: src/main/java/fabzo/kraken/Environment.java
import fabzo.kraken.components.InfrastructureComponent;
import fabzo.kraken.handler.LifecycleHandler;
import fabzo.kraken.utils.ShutdownHookManager;
import fabzo.kraken.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import static fabzo.kraken.utils.Utils.nameOf;

package fabzo.kraken;



@Slf4j
public class Environment {
    private final EnvironmentModule module;
    private final EnvironmentContext context;

    public Environment(final EnvironmentModule module) {
        this.module = module;

        val salt = RandomStringUtils.randomAlphabetic(8).toLowerCase();
        log.info("Using environment salt {}", salt);
        this.context = new EnvironmentContext(salt);

        setupEnvironment();
    }

    private void setupEnvironment() {
        Utils.getPublicFacingIP().forEach(context::setPublicFacingIP);
        registerShutdownHook();
    }

    private void registerShutdownHook() {
        ShutdownHookManager.addHook(1000, () -> context.shutdownHooks().forEach(Runnable::run));
    }

    public void start() {
        log.info("Starting all components of {}", nameOf(module));
        module.components().forEach(this::start);
    }

    private void start(final InfrastructureComponent component) {