io.takari.m2e.jdt.core.internal.JavaConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for io.takari.m2e.jdt.core.internal.JavaConfigurator.java

Source

/**
 * Copyright (c) 2014 Takari, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 */
package io.takari.m2e.jdt.core.internal;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.model.Dependency;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.m2e.core.embedder.ArtifactKey;
import org.eclipse.m2e.core.project.IMavenProjectFacade;
import org.eclipse.m2e.core.project.configurator.ProjectConfigurationRequest;
import org.eclipse.m2e.jdt.IClasspathDescriptor;
import org.eclipse.m2e.jdt.IClasspathEntryDescriptor;
import org.eclipse.m2e.jdt.IJavaProjectConfigurator;
import org.eclipse.m2e.jdt.internal.AbstractJavaProjectConfigurator;
import org.eclipse.osgi.framework.util.Headers;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.CharStreams;
import com.google.common.io.LineProcessor;

@SuppressWarnings("restriction")
public class JavaConfigurator extends AbstractJavaProjectConfigurator implements IJavaProjectConfigurator {

    private static final String COMPILER_PLUGIN_GROUP_ID = "io.takari.maven.plugins";

    private static final String COMPILER_PLUGIN_ARTIFACT_ID = "takari-lifecycle-plugin";

    private static final String GOAL_COMPILE = "compile";

    private static final String GOAL_TESTCOMPILE = "testCompile";

    private static final String DEFAULT_COMPILER_LEVEL = "1.7";

    private static final String PATH_EXPORT_PACKAGE = "META-INF/takari/export-package";

    private static final String PATH_MANIFESTMF = "META-INF/MANIFEST.MF";

    @Override
    protected List<MojoExecution> getCompilerMojoExecutions(ProjectConfigurationRequest request,
            IProgressMonitor monitor) throws CoreException {
        return request.getMavenProjectFacade().getMojoExecutions(COMPILER_PLUGIN_GROUP_ID,
                COMPILER_PLUGIN_ARTIFACT_ID, monitor, GOAL_COMPILE, GOAL_TESTCOMPILE);
    }

    @Override
    protected boolean isTestCompileExecution(MojoExecution execution) {
        return GOAL_TESTCOMPILE.equals(execution.getGoal());
    }

    @Override
    protected boolean isCompileExecution(MojoExecution execution) {
        return GOAL_COMPILE.equals(execution.getGoal());
    }

    @Override
    protected String getDefaultSourceLevel() {
        return DEFAULT_COMPILER_LEVEL;
    }

    @Override
    protected String getDefaultTargetLevel(String source) {
        if (source != null) {
            if ("1.2".equals(source) || "1.3".equals(source)) {
                return "1.4";
            }
            return source;
        }
        return DEFAULT_COMPILER_LEVEL;
    }

