2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.jetbrains
.idea
.maven
.indices
;
18 import com
.intellij
.codeInsight
.daemon
.DaemonCodeAnalyzer
;
19 import com
.intellij
.openapi
.application
.ApplicationManager
;
20 import com
.intellij
.openapi
.application
.ReadAction
;
21 import com
.intellij
.openapi
.application
.Result
;
22 import com
.intellij
.openapi
.components
.ApplicationComponent
;
23 import com
.intellij
.openapi
.extensions
.Extensions
;
24 import com
.intellij
.openapi
.progress
.BackgroundTaskQueue
;
25 import com
.intellij
.openapi
.progress
.ProcessCanceledException
;
26 import com
.intellij
.openapi
.progress
.ProgressIndicator
;
27 import com
.intellij
.openapi
.progress
.Task
;
28 import com
.intellij
.openapi
.project
.Project
;
29 import com
.intellij
.openapi
.util
.JDOMUtil
;
30 import com
.intellij
.openapi
.util
.Pair
;
31 import com
.intellij
.openapi
.util
.ShutDownTracker
;
32 import com
.intellij
.openapi
.util
.io
.FileUtil
;
33 import com
.intellij
.openapi
.util
.text
.StringUtil
;
34 import com
.intellij
.psi
.PsiManager
;
35 import com
.intellij
.psi
.impl
.PsiModificationTrackerImpl
;
36 import gnu
.trove
.THashSet
;
37 import org
.apache
.maven
.archetype
.catalog
.Archetype
;
38 import org
.apache
.maven
.archetype
.catalog
.ArchetypeCatalog
;
39 import org
.apache
.maven
.archetype
.source
.ArchetypeDataSource
;
40 import org
.apache
.maven
.archetype
.source
.ArchetypeDataSourceException
;
41 import org
.jdom
.Document
;
42 import org
.jdom
.Element
;
43 import org
.jdom
.JDOMException
;
44 import org
.jetbrains
.annotations
.NotNull
;
45 import org
.jetbrains
.annotations
.TestOnly
;
46 import org
.jetbrains
.idea
.maven
.embedder
.MavenEmbedderFactory
;
47 import org
.jetbrains
.idea
.maven
.embedder
.MavenEmbedderWrapper
;
48 import org
.jetbrains
.idea
.maven
.project
.MavenGeneralSettings
;
49 import org
.jetbrains
.idea
.maven
.project
.MavenProjectsManager
;
50 import org
.jetbrains
.idea
.maven
.utils
.MavenLog
;
51 import org
.jetbrains
.idea
.maven
.utils
.MavenUtil
;
54 import java
.io
.IOException
;
57 public class MavenIndicesManager
implements ApplicationComponent
{
58 private static final String ELEMENT_ARCHETYPES
= "archetypes";
59 private static final String ELEMENT_ARCHETYPE
= "archetype";
60 private static final String ELEMENT_GROUP_ID
= "groupId";
61 private static final String ELEMENT_ARTIFACT_ID
= "artifactId";
62 private static final String ELEMENT_VERSION
= "version";
63 private static final String ELEMENT_REPOSITORY
= "repository";
64 private static final String ELEMENT_DESCRIPTION
= "description";
66 private static final String LOCAL_REPOSITORY_ID
= "local";
68 public enum IndexUpdatingState
{
69 IDLE
, WAITING
, UPDATING
72 private volatile File myTestIndicesDir
;
74 private volatile MavenEmbedderWrapper myEmbedder
;
75 private volatile MavenIndices myIndices
;
77 private final Object myUpdatingIndicesLock
= new Object();
78 private final List
<MavenIndex
> myWaitingIndices
= new ArrayList
<MavenIndex
>();
79 private volatile MavenIndex myUpdatingIndex
;
80 private final BackgroundTaskQueue myUpdatingQueue
= new BackgroundTaskQueue(IndicesBundle
.message("maven.indices.updating"));
82 private volatile List
<ArchetypeInfo
> myUserArchetypes
= new ArrayList
<ArchetypeInfo
>();
84 public static MavenIndicesManager
getInstance() {
85 return ApplicationManager
.getApplication().getComponent(MavenIndicesManager
.class);
89 public String
getComponentName() {
90 return getClass().getSimpleName();
93 public void initComponent() {
94 ShutDownTracker
.getInstance().registerShutdownTask(new Runnable() {
102 public void setTestIndexDir(File indicesDir
) {
103 myTestIndicesDir
= indicesDir
;
106 private synchronized MavenIndices
getIndicesObject() {
111 private synchronized void ensureInitialized() {
112 if (myIndices
!= null) return;
114 MavenGeneralSettings defaultSettings
= new MavenGeneralSettings();
115 myEmbedder
= MavenEmbedderFactory
.createEmbedder(defaultSettings
);
116 myIndices
= new MavenIndices(myEmbedder
, getIndicesDir(), new MavenIndex
.IndexListener() {
117 public void indexIsBroken(MavenIndex index
) {
118 scheduleUpdate(null, Collections
.singletonList(index
), false);
122 loadUserArchetypes();
125 private File
getIndicesDir() {
126 return myTestIndicesDir
== null
127 ? MavenUtil
.getPluginSystemDir("Indices")
131 public void disposeComponent() {
133 if (ApplicationManager
.getApplication().isUnitTestMode()) {
134 FileUtil
.delete(getIndicesDir());
138 private synchronized void doShutdown() {
139 if (myIndices
!= null) {
143 catch (Exception e
) {
144 MavenLog
.LOG
.error("", e
);
149 if (myEmbedder
!= null) {
151 myEmbedder
.release();
153 catch (Exception e
) {
154 MavenLog
.LOG
.error("", e
);
161 public void doShutdownInTests() {
165 public List
<MavenIndex
> getIndices() {
166 return getIndicesObject().getIndices();
169 public synchronized List
<MavenIndex
> ensureIndicesExist(Project project
,
170 File localRepository
,
171 Collection
<Pair
<String
, String
>> remoteRepositoriesIdsAndUrls
) {
172 // MavenIndices.add method returns an existing index if it has already been added, thus we have to use set here.
173 LinkedHashSet
<MavenIndex
> result
= new LinkedHashSet
<MavenIndex
>();
175 MavenIndices indicesObjectCache
= getIndicesObject();
178 MavenIndex localIndex
= indicesObjectCache
.add(LOCAL_REPOSITORY_ID
, localRepository
.getPath(), MavenIndex
.Kind
.LOCAL
);
179 result
.add(localIndex
);
180 if (localIndex
.getUpdateTimestamp() == -1) {
181 scheduleUpdate(project
, Collections
.singletonList(localIndex
));
184 catch (MavenIndexException e
) {
185 MavenLog
.LOG
.warn(e
);
188 for (Pair
<String
, String
> eachIdAndUrl
: remoteRepositoriesIdsAndUrls
) {
190 result
.add(indicesObjectCache
.add(eachIdAndUrl
.first
, eachIdAndUrl
.second
, MavenIndex
.Kind
.REMOTE
));
192 catch (MavenIndexException e
) {
193 MavenLog
.LOG
.warn(e
);
197 return new ArrayList
<MavenIndex
>(result
);
200 public void addArtifact(File artifactFile
, String name
) {
201 String repositoryPath
= getRepositoryUrl(artifactFile
, name
);
203 MavenIndex index
= getIndicesObject().find(LOCAL_REPOSITORY_ID
, repositoryPath
, MavenIndex
.Kind
.LOCAL
);
205 index
.addArtifact(artifactFile
);
209 private String
getRepositoryUrl(File artifactFile
, String name
) {
210 List
<String
> parts
= getArtifactParts(name
);
212 File result
= artifactFile
;
213 for (int i
= 0; i
< parts
.size(); i
++) {
214 result
= result
.getParentFile();
216 return result
.getPath();
219 private List
<String
> getArtifactParts(String name
) {
220 return StringUtil
.split(name
, "/");
223 public void scheduleUpdate(Project project
, List
<MavenIndex
> indices
) {
224 scheduleUpdate(project
, indices
, true);
227 private void scheduleUpdate(final Project projectOrNull
, List
<MavenIndex
> indices
, final boolean fullUpdate
) {
228 final List
<MavenIndex
> toSchedule
= new ArrayList
<MavenIndex
>();
230 synchronized (myUpdatingIndicesLock
) {
231 for (MavenIndex each
: indices
) {
232 if (myWaitingIndices
.contains(each
)) continue;
233 toSchedule
.add(each
);
236 myWaitingIndices
.addAll(toSchedule
);
239 myUpdatingQueue
.run(new Task
.Backgroundable(projectOrNull
, IndicesBundle
.message("maven.indices.updating"), true) {
240 public void run(@NotNull ProgressIndicator indicator
) {
241 doUpdateIndices(projectOrNull
, toSchedule
, fullUpdate
, indicator
);
246 private void doUpdateIndices(final Project projectOrNull
, List
<MavenIndex
> indices
, boolean fullUpdate
, ProgressIndicator indicator
) {
247 List
<MavenIndex
> remainingWaiting
= new ArrayList
<MavenIndex
>(indices
);
250 for (MavenIndex each
: indices
) {
251 if (indicator
.isCanceled()) return;
253 indicator
.setText(IndicesBundle
.message("maven.indices.updating.index",
254 each
.getRepositoryId(),
255 each
.getRepositoryPathOrUrl()));
257 synchronized (myUpdatingIndicesLock
) {
258 remainingWaiting
.remove(each
);
259 myWaitingIndices
.remove(each
);
260 myUpdatingIndex
= each
;
264 MavenEmbedderWrapper embedderToUse
= null;
266 embedderToUse
= new ReadAction
<MavenEmbedderWrapper
>() {
268 protected void run(Result
<MavenEmbedderWrapper
> result
) throws Throwable
{
269 if (!projectOrNull
.isDisposed()) {
270 MavenGeneralSettings settings
= MavenProjectsManager
.getInstance(projectOrNull
).getGeneralSettings();
271 result
.setResult(MavenEmbedderFactory
.createEmbedder(settings
));
274 }.execute().getResultObject();
275 if (embedderToUse
== null /* project disposed */) throw new ProcessCanceledException();
279 getIndicesObject().updateOrRepair(each
, embedderToUse
, fullUpdate
, indicator
);
282 if (embedderToUse
!= null) embedderToUse
.release();
285 scheduleRehighlightAllPoms(projectOrNull
);
288 synchronized (myUpdatingIndicesLock
) {
289 myUpdatingIndex
= null;
294 catch (ProcessCanceledException ignore
) {
297 synchronized (myUpdatingIndicesLock
) {
298 myWaitingIndices
.removeAll(remainingWaiting
);
303 public IndexUpdatingState
getUpdatingState(MavenIndex index
) {
304 synchronized (myUpdatingIndicesLock
) {
305 if (myUpdatingIndex
== index
) return IndexUpdatingState
.UPDATING
;
306 if (myWaitingIndices
.contains(index
)) return IndexUpdatingState
.WAITING
;
307 return IndexUpdatingState
.IDLE
;
311 private void scheduleRehighlightAllPoms(final Project projectOrNull
) {
312 if (projectOrNull
== null) return;
313 MavenUtil
.invokeLater(projectOrNull
, new Runnable() {
315 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
317 ((PsiModificationTrackerImpl
)PsiManager
.getInstance(projectOrNull
).getModificationTracker()).incCounter();
318 DaemonCodeAnalyzer
.getInstance(projectOrNull
).restart();
325 public synchronized Set
<ArchetypeInfo
> getArchetypes() {
327 Set
<ArchetypeInfo
> result
= new THashSet
<ArchetypeInfo
>();
328 result
.addAll(getArchetypesFrom("internal-catalog"));
329 result
.addAll(getArchetypesFrom("nexus"));
330 result
.addAll(myUserArchetypes
);
332 for (MavenArchetypesProvider each
: Extensions
.getExtensions(MavenArchetypesProvider
.EP_NAME
)) {
333 result
.addAll(each
.getArchetypes());
338 public synchronized void addArchetype(ArchetypeInfo archetype
) {
340 myUserArchetypes
.add(archetype
);
341 saveUserArchetypes();
344 private void loadUserArchetypes() {
346 File file
= getUserArchetypesFile();
347 if (!file
.exists()) return;
349 Document doc
= JDOMUtil
.loadDocument(file
);
350 Element root
= doc
.getRootElement();
351 if (root
== null) return;
352 List
<ArchetypeInfo
> result
= new ArrayList
<ArchetypeInfo
>();
353 for (Element each
: (Iterable
<?
extends Element
>)root
.getChildren(ELEMENT_ARCHETYPE
)) {
354 String groupId
= each
.getAttributeValue(ELEMENT_GROUP_ID
);
355 String artifactId
= each
.getAttributeValue(ELEMENT_ARTIFACT_ID
);
356 String version
= each
.getAttributeValue(ELEMENT_VERSION
);
357 String repository
= each
.getAttributeValue(ELEMENT_REPOSITORY
);
358 String description
= each
.getAttributeValue(ELEMENT_DESCRIPTION
);
360 if (StringUtil
.isEmptyOrSpaces(groupId
)
361 || StringUtil
.isEmptyOrSpaces(artifactId
)
362 || StringUtil
.isEmptyOrSpaces(version
)) {
366 result
.add(new ArchetypeInfo(groupId
, artifactId
, version
, repository
, description
));
369 myUserArchetypes
= result
;
371 catch (IOException e
) {
372 MavenLog
.LOG
.warn(e
);
374 catch (JDOMException e
) {
375 MavenLog
.LOG
.warn(e
);
379 private void saveUserArchetypes() {
380 Element root
= new Element(ELEMENT_ARCHETYPES
);
381 for (ArchetypeInfo each
: myUserArchetypes
) {
382 Element childElement
= new Element(ELEMENT_ARCHETYPE
);
383 childElement
.setAttribute(ELEMENT_GROUP_ID
, each
.groupId
);
384 childElement
.setAttribute(ELEMENT_ARTIFACT_ID
, each
.artifactId
);
385 childElement
.setAttribute(ELEMENT_VERSION
, each
.version
);
386 if (each
.repository
!= null) {
387 childElement
.setAttribute(ELEMENT_REPOSITORY
, each
.repository
);
389 if (each
.description
!= null) {
390 childElement
.setAttribute(ELEMENT_DESCRIPTION
, each
.description
);
392 root
.addContent(childElement
);
395 File file
= getUserArchetypesFile();
396 file
.getParentFile().mkdirs();
397 JDOMUtil
.writeDocument(new Document(root
), file
, "\n");
399 catch (IOException e
) {
400 MavenLog
.LOG
.warn(e
);
404 private File
getUserArchetypesFile() {
405 return new File(getIndicesDir(), "UserArchetypes.xml");
408 private List
<ArchetypeInfo
> getArchetypesFrom(String roleHint
) {
410 ArchetypeDataSource source
= myEmbedder
.getComponent(ArchetypeDataSource
.class, roleHint
);
411 ArchetypeCatalog catalog
= source
.getArchetypeCatalog(new Properties());
413 List
<ArchetypeInfo
> result
= new ArrayList
<ArchetypeInfo
>();
414 for (Archetype each
: (Iterable
<?
extends Archetype
>)catalog
.getArchetypes()) {
415 result
.add(new ArchetypeInfo(each
));
420 catch (ArchetypeDataSourceException e
) {
421 MavenLog
.LOG
.warn(e
);
423 return Collections
.emptyList();