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

com.redhat.ceylon.compiler.java.runtime.Main Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.compiler.java.runtime;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Indexer;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.ArtifactOverrides;
import com.redhat.ceylon.cmr.api.DependencyOverride;
import com.redhat.ceylon.cmr.api.DependencyResolver;
import com.redhat.ceylon.cmr.api.ModuleDependencyInfo;
import com.redhat.ceylon.cmr.api.ModuleInfo;
import com.redhat.ceylon.cmr.api.Overrides;
import com.redhat.ceylon.cmr.api.PathFilterParser;
import com.redhat.ceylon.cmr.impl.AbstractArtifactResult;
import com.redhat.ceylon.cmr.impl.Configuration;
import com.redhat.ceylon.cmr.impl.OSGiDependencyResolver;
import com.redhat.ceylon.cmr.impl.PropertiesDependencyResolver;
import com.redhat.ceylon.cmr.impl.XmlDependencyResolver;
import com.redhat.ceylon.common.Java9ModuleUtil;
import com.redhat.ceylon.common.Versions;
import com.redhat.ceylon.common.tools.ModuleSpec;
import com.redhat.ceylon.common.tools.ModuleSpec.Option;
import com.redhat.ceylon.compiler.java.runtime.metamodel.Metamodel;
import com.redhat.ceylon.compiler.java.runtime.model.OverridesRuntimeResolver;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.cmr.ArtifactResultType;
import com.redhat.ceylon.model.cmr.JDKUtils;
import com.redhat.ceylon.model.cmr.JDKUtils.JDK;
import com.redhat.ceylon.model.cmr.PathFilter;
import com.redhat.ceylon.model.cmr.RepositoryException;
import com.redhat.ceylon.model.loader.Java9ModuleReader;
import com.redhat.ceylon.model.loader.Java9ModuleReader.Java9Module;
import com.redhat.ceylon.model.loader.NamingBase;
import com.redhat.ceylon.model.loader.OsgiUtil;

