All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gate.creole.Plugin Maven / Gradle / Ivy

Go to download

GATE - general achitecture for text engineering - is open source software capable of solving almost any text processing problem. This artifact enables you to embed the core GATE Embedded with its essential dependencies. You will able to use the GATE Embedded API and load and store GATE XML documents. This artifact is the perfect dependency for CREOLE plugins or for applications that need to customize the GATE dependencies due to confict with their own dependencies or for lower footprint.

The newest version!
/*
 * Plugin.java
 *
 * Copyright (c) 2016, The University of Sheffield. See the file COPYRIGHT.txt
 * in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
 *
 * This file is part of GATE (see http://gate.ac.uk/), and is free software,
 * licenced under the GNU Library General Public License, Version 3, June 2007
 * (in the distribution as file licence.html, and also available at
 * http://gate.ac.uk/gate/licence.html).
 *
 * Mark A. Greenwood, 3rd April 2016
 */

package gate.creole;

import static gate.util.maven.Utils.getRepositoryList;
import static gate.util.maven.Utils.getRepositorySession;
import static gate.util.maven.Utils.getRepositorySystem;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.apache.maven.model.Model;
import org.apache.maven.model.building.DefaultModelBuilderFactory;
import org.apache.maven.model.building.DefaultModelBuildingRequest;
import org.apache.maven.model.building.ModelBuilder;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.repository.WorkspaceReader;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.ArtifactResult;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResult;
import org.eclipse.aether.transfer.TransferCancelledException;
import org.eclipse.aether.transfer.TransferEvent;
import org.eclipse.aether.transfer.TransferListener;
import org.eclipse.aether.util.artifact.SubArtifact;
import org.eclipse.aether.util.version.GenericVersionScheme;
import org.eclipse.aether.version.InvalidVersionSpecificationException;
import org.eclipse.aether.version.Version;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

import gate.Gate;
import gate.Gate.ResourceInfo;
import gate.Resource;
import gate.Utils;
import gate.creole.metadata.CreoleResource;
import gate.util.asm.AnnotationVisitor;
import gate.util.asm.ClassReader;
import gate.util.asm.ClassVisitor;
import gate.util.asm.Opcodes;
import gate.util.asm.Type;
import gate.util.asm.commons.EmptyVisitor;
import gate.util.maven.SimpleMavenCache;
import gate.util.maven.SimpleModelResolver;
import gate.util.persistence.PersistenceManager;

public abstract class Plugin {

  public static interface DownloadListener {
    public void downloadStarted(String name);

    public void downloadProgressed(String name, long totalBytes,
        long transferredBytes);

    public void downloadSucceeded(String name);

    public void downloadFailed(String name, Throwable cause);
  }

  protected transient Vector downloadListeners = null;

  public void addDownloadListener(DownloadListener listener) {
    @SuppressWarnings("unchecked")
    Vector v = downloadListeners == null
        ? new Vector(2)
        : (Vector)downloadListeners.clone();
    if(!v.contains(listener)) {
      v.addElement(listener);
      downloadListeners = v;
    }
  }

  public void removeDownloadListener(DownloadListener listener) {
    if(downloadListeners != null && downloadListeners.contains(listener)) {
      @SuppressWarnings("unchecked")
      Vector v =
          (Vector)downloadListeners.clone();
      v.removeElement(listener);
      downloadListeners = v;
    }
  }

  protected void fireDownloadStarted(String name) {
    if(downloadListeners != null) {
      for(DownloadListener listener : downloadListeners) {
        listener.downloadStarted(name);
      }
    }
  }

  protected void fireDownloadSucceeded(String name) {
    if(downloadListeners != null) {
      for(DownloadListener listener : downloadListeners) {
        listener.downloadSucceeded(name);
      }
    }
  }

  protected void fireDownloadFailed(String name, Throwable cause) {
    if(downloadListeners != null) {
      for(DownloadListener listener : downloadListeners) {
        listener.downloadFailed(name, cause);
      }
    }
  }

  protected void fireDownloadProgressed(String name, long totalBytes,
      long transferredBytes) {
    if(downloadListeners != null) {
      for(DownloadListener listener : downloadListeners) {
        listener.downloadProgressed(name, totalBytes, transferredBytes);
      }
    }
  }

