blob: c8aeedd4e35cbac60fe6cda08576decfae14e084 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2013 Sonatype, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Sonatype, Inc. - initial API and implementation
* Red Hat, Inc. - discover proposals for ILifecycleMappingRequirements
*******************************************************************************/
package org.eclipse.m2e.internal.discovery;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.osgi.framework.Bundle;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.discovery.Catalog;
import org.eclipse.equinox.internal.p2.discovery.model.CatalogItem;
import org.eclipse.equinox.internal.p2.ui.discovery.wizards.Messages;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.engine.IProfile;
import org.eclipse.equinox.p2.engine.IProfileRegistry;
import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.query.IQuery;
import org.eclipse.equinox.p2.query.IQueryResult;
import org.eclipse.equinox.p2.query.QueryUtil;
import org.eclipse.equinox.p2.ui.ProvisioningUI;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.window.Window;
import org.eclipse.osgi.util.NLS;
import org.eclipse.ui.statushandlers.StatusManager;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.MavenProject;
import org.eclipse.m2e.core.MavenPlugin;
import org.eclipse.m2e.core.internal.lifecyclemapping.LifecycleMappingFactory;
import org.eclipse.m2e.core.internal.lifecyclemapping.LifecycleMappingResult;
import org.eclipse.m2e.core.internal.lifecyclemapping.MappingMetadataSource;
import org.eclipse.m2e.core.internal.lifecyclemapping.SimpleMappingMetadataSource;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.ILifecycleMappingRequirement;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.IMavenDiscovery;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.IMavenDiscoveryProposal;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.MojoExecutionMappingConfiguration;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.MojoExecutionMappingConfiguration.MojoExecutionMappingRequirement;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.MojoExecutionMappingConfiguration.ProjectConfiguratorMappingRequirement;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.PackagingTypeMappingConfiguration;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.PackagingTypeMappingConfiguration.LifecycleStrategyMappingRequirement;
import org.eclipse.m2e.core.internal.lifecyclemapping.discovery.PackagingTypeMappingConfiguration.PackagingTypeMappingRequirement;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.LifecycleMappingMetadata;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.LifecycleMappingMetadataSource;
import org.eclipse.m2e.core.internal.lifecyclemapping.model.PluginExecutionMetadata;
import org.eclipse.m2e.core.lifecyclemapping.model.IPluginExecutionMetadata;
import org.eclipse.m2e.core.lifecyclemapping.model.PluginExecutionAction;
import org.eclipse.m2e.core.project.configurator.MojoExecutionKey;
import org.eclipse.m2e.core.ui.internal.wizards.IMavenDiscoveryUI;
import org.eclipse.m2e.internal.discovery.operation.MavenDiscoveryInstallOperation;
import org.eclipse.m2e.internal.discovery.wizards.MavenDiscoveryUi;
@SuppressWarnings({"restriction", "rawtypes"})
public class MavenDiscoveryService implements IMavenDiscoveryUI, IMavenDiscovery, ServiceFactory {
private static final Logger log = LoggerFactory.getLogger(MavenDiscoveryService.class);
public static class CatalogItemCacheEntry {
private final CatalogItem item;
private final LifecycleMappingMetadataSource metadataSource;
private final List<String> projectConfigurators;
private final List<String> mappingStrategies;
public CatalogItemCacheEntry(CatalogItem item, LifecycleMappingMetadataSource metadataSource,
List<String> projectConfigurators, List<String> mappingStrategies) {
this.item = item;
this.metadataSource = metadataSource;
this.projectConfigurators = projectConfigurators;
this.mappingStrategies = mappingStrategies;
}
public CatalogItem getItem() {
return item;
}
public LifecycleMappingMetadataSource getMetadataSource() {
return metadataSource;
}
public List<String> getProjectConfigurators() {
return projectConfigurators;
}
public List<String> getMappingStrategies() {
return mappingStrategies;
}
}
private List<CatalogItemCacheEntry> items;
/**
* Lock guarding lazy instantiation of item instance
*/
private final Object itemsLock = new Object();
public MavenDiscoveryService() {
this(true);
}
public MavenDiscoveryService(boolean factory) {
}
public Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> discover(final MavenProject mavenProject,
final List<MojoExecution> mojoExecutions, final List<IMavenDiscoveryProposal> preselected,
final IProgressMonitor monitor) throws CoreException {
initializeCatalog(monitor);
if(items == null) {
return Collections.emptyMap();
}
return MavenPlugin.getMaven().execute(
(context, monitor1) -> discover0(mavenProject, mojoExecutions, preselected, monitor1), monitor);
}
private void initializeCatalog(final IProgressMonitor monitor) {
synchronized(itemsLock) {
if(items == null) {
items = new ArrayList<MavenDiscoveryService.CatalogItemCacheEntry>();
Catalog catalog = MavenDiscovery.getCatalog();
IStatus status = catalog.performDiscovery(monitor);
if(!status.isOK()) {
log.error(status.toString());
return;
}
IProvisioningAgent p2agent = ProvisioningUI.getDefaultUI().getSession().getProvisioningAgent();
IProfileRegistry profRegistry = (IProfileRegistry) p2agent.getService(IProfileRegistry.SERVICE_NAME);
IProfile profile = profRegistry.getProfile(IProfileRegistry.SELF);
for(CatalogItem item : catalog.getItems()) {
LifecycleMappingMetadataSource metadataSource = MavenDiscovery.getLifecycleMappingMetadataSource(item);
List<String> projectConfigurators = new ArrayList<String>();
List<String> mappingStrategies = new ArrayList<String>();
MavenDiscovery.getProvidedProjectConfigurators(item, projectConfigurators, mappingStrategies);
if(metadataSource != null && !itemInstalled(profile, item, monitor)) {
addCatalogItem(item, metadataSource, projectConfigurators, mappingStrategies);
}
}
catalog.dispose();
}
}
}
/*package*/Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> discover0(MavenProject mavenProject,
List<MojoExecution> mojoExecutions, List<IMavenDiscoveryProposal> preselected, IProgressMonitor monitor)
throws CoreException {
Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> proposals = new LinkedHashMap<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>>();
Collection<CatalogItem> selectedItems = toCatalogItems(preselected);
List<LifecycleMappingMetadataSource> selectedSources = toMetadataSources(preselected);
Map<String, List<MappingMetadataSource>> metadataSourcesMap = LifecycleMappingFactory.getProjectMetadataSourcesMap(
mavenProject, null, mojoExecutions, false, monitor);
for(CatalogItemCacheEntry itemEntry : items) {
CatalogItem item = itemEntry.getItem();
LifecycleMappingMetadataSource src = itemEntry.getMetadataSource();
boolean preselectItem = false;
for(CatalogItem selectedItem : selectedItems) {
if(selectedItem.getSiteUrl().equals(item.getSiteUrl())
&& selectedItem.getInstallableUnits().equals(item.getInstallableUnits())) {
preselectItem = true;
break;
}
}
if(src != null) {
log.debug("Considering catalog item '{}' for project {}", item.getName(), mavenProject.getName()); //$NON-NLS-1$
src.setSource(item);
LifecycleMappingResult mappingResult = new LifecycleMappingResult();
List<LifecycleMappingMetadataSource> sources = new ArrayList<LifecycleMappingMetadataSource>(selectedSources);
if(!preselectItem) {
sources.add(src);
}
metadataSourcesMap.put("bundleMetadataSources",
Collections.singletonList((MappingMetadataSource) new SimpleMappingMetadataSource(sources)));
List<MappingMetadataSource> metadataSources = LifecycleMappingFactory.asList(metadataSourcesMap);
LifecycleMappingFactory.calculateEffectiveLifecycleMappingMetadata(mappingResult, metadataSources,
mavenProject, mojoExecutions, false, monitor);
LifecycleMappingMetadata lifecycleMappingMetadata = mappingResult.getLifecycleMappingMetadata();
if(lifecycleMappingMetadata != null) {
IMavenDiscoveryProposal proposal = getProposal(lifecycleMappingMetadata.getSource());
if(proposal != null) {
put(proposals,
new PackagingTypeMappingConfiguration.PackagingTypeMappingRequirement(mavenProject.getPackaging()),
proposal);
} else if(!LifecycleMappingFactory.getLifecycleMappingExtensions().containsKey(
lifecycleMappingMetadata.getLifecycleMappingId())) {
if(itemEntry.getMappingStrategies().contains(lifecycleMappingMetadata.getLifecycleMappingId())) {
put(proposals, new PackagingTypeMappingConfiguration.LifecycleStrategyMappingRequirement(
lifecycleMappingMetadata.getPackagingType(), lifecycleMappingMetadata.getLifecycleMappingId()),
new InstallCatalogItemMavenDiscoveryProposal(item));
}
}
}
for(Map.Entry<MojoExecutionKey, List<IPluginExecutionMetadata>> entry : mappingResult.getMojoExecutionMapping()
.entrySet()) {
if(entry.getValue() != null) {
for(IPluginExecutionMetadata executionMapping : entry.getValue()) {
log.debug("mapping proposal {} => {}", entry.getKey().toString(), executionMapping.getAction().toString()); //$NON-NLS-1$
IMavenDiscoveryProposal proposal = getProposal(((PluginExecutionMetadata) executionMapping).getSource());
if(proposal != null) {
// assumes installation of mapping proposal installs all required project configurators
put(proposals, new MojoExecutionMappingConfiguration.MojoExecutionMappingRequirement(entry.getKey()),
proposal);
} else if(executionMapping.getAction() == PluginExecutionAction.configurator) {
// we have <configurator/> mapping from pom.xml
String configuratorId = LifecycleMappingFactory.getProjectConfiguratorId(executionMapping);
if(!LifecycleMappingFactory.getProjectConfiguratorExtensions().containsKey(configuratorId)) {
// User Story.
// Project pom.xml explicitly specifies lifecycle mapping strategy implementation,
// but the implementation is not currently installed. As a user I expect m2e to search
// marketplace for the implementation and offer installation if available
if(itemEntry.getProjectConfigurators().contains(configuratorId)) {
put(proposals,
new MojoExecutionMappingConfiguration.ProjectConfiguratorMappingRequirement(entry.getKey(),
configuratorId), new InstallCatalogItemMavenDiscoveryProposal(item));
}
}
}
}
}
}
}
}
return proposals;
}
/**
* Returns true if all IUs specified in the catalog item are installed in the profile
*/
public boolean itemInstalled(IProfile profile, CatalogItem item, IProgressMonitor monitor) {
if(profile == null) {
return false;
}
List<IQuery<IInstallableUnit>> queries = new ArrayList<IQuery<IInstallableUnit>>();
for(String iuId : item.getInstallableUnits()) {
queries.add(QueryUtil.createIUQuery(iuId));
}
IQueryResult<IInstallableUnit> result = profile.query(QueryUtil.createCompoundQuery(queries, true), monitor);
return !result.isEmpty();
}
public void addCatalogItem(CatalogItem item, LifecycleMappingMetadataSource metadataSource,
List<String> projectConfigurators, List<String> mappingStrategies) {
if(items == null) {
// for tests
items = new ArrayList<MavenDiscoveryService.CatalogItemCacheEntry>();
}
items.add(new CatalogItemCacheEntry(item, metadataSource, projectConfigurators, mappingStrategies));
}
private IMavenDiscoveryProposal getProposal(LifecycleMappingMetadataSource src) {
if(src == null) {
return null;
}
if(src.getSource() instanceof CatalogItem) {
return new InstallCatalogItemMavenDiscoveryProposal((CatalogItem) src.getSource());
}
return null;
}
private List<LifecycleMappingMetadataSource> toMetadataSources(List<IMavenDiscoveryProposal> proposals) {
List<LifecycleMappingMetadataSource> sources = new ArrayList<LifecycleMappingMetadataSource>();
for(IMavenDiscoveryProposal proposal : proposals) {
if(proposal instanceof InstallCatalogItemMavenDiscoveryProposal) {
CatalogItem catalogItem = ((InstallCatalogItemMavenDiscoveryProposal) proposal).getCatalogItem();
LifecycleMappingMetadataSource source = MavenDiscovery.getLifecycleMappingMetadataSource(catalogItem);
source.setSource(catalogItem);
sources.add(source);
}
}
return sources;
}
private void put(Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> allproposals,
ILifecycleMappingRequirement requirement, IMavenDiscoveryProposal proposal) {
List<IMavenDiscoveryProposal> proposals = allproposals.get(requirement);
if(proposals == null) {
proposals = new ArrayList<IMavenDiscoveryProposal>();
allproposals.put(requirement, proposals);
}
if(!proposals.contains(proposal)) {
proposals.add(proposal);
}
}
public boolean implement(List<IMavenDiscoveryProposal> proposals, IRunnableWithProgress postInstallHook,
IRunnableContext context, Collection<String> projectsToConfigure) {
try {
MavenDiscoveryInstallOperation runner = new MavenDiscoveryInstallOperation(toCatalogItems(proposals),
postInstallHook, true, false, projectsToConfigure);
context.run(true, true, runner);
int openInstallWizard = MavenDiscoveryUi.openInstallWizard(runner.getOperation(), true, context);
return openInstallWizard == Window.OK;
} catch(InvocationTargetException e) {
IStatus status = new Status(IStatus.ERROR, DiscoveryActivator.PLUGIN_ID, NLS.bind(
Messages.ConnectorDiscoveryWizard_installProblems, new Object[] {e.getCause().getMessage()}), e.getCause());
StatusManager.getManager().handle(status, StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG);
return false;
} catch(InterruptedException e) {
// canceled
return false;
}
}
private Collection<CatalogItem> toCatalogItems(List<IMavenDiscoveryProposal> proposals) {
if(proposals == null) {
return Collections.emptyList();
}
Set<CatalogItem> items = new HashSet<CatalogItem>(proposals.size());
for(IMavenDiscoveryProposal proposal : proposals) {
if(proposal instanceof InstallCatalogItemMavenDiscoveryProposal) {
items.add(((InstallCatalogItemMavenDiscoveryProposal) proposal).getCatalogItem());
}
}
return items;
}
public Object getService(Bundle bundle, ServiceRegistration registration) {
return new MavenDiscoveryService(false); // not a factory instance
}
public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
}
/* (non-Javadoc)
* @see org.eclipse.m2e.core.internal.lifecyclemapping.discovery.IMavenDiscovery#discover(java.util.Collection, java.util.List, org.eclipse.core.runtime.IProgressMonitor)
*/
public Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> discover(
Collection<ILifecycleMappingRequirement> requirements, List<IMavenDiscoveryProposal> preselected,
IProgressMonitor monitor) throws CoreException {
if(requirements == null || requirements.isEmpty()) {
return Collections.emptyMap();
}
initializeCatalog(monitor);
if(items == null) {
return Collections.emptyMap();
}
Collection<CatalogItem> selectedItems = toCatalogItems(preselected);
Map<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>> allproposals = new LinkedHashMap<ILifecycleMappingRequirement, List<IMavenDiscoveryProposal>>(
requirements.size());
for(CatalogItemCacheEntry itemEntry : items) {
if(monitor.isCanceled()) {
break;
}
CatalogItem item = itemEntry.getItem();
if(selectedItems.contains(item)) {
continue;
}
LifecycleMappingMetadataSource src = itemEntry.getMetadataSource();
log.debug("Considering catalog item '{}'", item.getName()); //$NON-NLS-1$
for(ILifecycleMappingRequirement requirement : requirements) {
boolean matchFound = false;
if(requirement instanceof MojoExecutionMappingRequirement) {
MojoExecutionMappingRequirement meReq = ((MojoExecutionMappingRequirement) requirement);
MojoExecutionKey mek = meReq.getExecution();
if(matchesFilter(src, mek, meReq.getPackaging())) {
matchFound = true;
}
} else if(requirement instanceof PackagingTypeMappingRequirement) {
String packaging = ((PackagingTypeMappingRequirement) requirement).getPackaging();
if(hasPackaging(src, packaging)) {
matchFound = true;
}
} else if(requirement instanceof LifecycleStrategyMappingRequirement) {
String mappingId = ((LifecycleStrategyMappingRequirement) requirement).getLifecycleMappingId();
if(itemEntry.getMappingStrategies().contains(mappingId)) {
matchFound = true;
}
} else if(requirement instanceof ProjectConfiguratorMappingRequirement) {
String configuratorId = ((ProjectConfiguratorMappingRequirement) requirement).getProjectConfiguratorId();
if(itemEntry.getProjectConfigurators().contains(configuratorId)) {
matchFound = true;
}
}
if(matchFound) {
IMavenDiscoveryProposal proposal = new InstallCatalogItemMavenDiscoveryProposal(item);
put(allproposals, requirement, proposal);
}
}
}
return allproposals;
}
private static boolean matchesFilter(LifecycleMappingMetadataSource src, MojoExecutionKey mojoExecution, String type) {
for(PluginExecutionMetadata p : src.getPluginExecutions()) {
if(p.getFilter().match(mojoExecution)) {
return true;
}
}
for(LifecycleMappingMetadata lm : src.getLifecycleMappings()) {
if((type == null && lm.getPackagingType() == null) || (type != null && type.equals(lm.getPackagingType()))) {
for(PluginExecutionMetadata p : lm.getPluginExecutions()) {
if(p.getFilter().match(mojoExecution)) {
return true;
}
}
}
}
return false;
}
private static boolean hasPackaging(LifecycleMappingMetadataSource lifecycleMappingMetadataSource,
String packagingType) {
for(LifecycleMappingMetadata lifecycleMappingMetadata : lifecycleMappingMetadataSource.getLifecycleMappings()) {
if(packagingType.equals(lifecycleMappingMetadata.getPackagingType())) {
return true;
}
}
return false;
}
}