[maven] IDEA-91662 doesn't take into account mirror repositories as Indexed Maven...
[idea/community.git] / plugins / maven / maven2-server-impl / src / org / jetbrains / idea / maven / server / embedder / Maven2ServerEmbedderImpl.java
1 // Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package org.jetbrains.idea.maven.server.embedder;
3
4 import com.intellij.openapi.util.text.StringUtilRt;
5 import com.intellij.util.Function;
6 import com.intellij.util.containers.ContainerUtilRt;
7 import org.apache.maven.artifact.Artifact;
8 import org.apache.maven.artifact.DefaultArtifact;
9 import org.apache.maven.artifact.InvalidRepositoryException;
10 import org.apache.maven.artifact.factory.ArtifactFactory;
11 import org.apache.maven.artifact.handler.DefaultArtifactHandler;
12 import org.apache.maven.artifact.manager.WagonManager;
13 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
14 import org.apache.maven.artifact.repository.ArtifactRepository;
15 import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
16 import org.apache.maven.artifact.repository.DefaultArtifactRepository;
17 import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
18 import org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager;
19 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
20 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
21 import org.apache.maven.artifact.resolver.ArtifactResolver;
22 import org.apache.maven.artifact.resolver.ResolutionListener;
23 import org.apache.maven.artifact.versioning.VersionRange;
24 import org.apache.maven.model.Activation;
25 import org.apache.maven.model.Model;
26 import org.apache.maven.model.Plugin;
27 import org.apache.maven.model.Profile;
28 import org.apache.maven.plugin.PluginManager;
29 import org.apache.maven.plugin.descriptor.PluginDescriptor;
30 import org.apache.maven.profiles.activation.*;
31 import org.apache.maven.project.*;
32 import org.apache.maven.project.artifact.ProjectArtifactFactory;
33 import org.apache.maven.project.inheritance.DefaultModelInheritanceAssembler;
34 import org.apache.maven.project.injection.DefaultProfileInjector;
35 import org.apache.maven.project.interpolation.AbstractStringBasedModelInterpolator;
36 import org.apache.maven.project.interpolation.ModelInterpolationException;
37 import org.apache.maven.project.interpolation.ModelInterpolator;
38 import org.apache.maven.project.path.DefaultPathTranslator;
39 import org.apache.maven.project.path.PathTranslator;
40 import org.apache.maven.project.validation.ModelValidationResult;
41 import org.apache.maven.shared.dependency.tree.DependencyNode;
42 import org.apache.maven.shared.dependency.tree.DependencyTreeResolutionListener;
43 import org.codehaus.plexus.PlexusContainer;
44 import org.codehaus.plexus.component.repository.ComponentDependency;
45 import org.codehaus.plexus.context.ContextException;
46 import org.codehaus.plexus.context.DefaultContext;
47 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
48 import org.jetbrains.annotations.NotNull;
49 import org.jetbrains.annotations.Nullable;
50 import org.jetbrains.idea.maven.model.*;
51 import org.jetbrains.idea.maven.server.*;
52 import org.jetbrains.idea.maven.server.security.MavenToken;
53 import org.jetbrains.maven.embedder.MavenEmbedder;
54 import org.jetbrains.maven.embedder.MavenEmbedderSettings;
55 import org.jetbrains.maven.embedder.MavenExecutionResult;
56 import org.jetbrains.maven.embedder.PlexusComponentConfigurator;
57
58 import java.io.File;
59 import java.lang.reflect.Field;
60 import java.lang.reflect.InvocationTargetException;
61 import java.rmi.RemoteException;
62 import java.rmi.server.UnicastRemoteObject;
63 import java.util.*;
64 import java.util.concurrent.*;
65
66 import static org.jetbrains.idea.maven.server.embedder.Maven2ModelConverter.convertRemoteRepositories;
67
68 public final class Maven2ServerEmbedderImpl extends MavenRemoteObject implements MavenServerEmbedder {
69   private final MavenEmbedder myImpl;
70   private final Maven2ServerConsoleWrapper myConsoleWrapper;
71   private volatile MavenServerProgressIndicator myCurrentIndicator;
72
73   private Maven2ServerEmbedderImpl(MavenEmbedder impl, Maven2ServerConsoleWrapper consoleWrapper) {
74     myImpl = impl;
75     myConsoleWrapper = consoleWrapper;
76   }
77
78   public static Maven2ServerEmbedderImpl create(MavenServerSettings facadeSettings) throws RemoteException {
79     MavenEmbedderSettings settings = new MavenEmbedderSettings();
80
81     List<String> commandLineOptions = new ArrayList<String>();
82     String mavenEmbedderCliOptions = System.getProperty(MavenServerEmbedder.MAVEN_EMBEDDER_CLI_ADDITIONAL_ARGS);
83     if (mavenEmbedderCliOptions != null) {
84       commandLineOptions.addAll(StringUtilRt.splitHonorQuotes(mavenEmbedderCliOptions, ' '));
85     }
86
87     settings.setConfigurator(new PlexusComponentConfigurator() {
88       @Override
89       public void configureComponents(@NotNull PlexusContainer c) {
90         setupContainer(c);
91       }
92     });
93     Maven2ServerConsoleWrapper consoleWrapper = new Maven2ServerConsoleWrapper();
94     consoleWrapper.setThreshold(facadeSettings.getLoggingLevel());
95     settings.setLogger(consoleWrapper);
96     settings.setRecursive(false);
97
98     settings.setWorkOffline(facadeSettings.isOffline());
99     settings.setUsePluginRegistry(false);
100
101     String mavenHomePath = facadeSettings.getMavenHomePath();
102     if (mavenHomePath != null) {
103       settings.setMavenHomePath(facadeSettings.getMavenHomePath());
104     }
105
106     settings.setUserSettingsPath(facadeSettings.getUserSettingsPath());
107     settings.setGlobalSettingsPath(facadeSettings.getGlobalSettingsPath());
108     settings.setLocalRepositoryPath(facadeSettings.getLocalRepositoryPath());
109
110     if (commandLineOptions.contains("-U") || commandLineOptions.contains("--update-snapshots")) {
111       settings.setSnapshotUpdatePolicy(MavenEmbedderSettings.UpdatePolicy.ALWAYS_UPDATE);
112     }
113     else {
114       settings.setSnapshotUpdatePolicy(convertUpdatePolicy(facadeSettings.getSnapshotUpdatePolicy()));
115     }
116     settings.setPluginUpdatePolicy(convertUpdatePolicy(facadeSettings.getPluginUpdatePolicy()));
117     settings.setProperties(MavenServerUtil.collectSystemProperties());
118
119     return new Maven2ServerEmbedderImpl(MavenEmbedder.create(settings), consoleWrapper);
120   }
121
122   private static MavenEmbedderSettings.UpdatePolicy convertUpdatePolicy(MavenServerSettings.UpdatePolicy policy) throws RemoteException {
123     switch (policy) {
124       case ALWAYS_UPDATE:
125         return MavenEmbedderSettings.UpdatePolicy.ALWAYS_UPDATE;
126       case DO_NOT_UPDATE:
127         return MavenEmbedderSettings.UpdatePolicy.DO_NOT_UPDATE;
128       default:
129         Maven2ServerGlobals.getLogger().error(new Throwable("unexpected update policy"));
130     }
131     return MavenEmbedderSettings.UpdatePolicy.DO_NOT_UPDATE;
132   }
133
134   private static Collection<String> collectProfilesIds(List<Profile> profiles) {
135     Collection<String> result = new HashSet<String>();
136     for (Profile each : profiles) {
137       if (each.getId() != null) {
138         result.add(each.getId());
139       }
140     }
141     return result;
142   }
143
144   @NotNull
145   public static MavenModel interpolateAndAlignModel(MavenModel model, File basedir) throws RemoteException {
146     Model result = Maven2ModelConverter.toNativeModel(model);
147     result = doInterpolate(result, basedir);
148
149     PathTranslator pathTranslator = new DefaultPathTranslator();
150     pathTranslator.alignToBaseDirectory(result, basedir);
151
152     return Maven2ModelConverter.convertModel(result, null);
153   }
154
155   private static Model doInterpolate(Model result, File basedir) throws RemoteException {
156     try {
157       AbstractStringBasedModelInterpolator interpolator = new CustomModelInterpolator(new DefaultPathTranslator());
158       interpolator.initialize();
159
160       Properties props = MavenServerUtil.collectSystemProperties();
161       ProjectBuilderConfiguration config = new DefaultProjectBuilderConfiguration().setExecutionProperties(props);
162       result = interpolator.interpolate(result, basedir, config, false);
163     }
164     catch (ModelInterpolationException e) {
165       Maven2ServerGlobals.getLogger().warn(e);
166     }
167     catch (InitializationException e) {
168       Maven2ServerGlobals.getLogger().error(e);
169     }
170     return result;
171   }
172
173   public static MavenModel assembleInheritance(MavenModel model, MavenModel parentModel) throws RemoteException {
174     Model result = Maven2ModelConverter.toNativeModel(model);
175     new DefaultModelInheritanceAssembler().assembleModelInheritance(result, Maven2ModelConverter.toNativeModel(parentModel));
176     return Maven2ModelConverter.convertModel(result, null);
177   }
178
179   public static ProfileApplicationResult applyProfiles(MavenModel model,
180                                                        File basedir,
181                                                        MavenExplicitProfiles explicitProfiles,
182                                                        Collection<String> alwaysOnProfiles) throws RemoteException {
183     Model nativeModel = Maven2ModelConverter.toNativeModel(model);
184
185     Collection<String> enabledProfiles = explicitProfiles.getEnabledProfiles();
186     Collection<String> disabledProfiles = explicitProfiles.getDisabledProfiles();
187     List<Profile> activatedPom = new ArrayList<Profile>();
188     List<Profile> activatedExternal = new ArrayList<Profile>();
189     List<Profile> activeByDefault = new ArrayList<Profile>();
190
191     List<Profile> rawProfiles = nativeModel.getProfiles();
192     List<Profile> expandedProfilesCache = null;
193     List<Profile> deactivatedProfiles = new ArrayList<Profile>();
194
195     for (int i = 0; i < rawProfiles.size(); i++) {
196       Profile eachRawProfile = rawProfiles.get(i);
197
198       if (disabledProfiles.contains(eachRawProfile.getId())) {
199         deactivatedProfiles.add(eachRawProfile);
200         continue;
201       }
202
203       boolean shouldAdd = enabledProfiles.contains(eachRawProfile.getId()) || alwaysOnProfiles.contains(eachRawProfile.getId());
204
205       Activation activation = eachRawProfile.getActivation();
206       if (activation != null) {
207         if (activation.isActiveByDefault()) {
208           activeByDefault.add(eachRawProfile);
209         }
210
211         // expand only if necessary
212         if (expandedProfilesCache == null) expandedProfilesCache = doInterpolate(nativeModel, basedir).getProfiles();
213         Profile eachExpandedProfile = expandedProfilesCache.get(i);
214
215         for (ProfileActivator eachActivator : getProfileActivators(basedir)) {
216           try {
217             if (eachActivator.canDetermineActivation(eachExpandedProfile) && eachActivator.isActive(eachExpandedProfile)) {
218               shouldAdd = true;
219               break;
220             }
221           }
222           catch (ProfileActivationException e) {
223             Maven2ServerGlobals.getLogger().warn(e);
224           }
225         }
226       }
227
228       if (shouldAdd) {
229         if (MavenConstants.PROFILE_FROM_POM.equals(eachRawProfile.getSource())) {
230           activatedPom.add(eachRawProfile);
231         }
232         else {
233           activatedExternal.add(eachRawProfile);
234         }
235       }
236     }
237
238     List<Profile> activatedProfiles = new ArrayList<Profile>(activatedPom.isEmpty() ? activeByDefault : activatedPom);
239     activatedProfiles.addAll(activatedExternal);
240
241     for (Profile each : activatedProfiles) {
242       new DefaultProfileInjector().inject(each, nativeModel);
243     }
244
245     return new ProfileApplicationResult(Maven2ModelConverter.convertModel(nativeModel, null),
246                                         new MavenExplicitProfiles(collectProfilesIds(activatedProfiles),
247                                                                   collectProfilesIds(deactivatedProfiles))
248     );
249   }
250
251   private static ProfileActivator[] getProfileActivators(File basedir) throws RemoteException {
252     SystemPropertyProfileActivator sysPropertyActivator = new SystemPropertyProfileActivator();
253     DefaultContext context = new DefaultContext();
254     context.put("SystemProperties", MavenServerUtil.collectSystemProperties());
255     try {
256       sysPropertyActivator.contextualize(context);
257     }
258     catch (ContextException e) {
259       Maven2ServerGlobals.getLogger().error(e);
260       return new ProfileActivator[0];
261     }
262
263     return new ProfileActivator[]{new MyFileProfileActivator(basedir),
264       sysPropertyActivator,
265       new JdkPrefixProfileActivator(),
266       new OperatingSystemProfileActivator()};
267   }
268
269   private static void setupContainer(PlexusContainer c) {
270     MavenEmbedder.setImplementation(c, ArtifactFactory.class, CustomArtifactFactory.class);
271     MavenEmbedder.setImplementation(c, ProjectArtifactFactory.class, CustomArtifactFactory.class);
272     MavenEmbedder.setImplementation(c, ArtifactResolver.class, CustomArtifactResolver.class);
273     MavenEmbedder.setImplementation(c, RepositoryMetadataManager.class, CustomRepositoryMetadataManager.class);
274     MavenEmbedder.setImplementation(c, WagonManager.class, CustomWagonManager.class);
275     MavenEmbedder.setImplementation(c, ModelInterpolator.class, CustomModelInterpolator.class);
276   }
277
278   @Override
279   @NotNull
280   public Collection<MavenServerExecutionResult> resolveProject(@NotNull final Collection<File> files,
281                                                                @NotNull final Collection<String> activeProfiles,
282                                                                @NotNull final Collection<String> inactiveProfiles, MavenToken token) {
283     MavenServerUtil.checkToken(token);
284     return ContainerUtilRt.map2List(files, new Function<File, MavenServerExecutionResult>() {
285       @Override
286       public MavenServerExecutionResult fun(final File file) {
287         try {
288           return doExecute(new Executor<MavenServerExecutionResult>() {
289             @NotNull
290             @Override
291             public MavenServerExecutionResult execute() throws Exception {
292               DependencyTreeResolutionListener listener = new DependencyTreeResolutionListener(myConsoleWrapper);
293               MavenExecutionResult result = myImpl.resolveProject(file,
294                                                                   new ArrayList<String>(activeProfiles),
295                                                                   new ArrayList<String>(inactiveProfiles),
296                                                                   Collections.<ResolutionListener>singletonList(listener));
297               return createExecutionResult(file, result, listener.getRootNode());
298             }
299           });
300         }
301         catch (MavenServerProcessCanceledException e) {
302           throw new RuntimeException(e);
303         }
304         catch (RemoteException e) {
305           throw new RuntimeException(e);
306         }
307       }
308     });
309   }
310
311   @NotNull
312   private MavenServerExecutionResult createExecutionResult(File file, MavenExecutionResult result, DependencyNode rootNode)
313     throws RemoteException {
314     Collection<MavenProjectProblem> problems = MavenProjectProblem.createProblemsList();
315     Set<MavenId> unresolvedArtifacts = new HashSet<MavenId>();
316
317     validate(file, result.getExceptions(), problems, unresolvedArtifacts);
318
319     MavenProject mavenProject = result.getMavenProject();
320     if (mavenProject == null) return new MavenServerExecutionResult(null, problems, unresolvedArtifacts);
321
322     MavenModel model = Maven2ModelConverter.convertModel(mavenProject.getModel(),
323                                                          mavenProject.getCompileSourceRoots(),
324                                                          mavenProject.getTestCompileSourceRoots(),
325                                                          mavenProject.getArtifacts(),
326                                                          (rootNode == null ? Collections.emptyList() : rootNode.getChildren()),
327                                                          mavenProject.getExtensionArtifacts(),
328                                                          getLocalRepositoryFile());
329
330     List<MavenRemoteRepository> remoteRepositories = convertRemoteRepositories(mavenProject.getRemoteArtifactRepositories());
331     model.setRemoteRepositories(remoteRepositories);
332
333     RemoteNativeMavenProjectHolder holder = new RemoteNativeMavenProjectHolder(mavenProject);
334     try {
335       UnicastRemoteObject.exportObject(holder, 0);
336     }
337     catch (RemoteException e) {
338       throw new RuntimeException(e);
339     }
340
341     Collection<String> activatedProfiles = collectActivatedProfiles(mavenProject);
342
343     MavenServerExecutionResult.ProjectData data = new MavenServerExecutionResult.ProjectData(
344       model, Maven2ModelConverter.convertToMap(mavenProject.getModel()), holder, activatedProfiles);
345     return new MavenServerExecutionResult(data, problems, unresolvedArtifacts);
346   }
347
348   private Collection<String> collectActivatedProfiles(MavenProject mavenProject) {
349     // for some reason project's active profiles do not contain parent's profiles - only local and settings'.
350     // parent's profiles do not contain settings' profiles.
351
352     List<Profile> profiles = new ArrayList<Profile>();
353     while (mavenProject != null) {
354       if (profiles != null) {
355         profiles.addAll(mavenProject.getActiveProfiles());
356       }
357       mavenProject = mavenProject.getParent();
358     }
359     return collectProfilesIds(profiles);
360   }
361
362   @Override
363   @Nullable
364   public String evaluateEffectivePom(@NotNull File file,
365                                      @NotNull List<String> activeProfiles,
366                                      @NotNull List<String> inactiveProfiles,
367                                      MavenToken token) {
368     MavenServerUtil.checkToken(token);
369     throw new UnsupportedOperationException();
370   }
371
372   @Override
373   @NotNull
374   public MavenArtifact resolve(@NotNull final MavenArtifactInfo info,
375                                @NotNull final List<MavenRemoteRepository> remoteRepositories, MavenToken token)
376     throws MavenServerProcessCanceledException, RemoteException {
377     MavenServerUtil.checkToken(token);
378     return doExecute(new Executor<MavenArtifact>() {
379       @NotNull
380       @Override
381       public MavenArtifact execute() throws Exception {
382         return doResolve(info, remoteRepositories);
383       }
384     });
385   }
386
387   @Override
388   @NotNull
389   public List<MavenArtifact> resolveTransitively(@NotNull final List<MavenArtifactInfo> artifacts,
390                                                  @NotNull final List<MavenRemoteRepository> remoteRepositories, MavenToken token) throws RemoteException {
391     MavenServerUtil.checkToken(token);
392     try {
393       Set<Artifact> toResolve = new LinkedHashSet<Artifact>();
394       for (MavenArtifactInfo each : artifacts) {
395         toResolve.add(createArtifact(each));
396       }
397
398       return Maven2ModelConverter.convertArtifacts(myImpl.resolveTransitively(toResolve, convertRepositories(remoteRepositories)),
399                                                    new HashMap<Artifact, MavenArtifact>(), getLocalRepositoryFile());
400     }
401     catch (ArtifactResolutionException e) {
402       Maven2ServerGlobals.getLogger().info(e);
403     }
404     catch (ArtifactNotFoundException e) {
405       Maven2ServerGlobals.getLogger().info(e);
406     }
407     catch (Exception e) {
408       throw rethrowException(e);
409     }
410     return Collections.emptyList();
411   }
412
413   @NotNull
414   private MavenArtifact doResolve(MavenArtifactInfo info, List<MavenRemoteRepository> remoteRepositories) throws RemoteException {
415     Artifact resolved = doResolve(createArtifact(info), convertRepositories(remoteRepositories));
416     return Maven2ModelConverter.convertArtifact(resolved, getLocalRepositoryFile());
417   }
418
419   private Artifact createArtifact(MavenArtifactInfo info) {
420     return getComponent(ArtifactFactory.class).createArtifactWithClassifier(info.getGroupId(),
421                                                                             info.getArtifactId(),
422                                                                             info.getVersion(),
423                                                                             info.getPackaging(),
424                                                                             info.getClassifier());
425   }
426
427   private Artifact doResolve(Artifact artifact, List<ArtifactRepository> remoteRepositories) throws RemoteException {
428     try {
429       myImpl.resolve(artifact, remoteRepositories);
430       return artifact;
431     }
432     catch (Exception e) {
433       Maven2ServerGlobals.getLogger().info(e);
434     }
435     return artifact;
436   }
437
438   private List<ArtifactRepository> convertRepositories(List<MavenRemoteRepository> repositories) throws RemoteException {
439     List<ArtifactRepository> result = new ArrayList<ArtifactRepository>();
440     for (MavenRemoteRepository each : repositories) {
441       try {
442         ArtifactRepositoryFactory factory = getComponent(ArtifactRepositoryFactory.class);
443         result.add(ProjectUtils.buildArtifactRepository(Maven2ModelConverter.toNativeRepository(each), factory, getContainer()));
444       }
445       catch (InvalidRepositoryException e) {
446         Maven2ServerGlobals.getLogger().warn(e);
447       }
448     }
449     return result;
450   }
451
452   @Override
453   public Collection<MavenArtifact> resolvePlugin(@NotNull final MavenPlugin plugin,
454                                                  @NotNull final List<MavenRemoteRepository> repositories,
455                                                  final int nativeMavenProjectId,
456                                                  final boolean transitive, MavenToken token) throws MavenServerProcessCanceledException, RemoteException {
457     MavenServerUtil.checkToken(token);
458     return doExecute(new Executor<Collection<MavenArtifact>>() {
459       @NotNull
460       @Override
461       public Collection<MavenArtifact> execute() throws Exception {
462         try {
463           Plugin mavenPlugin = new Plugin();
464           mavenPlugin.setGroupId(plugin.getGroupId());
465           mavenPlugin.setArtifactId(plugin.getArtifactId());
466           mavenPlugin.setVersion(plugin.getVersion());
467           MavenProject project = RemoteNativeMavenProjectHolder.findProjectById(nativeMavenProjectId);
468           PluginDescriptor result = getComponent(PluginManager.class).verifyPlugin(mavenPlugin, project,
469                                                                                    myImpl.getSettings(), myImpl.getLocalRepository());
470
471           Map<MavenArtifactInfo, MavenArtifact> resolvedArtifacts = new HashMap<MavenArtifactInfo, MavenArtifact>();
472
473           Artifact pluginArtifact = result.getPluginArtifact();
474
475           MavenArtifactInfo artifactInfo = new MavenArtifactInfo(pluginArtifact.getGroupId(),
476                                                                  pluginArtifact.getArtifactId(),
477                                                                  pluginArtifact.getVersion(),
478                                                                  pluginArtifact.getType(), null);
479
480           resolveIfNecessary(artifactInfo, repositories, resolvedArtifacts);
481
482           if (transitive) {
483             // todo try to use parallel downloading
484             for (Artifact each : (Iterable<Artifact>)result.getIntroducedDependencyArtifacts()) {
485               resolveIfNecessary(new MavenArtifactInfo(each.getGroupId(), each.getArtifactId(), each.getVersion(), each.getType(), null),
486                                  repositories, resolvedArtifacts);
487             }
488             for (ComponentDependency each : (List<ComponentDependency>)result.getDependencies()) {
489               resolveIfNecessary(new MavenArtifactInfo(each.getGroupId(), each.getArtifactId(), each.getVersion(), each.getType(), null),
490                                  repositories, resolvedArtifacts);
491             }
492           }
493
494           return new HashSet<MavenArtifact>(resolvedArtifacts.values());
495         }
496         catch (Exception e) {
497           Maven2ServerGlobals.getLogger().info(e);
498           return Collections.emptyList();
499         }
500       }
501     });
502   }
503
504   private void resolveIfNecessary(MavenArtifactInfo info,
505                                   List<MavenRemoteRepository> repos,
506                                   Map<MavenArtifactInfo, MavenArtifact> resolvedArtifacts) throws RemoteException {
507     if (resolvedArtifacts.containsKey(info)) return;
508     resolvedArtifacts.put(info, doResolve(info, repos));
509   }
510
511   @NotNull
512   @Override
513   public MavenServerExecutionResult execute(@NotNull final File file,
514                                             @NotNull final Collection<String> activeProfiles,
515                                             @NotNull final Collection<String> inactiveProfiles,
516                                             @NotNull final List<String> goals,
517                                             @NotNull final List<String> selectedProjects,
518                                             final boolean alsoMake,
519                                             final boolean alsoMakeDependents, MavenToken token) throws RemoteException, MavenServerProcessCanceledException {
520     MavenServerUtil.checkToken(token);
521     return doExecute(new Executor<MavenServerExecutionResult>() {
522       @NotNull
523       @Override
524       public MavenServerExecutionResult execute() throws Exception {
525         MavenExecutionResult result = myImpl
526           .execute(file, new ArrayList<String>(activeProfiles), new ArrayList<String>(inactiveProfiles), goals, selectedProjects, alsoMake,
527                    alsoMakeDependents);
528         return createExecutionResult(file, result, null);
529       }
530     });
531   }
532
533   private void validate(File file,
534                         Collection<Exception> exceptions,
535                         Collection<MavenProjectProblem> problems,
536                         Collection<MavenId> unresolvedArtifacts) throws RemoteException {
537     for (Exception each : exceptions) {
538       Maven2ServerGlobals.getLogger().info(each);
539
540       if (each instanceof InvalidProjectModelException) {
541         ModelValidationResult modelValidationResult = ((InvalidProjectModelException)each).getValidationResult();
542         if (modelValidationResult != null) {
543           for (Object eachValidationProblem : modelValidationResult.getMessages()) {
544             problems.add(MavenProjectProblem.createStructureProblem(file.getPath(), (String)eachValidationProblem));
545           }
546         }
547         else {
548           problems.add(MavenProjectProblem.createStructureProblem(file.getPath(), each.getCause().getMessage()));
549         }
550       }
551       else if (each instanceof ProjectBuildingException) {
552         String causeMessage = each.getCause() != null ? each.getCause().getMessage() : each.getMessage();
553         problems.add(MavenProjectProblem.createStructureProblem(file.getPath(), causeMessage));
554       }
555       else {
556         problems.add(MavenProjectProblem.createStructureProblem(file.getPath(), each.getMessage()));
557       }
558     }
559     unresolvedArtifacts.addAll(retrieveUnresolvedArtifactIds());
560   }
561
562   private Set<MavenId> retrieveUnresolvedArtifactIds() {
563     Set<MavenId> result = new HashSet<MavenId>();
564     ((CustomWagonManager)getComponent(WagonManager.class)).getUnresolvedCollector().retrieveUnresolvedIds(result);
565     ((CustomArtifactResolver)getComponent(ArtifactResolver.class)).getUnresolvedCollector().retrieveUnresolvedIds(result);
566     return result;
567   }
568
569   @NotNull
570   public File getLocalRepositoryFile() {
571     return myImpl.getLocalRepositoryFile();
572   }
573
574   public <T> T getComponent(Class<T> clazz) {
575     return myImpl.getComponent(clazz);
576   }
577
578   public <T> T getComponent(Class<T> clazz, String roleHint) {
579     return myImpl.getComponent(clazz, roleHint);
580   }
581
582   public PlexusContainer getContainer() {
583     return myImpl.getContainer();
584   }
585
586   @NotNull
587   private <T> T doExecute(final Executor<T> executor) throws MavenServerProcessCanceledException, RemoteException {
588     Future<T> future = ExecutorManager.execute(new Callable<T>() {
589       @Override
590       @NotNull
591       public T call() throws Exception {
592         return executor.execute();
593       }
594     });
595
596     MavenServerProgressIndicator indicator = myCurrentIndicator;
597     while (true) {
598       if (indicator.isCanceled()) throw new MavenServerProcessCanceledException();
599
600       try {
601         return future.get(50, TimeUnit.MILLISECONDS);
602       }
603       catch (TimeoutException ignore) {
604       }
605       catch (ExecutionException e) {
606         Throwable cause = e.getCause();
607         if (cause instanceof MavenProcessCanceledRuntimeException) {
608           throw new MavenServerProcessCanceledException();
609         }
610         if (cause instanceof RuntimeRemoteException) {
611           throw ((RuntimeRemoteException)cause).getCause();
612         }
613         throw getRethrowable(cause);
614       }
615       catch (InterruptedException e) {
616         throw new MavenServerProcessCanceledException();
617       }
618     }
619   }
620
621   private RuntimeException getRethrowable(Throwable throwable) {
622     if (throwable instanceof InvocationTargetException) throwable = throwable.getCause();
623     return rethrowException(throwable);
624   }
625
626
627   @NotNull
628   @Override
629   public MavenServerPullProgressIndicator customizeAndGetProgressIndicator(@Nullable MavenWorkspaceMap workspaceMap,
630                                                                            boolean failOnUnresolvedDependency,
631                                                                            boolean alwaysUpdateSnapshots,
632                                                                            @Nullable Properties userProperties,
633                                                                            MavenToken token) throws RemoteException {
634     MavenServerUtil.checkToken(token);
635     try {
636       ((CustomArtifactFactory)getComponent(ArtifactFactory.class)).customize();
637       ((CustomArtifactFactory)getComponent(ProjectArtifactFactory.class)).customize();
638       ((CustomArtifactResolver)getComponent(ArtifactResolver.class)).customize(workspaceMap, failOnUnresolvedDependency);
639       ((CustomRepositoryMetadataManager)getComponent(RepositoryMetadataManager.class)).customize(workspaceMap);
640       ((CustomWagonManager)getComponent(WagonManager.class)).customize(failOnUnresolvedDependency);
641       myImpl.setUserProperties(userProperties);
642
643       return null;
644     }
645     catch (Exception e) {
646       throw rethrowException(e);
647     }
648   }
649
650   @NotNull
651   @Override
652   public List<String> retrieveAvailableVersions(@NotNull String groupId,
653                                                 @NotNull String artifactId,
654                                                 @NotNull List<MavenRemoteRepository> remoteRepositories, MavenToken token)
655     throws RemoteException {
656     MavenServerUtil.checkToken(token);
657     try {
658       Artifact artifact =
659         new DefaultArtifact(groupId, artifactId, VersionRange.createFromVersion(""), Artifact.SCOPE_COMPILE, "pom", null,
660                             new DefaultArtifactHandler("pom"));
661       ArtifactRepositoryLayout repositoryLayout = getComponent(ArtifactRepositoryLayout.class);
662       List versions = getComponent(ArtifactMetadataSource.class).retrieveAvailableVersions(
663         artifact,
664         new DefaultArtifactRepository(
665           "local",
666           getLocalRepositoryFile().getPath(),
667           repositoryLayout),
668         convertRepositories(remoteRepositories));
669
670       List<String> result = new ArrayList<String>();
671       for (Object version : versions) {
672         result.add(version.toString());
673       }
674       return result;
675     }
676     catch (Exception e) {
677       Maven2ServerGlobals.getLogger().info(e);
678     }
679     return Collections.emptyList();
680   }
681
682   @Override
683   public void customizeComponents(MavenToken token) throws RemoteException {
684     MavenServerUtil.checkToken(token);
685   }
686
687   private void setConsoleAndIndicator(MavenServerConsole console, MavenServerProgressIndicator indicator) {
688     myConsoleWrapper.setWrappee(console);
689     myCurrentIndicator = indicator;
690
691     WagonManager wagon = getComponent(WagonManager.class);
692     wagon.setDownloadMonitor(indicator == null ? null : new TransferListenerAdapter(indicator));
693   }
694
695   @Override
696   public void reset(MavenToken token) {
697     MavenServerUtil.checkToken(token);
698     try {
699       setConsoleAndIndicator(null, null);
700
701       ((CustomArtifactFactory)getComponent(ProjectArtifactFactory.class)).reset();
702       ((CustomArtifactFactory)getComponent(ArtifactFactory.class)).reset();
703       ((CustomArtifactResolver)getComponent(ArtifactResolver.class)).reset();
704       ((CustomRepositoryMetadataManager)getComponent(RepositoryMetadataManager.class)).reset();
705       ((CustomWagonManager)getComponent(WagonManager.class)).reset();
706     }
707     catch (Exception e) {
708       throw rethrowException(e);
709     }
710   }
711
712   @Override
713   public void release(MavenToken token) {
714     MavenServerUtil.checkToken(token);
715     try {
716       myImpl.release();
717     }
718     catch (Exception e) {
719       throw rethrowException(e);
720     }
721   }
722
723   @Override
724   public void clearCaches(MavenToken token) throws RemoteException {
725     MavenServerUtil.checkToken(token);
726     withProjectCachesDo(new Function<Map, Object>() {
727       @Override
728       public Object fun(Map map) {
729         map.clear();
730         return null;
731       }
732     });
733   }
734
735   @Override
736   public void clearCachesFor(final MavenId projectId, MavenToken token) throws RemoteException {
737     MavenServerUtil.checkToken(token);
738     withProjectCachesDo(new Function<Map, Object>() {
739       @Override
740       public Object fun(Map map) {
741         map.remove(projectId.getKey());
742         return null;
743       }
744     });
745   }
746
747   @Override
748   public MavenModel readModel(File file, MavenToken token) throws RemoteException {
749     MavenServerUtil.checkToken(token);
750     return null;
751   }
752
753   private void withProjectCachesDo(Function<Map, ?> func) throws RemoteException {
754     MavenProjectBuilder builder = myImpl.getComponent(MavenProjectBuilder.class);
755     Field field;
756     try {
757       field = builder.getClass().getDeclaredField("rawProjectCache");
758       field.setAccessible(true);
759       func.fun(((Map)field.get(builder)));
760
761       field = builder.getClass().getDeclaredField("processedProjectCache");
762       field.setAccessible(true);
763       func.fun(((Map)field.get(builder)));
764     }
765     catch (NoSuchFieldException e) {
766       Maven2ServerGlobals.getLogger().info(e);
767     }
768     catch (IllegalAccessException e) {
769       Maven2ServerGlobals.getLogger().info(e);
770     }
771     catch (Exception e) {
772       throw rethrowException(e);
773     }
774   }
775
776   private interface Executor<T> {
777     @NotNull
778     T execute() throws Exception;
779   }
780 }
781