  protected static final Logger log = LoggerFactory.getLogger(Plugin.class);
  
  private static GenericVersionScheme versionScheme =
      new GenericVersionScheme();

  /**
   * Is the plugin valid (i.e. is the location reachable and the creole.xml file
   * parsable).
   */
  protected transient boolean valid = true;

  /**
   * This is the URL against which all relative URLs in the CREOLE metadata are
   * resolved
   */
  protected transient URL baseURL;

  protected transient String name;

  protected transient String description;
  
  protected transient String version;
  
  protected transient String minGateVersion;

  /**
   * The list of {@link gate.Gate.ResourceInfo} objects within this plugin
   */
  protected transient List resourceInfoList = null;

  /**
   * The set of other plugins that must be loaded prior to the loading of this
   * plugin
   */
  protected transient Set requiredPlugins = null;

  /**
   * Returns a fully expanded creole.xml file that can be used for loading the
   * plugin into GATE
   * 
   * @return a fully expanded creole.xml file that can be used for loading the
   *         plugin into GATE
   */
  public abstract org.jdom.Document getCreoleXML() throws Exception;

  /**
   * Returns a creole.xml file which is only guaranteed to contain information
   * about the resources within the plugin and not dependency requirements.
   * 
   * @return a creole.xml file which is only guaranteed to contain information
   *         about the resources within the plugin and not dependency
   *         requirements.
   */
  public org.jdom.Document getMetadataXML() throws Exception {
    return getCreoleXML();
  }

  public String getName() {
    return name;
  }

  public String getDescription() {
    return description;
  }
  
  /**
   * Get the version of this plugin.
   */
  public String getVersion() {
    return version == null ? "" : version;
  }
  
  public String getMinimumGateVersion() {
    return minGateVersion;
  }

  public Set getRequiredPlugins() {
    if(requiredPlugins == null) parseCreole();

    return requiredPlugins;
  }

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

  @Override
  public String toString() {
    return getName();
  }

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

  public List getResourceInfoList() {
    if(resourceInfoList == null) parseCreole();

    return resourceInfoList;
  }

  public URL getBaseURL() {
    if(baseURL == null) {
      try {
        getCreoleXML();
      } catch(Exception e) {
        throw new RuntimeException("Unable to determine base URL",e);
      }
    }
    return baseURL;
  }

  public URI getBaseURI() throws URISyntaxException {
    return getBaseURL().toURI();
  }

  public boolean isValid() {
    // we need to ensure parseCreole() has been called before we can know if the
    // valid flag is .... valid
    getResourceInfoList();
    
    //Main.version
    if (Gate.VERSION == null || minGateVersion == null || minGateVersion.isEmpty()) {
      Utils.logOnce(log, Level.DEBUG,
          "unable to check minimum GATE version, plugins may not load or might produce errors");
      return valid;
    }
    
    try {
      Version pluginVersion = versionScheme.parseVersion(minGateVersion);
      
      boolean validGateVersion = Gate.VERSION.compareTo(pluginVersion) >= 0;
      
      if(!validGateVersion) {
        Utils.logOnce(log, Level.WARN,
            getName()
            + " is not compatible with this version of GATE, requires at least GATE"
            + pluginVersion.toString());
      }
      
      valid = valid && validGateVersion; 
    } catch(InvalidVersionSpecificationException e) {
      Utils.logOnce(log, Level.DEBUG,
          "unable to parse minimum GATE version, plugin, " + getName()
              + ", may not load or might produce errors");
    }
    
    return valid;
  }

  public void copyResources(File dir) throws IOException, URISyntaxException {
    throw new UnsupportedOperationException(
        "This plugin does not contain any resources that can be copied");
  }

  public boolean hasResources() {
    return false;
  }

  /**
   * Given a class return the Plugin instance from which it was loaded.
   * 
   * @return the Plugin instance from which the specified class was loaded
   */
  public static Plugin getPlugin(Class clazz) throws IOException {
    URL creoleURL = clazz.getResource("/creole.xml");

    if(creoleURL == null) return null;

    URL baseURL = new URL(creoleURL, ".");

    for(Plugin plugin : Gate.getCreoleRegister().getPlugins()) {
      if(plugin.getBaseURL().equals(baseURL)) return plugin;
    }

    return null;
  }

