Package org.jboss.forge.container.impl

Source Code of org.jboss.forge.container.impl.AddonRepositoryImpl

/*
* Copyright 2012 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.container.impl;

import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.enterprise.inject.Typed;

import org.jboss.forge.container.Forge;
import org.jboss.forge.container.addons.AddonId;
import org.jboss.forge.container.lock.LockManager;
import org.jboss.forge.container.lock.LockMode;
import org.jboss.forge.container.repositories.AddonDependencyEntry;
import org.jboss.forge.container.repositories.AddonRepository;
import org.jboss.forge.container.repositories.MutableAddonRepository;
import org.jboss.forge.container.util.Assert;
import org.jboss.forge.container.util.Files;
import org.jboss.forge.container.util.OperatingSystemUtils;
import org.jboss.forge.container.util.Streams;
import org.jboss.forge.container.versions.SingleVersion;
import org.jboss.forge.container.versions.Version;
import org.jboss.forge.container.versions.Versions;
import org.jboss.forge.parser.xml.Node;
import org.jboss.forge.parser.xml.XMLParser;
import org.jboss.forge.parser.xml.XMLParserException;

/**
* Used to perform Addon installation/registration operations.
*
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
* @author <a href="mailto:koen.aers@gmail.com">Koen Aers</a>
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a>
*/
@Typed()
public final class AddonRepositoryImpl implements MutableAddonRepository
{
   private static final Logger logger = Logger.getLogger(AddonRepositoryImpl.class.getName());

   private static final String ATTR_API_VERSION = "api-version";
   private static final String ATTR_EXPORT = "export";
   private static final String ATTR_NAME = "name";
   private static final String ATTR_OPTIONAL = "optional";
   private static final String ATTR_VERSION = "version";

   private static final String DEFAULT_ADDON_DIR = ".forge/addons";
   private static final String REGISTRY_DESCRIPTOR_NAME = "installed.xml";
   private static final String ADDON_DESCRIPTOR_FILENAME = "addon.xml";

   private static final String DEPENDENCY_TAG_NAME = "dependency";
   private static final String DEPENDENCIES_TAG_NAME = "dependencies";

   private LockManager lock;

   public static MutableAddonRepository forDirectory(Forge forge, File dir)
   {
      return new AddonRepositoryImpl(forge.getLockManager(), dir);
   }

   public static MutableAddonRepository forDefaultDirectory(Forge forge)
   {
      return new AddonRepositoryImpl(forge.getLockManager(), new File(OperatingSystemUtils.getUserHomePath(),
               DEFAULT_ADDON_DIR));
   }

   public static String getRuntimeAPIVersion()
   {
      String version = AddonRepository.class.getPackage()
               .getImplementationVersion();
      return version;
   }

   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 File addonDir;

   private int version = 1;

   private AddonRepositoryImpl(LockManager lock, File dir)
   {
      // TODO Assert.notNull(lock, "LockManager must not be null.");
      Assert.notNull(dir, "Addon directory must not be null.");
      this.addonDir = dir;
      this.lock = lock;
   }

   @Override
   public boolean deploy(final AddonId addon, final List<AddonDependencyEntry> dependencies, final List<File> resources)
   {
      return lock.performLocked(LockMode.WRITE, new Callable<Boolean>()
      {
         @Override
         public Boolean call() throws Exception
         {
            File addonSlotDir = getAddonBaseDir(addon);
            File descriptor = getAddonDescriptor(addon);
            try
            {
               if (resources != null && !resources.isEmpty())
               {
                  for (File resource : resources)
                  {
                     if (resource.isDirectory())
                     {
                        String child = addon.getName()
                                 + resource.getParentFile().getParentFile().getName();
                        child = OperatingSystemUtils.getSafeFilename(child);
                        Files.copyDirectory(resource, new File(addonSlotDir, child));
                     }
                     else
                     {
                        Files.copyFileToDirectory(resource, addonSlotDir);
                     }
                  }
               }
               /*
                * Write out the addon module dependency configuration
                */
               Node addonXml = getXmlRoot(descriptor);
               Node dependenciesNode = addonXml.getOrCreate(DEPENDENCIES_TAG_NAME);

               for (AddonDependencyEntry dependency : dependencies)
               {
                  String name = dependency.getId().getName();
                  Node dep = null;
                  for (Node node : dependenciesNode.get(DEPENDENCY_TAG_NAME))
                  {
                     if (name.equals(node.getAttribute(ATTR_NAME)))
                     {
                        dep = node;
                        break;
                     }
                  }
                  if (dep == null)
                  {
                     dep = dependenciesNode.createChild(DEPENDENCY_TAG_NAME);
                     dep.attribute(ATTR_NAME, name);
                  }
                  dep.attribute(ATTR_VERSION, dependency.getId().getVersion());
                  dep.attribute(ATTR_EXPORT, dependency.isExported());
                  dep.attribute(ATTR_OPTIONAL, dependency.isOptional());
               }

               FileOutputStream fos = null;
               try
               {
                  fos = new FileOutputStream(descriptor);
                  Streams.write(XMLParser.toXMLInputStream(addonXml), fos);
               }
               finally
               {
                  Streams.closeQuietly(fos);
               }
               return true;
            }
            catch (IOException io)
            {
               io.printStackTrace();
               return false;
            }
         }
      });
   }

