Package com.redhat.ceylon.compiler.java.tools

Source Code of com.redhat.ceylon.compiler.java.tools.JarOutputRepositoryManager

/*
* Copyright Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the authors tag. All rights reserved.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU General Public License version 2.
*
* This particular file is subject to the "Classpath" exception as provided in the
* LICENSE file that accompanied this code.
*
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE.  See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License,
* along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*/

package com.redhat.ceylon.compiler.java.tools;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;

import javax.tools.JavaFileObject;
import javax.tools.StandardLocation;

import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.RepositoryManager;
import com.redhat.ceylon.cmr.api.ArtifactCreator;
import com.redhat.ceylon.cmr.ceylon.CeylonUtils;
import com.redhat.ceylon.cmr.util.JarUtils;
import com.redhat.ceylon.common.Constants;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.log.Logger;
import com.redhat.ceylon.compiler.java.tools.JarEntryManifestFileObject.OsgiManifest;
import com.redhat.ceylon.compiler.typechecker.model.Module;
import com.sun.source.util.TaskListener;
import com.sun.tools.javac.main.OptionName;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Options;

public class JarOutputRepositoryManager {
   
    private Map<Module,ProgressiveJar> openJars = new HashMap<Module, ProgressiveJar>();
    private Log log;
    private Options options;
    private CeyloncFileManager ceyloncFileManager;
    private TaskListener taskListener;
   
    JarOutputRepositoryManager(Log log, Options options, CeyloncFileManager ceyloncFileManager, TaskListener taskListener){
        this.log = log;
        this.options = options;
        this.ceyloncFileManager = ceyloncFileManager;
        this.taskListener = taskListener;
    }
   
    public JavaFileObject getFileObject(RepositoryManager repositoryManager, Module module, String fileName, File sourceFile) throws IOException{
        ProgressiveJar progressiveJar = getProgressiveJar(repositoryManager, module);
        return progressiveJar.getJavaFileObject(fileName, sourceFile);
    }
   
    private ProgressiveJar getProgressiveJar(RepositoryManager repositoryManager, Module module) throws IOException {
        ProgressiveJar jarFile = openJars.get(module);
        if(jarFile == null){
            jarFile = new ProgressiveJar(repositoryManager, module, log, options, ceyloncFileManager, taskListener);
            openJars.put(module, jarFile);
        }
        return jarFile;
    }

    public void flush() throws IOException {
        Exception ex = null;
        try{
            for(ProgressiveJar jarFile : openJars.values()){
                try {
                    jarFile.close();
                } catch (Exception e) {
                    ex = e;
                }
            }
        }finally{
            // make sure we clear on return and throw, so we don't try to flush again on throw
            openJars.clear();
        }
        if (ex instanceof IOException) {
            throw (IOException)ex;
        }
    }
   
    static class ProgressiveJar {
        private static final String META_INF = "META-INF";
        private static final String MAPPING_FILE = META_INF+"/mapping.txt";
        private File originalJarFile;
        private File outputJarFile;
        private JarOutputStream jarOutputStream;
        final private Set<String> modifiedSourceFiles = new HashSet<String>();
        final private Set<String> modifiedResourceFilesRel = new HashSet<String>();
        final private Set<String> modifiedResourceFilesFull = new HashSet<String>();
        final private Properties writtenClassesMapping = new Properties();
        private Logger cmrLog;
        private Options options;
        private RepositoryManager repoManager;
        private ArtifactContext carContext;
        private ArtifactCreator srcCreator;
        private ArtifactCreator resourceCreator;
        private Module module;
        private Set<String> folders = new HashSet<String>();
        private boolean manifestWritten = false;
        private boolean writeOsgiManifest;
        private final String resourceRootPath;
        private boolean writeMavenManifest;
        private TaskListener taskListener;