  protected void parseCreole() {

    valid = true;

    resourceInfoList = new ArrayList();
    requiredPlugins = new LinkedHashSet();

    String relativePathMarker = "$relpath$";
    String gatehomePathMarker = "$gatehome$";
    String gatepluginsPathMarker = "$gateplugins$";

    try {
      org.jdom.Document creoleDoc = getMetadataXML();
      
      minGateVersion = creoleDoc.getRootElement().getAttributeValue("GATE-MIN","");
      version = creoleDoc.getRootElement().getAttributeValue("VERSION","");
      description = creoleDoc.getRootElement().getAttributeValue("DESCRIPTION");
      name = creoleDoc.getRootElement().getAttributeValue("NAME");

      final Map resInfos =
          new LinkedHashMap();
      List jobsList = new ArrayList();
      List jarsToScan = new ArrayList();
      List allJars = new ArrayList();
      jobsList.add(creoleDoc.getRootElement());
      while(!jobsList.isEmpty()) {
        Element currentElem = jobsList.remove(0);
        if(currentElem.getName().equalsIgnoreCase("JAR")) {
          @SuppressWarnings("unchecked")
          List attrs = currentElem.getAttributes();
          Iterator attrsIt = attrs.iterator();
          while(attrsIt.hasNext()) {
            Attribute attr = attrsIt.next();
            if(attr.getName().equalsIgnoreCase("SCAN")
                && attr.getBooleanValue()) {
              jarsToScan.add(currentElem.getTextTrim());
              break;
            }
          }
          allJars.add(currentElem.getTextTrim());
        } else if(currentElem.getName().equalsIgnoreCase("RESOURCE")) {
          // we don't go deeper than resources so no recursion here
          String resName = currentElem.getChildTextTrim("NAME");
          String resClass = currentElem.getChildTextTrim("CLASS");
          String resComment = currentElem.getChildTextTrim("COMMENT");
          String resHelpURL = currentElem.getChildText("HELPURL");
          if(!resInfos.containsKey(resClass)) {
            // create the handler
            ResourceInfo rHandler =
                new ResourceInfo(resName, resClass, resComment, resHelpURL);
            resInfos.put(resClass, rHandler);
          }
        } else if(currentElem.getName().equalsIgnoreCase("REQUIRES")) {

          if(currentElem.getAttribute("GROUP") != null) {
            // TODO probably need more error checking here
            requiredPlugins
                .add(new Plugin.Maven(currentElem.getAttributeValue("GROUP"),
                    currentElem.getAttributeValue("ARTIFACT"),
                    currentElem.getAttributeValue("VERSION")));
          } else {
            URL url = null;
            String urlString = currentElem.getTextTrim();
            if(urlString.startsWith(relativePathMarker)) {
              url = new URL(getBaseURL(),
                  urlString.substring(relativePathMarker.length()));
            } else if(urlString.startsWith(gatehomePathMarker)) {
              throw new IOException("$gatehome$ variable no longer supported in REQUIRES element");
            } else if(urlString.startsWith(gatepluginsPathMarker)) {
              throw new IOException("$gateplugins$ variable no longer supported in REQUIRES element");
            } else {
              url = new URL(getBaseURL(), urlString);
            }

            requiredPlugins.add(new Plugin.Directory(url));

            Utils.logOnce(log, Level.WARN,
                "Dependencies on other plugins via URL is deprecated ("
                    + getName() + " plugin)");
          }
        } else {
          // this is some higher level element -> simulate recursion
          // we want Depth-first-search so we need to add at the beginning
          @SuppressWarnings("unchecked")
          List newJobsList =
              new ArrayList(currentElem.getChildren());
          newJobsList.addAll(jobsList);
          jobsList = newJobsList;
        }
      }

      // now process the jar files with SCAN="true", looking for any extra
      // CreoleResource annotated classes.
      for(String jarFile : jarsToScan) {
        URL jarUrl = new URL(getBaseURL(), jarFile);
        scanJar(jarUrl, resInfos);
      }

      // see whether any of the ResourceInfo objects are still incomplete
      // (don't have a name)
      List incompleteResInfos = new ArrayList();
      for(ResourceInfo ri : resInfos.values()) {
        if(ri.getResourceName() == null) {
          incompleteResInfos.add(ri);
        }
      }

      if(!incompleteResInfos.isEmpty()) {
        fillInResInfos(incompleteResInfos, allJars);
      }

      // if any of the resource infos still don't have a name, take it from
      // the class name.
      for(ResourceInfo ri : incompleteResInfos) {
        if(ri.getResourceName() == null) {
          ri.setResourceName(ri.getResourceClassName()
              .substring(ri.getResourceClassName().lastIndexOf('.') + 1));
        }
      }

      // finally, we have the complete list of ResourceInfos
      resourceInfoList.addAll(resInfos.values());
    } catch(IOException ioe) {
      valid = false;
      log.error("Problem while parsing plugin " + toString() + "!\n"
          + ioe.toString() + "\nPlugin not available!");
    } catch(JDOMException jde) {
      valid = false;
      log.error("Problem while parsing plugin " + toString() + "!\n"
          + jde.toString() + "\nPlugin not available!");
    } catch(Exception e) {
      valid = false;
      log.error("Problem while parsing plugin " + toString() + "!\n"
          + e.toString() + "\nPlugin not available!");
    }
  }