   @Override
   public boolean disable(final AddonId addon)
   {
      return lock.performLocked(LockMode.WRITE, new Callable<Boolean>()
      {
         @Override
         public Boolean call() throws Exception
         {
            if (addon == null)
            {
               throw new RuntimeException("Addon must not be null");
            }

            File registryFile = getRepositoryRegistryFile();
            if (registryFile.exists())
            {
               try
               {
                  Node installed = getXmlRoot(registryFile);

                  Node child = installed.getSingle("addon@" + ATTR_NAME + "=" + addon.getName() + "&"
                           + ATTR_VERSION + "=" + addon.getVersion());
                  installed.removeChild(child);
                  saveRegistryFile(installed);
                  return true;
               }
               catch (IOException e)
               {
                  throw new RuntimeException("Could not modify [" + registryFile.getAbsolutePath() + "] - ", e);
               }
            }
            return false;
         }
      });
   }

   @Override
   public boolean enable(final AddonId addon)
   {
      return lock.performLocked(LockMode.WRITE, new Callable<Boolean>()
      {
         @Override
         public Boolean call() throws Exception
         {
            if (addon == null)
            {
               throw new RuntimeException("AddonId must not be null");
            }

            File registryFile = getRepositoryRegistryFile();
            try
            {
               Node installed = getXmlRoot(registryFile);

               installed.getOrCreate("addon@" + ATTR_NAME + "=" + (addon.getName() == null ? "" : addon.getName()) +
                        "&" + ATTR_VERSION + "=" + addon.getVersion())
                        .attribute(ATTR_API_VERSION, (addon.getApiVersion() == null ? "" : addon.getApiVersion()));

               saveRegistryFile(installed);
               return true;
            }
            catch (FileNotFoundException e)
            {
               throw new RuntimeException("Could not read [" + registryFile.getAbsolutePath() + "] - ", e);
            }
         }
      });
   }

   @Override
   public File getAddonBaseDir(final AddonId found)
   {
      Assert.notNull(found, "Addon must be specified.");
      Assert.notNull(found.getVersion(), "Addon version must be specified.");
      Assert.notNull(found.getName(), "Addon name must be specified.");

      return lock.performLocked(LockMode.READ, new Callable<File>()
      {
         @Override
         public File call() throws Exception
         {

            File addonDir = new File(getRootDirectory(), found.toCoordinates().replaceAll("[^a-zA-Z0-9]+", "-"));
            return addonDir;
         }
      });
   }

   @Override
   public Set<AddonDependencyEntry> getAddonDependencies(final AddonId addon)
   {
      return lock.performLocked(LockMode.READ, new Callable<Set<AddonDependencyEntry>>()
      {
         @Override
         public Set<AddonDependencyEntry> call() throws Exception
         {
            Set<AddonDependencyEntry> result = new HashSet<AddonDependencyEntry>();
            File descriptor = getAddonDescriptor(addon);

            try
            {
               Node installed = getXmlRoot(descriptor);

               List<Node> children = installed.get("dependencies/dependency");
               for (final Node child : children)
               {
                  if (child != null)
                  {
                     result.add(AddonDependencyEntry.create(AddonId.from(child.getAttribute(ATTR_NAME),
                              child.getAttribute(ATTR_VERSION)), Boolean.valueOf(child.getAttribute(ATTR_EXPORT)),
                              Boolean.valueOf(child.getAttribute(ATTR_OPTIONAL)))
                              );
                  }
               }
            }
            catch (FileNotFoundException e)
            {
               // already removed
            }

            return result;
         }
      });
   }

