blob: 3daa16632b711defdfe4941a153c2575069ceb1c [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.actions;
import org.codehaus.plexus.util.FileUtils;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.SwingUtilities;
import org.apache.maven.artifact.Artifact;
import org.netbeans.api.annotations.common.SuppressWarnings;
import org.netbeans.modules.maven.embedder.MavenEmbedder;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.dependency.tree.DependencyNode;
import org.netbeans.api.annotations.common.CheckForNull;
import org.netbeans.api.annotations.common.StaticResource;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
import org.netbeans.api.project.libraries.LibrariesCustomizer;
import org.netbeans.api.project.libraries.Library;
import org.netbeans.api.project.libraries.LibraryManager;
import org.netbeans.modules.maven.api.FileUtilities;
import org.netbeans.modules.maven.embedder.EmbedderFactory;
import org.netbeans.modules.maven.embedder.exec.ProgressTransferListener;
import org.netbeans.spi.java.project.support.JavadocAndSourceRootDetection;
import org.netbeans.spi.project.libraries.support.LibrariesSupport;
import org.openide.DialogDescriptor;
import org.openide.DialogDisplayer;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.LookupEvent;
import org.openide.util.LookupListener;
import org.openide.util.NbBundle.Messages;
import org.openide.util.RequestProcessor;
import static org.netbeans.modules.maven.actions.Bundle.*;
import org.netbeans.modules.maven.api.ModelUtils;
import org.openide.NotifyDescriptor;
import org.openide.util.Utilities;
/**
*
* @author mkleint
*/
public class CreateLibraryAction extends AbstractAction implements LookupListener {
private final Lookup lookup;
private final Lookup.Result<DependencyNode> result;
private final static @StaticResource String LIBRARIES_ICON = "org/netbeans/modules/maven/actions/libraries.gif";;
private boolean createRunning;
@Messages("ACT_Library=Create Library")
@java.lang.SuppressWarnings("LeakingThisInConstructor")
public CreateLibraryAction(Lookup lkp) {
this.lookup = lkp;
putValue(NAME, ACT_Library());
//TODO proper icon
putValue(SMALL_ICON, ImageUtilities.image2Icon(ImageUtilities.loadImage(LIBRARIES_ICON, true))); //NOI18N
putValue("iconBase", "org/netbeans/modules/maven/actions/libraries.gif"); //NOI18N
result = lookup.lookupResult(DependencyNode.class);
setEnabled(result.allInstances().size() > 0);
result.addLookupListener(this);
}
@Messages("LBL_CreateLibrary=Create a NetBeans Library from Maven metadata")
public @Override void actionPerformed(ActionEvent e) {
Iterator<? extends DependencyNode> roots = result.allInstances().iterator();
if (!roots.hasNext()) {
return;
}
final DependencyNode root = roots.next();
final MavenProject project = lookup.lookup(MavenProject.class);
final CreateLibraryPanel pnl = new CreateLibraryPanel(root);
DialogDescriptor dd = new DialogDescriptor(pnl, LBL_CreateLibrary());
pnl.createValidations(dd);
if (DialogDisplayer.getDefault().notify(dd) == DialogDescriptor.OK_OPTION) {
createRunning = true;
setEnabled();
RequestProcessor.getDefault().post(new Runnable() {
public @Override void run() {
try {
Library lib = createLibrary(pnl.getLibraryManager(), pnl.getLibraryName(), pnl.getIncludeArtifacts(), pnl.isAllSourceAndJavadoc(), project, pnl.getCopyDirectory());
if (lib != null) {
LibrariesCustomizer.showCustomizer(lib, pnl.getLibraryManager());
}
} finally {
createRunning = false;
setEnabled();
}
}
});
}
}
public @Override void resultChanged(LookupEvent ev) {
setEnabled();
}
private void setEnabled() {
SwingUtilities.invokeLater(new Runnable() {
public @Override void run() {
setEnabled(!createRunning && result.allInstances().size() > 0);
}
});
}
@Messages({
"MSG_Create_Library=Create Library",
"# {0} - Maven coordinates", "MSG_Downloading=Maven: downloading {0}",
"# {0} - Maven coordinates", "MSG_Downloading_javadoc=Maven: downloading Javadoc {0}",
"# {0} - Maven coordinates", "MSG_Downloading_sources=Maven: downloading sources {0}",
"# {0} - Maven coordinates", "MSG_NoJar=No jar file available for {0}"
})
@SuppressWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE") // baseFolder.mkdirs; will throw IOE later from getJarUri
private static @CheckForNull Library createLibrary(LibraryManager libraryManager, String libraryName, List<Artifact> includeArtifacts, boolean allSourceAndJavadoc, MavenProject project, String copyTo) {
ProgressHandle handle = ProgressHandleFactory.createHandle(MSG_Create_Library(),
ProgressTransferListener.cancellable());
int count = includeArtifacts.size() * (allSourceAndJavadoc ? 3 : 1) + 5;
handle.start(count);
try {
MavenEmbedder online = EmbedderFactory.getOnlineEmbedder();
int index = 1;
List<URI> classpathVolume = new ArrayList<URI>();
List<URI> javadocVolume = new ArrayList<URI>();
List<URI> sourceVolume = new ArrayList<URI>();
Map<String, String> properties = new HashMap<String, String>();
Map<String, List<URI>> volumes = new HashMap<String, List<URI>>();
File baseFolder = null;
File nonDefaultLibBase = null;
if (copyTo != null) {
//resolve there to copy files
URL libRoot = libraryManager.getLocation();
File base;
if (libRoot != null) {
try {
base = Utilities.toFile(libRoot.toURI());
//getLocation() points to a file
base = base.getParentFile();
nonDefaultLibBase = base;
} catch (URISyntaxException ex) {
Exceptions.printStackTrace(ex);
base = new File(System.getProperty("netbeans.user"), "libraries");
}
} else {
base = new File(System.getProperty("netbeans.user"), "libraries");
}
base = FileUtil.normalizeFile(base);
baseFolder = FileUtilities.resolveFilePath(base, copyTo);
baseFolder.mkdirs();
}
volumes.put("classpath", classpathVolume); //NOI18N
if (allSourceAndJavadoc) {
volumes.put("javadoc", javadocVolume); //NOI18N
volumes.put("src", sourceVolume); //NOI18N
}
StringBuilder mavendeps = new StringBuilder();
for (Artifact a : includeArtifacts) {
if (mavendeps.length() > 0) {
mavendeps.append(" ");
}
mavendeps.append(a.getGroupId()).append(":").append(a.getArtifactId()).append(":").append(a.getVersion()).append(":");
if (a.hasClassifier()) {
mavendeps.append(a.getClassifier()).append(":");
}
mavendeps.append(a.getType());
handle.progress(MSG_Downloading(a.getId()), index);
//XXX --------
//XXX project.getRemoteArtifactRepositories() might not be entirely reliable, we might want to use
//XXX RepositoryPreferences.getInstance().getRepositoryInfos() as well..
try {
online.resolve(a, project.getRemoteArtifactRepositories(), online.getLocalRepository());
AtomicBoolean cancel = ProgressTransferListener.activeListener().cancel;
if (cancel != null && cancel.get()) {
return null;
}
if(!a.getFile().exists()) {
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(MSG_NoJar(a.getId()), NotifyDescriptor.WARNING_MESSAGE));
return null;
}
classpathVolume.add(getJarUri(a, baseFolder, nonDefaultLibBase, ClassifierType.BINARY));
try {
if (allSourceAndJavadoc) {
handle.progress(MSG_Downloading_javadoc(a.getId()), index + 1);
Artifact javadoc = online.createArtifactWithClassifier(
a.getGroupId(),
a.getArtifactId(),
a.getVersion(),
a.getType(),
"javadoc"); //NOI18N
online.resolve(javadoc, project.getRemoteArtifactRepositories(), online.getLocalRepository());
cancel = ProgressTransferListener.activeListener().cancel;
if (cancel != null && cancel.get()) {
return null;
}
if (javadoc.getFile().exists()) {
URI javadocUri = getJarUri(javadoc, baseFolder, nonDefaultLibBase, ClassifierType.JAVADOC);
javadocVolume.add(javadocUri);
}
handle.progress(MSG_Downloading_sources(a.getId()), index + 2);
Artifact sources = online.createArtifactWithClassifier(
a.getGroupId(),
a.getArtifactId(),
a.getVersion(),
a.getType(),
"sources"); //NOI18N
online.resolve(sources, project.getRemoteArtifactRepositories(), online.getLocalRepository());
cancel = ProgressTransferListener.activeListener().cancel;
if (cancel != null && cancel.get()) {
return null;
}
if (sources.getFile().exists()) {
sourceVolume.add(getJarUri(sources, baseFolder, nonDefaultLibBase, ClassifierType.SOURCES));
}
}
} catch (Exception ex) {
Logger.getLogger(CreateLibraryAction.class.getName()).log(Level.FINE, "Failed to download artifact", ex);
}
} catch (Exception ex) {
Logger.getLogger(CreateLibraryAction.class.getName()).log(Level.FINE, "Failed to download artifact", ex);
}
index = index + (allSourceAndJavadoc ? 3 : 1);
}
try {
handle.progress("Adding library", index + 4);
properties.put(ModelUtils.LIBRARY_PROP_DEPENDENCIES, mavendeps.toString());
return libraryManager.createURILibrary("j2se", libraryName, libraryName, "Library created from Maven artifacts", volumes, properties); //NOI18N
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
} catch (ThreadDeath d) { // download interrupted
} catch (IllegalStateException ise) { //download interrupted in dependent thread. #213812
if (!(ise.getCause() instanceof ThreadDeath)) {
throw ise;
}
} finally {
handle.finish();
}
return null;
}
/** append path to given jar root uri */
private static URI appendJarFolder(URI u, String jarFolder) {
try {
if (u.isAbsolute()) {
return new URI("jar:" + u.toString() + "!/" + (jarFolder == null ? "" : jarFolder.replace('\\', '/'))); // NOI18N
} else {
return new URI(u.toString() + "!/" + (jarFolder == null ? "" : jarFolder.replace('\\', '/'))); // NOI18N
}
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
}
private enum ClassifierType {BINARY, JAVADOC, SOURCES}
private static URI getJarUri(Artifact a, File copyTo, File nonDefaultLibBase, ClassifierType type) throws IOException {
File res = a.getFile();
URI uri = Utilities.toURI(res);
String jarPath = null;
if (copyTo != null) {
res = new File(copyTo, a.getFile().getName());
FileUtils.copyFile(a.getFile(), res);
if (nonDefaultLibBase != null) {
String path = FileUtilities.getRelativePath(nonDefaultLibBase, res);
if (path != null) {
uri = LibrariesSupport.convertFilePathToURI(path);
}
}
}
FileUtil.refreshFor(res);
FileObject fo = FileUtil.toFileObject(res);
if (type == ClassifierType.JAVADOC && FileUtil.isArchiveFile(fo)) {
fo = FileUtil.getArchiveRoot(fo);
FileObject docRoot = JavadocAndSourceRootDetection.findJavadocRoot(fo);
if (docRoot != null) {
jarPath = FileUtil.getRelativePath(fo, docRoot);
}
} else if (type == ClassifierType.SOURCES && FileUtil.isArchiveFile(fo)) {
fo = FileUtil.getArchiveRoot(fo);
FileObject srcRoot = JavadocAndSourceRootDetection.findSourceRoot(fo);
if (srcRoot != null) {
jarPath = FileUtil.getRelativePath(fo, srcRoot);
}
}
return appendJarFolder(uri, jarPath);
}
}