blob: f0350548c027f33783041b0474fd03fa5a675812 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.maven.queries;
import java.io.File;
import java.lang.ref.SoftReference;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingException;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.project.ProjectBuildingResult;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.modules.maven.NbMavenProjectFactory;
import org.netbeans.modules.maven.embedder.EmbedderFactory;
import org.netbeans.modules.maven.embedder.MavenEmbedder;
import org.netbeans.spi.java.classpath.ClassPathFactory;
import org.netbeans.spi.java.classpath.ClassPathImplementation;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.PathResourceImplementation;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Utilities;
import org.openide.util.lookup.ServiceProvider;
/**
*
* @author mkleint
*/
@ServiceProvider (service=ClassPathProvider.class, position=11)
public class RepositoryMavenCPProvider implements ClassPathProvider {
private static final Logger LOG = Logger.getLogger(RepositoryMavenCPProvider.class.getName());
private static final int CACHE_MAX_SIZE = Integer.getInteger("RepositoryMavenCPProvider.cacheMaxSize", 10); // NOI18N
private final Map<FileObject, SoftReference<MavenProject>> cache = new LinkedHashMap<FileObject, SoftReference<MavenProject>>() {
@Override
protected boolean removeEldestEntry(Entry<FileObject, SoftReference<MavenProject>> eldest) {
return size() > CACHE_MAX_SIZE;
}
};
@Override
public ClassPath findClassPath(FileObject file, String type) {
//#223841 at least one project opened is a stronger condition, embedder gets sometimes reset.
//once we have the project loaded, not loaded embedder doesn't matter anymore, we have to process.
// sometimes the embedder is loaded even though a maven project is not yet loaded, it doesn't hurt to proceed then.
if (!NbMavenProjectFactory.isAtLeastOneMavenProjectAround() && !EmbedderFactory.isProjectEmbedderLoaded()) {
return null;
}
FileObject archive = FileUtil.getArchiveFile(file);
if (archive != null && archive.getNameExt().endsWith("-sources.jar")) { //first simple check
File sourceFile = FileUtil.toFile(archive);
if (sourceFile != null) {
// String name = jarFile.getName();
File parent = sourceFile.getParentFile();
if (parent != null) {
File parentParent = parent.getParentFile();
if (parentParent != null) {
// each repository artifact should have this structure
String artifact = parentParent.getName();
String version = parent.getName();
if (archive.getNameExt().startsWith(artifact + "-" + version)) { //#211158 another heuristic check to avoid calling EmbedderFactory for non- local maven repository artifacts
//TODO is there a need for generified extension lookup or is just .jar files ok?
//TODO can the .jar extension be hardwired? on CP..
File bin = new File(parent, artifact + "-" + version + ".jar"); //NOI18N
File pom = new File(parent, artifact + "-" + version + ".pom"); //NOI18N
URI localRepo = Utilities.toURI(EmbedderFactory.getProjectEmbedder().getLocalRepositoryFile());
URI rel = localRepo.relativize(Utilities.toURI(parentParent.getParentFile()));
if (!rel.isAbsolute()) {
String groupId = rel.getPath();
if (groupId != null && !groupId.equals("")) {
groupId = groupId.replace("/", ".");
if (groupId.endsWith(".")) {
groupId = groupId.substring(0, groupId.length() - 1);
}
if (ClassPath.SOURCE.equals(type)) {
return ClassPathFactory.createClassPath(createSourceCPI(sourceFile));
}
if (ClassPath.BOOT.equals(type)) {
return JavaPlatform.getDefault().getBootstrapLibraries();
}
if (ClassPath.COMPILE.equals(type)) {
MavenProject mp = getMavenProject(archive, pom, groupId, artifact, version);
return ClassPathFactory.createClassPath(createCompileCPI(mp, bin));
}
if (ClassPath.EXECUTE.equals(type)) {
MavenProject mp = getMavenProject(archive, pom, groupId, artifact, version);
return ClassPathFactory.createClassPath(createExecuteCPI(mp, bin));
}
} else {
//some sort of weird groupId?
}
}
}
}
}
}
}
return null;
}
private MavenProject getMavenProject(FileObject archive, File pom, String groupId, String artifact, String version) {
SoftReference<MavenProject> ref = cache.get(archive);
MavenProject mp = ref != null ? ref.get() : null;
if(mp == null) {
mp = loadMavenProject(pom, groupId, artifact, version);
cache.put(archive, new SoftReference<>(mp));
}
return mp;
}
private MavenProject loadMavenProject(File pom, String groupId, String artifactId, String version) {
MavenEmbedder embedder = EmbedderFactory.getProjectEmbedder();
Artifact projectArtifact = embedder.createArtifact(groupId, artifactId, version, "jar");
try {
ProjectBuildingRequest dpbr = embedder.createMavenExecutionRequest().getProjectBuildingRequest();
dpbr.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
dpbr.setProcessPlugins(false);
dpbr.setResolveDependencies(true);
ArrayList<ArtifactRepository> remoteRepos = new ArrayList<ArtifactRepository>();
//for project embedder doens't matter
// remoteRepos = RepositoryPreferences.getInstance().remoteRepositories();
dpbr.setRemoteRepositories(remoteRepos);
ProjectBuildingResult res = embedder.buildProject(projectArtifact, dpbr);
if (res.getProject() != null) {
return res.getProject();
} else {
LOG.log(Level.INFO, "No project model from repository for {0}: {1}", new Object[] {projectArtifact, res.getProblems()});
}
} catch (ProjectBuildingException ex) {
LOG.log(Level.FINER, "Failed to load project model from repository for {0}: {1}", new Object[] {projectArtifact, ex});
} catch (Exception exception) {
LOG.log(Level.FINER, "Failed to load project model from repository for " + projectArtifact, exception);
}
return null;
}
private ClassPathImplementation createCompileCPI(MavenProject project, File binary) {
List<PathResourceImplementation> items = new ArrayList<PathResourceImplementation>();
//according to jglick this could be posisble to leave out on compilation CP..
items.add(ClassPathSupport.createResource(FileUtil.urlForArchiveOrDir(binary)));
if (project != null) {
for (Artifact s : project.getCompileArtifacts()) {
File file = s.getFile(); // TODO perhaps needs patching
if (file == null) continue;
URL u = FileUtil.urlForArchiveOrDir(file);
if(u != null) {
items.add(ClassPathSupport.createResource(u));
} else {
LOG.log(Level.FINE, "Could not retrieve URL for artifact file {0}", new Object[] {file}); // NOI18N
}
}
}
return ClassPathSupport.createClassPathImplementation(items);
}
private ClassPathImplementation createExecuteCPI(MavenProject project, File binary) {
List<PathResourceImplementation> items = new ArrayList<PathResourceImplementation>();
items.add(ClassPathSupport.createResource(FileUtil.urlForArchiveOrDir(binary)));
if (project != null) {
for (Artifact s : project.getRuntimeArtifacts()) {
if (s.getFile() == null) continue;
items.add(ClassPathSupport.createResource(FileUtil.urlForArchiveOrDir(s.getFile())));
}
}
return ClassPathSupport.createClassPathImplementation(items);
}
private ClassPathImplementation createSourceCPI(File sourceFile) {
return ClassPathSupport.createClassPathImplementation(Collections.singletonList(ClassPathSupport.createResource(FileUtil.urlForArchiveOrDir(sourceFile))));
}
}