   @Override
   public File getAddonDescriptor(final AddonId addon)
   {
      return lock.performLocked(LockMode.READ, new Callable<File>()
      {
         @Override
         public File call() throws Exception
         {
            File descriptorFile = getAddonDescriptorFile(addon);
            try
            {
               if (!descriptorFile.exists())
               {
                  descriptorFile.mkdirs();
                  descriptorFile.delete();
                  descriptorFile.createNewFile();

                  FileOutputStream stream = null;
                  try
                  {
                     stream = new FileOutputStream(descriptorFile);
                     Streams.write(XMLParser.toXMLInputStream(XMLParser.parse("<addon/>")), stream);
                  }
                  finally
                  {
                     Streams.closeQuietly(stream);
                  }
               }
               return descriptorFile;
            }
            catch (Exception e)
            {
               throw new RuntimeException("Error initializing addon descriptor file.", e);
            }
         }
      });
   }

   private File getAddonDescriptorFile(final AddonId addon)
   {
      return lock.performLocked(LockMode.READ, new Callable<File>()
      {

         @Override
         public File call() throws Exception
         {
            return new File(getAddonBaseDir(addon), ADDON_DESCRIPTOR_FILENAME);
         }
      });
   }

   @Override
   public List<File> getAddonResources(final AddonId found)
   {
      return lock.performLocked(LockMode.READ, new Callable<List<File>>()
      {
         @Override
         public List<File> call() throws Exception
         {
            File dir = getAddonBaseDir(found);
            if (dir.exists())
            {
               File[] files = dir.listFiles(new FileFilter()
               {
                  @Override
                  public boolean accept(File pathname)
                  {
                     return pathname.isDirectory() || pathname.getName().endsWith(".jar");
                  }
               });
               return Arrays.asList(files);
            }
            return Collections.emptyList();
         }
      });
   }

   private AddonId getEnabled(final AddonId addon)
   {
      return lock.performLocked(LockMode.READ, new Callable<AddonId>()
      {
         @Override
         public AddonId call() throws Exception
         {
            if (addon == null)
            {
               throw new RuntimeException("Addon must not be null");
            }

            File registryFile = getRepositoryRegistryFile();
            try
            {
               Node installed = getXmlRoot(registryFile);

               List<Node> children = installed.get("addon@" + ATTR_NAME + "=" + addon.getName());
               for (Node child : children)
               {
                  if (child != null)
                  {
                     if ((addon.getApiVersion() == null)
                              || Versions.areEqual(new SingleVersion(child.getAttribute(ATTR_API_VERSION)),
                                       addon.getApiVersion()))
                     {
                        if ((addon.getVersion() == null)
                                 || Versions.areEqual(new SingleVersion(child.getAttribute(ATTR_VERSION)),
                                          addon.getVersion()))
                        {
                           return AddonId.from(child.getAttribute(ATTR_NAME),
                                    child.getAttribute(ATTR_VERSION),
                                    child.getAttribute(ATTR_API_VERSION));
                        }
                     }
                  }
               }
            }
            catch (FileNotFoundException e)
            {
               // already removed
            }
            return null;
         }
      });
   }

   @Override
   public File getRootDirectory()
   {
      return lock.performLocked(LockMode.READ, new Callable<File>()
      {
         @Override
         public File call() throws Exception
         {
            if (!addonDir.exists() || !addonDir.isDirectory())
            {
               addonDir.delete();
               System.gc();
               if (!addonDir.mkdirs())
               {
                  throw new RuntimeException("Could not create Addon Directory [" + addonDir + "]");
               }
            }
            return addonDir;
         }
      });
   }

   private File getRepositoryRegistryFile()
   {
      return lock.performLocked(LockMode.READ, new Callable<File>()
      {
         @Override
         public File call() throws Exception
         {
            File registryFile = new File(getRootDirectory(), REGISTRY_DESCRIPTOR_NAME);
            try
            {
               if (!registryFile.exists())
               {
                  registryFile.createNewFile();

                  FileOutputStream out = null;
                  try
                  {
                     out = new FileOutputStream(registryFile);
                     Streams.write(XMLParser.toXMLInputStream(XMLParser.parse("<installed></installed>")), out);
                  }
                  finally
                  {
                     Streams.closeQuietly(out);
                  }
               }
               return registryFile;
            }
            catch (Exception e)
            {
               throw new RuntimeException("Error initializing addon registry file [" + registryFile + "]", e);
            }
         }
      });
   }

