blob: e849efea91ed445cf12393bc0b653aaca7905443 [file] [log] [blame]
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed 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.jetbrains.idea.maven.server.embedder;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.util.ShutDownTracker;
import com.intellij.openapi.util.text.StringUtil;
import gnu.trove.THashSet;
import gnu.trove.TIntObjectHashMap;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.maven.archetype.catalog.Archetype;
import org.apache.maven.archetype.catalog.ArchetypeCatalog;
import org.apache.maven.archetype.source.ArchetypeDataSource;
import org.apache.maven.archetype.source.ArchetypeDataSourceException;
import org.apache.maven.artifact.manager.WagonManager;
import org.apache.maven.wagon.events.TransferEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.model.MavenArchetype;
import org.jetbrains.idea.maven.model.MavenArtifactInfo;
import org.jetbrains.idea.maven.model.MavenId;
import org.jetbrains.idea.maven.server.*;
import org.sonatype.nexus.index.*;
import org.sonatype.nexus.index.context.IndexUtils;
import org.sonatype.nexus.index.context.IndexingContext;
import org.sonatype.nexus.index.updater.IndexUpdateRequest;
import org.sonatype.nexus.index.updater.IndexUpdater;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.*;
public class Maven2ServerIndexerImpl extends MavenRemoteObject implements MavenServerIndexer {
private Maven2ServerEmbedderImpl myEmbedder;
private final NexusIndexer myIndexer;
private final IndexUpdater myUpdater;
private final ArtifactContextProducer myArtifactContextProducer;
private final TIntObjectHashMap<IndexingContext> myIndices = new TIntObjectHashMap<IndexingContext>();
public Maven2ServerIndexerImpl() throws RemoteException {
myEmbedder = Maven2ServerEmbedderImpl.create(new MavenServerSettings());
myIndexer = myEmbedder.getComponent(NexusIndexer.class);
myUpdater = myEmbedder.getComponent(IndexUpdater.class);
myArtifactContextProducer = myEmbedder.getComponent(ArtifactContextProducer.class);
ShutDownTracker.getInstance().registerShutdownTask(new Runnable() {
@Override
public void run() {
release();
}
});
}
public int createIndex(@NotNull String indexId,
@NotNull String repositoryId,
@Nullable File file,
@Nullable String url,
@NotNull File indexDir) throws MavenServerIndexerException {
try {
IndexingContext context = myIndexer.addIndexingContextForced(indexId,
repositoryId,
file,
indexDir,
url,
null, // repo update url
NexusIndexer.FULL_INDEX);
int id = System.identityHashCode(context);
myIndices.put(id, context);
return id;
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
public void releaseIndex(int id) throws MavenServerIndexerException {
try {
myIndexer.removeIndexingContext(getIndex(id), false);
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
@NotNull
private IndexingContext getIndex(int id) {
IndexingContext index = myIndices.get(id);
if (index == null) throw new RuntimeException("Index not found for id: " + id);
return index;
}
@Override
public boolean indexExists(File dir) throws RemoteException {
try {
return IndexReader.indexExists(dir);
}
catch (Exception e) {
Maven2ServerGlobals.getLogger().warn(e);
}
return false;
}
private String getRepositoryPathOrUrl(IndexingContext index) {
File file = index.getRepository();
return file == null ? index.getRepositoryUrl() : file.getPath();
}
private boolean isLocal(IndexingContext index) {
return index.getRepository() != null;
}
public int getIndexCount() {
return myIndexer.getIndexingContexts().size();
}
public void updateIndex(int id, MavenServerSettings settings, MavenServerProgressIndicator indicator) throws
MavenServerIndexerException,
MavenServerProcessCanceledException,
RemoteException {
IndexingContext index = getIndex(id);
try {
if (isLocal(index)) {
File repository = index.getRepository();
if (repository != null && repository.exists()) {
indicator.setIndeterminate(true);
try {
myIndexer.scan(index, new MyScanningListener(indicator), false);
}
finally {
indicator.setIndeterminate(false);
}
}
}
else {
IndexUpdateRequest request = new IndexUpdateRequest(index);
Maven2ServerEmbedderImpl embedder = Maven2ServerEmbedderImpl.create(settings);
try {
request.setResourceFetcher(new Maven2ServerIndexFetcher(index.getRepositoryId(),
index.getRepositoryUrl(),
embedder.getComponent(WagonManager.class),
new TransferListenerAdapter(indicator) {
@Override
protected void downloadProgress(long downloaded, long total) {
super.downloadProgress(downloaded, total);
try {
myIndicator.setFraction(((double)downloaded) / total);
}
catch (RemoteException e) {
throw new RuntimeRemoteException(e);
}
}
@Override
public void transferCompleted(TransferEvent event) {
super.transferCompleted(event);
try {
myIndicator.setText2("Processing indices...");
}
catch (RemoteException e) {
throw new RuntimeRemoteException(e);
}
}
}));
myUpdater.fetchAndUpdateIndex(request);
}
finally {
embedder.release();
}
}
}
catch (RuntimeRemoteException e) {
throw e.getCause();
}
catch (ProcessCanceledException e) {
throw new MavenServerProcessCanceledException();
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
public void processArtifacts(int indexId, MavenServerIndicesProcessor processor) throws MavenServerIndexerException {
try {
final int CHUNK_SIZE = 10000;
IndexReader r = getIndex(indexId).getIndexReader();
int total = r.numDocs();
List<MavenId> result = new ArrayList<MavenId>(Math.min(CHUNK_SIZE, total));
for (int i = 0; i < total; i++) {
if (r.isDeleted(i)) continue;
Document doc = r.document(i);
String uinfo = doc.get(SEARCH_TERM_COORDINATES);
if (uinfo == null) continue;
List<String> parts = StringUtil.split(uinfo, "|");
String groupId = parts.get(0);
String artifactId = parts.get(1);
String version = parts.get(2);
if (groupId == null || artifactId == null || version == null) continue;
result.add(new MavenId(groupId, artifactId, version));
if (result.size() == CHUNK_SIZE) {
processor.processArtifacts(result);
result.clear();
}
}
if (!result.isEmpty()) {
processor.processArtifacts(result);
}
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
public MavenId addArtifact(int indexId, File artifactFile) throws MavenServerIndexerException {
try {
IndexingContext index = getIndex(indexId);
ArtifactContext artifactContext = myArtifactContextProducer.getArtifactContext(index, artifactFile);
if (artifactContext == null) return null;
addArtifact(myIndexer, index, artifactContext);
org.sonatype.nexus.index.ArtifactInfo a = artifactContext.getArtifactInfo();
return new MavenId(a.groupId, a.artifactId, a.version);
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
public static void addArtifact(NexusIndexer indexer, IndexingContext index, ArtifactContext artifactContext)
throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
indexer.addArtifactToIndex(artifactContext, index);
// this hack is necessary to invalidate searcher's and reader's cache (may not be required then lucene or nexus library change
Method m = index.getClass().getDeclaredMethod("closeReaders");
m.setAccessible(true);
m.invoke(index);
}
public Set<MavenArtifactInfo> search(int indexId, Query query, int maxResult) throws MavenServerIndexerException {
try {
IndexingContext index = getIndex(indexId);
TopDocs docs = null;
try {
BooleanQuery.setMaxClauseCount(Integer.MAX_VALUE);
docs = index.getIndexSearcher().search(query, null, maxResult);
}
catch (BooleanQuery.TooManyClauses ignore) {
// this exception occurs when too wide wildcard is used on too big data.
}
if (docs == null || docs.scoreDocs.length == 0) return Collections.emptySet();
Set<MavenArtifactInfo> result = new THashSet<MavenArtifactInfo>();
for (int i = 0; i < docs.scoreDocs.length; i++) {
int docIndex = docs.scoreDocs[i].doc;
Document doc = index.getIndexReader().document(docIndex);
ArtifactInfo a = IndexUtils.constructArtifactInfo(doc, index);
if (a == null) continue;
a.repository = getRepositoryPathOrUrl(index);
result.add(Maven2ModelConverter.convertArtifactInfo(a));
}
return result;
}
catch (Exception e) {
throw new MavenServerIndexerException(wrapException(e));
}
}
public Collection<MavenArchetype> getArchetypes() throws RemoteException {
Set<MavenArchetype> result = new THashSet<MavenArchetype>();
doCollectArchetypes("internal-catalog", result);
doCollectArchetypes("nexus", result);
return result;
}
private void doCollectArchetypes(String roleHint, Set<MavenArchetype> result) throws RemoteException {
try {
ArchetypeDataSource source = myEmbedder.getComponent(ArchetypeDataSource.class, roleHint);
ArchetypeCatalog catalog = source.getArchetypeCatalog(new Properties());
for (Archetype each : (Iterable<? extends Archetype>)catalog.getArchetypes()) {
result.add(Maven2ModelConverter.convertArchetype(each));
}
}
catch (ArchetypeDataSourceException e) {
Maven2ServerGlobals.getLogger().warn(e);
}
}
public void release() {
try {
myEmbedder.release();
}
catch (Exception e) {
throw rethrowException(e);
}
}
private static class MyScanningListener implements ArtifactScanningListener {
private final MavenServerProgressIndicator p;
public MyScanningListener(MavenServerProgressIndicator indicator) {
p = indicator;
}
public void scanningStarted(IndexingContext ctx) {
try {
if (p.isCanceled()) throw new ProcessCanceledException();
}
catch (RemoteException e) {
throw new RuntimeRemoteException(e);
}
}
public void scanningFinished(IndexingContext ctx, ScanningResult result) {
try {
if (p.isCanceled()) throw new ProcessCanceledException();
}
catch (RemoteException e) {
throw new RuntimeRemoteException(e);
}
}
public void artifactError(ArtifactContext ac, Exception e) {
}
public void artifactDiscovered(ArtifactContext ac) {
try {
if (p.isCanceled()) throw new ProcessCanceledException();
ArtifactInfo info = ac.getArtifactInfo();
p.setText2(info.groupId + ":" + info.artifactId + ":" + info.version);
}
catch (RemoteException e) {
throw new RuntimeRemoteException(e);
}
}
}
}