public interface Furnace extends AutoCloseable
{
   /**
    * Start this {@link Furnace} instance in a new background {@link Thread}. Return a {@link Future} that can be used
    * to block until the container has started.
    */
   public Future<Furnace> startAsync();

   /**
    * Start this {@link Furnace} instance in a new background {@link Thread}, using the given {@link ClassLoader} to
    * load core implementation resources.Return a {@link Future} that can be used to block until the container has
    * started.
    */
   public Future<Furnace> startAsync(ClassLoader loader);

   /**
    * Start this {@link Furnace} instance and wait for completion.
    */
   public void start();

   /**
    * Start this {@link Furnace} instance and wait for completion, using the given {@link ClassLoader} to load core
    * implementation resources.
    */
   public void start(ClassLoader loader);

   /**
    * Stop this {@link Furnace} instance.
    */
   public Furnace stop();

   /**
    * Returns <code>true</code> or <code>false</code> if {@link Furnace#setServerMode(boolean)} has been set.
    */
   public boolean isServerMode();

   /**
    * When server mode is set to <code>true</code>, {@link Furnace} will run as a server process until explicitly
    * stopped. When server mode is false, {@link Furnace} will process all inputs and exit once addons have completed
    * any requested operations.
    */
   public Furnace setServerMode(boolean server);

   /**
    * Get the central {@link AddonRegistry} for this {@link Furnace} instance. Contains {@link Addon} registration and
    * service information. If specific {@link AddonRepository} instances are supplied, return an registry that functions
    * with the given repositories only.
    */
   public AddonRegistry getAddonRegistry(AddonRepository... repositories);

   /**
    * Get an immutable {@link List} of configured {@link AddonRepository} instances.
    */
   public List<AddonRepository> getRepositories();

   /**
    * Add an {@link AddonRepository} to be scanned for deployed and enabled {@link Addon} instances. This method must
    * not be called once {@link Furnace} is started.
    */
   public AddonRepository addRepository(AddonRepositoryMode mode, File repository);

   /**
    * Add an {@link AddonRepository} to be scanned for deployed and enabled {@link Addon} instances. This method must
    * not be called once {@link Furnace} is started.
    */
   public AddonRepository addRepository(AddonRepository repository);

   /**
    * Get the current runtime API version of {@link Furnace}.
    */
   public Version getVersion();

   /**
    * Register a {@link ContainerLifecycleListener} instance. Returns a {@link ListenerRegistration} that can be used to
    * un-register the listener.
    */
   public ListenerRegistration<ContainerLifecycleListener> addContainerLifecycleListener(
            ContainerLifecycleListener listener);

   /**
    * Get the {@link ClassLoader} from which {@link Furnace} loaded its internal classes.
    */
   public ClassLoader getRuntimeClassLoader();

   /**
    * Get the {@link LockManager} associated with this {@link Furnace} instance
    */
   public LockManager getLockManager();

   /**
    * Get the current status of this {@link Furnace} container
    */
   public ContainerStatus getStatus();

   /**
    * Set the arguments with which {@link Furnace} should start. Typically this will simply be passed through from
    * <code>public static void main(String[] args)</code>.
    */
   public void setArgs(String[] args);

   /**
    * Get the arguments with which {@link Furnace} should start. Typically this will simply be passed through from
    * <code>public static void main(String[] args)</code>.
    *
    * @return the arguments, or <code>null</code> if no arguments were set.
    */
   public String[] getArgs();

   /**
    * Returns <code>true</code> or <code>false</code> if this {@link Furnace} instance is running in a test environment.
    */
   public boolean isTestMode();

   /**
    * Sets the {@link AddonCompatibilityStrategy} for this {@link Furnace} instance. This policy is used to determine
    * version compatibility when loading addons.
    * 
    * Default is {@link AddonCompatibilityStrategies#STRICT}
    */
   public void setAddonCompatibilityStrategy(AddonCompatibilityStrategy policy);

   /**
    * Returns the current {@link AddonCompatibilityStrategy} for this {@link Furnace} instance.
    */
   public AddonCompatibilityStrategy getAddonCompatibilityStrategy();

   /**
    * Stops the server when closed
    */
   @Override
   default void close()
   {
      stop();
   }