   @Override
   public boolean isDeployed(final AddonId addon)
   {
      return lock.performLocked(LockMode.READ, new Callable<Boolean>()
      {
         @Override
         public Boolean call() throws Exception
         {
            File addonBaseDir = getAddonBaseDir(addon);
            File addonDescriptorFile = getAddonDescriptorFile(addon);
            List<File> addonResources = getAddonResources(addon);

            return addonBaseDir.exists() && addonDescriptorFile.exists() && !addonResources.isEmpty();
         }
      });
   }

   @Override
   public boolean isEnabled(final AddonId addon)
   {
      return getEnabled(addon) != null;
   }

   @Override
   public List<AddonId> listEnabled()
   {
      return lock.performLocked(LockMode.READ, new Callable<List<AddonId>>()
      {
         @Override
         public List<AddonId> call() throws Exception
         {
            List<AddonId> result = new ArrayList<AddonId>();
            File registryFile = getRepositoryRegistryFile();
            try
            {
               Node installed = getXmlRoot(registryFile);
               if (installed == null)
               {
                  return Collections.emptyList();
               }
               List<Node> list = installed.get("addon");
               for (Node addon : list)
               {
                  AddonId entry = AddonId.from(addon.getAttribute(ATTR_NAME),
                           addon.getAttribute(ATTR_VERSION),
                           addon.getAttribute(ATTR_API_VERSION));
                  result.add(entry);
               }
            }
            catch (XMLParserException e)
            {
               throw new RuntimeException("Invalid syntax in [" + registryFile.getAbsolutePath()
                        + "] - Please delete this file and restart Forge", e);
            }
            catch (FileNotFoundException e)
            {
               // this is OK, no addons installed
            }
            return result;
         }
      });
   }

   @Override
   public List<AddonId> listEnabledCompatibleWithVersion(final String version)
   {
      return lock.performLocked(LockMode.READ, new Callable<List<AddonId>>()
      {
         @Override
         public List<AddonId> call() throws Exception
         {
            List<AddonId> list = listEnabled();
            List<AddonId> result = list;

            result = new ArrayList<AddonId>();
            for (AddonId entry : list)
            {
               if (version == null || entry.getApiVersion() == null
                        || Versions.isApiCompatible(new SingleVersion(version), entry.getApiVersion()))
               {
                  result.add(entry);
               }
            }
            return result;
         }
      });
   }

   @Override
   public boolean undeploy(final AddonId addon)
   {
      return lock.performLocked(LockMode.WRITE, new Callable<Boolean>()
      {
         @Override
         public Boolean call() throws Exception
         {
            File dir = getAddonBaseDir(addon);
            disable(addon);
            return Files.delete(dir, true);
         }
      });
   }

   private Node getXmlRoot(File registryFile) throws FileNotFoundException, InterruptedException
   {
      Node installed = null;

      while (installed == null)
      {
         try
         {
            installed = XMLParser.parse(registryFile);
         }
         catch (XMLParserException e)
         {
            logger.log(Level.WARNING, "Error occurred while parsing [" + registryFile + "]", e);
         }
      }

      return installed;
   }

   @Override
   public Date getLastModified()
   {
      return lock.performLocked(LockMode.READ, new Callable<Date>()
      {
         @Override
         public Date call() throws Exception
         {
            return new Date(getRepositoryRegistryFile().lastModified());
         }
      });
   }

   @Override
   public int getVersion()
   {
      return version;
   }

   private void saveRegistryFile(Node installed) throws FileNotFoundException
   {
      FileOutputStream outStream = null;
      try
      {
         // TODO need to replace this with actual file-system transactionality, but should work for the common case
         outStream = new FileOutputStream(getRepositoryRegistryFile());
         incrementVersion();
         Streams.write(XMLParser.toXMLInputStream(installed), outStream);
      }
      finally
      {
         Streams.closeQuietly(outStream);
      }
   }

   private void incrementVersion()
   {
      version++;
   }

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

}
TOP

Related Classes of org.jboss.forge.container.impl.AddonRepositoryImpl

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.