/**
 * 

* Main entry point when you want to initialise the Ceylon system when you are not running * JBoss modules and have all the relevant Ceylon libraries in your JVM classpath. This * will allow you to initialize the metamodel with {@link Main#setupMetamodel()}, which you * only need to do once before you execute code from a Ceylon module. Once initialized, the * metamodel will remain useable until you reset it, from every thread and user of the system * ClassLoader. You can repeatedly call {@link Main#setupMetamodel()} to add new Ceylon modules * to the metamodel. *

*

* If you need to reset the metamodel you can call {@link Main#resetMetamodel()}, but again beware * that the metamodel is static to the system ClassLoader, so it may be shared by other threads, * and any Ceylon code that continues to run after you reset it will likely fail at some point. *

*

* You can also directly run Ceylon programs by using {@link Main#runModule(String, String, Class, String...)} * or {@link Main#runModule(String, String, String, String...)}, which will first initialize the * metamodel, then run the given Java Class’s main method (which sets up the process.arguments * and executes the given toplevel Ceylon class or method. *

* *

* WARNING: this API is not supported and will be superseded by an official API later. *

* * @author Stéphane Épardaud */ public class Main { private boolean allowMissingModules; private boolean allowMissingSystem; private String overrides; private ClassPath classPath; private Set visited; private ClassLoader moduleClassLoader; public static Main instance() { return new Main(); } private Main() { } /** * When enabled no exceptions will be thrown for not encountering * proper module dependency information for the module to be loaded * or any of its dependencies. The system modules still need to be * present and have proper dependency information. * @param allowMissingModules If true no exceptions will be thrown * for missing module dependency information * @return This object for chaining */ public Main allowMissingModules(boolean allowMissingModules) { this.allowMissingModules = allowMissingModules; return this; } /** * Sets the overrides file to use for module overrides * @param overrides a file path to the module overrides * @return This object for chaining */ public Main overrides(String overrides) { this.overrides = overrides; return this; } /** * When enabled no exceptions will be thrown for not encountering * proper module dependency information for the system modules or * any of their dependencies. * @param allowMissingModules If true no exceptions will be thrown * for missing module dependency information * @return This object for chaining */ public Main allowMissingSystem(boolean allowMissingSystem) { this.allowMissingSystem = allowMissingSystem; return this; } static class ClassPath { private static final String METAINF_JBOSSMODULES = "META-INF/jbossmodules/"; private static final String METAINF_MAVEN = "META-INF/maven/"; private static final String MODULE_PROPERTIES = "module.properties"; private static final String MODULE_XML = "module.xml"; private static final String POM_XML = "pom.xml"; private static final String JAVA9_MODULE = "module-info.class"; @SuppressWarnings("serial") public class ModuleNotFoundException extends Exception { public ModuleNotFoundException(String string) { super(string); } } private enum Type { CEYLON, JBOSS_MODULES, MAVEN, OSGi, UNKNOWN, JDK, JAVA9; } static class Dependency extends AbstractArtifactResult { public final boolean optional, shared; public Dependency(String name, String version, boolean optional, boolean shared) { super(null, name, version); this.optional = optional; this.shared = shared; } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("Import{ name = ").append(name()); b.append(", version = ").append(version()); b.append(", optional = ").append(optional); b.append(", shared = ").append(shared); b.append(" }"); return b.toString(); } @Override public boolean equals(Object obj) { if(obj == null) return false; if(obj == this) return true; if(obj instanceof Dependency == false) return false; Dependency other = (Dependency) obj; return Objects.equals(name(), other.name()) && Objects.equals(version(), other.version()) && optional == other.optional && shared == other.shared; } @Override public int hashCode() { int ret = 17; ret = (ret * 23) + (name() != null ? name().hashCode() : 0); ret = (ret * 23) + (version() != null ? version().hashCode() : 0); ret = (ret * 23) + (optional ? 1 : 0); ret = (ret * 23) + (shared ? 1 : 0); return ret; } @Override public ArtifactResultType type() { throw new UnsupportedOperationException(); } @Override public List dependencies() throws RepositoryException { throw new UnsupportedOperationException(); } @Override public String repositoryDisplayString() { throw new UnsupportedOperationException(); } @Override protected File artifactInternal() { throw new UnsupportedOperationException(); } } static class Module extends AbstractArtifactResult { public final File jar; public final Type type; public final List dependencies = new LinkedList(); public Module(String name, String version, Type type, File jar) { super(null, name, version); this.type = type; this.jar = jar; } public void addDependency(String name, String version, boolean optional, boolean shared) { dependencies.add(new Dependency(name, version, optional, shared)); } @Override public int hashCode() { int ret = 31; ret = 37 * ret + name().hashCode(); ret = 37 * ret + (version() != null ? version().hashCode() : 0); return ret; } @Override public boolean equals(Object obj) { if(obj == null) return false; if(obj == this) return true; if(obj instanceof Module == false) return false; Module other = (Module) obj; return name().equals(other.name()) && Objects.equals(version(), other.version()); } @Override public String toString() { StringBuilder b = new StringBuilder(); b.append("Module{ name = ").append(name()); b.append(", version = ").append(version()); b.append(", jar = ").append(jar); b.append(", type = ").append(type); b.append(", dependencies = ["); boolean once = true; for(Dependency dep : dependencies){ if(once) once = false; else b.append(", "); b.append(dep); } b.append(" ] }"); return b.toString(); } @Override public ArtifactResultType type() { switch(type){ case CEYLON: case JBOSS_MODULES: case OSGi: case JDK: return ArtifactResultType.CEYLON; case MAVEN: return ArtifactResultType.MAVEN; case UNKNOWN: default: return ArtifactResultType.OTHER; } } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public List dependencies() throws RepositoryException { return (List)dependencies; } @Override public String repositoryDisplayString() { return name()+"/"+version(); } @Override protected File artifactInternal() { return jar; } public void setFilter(PathFilter filter) { setFilterInternal(filter); } } private List potentialJars = new LinkedList(); private Map modules = new HashMap(); private Overrides overrides; private static DependencyResolver MavenResolver = getResolver(Configuration.MAVEN_RESOLVER_CLASS); private static final Module NO_MODULE = new Module("$$$", "$$$", Type.UNKNOWN, null); ClassPath(Overrides overrides){ String classPath = System.getProperty("java.class.path"); String[] classPathEntries = classPath.split(File.pathSeparator); for(String classPathEntry : classPathEntries){ File entry = new File(classPathEntry); if(entry.isFile()){ potentialJars.add(entry); } } if(JDKUtils.jdk.providesVersion(JDK.JDK9.version)){ String modulePath = System.getProperty("jdk.module.path"); if(modulePath != null){ String[] modulePathEntries = modulePath.split(File.pathSeparator); for(String moduleFolder : modulePathEntries){ File folder = new File(moduleFolder); if(folder.isDirectory()){ scanFolderForJars(folder); } } } } this.overrides = overrides; initJars(); } private void scanFolderForJars(File folder) { for(File file : folder.listFiles()){ if(file.isFile() && (file.getName().endsWith(".car") || file.getName().endsWith(".jar") || file.getName().endsWith(".zip"))){ potentialJars.add(file); } } } // for tests ClassPath(List potentialJars){ this.potentialJars = potentialJars; initJars(); } private List findEntries(ZipFile zipFile, String startFolder, String entryName) { List result = new LinkedList(); Enumeration entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String name = entry.getName(); if (name.startsWith(startFolder) && name.endsWith(entryName)) { result.add(entry); } } return result; } private ModuleSpec moduleFromEntry(ZipEntry entry) { String fullName = entry.getName(); if (fullName.startsWith(METAINF_JBOSSMODULES)) { fullName = fullName.substring(METAINF_JBOSSMODULES.length()); } if (fullName.endsWith(MODULE_PROPERTIES)) { fullName = fullName.substring(0, fullName.length() - MODULE_PROPERTIES.length() - 1); } else if (fullName.endsWith(MODULE_XML)) { fullName = fullName.substring(0, fullName.length() - MODULE_XML.length() - 1); } int p = fullName.lastIndexOf('/'); if (p > 0) { String name = fullName.substring(0, p); String version = fullName.substring(p + 1); if (!name.isEmpty() && !version.isEmpty()) { name = name.replace('/', '.'); return new ModuleSpec(name, version); } } return null; } private static DependencyResolver getResolver(String className) { try { ClassLoader cl = Configuration.class.getClassLoader(); return (DependencyResolver) cl.loadClass(className).newInstance(); } catch (Throwable t) { return null; } } public Module loadModule(String name, String version) throws ModuleNotFoundException{ return loadModule(name, version, false); } public Module loadModule(String name, String version, boolean allowMissingModules) throws ModuleNotFoundException{ String key = name + "/" + version; Module module = modules.get(key); if(module != null) return module; if(JDKUtils.jdk.providesVersion(JDK.JDK9.version)){ // unalias jdk7-8 module names if we're running on jdk9+ name = JDKUtils.getJava9ModuleName(name, version); } if(JDKUtils.isJDKModule(name) || JDKUtils.isOracleJDKModule(name)){ module = new Module(name, JDKUtils.jdk.version, Type.JDK, null); modules.put(key, module); return module; } module = searchJars(name, version); if (module != null) { return module; } else { if(allowMissingModules){ return new Module(name, version, Type.UNKNOWN, null); }else{ throw new ModuleNotFoundException("Module "+key+" not found"); } } } // Pre-loads as much modules as possible by going over all potential jar/car files // in the class path and trying to determine which modules they contain private void initJars() { searchJars(null, null); } // Goes over all the potential jar/car files in the class path and if a name and // version were given tries to determine if any of them contain that module. // If name and version are both `null` it will try to determine the module // information from the jar/car file itself. private Module searchJars(String name, String version) { if(overrides != null){ ArtifactContext ctx = new ArtifactContext(name, version); ArtifactContext replacement = overrides.replace(ctx); if(replacement != null){ name = replacement.getName(); version = replacement.getVersion(); ctx = replacement; } if(overrides.isVersionOverridden(ctx)){ version = overrides.getVersionOverride(ctx); } } Module module; Iterator iterator = potentialJars.iterator(); while(iterator.hasNext()){ File file = iterator.next(); try { if (name == null && version == null) { module = initJar(file); } else { module = loadJar(file, name, version); } } catch (IOException e) { // faulty jar iterator.remove(); e.printStackTrace(); System.err.println("Non-zip jar file in classpath: "+file+". Skipping it next time."); continue; } if(module != null){ if (module != NO_MODULE) { String key = module.name() + "/" + module.version(); modules.put(key, module); } iterator.remove(); if (name != null || version != null) { return module; } } } return null; } private Module initJar(File file) throws IOException { ZipFile zipFile = new ZipFile(file); try{ // Try Ceylon module first List moduleDescriptors = findEntries(zipFile, "", NamingBase.MODULE_DESCRIPTOR_CLASS_NAME+".class"); if(moduleDescriptors.size() == 1) { try { return loadCeylonModuleCar(file, zipFile, moduleDescriptors.get(0), null, null); } catch (IOException ex) { // Ignore } } // Try JBoss modules next List moduleXmls = findEntries(zipFile, METAINF_JBOSSMODULES, MODULE_XML); if(moduleXmls.size() == 1) { ModuleSpec mod = moduleFromEntry(moduleXmls.get(0)); if (mod != null) { return loadJBossModuleXmlJar(file, zipFile, moduleXmls.get(0), mod.getName(), mod.getVersion()); } } List moduleProperties = findEntries(zipFile, METAINF_JBOSSMODULES, MODULE_PROPERTIES); if(moduleProperties.size() == 1) { ModuleSpec mod = moduleFromEntry(moduleProperties.get(0)); if (mod != null) { return loadJBossModulePropertiesJar(file, zipFile, moduleProperties.get(0), mod.getName(), mod.getVersion()); } } // Java 9 module List java9Module = findEntries(zipFile, "", JAVA9_MODULE); if(java9Module.size() == 1) { return loadJava9ModuleJar(file, zipFile, java9Module.get(0), null, null); } // try Maven List mavenDescriptors = findEntries(zipFile, METAINF_MAVEN, POM_XML); // TODO: we should try to retrieve the module information (name/version) from the pom.xml entry // last OSGi ZipEntry osgiProperties = zipFile.getEntry(JarFile.MANIFEST_NAME); if(osgiProperties != null){ Module module = loadOsgiJar(file, zipFile, osgiProperties, null, null); // it's possible we have a MANIFEST but not for the module we're looking for if(module != null) return module; } if (moduleDescriptors.isEmpty() && moduleXmls.isEmpty() && moduleProperties.isEmpty() && mavenDescriptors.isEmpty() && osgiProperties == null) { // There's nothing we can retrieve from this jar // let's return a dummy module so the jar will // get removed from the list of potentials at least return NO_MODULE; } // not found return null; }finally{ zipFile.close(); } } private Module loadJar(File file, String name, String version) throws IOException { ZipFile zipFile = new ZipFile(file); try{ // Modules that have a : MUST be Maven modules int mavenSeparator = name.indexOf(":"); if(mavenSeparator != -1){ String groupId = name.substring(0, mavenSeparator); String artifactId = name.substring(mavenSeparator+1); String descriptorPath = String.format("META-INF/maven/%s/%s/pom.xml", groupId, artifactId); ZipEntry mavenDescriptor = zipFile.getEntry(descriptorPath); if(mavenDescriptor != null){ return loadMavenJar(file, zipFile, mavenDescriptor, name, version); } // If the jar comes straight out of a Maven repo/cache we may have an external pom file String externalDescriptorPath = String.format("%s-%s.pom", artifactId, version); File externalDescriptor = new File(file.getParentFile(), externalDescriptorPath); if(externalDescriptor.exists()){ return loadMavenJar(file, externalDescriptor, name, version); } } // Try Ceylon module first String ceylonPath = name.replace('.', '/'); ZipEntry moduleDescriptor = zipFile.getEntry(ceylonPath+"/"+NamingBase.MODULE_DESCRIPTOR_CLASS_NAME+".class"); if(moduleDescriptor != null) return loadCeylonModuleCar(file, zipFile, moduleDescriptor, name, version); // JBoss modules next ZipEntry moduleXml = zipFile.getEntry("META-INF/jbossmodules/"+ceylonPath+"/"+version+"/module.xml"); if(moduleXml != null) return loadJBossModuleXmlJar(file, zipFile, moduleXml, name, version); ZipEntry moduleProperties = zipFile.getEntry("META-INF/jbossmodules/"+ceylonPath+"/"+version+"/module.properties"); if(moduleProperties != null) return loadJBossModulePropertiesJar(file, zipFile, moduleProperties, name, version); // try other combinations for Maven if(MavenResolver != null){ // the case with : has already been taken care of first int lastDot = name.lastIndexOf('.'); while(lastDot != -1){ String groupId = name.substring(0, lastDot); String artifactId = name.substring(lastDot+1); String descriptorPath = String.format("META-INF/maven/%s/%s/pom.xml", groupId, artifactId); ZipEntry mavenDescriptor = zipFile.getEntry(descriptorPath); if(mavenDescriptor != null){ return loadMavenJar(file, zipFile, mavenDescriptor, name, version); } lastDot = name.lastIndexOf('.', lastDot - 1); } } // Java 9 module List java9Module = findEntries(zipFile, "", JAVA9_MODULE); if(java9Module.size() == 1) { return loadJava9ModuleJar(file, zipFile, java9Module.get(0), name, version); } // last OSGi ZipEntry osgiProperties = zipFile.getEntry(JarFile.MANIFEST_NAME); if(osgiProperties != null){ Module module = loadOsgiJar(file, zipFile, osgiProperties, name, version); // it's possible we have a MANIFEST but not for the module we're looking for if(module != null) return module; } // not found return null; }finally{ zipFile.close(); } } private Module loadCeylonModuleCar(File file, ZipFile zipFile, ZipEntry moduleDescriptor, String name, String version) throws IOException { InputStream inputStream = zipFile.getInputStream(moduleDescriptor); try{ Indexer indexer = new Indexer(); ClassInfo classInfo = indexer.index(inputStream); if(classInfo == null) throw new IOException("Failed to read class info"); Map> annotations = classInfo.annotations(); DotName moduleAnnotationName = DotName.createSimple(com.redhat.ceylon.compiler.java.metadata.Module.class.getName()); List moduleAnnotations = annotations.get(moduleAnnotationName); if(moduleAnnotations == null || moduleAnnotations.size() != 1) throw new IOException("Missing module annotation: "+annotations); AnnotationInstance moduleAnnotation = moduleAnnotations.get(0); AnnotationValue moduleName = moduleAnnotation.value("name"); AnnotationValue moduleVersion = moduleAnnotation.value("version"); if(moduleName == null || moduleVersion == null) throw new IOException("Invalid module annotation"); if(name != null && !moduleName.asString().equals(name)) throw new IOException("Module name does not match module descriptor"); if(version != null && !moduleVersion.asString().equals(version)) throw new IOException("Module version does not match module descriptor"); name = moduleName.asString(); version = moduleVersion.asString(); Module module = new Module(name, version, Type.CEYLON, file); AnnotationValue moduleDependencies = moduleAnnotation.value("dependencies"); ArtifactOverrides ao = null; if(overrides != null){ ao = overrides.getArtifactOverrides(new ArtifactContext(name, version)); } if(moduleDependencies != null){ for(AnnotationInstance dependency : moduleDependencies.asNestedArray()){ AnnotationValue importName = dependency.value("name"); AnnotationValue importVersion = dependency.value("version"); AnnotationValue importOptional = dependency.value("optional"); AnnotationValue importExport = dependency.value("export"); if(importName == null || importVersion == null) throw new IOException("Invalid module import"); String depName = importName.asString(); String depVersion = importVersion.asString(); boolean export = importExport != null ? importExport.asBoolean() : false; boolean optional = importOptional != null ? importOptional.asBoolean() : false; if(overrides != null){ ArtifactContext depCtx = new ArtifactContext(depName, depVersion); ArtifactContext replacement = overrides.replace(depCtx); if(replacement != null){ depCtx = replacement; depName = replacement.getName(); depVersion = replacement.getVersion(); } if(overrides.isVersionOverridden(depCtx)){ depVersion = overrides.getVersionOverride(depCtx); depCtx.setVersion(depVersion); } if(overrides.isRemoved(depCtx)) continue; if(ao != null){ if(ao.isAddedOrUpdated(depCtx)) continue; if(ao.isOptionalOverridden(depCtx)) optional = ao.isOptional(depCtx); if(ao.isShareOverridden(depCtx)) export = ao.isShared(depCtx); } } module.addDependency(depName, depVersion, optional, export); } if(ao != null){ for(DependencyOverride dep : ao.getAdd()){ module.addDependency(dep.getArtifactContext().getName(), dep.getArtifactContext().getVersion(), dep.isOptional(), dep.isShared()); } } } return module; }finally{ inputStream.close(); } } private Module loadJBossModulePropertiesJar(File file, ZipFile zipFile, ZipEntry moduleProperties, String name, String version) throws IOException { return loadJBossModuleJar(file, zipFile, moduleProperties, PropertiesDependencyResolver.INSTANCE, name, version); } private Module loadJBossModuleJar(File file, ZipFile zipFile, ZipEntry moduleDescriptor, DependencyResolver dependencyResolver, String name, String version) throws IOException { return loadFromResolver(file, zipFile, moduleDescriptor, dependencyResolver, name, version, Type.JBOSS_MODULES); } private Module loadFromResolver(File file, ZipFile zipFile, ZipEntry moduleDescriptor, DependencyResolver dependencyResolver, String name, String version, Type moduleType) throws IOException { InputStream inputStream = zipFile.getInputStream(moduleDescriptor); try{ return loadFromResolver(file, inputStream, dependencyResolver, name, version, moduleType); }finally{ inputStream.close(); } } private Module loadFromResolver(File file, File moduleDescriptor, DependencyResolver dependencyResolver, String name, String version, Type moduleType) throws IOException { InputStream inputStream = new FileInputStream(moduleDescriptor); try{ return loadFromResolver(file, inputStream, dependencyResolver, name, version, moduleType); }finally{ inputStream.close(); } } private Module loadFromResolver(File file, InputStream inputStream, DependencyResolver dependencyResolver, String name, String version, Type moduleType) throws IOException { ModuleInfo moduleDependencies = dependencyResolver.resolveFromInputStream(inputStream, name, version, overrides); if (moduleDependencies != null) { System.err.println("Load from resolver: "+name+"/"+version); Module module = new Module(name, version, moduleType, file); for(ModuleDependencyInfo dep : moduleDependencies.getDependencies()){ module.addDependency(dep.getName(), dep.getVersion(), dep.isOptional(), dep.isExport()); } if(moduleDependencies.getFilter() != null) module.setFilter(PathFilterParser.parse(moduleDependencies.getFilter())); return module; } return null; } private Module loadJBossModuleXmlJar(File file, ZipFile zipFile, ZipEntry moduleXml, String name, String version) throws IOException { return loadJBossModuleJar(file, zipFile, moduleXml, XmlDependencyResolver.INSTANCE, name, version); } private Module loadMavenJar(File file, ZipFile zipFile, ZipEntry moduleDescriptor, String name, String version) throws IOException { return loadFromResolver(file, zipFile, moduleDescriptor, MavenResolver, name, version, Type.MAVEN); } private Module loadMavenJar(File file, File moduleDescriptor, String name, String version) throws IOException { return loadFromResolver(file, moduleDescriptor, MavenResolver, name, version, Type.MAVEN); } private Module loadDefaultJar(File file) throws IOException { return new Module("default", null, Type.CEYLON, file); } private Module loadJava9ModuleJar(File file, ZipFile zipFile, ZipEntry moduleDescriptor, String name, String version) throws IOException { Java9Module java9Module = Java9ModuleReader.getJava9Module(zipFile, moduleDescriptor); if(java9Module != null) return new Module(java9Module.name, java9Module.version, Type.JAVA9, file); // or throw? return null; } private Module loadOsgiJar(File file, ZipFile zipFile, ZipEntry moduleDescriptor, String name, String version) throws IOException { // first verify that it is indeed for the module we're looking for InputStream inputStream = zipFile.getInputStream(moduleDescriptor); try{ Manifest manifest = new Manifest(inputStream); if (OsgiUtil.DefaultModuleManifest.isDefaultModule(manifest)) { return loadDefaultJar(file); } Attributes attributes = manifest.getMainAttributes(); String bundleName = attributes.getValue(OsgiUtil.OsgiManifest.Bundle_SymbolicName); String bundleVersion = attributes.getValue(OsgiUtil.OsgiManifest.Bundle_Version); if (name != null && version != null) { if(!Objects.equals(name, bundleName)|| !Objects.equals(version, bundleVersion)) return null; } else { name = bundleName; version = bundleVersion; } }finally{ inputStream.close(); } return loadFromResolver(file, zipFile, moduleDescriptor, OSGiDependencyResolver.INSTANCE, name, version, Type.OSGi); } } /** * Sets up the metamodel for the specified module and execute the main method on the * specified Java class name representing a toplevel Ceylon class or method. * * @param module the module name to initialise in the metamodel * @param version the module version * @param runClass the Java class name representing a toplevel Ceylon class or method * @param arguments the arguments to pass to the Ceylon program * * @throws RuntimeException if anything wrong happens */ public static void runModule(String module, String version, String runClass, String... arguments){ instance().run(module, version, runClass, arguments); } /** * Sets up the metamodel for the specified module and execute the main method on the * specified Java class name representing a toplevel Ceylon class or method. * * @param module the module name to initialise in the metamodel * @param version the module version * @param runClass the Java class name representing a toplevel Ceylon class or method * @param arguments the arguments to pass to the Ceylon program * * @throws RuntimeException if anything wrong happens */ public void run(String module, String version, String runClass, String... arguments){ setup(module, version); try { Class klass = moduleClassLoader.loadClass(runClass); invokeMain(klass, arguments); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } /** * Sets up the metamodel for the specified module and execute the main method on the * specified Java class representing a toplevel Ceylon class or method. * * @param module the module name to initialise in the metamodel * @param version the module version * @param runClass the Java class representing a toplevel Ceylon class or method * @param arguments the arguments to pass to the Ceylon program * * @throws RuntimeException if anything wrong happens */ public static void runModule(String module, String version, Class runClass, String... arguments){ instance().run(module, version, runClass, arguments); } /** * Sets up the metamodel for the specified module and execute the main method on the * specified Java class representing a toplevel Ceylon class or method. * * @param module the module name to initialise in the metamodel * @param version the module version * @param runClass the Java class representing a toplevel Ceylon class or method * @param arguments the arguments to pass to the Ceylon program * * @throws RuntimeException if anything wrong happens */ public void run(String module, String version, Class runClass, String... arguments){ setup(module, version); try { invokeMain(runClass, arguments); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new RuntimeException(e); } } private static void invokeMain(Class klass, String[] arguments) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method main = klass.getMethod("main", String[].class); main.invoke(null, (Object)arguments); } /** * Sets up the Ceylon metamodel by adding the specified module to it. This does not run any Ceylon code, * nor does it reset the metamodel first. You can repeatedly invoke this method to add new Ceylon modules * to the metamodel. * * @param module the module name to load. * @param version the version to load. Ignored if the module is the default module. */ public static void setupMetamodel(String module, String version){ instance().setup(module, version); } /** * Sets up the Ceylon metamodel by adding the specified module to it. This does not run any Ceylon code, * nor does it reset the metamodel first. You can repeatedly invoke this method to add new Ceylon modules * to the metamodel. * * @param module the module name to load. * @param version the version to load. Ignored if the module is the default module. */ public void setup(String module, String version){ Overrides parsedOverrides = Overrides.getDistOverrides(); if (overrides != null) { try { parsedOverrides = parsedOverrides.append(overrides); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } // FIXME: we should get rid of that and call reset with the runtimeResolver new OverridesRuntimeResolver(parsedOverrides).installInThreadLocal(); if(moduleClassLoader == null) setupModuleClassLoader(module); if (classPath == null) { classPath = new ClassPath(parsedOverrides); visited = new HashSet(); registerInMetamodel("ceylon.language", Versions.CEYLON_VERSION_NUMBER, false); registerInMetamodel("com.redhat.ceylon.common", Versions.CEYLON_VERSION_NUMBER, false); registerInMetamodel("com.redhat.ceylon.model", Versions.CEYLON_VERSION_NUMBER, false); registerInMetamodel("com.redhat.ceylon.module-resolver", Versions.CEYLON_VERSION_NUMBER, false); } if(module.equals(com.redhat.ceylon.model.typechecker.model.Module.DEFAULT_MODULE_NAME)) version = null; registerInMetamodel(module, version, false); } private void setupModuleClassLoader(String module) { if(JDKUtils.jdk.providesVersion(JDK.JDK9.version)){ try{ Object mod = Java9ModuleUtil.getModule(getClass()); // make sure we're running as a module, not from the classpath if(Java9ModuleUtil.isNamedModule(mod)){ // also add a read to it Object otherModule = Java9ModuleUtil.findModule(mod, module); if(otherModule == null){ // try to load it dynamically otherModule = Java9ModuleUtil.loadModuleDynamically(module); } if(otherModule != null){ Class moduleClass = ClassLoader.getSystemClassLoader().loadClass("java.lang.reflect.Module"); // also add a read to it Method addReads = moduleClass.getMethod("addReads", moduleClass); addReads.invoke(mod, otherModule); moduleClassLoader = Java9ModuleUtil.getClassLoader(otherModule); } } }catch(InvocationTargetException | IllegalAccessException | IllegalArgumentException | ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException x){ throw new RuntimeException(x); } } if(moduleClassLoader == null) moduleClassLoader = ClassLoader.getSystemClassLoader(); } /** * Resets the metamodel. This will impact any Ceylon code running on the same ClassLoader, across * threads, and will crash them if they are not done running. */ public static void resetMetamodel(){ instance().reset(); } /** * Resets the metamodel. This will impact any Ceylon code running on the same ClassLoader, across * threads, and will crash them if they are not done running. */ public void reset(){ Metamodel.resetModuleManager(); } private void registerInMetamodel(String name, String version, boolean optional) { ClassPath.Module module; try { module = classPath.loadModule(name, version, allowMissingModules); } catch (com.redhat.ceylon.compiler.java.runtime.Main.ClassPath.ModuleNotFoundException e) { if(optional) return; throw new RuntimeException(e); } if(!visited.add(module)) return; // skip JDK modules which are already in the metamodel if(module.type == ClassPath.Type.JDK) return; Metamodel.loadModule(name, version, module, moduleClassLoader); // also register its dependencies for(ClassPath.Dependency dep : module.dependencies) registerInMetamodel(dep.name(), dep.version(), dep.optional); } /** *

* Main entry point, invoke with: moduleSpec mainJavaClassName args*. *

*

* WARNING: this code will call @{link {@link System#exit(int)} if the arguments * are incorrect or missing. This is really only intended to be called from the java * command-line. All it does is parse the arguments and invoke * @{link {@link Main#runModule(String, String, String, String...)}. *

*/ public static void main(String[] args) { // WARNING: keep in sync with Main2 int idx = 0; boolean allowMissingModules = false; String overrides = null; for(int i=0;i= args.length){ usage(); } overrides = args[++i]; idx++; }else usage(); idx++; } if(args.length < (2 + idx)){ usage(); } ModuleSpec moduleSpec = ModuleSpec.parse(args[idx], Option.VERSION_REQUIRED); String version; if(moduleSpec.getName().equals(com.redhat.ceylon.model.typechecker.model.Module.DEFAULT_MODULE_NAME)) version = null; else version = moduleSpec.getVersion(); String[] moduleArgs = Arrays.copyOfRange(args, 2 + idx, args.length); instance() .allowMissingModules(allowMissingModules) .overrides(overrides) .run(moduleSpec.getName(), version, args[idx + 1], moduleArgs); } private static void usage() { System.err.println("Invalid arguments."); System.err.println("Usage: \n"); System.err.println(Main.class.getName()+" [--allow-missing-modules] [--overrides overridesFile.xml] moduleSpec mainJavaClassName args*"); System.exit(1); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy