update copyright
[fedora-idea.git] / plugins / maven / src / main / java / org / jetbrains / idea / maven / indices / MavenIndicesManager.java
blobd9ed6836a471997a9a9b8944d022b346af43d189
1 /*
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;
53 import java.io.File;
54 import java.io.IOException;
55 import java.util.*;
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);
88 @NotNull
89 public String getComponentName() {
90 return getClass().getSimpleName();
93 public void initComponent() {
94 ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
95 public void run() {
96 doShutdown();
98 });
101 @TestOnly
102 public void setTestIndexDir(File indicesDir) {
103 myTestIndicesDir = indicesDir;
106 private synchronized MavenIndices getIndicesObject() {
107 ensureInitialized();
108 return myIndices;
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")
128 : myTestIndicesDir;
131 public void disposeComponent() {
132 doShutdown();
133 if (ApplicationManager.getApplication().isUnitTestMode()) {
134 FileUtil.delete(getIndicesDir());
138 private synchronized void doShutdown() {
139 if (myIndices != null) {
140 try {
141 myIndices.close();
143 catch (Exception e) {
144 MavenLog.LOG.error("", e);
146 myIndices = null;
149 if (myEmbedder != null) {
150 try {
151 myEmbedder.release();
153 catch (Exception e) {
154 MavenLog.LOG.error("", e);
156 myEmbedder = null;
160 @TestOnly
161 public void doShutdownInTests() {
162 doShutdown();
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();
177 try {
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) {
189 try {
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);
204 if (index != null) {
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);
249 try {
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;
263 try {
264 MavenEmbedderWrapper embedderToUse = null;
265 if (fullUpdate) {
266 embedderToUse = new ReadAction<MavenEmbedderWrapper>() {
267 @Override
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();
278 try {
279 getIndicesObject().updateOrRepair(each, embedderToUse, fullUpdate, indicator);
281 finally {
282 if (embedderToUse != null) embedderToUse.release();
285 scheduleRehighlightAllPoms(projectOrNull);
287 finally {
288 synchronized (myUpdatingIndicesLock) {
289 myUpdatingIndex = null;
294 catch (ProcessCanceledException ignore) {
296 finally {
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() {
314 public void run() {
315 ApplicationManager.getApplication().runWriteAction(new Runnable() {
316 public void run() {
317 ((PsiModificationTrackerImpl)PsiManager.getInstance(projectOrNull).getModificationTracker()).incCounter();
318 DaemonCodeAnalyzer.getInstance(projectOrNull).restart();
325 public synchronized Set<ArchetypeInfo> getArchetypes() {
326 ensureInitialized();
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());
335 return result;
338 public synchronized void addArchetype(ArchetypeInfo archetype) {
339 ensureInitialized();
340 myUserArchetypes.add(archetype);
341 saveUserArchetypes();
344 private void loadUserArchetypes() {
345 try {
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)) {
363 continue;
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);
394 try {
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) {
409 try {
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));
418 return result;
420 catch (ArchetypeDataSourceException e) {
421 MavenLog.LOG.warn(e);
423 return Collections.emptyList();