    @Override
    protected void addJavaProjectOptions(Map<String, String> options, ProjectConfigurationRequest request,
            IProgressMonitor monitor) throws CoreException {
        super.addJavaProjectOptions(options, request, monitor);
        IMavenProjectFacade facade = request.getMavenProjectFacade();
        if (getEnforce(facade, "transitiveDependencyReference", monitor)
                || getEnforce(facade, "privatePackageReference", monitor)) {
            options.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, "error"); //$NON-NLS-1$
        }
    }

    private boolean getEnforce(IMavenProjectFacade facade, String policy, IProgressMonitor monitor)
            throws CoreException {
        MavenProject mavenProject = facade.getMavenProject(monitor);
        for (MojoExecution execution : facade.getMojoExecutions(COMPILER_PLUGIN_GROUP_ID,
                COMPILER_PLUGIN_ARTIFACT_ID, monitor, GOAL_COMPILE)) {
            String value = getParameterValue(mavenProject, policy, String.class, execution, monitor);
            if ("error".equals(value)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void configureClasspath(IMavenProjectFacade facade, IClasspathDescriptor classpath,
            IProgressMonitor monitor) throws CoreException {
        boolean transitiveDependencyReference = getEnforce(facade, "transitiveDependencyReference", monitor);
        boolean privatePackageReference = getEnforce(facade, "privatePackageReference", monitor);
        if (transitiveDependencyReference || privatePackageReference) {
            configureClasspathAccessRules(facade, classpath, transitiveDependencyReference, privatePackageReference,
                    monitor);
        }
    }

    private void configureClasspathAccessRules(IMavenProjectFacade facade, IClasspathDescriptor classpath,
            boolean transitiveDependencyReference, boolean privatePackageReference, IProgressMonitor monitor)
            throws CoreException {

        MavenProject mavenProject = facade.getMavenProject(monitor);

        Map<ArtifactKey, Artifact> dependencies = new HashMap<>();
        Set<ArtifactKey> directDependencies = new HashSet<>();

        for (Artifact artifact : mavenProject.getArtifacts()) {
            dependencies.put(toArtifactKey(artifact), artifact);
        }
        for (Dependency artifact : mavenProject.getDependencies()) {
            directDependencies.add(toArtifactKey(artifact));
        }

        for (IClasspathEntryDescriptor entry : classpath.getEntryDescriptors()) {
            ArtifactKey artifactKey = entry.getArtifactKey();
            if (!dependencies.containsKey(artifactKey)) {
                continue; // ignore custom classpath entries
            }
            if (transitiveDependencyReference && !directDependencies.contains(artifactKey)) {
                entry.addAccessRule(JavaCore.newAccessRule(new Path("**"),
                        IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER));
            } else if (privatePackageReference) {
                Collection<String> exportedPackages = getExportedPackages(dependencies.get(artifactKey));
                if (exportedPackages != null) {
                    for (String exportedPackage : exportedPackages) {
                        IPath pattern = new Path(exportedPackage).append("/*");
                        entry.addAccessRule(JavaCore.newAccessRule(pattern, IAccessRule.K_ACCESSIBLE));
                    }
                    entry.addAccessRule(JavaCore.newAccessRule(new Path("**"),
                            IAccessRule.K_NON_ACCESSIBLE | IAccessRule.IGNORE_IF_BETTER));
                }
            }
        }
    }

    private Collection<String> getExportedPackages(Artifact artifact) {
        File file = artifact.getFile();
        if (file.isDirectory()) {
            return getExportedPackages(file);
        } else if (file.isFile()) {
            try (ZipFile zip = new ZipFile(file)) {
                return getExportedPackages(zip);
            } catch (IOException e) {
                // silently ignore
            }
        }
        return null;
    }

    private Collection<String> getExportedPackages(ZipFile zip) {
        ZipEntry entry = zip.getEntry(PATH_EXPORT_PACKAGE);
        if (entry != null) {
            try (InputStream is = zip.getInputStream(entry)) {
                return parseExportPackage(is);
            } catch (IOException e) {
                // silently ignore
            }
        }
        entry = zip.getEntry(PATH_MANIFESTMF);
        if (entry != null) {
            try (InputStream is = zip.getInputStream(entry)) {
                return parseBundleManifest(is);
            } catch (IOException | BundleException e) {
                // silently ignore
            }
        }
        return null;
    }

    private Collection<String> getExportedPackages(File directory) {
        try (InputStream is = new FileInputStream(new File(directory, PATH_EXPORT_PACKAGE))) {
            return parseExportPackage(is);
        } catch (IOException e) {
            // silently ignore
        }
        try (InputStream is = new FileInputStream(new File(directory, PATH_MANIFESTMF))) {
            return parseBundleManifest(is);
        } catch (IOException | BundleException e) {
            // silently ignore
        }
        return null;
    }

    protected static Collection<String> parseExportPackage(InputStream is) throws IOException {
        LineProcessor<List<String>> processor = new LineProcessor<List<String>>() {
            final List<String> result = new ArrayList<String>();

            @Override
            public boolean processLine(String line) throws IOException {
                result.add(line.replace('.', '/'));
                return true; // keep reading
            }

            @Override
            public List<String> getResult() {
                return result;
            }
        };
        return CharStreams.readLines(new InputStreamReader(is, Charsets.UTF_8), processor);
    }

    protected static Collection<String> parseBundleManifest(InputStream is) throws IOException, BundleException {
        Headers<String, String> headers = Headers.parseManifest(is);
        if (!headers.containsKey(Constants.BUNDLE_SYMBOLICNAME)) {
            return null; // not an OSGi bundle
        }
        String exportPackageHeader = headers.get(Constants.EXPORT_PACKAGE);
        if (exportPackageHeader == null) {
            return ImmutableSet.of(); // nothing is exported
        }
        Set<String> packages = new HashSet<>();
        for (ManifestElement element : ManifestElement.parseHeader(Constants.EXPORT_PACKAGE, exportPackageHeader)) {
            packages.add(element.getValue().replace('.', '/'));
        }
        return packages;
    }

    private ArtifactKey toArtifactKey(Artifact artifact) {
        return new ArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion(),
                artifact.getClassifier());
    }

    private ArtifactKey toArtifactKey(Dependency artifact) {
        return new ArtifactKey(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
                artifact.getClassifier());
    }

    @Override
    public void configureRawClasspath(ProjectConfigurationRequest request, IClasspathDescriptor classpath,
            IProgressMonitor monitor) throws CoreException {
    }
}