   /**
    * Returns the {@link Furnace} instance
    * 
    * @param loader
    * @return
    */
   static Furnace instance(ClassLoader loader)
   {
      for (FurnaceProvider provider : ServiceLoader.load(FurnaceProvider.class, loader))
      {
         return provider.getFurnace(loader);
      }

      throw new IllegalArgumentException("No Furnace container found in the  " + loader
               + ". This method must be called within a running Furnace Addon that references a valid Furnace Container.");
   }
} public interface AddonCompatibilityStrategy
{
   /**
    * Implementations should check if the given {@link AddonId} is compatible with the given {@link Furnace} instance
    * 
    * @return <code>true</code> if the {@link AddonId} is compatible with the given {@link Furnace} instance
    */
   boolean isCompatible(Furnace furnace, AddonId addonId);
} public interface AddonRegistry extends AddonView
{
   /**
    * Return an {@link Imported} instance that can be used to obtain all currently available services of the given
    * {@link Class} type.
    * 
    * @return the {@link Imported} (Never null.)
    */
   <T> Imported<T> getServices(Class<T> clazz);

   /**
    * Return an {@link Imported} instance that can be used to obtain all currently available services with
    * {@link Class#getName()} matching the given name.
    * 
    * @return the {@link Imported} (Never null.)
    */
   <T> Imported<T> getServices(String clazz);

   /**
    * Get a {@link Set} of all currently available service types.
    * 
    * @return the {@link Set} of {@link Class} types (Never null.)
    */
   Set<Class<?>> getExportedTypes();

   /**
    * Get a {@link Set} of currently available service types for which {@link Class#isAssignableFrom(Class)} returns
    * <code>true</code>.
    * 
    * @return the {@link Set} of {@link Class} types (Never null.)
    */
   <T> Set<Class<T>> getExportedTypes(Class<T> type);

   /**
    * Return the current version of this {@link AddonRegistry}, which is incremented on every addon {@link PostStartup}
    * and {@link PreShutdown} event for registered {@link Addon} instances. (Starts at 0 and counts up.)
    */
   long getVersion();

   /**
    * The {@link EventManager} bound to this {@link AddonRegistry}
    * 
    * @return a composite {@link EventManager} that delegates calls to {@link Addon#getEventManager()} in
    *         {@link AddonStatus#STARTED} state
    */
   EventManager getEventManager();
} public final class AddonRepositoryImpl implements MutableAddonRepository, DirtyCheckableRepository
{

   private static final String DEFAULT_ADDON_DIR = ".forge/addons";

   public static MutableAddonRepository forDirectory(Furnace furnace, File dir)
   {
      return new AddonRepositoryImpl(furnace, dir);
   }

   public static MutableAddonRepository forDefaultDirectory(Furnace furnace)
   {
      return new AddonRepositoryImpl(furnace, new File(OperatingSystemUtils.getUserHomePath(), DEFAULT_ADDON_DIR));
   }

   public static Version getRuntimeAPIVersion()
   {
      String versionOverride = System.getProperty("furnace.version.override");
      if (versionOverride != null)
      {
         return SingleVersion.valueOf(versionOverride);
      }
      return Versions.getImplementationVersionFor(AddonRepository.class);
   }

   public static boolean hasRuntimeAPIVersion()
   {
      return getRuntimeAPIVersion() != null;
   }

   public static boolean isApiCompatible(Version runtimeVersion, AddonId entry)
   {
      Assert.notNull(entry, "Addon entry must not be null.");

      return Versions.isApiCompatible(runtimeVersion, entry.getApiVersion());
   }

   private final MutableAddonRepositoryStorageStrategy storageRepository;

   private final MutableAddonRepositoryStateStrategy stateRepository;

   private final File addonDir;

   private AddonRepositoryImpl(Furnace furnace, File addonDir)
   {
      this(new AddonRepositoryStorageStrategyImpl(furnace.getLockManager(), addonDir),
              new AddonRepositoryStateStrategyImpl(furnace, addonDir), addonDir);
   }

   public AddonRepositoryImpl(MutableAddonRepositoryStorageStrategy storageRepository,
                              MutableAddonRepositoryStateStrategy stateRepository, File addonDir)
   {
      Assert.notNull(addonDir, "Addon directory must not be null.");

      this.storageRepository = storageRepository;
      this.stateRepository = stateRepository;
      this.addonDir = addonDir;
   }

   @Override
   public String toString()
   {
      return addonDir.getAbsolutePath();
   }

   @Override
   public int hashCode()
   {
      final int prime = 31;
      int result = 1;
      result = prime * result + ((addonDir == null) ? 0 : addonDir.hashCode());
      return result;
   }

   @Override
   public boolean equals(Object obj)
   {
      if (this == obj)
         return true;
      if (obj == null)
         return false;
      if (getClass() != obj.getClass())
         return false;
      AddonRepositoryImpl other = (AddonRepositoryImpl) obj;
      if (addonDir == null)
      {
         if (other.addonDir != null)
            return false;
      }
      else if (!addonDir.equals(other.addonDir))
         return false;
      return true;
   }

   @Override
   public boolean disable(AddonId addon)
   {
      return stateRepository.disable(addon);
   }

   @Override
   public boolean enable(AddonId addon)
   {
      return stateRepository.enable(addon);
   }

   @Override
   public boolean isEnabled(AddonId addon)
   {
      return stateRepository.isEnabled(addon);
   }

   @Override
   public List<AddonId> listAll()
   {
      return stateRepository.listAll();
   }

   @Override
   public List<AddonId> listEnabled()
   {
      return stateRepository.listEnabled();
   }

   @Override
   public List<AddonId> listEnabledCompatibleWithVersion(Version version)
   {
      return stateRepository.listEnabledCompatibleWithVersion(version);
   }

   @Override
   public int getVersion()
   {
      return stateRepository.getVersion();
   }

   @Override
   public boolean deploy(AddonId addon, Iterable<AddonDependencyEntry> dependencies, Iterable<File> resourceJars)
   {
      return storageRepository.deploy(addon, dependencies, resourceJars);
   }

   @Override
   public boolean undeploy(AddonId addonEntry)
   {
      return storageRepository.undeploy(addonEntry);
   }

   @Override
   public File getAddonBaseDir(AddonId addon)
   {
      return storageRepository.getAddonBaseDir(addon);
   }

   @Override
   public Set<AddonDependencyEntry> getAddonDependencies(AddonId addon)
   {
      return storageRepository.getAddonDependencies(addon);
   }

   @Override
   public File getAddonDescriptor(AddonId addon)
   {
      return storageRepository.getAddonDescriptor(addon);
   }

   @Override
   public List<File> getAddonResources(AddonId addon)
   {
      return storageRepository.getAddonResources(addon);
   }

   @Override
   public boolean isDeployed(AddonId addon)
   {
      return storageRepository.isDeployed(addon);
   }

   @Override
   public File getRootDirectory()
   {
      return addonDir;
   }

   @Override
   public Date getLastModified()
   {
      return new Date(addonDir.lastModified());
   }

   @Override
   public DirtyChecker createDirtyChecker()
   {
      return new CompositeDirtyChecker(storageRepository.createDirtyChecker(), stateRepository.createDirtyChecker());
   }
} public interface DirtyCheckableRepository
{
    DirtyChecker createDirtyChecker();
} public interface DirtyChecker extends AutoCloseable
{

    boolean isDirty();

    default void resetDirtyStatus()
    {
    }

    @Override
    default void close() throws Exception
    {
    }
} public class VersionDirtyChecker implements DirtyChecker
{

    private final Supplier<Integer> versionSupplier;

    private int lastRepoVersionSeen = 0;

    public VersionDirtyChecker(Supplier<Integer> versionSupplier)
    {
        this.versionSupplier = versionSupplier;
    }

    @Override
    public boolean isDirty()
    {
        return versionSupplier.get() > lastRepoVersionSeen;
    }

    @Override
    public void resetDirtyStatus()
    {
        lastRepoVersionSeen = versionSupplier.get();
    }
} public interface ListenerRegistration<T> extends AutoCloseable
{
   T removeListener();

   @Override
   default void close() throws Exception
   {
      removeListener();
   }
} public enum AddonCompatibilityStrategies implements AddonCompatibilityStrategy
{
   /**
    * A strict policy allows an addon to be loaded only when the {@link AddonId#getApiVersion()} is compatible with
    * {@link Furnace#getVersion()}
    * 
    * @see Versions#isApiCompatible(org.jboss.forge.furnace.versions.Version, org.jboss.forge.furnace.versions.Version)
    */
   STRICT
   {
      @Override
      public boolean isCompatible(Furnace furnace, AddonId addonId)
      {
         return Versions.isApiCompatible(furnace.getVersion(), addonId.getApiVersion());
      }
   },
   /**
    * A lenient policy allows an addon to be loaded without checking for API compatibility.
    */
   LENIENT
   {
      @Override
      public boolean isCompatible(Furnace furnace, AddonId addonId)
      {
         return true;
      }
   };
} public final class OperatingSystemUtils
{
   private static boolean PRETEND_WINDOWS = Boolean.getBoolean("forge.pretend_windows");

   private static String operatingSystem = null;

   /**
    * Return the name of the host operating system.
    */
   public static String getOsName()
   {
      if (operatingSystem == null)
      {
         operatingSystem = System.getProperty("os.name").toLowerCase();
      }
      return operatingSystem;
   }

   /**
    * Return <code>true</code> if the host environment is Windows.
    */
   public static boolean isWindows()
   {
      return PRETEND_WINDOWS || getOsName().startsWith("windows");
   }

   /**
    * Return <code>true</code> if the host environment is OSX.
    */
   public static boolean isOSX()
   {
      return getOsName().startsWith("mac");
   }

   /**
    * Return <code>true</code> if the host environment is Linux.
    */
   public static boolean isLinux()
   {
      return getOsName().startsWith("linux");
   }

   /**
    * Get the directory from which this application was launched.
    */
   public static File getWorkingDir()
   {
      return new File(System.getProperty("user.dir"));
   }

   /**
    * Get the FORGE_HOME directory as a {@link File}. Returns <code>null</code> if it is not defined.
    */
   public static File getForgeHomeDir()
   {
      String property = System.getProperty("forge.home");
      return property == null ? null : new File(property).getAbsoluteFile();
   }

   /**
    * Get the logged in user's home directory as a {@link File}.
    */
   public static File getUserHomeDir()
   {
      return new File(System.getProperty("user.home")).getAbsoluteFile();
   }

   /**
    * Get the path of the logged in user's home directory.
    */
   public static String getUserHomePath()
   {
      return getUserHomeDir().getAbsolutePath();
   }

   /**
    * Get the logged in user's Furnace directory.
    */
   public static File getUserForgeDir()
   {
      return new File(getUserHomeDir(), ".forge").getAbsoluteFile();
   }

   /**
    * Set this utility to pretend that the current host environment is actually Windows.
    */
   public static void setPretendWindows(boolean value)
   {
      PRETEND_WINDOWS = value;
   }

   /**
    * Return the {@link String} used as line separator for carriage returns.
    */
   public static String getLineSeparator()
   {
      return System.lineSeparator();
   }

   /**
    * Generate a string that is usable as a single file name or directory path segment on any operating system. Replaces
    * unsafe characters with the underscore '_' character
    */
   public static String getSafeFilename(String filename)
   {
      String result = filename;
      if (result != null)
      {
         result = result.replaceAll("[^a-zA-Z0-9]+", "-").replaceAll("^-+", "");
      }
      return result;
   }

   /**
    * Create a temporary directory.
    */
   public static File createTempDir() throws IllegalStateException
   {
      File baseDir = getTempDirectory();
      try
      {
         return Files.createTempDirectory(baseDir.toPath(), "tmpdir").toFile();
      }
      catch (IOException e)
      {
         throw new IllegalStateException("Error while creating temporary directory", e);
      }
   }

   /**
    * Get the current system temp directory.
    */
   public static File getTempDirectory()
   {
      return new File(System.getProperty("java.io.tmpdir"));
   }

   /**
    * @return true if running in JDK 8
    */
   public static boolean isJava8()
   {
      return System.getProperty("java.version", "").startsWith("1.8");
   }

   /**
    * @return true if running in JDK 9
    */
   public static boolean isJava9()
   {
      return System.getProperty("java.version", "").startsWith("9");
   }

}
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.jboss.forge.furnace.ContainerStatus;
import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.AddonCompatibilityStrategy;
import org.jboss.forge.furnace.addons.AddonRegistry;
import org.jboss.forge.furnace.addons.AddonView;
import org.jboss.forge.furnace.impl.addons.AddonLifecycleManager;
import org.jboss.forge.furnace.impl.addons.AddonRegistryImpl;
import org.jboss.forge.furnace.impl.addons.AddonRepositoryImpl;
import org.jboss.forge.furnace.impl.addons.DirtyCheckableRepository;
import org.jboss.forge.furnace.impl.addons.DirtyChecker;
import org.jboss.forge.furnace.impl.addons.ImmutableAddonRepository;
import org.jboss.forge.furnace.impl.addons.VersionDirtyChecker;
import org.jboss.forge.furnace.impl.lock.LockManagerImpl;
import org.jboss.forge.furnace.lock.LockManager;
import org.jboss.forge.furnace.lock.LockMode;
import org.jboss.forge.furnace.repositories.AddonRepository;
import org.jboss.forge.furnace.repositories.AddonRepositoryMode;
import org.jboss.forge.furnace.spi.ContainerLifecycleListener;
import org.jboss.forge.furnace.spi.ListenerRegistration;
import org.jboss.forge.furnace.util.AddonCompatibilityStrategies;
import org.jboss.forge.furnace.util.Assert;
import org.jboss.forge.furnace.util.OperatingSystemUtils;
import org.jboss.forge.furnace.util.Strings;
import org.jboss.forge.furnace.versions.Version;
import org.jboss.modules.Module;
import org.jboss.modules.log.StreamModuleLogger;
      return args;
   }

   @Override
   public boolean isServerMode()
   {
      return serverMode;
   }

   @Override
   public Furnace setServerMode(boolean server)
   {
      assertNotAlive();
      this.serverMode = server;
      return this;
   }

   @Override
   public AddonRegistry getAddonRegistry(final AddonRepository... repositories)
   {
      assertIsAlive();

      AddonRegistry result = getLifecycleManager().findView(repositories);

      if (result == null)
      {
         result = lock.performLocked(LockMode.WRITE, new Callable<AddonRegistry>()
         {
            @Override
            public AddonRegistry call() throws Exception
            {
               AddonRegistry registry = getLifecycleManager().findView(repositories);
               if (registry == null)
               {
                  if (repositories == null || repositories.length == 0)
                  {
                     String name = "ROOT" + "_" + UUID.randomUUID().toString();
                     registry = new AddonRegistryImpl(lock, getLifecycleManager(), getRepositories(), name);
                  }
                  else
                  {
                     String name = String.valueOf(registryCount++ + "_" + UUID.randomUUID().toString());
                     registry = new AddonRegistryImpl(lock, getLifecycleManager(), Arrays.asList(repositories), name);
                  }
                  getLifecycleManager().addView(registry);
                  getLifecycleManager().forceUpdate();
               }
               return registry;
            }
         });
      }

      return result;
   }

   public void disposeAddonView(AddonView view)
   {
      assertIsAlive();

      if (getAddonRegistry().equals(view))
         throw new IllegalArgumentException(
                  "Cannot dispose the root AddonRegistry. Call .stop() instead.");

      getLifecycleManager().removeView(view);
      getLifecycleManager().forceUpdate();
   }

   @Override
   public Version getVersion()
   {
      return AddonRepositoryImpl.getRuntimeAPIVersion();
   }

   @Override
   public ListenerRegistration<ContainerLifecycleListener> addContainerLifecycleListener(
            final ContainerLifecycleListener listener)
   {
      registeredListeners.add(listener);
      return new ListenerRegistration<ContainerLifecycleListener>()
      {
         @Override
         public ContainerLifecycleListener removeListener()
         {
            registeredListeners.remove(listener);
            return listener;
         }
      };
   }

   @Override
   public List<AddonRepository> getRepositories()
   {
      return repositories.stream().map(RepositoryEntry::getRepository).collect(Collectors.toList());
   }

   @Override
   public AddonRepository addRepository(AddonRepositoryMode mode, File directory)
   {
      Assert.notNull(mode, "Addon repository mode must not be null.");
      Assert.notNull(directory, "Addon repository directory must not be null.");

      AddonRepository repository = AddonRepositoryImpl.forDirectory(this, directory);

      if (mode.isImmutable())
         repository = new ImmutableAddonRepository(repository);
      return addRepository(repository);
   }

   @Override
   public AddonRepository addRepository(final AddonRepository repository)
   {
      Assert.notNull(repository, "Addon repository must not be null.");

      final DirtyChecker dirtyChecker;
      if (repository instanceof DirtyCheckableRepository)
      {
         dirtyChecker = ((DirtyCheckableRepository) repository).createDirtyChecker();
      }
      else
      {
         dirtyChecker = new VersionDirtyChecker(repository::getVersion);