  protected void scanJar(URL jarUrl, Map resInfos)
      throws IOException {
    JarInputStream jarInput = new JarInputStream(jarUrl.openStream(), false);
    JarEntry entry = null;
    while((entry = jarInput.getNextJarEntry()) != null) {
      String entryName = entry.getName();
      if(entryName != null && entryName.endsWith(".class")) {
        final String className =
            entryName.substring(0, entryName.length() - 6).replace('/', '.');
        if(!resInfos.containsKey(className)) {
          ClassReader classReader = new ClassReader(jarInput);
          ResourceInfo resInfo = new ResourceInfo(null, className, null);
          ResourceInfoVisitor visitor = new ResourceInfoVisitor(resInfo);

          classReader.accept(visitor, ClassReader.SKIP_CODE
              | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
          if(visitor.isCreoleResource()) {
            resInfos.put(className, resInfo);
          }
        }
      }
    }

    jarInput.close();
  }

  protected void fillInResInfos(List incompleteResInfos,
      List allJars) throws IOException {
    // now create a temporary class loader with all the JARs (scanned or
    // not), so we can look up all the referenced classes in the normal
    // way and read their CreoleResource annotations (if any).
    URL[] jarUrls = new URL[allJars.size()];
    for(int i = 0; i < jarUrls.length; i++) {
      jarUrls[i] = new URL(getBaseURL(), allJars.get(i));
    }

    // TODO shouldn't we use a proper temp gate class loader which we
    // can then throw away?
    try (URLClassLoader tempClassLoader =
        new URLClassLoader(jarUrls, Gate.class.getClassLoader());) {
      for(ResourceInfo ri : incompleteResInfos) {
        String classFile =
            ri.getResourceClassName().replace('.', '/') + ".class";
        InputStream classStream =
            tempClassLoader.getResourceAsStream(classFile);
        if(classStream != null) {
          ClassReader classReader = new ClassReader(classStream);
          ClassVisitor visitor = new ResourceInfoVisitor(ri);
          classReader.accept(visitor, ClassReader.SKIP_CODE
              | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
          classStream.close();
        }
      }
    }
  }

  public static class Directory extends Plugin {

    public Directory(URL directoryURL) {
      baseURL = Gate.normaliseCreoleUrl(directoryURL);
    }

    @Override
    public Document getCreoleXML() throws Exception {
      SAXBuilder builder = new SAXBuilder(false);
      URL creoleFileURL = new URL(getBaseURL(), "creole.xml");
      return builder.build(creoleFileURL);
    }

    @Override
    public String getName() {
      if(name != null) return name;

      // url.getPath() works for jar URLs; url.toURI().getPath() doesn't
      // because jars aren't considered "hierarchical"
      name = getBaseURL().getPath();
      if(name.endsWith("/")) {
        name = name.substring(0, name.length() - 1);
      }
      int lastSlash = name.lastIndexOf("/");
      if(lastSlash != -1) {
        name = name.substring(lastSlash + 1);
      }
      try {
        // convert to (relative) URI and extract path. This will
        // decode any %20 escapes in the name.
        name = new URI(name).getPath();
      } catch(URISyntaxException ex) {
        // ignore, this should have been checked when adding the URL!
      }
      return name;
    }
  }

  public static class Maven extends Plugin
                            implements Serializable, TransferListener {

    private static final long serialVersionUID = -6944695755723023537L;

    private String group, artifact, version;

    private transient URL artifactURL, metadataArtifactURL;

    public Maven(String group, String artifact, String version) {
      this.group = group;
      this.artifact = artifact;
      this.version = version;
      
      // set the super class version as well at this point. It will get
      // overwritten later by the same value pulled from the creole.xml but this
      // ensures that when we check to see if the plugin is already known we
      // have a version string to use
      super.version = this.version;

      name = artifact;// group+":"+artifact+":"+version;
    }

    @Override
    public String getName() {
      if(name == null) {
        try {
          getMetadataXML();
        } catch(Exception e) {
          // ignore this for now
        }
        
        if (name == null) {
          name = artifact;
        }
      }

      return name;
    }

    @Override
    public URI getBaseURI() throws URISyntaxException {
      return new URI(
          "creole://" + group + ";" + artifact + ";" + version + "/");
    }

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

    @Override
    public boolean equals(Object obj) {

      if(this == obj) return true;
      if(obj == null) return false;

      // Usualy an equals method checks for class equality, but we want to allow
      // subclasses of the Maven plugin to be considered equal if they use the
      // same maven coordinates to locate the plugin, so we simply check that
      // the object being passed in can be assigned to Plugin.Maven 
      if(!(obj instanceof Plugin.Maven)) return false;
      
      Plugin.Maven other = (Plugin.Maven)obj;
      
      if(artifact == null) {
        if(other.artifact != null) return false;
      } else if(!artifact.equals(other.artifact)) return false;
      if(group == null) {
        if(other.group != null) return false;
      } else if(!group.equals(other.group)) return false;
      if(version == null) {
        if(other.version != null) return false;
      } else if(!version.equals(other.version)) return false;
      return true;
    }

    @Override
    public void copyResources(File dir) throws URISyntaxException, IOException {

      if(!hasResources())
        throw new UnsupportedOperationException(
            "this plugin doesn't have any resources you can copy as you would know had you called hasResources first :P");
      
      Path target = Paths.get(dir.toURI());
      try (FileSystem zipFs =
          FileSystems.newFileSystem(getArtifactURL().toURI(), new HashMap<>());) {

        Path pathInZip = zipFs.getPath("/resources");

        Files.walkFileTree(pathInZip, new SimpleFileVisitor() {
          @Override
          public FileVisitResult visitFile(Path filePath,
              BasicFileAttributes attrs) throws IOException {
            // Make sure that we conserve the hierachy of files and folders
            // inside the zip
            Path relativePathInZip = pathInZip.relativize(filePath);
            Path targetPath = target.resolve(relativePathInZip.toString());
            Files.createDirectories(targetPath.getParent());

            // And extract the file
            Files.copy(filePath, targetPath,
                StandardCopyOption.REPLACE_EXISTING);

            return FileVisitResult.CONTINUE;
          }
        });
      } catch(Exception e) {
        // TODO Auto-generated catch block
        throw new IOException(e);
      }
    }

    public void walkResources(FileVisitor visitor) throws URISyntaxException, IOException {
      try (FileSystem zipFs =
                   FileSystems.newFileSystem(artifactURL.toURI(), new HashMap<>())) {
        Path resourcesPath = zipFs.getPath("/resources");
        if(Files.isDirectory(resourcesPath)) {
          Files.walkFileTree(resourcesPath, visitor);
        }
      }
    }

    @Override
    public boolean hasResources() {
      try (FileSystem zipFs = FileSystems
          .newFileSystem(getMetadataArtifactURL().toURI(), new HashMap<>());) {
        Path pathInZip = zipFs.getPath("/resources");
        return Files.isDirectory(pathInZip);
      } catch(Exception e) {
        return false;
      }
    }

    protected URL getMetadataArtifactURL() throws Exception {
      if(metadataArtifactURL != null) return metadataArtifactURL;

      if(artifactURL != null) return artifactURL;

      getMetadataXML();

      return metadataArtifactURL;
    }

    protected URL getArtifactURL() throws Exception {
      if(artifactURL == null) {
        getCreoleXML();
      }

      return artifactURL;
    }

    @Override
    public Document getMetadataXML() throws Exception {
      Artifact artifactObj =
          new DefaultArtifact(group, artifact, "creole", "jar", version);

      List repos = getRepositoryList();

      ArtifactRequest artifactRequest =
          new ArtifactRequest(artifactObj, repos, null);

      RepositorySystem repoSystem = getRepositorySystem();

      WorkspaceReader workspace = null;

      List persistenceURLStack =
          PersistenceManager.currentPersistenceURLStack();

      List workspaces = new ArrayList();

      if(persistenceURLStack != null && !persistenceURLStack.isEmpty()) {
        for(ResourceReference rr : persistenceURLStack) {
          try {
            File file = gate.util.Files.fileFromURL(rr.toURL());
            File cache = new File(file.getParentFile(), "maven-cache.gate");
            if(cache.exists() && cache.isDirectory()) {
              workspaces.add(cache);
            }
          } catch(IllegalArgumentException e) {
            // ignore this for now
          }
        }
      }
      workspaces.addAll(gate.util.maven.Utils.getExtraCacheDirectories());

      if(!workspaces.isEmpty()) {
        workspace = new SimpleMavenCache(
            workspaces.toArray(new File[workspaces.size()]));
      }

      RepositorySystemSession repoSession =
          getRepositorySession(repoSystem, workspace);

      try {
        ArtifactResult artifactResult =
            repoSystem.resolveArtifact(repoSession, artifactRequest);

        metadataArtifactURL = new URL("jar:"
            + artifactResult.getArtifact().getFile().toURI().toURL() + "!/");

        // check it has a creole.xml at the root
        URL expandedCreoleUrl =
            new URL(metadataArtifactURL, "META-INF/gate/creole.xml");

        try (InputStream creoleStream = expandedCreoleUrl.openStream()) {
          // no op just to check the file exists
        } catch(IOException ioe) {
          throw new IOException(expandedCreoleUrl.toExternalForm()
              + " does not exist so this artifact is not a GATE plugin");
        }

        // get the creole.xml out of the jar and add jar elements for this
        // jar (marked for scanning) and the dependencies
        SAXBuilder builder = new SAXBuilder(false);
        Document creoleDoc = builder.build(expandedCreoleUrl);
        
        if(creoleDoc.getRootElement().getAttributeValue("NAME") == null
            || creoleDoc.getRootElement()
                .getAttributeValue("VERSION") == null) {
          // if the root element doesn't specifiy a name and version then either
          // this is an old plugin from early in 8.5 or a file produced in
          // some other way than DumpCreoleToXML. Either way it's not valid so
          // fall back to using the full creole.xml expansion in getCreoleXML()
          return getCreoleXML();
        }
        
        return creoleDoc;

      } catch(IOException | ArtifactResolutionException e) {
        e.printStackTrace();
        return getCreoleXML();
      }
    }

    @Override
    public Document getCreoleXML() throws Exception {
      Artifact artifactObj =
          new DefaultArtifact(group, artifact, "jar", version);

      Dependency dependency = new Dependency(artifactObj, "runtime");

      List repos = getRepositoryList();

      ArtifactRequest artifactRequest =
          new ArtifactRequest(artifactObj, repos, null);

      RepositorySystem repoSystem = getRepositorySystem();

      WorkspaceReader workspace = null;

      List persistenceURLStack =
          PersistenceManager.currentPersistenceURLStack();

      List workspaces = new ArrayList();

      if(persistenceURLStack != null && !persistenceURLStack.isEmpty()) {
        for(ResourceReference rr : persistenceURLStack) {
          try {
            File file = gate.util.Files.fileFromURL(rr.toURL());
            File cache = new File(file.getParentFile(), "maven-cache.gate");
            if(cache.exists() && cache.isDirectory()) {
              workspaces.add(cache);
            }
          } catch(IllegalArgumentException e) {
            // ignore this for now
          }
        }
      }
      workspaces.addAll(gate.util.maven.Utils.getExtraCacheDirectories());

      if(!workspaces.isEmpty()) {
        workspace = new SimpleMavenCache(
            workspaces.toArray(new File[workspaces.size()]));
      }

      DefaultRepositorySystemSession repoSession =
          getRepositorySession(repoSystem, workspace);
      repoSession.setTransferListener(this);

      ArtifactResult artifactResult =
          repoSystem.resolveArtifact(repoSession, artifactRequest);

      artifactURL = new URL("jar:"
          + artifactResult.getArtifact().getFile().toURI().toURL() + "!/");

      metadataArtifactURL = artifactURL;

      baseURL = artifactURL;

      // check it has a creole.xml at the root
      URL directoryXmlFileUrl = new URL(artifactURL, "creole.xml");

      InputStream creoleStream = null;

      try {
        creoleStream = directoryXmlFileUrl.openStream();
      } catch(IOException ioe) {
        throw new IOException(directoryXmlFileUrl.toExternalForm()
            + " does not exist so this artifact is not a GATE plugin");
      }

      CollectRequest collectRequest = new CollectRequest(dependency, repos);

      DependencyNode node =
          repoSystem.collectDependencies(repoSession, collectRequest).getRoot();

      DependencyRequest dependencyRequest = new DependencyRequest();
      dependencyRequest.setRoot(node);

      DependencyResult result =
          repoSystem.resolveDependencies(repoSession, dependencyRequest);

      // get the creole.xml out of the jar and add jar elements for this
      // jar (marked for scanning) and the dependencies
      SAXBuilder builder = new SAXBuilder(false);
      Document jdomDoc =
          builder.build(creoleStream, getBaseURL().toExternalForm());

      Element creoleRoot = jdomDoc.getRootElement();

      for(ArtifactResult ar : result.getArtifactResults()) {

        Element jarElement = new Element("JAR");
        jarElement.setText(
            ar.getArtifact().getFile().toURI().toURL().toExternalForm());

        if(ar.getArtifact().equals(artifactResult.getArtifact())) {
          jarElement.setAttribute("SCAN", "true");
        }

        creoleRoot.addContent(jarElement);
      }

      artifactObj = new SubArtifact(artifactObj, "", "pom");

      artifactRequest.setArtifact(artifactObj);
      artifactResult = repoSystem.resolveArtifact(repoSession, artifactRequest);

      ModelBuildingRequest req = new DefaultModelBuildingRequest();
      req.setProcessPlugins(false);
      req.setPomFile(artifactResult.getArtifact().getFile());
      req.setSystemProperties(System.getProperties());
      req.setModelResolver(new SimpleModelResolver(repoSystem, repoSession,
          repos));
      req.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);

      ModelBuilder modelBuilder =
          new DefaultModelBuilderFactory().newInstance();
      Model model = modelBuilder.build(req).getEffectiveModel();

      if(model.getDescription() != null
          && model.getDescription().trim().equals(""))
        jdomDoc.getRootElement().setAttribute("DESCRIPTION", model.getDescription());
      
      jdomDoc.getRootElement().setAttribute("VERSION", version);
      
      if(model.getName() != null && !model.getName().trim().equals(""))
        jdomDoc.getRootElement().setAttribute("NAME",model.getName());
      else
        jdomDoc.getRootElement().setAttribute("NAME",model.getArtifactId());
      
      String creoleMinGate =
          jdomDoc.getRootElement().getAttributeValue("GATE-MIN");
      if(creoleMinGate == null) {
        for(org.apache.maven.model.Dependency effectiveDependency : model
            .getDependencies()) {
          if(effectiveDependency.getGroupId().equals("uk.ac.gate")
              && effectiveDependency.getArtifactId().equals("gate-core")) {
            jdomDoc.getRootElement().setAttribute("GATE-MIN",
                effectiveDependency.getVersion());
            break;
          }
        }
      }

      /*
       * System.out.println(model.getOrganization().getName()); for
       * (org.apache.maven.model.Repository r : model.getRepositories()) {
       * System.out.println(r.getName()); } for (License l :
       * model.getLicenses()) { System.out.println(l.getName()); }
       */

      return jdomDoc;
    }

    // accessors for the Maven coordinates

    /**
     * Get the Maven group ID of this plugin.
     */
    public String getGroup() {
      return group;
    }

    /**
     * Get the Maven artifact ID of this plugin.
     */
    public String getArtifact() {
      return artifact;
    }

    @Override
    public void transferInitiated(TransferEvent event)
        throws TransferCancelledException {
      // ignore this one
    }

    @Override
    public void transferStarted(TransferEvent event)
        throws TransferCancelledException {
      fireDownloadStarted(event.getResource().getFile().getName());

    }

    @Override
    public void transferProgressed(TransferEvent event)
        throws TransferCancelledException {
      fireDownloadProgressed(event.getResource().getFile().getName(),
          event.getResource().getContentLength(), event.getTransferredBytes());

    }

    @Override
    public void transferCorrupted(TransferEvent event)
        throws TransferCancelledException {
      fireDownloadFailed(event.getResource().getFile().getName(),
          event.getException());
    }

    @Override
    public void transferSucceeded(TransferEvent event) {
      fireDownloadSucceeded(event.getResource().getFile().getName());
    }

    @Override
    public void transferFailed(TransferEvent event) {
      fireDownloadFailed(event.getResource().getFile().getName(),
          event.getException());
    }
  }

  /**
   * ClassVisitor that uses information from a CreoleResource annotation on the
   * visited class (if such exists) to fill in the name and comment in the
   * corresponding ResourceInfo.
   */
  protected static class ResourceInfoVisitor extends EmptyVisitor {
    private ResourceInfo resInfo;

    private boolean foundCreoleResource = false;

    private boolean isAbstract = false;

    public ResourceInfoVisitor(ResourceInfo resInfo) {
      this.resInfo = resInfo;
    }

    public boolean isCreoleResource() {
      return foundCreoleResource && !isAbstract;
    }

    /**
     * Type descriptor for the CreoleResource annotation type.
     */
    private static final String CREOLE_RESOURCE_DESC =
        Type.getDescriptor(CreoleResource.class);

    /**
     * Visit the class header, checking whether this is an abstract class or
     * interface and setting the isAbstract flag appropriately.
     */
    @Override
    public void visit(int version, int access, String name, String signature,
        String superName, String[] interfaces) {
      isAbstract =
          ((access & (Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)) != 0);
    }

    /**
     * Visit an annotation on the class.
     */
    @Override
    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
      // we've found a CreoleResource annotation on this class
      if(desc.equals(CREOLE_RESOURCE_DESC)) {
        foundCreoleResource = true;
        return new AnnotationVisitor(Opcodes.ASM5) {
          @Override
          public void visit(String name, Object value) {
            if(name.equals("name") && resInfo.getResourceName() == null) {
              resInfo.setResourceName((String)value);
            } else if(name.equals("comment")
                && resInfo.getResourceComment() == null) {
              resInfo.setResourceComment((String)value);
            } else if (name.equals("helpURL")
                && resInfo.getHelpURL() == null) {
              resInfo.setHelpURL((String)value);
            }
          }

          @Override
          public AnnotationVisitor visitAnnotation(String name, String desc) {
            // don't want to recurse into AutoInstance annotations
            return this;
          }
        };
      } else {
        return super.visitAnnotation(desc, visible);
      }
    }
  }

  public static class Component extends Plugin {

    private Class resourceClass;

    public Component(Class resourceClass)
        throws MalformedURLException {
      this.resourceClass = resourceClass;
      baseURL = (new URL(
          resourceClass.getResource("/gate/creole/CreoleRegisterImpl.class"),
          "."));
    }

    public Component(Class resourceClass, URL baseURL) throws MalformedURLException {
      this.resourceClass = resourceClass;
      this.baseURL = baseURL;
    }

    public String getName() {
      return resourceClass.getName();
    }

    @Override
    public Document getCreoleXML() throws Exception, JDOMException {
      Document doc = new Document();
      Element element;
      doc.addContent(element = new Element("CREOLE-DIRECTORY"));
      element.addContent(element = new Element("CREOLE"));
      element.addContent(element = new Element("RESOURCE"));
      Element classElement = new Element("CLASS");
      classElement.setText(resourceClass.getName());
      element.addContent(classElement);

      return doc;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy