Package org.jboss.forge.furnace.impl

Source Code of org.jboss.forge.furnace.impl.FurnaceImpl

/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.furnace.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
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 org.jboss.forge.furnace.ContainerStatus;
import org.jboss.forge.furnace.Furnace;
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.ImmutableAddonRepository;
import org.jboss.forge.furnace.lock.LockManager;
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.Assert;
import org.jboss.forge.furnace.versions.Version;
import org.jboss.modules.Module;
import org.jboss.modules.log.StreamModuleLogger;

/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*/
public class FurnaceImpl implements Furnace
{
   private static Logger logger = Logger.getLogger(FurnaceImpl.class.getName());

   private volatile boolean alive = false;
   private volatile ContainerStatus status = ContainerStatus.STOPPED;

   private final ExecutorService executor = Executors.newSingleThreadExecutor();

   private boolean serverMode = true;
   private AddonLifecycleManager manager;
   private final List<ContainerLifecycleListener> registeredListeners = new ArrayList<>();
   private final List<ListenerRegistration<ContainerLifecycleListener>> loadedListenerRegistrations = new ArrayList<>();

   private ClassLoader loader;

   private final List<AddonRepository> repositories = new ArrayList<>();
   private final Map<AddonRepository, Integer> lastRepoVersionSeen = new HashMap<>();

   private final LockManager lock = new LockManagerImpl();

   private String[] args;

   private int registryCount = 0;

   private WatchService watcher;

   public FurnaceImpl()
   {
      if (!AddonRepositoryImpl.hasRuntimeAPIVersion())
      {
         logger.warning("Could not detect Furnace runtime version - " +
                  "loading all addons, but failures may occur if versions are not compatible.");
      }

      if (!Boolean.getBoolean("furnace.logging.leak"))
      {
         /*
          * If enabled, allows the JDK java.util.logging.Level to leak ClassLoaders.
          */
         LoggingRepair.init();
      }

      if (Boolean.getBoolean("furnace.debug"))
      {
         /*
          * If enabled, prints a LOT of debug logging from JBoss Modules.
          */
         enableLogging();
      }

      try
      {
         watcher = FileSystems.getDefault().newWatchService();
      }
      catch (IOException e)
      {
         logger.log(Level.WARNING, "File monitoring could not be started.", e);
      }
   }

   @Override
   public LockManager getLockManager()
   {
      return lock;
   }

   @Override
   public ClassLoader getRuntimeClassLoader()
   {
      return loader;
   }

   public Furnace enableLogging()
   {
      assertNotAlive();
      Module.setModuleLogger(new StreamModuleLogger(System.err));
      return this;
   }

   @Override
   public Future<Furnace> startAsync()
   {
      return startAsync(FurnaceImpl.class.getClassLoader());
   }

   @Override
   public Future<Furnace> startAsync(final ClassLoader loader)
   {
      return executor.submit(new Callable<Furnace>()
      {
         @Override
         public Furnace call() throws Exception
         {
            Thread thread = new Thread()
            {
               @Override
               public void run()
               {
                  Thread.currentThread().setName("Furnace Container " + FurnaceImpl.this);
                  FurnaceImpl.this.start(loader);
               }
            };
            thread.start();

            while (!ContainerStatus.STARTED.equals(getStatus()))
               Thread.sleep(25);

            return FurnaceImpl.this;
         }
      });
   }

   @Override
   public void start()
   {
      start(FurnaceImpl.class.getClassLoader());
   }

   @Override
   public void start(ClassLoader loader)
   {
      logger.log(Level.INFO, "Furnace [" + AddonRepositoryImpl.getRuntimeAPIVersion() + "] starting.");
      assertNotAlive();
      alive = true;

      this.loader = loader;

      for (ContainerLifecycleListener listener : ServiceLoader.load(ContainerLifecycleListener.class, loader))
      {
         ListenerRegistration<ContainerLifecycleListener> registration = addContainerLifecycleListener(listener);
         loadedListenerRegistrations.add(registration);
      }

      fireBeforeContainerStartedEvent();

      try
      {
         getAddonRegistry();
         do
         {
            boolean dirty = false;
            if (!getLifecycleManager().isStartingAddons())
            {
               for (AddonRepository repository : repositories)
               {
                  int repoVersion = repository.getVersion();
                  if (repoVersion > lastRepoVersionSeen.get(repository))
                  {
                     logger.log(Level.INFO, "Detected changes in repository [" + repository + "].");
                     lastRepoVersionSeen.put(repository, repoVersion);
                     dirty = true;
                  }

               }

               WatchKey key = watcher.poll();
               while (key != null)
               {
                  List<WatchEvent<?>> events = key.pollEvents();
                  if (!events.isEmpty())
                  {
                     logger.log(Level.INFO, "Detected changes in repository [" + events.iterator().next().context()
                              + "].");
                     dirty = true;
                  }
                  key.reset();
                  key = watcher.poll();
               }

               if (dirty)
               {
                  try
                  {
                     fireBeforeConfigurationScanEvent();
                     getLifecycleManager().forceUpdate();
                     fireAfterConfigurationScanEvent();
                  }
                  catch (Exception e)
                  {
                     logger.log(Level.SEVERE, "Error occurred.", e);
                  }
               }
            }
            status = ContainerStatus.STARTED;

            Thread.sleep(100);
         }
         while (alive && serverMode);

         while (alive && getLifecycleManager().isStartingAddons())
         {
            Thread.sleep(100);
         }
      }
      catch (Exception e)
      {
         logger.log(Level.SEVERE, "Error occurred.", e);
      }
      finally
      {
         fireBeforeContainerStoppedEvent();
         status = ContainerStatus.STOPPED;
         getLifecycleManager().stopAll();
      }

      fireAfterContainerStoppedEvent();
      cleanup();
   }