        public ProgressiveJar(RepositoryManager repoManager, Module module, Log log, Options options, CeyloncFileManager ceyloncFileManager, TaskListener taskListener) throws IOException{
            this.options = options;
            this.repoManager = repoManager;
            this.carContext = new ArtifactContext(module.getNameAsString(), module.getVersion(), ArtifactContext.CAR);
            this.cmrLog = new JavacLogger(options, Log.instance(ceyloncFileManager.getContext()));
            this.srcCreator = CeylonUtils.makeSourceArtifactCreator(
                    repoManager,
                    ceyloncFileManager.getLocation(StandardLocation.SOURCE_PATH),
                    module.getNameAsString(), module.getVersion(),
                    options.get(OptionName.VERBOSE) != null, cmrLog);
            this.resourceCreator = CeylonUtils.makeResourceArtifactCreator(
                    repoManager,
                    ceyloncFileManager.getLocation(StandardLocation.SOURCE_PATH),
                    ceyloncFileManager.getLocation(CeylonLocation.RESOURCE_PATH),
                    options.get(OptionName.CEYLONRESOURCEROOT),
                    module.getNameAsString(), module.getVersion(),
                    options.get(OptionName.VERBOSE) != null, cmrLog);
            this.module = module;
            this.writeOsgiManifest = !options.isSet(OptionName.CEYLONNOOSGI);
            this.writeMavenManifest = !options.isSet(OptionName.CEYLONNOPOM);
           
            // Determine the special path that signals that the files it contains
            // should be moved to the root of the output JAR/CAR
            String rrp = module.getNameAsString().replace('.', '/');
            if (!rrp.isEmpty() && !rrp.endsWith("/")) {
                rrp = rrp + "/";
            }
            String rootName = options.get(OptionName.CEYLONRESOURCEROOT);
            if (rootName == null) {
                rootName = Constants.DEFAULT_RESOURCE_ROOT;
            }
            this.resourceRootPath = rrp + rootName + "/";
            this.taskListener = taskListener;
           
            this.originalJarFile = repoManager.getArtifact(carContext);
            this.outputJarFile = File.createTempFile("ceylon-compiler-", ".car");
            this.jarOutputStream = new JarOutputStream(new FileOutputStream(outputJarFile));
        }

        private Properties getPreviousMapping() throws IOException {
            if (originalJarFile != null) {
                JarFile jarFile = null;
                jarFile = new JarFile(originalJarFile);
                try {
                    JarEntry entry = jarFile.getJarEntry(MAPPING_FILE);
                    if (entry != null) {
                        InputStream inputStream = jarFile.getInputStream(entry);
                        try {
                            Properties previousMapping = new Properties();
                            previousMapping.load(inputStream);
                            return previousMapping;
                        } finally {
                            inputStream.close();
                        }
                    }
                } finally {
                    jarFile.close();
                }
            }
            return null;
        }

        private Manifest getPreviousManifest() throws IOException {
            if (originalJarFile != null) {
                JarFile jarFile = null;
                jarFile = new JarFile(originalJarFile);
                try {
                    return jarFile.getManifest();
                } finally {
                    jarFile.close();
                }
            }
            return null;
        }

        public void close() throws IOException {
            try {
                Set<String> copiedSourceFiles = srcCreator.copy(modifiedSourceFiles);
                resourceCreator.copy(modifiedResourceFilesFull);
   
                if (writeOsgiManifest && !manifestWritten) {
                    Manifest manifest = new OsgiManifest(module, getPreviousManifest()).build();
                    writeManifestJarEntry(manifest);
                }
                if (writeMavenManifest) {
                    writeMavenManifest(module);
                }
   
                Properties previousMapping = getPreviousMapping();
                writeMappingJarEntry(previousMapping, getJarFilter(previousMapping, copiedSourceFiles));
               
                JarUtils.finishUpdatingJar(
                        originalJarFile, outputJarFile, carContext, jarOutputStream,
                        getJarFilter(previousMapping, copiedSourceFiles),
                        repoManager, options.get(OptionName.VERBOSE) != null, cmrLog, folders, options.isSet(OptionName.CEYLONPACK200));
               
                String info;
                if(module.isDefault())
                    info = module.getNameAsString();
                else
                    info = module.getNameAsString() + "/" + module.getVersion();
                cmrLog.info("Created module " + info);
                if(taskListener instanceof CeylonTaskListener){
                    ((CeylonTaskListener) taskListener).moduleCompiled(module.getNameAsString(), module.getVersion());
                }
            } finally {
                FileUtil.deleteQuietly(outputJarFile);
            }
        }

