blob: ec71100af4371b294f070004b7795db69b4d5eb7 [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.embedder;
import java.io.File;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.model.Model;
import org.apache.maven.model.building.*;
import org.apache.maven.properties.internal.EnvironmentUtils;
import org.codehaus.plexus.ContainerConfiguration;
import org.codehaus.plexus.DefaultContainerConfiguration;
import org.codehaus.plexus.DefaultPlexusContainer;
import org.codehaus.plexus.PlexusConstants;
import org.codehaus.plexus.PlexusContainerException;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.BaseLoggerManager;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.netbeans.api.annotations.common.NonNull;
import org.netbeans.api.project.ui.OpenProjects;
import org.netbeans.api.project.ui.ProjectGroup;
import org.netbeans.api.project.ui.ProjectGroupChangeEvent;
import org.netbeans.api.project.ui.ProjectGroupChangeListener;
import org.netbeans.modules.maven.embedder.impl.ExtensionModule;
import org.openide.filesystems.FileUtil;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbPreferences;
import org.openide.util.RequestProcessor;
import org.openide.util.BaseUtilities;
/**
* Factory for creating {@link MavenEmbedder}s.
*/
public final class EmbedderFactory {
public static final String PROP_COMMANDLINE_PATH = "commandLineMavenPath";
//same prop constant in MavenSettings.java
static final String PROP_DEFAULT_OPTIONS = "defaultOptions";
private static final Set<String> forbidden = new HashSet<String>();
static {
forbidden.add("netbeans.logger.console"); //NOI18N
forbidden.add("java.util.logging.config.class"); //NOI18N
forbidden.add("netbeans.autoupdate.language"); //NOI18N
forbidden.add("netbeans.dirs"); //NOI18N
forbidden.add("netbeans.home"); //NOI18N
forbidden.add("sun.awt.exception.handler"); //NOI18N
forbidden.add("org.openide.TopManager.GUI"); //NOI18N
forbidden.add("org.openide.major.version"); //NOI18N
forbidden.add("netbeans.autoupdate.variant"); //NOI18N
forbidden.add("netbeans.dynamic.classpath"); //NOI18N
forbidden.add("netbeans.autoupdate.country"); //NOI18N
forbidden.add("netbeans.hash.code"); //NOI18N
forbidden.add("org.openide.TopManager"); //NOI18N
forbidden.add("org.openide.version"); //NOI18N
forbidden.add("netbeans.buildnumber"); //NOI18N
forbidden.add("javax.xml.parsers.DocumentBuilderFactory"); //NOI18N
forbidden.add("javax.xml.parsers.SAXParserFactory"); //NOI18N
forbidden.add("rave.build"); //NOI18N
forbidden.add("netbeans.accept_license_class"); //NOI18N
forbidden.add("rave.version"); //NOI18N
forbidden.add("netbeans.autoupdate.version"); //NOI18N
forbidden.add("netbeans.importclass"); //NOI18N
forbidden.add("netbeans.user"); //NOI18N
// forbidden.add("java.class.path");
// forbidden.add("https.nonProxyHosts");
}
private static final Logger LOG = Logger.getLogger(EmbedderFactory.class.getName());
private static MavenEmbedder project;
private static final AtomicBoolean projectLoaded = new AtomicBoolean(false);
private static final Object PROJECT_LOCK = new Object();
private static MavenEmbedder online;
private static final Object ONLINE_LOCK = new Object();
private static final RequestProcessor RP = new RequestProcessor("Maven Embedder warmup");
private static final RequestProcessor.Task warmupTask = RP.create(new Runnable() {
@Override
public void run() {
//#211158 after being reset, recreate the instance for followup usage.
//makes the performance stats of the project embedder after resetting more predictable
getProjectEmbedder();
}
});
static {
RP.post(new Runnable() {
@Override
public void run() { //#228379
OpenProjects.getDefault().addProjectGroupChangeListener(new ProjectGroupChangeListener() {
@Override
public void projectGroupChanging(ProjectGroupChangeEvent event) {
resetCachedEmbedders();
}
@Override
public void projectGroupChanged(ProjectGroupChangeEvent event) {
}
});
}
});
}
private EmbedderFactory() {
}
/**
* embedder seems to cache some values..
*/
public static void resetCachedEmbedders() {
synchronized (PROJECT_LOCK) {
projectLoaded.set(false);
project = null;
}
synchronized (ONLINE_LOCK) {
online = null;
}
//just delay a bit in case both MavenSettings.setDefaultOptions and Embedderfactory.setMavenHome are called..
RP.post(warmupTask, 100);
}
public static File getDefaultMavenHome() {
return InstalledFileLocator.getDefault().locate("maven", "org.netbeans.modules.maven.embedder", false);
}
private static Preferences getPreferences() { // compatibility; used to be in MavenSettings
return NbPreferences.root().node("org/netbeans/modules/maven");
}
private static Preferences getGroupedPreferences(ProjectGroup grp) {
if (grp != null) {
return grp.preferencesForPackage(EmbedderFactory.class);
}
return null;
}
/**
* global settings value for maven installation root folder.
* @return
*/
public static @NonNull File getMavenHome() {
String str = getPreferences().get(PROP_COMMANDLINE_PATH, null);
if (str != null) {
return FileUtil.normalizeFile(new File(str));
} else {
return getDefaultMavenHome();
}
}
/**
* maven home (installation root) taken from various places (global settings, project group settings ,...)
* @return
* @since 2.32
*/
public static @NonNull File getEffectiveMavenHome() {
ProjectGroup grp = OpenProjects.getDefault().getActiveProjectGroup();
return getEffectiveMavenHome(grp);
}
/**
* @since 2.39
* @param grp
* @return
*/
public static @NonNull File getEffectiveMavenHome(ProjectGroup grp) {
Preferences grPref = getGroupedPreferences(grp);
String str = grPref != null ? grPref.get(PROP_COMMANDLINE_PATH, null) : null;
if (str == null) {
str = getPreferences().get(PROP_COMMANDLINE_PATH, null);
}
if (str != null) {
return FileUtil.normalizeFile(new File(str));
} else {
return getDefaultMavenHome();
}
}
public static void setMavenHome(File path) {
File oldValue = getMavenHome();
File defValue = getDefaultMavenHome();
if (oldValue.equals(path) || path == null && oldValue.equals(defValue)) {
//no change happened, prevent resetting the embedders
return;
}
if (path == null || path.equals(defValue)) {
getPreferences().remove(PROP_COMMANDLINE_PATH);
} else {
getPreferences().put(PROP_COMMANDLINE_PATH, FileUtil.normalizeFile(path).getAbsolutePath());
}
resetCachedEmbedders();
}
public static void setGroupedMavenHome(ProjectGroup grp, File path) {
File oldValue = getEffectiveMavenHome(grp);
File defValue = getMavenHome();
if (oldValue.equals(path) || path == null && oldValue.equals(defValue)) {
//no change happened, prevent resetting the embedders
return;
}
Preferences prefs = grp.preferencesForPackage(EmbedderFactory.class);
if (path == null || path.equals(defValue)) {
prefs.remove(PROP_COMMANDLINE_PATH);
} else {
prefs.put(PROP_COMMANDLINE_PATH, FileUtil.normalizeFile(path).getAbsolutePath());
}
resetCachedEmbedders();
}
static Map<String, String> getCustomGlobalUserProperties() {
//maybe set org.eclipse.aether.ConfigurationProperties.USER_AGENT with netbeans specific value.
Map<String, String> toRet = new HashMap<String, String>();
String options = getPreferences().get(PROP_DEFAULT_OPTIONS, "");
try {
String[] cmdlines = CommandLineUtils.translateCommandline(options);
if (cmdlines != null) {
for (String cmd : cmdlines) {
if (cmd != null && cmd.startsWith("-D")) {
cmd = cmd.substring("-D".length());
int ind = cmd.indexOf('=');
if (ind > -1) {
String key = cmd.substring(0, ind);
String val = cmd.substring(ind + 1);
toRet.put(key, val);
}
}
}
}
return toRet;
} catch (Exception ex) {
LOG.log(Level.FINE, "cannot parse " + options, ex);
return Collections.emptyMap();
}
}
private static File getSettingsXml() {
return new File(getEffectiveMavenHome(), "conf/settings.xml");
}
/**
* #191267: suppresses logging from embedded Maven, since interesting results normally appear elsewhere.
*/
private static class NbLoggerManager extends BaseLoggerManager {
protected @Override org.codehaus.plexus.logging.Logger createLogger(String name) {
int level = levelOf(LOG).intValue();
return new NbLogger(level <= Level.FINEST.intValue() ? org.codehaus.plexus.logging.Logger.LEVEL_DEBUG :
level <= Level.FINER.intValue() ? org.codehaus.plexus.logging.Logger.LEVEL_INFO :
level <= Level.FINE.intValue() ? org.codehaus.plexus.logging.Logger.LEVEL_WARN :
org.codehaus.plexus.logging.Logger.LEVEL_DISABLED,
name);
}
private Level levelOf(Logger log) {
Level lvl = log.getLevel();
if (lvl != null) {
return lvl;
} else {
Logger par = log.getParent();
if (par != null) {
return levelOf(par);
} else {
return Level.INFO;
}
}
}
private static class NbLogger extends org.codehaus.plexus.logging.AbstractLogger {
NbLogger(int threshold, String name) {
super(threshold, name);
LOG.log(Level.FINEST, "created Plexus logger {0} at threshold {1}", new Object[] {name, threshold});
}
private Logger logger() {
return Logger.getLogger(LOG.getName() + "." + getName());
}
public @Override void debug(String m, Throwable t) {
logger().log(Level.FINEST, m, t);
}
public @Override void info(String m, Throwable t) {
logger().log(Level.FINER, m, t);
}
public @Override void warn(String m, Throwable t) {
logger().log(Level.FINE, m, t);
}
public @Override void error(String m, Throwable t) {
logger().log(Level.FINE, m, t);
}
public @Override void fatalError(String m, Throwable t) {
logger().log(Level.FINE, m, t);
}
public @Override org.codehaus.plexus.logging.Logger getChildLogger(String name) {
return new NbLogger(getThreshold(), getName() + "." + name);
}
}
}
public static @NonNull MavenEmbedder createProjectLikeEmbedder() throws PlexusContainerException {
final String mavenCoreRealmId = "plexus.core";
ContainerConfiguration dpcreq = new DefaultContainerConfiguration()
.setClassWorld( new ClassWorld(mavenCoreRealmId, EmbedderFactory.class.getClassLoader()) )
.setClassPathScanning( PlexusConstants.SCANNING_INDEX )
.setName("maven");
DefaultPlexusContainer pc = new DefaultPlexusContainer(dpcreq, new ExtensionModule());
pc.setLoggerManager(new NbLoggerManager());
Properties userprops = new Properties();
userprops.putAll(getCustomGlobalUserProperties());
EmbedderConfiguration configuration = new EmbedderConfiguration(pc, cloneStaticProps(), userprops, true, getSettingsXml());
try {
return new MavenEmbedder(configuration);
//MEVENIDE-634 make all instances non-interactive
// WagonManager wagonManager = (WagonManager) embedder.getPlexusContainer().lookup(WagonManager.ROLE);
// wagonManager.setInteractive(false);
} catch (ComponentLookupException ex) {
throw new PlexusContainerException(ex.toString(), ex);
}
}
private static void rethrowThreadDeath(Throwable t) { // #201098
if (t instanceof ThreadDeath) {
throw (ThreadDeath) t;
}
Throwable t2 = t.getCause();
if (t2 != null) {
rethrowThreadDeath(t2);
}
}
private static final Properties statics = new Properties();
static Properties cloneStaticProps() {
synchronized (statics) {
if (statics.isEmpty()) { // not yet initialized
// Now a misnomer, but available to activate profiles only during NB project parse:
statics.setProperty("netbeans.execution", "true"); // NOI18N
EmbedderFactory.fillEnvVars(statics);
statics.putAll(excludeNetBeansProperties(System.getProperties()));
}
Properties toRet = new Properties();
toRet.putAll(statics);
return toRet;
}
}
@SuppressWarnings("element-type-mismatch")
static Properties excludeNetBeansProperties(Properties props) {
Properties toRet = new Properties();
for (Map.Entry<Object,Object> entry : props.entrySet()) {
if (!forbidden.contains(entry.getKey())) {
toRet.put(entry.getKey(), entry.getValue());
}
}
return toRet;
}
/**
* a simple way to tell if projectEmbedder is loaded or not.
* just for performance reasons if someone wants to skip processing because of risk of loading the embedder.
* Mostly applies to global services only.
* @return
* @since 2.35
*/
public static boolean isProjectEmbedderLoaded() {
return projectLoaded.get();
}
public static @NonNull MavenEmbedder getProjectEmbedder() {
synchronized (PROJECT_LOCK) {
if (project == null) {
try {
project = createProjectLikeEmbedder();
} catch (PlexusContainerException ex) {
rethrowThreadDeath(ex);
throw new IllegalStateException(ex);
}
projectLoaded.set(true);
}
return project;
}
}
public static @NonNull MavenEmbedder getOnlineEmbedder() {
synchronized (ONLINE_LOCK) {
if (online == null) {
try {
online = createOnlineEmbedder();
} catch (PlexusContainerException ex) {
rethrowThreadDeath(ex);
throw new IllegalStateException(ex);
}
}
return online;
}
}
/*public*/ @NonNull static MavenEmbedder createOnlineEmbedder() throws PlexusContainerException {
final String mavenCoreRealmId = "plexus.core";
ContainerConfiguration dpcreq = new DefaultContainerConfiguration()
.setClassWorld( new ClassWorld(mavenCoreRealmId, EmbedderFactory.class.getClassLoader()) )
.setClassPathScanning( PlexusConstants.SCANNING_INDEX )
.setName("maven");
DefaultPlexusContainer pc = new DefaultPlexusContainer(dpcreq);
pc.setLoggerManager(new NbLoggerManager());
Properties userprops = new Properties();
userprops.putAll(getCustomGlobalUserProperties());
EmbedderConfiguration req = new EmbedderConfiguration(pc, cloneStaticProps(), userprops, false, getSettingsXml());
// //TODO remove explicit activation
// req.addActiveProfile("netbeans-public").addActiveProfile("netbeans-private"); //NOI18N
// req.setConfigurationCustomizer(new ContainerCustomizer() {
//
// public void customize(PlexusContainer plexusContainer) {
// //MEVENIDE-634
// ComponentDescriptor desc = plexusContainer.getComponentDescriptor(KnownHostsProvider.ROLE, "file"); //NOI18N
// desc.getConfiguration().getChild("hostKeyChecking").setValue("no"); //NOI18N
//
// //MEVENIDE-634
// desc = plexusContainer.getComponentDescriptor(KnownHostsProvider.ROLE, "null"); //NOI18N
// desc.getConfiguration().getChild("hostKeyChecking").setValue("no"); //NOI18N
// }
// });
try {
return new MavenEmbedder(req);
//MEVENIDE-634 make all instances non-interactive
// WagonManager wagonManager = (WagonManager) embedder.getPlexusContainer().lookup(WagonManager.ROLE);
// wagonManager.setInteractive(false);
} catch (ComponentLookupException ex) {
throw new PlexusContainerException(ex.toString(), ex);
}
// try {
// //MEVENIDE-634 make all instances non-interactive
// WagonManager wagonManager = (WagonManager) embedder.getPlexusContainer().lookup(WagonManager.ROLE);
// wagonManager.setInteractive( false );
// wagonManager.setDownloadMonitor(new ProgressTransferListener());
// } catch (ComponentLookupException ex) {
// ErrorManager.getDefault().notify(ex);
// }
}
/**
* using this method one creates an ArtifactRepository instance with injected mirrors and proxies
* @param embedder
* @param url
* @param id
* @return
* @deprecated use MavenEmbedder.createRemoteRepository
*/
@Deprecated
public static ArtifactRepository createRemoteRepository(MavenEmbedder embedder, String url, String id) {
return embedder.createRemoteRepository(url, id);
}
/**
* Creates a list of POM models in an inheritance lineage.
* Each resulting model is "raw", so contains no interpolation or inheritance.
* In particular beware that groupId and/or version may be null if inherited from a parent; use {@link Model#getParent} to resolve.
* @param pom a POM to inspect
* @param embedder an embedder to use
* @return a list of models, starting with the specified POM, going through any parents, finishing with the Maven superpom (with a null artifactId)
* @throws ModelBuildingException if the POM or parents could not even be parsed; warnings are not reported
* @deprecated use MavenEmbedder.createModelLineage
*/
@Deprecated
public static List<Model> createModelLineage(File pom, MavenEmbedder embedder) throws ModelBuildingException {
return embedder.createModelLineage(pom);
}
/**
* Maven assumes the env vars are included in execution properties with the "env." prefix.
* @param properties
* @return
* @see EnvironmentUtils#addEnvVars
*/
public static Properties fillEnvVars(Properties properties) {
for (Map.Entry<String,String> entry : System.getenv().entrySet()) {
String key = entry.getKey();
if (BaseUtilities.isWindows()) {
key = key.toUpperCase(Locale.ENGLISH);
}
properties.setProperty("env." + key, entry.getValue());
}
return properties;
}
}