   private void cleanup()
   {
      for (ListenerRegistration<ContainerLifecycleListener> registation : loadedListenerRegistrations)
      {
         registation.removeListener();
      }
      registeredListeners.clear();
      lastRepoVersionSeen.clear();
      loader = null;
      manager.dispose();
      manager = null;
      repositories.clear();
      executor.shutdownNow();
   }

   private void fireBeforeConfigurationScanEvent()
   {
      for (ContainerLifecycleListener listener : registeredListeners)
      {
         listener.beforeConfigurationScan(this);
      }
   }

   private void fireAfterConfigurationScanEvent()
   {
      for (ContainerLifecycleListener listener : registeredListeners)
      {
         listener.afterConfigurationScan(this);
      }
   }

   private void fireBeforeContainerStartedEvent()
   {
      for (ContainerLifecycleListener listener : registeredListeners)
      {
         listener.beforeStart(this);
      }
   }

   private void fireBeforeContainerStoppedEvent()
   {
      for (ContainerLifecycleListener listener : registeredListeners)
      {
         listener.beforeStop(this);
      }
   }

   private void fireAfterContainerStoppedEvent()
   {
      for (ContainerLifecycleListener listener : registeredListeners)
      {
         listener.afterStop(this);
      }
   }

   @Override
   public Furnace stop()
   {
      alive = false;
      return this;
   }

   @Override
   public void setArgs(String[] args)
   {
      assertNotAlive();
      this.args = args;
   }

   @Override
   public String[] getArgs()
   {
      return args;
   }

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

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

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

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

      if (result == null)
      {
         if (repositories == null || repositories.length == 0)
         {
            result = new AddonRegistryImpl(lock, getLifecycleManager(), getRepositories(), "ROOT");
            getLifecycleManager().addView(result);
         }
         else
         {
            result = new AddonRegistryImpl(lock, getLifecycleManager(), Arrays.asList(repositories),
                     String.valueOf(registryCount++));
            getLifecycleManager().addView(result);
            getLifecycleManager().forceUpdate();
         }
      }

      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() == null ? null : 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 Collections.unmodifiableList(repositories);
   }

   @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);

      try
      {
         if (watcher != null)
         {
            if ((directory.exists() && directory.isDirectory()) || directory.mkdirs())
            {
               directory.toPath().register(watcher,
                        StandardWatchEventKinds.ENTRY_MODIFY,
                        StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_DELETE,
                        StandardWatchEventKinds.OVERFLOW);
               logger.log(Level.INFO, "Monitoring repository [" + directory.toString() + "] for file changes.");
            }
            else
            {
               logger.log(Level.WARNING, "Cannot monitor repository [" + directory
                        + "] for changes because it is not a directory.");
            }
         }
      }
      catch (IOException e)
      {
         logger.log(Level.WARNING, "Could not monitor repository [" + directory.toString() + "] for file changes.", e);
      }

      return addRepository(repository);
   }

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

      for (AddonRepository registeredRepo : repositories)
      {
         if (registeredRepo.getRootDirectory().equals(repository.getRootDirectory()))
         {
            return registeredRepo;
         }
      }

      this.repositories.add(repository);
      lastRepoVersionSeen.put(repository, 0);

      return repository;
   }

   public void assertIsAlive()
   {
      if (!alive)
         throw new IllegalStateException(
                  "Cannot access this method until Furnace is running. Call .start() or .startAsync() first.");
   }

   public void assertNotAlive()
   {
      if (alive)
         throw new IllegalStateException("Cannot modify a running Furnace instance. Call .stop() first.");
   }

   @Override
   public ContainerStatus getStatus()
   {
      if (!alive)
         return ContainerStatus.STOPPED;

      boolean startingAddons = getLifecycleManager().isStartingAddons();
      return startingAddons ? ContainerStatus.STARTING : status;
   }

   public List<ContainerLifecycleListener> getRegisteredListeners()
   {
      return Collections.unmodifiableList(registeredListeners);
   }

   public AddonLifecycleManager getAddonLifecycleManager()
   {
      return getLifecycleManager();
   }

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

   private AddonLifecycleManager getLifecycleManager()
   {
      if (manager == null)
         manager = new AddonLifecycleManager(this);
      return manager;
   }

   @Override
   public boolean isTestMode()
   {
      return Boolean.getBoolean("org.jboss.forge.furnace.test");
   }
}
TOP

Related Classes of org.jboss.forge.furnace.impl.FurnaceImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.