        private JarUtils.JarEntryFilter getJarFilter(final Properties previousMapping, final Set<String> copiedSourceFiles) {
            return new JarUtils.JarEntryFilter() {
                @Override
                public boolean avoid(String entryFullName) {
                    if (entryFullName.endsWith(".class")) {
                        boolean classWasUpdated = writtenClassesMapping.containsKey(entryFullName);
                        if (previousMapping != null) {
                            String sourceFileForClass = previousMapping.getProperty(entryFullName);
                            classWasUpdated = classWasUpdated || copiedSourceFiles.contains(sourceFileForClass);
                        }
                        return classWasUpdated;
                    } else {
                        return modifiedResourceFilesRel.contains(entryFullName)
                                || entryFullName.equals(MAPPING_FILE)
                                || (writeOsgiManifest && OsgiManifest.isManifestFileName(entryFullName))
                                || (writeMavenManifest && MavenPomUtil.isMavenDescriptor(entryFullName, module));
                    }
                }
            };
        }


        private void writeManifestJarEntry(Manifest manifest) {
            try {
                folders.add(META_INF+"/");
                jarOutputStream.putNextEntry(new ZipEntry(OsgiManifest.MANIFEST_FILE_NAME));
                manifest.write(jarOutputStream);
            }
            catch (IOException e) {
                // TODO : log to the right place
            }
            finally {
                try {
                    jarOutputStream.closeEntry();
                }
                catch (IOException ignore) {
                }
            }
        }

        private void writeMavenManifest(Module module) {
            MavenPomUtil.writeMavenManifest(jarOutputStream, module, folders);
        }

        private void writeMappingJarEntry(Properties previousMapping, JarUtils.JarEntryFilter filter) {
            Properties newMapping = new Properties();
            newMapping.putAll(writtenClassesMapping);
            if (previousMapping != null) {
                // Add the previous mapping entries that are not related to an updated source file
                for (String classFullName : previousMapping.stringPropertyNames()) {
                    if (!filter.avoid(classFullName)) {
                        newMapping.setProperty(classFullName, previousMapping.getProperty(classFullName));
                    }
                }
            }
            // Write the mapping file to the Jar
            try {
                folders.add(META_INF+"/");
                jarOutputStream.putNextEntry(new ZipEntry(MAPPING_FILE));
                newMapping.store(jarOutputStream, "");
            }
            catch(IOException e) {
                // TODO : log to the right place
            }
            finally {
                try {
                    jarOutputStream.closeEntry();
                } catch (IOException e) {
                }
            }
        }

        public JavaFileObject getJavaFileObject(String fileName, File sourceFile) {
            String entryName = fileName.replace(File.separatorChar, '/');
           
            if (!resourceRootPath.isEmpty() && entryName.startsWith(resourceRootPath)) {
                // Files in the special "resource root path" get moved
                // to the root of the output JAR/CAR
                entryName = entryName.substring(resourceRootPath.length());
            }
           
            String folder = JarUtils.getFolder(entryName);
            if (folder != null) {
                folders.add(folder);
            }

            if (sourceFile != null) {
                modifiedSourceFiles.add(sourceFile.getPath());
                // record the class file we produce so that we don't save it from the original jar
              addMappingEntry(entryName, JarUtils.toPlatformIndependentPath(srcCreator.getPaths(), sourceFile.getPath()));
            } else {
                modifiedResourceFilesRel.add(entryName);
                modifiedResourceFilesFull.add(FileUtil.applyPath(resourceCreator.getPaths(), fileName).getPath());
                if (writeOsgiManifest && OsgiManifest.isManifestFileName(entryName)) {
                    this.manifestWritten = true;
                    return new JarEntryManifestFileObject(outputJarFile.getPath(), jarOutputStream, entryName, module);
                }
            }
            return new JarEntryFileObject(outputJarFile.getPath(), jarOutputStream, entryName);
        }

        private void addMappingEntry(String className,
                String sourcePath) {
            writtenClassesMapping.put(className, sourcePath);
        }
    }
}
TOP

Related Classes of com.redhat.ceylon.compiler.java.tools.JarOutputRepositoryManager

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.